blob: 29725abcc22e49d0e058be523c28142654721e6f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.log4j.net;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.apache.log4j.plugins.Pauseable;
import org.apache.log4j.plugins.Plugin;
import org.apache.log4j.plugins.Receiver;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggingEvent;
/**
SocketReceiver receives a remote logging event on a configured
socket and "posts" it to a LoggerRepository as if the event was
generated locally. This class is designed to receive events from
the SocketAppender class (or classes that send compatible events).
<p>Once the event has been "posted", it will be handled by the
appenders currently configured in the LoggerRespository.
@author Mark Womack
@author Scott Deboy (sdeboy@apache.org)
@author Paul Smith (psmith@apache.org)
*/
public class SocketReceiver extends Receiver implements Runnable, PortBased,
Pauseable {
/**
* socket map.
*/
private Map<Socket, SocketNode13> socketMap = new HashMap<>();
/**
* Paused.
*/
private boolean paused;
/**
* Thread.
*/
private Thread rThread;
/**
* Port.
*/
protected int port;
/**
* Server socket.
*/
private ServerSocket serverSocket;
/**
* Socket list.
*/
private Vector<Socket> socketList = new Vector<>();
/**
* The MulticastDNS zone advertised by a SocketReceiver
*/
public static final String ZONE = "_log4j_obj_tcpaccept_receiver.local.";
/**
* Listener.
*/
private SocketNodeEventListener listener = null;
/**
* Listeners.
*/
private final List listenerList = Collections.synchronizedList(new ArrayList());
private boolean advertiseViaMulticastDNS;
private ZeroConfSupport zeroConf;
/**
* Create new instance.
*/
public SocketReceiver() {
super();
}
/**
* Create new instance.
* @param p port
*/
public SocketReceiver(final int p) {
super();
port = p;
}
/**
* Create new instance.
* @param p port
* @param repo logger repository
*/
public SocketReceiver(final int p, final LoggerRepository repo) {
super();
this.port = p;
repository = repo;
}
/** {@inheritDoc} */
public int getPort() {
return port;
}
/** {@inheritDoc} */
public void setPort(final int p) {
port = p;
}
/**
* Returns true if the receiver is the same class and they are
* configured for the same properties, and super class also considers
* them to be equivalent. This is used by PluginRegistry when determining
* if the a similarly configured receiver is being started.
*
* @param testPlugin The plugin to test equivalency against.
* @return boolean True if the testPlugin is equivalent to this plugin.
*/
public boolean isEquivalent(final Plugin testPlugin) {
if ((testPlugin != null) && testPlugin instanceof SocketReceiver) {
SocketReceiver sReceiver = (SocketReceiver) testPlugin;
return (port == sReceiver.getPort() && super.isEquivalent(testPlugin));
}
return false;
}
/**
Starts the SocketReceiver with the current options. */
public void activateOptions() {
if (!isActive()) {
// shutdown();
rThread = new Thread(this);
rThread.setDaemon(true);
rThread.start();
if (advertiseViaMulticastDNS) {
zeroConf = new ZeroConfSupport(ZONE, port, getName());
zeroConf.advertise();
}
active = true;
}
}
/**
* Called when the receiver should be stopped. Closes the
* server socket and all of the open sockets.
*/
public synchronized void shutdown() {
getLogger().debug(getName() + " received shutdown request");
// mark this as no longer running
active = false;
if (rThread != null) {
rThread.interrupt();
rThread = null;
}
if (advertiseViaMulticastDNS) {
zeroConf.unadvertise();
}
doShutdown();
}
/**
* Does the actual shutting down by closing the server socket
* and any connected sockets that have been created.
*/
private synchronized void doShutdown() {
active = false;
getLogger().debug(getName() + " doShutdown called");
// close the server socket
closeServerSocket();
// close all of the accepted sockets
closeAllAcceptedSockets();
}
/**
* Closes the server socket, if created.
*/
private void closeServerSocket() {
getLogger().debug("{} closing server socket", getName());
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (Exception e) {
// ignore for now
}
serverSocket = null;
}
/**
* Closes all the connected sockets in the List.
*/
private synchronized void closeAllAcceptedSockets() {
for (Object aSocketList : socketList) {
try {
((Socket) aSocketList).close();
} catch (Exception e) {
// ignore for now
}
}
// clear member variables
socketMap.clear();
socketList.clear();
}
/**
Sets the flag to indicate if receiver is active or not.
@param b new value
*/
protected synchronized void setActive(final boolean b) {
active = b;
}
public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
}
public boolean isAdvertiseViaMulticastDNS() {
return advertiseViaMulticastDNS;
}
/**
Loop, accepting new socket connections. */
public void run() {
/**
* Ensure we start fresh.
*/
closeServerSocket();
closeAllAcceptedSockets();
// start the server socket
try {
serverSocket = new ServerSocket(port);
} catch (Exception e) {
getLogger().error(
"error starting SocketReceiver (" + this.getName()
+ "), receiver did not start", e);
active = false;
return;
}
Socket socket = null;
try {
getLogger().debug("in run-about to enter while not interrupted loop");
active = true;
while (!rThread.isInterrupted()) {
// if we have a socket, start watching it
if (socket != null) {
getLogger().debug(
"socket not null - creating and starting socketnode");
socketList.add(socket);
SocketNode13 node = new SocketNode13(socket, this);
synchronized (listenerList) {
for (Object aListenerList : listenerList) {
SocketNodeEventListener l =
(SocketNodeEventListener) aListenerList;
node.addSocketNodeEventListener(l);
}
}
socketMap.put(socket, node);
new Thread(node).start();
socket = null;
}
getLogger().debug("waiting to accept socket");
// wait for a socket to open, then loop to start it
socket = serverSocket.accept();
getLogger().debug("accepted socket");
}
} catch (Exception e) {
getLogger().warn(
"exception while watching socket server in SocketReceiver ("
+ this.getName() + "), stopping");
}
getLogger().debug("{} has exited the not interrupted loop", getName());
// socket not watched because we a no longer running
// so close it now.
if (socket != null) {
try {
socket.close();
} catch (IOException e1) {
getLogger().warn("socket exception caught - socket closed");
}
}
getLogger().debug("{} is exiting main run loop", getName());
}
/**
* Returns a Vector of SocketDetail representing the IP/Domain name
* of the currently connected sockets that this receiver has
* been responsible for creating.
* @return Vector of SocketDetails
*/
public Vector<SocketDetail> getConnectedSocketDetails() {
Vector<SocketDetail> details = new Vector<>(socketList.size());
for (Enumeration<Socket> enumeration = socketList.elements();
enumeration.hasMoreElements();
) {
Socket socket = enumeration.nextElement();
details.add(
new SocketDetail(socket, socketMap.get(socket)));
}
return details;
}
/**
* Returns the currently configured SocketNodeEventListener that
* will be automatically set for each SocketNode created.
* @return SocketNodeEventListener currently configured
*
* @deprecated This receiver now supports multiple listeners
*/
public SocketNodeEventListener getListener() {
return listener;
}
/**
* Adds the listener to the list of listeners to be notified of the
* respective event.
* @param l the listener to add to the list
*/
public void addSocketNodeEventListener(
final SocketNodeEventListener l) {
listenerList.add(l);
}
/**
* Removes the registered Listener from this instances list of
* listeners. If the listener has not been registered, then invoking
* this method has no effect.
*
* @param l the SocketNodeEventListener to remove
*/
public void removeSocketNodeEventListener(
final SocketNodeEventListener l) {
listenerList.remove(l);
}
/**
* Sets the SocketNodeEventListener that will be used for each
* created SocketNode.
* @param l the listener to set on each creation of a SocketNode
* @deprecated This receiver now supports multiple listeners and
* so this method simply removes the listener (if there already)
* and readds it to the list.
*
* The passed listener will also be returned via the getListener()
* method still, but this is also deprecated
*/
public void setListener(final SocketNodeEventListener l) {
removeSocketNodeEventListener(l);
addSocketNodeEventListener(l);
this.listener = l;
}
/** {@inheritDoc} */
public boolean isPaused() {
return paused;
}
/** {@inheritDoc} */
public void setPaused(final boolean b) {
paused = b;
}
/**
* Socket detail.
*/
private static final class SocketDetail implements AddressBased, PortBased,
Pauseable {
/**
* Address.
*/
private String address;
/**
* Port.
*/
private int port;
/**
* Socket node.
*/
private SocketNode13 socketNode;
/**
* Create new instance.
* @param socket socket
* @param node socket node
*/
private SocketDetail(final Socket socket,
final SocketNode13 node) {
super();
this.address = socket.getInetAddress().getHostName();
this.port = socket.getPort();
this.socketNode = node;
}
/** {@inheritDoc} */
public String getAddress() {
return address;
}
/** {@inheritDoc} */
public int getPort() {
return port;
}
/** {@inheritDoc} */
public String getName() {
return "Socket";
}
/** {@inheritDoc} */
public boolean isActive() {
return true;
}
/** {@inheritDoc} */
public boolean isPaused() {
return socketNode.isPaused();
}
/** {@inheritDoc} */
public void setPaused(final boolean b) {
socketNode.setPaused(b);
}
}
/** {@inheritDoc} */
public void doPost(final LoggingEvent event) {
if (!isPaused()) {
super.doPost(event);
}
}
}