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 }