View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.log4j.net;
    19
    20  import java.io.IOException;
    21  import java.net.ServerSocket;
    22  import java.net.Socket;
    23  import java.util.ArrayList;
    24  import java.util.Collections;
    25  import java.util.Enumeration;
    26  import java.util.HashMap;
    27  import java.util.Iterator;
    28  import java.util.List;
    29  import java.util.Map;
    30  import java.util.Vector;
    31
    32  import org.apache.log4j.plugins.Pauseable;
    33  import org.apache.log4j.plugins.Plugin;
    34  import org.apache.log4j.plugins.Receiver;
    35  import org.apache.log4j.spi.LoggerRepository;
    36  import org.apache.log4j.spi.LoggingEvent;
    37
    38
    39  /**
    40    SocketReceiver receives a remote logging event on a configured
    41    socket and "posts" it to a LoggerRepository as if the event was
    42    generated locally. This class is designed to receive events from
    43    the SocketAppender class (or classes that send compatible events).
    44
    45    <p>Once the event has been "posted", it will be handled by the
    46    appenders currently configured in the LoggerRespository.
    47
    48    @author Mark Womack
    49    @author Scott Deboy (sdeboy@apache.org)
    50    @author Paul Smith (psmith@apache.org)
    51  */
    52  public class SocketReceiver extends Receiver implements Runnable, PortBased,
    53    Pauseable {
    54      /**
    55       * socket map.
    56       */
    57    private Map socketMap = new HashMap();
    58      /**
    59       * Paused.
    60       */
    61    private boolean paused;
    62      /**
    63       * Thread.
    64       */
    65    private Thread rThread;
    66      /**
    67       * Port.
    68       */
    69    protected int port;
    70      /**
    71       * Server socket.
    72       */
    73    private ServerSocket serverSocket;
    74      /**
    75       * Socket list.
    76       */
    77    private Vector socketList = new Vector();
    78
    79    /**
    80     * The MulticastDNS zone advertised by a SocketReceiver
    81     */
    82    public static final String ZONE = "_log4j_obj_tcpaccept_receiver.local.";
    83
    84      /**
    85       * Listener.
    86       */
    87    private SocketNodeEventListener listener = null;
    88      /**
    89       * Listeners.
    90       */
    91    private List listenerList = Collections.synchronizedList(new ArrayList());
    92    private boolean advertiseViaMulticastDNS;
    93    private ZeroConfSupport zeroConf;
    94
    95      /**
    96       * Create new instance.
    97       */
    98    public SocketReceiver() {
    99          super();
    100   }
    101
    102     /**
    103      * Create new instance.
    104      * @param p port
    105      */
    106   public SocketReceiver(final int p) {
    107     super();
    108     port = p;
    109   }
    110
    111     /**
    112      * Create new instance.
    113      * @param p port
    114      * @param repo logger repository
    115      */
    116   public SocketReceiver(final int p, final LoggerRepository repo) {
    117     super();
    118     this.port = p;
    119     repository = repo;
    120   }
    121
    122     /** {@inheritDoc} */
    123   public int getPort() {
    124     return port;
    125   }
    126
    127   /** {@inheritDoc} */
    128   public void setPort(final int p) {
    129     port = p;
    130   }
    131
    132   /**
    133    * Returns true if the receiver is the same class and they are
    134    * configured for the same properties, and super class also considers
    135    * them to be equivalent. This is used by PluginRegistry when determining
    136    * if the a similarly configured receiver is being started.
    137    *
    138    * @param testPlugin The plugin to test equivalency against.
    139    * @return boolean True if the testPlugin is equivalent to this plugin.
    140    */
    141   public boolean isEquivalent(final Plugin testPlugin) {
    142     if ((testPlugin != null) && testPlugin instanceof SocketReceiver) {
    143       SocketReceiver sReceiver = (SocketReceiver) testPlugin;
    144
    145       return (port == sReceiver.getPort() && super.isEquivalent(testPlugin));
    146     }
    147
    148     return false;
    149   }
    150
    151   /**
    152     Starts the SocketReceiver with the current options. */
    153   public void activateOptions() {
    154     if (!isActive()) {
    155       //      shutdown();
    156       rThread = new Thread(this);
    157       rThread.setDaemon(true);
    158       rThread.start();
    159       if (advertiseViaMulticastDNS) {
    160         zeroConf = new ZeroConfSupport(ZONE, port, getName());
    161         zeroConf.advertise();
    162       }
    163
    164       active = true;
    165     }
    166   }
    167
    168   /**
    169    * Called when the receiver should be stopped. Closes the
    170    * server socket and all of the open sockets.
    171    */
    172   public synchronized void shutdown() {
    173     getLogger().debug(getName() + " received shutdown request");
    174
    175     // mark this as no longer running
    176     active = false;
    177
    178     if (rThread != null) {
    179       rThread.interrupt();
    180       rThread = null;
    181     }
    182     if (advertiseViaMulticastDNS) {
    183         zeroConf.unadvertise();
    184     }
    185
    186     doShutdown();
    187   }
    188
    189   /**
    190    * Does the actual shutting down by closing the server socket
    191    * and any connected sockets that have been created.
    192    */
    193   private synchronized void doShutdown() {
    194     active = false;
    195
    196     getLogger().debug(getName() + " doShutdown called");
    197
    198     // close the server socket
    199     closeServerSocket();
    200
    201     // close all of the accepted sockets
    202     closeAllAcceptedSockets();
    203   }
    204
    205   /**
    206    * Closes the server socket, if created.
    207    */
    208   private void closeServerSocket() {
    209     getLogger().debug("{} closing server socket", getName());
    210
    211     try {
    212       if (serverSocket != null) {
    213         serverSocket.close();
    214       }
    215     } catch (Exception e) {
    216       // ignore for now
    217     }
    218
    219     serverSocket = null;
    220   }
    221
    222   /**
    223    * Closes all the connected sockets in the List.
    224    */
    225   private synchronized void closeAllAcceptedSockets() {
    226     for (int x = 0; x < socketList.size(); x++) {
    227       try {
    228         ((Socket) socketList.get(x)).close();
    229       } catch (Exception e) {
    230         // ignore for now
    231       }
    232     }
    233
    234     // clear member variables
    235     socketMap.clear();
    236     socketList.clear();
    237   }
    238
    239   /**
    240     Sets the flag to indicate if receiver is active or not.
    241    @param b new value
    242    */
    243   protected synchronized void setActive(final boolean b) {
    244     active = b;
    245   }
    246
    247   public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
    248       this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
    249   }
    250
    251   public boolean isAdvertiseViaMulticastDNS() {
    252       return advertiseViaMulticastDNS;
    253   }
    254
    255   /**
    256     Loop, accepting new socket connections. */
    257   public void run() {
    258     /**
    259      * Ensure we start fresh.
    260      */
    261     closeServerSocket();
    262     closeAllAcceptedSockets();
    263
    264     // start the server socket
    265     try {
    266       serverSocket = new ServerSocket(port);
    267     } catch (Exception e) {
    268       getLogger().error(
    269         "error starting SocketReceiver (" + this.getName()
    270         + "), receiver did not start", e);
    271       active = false;
    272
    273       return;
    274     }
    275
    276     Socket socket = null;
    277
    278     try {
    279       getLogger().debug("in run-about to enter while not interrupted loop");
    280
    281       active = true;
    282
    283       while (!rThread.isInterrupted()) {
    284         // if we have a socket, start watching it
    285         if (socket != null) {
    286           getLogger().debug(
    287                   "socket not null - creating and starting socketnode");
    288           socketList.add(socket);
    289
    290           SocketNode13 node = new SocketNode13(socket, this);
    291           synchronized (listenerList) {
    292             for (Iterator iter = listenerList.iterator();
    293                  iter.hasNext();) {
    294                 SocketNodeEventListener l =
    295                         (SocketNodeEventListener) iter.next();
    296                 node.addSocketNodeEventListener(l);
    297             }
    298           }
    299           socketMap.put(socket, node);
    300           new Thread(node).start();
    301           socket = null;
    302         }
    303
    304         getLogger().debug("waiting to accept socket");
    305
    306         // wait for a socket to open, then loop to start it
    307         socket = serverSocket.accept();
    308         getLogger().debug("accepted socket");
    309       }
    310     } catch (Exception e) {
    311       getLogger().warn(
    312         "exception while watching socket server in SocketReceiver ("
    313         + this.getName() + "), stopping");
    314     }
    315
    316     getLogger().debug("{} has exited the not interrupted loop", getName());
    317
    318     // socket not watched because we a no longer running
    319     // so close it now.
    320     if (socket != null) {
    321       try {
    322         socket.close();
    323       } catch (IOException e1) {
    324         getLogger().warn("socket exception caught - socket closed");
    325       }
    326     }
    327
    328     getLogger().debug("{} is exiting main run loop", getName());
    329   }
    330
    331   /**
    332    * Returns a Vector of SocketDetail representing the IP/Domain name
    333    * of the currently connected sockets that this receiver has
    334    * been responsible for creating.
    335    * @return Vector of SocketDetails
    336    */
    337   public Vector getConnectedSocketDetails() {
    338     Vector details = new Vector(socketList.size());
    339
    340     for (Enumeration enumeration = socketList.elements();
    341          enumeration.hasMoreElements();
    342             ) {
    343       Socket socket = (Socket) enumeration.nextElement();
    344       details.add(
    345         new SocketDetail(socket, (SocketNode13) socketMap.get(socket)));
    346     }
    347
    348     return details;
    349   }
    350
    351   /**
    352    * Returns the currently configured SocketNodeEventListener that
    353    * will be automatically set for each SocketNode created.
    354    * @return SocketNodeEventListener currently configured
    355    *
    356    * @deprecated This receiver now supports multiple listeners
    357    */
    358   public SocketNodeEventListener getListener() {
    359     return listener;
    360   }
    361
    362   /**
    363    * Adds the listener to the list of listeners to be notified of the
    364    * respective event.
    365    * @param l the listener to add to the list
    366    */
    367   public void addSocketNodeEventListener(
    368           final SocketNodeEventListener l) {
    369     listenerList.add(l);
    370   }
    371
    372   /**
    373    * Removes the registered Listener from this instances list of
    374    * listeners.  If the listener has not been registered, then invoking
    375    * this method has no effect.
    376    *
    377    * @param l the SocketNodeEventListener to remove
    378    */
    379   public void removeSocketNodeEventListener(
    380           final SocketNodeEventListener l) {
    381     listenerList.remove(l);
    382   }
    383
    384   /**
    385    * Sets the SocketNodeEventListener that will be used for each
    386    * created SocketNode.
    387    * @param l the listener to set on each creation of a SocketNode
    388    * @deprecated This receiver now supports multiple listeners and
    389    * so this method simply removes the listener (if there already)
    390    * and readds it to the list.
    391    *
    392    * The passed listener will also be returned via the getListener()
    393    * method still, but this is also deprecated
    394    */
    395   public void setListener(final SocketNodeEventListener l) {
    396     removeSocketNodeEventListener(l);
    397     addSocketNodeEventListener(l);
    398     this.listener = l;
    399   }
    400
    401     /** {@inheritDoc} */
    402   public boolean isPaused() {
    403     return paused;
    404   }
    405
    406     /** {@inheritDoc} */
    407   public void setPaused(final boolean b) {
    408     paused = b;
    409   }
    410
    411     /**
    412      * Socket detail.
    413      */
    414   private static final class SocketDetail implements AddressBased, PortBased,
    415     Pauseable {
    416       /**
    417        * Address.
    418        */
    419     private String address;
    420       /**
    421        * Port.
    422        */
    423     private int port;
    424       /**
    425        * Socket node.
    426        */
    427     private SocketNode13 socketNode;
    428
    429       /**
    430        * Create new instance.
    431        * @param socket socket
    432        * @param node socket node
    433        */
    434     private SocketDetail(final Socket socket,
    435                          final SocketNode13 node) {
    436       super();
    437       this.address = socket.getInetAddress().getHostName();
    438       this.port = socket.getPort();
    439       this.socketNode = node;
    440     }
    441
    442       /** {@inheritDoc} */
    443     public String getAddress() {
    444       return address;
    445     }
    446
    447       /** {@inheritDoc} */
    448     public int getPort() {
    449       return port;
    450     }
    451
    452       /** {@inheritDoc} */
    453     public String getName() {
    454       return "Socket";
    455     }
    456
    457       /** {@inheritDoc} */
    458     public boolean isActive() {
    459       return true;
    460     }
    461
    462       /** {@inheritDoc} */
    463     public boolean isPaused() {
    464       return socketNode.isPaused();
    465     }
    466
    467       /** {@inheritDoc} */
    468     public void setPaused(final boolean b) {
    469       socketNode.setPaused(b);
    470     }
    471   }
    472     /** {@inheritDoc} */
    473   public void doPost(final LoggingEvent event) {
    474     if (!isPaused()) {
    475       super.doPost(event);
    476     }
    477   }
    478
    479 }