/* * 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.receivers.net; import org.apache.log4j.component.plugins.Pauseable; import org.apache.log4j.component.plugins.Plugin; import org.apache.log4j.component.plugins.Receiver; import org.apache.log4j.net.ZeroConfSupport; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.LoggingEvent; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.*; /** 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 socketMap = new HashMap(); /** * Paused. */ private boolean paused; /** * Thread. */ private Thread rThread; /** * Port. */ protected int port; /** * Server socket. */ private ServerSocket serverSocket; /** * Socket list. */ private Vector 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 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; } public int getPort() { return port; } 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 (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(); } /** 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 (Iterator iter = listenerList.iterator(); iter.hasNext();) { SocketNodeEventListener l = (SocketNodeEventListener) iter.next(); 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 getConnectedSocketDetails() { Vector details = new Vector(socketList.size()); for (Enumeration enumeration = socketList.elements(); enumeration.hasMoreElements(); ) { Socket socket = (Socket) enumeration.nextElement(); details.add( new SocketDetail(socket, (SocketNode13) 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); } } }