View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
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   * Implements a processor for mails, directing the mail down
59   * the chain of matchers/mailets.
60   *
61   *  SAMPLE CONFIGURATION
62   *  <processor name="try" onerror="return,log">
63   *      <mailet match="RecipientIsLocal" class="LocalDelivery">
64   *      </mailet>
65   *      <mailet match="All" class="RemoteDelivery">
66   *          <delayTime>21600000</delayTime>
67   *          <maxRetries>5</maxRetries>
68   *      </mailet>
69   *  </processor>
70   *
71   * Note that the 'onerror' attribute is not yet supported.
72   *
73   * As of James v2.2.0a5, 'onerror' functionality is implemented, but
74   * it is implemented on the <mailet> tag.  The specification is:
75   *
76   *   <mailet match="..." class="..."
77   *       [onMatchException="{noMatch|matchAll|error|<aProcessorName>}"] 
78   *       [onMailetException="{ignore|error|<aProcessorName>}"]>
79   *
80   * noMatch:   no addresses are considered to match
81   * matchAll:  all addresses are considered to match
82   * error:     as before, send the message to the ERROR processor
83   *
84   * Otherwise, a processor name can be specified, and the message will
85   * be sent there.
86   *
87   * <P>CVS $Id: LinearProcessor.java 717869 2008-11-15 15:56:18Z rdonkin $</P>
88   * @version 2.2.0
89   */
90  public class LinearProcessor 
91      extends AbstractLogEnabled
92      implements Disposable, Configurable, Serviceable, MailProcessor, MailetContainer {
93  
94      /**
95       *  The name of the matcher used to terminate the matcher chain.  The
96       *  end of the matcher/mailet chain must be a matcher that matches
97       *  all mails and a mailet that sets every mail to GHOST status.
98       *  This is necessary to ensure that mails are removed from the spool
99       *  in an orderly fashion.
100      */
101     private static final String TERMINATING_MATCHER_NAME = "Terminating%Matcher%Name";
102 
103     /**
104      *  The name of the mailet used to terminate the mailet chain.  The
105      *  end of the matcher/mailet chain must be a matcher that matches
106      *  all mails and a mailet that sets every mail to GHOST status.
107      *  This is necessary to ensure that mails are removed from the spool
108      *  in an orderly fashion.
109      */
110     private static final String TERMINATING_MAILET_NAME = "Terminating%Mailet%Name";
111 
112     private List mailets;  // The list of mailets for this processor
113     private List matchers; // The list of matchers for this processor
114     private volatile boolean listsClosed;  // Whether the matcher/mailet lists have been closed.
115     private SpoolRepository spool;  // The spool on which this processor is acting
116 
117     private MailetLoader mailetLoader;
118 
119     private MatcherLoader matchLoader;
120 
121     /**
122      * Set the spool to be used by this LinearProcessor.
123      *
124      * @param spool the spool to be used by this processor
125      *
126      * @throws IllegalArgumentException when the spool passed in is null
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      * Set the MailetLoader
137      * 
138      * @param mailetLoader the MailetLoader
139      */
140     public void setMailetLoader(MailetLoader mailetLoader) {
141         this.mailetLoader = mailetLoader;
142     }
143 
144     /**
145      * Set the MatcherLoader
146      * 
147      * @param matchLoader the MatcherLoader
148      */
149     public void setMatchLoader(MatcherLoader matchLoader) {
150         this.matchLoader = matchLoader;
151     }
152 
153     /**
154      * <p>The dispose operation is called at the end of a components lifecycle.
155      * Instances of this class use this method to release and destroy any
156      * resources that they own.</p>
157      *
158      * <p>This implementation disposes of all the mailet instances added to the
159      * processor</p>
160      *
161      * @see org.apache.avalon.framework.activity.Disposable#dispose()
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      * <p>Adds a new <code>Matcher</code> / <code>Mailet</code> pair
177      * to the processor.  Checks to ensure that the matcher and
178      * mailet passed in are not null.  Synchronized to ensure that
179      * the matchers and mailets are kept in sync.</p>
180      *
181      * <p>It is an essential part of the contract of the LinearProcessor
182      * that a particular matcher/mailet combination be used to
183      * terminate the processor chain.  This is done by calling the  
184      * closeProcessorList method.</p>
185      *
186      * <p>Once the closeProcessorList has been called any subsequent
187      * call to the add method will result in an IllegalStateException.</p>
188      *
189      * <p>This method is synchronized to protect against corruption of
190      * matcher/mailets lists</p>
191      *
192      * @param matcher the new matcher being added
193      * @param mailet the new mailet being added
194      *
195      * @throws IllegalArgumentException when the matcher or mailet passed in is null
196      * @throws IllegalStateException when this method is called after the processor lists have been closed
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      * <p>Closes the processor matcher/mailet list.</p>
214      *
215      * <p>This method is synchronized to protect against corruption of
216      * matcher/mailets lists</p>
217      *
218      * @throws IllegalStateException when this method is called after the processor lists have been closed
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                         // Don't complain if we fall off the end of the
239                         // error processor.  That is currently the
240                         // normal situation for James, and the message
241                         // will show up in the error store.
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      * <p>Processes a single mail message through the chain of matchers and mailets.</p>
265      *
266      * <p>Calls to this method before setSpool has been called with a non-null argument
267      * will result in an <code>IllegalStateException</code>.</p>
268      *
269      * <p>If the matcher/mailet lists have not been closed by a call to the closeProcessorLists
270      * method then a call to this method will result in an <code>IllegalStateException</code>.
271      * The end of the matcher/mailet chain must be a matcher that matches all mails and 
272      * a mailet that sets every mail to GHOST status.  This is necessary to ensure that 
273      * mails are removed from the spool in an orderly fashion.  The closeProcessorLists method
274      * ensures this.</p>
275      * 
276      * @param mail the new mail to be processed
277      *
278      * @throws IllegalStateException when this method is called before the processor lists have been closed
279      *                                  or the spool has been initialized
280      *
281      * @see org.apache.james.transport.MailProcessor#service(org.apache.mailet.Mail)
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         //  unprocessed is an array of Lists of Mail objects
296         //  the array indicates which matcher/mailet (stage in the linear
297         //  processor) that this Mail needs to be processed.
298         //  e.g., a Mail in unprocessed[0] needs to be
299         //  processed by the first matcher/mailet.
300         //
301         //  It is a List of Mail objects at each array spot as multiple Mail
302         //  objects could be at the same stage.
303         //
304         //  Note that every Mail object in this array will either be the 
305         //  original Mail object passed in, or a result of this method's
306         //  (and hence this thread's) processing.
307 
308         List[] unprocessed = new List[matchers.size() + 1];
309 
310         for (int i = 0; i < unprocessed.length; i++) {
311             // No need to use synchronization, as this is totally
312             // local to the method
313             unprocessed[i] = new LinkedList();
314         }
315 
316         //Add the object to the bottom of the list
317         unprocessed[0].add(mail);
318 
319         //This is the original state of the message
320         String originalState = mail.getState();
321         
322         
323         // The original mail: we should not care to save this mail.
324         // This should be saved in the spoolmanager.
325         Mail originalMail = mail;
326 
327         //We'll use these as temporary variables in the loop
328         mail = null;  // the message we're currently processing
329         int i = 0;    // where in the stage we're looking
330         while (true) {
331             //  The last element in the unprocessed array has mail messages
332             //  that have completed all stages.  We want them to just die,
333             //  so we clear that spot to allow garbage collection of the
334             //  objects.
335             //
336             //  Please note that the presence of the terminating mailet at the end
337             //  of the chain is critical to the proper operation
338             //  of the LinearProcessor code.  If this mailet is not placed
339             //  at the end of the chain with a terminating matcher, there is a 
340             //  potential for configuration or implementation errors to 
341             //  lead to mails trapped in the spool.  This matcher/mailet
342             //  combination is added when the closeProcessorList method
343             //  is called.
344             unprocessed[unprocessed.length - 1].clear();
345 
346             //initialize the mail reference we will be searching on
347             mail = null;
348 
349             //Scan through all stages, trying to find a message to process
350             for (i = 0; i < unprocessed.length; i++) {
351                 if (unprocessed[i].size() > 0) {
352                     //Get the first element from the queue, and remove it from there
353                     mail = (Mail)unprocessed[i].remove(0);
354                     break;
355                 }
356             }
357 
358             //Check it we found anything
359             if (mail == null) {
360                 //We found no messages to process... we're done servicing the mail object
361                 return;
362             }
363 
364 
365             //Call the matcher and find what recipients match
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                     //In case the matcher returned null, create an empty Collection
382                     recipients = new ArrayList(0);
383                 } else if (recipients != mail.getRecipients()) {
384                     //Make sure all the objects are MailAddress objects
385                     verifyMailAddresses(recipients);
386                 }
387             } catch (MessagingException me) {
388                 // look in the matcher's mailet's init attributes
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                     //In case the matcher returned null, create an empty Collection
398                     recipients = new ArrayList(0);
399                 } else if (onMatchException.compareTo("matchall") == 0) {
400                     recipients = mail.getRecipients();
401                     // no need to verify addresses
402                 } else {
403                     handleException(me, mail, matcher.getMatcherConfig().getMatcherName(), onMatchException);
404                 }
405             }
406 
407             // Split the recipients into two pools.  notRecipients will contain the
408             // recipients on the message that the matcher did not return.
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                 //Everything was not a match... store it in the next spot in the array
419                 unprocessed[i + 1].add(mail);
420                 continue;
421             }
422             if (notRecipients.size() != 0) {
423                 // There are a mix of recipients and not recipients.
424                 // We need to clone this message, put the notRecipients on the clone
425                 // and store it in the next spot
426                 Mail notMail = new MailImpl(mail);
427                 notMail.setRecipients(notRecipients);
428                 // set the state to the current processor
429                 notMail.setState(originalState);
430                 unprocessed[i + 1].add(notMail);
431                 //We have to set the reduce possible recipients on the old message
432                 mail.setRecipients(recipients);
433             }
434             // We have messages that need to process... time to run the mailet.
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                 // Make sure all the recipients are still MailAddress objects
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                     // ignore the exception and continue
459                     // this option should not be used if the mail object can be changed by the mailet
460                     verifyMailAddresses(mail.getRecipients());
461                 } else {
462                     handleException(me, mail, mailet.getMailetConfig().getMailetName(), onMailetException);
463                 }
464             }
465 
466             // See if the state was changed by the mailet
467             if (!mail.getState().equals(originalState)) {
468                 //If this message was ghosted, we just want to let it die
469                 if (mail.getState().equals(Mail.GHOST)) {
470                     // let this instance die...
471                     ContainerUtil.dispose(mail);
472                     mail = null;
473                     continue;
474                 }
475                 // This was just set to another state requiring further processing... 
476                 // Store this back in the spool and it will get picked up and 
477                 // run in that processor
478                 // We store only mails created by the matcher "splitting"
479                 // The original mail will be "stored" by the caller.
480                 if (originalMail != mail) {
481                     spool.store(mail);
482                     ContainerUtil.dispose(mail);
483                 }
484                 mail = null;
485                 continue;
486             } else {
487                 // Ok, we made it through with the same state... move it to the next
488                 //  spot in the array
489                 unprocessed[i + 1].add(mail);
490             }
491 
492         }
493     }
494 
495 
496     /**
497      * Checks that all objects in this class are of the form MailAddress.
498      *
499      * @throws MessagingException when the <code>Collection</code> contains objects that are not <code>MailAddress</code> objects
500      */
501     private void verifyMailAddresses(Collection col) throws MessagingException {
502         try {
503             MailAddress addresses[] = (MailAddress[])col.toArray(new MailAddress[0]);
504 
505             // Why is this here?  According to the javadoc for
506             // java.util.Collection.toArray(Object[]), this should
507             // never happen.  The exception will be thrown.
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      * This is a helper method that updates the state of the mail object to
518      * Mail.ERROR as well as recording the exception to the log
519      *
520      * @param me the exception to be handled
521      * @param mail the mail being processed when the exception was generated
522      * @param offendersName the matcher or mailet than generated the exception
523      * @param nextState the next state to set
524      *
525      * @throws MessagingException thrown always, rethrowing the passed in exception
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      * <p>Initialize the processor matcher/mailet list.</p>
556      */
557     public void openProcessorList() {
558         matchers = new ArrayList();
559         mailets = new ArrayList();
560     }
561 
562     /**
563      * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
564      */
565     public void configure(Configuration processorConf) throws ConfigurationException {
566         openProcessorList();
567         
568         final Configuration[] mailetConfs
569             = processorConf.getChildren( "mailet" );
570         // Loop through the mailet configuration, load
571         // all of the matcher and mailets, and add
572         // them to the processor.
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                     // if no matcher is configured throw an Exception
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                     // default matcher is All
596                     matcher = matchLoader.getMatcher("All");
597                 }
598                 
599                 //The matcher itself should log that it's been inited.
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                 // **** Do better job printing out exception
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                 //System.exit(1);
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                 // **** Do better job printing out exception
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             //Add this pair to the processor
656             add(matcher, mailet);
657         }
658 
659         // Close the processor matcher/mailet lists.
660         //
661         // Please note that this is critical to the proper operation
662         // of the LinearProcessor code.  The processor will not be
663         // able to service mails until this call is made.
664         closeProcessorLists();
665     }
666 
667     /**
668      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
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()); // placeholder
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()); // placeholder
695             else matcherConfigs.add(matcherConfig);
696         }
697         return matcherConfigs;
698     }
699 }