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 org.apache.log4j.plugins.Pauseable;
21  import org.apache.log4j.plugins.Plugin;
22  import org.apache.log4j.plugins.Receiver;
23  import org.apache.log4j.spi.LoggerRepository;
24  import org.apache.log4j.spi.LoggingEvent;
25  
26  import java.net.ServerSocket;
27  import java.net.Socket;
28  import java.util.List;
29  import java.util.Vector;
30  
31  
32  /**
33   * XMLSocketReceiver receives a remote logging event via XML on a configured
34   * socket and "posts" it to a LoggerRepository as if the event were
35   * generated locally. This class is designed to receive events from
36   * the XMLSocketAppender class (or classes that send compatible events).
37   * <p>
38   * This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging
39   * XMLFormatter (via the org.apache.log4j.spi.Decoder interface).
40   * <p>
41   * By default, log4j's XMLLayout is supported (no need to specify a decoder in that case).
42   * <p>
43   * To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param
44   * of org.apache.log4j.xml.UtilLoggingXMLDecoder.
45   * <p>
46   * Once the event has been "posted", it will be handled by the
47   * appenders currently configured in the LoggerRespository.
48   *
49   * @author Mark Womack
50   * @author Scott Deboy &lt;sdeboy@apache.org&gt;
51   */
52  public class XMLSocketReceiver extends Receiver implements Runnable, PortBased, Pauseable {
53      private boolean paused;
54      //default to log4j xml decoder
55      protected String decoder = "org.apache.log4j.xml.XMLDecoder";
56      private ServerSocket serverSocket;
57      private List<Socket> socketList = new Vector<>();
58      private Thread rThread;
59      public static final int DEFAULT_PORT = 4448;
60      protected int port = DEFAULT_PORT;
61      private boolean advertiseViaMulticastDNS;
62      private ZeroConfSupport zeroConf;
63  
64      /**
65       * The MulticastDNS zone advertised by an XMLSocketReceiver
66       */
67      public static final String ZONE = "_log4j_xml_tcpaccept_receiver.local.";
68  
69      /*
70       * Log4j doesn't provide an XMLSocketAppender, but the MulticastDNS zone that should be advertised by one is:
71       * _log4j_xml_tcpconnect_appender.local.
72       */
73  
74      public XMLSocketReceiver() {
75      }
76  
77      public XMLSocketReceiver(int _port) {
78          port = _port;
79      }
80  
81      public XMLSocketReceiver(int _port, LoggerRepository _repository) {
82          port = _port;
83          repository = _repository;
84      }
85  
86      /**
87       * Get the port to receive logging events on.
88       */
89      public int getPort() {
90          return port;
91      }
92  
93      /**
94       * Set the port to receive logging events on.
95       */
96      public void setPort(int _port) {
97          port = _port;
98      }
99  
100     public String getDecoder() {
101         return decoder;
102     }
103 
104     /**
105      * Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file.
106      */
107     public void setDecoder(String _decoder) {
108         decoder = _decoder;
109     }
110 
111     public boolean isPaused() {
112         return paused;
113     }
114 
115     public void setPaused(boolean b) {
116         paused = b;
117     }
118 
119     /**
120      * Returns true if the receiver is the same class and they are
121      * configured for the same properties, and super class also considers
122      * them to be equivalent. This is used by PluginRegistry when determining
123      * if the a similarly configured receiver is being started.
124      *
125      * @param testPlugin The plugin to test equivalency against.
126      * @return boolean True if the testPlugin is equivalent to this plugin.
127      */
128     public boolean isEquivalent(Plugin testPlugin) {
129         if ((testPlugin != null) && testPlugin instanceof XMLSocketReceiver) {
130             XMLSocketReceiver sReceiver = (XMLSocketReceiver) testPlugin;
131 
132             return (port == sReceiver.getPort() && super.isEquivalent(testPlugin));
133         }
134 
135         return false;
136     }
137 
138     public int hashCode() {
139 
140         int result = 37 * (repository != null ? repository.hashCode() : 0);
141         result = result * 37 + port;
142         return (result * 37 + (getName() != null ? getName().hashCode() : 0));
143     }
144 
145     /**
146      * Sets the flag to indicate if receiver is active or not.
147      *
148      * @param b new value
149      */
150     protected synchronized void setActive(final boolean b) {
151         active = b;
152     }
153 
154     /**
155      * Starts the XMLSocketReceiver with the current options.
156      */
157     public void activateOptions() {
158         if (!isActive()) {
159             rThread = new Thread(this);
160             rThread.setDaemon(true);
161             rThread.start();
162 
163             if (advertiseViaMulticastDNS) {
164                 zeroConf = new ZeroConfSupport(ZONE, port, getName());
165                 zeroConf.advertise();
166             }
167 
168             active = true;
169         }
170     }
171 
172     public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
173         this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
174     }
175 
176     public boolean isAdvertiseViaMulticastDNS() {
177         return advertiseViaMulticastDNS;
178     }
179 
180     /**
181      * Called when the receiver should be stopped. Closes the
182      * server socket and all of the open sockets.
183      */
184     public synchronized void shutdown() {
185         // mark this as no longer running
186         active = false;
187 
188         if (rThread != null) {
189             rThread.interrupt();
190             rThread = null;
191         }
192         doShutdown();
193     }
194 
195     /**
196      * Does the actual shutting down by closing the server socket
197      * and any connected sockets that have been created.
198      */
199     private synchronized void doShutdown() {
200         active = false;
201 
202         getLogger().debug("{} doShutdown called", getName());
203 
204         // close the server socket
205         closeServerSocket();
206 
207         // close all of the accepted sockets
208         closeAllAcceptedSockets();
209 
210         if (advertiseViaMulticastDNS) {
211             zeroConf.unadvertise();
212         }
213     }
214 
215     /**
216      * Closes the server socket, if created.
217      */
218     private void closeServerSocket() {
219         getLogger().debug("{} closing server socket", getName());
220 
221         try {
222             if (serverSocket != null) {
223                 serverSocket.close();
224             }
225         } catch (Exception e) {
226             // ignore for now
227         }
228 
229         serverSocket = null;
230     }
231 
232     /**
233      * Closes all the connected sockets in the List.
234      */
235     private synchronized void closeAllAcceptedSockets() {
236         for (Object aSocketList : socketList) {
237             try {
238                 ((Socket) aSocketList).close();
239             } catch (Exception e) {
240                 // ignore for now
241             }
242         }
243 
244         // clear member variables
245         socketList.clear();
246     }
247 
248     /**
249      * Loop, accepting new socket connections.
250      */
251     public void run() {
252         /**
253          * Ensure we start fresh.
254          */
255         getLogger().debug("performing socket cleanup prior to entering loop for {}", name);
256         closeServerSocket();
257         closeAllAcceptedSockets();
258         getLogger().debug("socket cleanup complete for {}", name);
259         active = true;
260 
261         // start the server socket
262         try {
263             serverSocket = new ServerSocket(port);
264         } catch (Exception e) {
265             getLogger().error(
266                 "error starting XMLSocketReceiver (" + this.getName()
267                     + "), receiver did not start", e);
268             active = false;
269             doShutdown();
270 
271             return;
272         }
273 
274         Socket socket = null;
275 
276         try {
277             getLogger().debug("in run-about to enter while isactiveloop");
278 
279             active = true;
280 
281             while (!rThread.isInterrupted()) {
282                 // if we have a socket, start watching it
283                 if (socket != null) {
284                     getLogger().debug("socket not null - creating and starting socketnode");
285                     socketList.add(socket);
286 
287                     XMLSocketNode node = new XMLSocketNode(decoder, socket, this);
288                     node.setLoggerRepository(this.repository);
289                     new Thread(node).start();
290                 }
291 
292                 getLogger().debug("waiting to accept socket");
293 
294                 // wait for a socket to open, then loop to start it
295                 socket = serverSocket.accept();
296                 getLogger().debug("accepted socket");
297             }
298 
299             // socket not watched because we a no longer running
300             // so close it now.
301             if (socket != null) {
302                 socket.close();
303             }
304         } catch (Exception e) {
305             getLogger().warn(
306                 "socket server disconnected, stopping");
307         }
308     }
309 
310     /* (non-Javadoc)
311      * @see org.apache.log4j.plugins.Receiver#doPost(org.apache.log4j.spi.LoggingEvent)
312      */
313     public void doPost(LoggingEvent event) {
314         if (!isPaused()) {
315             super.doPost(event);
316         }
317     }
318 
319 
320 }