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 }