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       * Listener.
80       */
81    private SocketNodeEventListener listener = null;
82      /***
83       * Listeners.
84       */
85    private List listenerList = Collections.synchronizedList(new ArrayList());
86  
87      /***
88       * Create new instance.
89       */
90    public SocketReceiver() {
91          super();
92    }
93  
94      /***
95       * Create new instance.
96       * @param p port
97       */
98    public SocketReceiver(final int p) {
99      super();
100     port = p;
101   }
102 
103     /***
104      * Create new instance.
105      * @param p port
106      * @param repo logger repository
107      */
108   public SocketReceiver(final int p, final LoggerRepository repo) {
109     super();
110     this.port = p;
111     repository = repo;
112   }
113 
114     /*** {@inheritDoc} */
115   public int getPort() {
116     return port;
117   }
118 
119   /*** {@inheritDoc} */
120   public void setPort(final int p) {
121     port = p;
122   }
123 
124   /***
125    * Returns true if the receiver is the same class and they are
126    * configured for the same properties, and super class also considers
127    * them to be equivalent. This is used by PluginRegistry when determining
128    * if the a similarly configured receiver is being started.
129    *
130    * @param testPlugin The plugin to test equivalency against.
131    * @return boolean True if the testPlugin is equivalent to this plugin.
132    */
133   public boolean isEquivalent(final Plugin testPlugin) {
134     if ((testPlugin != null) && testPlugin instanceof SocketReceiver) {
135       SocketReceiver sReceiver = (SocketReceiver) testPlugin;
136 
137       return (port == sReceiver.getPort() && super.isEquivalent(testPlugin));
138     }
139 
140     return false;
141   }
142 
143   /***
144     Starts the SocketReceiver with the current options. */
145   public void activateOptions() {
146     if (!isActive()) {
147       //      shutdown();
148       rThread = new Thread(this);
149       rThread.setDaemon(true);
150       rThread.start();
151       active = true;
152     }
153   }
154 
155   /***
156    * Called when the receiver should be stopped. Closes the
157    * server socket and all of the open sockets.
158    */
159   public synchronized void shutdown() {
160     getLogger().debug(getName() + " received shutdown request");
161 
162     // mark this as no longer running
163     active = false;
164 
165     if (rThread != null) {
166       rThread.interrupt();
167       rThread = null;
168     }
169 
170     doShutdown();
171   }
172 
173   /***
174    * Does the actual shutting down by closing the server socket
175    * and any connected sockets that have been created.
176    */
177   private synchronized void doShutdown() {
178     active = false;
179 
180     getLogger().debug(getName() + " doShutdown called");
181 
182     // close the server socket
183     closeServerSocket();
184 
185     // close all of the accepted sockets
186     closeAllAcceptedSockets();
187   }
188 
189   /***
190    * Closes the server socket, if created.
191    */
192   private void closeServerSocket() {
193     getLogger().debug("{} closing server socket", getName());
194 
195     try {
196       if (serverSocket != null) {
197         serverSocket.close();
198       }
199     } catch (Exception e) {
200       // ignore for now
201     }
202 
203     serverSocket = null;
204   }
205 
206   /***
207    * Closes all the connected sockets in the List.
208    */
209   private synchronized void closeAllAcceptedSockets() {
210     for (int x = 0; x < socketList.size(); x++) {
211       try {
212         ((Socket) socketList.get(x)).close();
213       } catch (Exception e) {
214         // ignore for now
215       }
216     }
217 
218     // clear member variables
219     socketMap.clear();
220     socketList.clear();
221   }
222 
223   /***
224     Loop, accepting new socket connections. */
225   public void run() {
226     /***
227      * Ensure we start fresh.
228      */
229     closeServerSocket();
230     closeAllAcceptedSockets();
231 
232     // start the server socket
233     try {
234       serverSocket = new ServerSocket(port);
235     } catch (Exception e) {
236       getLogger().error(
237         "error starting SocketReceiver (" + this.getName()
238         + "), receiver did not start", e);
239       active = false;
240 
241       return;
242     }
243 
244     Socket socket = null;
245 
246     try {
247       getLogger().debug("in run-about to enter while not interrupted loop");
248 
249       active = true;
250 
251       while (!rThread.isInterrupted()) {
252         // if we have a socket, start watching it
253         if (socket != null) {
254           getLogger().debug(
255                   "socket not null - creating and starting socketnode");
256           socketList.add(socket);
257 
258           SocketNode13 node = new SocketNode13(socket, this);
259           synchronized (listenerList) {
260             for (Iterator iter = listenerList.iterator();
261                  iter.hasNext();) {
262                 SocketNodeEventListener l =
263                         (SocketNodeEventListener) iter.next();
264                 node.addSocketNodeEventListener(l);
265             }
266           }
267           socketMap.put(socket, node);
268           new Thread(node).start();
269           socket = null;
270         }
271 
272         getLogger().debug("waiting to accept socket");
273 
274         // wait for a socket to open, then loop to start it
275         socket = serverSocket.accept();
276         getLogger().debug("accepted socket");
277       }
278     } catch (Exception e) {
279       getLogger().warn(
280         "exception while watching socket server in SocketReceiver ("
281         + this.getName() + "), stopping");
282     }
283 
284     getLogger().debug("{} has exited the not interrupted loop", getName());
285 
286     // socket not watched because we a no longer running
287     // so close it now.
288     if (socket != null) {
289       try {
290         socket.close();
291       } catch (IOException e1) {
292         getLogger().warn("socket exception caught - socket closed");
293       }
294     }
295 
296     getLogger().debug("{} is exiting main run loop", getName());
297   }
298 
299   /***
300    * Returns a Vector of SocketDetail representing the IP/Domain name
301    * of the currently connected sockets that this receiver has
302    * been responsible for creating.
303    * @return Vector of SocketDetails
304    */
305   public Vector getConnectedSocketDetails() {
306     Vector details = new Vector(socketList.size());
307 
308     for (Enumeration enumeration = socketList.elements();
309          enumeration.hasMoreElements();
310             ) {
311       Socket socket = (Socket) enumeration.nextElement();
312       details.add(
313         new SocketDetail(socket, (SocketNode13) socketMap.get(socket)));
314     }
315 
316     return details;
317   }
318 
319   /***
320    * Returns the currently configured SocketNodeEventListener that
321    * will be automatically set for each SocketNode created.
322    * @return SocketNodeEventListener currently configured
323    *
324    * @deprecated This receiver now supports multiple listeners
325    */
326   public SocketNodeEventListener getListener() {
327     return listener;
328   }
329 
330   /***
331    * Adds the listener to the list of listeners to be notified of the
332    * respective event.
333    * @param l the listener to add to the list
334    */
335   public void addSocketNodeEventListener(
336           final SocketNodeEventListener l) {
337     listenerList.add(l);
338   }
339 
340   /***
341    * Removes the registered Listener from this instances list of
342    * listeners.  If the listener has not been registered, then invoking
343    * this method has no effect.
344    *
345    * @param l the SocketNodeEventListener to remove
346    */
347   public void removeSocketNodeEventListener(
348           final SocketNodeEventListener l) {
349     listenerList.remove(l);
350   }
351 
352   /***
353    * Sets the SocketNodeEventListener that will be used for each
354    * created SocketNode.
355    * @param l the listener to set on each creation of a SocketNode
356    * @deprecated This receiver now supports multiple listeners and
357    * so this method simply removes the listener (if there already)
358    * and readds it to the list.
359    *
360    * The passed listener will also be returned via the getListener()
361    * method still, but this is also deprecated
362    */
363   public void setListener(final SocketNodeEventListener l) {
364     removeSocketNodeEventListener(l);
365     addSocketNodeEventListener(l);
366     this.listener = l;
367   }
368 
369     /*** {@inheritDoc} */
370   public boolean isPaused() {
371     return paused;
372   }
373 
374     /*** {@inheritDoc} */
375   public void setPaused(final boolean b) {
376     paused = b;
377   }
378 
379     /***
380      * Socket detail.
381      */
382   private static final class SocketDetail implements AddressBased, PortBased,
383     Pauseable {
384       /***
385        * Address.
386        */
387     private String address;
388       /***
389        * Port.
390        */
391     private int port;
392       /***
393        * Socket node.
394        */
395     private SocketNode13 socketNode;
396 
397       /***
398        * Create new instance.
399        * @param socket socket
400        * @param node socket node
401        */
402     private SocketDetail(final Socket socket,
403                          final SocketNode13 node) {
404       super();
405       this.address = socket.getInetAddress().getHostName();
406       this.port = socket.getPort();
407       this.socketNode = node;
408     }
409 
410       /*** {@inheritDoc} */
411     public String getAddress() {
412       return address;
413     }
414 
415       /*** {@inheritDoc} */
416     public int getPort() {
417       return port;
418     }
419 
420       /*** {@inheritDoc} */
421     public String getName() {
422       return "Socket";
423     }
424 
425       /*** {@inheritDoc} */
426     public boolean isActive() {
427       return true;
428     }
429 
430       /*** {@inheritDoc} */
431     public boolean isPaused() {
432       return socketNode.isPaused();
433     }
434 
435       /*** {@inheritDoc} */
436     public void setPaused(final boolean b) {
437       socketNode.setPaused(b);
438     }
439   }
440     /*** {@inheritDoc} */
441   public void doPost(final LoggingEvent event) {
442     if (!isPaused()) {
443       super.doPost(event);
444     }
445   }
446 
447 }