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.transport;
23
24 import org.apache.avalon.framework.activity.Disposable;
25 import org.apache.avalon.framework.configuration.Configurable;
26 import org.apache.avalon.framework.configuration.Configuration;
27 import org.apache.avalon.framework.configuration.ConfigurationException;
28 import org.apache.avalon.framework.container.ContainerUtil;
29 import org.apache.avalon.framework.logger.AbstractLogEnabled;
30 import org.apache.avalon.framework.service.ServiceException;
31 import org.apache.avalon.framework.service.ServiceManager;
32 import org.apache.avalon.framework.service.Serviceable;
33 import org.apache.james.core.MailImpl;
34 import org.apache.james.services.SpoolRepository;
35 import org.apache.mailet.base.MatcherInverter;
36 import org.apache.mailet.base.GenericMailet;
37 import org.apache.mailet.base.GenericMatcher;
38 import org.apache.mailet.Mail;
39 import org.apache.mailet.MailAddress;
40 import org.apache.mailet.Mailet;
41 import org.apache.mailet.MailetConfig;
42 import org.apache.mailet.MailetException;
43 import org.apache.mailet.Matcher;
44 import org.apache.mailet.MatcherConfig;
45
46 import javax.mail.MessagingException;
47
48 import java.io.PrintWriter;
49 import java.io.StringWriter;
50 import java.util.ArrayList;
51 import java.util.Collection;
52 import java.util.Iterator;
53 import java.util.LinkedList;
54 import java.util.List;
55 import java.util.Locale;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public class LinearProcessor
91 extends AbstractLogEnabled
92 implements Disposable, Configurable, Serviceable, MailProcessor, MailetContainer {
93
94
95
96
97
98
99
100
101 private static final String TERMINATING_MATCHER_NAME = "Terminating%Matcher%Name";
102
103
104
105
106
107
108
109
110 private static final String TERMINATING_MAILET_NAME = "Terminating%Mailet%Name";
111
112 private List mailets;
113 private List matchers;
114 private volatile boolean listsClosed;
115 private SpoolRepository spool;
116
117 private MailetLoader mailetLoader;
118
119 private MatcherLoader matchLoader;
120
121
122
123
124
125
126
127
128 public void setSpool(SpoolRepository spool) {
129 if (spool == null) {
130 throw new IllegalArgumentException("The spool cannot be null");
131 }
132 this.spool = spool;
133 }
134
135
136
137
138
139
140 public void setMailetLoader(MailetLoader mailetLoader) {
141 this.mailetLoader = mailetLoader;
142 }
143
144
145
146
147
148
149 public void setMatchLoader(MatcherLoader matchLoader) {
150 this.matchLoader = matchLoader;
151 }
152
153
154
155
156
157
158
159
160
161
162
163 public void dispose() {
164 Iterator it = mailets.iterator();
165 boolean debugEnabled = getLogger().isDebugEnabled();
166 while (it.hasNext()) {
167 Mailet mailet = (Mailet)it.next();
168 if (debugEnabled) {
169 getLogger().debug("Shutdown mailet " + mailet.getMailetInfo());
170 }
171 mailet.destroy();
172 }
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 public synchronized void add(Matcher matcher, Mailet mailet) {
199 if (matcher == null) {
200 throw new IllegalArgumentException("Null valued matcher passed to LinearProcessor.");
201 }
202 if (mailet == null) {
203 throw new IllegalArgumentException("Null valued mailet passed to LinearProcessor.");
204 }
205 if (listsClosed) {
206 throw new IllegalStateException("Attempt to add matcher/mailet after lists have been closed");
207 }
208 matchers.add(matcher);
209 mailets.add(mailet);
210 }
211
212
213
214
215
216
217
218
219
220 public synchronized void closeProcessorLists() {
221 if (listsClosed) {
222 throw new IllegalStateException("Processor's matcher/mailet lists have already been closed.");
223 }
224 Matcher terminatingMatcher =
225 new GenericMatcher() {
226 public Collection match(Mail mail) {
227 return mail.getRecipients();
228 }
229
230 public String getMatcherInfo() {
231 return TERMINATING_MATCHER_NAME;
232 }
233 };
234 Mailet terminatingMailet =
235 new GenericMailet() {
236 public void service(Mail mail) {
237 if (!(Mail.ERROR.equals(mail.getState()))) {
238
239
240
241
242 StringBuffer warnBuffer = new StringBuffer(256)
243 .append("Message ")
244 .append(mail.getName())
245 .append(" reached the end of this processor, and is automatically deleted. This may indicate a configuration error.");
246 LinearProcessor.this.getLogger().warn(warnBuffer.toString());
247 }
248 mail.setState(Mail.GHOST);
249 }
250
251 public String getMailetInfo() {
252 return getMailetName();
253 }
254
255 public String getMailetName() {
256 return TERMINATING_MAILET_NAME;
257 }
258 };
259 add(terminatingMatcher, terminatingMailet);
260 listsClosed = true;
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283 public void service(Mail mail) throws MessagingException {
284 if (spool == null) {
285 throw new IllegalStateException("Attempt to service mail before the spool has been set to a non-null value");
286 }
287
288 if (!listsClosed) {
289 throw new IllegalStateException("Attempt to service mail before matcher/mailet lists have been closed");
290 }
291
292 if (getLogger().isDebugEnabled()) {
293 getLogger().debug("Servicing mail: " + mail.getName());
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308 List[] unprocessed = new List[matchers.size() + 1];
309
310 for (int i = 0; i < unprocessed.length; i++) {
311
312
313 unprocessed[i] = new LinkedList();
314 }
315
316
317 unprocessed[0].add(mail);
318
319
320 String originalState = mail.getState();
321
322
323
324
325 Mail originalMail = mail;
326
327
328 mail = null;
329 int i = 0;
330 while (true) {
331
332
333
334
335
336
337
338
339
340
341
342
343
344 unprocessed[unprocessed.length - 1].clear();
345
346
347 mail = null;
348
349
350 for (i = 0; i < unprocessed.length; i++) {
351 if (unprocessed[i].size() > 0) {
352
353 mail = (Mail)unprocessed[i].remove(0);
354 break;
355 }
356 }
357
358
359 if (mail == null) {
360
361 return;
362 }
363
364
365
366 Collection recipients = null;
367 Matcher matcher = (Matcher) matchers.get(i);
368 StringBuffer logMessageBuffer = null;
369 if (getLogger().isDebugEnabled()) {
370 logMessageBuffer =
371 new StringBuffer(128)
372 .append("Checking ")
373 .append(mail.getName())
374 .append(" with ")
375 .append(matcher);
376 getLogger().debug(logMessageBuffer.toString());
377 }
378 try {
379 recipients = matcher.match(mail);
380 if (recipients == null) {
381
382 recipients = new ArrayList(0);
383 } else if (recipients != mail.getRecipients()) {
384
385 verifyMailAddresses(recipients);
386 }
387 } catch (MessagingException me) {
388
389 MailetConfig mailetConfig = ((Mailet) mailets.get(i)).getMailetConfig();
390 String onMatchException = ((MailetConfigImpl) mailetConfig).getInitAttribute("onMatchException");
391 if (onMatchException == null) {
392 onMatchException = Mail.ERROR;
393 } else {
394 onMatchException = onMatchException.trim().toLowerCase(Locale.US);
395 }
396 if (onMatchException.compareTo("nomatch") == 0) {
397
398 recipients = new ArrayList(0);
399 } else if (onMatchException.compareTo("matchall") == 0) {
400 recipients = mail.getRecipients();
401
402 } else {
403 handleException(me, mail, matcher.getMatcherConfig().getMatcherName(), onMatchException);
404 }
405 }
406
407
408
409 Collection notRecipients;
410 if (recipients == mail.getRecipients() || recipients.size() == 0) {
411 notRecipients = new ArrayList(0);
412 } else {
413 notRecipients = new ArrayList(mail.getRecipients());
414 notRecipients.removeAll(recipients);
415 }
416
417 if (recipients.size() == 0) {
418
419 unprocessed[i + 1].add(mail);
420 continue;
421 }
422 if (notRecipients.size() != 0) {
423
424
425
426 Mail notMail = new MailImpl(mail);
427 notMail.setRecipients(notRecipients);
428
429 notMail.setState(originalState);
430 unprocessed[i + 1].add(notMail);
431
432 mail.setRecipients(recipients);
433 }
434
435 Mailet mailet = (Mailet) mailets.get(i);
436 if (getLogger().isDebugEnabled()) {
437 logMessageBuffer =
438 new StringBuffer(128)
439 .append("Servicing ")
440 .append(mail.getName())
441 .append(" by ")
442 .append(mailet.getMailetInfo());
443 getLogger().debug(logMessageBuffer.toString());
444 }
445 try {
446 mailet.service(mail);
447
448 verifyMailAddresses(mail.getRecipients());
449 } catch (MessagingException me) {
450 MailetConfig mailetConfig = mailet.getMailetConfig();
451 String onMailetException = ((MailetConfigImpl) mailetConfig).getInitAttribute("onMailetException");
452 if (onMailetException == null) {
453 onMailetException = Mail.ERROR;
454 } else {
455 onMailetException = onMailetException.trim().toLowerCase(Locale.US);
456 }
457 if (onMailetException.compareTo("ignore") == 0) {
458
459
460 verifyMailAddresses(mail.getRecipients());
461 } else {
462 handleException(me, mail, mailet.getMailetConfig().getMailetName(), onMailetException);
463 }
464 }
465
466
467 if (!mail.getState().equals(originalState)) {
468
469 if (mail.getState().equals(Mail.GHOST)) {
470
471 ContainerUtil.dispose(mail);
472 mail = null;
473 continue;
474 }
475
476
477
478
479
480 if (originalMail != mail) {
481 spool.store(mail);
482 ContainerUtil.dispose(mail);
483 }
484 mail = null;
485 continue;
486 } else {
487
488
489 unprocessed[i + 1].add(mail);
490 }
491
492 }
493 }
494
495
496
497
498
499
500
501 private void verifyMailAddresses(Collection col) throws MessagingException {
502 try {
503 MailAddress addresses[] = (MailAddress[])col.toArray(new MailAddress[0]);
504
505
506
507
508 if (addresses.length != col.size()) {
509 throw new MailetException("The recipient list contains objects other than MailAddress objects");
510 }
511 } catch (ArrayStoreException ase) {
512 throw new MailetException("The recipient list contains objects other than MailAddress objects");
513 }
514 }
515
516
517
518
519
520
521
522
523
524
525
526
527 private void handleException(MessagingException me, Mail mail, String offendersName, String nextState) throws MessagingException {
528 System.err.println("exception! " + me);
529 mail.setState(nextState);
530 StringWriter sout = new StringWriter();
531 PrintWriter out = new PrintWriter(sout, true);
532 StringBuffer exceptionBuffer =
533 new StringBuffer(128)
534 .append("Exception calling ")
535 .append(offendersName)
536 .append(": ")
537 .append(me.getMessage());
538 out.println(exceptionBuffer.toString());
539 Exception e = me;
540 while (e != null) {
541 e.printStackTrace(out);
542 if (e instanceof MessagingException) {
543 e = ((MessagingException)e).getNextException();
544 } else {
545 e = null;
546 }
547 }
548 String errorString = sout.toString();
549 mail.setErrorMessage(errorString);
550 getLogger().error(errorString);
551 throw me;
552 }
553
554
555
556
557 public void openProcessorList() {
558 matchers = new ArrayList();
559 mailets = new ArrayList();
560 }
561
562
563
564
565 public void configure(Configuration processorConf) throws ConfigurationException {
566 openProcessorList();
567
568 final Configuration[] mailetConfs
569 = processorConf.getChildren( "mailet" );
570
571
572
573 for ( int j = 0; j < mailetConfs.length; j++ )
574 {
575 Configuration c = mailetConfs[j];
576 String mailetClassName = c.getAttribute("class");
577 String matcherName = c.getAttribute("match",null);
578 String invertedMatcherName = c.getAttribute("notmatch",null);
579
580 Mailet mailet = null;
581 Matcher matcher = null;
582 try {
583
584 if (matcherName != null && invertedMatcherName != null) {
585
586 throw new ConfigurationException(
587 "Please configure only match or nomatch per mailet");
588 } else if (matcherName != null) {
589 matcher = matchLoader.getMatcher(matcherName);
590 } else if (invertedMatcherName != null) {
591 matcher = new MatcherInverter(matchLoader
592 .getMatcher(invertedMatcherName));
593
594 } else {
595
596 matcher = matchLoader.getMatcher("All");
597 }
598
599
600 if (getLogger().isInfoEnabled()) {
601 StringBuffer infoBuffer =
602 new StringBuffer(64)
603 .append("Matcher ")
604 .append(matcherName)
605 .append(" instantiated.");
606 getLogger().info(infoBuffer.toString());
607 }
608 } catch (MessagingException ex) {
609
610 if (getLogger().isErrorEnabled()) {
611 StringBuffer errorBuffer =
612 new StringBuffer(256)
613 .append("Unable to init matcher ")
614 .append(matcherName)
615 .append(": ")
616 .append(ex.toString());
617 getLogger().error( errorBuffer.toString(), ex );
618 if (ex.getNextException() != null) {
619 getLogger().error( "Caused by nested exception: ", ex.getNextException());
620 }
621 }
622 System.err.println("Unable to init matcher " + matcherName);
623 System.err.println("Check spool manager logs for more details.");
624
625 throw new ConfigurationException("Unable to init matcher",c,ex);
626 }
627 try {
628 mailet = mailetLoader.getMailet(mailetClassName, c);
629 if (getLogger().isInfoEnabled()) {
630 StringBuffer infoBuffer =
631 new StringBuffer(64)
632 .append("Mailet ")
633 .append(mailetClassName)
634 .append(" instantiated.");
635 getLogger().info(infoBuffer.toString());
636 }
637 } catch (MessagingException ex) {
638
639 if (getLogger().isErrorEnabled()) {
640 StringBuffer errorBuffer =
641 new StringBuffer(256)
642 .append("Unable to init mailet ")
643 .append(mailetClassName)
644 .append(": ")
645 .append(ex.toString());
646 getLogger().error( errorBuffer.toString(), ex );
647 if (ex.getNextException() != null) {
648 getLogger().error( "Caused by nested exception: ", ex.getNextException());
649 }
650 }
651 System.err.println("Unable to init mailet " + mailetClassName);
652 System.err.println("Check spool manager logs for more details.");
653 throw new ConfigurationException("Unable to init mailet",c,ex);
654 }
655
656 add(matcher, mailet);
657 }
658
659
660
661
662
663
664 closeProcessorLists();
665 }
666
667
668
669
670 public void service(ServiceManager comp) throws ServiceException {
671 setMailetLoader((MailetLoader) comp.lookup(MailetLoader.ROLE));
672 setMatchLoader((MatcherLoader) comp.lookup(MatcherLoader.ROLE));
673 setSpool( (SpoolRepository) comp.lookup(SpoolRepository.ROLE));
674 }
675
676 public List getMailetConfigs() {
677 List mailetConfigs = new ArrayList();
678 Iterator iterator = mailets.iterator();
679 while (iterator.hasNext()) {
680 Mailet mailet = (Mailet) iterator.next();
681 MailetConfig mailetConfig = mailet.getMailetConfig();
682 if (mailetConfig == null) mailetConfigs.add(new MailetConfigImpl());
683 else mailetConfigs.add(mailetConfig);
684 }
685 return mailetConfigs;
686 }
687
688 public List getMatcherConfigs() {
689 List matcherConfigs = new ArrayList();
690 Iterator iterator = matchers.iterator();
691 while (iterator.hasNext()) {
692 Matcher matcher = (Matcher) iterator.next();
693 MatcherConfig matcherConfig = matcher.getMatcherConfig();
694 if (matcherConfig == null) matcherConfigs.add(new MatcherConfigImpl());
695 else matcherConfigs.add(matcherConfig);
696 }
697 return matcherConfigs;
698 }
699 }