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 <sdeboy@apache.org>
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 }