| /* |
| * 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.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.logging.log4j.core.LogEvent; |
| |
| |
| /** |
| 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; |
| } |
| |
| /** {@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 (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 LogEvent event) { |
| if (!isPaused()) { |
| super.doPost(event); |
| } |
| } |
| |
| } |