1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
183 closeServerSocket();
184
185
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
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
215 }
216 }
217
218
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
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
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
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
287
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 }