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 org.apache.log4j.plugins.Plugin;
21 import org.apache.log4j.plugins.Receiver;
22 import org.apache.log4j.spi.LoggerRepository;
23
24 import java.io.IOException;
25 import java.net.Socket;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Iterator;
29 import java.util.List;
30
31 /***
32 SocketHubReceiver receives a remote logging event on a configured
33 socket and "posts" it to a LoggerRepository as if the event was
34 generated locally. This class is designed to receive events from
35 the SocketHubAppender class (or classes that send compatible events).
36
37 <p>Once the event has been "posted", it will be handled by the
38 appenders currently configured in the LoggerRespository.
39
40 @author Mark Womack
41 @author Ceki Gülcü
42 @author Paul Smith (psmith@apache.org)
43 */
44 public class SocketHubReceiver
45 extends Receiver implements SocketNodeEventListener, PortBased {
46
47 /***
48 * Default reconnection delay.
49 */
50 static final int DEFAULT_RECONNECTION_DELAY = 30000;
51
52 /***
53 * Host.
54 */
55 protected String host;
56
57 /***
58 * Port.
59 */
60 protected int port;
61 /***
62 * Reconnection delay.
63 */
64 protected int reconnectionDelay = DEFAULT_RECONNECTION_DELAY;
65
66 /***
67 * Active.
68 */
69 protected boolean active = false;
70
71 /***
72 * Connector.
73 */
74 protected Connector connector;
75
76 /***
77 * Socket.
78 */
79 protected Socket socket;
80
81 /***
82 * Listener list.
83 */
84 private List listenerList = Collections.synchronizedList(new ArrayList());
85
86 /***
87 * Create new instance.
88 */
89 public SocketHubReceiver() {
90 super();
91 }
92
93 /***
94 * Create new instance.
95 * @param h host
96 * @param p port
97 */
98 public SocketHubReceiver(final String h,
99 final int p) {
100 super();
101 host = h;
102 port = p;
103 }
104
105 /***
106 * Create new instance.
107 * @param h host
108 * @param p port
109 * @param repo logger repository
110 */
111 public SocketHubReceiver(final String h,
112 final int p,
113 final LoggerRepository repo) {
114 super();
115 host = h;
116 port = p;
117 repository = repo;
118 }
119
120 /***
121 * Adds a SocketNodeEventListener to this receiver to be notified
122 * of SocketNode events.
123 * @param l listener
124 */
125 public void addSocketNodeEventListener(final SocketNodeEventListener l) {
126 listenerList.add(l);
127 }
128
129 /***
130 * Removes a specific SocketNodeEventListener from this instance
131 * so that it will no longer be notified of SocketNode events.
132 * @param l listener
133 */
134 public void removeSocketNodeEventListener(
135 final SocketNodeEventListener l) {
136 listenerList.remove(l);
137 }
138
139 /***
140 Get the remote host to connect to for logging events.
141 @return host
142 */
143 public String getHost() {
144 return host;
145 }
146
147 /***
148 * Configures the Host property, this will require activateOptions
149 * to be called for this to take effect.
150 * @param remoteHost address of remote host.
151 */
152 public void setHost(final String remoteHost) {
153 this.host = remoteHost;
154 }
155 /***
156 Set the remote host to connect to for logging events.
157 Equivalent to setHost.
158 @param remoteHost address of remote host.
159 */
160 public void setPort(final String remoteHost) {
161 host = remoteHost;
162 }
163
164 /***
165 Get the remote port to connect to for logging events.
166 @return port
167 */
168 public int getPort() {
169 return port;
170 }
171
172 /***
173 Set the remote port to connect to for logging events.
174 @param p port
175 */
176 public void setPort(final int p) {
177 this.port = p;
178 }
179
180 /***
181 The <b>ReconnectionDelay</b> option takes a positive integer
182 representing the number of milliseconds to wait between each
183 failed connection attempt to the server. The default value of
184 this option is 30000 which corresponds to 30 seconds.
185
186 <p>Setting this option to zero turns off reconnection
187 capability.
188 @param delay milliseconds to wait or zero to not reconnect.
189 */
190 public void setReconnectionDelay(final int delay) {
191 int oldValue = this.reconnectionDelay;
192 this.reconnectionDelay = delay;
193 firePropertyChange("reconnectionDelay", oldValue, this.reconnectionDelay);
194 }
195
196 /***
197 Returns value of the <b>ReconnectionDelay</b> option.
198 @return value of reconnection delay option.
199 */
200 public int getReconnectionDelay() {
201 return reconnectionDelay;
202 }
203
204 /***
205 * Returns true if the receiver is the same class and they are
206 * configured for the same properties, and super class also considers
207 * them to be equivalent. This is used by PluginRegistry when determining
208 * if the a similarly configured receiver is being started.
209 *
210 * @param testPlugin The plugin to test equivalency against.
211 * @return boolean True if the testPlugin is equivalent to this plugin.
212 */
213 public boolean isEquivalent(final Plugin testPlugin) {
214 if (testPlugin != null && testPlugin instanceof SocketHubReceiver) {
215 SocketHubReceiver sReceiver = (SocketHubReceiver) testPlugin;
216
217 return (port == sReceiver.getPort()
218 && host.equals(sReceiver.getHost())
219 && reconnectionDelay == sReceiver.getReconnectionDelay()
220 && super.isEquivalent(testPlugin));
221 }
222 return false;
223 }
224
225 /***
226 Returns true if this receiver is active.
227 @return true if receiver is active
228 */
229 public synchronized boolean isActive() {
230 return active;
231 }
232
233 /***
234 Sets the flag to indicate if receiver is active or not.
235 @param b new value
236 */
237 protected synchronized void setActive(final boolean b) {
238 active = b;
239 }
240
241 /***
242 Starts the SocketReceiver with the current options. */
243 public void activateOptions() {
244 if (!isActive()) {
245 setActive(true);
246 fireConnector(false);
247 }
248 }
249
250 /***
251 Called when the receiver should be stopped. Closes the socket */
252 public synchronized void shutdown() {
253
254 active = false;
255
256
257 try {
258 if (socket != null) {
259 socket.close();
260 }
261 } catch (Exception e) {
262
263 }
264 socket = null;
265
266
267 if (connector != null) {
268 connector.interrupted = true;
269 connector = null;
270 }
271 }
272
273 /***
274 Listen for a socketClosedEvent from the SocketNode. Reopen the
275 socket if this receiver is still active.
276 @param e exception not used.
277 */
278 public void socketClosedEvent(final Exception e) {
279
280
281
282 connector = null;
283 fireConnector(true);
284 }
285
286 /***
287 * Fire connectors.
288 * @param isReconnect true if reconnect.
289 */
290 private synchronized void fireConnector(final boolean isReconnect) {
291 if (active && connector == null) {
292 getLogger().debug("Starting a new connector thread.");
293 connector = new Connector(isReconnect);
294 connector.setDaemon(true);
295 connector.setPriority(Thread.MIN_PRIORITY);
296 connector.start();
297 }
298 }
299
300 /***
301 * Set socket.
302 * @param newSocket new value for socket.
303 */
304 private synchronized void setSocket(final Socket newSocket) {
305 connector = null;
306 socket = newSocket;
307 SocketNode13 node = new SocketNode13(socket, this);
308 node.addSocketNodeEventListener(this);
309
310 synchronized (listenerList) {
311 for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
312 SocketNodeEventListener listener =
313 (SocketNodeEventListener) iter.next();
314 node.addSocketNodeEventListener(listener);
315 }
316 }
317 new Thread(node).start();
318 }
319
320 /***
321 The Connector will reconnect when the server becomes available
322 again. It does this by attempting to open a new connection every
323 <code>reconnectionDelay</code> milliseconds.
324
325 <p>It stops trying whenever a connection is established. It will
326 restart to try reconnect to the server when previpously open
327 connection is droppped.
328
329 @author Ceki Gülcü
330 */
331 private final class Connector extends Thread {
332
333 /***
334 * Interruption status.
335 */
336 boolean interrupted = false;
337 /***
338 * If true, then delay on next iteration.
339 */
340 boolean doDelay;
341
342 /***
343 * Create new instance.
344 * @param isReconnect true if reconnecting.
345 */
346 public Connector(final boolean isReconnect) {
347 super();
348 doDelay = isReconnect;
349 }
350
351 /***
352 * Attempt to connect until interrupted.
353 */
354 public void run() {
355 while (!interrupted) {
356 try {
357 if (doDelay) {
358 getLogger().debug("waiting for " + reconnectionDelay
359 + " milliseconds before reconnecting.");
360 sleep(reconnectionDelay);
361 }
362 doDelay = true;
363 getLogger().debug("Attempting connection to " + host);
364 Socket s = new Socket(host, port);
365 setSocket(s);
366 getLogger().debug(
367 "Connection established. Exiting connector thread.");
368 break;
369 } catch (InterruptedException e) {
370 getLogger().debug("Connector interrupted. Leaving loop.");
371 return;
372 } catch (java.net.ConnectException e) {
373 getLogger().debug("Remote host {} refused connection.", host);
374 } catch (IOException e) {
375 getLogger().debug("Could not connect to {}. Exception is {}.",
376 host, e);
377 }
378 }
379 }
380 }
381
382 /***
383 * This method does nothing.
384 * @param remoteInfo remote info.
385 */
386 public void socketOpened(final String remoteInfo) {
387
388
389 }
390 }