blob: 1acfc0c7b37f4282d8cf73b977be920a18668381 [file] [log] [blame]
/*
* Copyright 1999,2004 The Apache Software Foundation.
*
* Licensed 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.Iterator;
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>
@since 1.3
*/
public class SocketReceiver extends Receiver implements Runnable, PortBased,
Pauseable {
private Map socketMap = new HashMap();
private boolean paused;
private boolean shutdown;
private Thread rThread;
protected int port;
private ServerSocket serverSocket;
private Vector socketList = new Vector();
private SocketNodeEventListener listener = null;
private List listenerList = Collections.synchronizedList(new ArrayList());
public SocketReceiver() {
}
public SocketReceiver(int _port) {
port = _port;
}
public SocketReceiver(int _port, LoggerRepository _repository) {
port = _port;
repository = _repository;
}
/**
Get the port to receive logging events on. */
public int getPort() {
return port;
}
/**
Set the port to receive logging events on. */
public void setPort(int _port) {
port = _port;
}
/**
* 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(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();
active = true;
setShutdown(false);
}
}
/**
* 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;
}
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();
setShutdown(true);
}
/**
* 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 (int x = 0; x < socketList.size(); x++) {
try {
((Socket) socketList.get(x)).close();
} catch (Exception e) {
// ignore for now
}
}
// clear member variables
socketMap.clear();
socketList.clear();
}
/**
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;
setShutdown(true);
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);
SocketNode node = new SocketNode(socket, this);
synchronized(listenerList){
for (Iterator iter = listenerList.iterator(); iter
.hasNext();) {
SocketNodeEventListener listener = (SocketNodeEventListener) iter.next();
node.addSocketNodeEventListener(listener);
}
}
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 getConnectedSocketDetails() {
Vector details = new Vector(socketList.size());
for (Enumeration enumeration = socketList.elements(); enumeration.hasMoreElements();) {
Socket socket = (Socket) enumeration.nextElement();
details.add(
new SocketDetail(socket, (SocketNode) 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 listener the listener to add to the list
*/
public void addSocketNodeEventListener(SocketNodeEventListener listener) {
listenerList.add(listener);
}
/**
* 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 listener the SocketNodeEventListener to remove
*/
public void removeSocketNodeEventListener(SocketNodeEventListener listener) {
listenerList.remove(listener);
}
/**
* Sets the SocketNodeEventListener that will be used for each
* created SocketNode
* @param listener 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(SocketNodeEventListener listener) {
removeSocketNodeEventListener(listener);
addSocketNodeEventListener(listener);
this.listener = listener;
}
/**
* Returns the shutdown property of this Receiver
* @return
*/
private boolean isShutdown() {
return shutdown;
}
/**
* @param b
*/
private void setShutdown(boolean b) {
shutdown = b;
}
public boolean isPaused() {
return paused;
}
public void setPaused(boolean b) {
paused = b;
}
public static class SocketDetail implements AddressBased, PortBased,
Pauseable {
private String address;
private int port;
private SocketNode socketNode;
private SocketDetail(Socket socket, SocketNode socketNode) {
this.address = socket.getInetAddress().getHostName();
this.port = socket.getPort();
this.socketNode = socketNode;
}
public String getAddress() {
return address;
}
public int getPort() {
return port;
}
public String getName() {
return "Socket";
}
public boolean isActive() {
return true;
}
public boolean isPaused() {
return socketNode.isPaused();
}
public void setPaused(boolean paused) {
socketNode.setPaused(paused);
}
}
/* (non-Javadoc)
* @see org.apache.log4j.plugins.Receiver#doPost(org.apache.log4j.spi.LoggingEvent)
*/
public void doPost(LoggingEvent event) {
if(!isPaused()){
super.doPost(event);
}
}
}