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