1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.james.socket;
23
24 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
25 import org.apache.avalon.excalibur.pool.Poolable;
26 import org.apache.avalon.framework.container.ContainerUtil;
27 import org.apache.avalon.framework.logger.AbstractLogEnabled;
28 import org.apache.avalon.framework.logger.Logger;
29 import org.apache.avalon.framework.service.ServiceException;
30 import org.apache.avalon.framework.service.ServiceManager;
31 import org.apache.avalon.framework.service.Serviceable;
32 import org.apache.james.api.dnsservice.DNSService;
33 import org.apache.james.util.InternetPrintWriter;
34 import org.apache.james.util.watchdog.Watchdog;
35 import org.apache.james.util.watchdog.WatchdogTarget;
36
37 import java.io.BufferedInputStream;
38 import java.io.BufferedOutputStream;
39 import java.io.File;
40 import java.io.FileOutputStream;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.InterruptedIOException;
44 import java.io.OutputStream;
45 import java.io.PrintWriter;
46 import java.net.Socket;
47 import java.net.SocketException;
48
49
50
51
52 public abstract class AbstractJamesHandler extends AbstractLogEnabled implements ConnectionHandler, Poolable,Serviceable {
53
54
55 private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 1024;
56
57 private static final int DEFAULT_INPUT_BUFFER_SIZE = 1024;
58
59
60 private static final String DEFAULT_NAME = "Handler-ANON";
61
62
63
64
65 private Thread handlerThread;
66
67
68
69
70
71 protected Socket socket;
72
73
74
75
76 protected PrintWriter out;
77
78
79
80
81 protected InputStream in;
82
83
84
85
86 protected CRLFTerminatedReader inReader;
87
88
89
90
91 protected OutputStream outs;
92
93
94
95
96 protected Watchdog theWatchdog;
97
98
99
100
101 private WatchdogTarget theWatchdogTarget = new JamesWatchdogTarget();
102
103
104
105
106
107
108
109 public abstract void setConfigurationData(Object theData);
110
111
112
113
114
115 protected String remoteHost = null;
116
117
118
119
120 protected String remoteIP = null;
121
122
123
124
125 protected DNSService dnsServer = null;
126
127
128
129
130 private String tcplogprefix = null;
131
132
133
134
135
136 private String name = DEFAULT_NAME;
137
138
139
140
141
142 public void service(ServiceManager arg0) throws ServiceException {
143 setDnsServer((DNSService) arg0.lookup(DNSService.ROLE));
144 }
145
146
147
148
149
150
151
152
153 protected void initHandler( Socket connection ) throws IOException {
154 this.socket = connection;
155 remoteIP = socket.getInetAddress().getHostAddress();
156 remoteHost = dnsServer.getHostName(socket.getInetAddress());
157
158 try {
159 synchronized (this) {
160 handlerThread = Thread.currentThread();
161 }
162 in = new BufferedInputStream(socket.getInputStream(), DEFAULT_INPUT_BUFFER_SIZE);
163 outs = new BufferedOutputStream(socket.getOutputStream(), DEFAULT_OUTPUT_BUFFER_SIZE);
164
165 if (tcplogprefix != null) {
166 outs = new SplitOutputStream(outs, new FileOutputStream(tcplogprefix+"out"));
167 in = new CopyInputStream(in, new FileOutputStream(tcplogprefix+"in"));
168 }
169
170
171
172
173 inReader = new CRLFTerminatedReader(in, "ASCII");
174
175 out = new InternetPrintWriter(outs, true);
176 } catch (RuntimeException e) {
177 StringBuffer exceptionBuffer =
178 new StringBuffer(256)
179 .append("[" + toString() + "] Unexpected exception opening from ")
180 .append(remoteHost)
181 .append(" (")
182 .append(remoteIP)
183 .append("): ")
184 .append(e.getMessage());
185 String exceptionString = exceptionBuffer.toString();
186 getLogger().error(exceptionString, e);
187 throw e;
188 } catch (IOException e) {
189 StringBuffer exceptionBuffer =
190 new StringBuffer(256)
191 .append("[" + toString() + "] Cannot open connection from ")
192 .append(remoteHost)
193 .append(" (")
194 .append(remoteIP)
195 .append("): ")
196 .append(e.getMessage());
197 String exceptionString = exceptionBuffer.toString();
198 getLogger().error(exceptionString, e);
199 throw e;
200 }
201
202 if (getLogger().isInfoEnabled()) {
203 StringBuffer infoBuffer =
204 new StringBuffer(128)
205 .append("[" + toString() + "]Connection from ")
206 .append(remoteHost)
207 .append(" (")
208 .append(remoteIP)
209 .append(")");
210 getLogger().info(infoBuffer.toString());
211 }
212 }
213
214
215
216
217 private void cleanHandler() {
218
219 if (theWatchdog != null) {
220 ContainerUtil.dispose(theWatchdog);
221 theWatchdog = null;
222 }
223
224
225 try {
226 if (inReader != null) {
227 inReader.close();
228 }
229 } catch (IOException ioe) {
230 getLogger().warn("[" + toString() + "] Unexpected exception occurred while closing reader: " + ioe);
231 } finally {
232 inReader = null;
233 }
234
235 in = null;
236
237 if (out != null) {
238 out.close();
239 out = null;
240 }
241 outs = null;
242
243 try {
244 if (socket != null) {
245 socket.close();
246 }
247 } catch (IOException ioe) {
248 getLogger().warn("[" + toString() + "] Unexpected exception occurred while closing socket: " + ioe);
249 } finally {
250 socket = null;
251 }
252
253 remoteIP = null;
254 remoteHost = null;
255
256 synchronized (this) {
257 handlerThread = null;
258 }
259 }
260
261
262
263
264 public void handleConnection(Socket connection) throws IOException {
265 initHandler(connection);
266
267 final Logger logger = getLogger();
268 try {
269
270
271 handleProtocol();
272
273 logger.debug("[" + toString() + "] Closing socket");
274 } catch (SocketException se) {
275
276 if (logger.isWarnEnabled()) {
277 String message =
278 new StringBuffer(64)
279 .append("[" + toString() + "]Socket to ")
280 .append(remoteHost)
281 .append(" (")
282 .append(remoteIP)
283 .append("): ")
284 .append(se.getMessage()).toString();
285 logger.warn(message);
286 logger.debug(se.getMessage(), se);
287 }
288 } catch ( InterruptedIOException iioe ) {
289 if (logger.isErrorEnabled()) {
290 StringBuffer errorBuffer =
291 new StringBuffer(64)
292 .append("[" + toString() + "] Socket to ")
293 .append(remoteHost)
294 .append(" (")
295 .append(remoteIP)
296 .append(") timeout.");
297 logger.error( errorBuffer.toString(), iioe );
298 }
299 } catch ( IOException ioe ) {
300 if (logger.isWarnEnabled()) {
301 String message =
302 new StringBuffer(256)
303 .append("[" + toString() + "] Exception handling socket to ")
304 .append(remoteHost)
305 .append(" (")
306 .append(remoteIP)
307 .append(") : ")
308 .append(ioe.getMessage()).toString();
309 logger.warn(message);
310 logger.debug( ioe.getMessage(), ioe );
311 }
312 } catch (RuntimeException e) {
313 errorHandler(e);
314 } finally {
315
316 cleanHandler();
317 resetHandler();
318 }
319 }
320
321
322
323
324
325
326 protected void errorHandler(RuntimeException e) {
327 if (getLogger().isErrorEnabled()) {
328 getLogger().error( "[" + toString() + "] Unexpected runtime exception: "
329 + e.getMessage(), e );
330 }
331 }
332
333
334
335
336
337
338
339 protected abstract void handleProtocol() throws IOException;
340
341
342
343
344 protected abstract void resetHandler();
345
346
347
348
349
350
351
352 public void setWatchdog(Watchdog theWatchdog) {
353 this.theWatchdog = theWatchdog;
354 }
355
356
357
358
359
360
361
362 WatchdogTarget getWatchdogTarget() {
363 return theWatchdogTarget;
364 }
365
366
367
368
369 void idleClose() {
370 if (getLogger() != null) {
371 getLogger().error("[" + toString() + "] Service Connection has idled out.");
372 }
373 try {
374 if (socket != null) {
375 socket.close();
376 }
377 } catch (Exception e) {
378
379 } finally {
380 socket = null;
381 }
382
383 synchronized (this) {
384
385 if (handlerThread != null) {
386 handlerThread.interrupt();
387 handlerThread = null;
388 }
389 }
390
391 }
392
393
394
395
396
397
398
399
400
401 private final void logResponseString(String responseString) {
402 if (getLogger().isDebugEnabled()) {
403 getLogger().debug("[" + toString() + "] Sent: " + responseString);
404 }
405 }
406
407
408
409
410
411
412
413
414 public final void writeLoggedFlushedResponse(String responseString) {
415 out.println(responseString);
416 out.flush();
417 logResponseString(responseString);
418 }
419
420
421
422
423
424
425
426 public final void writeLoggedResponse(String responseString) {
427 out.println(responseString);
428 logResponseString(responseString);
429 }
430
431
432
433
434
435
436 private class JamesWatchdogTarget
437 implements WatchdogTarget {
438
439
440
441
442 public void execute() {
443 AbstractJamesHandler.this.idleClose();
444 }
445
446
447
448
449 @Override
450 public String toString() {
451 return AbstractJamesHandler.this.toString();
452 }
453 }
454
455
456
457
458
459
460 public void setStreamDumpDir(String streamDumpDir) {
461 if (streamDumpDir != null) {
462 String streamdumpDir=streamDumpDir;
463 this.tcplogprefix = streamdumpDir+"/" + getName() + "_TCP-DUMP."+System.currentTimeMillis()+".";
464 File logdir = new File(streamdumpDir);
465 if (!logdir.exists()) {
466 logdir.mkdir();
467 }
468 } else {
469 this.tcplogprefix = null;
470 }
471 }
472
473 public void setDnsServer(DNSService dnsServer) {
474 this.dnsServer = dnsServer;
475 }
476
477
478
479
480
481
482 public final String getName() {
483 return name;
484 }
485
486
487
488
489
490
491
492 public final void setName(final String name) {
493 if (name == null) {
494 this.name = DEFAULT_NAME;
495 } else {
496 this.name = name;
497 }
498 }
499
500
501
502
503
504 @Override
505 public String toString() {
506 return name;
507 }
508 }