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.BufferedInputStream;
    21  import java.io.IOException;
    22  import java.io.ObjectInputStream;
    23  import java.net.Socket;
    24  import java.util.ArrayList;
    25  import java.util.Collections;
    26  import java.util.Iterator;
    27  import java.util.List;
    28
    29  import org.apache.log4j.Logger;
    30  import org.apache.log4j.helpers.Constants;
    31  import org.apache.log4j.plugins.Pauseable;
    32  import org.apache.log4j.plugins.Receiver;
    33  import org.apache.log4j.spi.ComponentBase;
    34  import org.apache.log4j.spi.LoggerRepository;
    35  import org.apache.log4j.spi.LoggingEvent;
    36
    37
    38  // Contributors:  Moses Hohman <mmhohman@rainbow.uchicago.edu>
    39
    40  /**
    41     Read {@link LoggingEvent} objects sent from a remote client using
    42     Sockets (TCP). These logging events are logged according to local
    43     policy, as if they were generated locally.
    44
    45     <p>For example, the socket node might decide to log events to a
    46     local file and also resent them to a second socket node.
    47
    48      Implementation lifted from org.apache.log4j.net.SocketNode
    49      in log4j 1.3 and renamed to prevent collision with
    50      log4j 1.2 implementation.
    51
    52      @author  Ceki G&uuml;lc&uuml;
    53      @author  Paul Smith (psmith@apache.org)
    54
    55
    56  */
    57  public class SocketNode13 extends ComponentBase implements Runnable, Pauseable {
    58
    59      /**
    60       * Paused state.
    61       */
    62    private boolean paused;
    63      /**
    64       * Closed state.
    65       */
    66    private boolean closed;
    67      /**
    68       * Socket.
    69       */
    70    private Socket socket;
    71      /**
    72       * Receiver.
    73       */
    74    private Receiver receiver;
    75      /**
    76       * List of listeners.
    77       */
    78    private List listenerList = Collections.synchronizedList(new ArrayList());
    79
    80
    81
    82    /**
    83      Constructor for socket and logger repository.
    84     @param s socket
    85     @param hierarchy logger repository
    86     */
    87    public SocketNode13(final Socket s,
    88                      final LoggerRepository hierarchy) {
    89      super();
    90      this.socket = s;
    91      this.repository = hierarchy;
    92    }
    93
    94    /**
    95      Constructor for socket and receiver.
    96     @param s socket
    97     @param r receiver
    98     */
    99    public SocketNode13(final Socket s, final Receiver r) {
    100     super();
    101     this.socket = s;
    102     this.receiver = r;
    103   }
    104
    105   /**
    106    * Set the event listener on this node.
    107    *
    108    * @deprecated Now supports mutliple listeners, this method
    109    * simply invokes the removeSocketNodeEventListener() to remove
    110    * the listener, and then readds it.
    111    * @param l listener
    112    */
    113   public void setListener(final SocketNodeEventListener l) {
    114     removeSocketNodeEventListener(l);
    115     addSocketNodeEventListener(l);
    116   }
    117
    118   /**
    119    * Adds the listener to the list of listeners to be notified of the
    120    * respective event.
    121    * @param listener the listener to add to the list
    122    */
    123   public void addSocketNodeEventListener(
    124           final SocketNodeEventListener listener) {
    125     listenerList.add(listener);
    126   }
    127
    128   /**
    129    * Removes the registered Listener from this instances list of
    130    * listeners.  If the listener has not been registered, then invoking
    131    * this method has no effect.
    132    *
    133    * @param listener the SocketNodeEventListener to remove
    134    */
    135   public void removeSocketNodeEventListener(
    136           final SocketNodeEventListener listener) {
    137     listenerList.remove(listener);
    138   }
    139
    140
    141     /**
    142      * Deserialize events from socket until interrupted.
    143      */
    144   public void run() {
    145     LoggingEvent event;
    146     Logger remoteLogger;
    147     Exception listenerException = null;
    148     ObjectInputStream ois = null;
    149
    150     try {
    151       ois =
    152         new ObjectInputStream(
    153           new BufferedInputStream(socket.getInputStream()));
    154     } catch (Exception e) {
    155       ois = null;
    156       listenerException = e;
    157       getLogger().error("Exception opening ObjectInputStream to " + socket, e);
    158     }
    159
    160     if (ois != null) {
    161
    162       String hostName = socket.getInetAddress().getHostName();
    163       String remoteInfo = hostName + ":" + socket.getPort();
    164
    165       /**
    166        * notify the listener that the socket has been
    167        * opened and this SocketNode is ready and waiting
    168        */
    169       fireSocketOpened(remoteInfo);
    170
    171       try {
    172         while (!isClosed()) {
    173           // read an event from the wire
    174           event = (LoggingEvent) ois.readObject();
    175           event.setProperty(Constants.HOSTNAME_KEY, hostName);
    176           // store the known remote info in an event property
    177           event.setProperty("log4j.remoteSourceInfo", remoteInfo);
    178
    179           // if configured with a receiver, tell it to post the event
    180           if (!isPaused() && !isClosed()) {
    181             if ((receiver != null)) {
    182               receiver.doPost(event);
    183
    184               // else post it via the hierarchy
    185             } else {
    186               // get a logger from the hierarchy. The name of the logger
    187               // is taken to be the name contained in the event.
    188               remoteLogger = repository.getLogger(event.getLoggerName());
    189
    190               //event.logger = remoteLogger;
    191               // apply the logger-level filter
    192               if (event
    193                 .getLevel()
    194                 .isGreaterOrEqual(remoteLogger.getEffectiveLevel())) {
    195                 // finally log the event as if was generated locally
    196                 remoteLogger.callAppenders(event);
    197               }
    198             }
    199           } else {
    200             //we simply discard this event.
    201           }
    202         }
    203       } catch (java.io.EOFException e) {
    204         getLogger().info("Caught java.io.EOFException closing connection.");
    205         listenerException = e;
    206       } catch (java.net.SocketException e) {
    207         getLogger().info("Caught java.net.SocketException closing connection.");
    208         listenerException = e;
    209       } catch (IOException e) {
    210         getLogger().info("Caught java.io.IOException: " + e);
    211         getLogger().info("Closing connection.");
    212         listenerException = e;
    213       } catch (Exception e) {
    214         getLogger().error("Unexpected exception. Closing connection.", e);
    215         listenerException = e;
    216       }
    217     }
    218
    219     // close the socket
    220     try {
    221       if (ois != null) {
    222         ois.close();
    223       }
    224     } catch (Exception e) {
    225       //getLogger().info("Could not close connection.", e);
    226     }
    227
    228     // send event to listener, if configured
    229     if (listenerList.size() > 0 && !isClosed()) {
    230       fireSocketClosedEvent(listenerException);
    231     }
    232   }
    233
    234   /**
    235    * Notifies all registered listeners regarding the closing of the Socket.
    236    * @param listenerException listener exception
    237    */
    238   private void fireSocketClosedEvent(final Exception listenerException) {
    239     synchronized (listenerList) {
    240         for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
    241             SocketNodeEventListener snel =
    242                     (SocketNodeEventListener) iter.next();
    243             if (snel != null) {
    244                 snel.socketClosedEvent(listenerException);
    245             }
    246         }
    247     }
    248   }
    249
    250   /**
    251    * Notifies all registered listeners regarding the opening of a Socket.
    252    * @param remoteInfo remote info
    253    */
    254   private void fireSocketOpened(final String remoteInfo) {
    255     synchronized (listenerList) {
    256         for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
    257             SocketNodeEventListener snel =
    258                     (SocketNodeEventListener) iter.next();
    259             if (snel != null) {
    260                 snel.socketOpened(remoteInfo);
    261             }
    262         }
    263     }
    264   }
    265
    266     /**
    267      * Sets if node is paused.
    268      * @param b new value
    269      */
    270   public void setPaused(final boolean b) {
    271     this.paused = b;
    272   }
    273
    274     /**
    275      * Get if node is paused.
    276      * @return true if pause.
    277      */
    278   public boolean isPaused() {
    279     return this.paused;
    280   }
    281
    282     /**
    283      * Close the node and underlying socket
    284      */
    285   public void close() throws IOException {
    286     getLogger().debug("closing socket");
    287     this.closed = true;
    288     socket.close();
    289     fireSocketClosedEvent(null);
    290   }
    291
    292     /**
    293      * Get if node is closed.
    294      * @return true if closed.
    295      */
    296   public boolean isClosed() {
    297     return this.closed;
    298   }
    299 }