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.fetchmail;
23  
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Enumeration;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  
33  import javax.mail.MessagingException;
34  import javax.mail.Session;
35  
36  import org.apache.avalon.cornerstone.services.scheduler.Target;
37  import org.apache.avalon.framework.configuration.Configurable;
38  import org.apache.avalon.framework.configuration.Configuration;
39  import org.apache.avalon.framework.configuration.ConfigurationException;
40  import org.apache.avalon.framework.logger.AbstractLogEnabled;
41  import org.apache.avalon.framework.service.ServiceException;
42  import org.apache.avalon.framework.service.ServiceManager;
43  import org.apache.avalon.framework.service.Serviceable;
44  import org.apache.james.api.dnsservice.DNSService;
45  import org.apache.james.api.user.UsersRepository;
46  import org.apache.james.services.MailServer;
47  
48  /**
49   * <p>Class <code>FetchMail</code> is an Avalon task that is periodically
50   * triggered to fetch mail from a JavaMail Message Store.</p>
51   * 
52   * <p>The lifecycle of an instance of <code>FetchMail</code> is managed by
53   * Avalon. The <code>configure(Configuration)</code> method is invoked to parse
54   * and validate Configuration properties. The targetTriggered(String) method is
55   * invoked to execute the task.</p>
56   *  
57   * <p>When triggered, a sorted list of Message Store Accounts to be processed is
58   * built. Each Message Store Account is processed by delegating to 
59   * <code>StoreProcessor</code>.</p>
60   * 
61   * <p>There are two kinds of Message Store Accounts, static and dynamic. Static 
62   * accounts are expliciltly declared in the Configuration. Dynamic accounts are
63   * built each time the task is executed, one per each user defined to James,
64   * using the James user name with a configurable prefix and suffix to define
65   * the host user identity and recipient identity for each Account. Dynamic
66   * accounts allow <code>FetchMail</code> to fetch mail for all James users
67   * without modifying the Configuration parameters or restarting the Avalon 
68   * server.</p>
69   * 
70   * <p>To fully understand the operations supported by this task, read the Class
71   * documention for each Class in the delegation chain starting with this 
72   * class' delegate, <code>StoreProcessor</code>. </p>
73   * 
74   * <p>Creation Date: 24-May-03</p>
75   * 
76   */
77  public class FetchMail extends AbstractLogEnabled implements Configurable, Target, Serviceable
78  {
79      /**
80       * Key fields for DynamicAccounts.
81       */
82      private class DynamicAccountKey
83      {
84          /**
85           * The base user name without prfix or suffix
86           */
87          private String fieldUserName;
88          
89          /**
90           * The sequence number of the parameters used to construct the Account
91           */
92          private int fieldSequenceNumber;                
93  
94          /**
95           * Constructor for DynamicAccountKey.
96           */
97          private DynamicAccountKey()
98          {
99              super();
100         }
101         
102         /**
103          * Constructor for DynamicAccountKey.
104          */
105         public DynamicAccountKey(String userName, int sequenceNumber)
106         {
107             this();
108             setUserName(userName);
109             setSequenceNumber(sequenceNumber);
110         }        
111 
112         /**
113          * @see java.lang.Object#equals(Object)
114          */
115         public boolean equals(Object obj)
116         {
117             if (null == obj)
118                 return false;
119             if (!(obj.getClass() == getClass()))
120                 return false;
121             return (
122                 getUserName().equals(((DynamicAccountKey) obj).getUserName())
123                     && getSequenceNumber()
124                         == ((DynamicAccountKey) obj).getSequenceNumber());
125         }
126 
127         /**
128          * @see java.lang.Object#hashCode()
129          */
130         public int hashCode()
131         {
132             return getUserName().hashCode() ^ getSequenceNumber();
133         }
134 
135         /**
136          * Returns the sequenceNumber.
137          * @return int
138          */
139         public int getSequenceNumber()
140         {
141             return fieldSequenceNumber;
142         }
143 
144         /**
145          * Returns the userName.
146          * @return String
147          */
148         public String getUserName()
149         {
150             return fieldUserName;
151         }
152 
153         /**
154          * Sets the sequenceNumber.
155          * @param sequenceNumber The sequenceNumber to set
156          */
157         protected void setSequenceNumber(int sequenceNumber)
158         {
159             fieldSequenceNumber = sequenceNumber;
160         }
161 
162         /**
163          * Sets the userName.
164          * @param userName The userName to set
165          */
166         protected void setUserName(String userName)
167         {
168             fieldUserName = userName;
169         }
170 
171     }
172     /**
173      * Creation Date: 06-Jun-03
174      */
175     private class ParsedDynamicAccountParameters
176     {
177         private String fieldUserPrefix;
178         private String fieldUserSuffix;
179         
180         private String fieldPassword;
181         
182         private int fieldSequenceNumber;
183 
184         private boolean fieldIgnoreRecipientHeader;     
185         private String fieldRecipientPrefix;
186         private String fieldRecipientSuffix;
187         private String customRecipientHeader;
188 
189         /**
190          * Constructor for ParsedDynamicAccountParameters.
191          */
192         private ParsedDynamicAccountParameters()
193         {
194             super();
195         }
196 
197         /**
198          * Constructor for ParsedDynamicAccountParameters.
199          */
200         public ParsedDynamicAccountParameters(
201             int sequenceNumber,
202             Configuration configuration)
203             throws ConfigurationException
204         {
205             this();
206             setSequenceNumber(sequenceNumber);
207             setUserPrefix(configuration.getAttribute("userprefix", ""));
208             setUserSuffix(configuration.getAttribute("usersuffix", ""));
209             setRecipientPrefix(configuration.getAttribute("recipientprefix", ""));
210             setRecipientSuffix(configuration.getAttribute("recipientsuffix", ""));
211             setPassword(configuration.getAttribute("password"));
212             setIgnoreRecipientHeader(
213                 configuration.getAttributeAsBoolean("ignorercpt-header"));
214             setCustomRecipientHeader(configuration.getAttribute("customrcpt-header", ""));
215         }                       
216 
217         /**
218          * Returns the custom recipient header.
219          * @return String
220          */
221         public String getCustomRecipientHeader() {
222             return this.customRecipientHeader;
223         }
224 
225         /**
226          * Returns the recipientprefix.
227          * @return String
228          */
229         public String getRecipientPrefix()
230         {
231             return fieldRecipientPrefix;
232         }
233 
234         /**
235          * Returns the recipientsuffix.
236          * @return String
237          */
238         public String getRecipientSuffix()
239         {
240             return fieldRecipientSuffix;
241         }
242 
243         /**
244          * Returns the userprefix.
245          * @return String
246          */
247         public String getUserPrefix()
248         {
249             return fieldUserPrefix;
250         }
251 
252         /**
253          * Returns the userSuffix.
254          * @return String
255          */
256         public String getUserSuffix()
257         {
258             return fieldUserSuffix;
259         }
260 
261         /**
262          * Sets the custom recipient header.
263          * @param customRecipientHeader The header to be used
264          */
265         public void setCustomRecipientHeader(String customRecipientHeader) {
266             this.customRecipientHeader = customRecipientHeader;
267         }
268 
269         /**
270          * Sets the recipientprefix.
271          * @param recipientprefix The recipientprefix to set
272          */
273         protected void setRecipientPrefix(String recipientprefix)
274         {
275             fieldRecipientPrefix = recipientprefix;
276         }
277 
278         /**
279          * Sets the recipientsuffix.
280          * @param recipientsuffix The recipientsuffix to set
281          */
282         protected void setRecipientSuffix(String recipientsuffix)
283         {
284             fieldRecipientSuffix = recipientsuffix;
285         }
286 
287         /**
288          * Sets the userprefix.
289          * @param userprefix The userprefix to set
290          */
291         protected void setUserPrefix(String userprefix)
292         {
293             fieldUserPrefix = userprefix;
294         }
295 
296         /**
297          * Sets the userSuffix.
298          * @param userSuffix The userSuffix to set
299          */
300         protected void setUserSuffix(String userSuffix)
301         {
302             fieldUserSuffix = userSuffix;
303         }
304 
305         /**
306          * Returns the password.
307          * @return String
308          */
309         public String getPassword()
310         {
311             return fieldPassword;
312         }
313 
314         /**
315          * Sets the ignoreRecipientHeader.
316          * @param ignoreRecipientHeader The ignoreRecipientHeader to set
317          */
318         protected void setIgnoreRecipientHeader(boolean ignoreRecipientHeader)
319         {
320             fieldIgnoreRecipientHeader = ignoreRecipientHeader;
321         }
322 
323         /**
324          * Sets the password.
325          * @param password The password to set
326          */
327         protected void setPassword(String password)
328         {
329             fieldPassword = password;
330         }
331 
332         /**
333          * Returns the ignoreRecipientHeader.
334          * @return boolean
335          */
336         public boolean isIgnoreRecipientHeader()
337         {
338             return fieldIgnoreRecipientHeader;
339         }
340 
341         /**
342          * Returns the sequenceNumber.
343          * @return int
344          */
345         public int getSequenceNumber()
346         {
347             return fieldSequenceNumber;
348         }
349 
350         /**
351          * Sets the sequenceNumber.
352          * @param sequenceNumber The sequenceNumber to set
353          */
354         protected void setSequenceNumber(int sequenceNumber)
355         {
356             fieldSequenceNumber = sequenceNumber;
357         }
358 
359     }
360     /**
361      * @see org.apache.avalon.cornerstone.services.scheduler.Target#targetTriggered(String)
362      */
363     private boolean fieldFetching = false;
364     
365     /**
366      * The Configuration for this task
367      */
368     private ParsedConfiguration fieldConfiguration;
369     
370     /**
371      * A List of ParsedDynamicAccountParameters, one for every <alllocal> entry
372      * in the configuration.
373      */
374     private List fieldParsedDynamicAccountParameters;    
375     
376     /**
377      * The Static Accounts for this task.
378      * These are setup when the task is configured.
379      */
380     private List fieldStaticAccounts;
381     
382     /**
383      * The JavaMail Session for this fetch task.
384      */ 
385 
386     private Session fieldSession;
387     
388     /**
389      * The Dynamic Accounts for this task.
390      * These are setup each time the fetchtask is run.
391      */
392     private Map fieldDynamicAccounts;        
393     
394    /**
395      * The MailServer service
396      */
397     private MailServer fieldServer;
398     
399    /**
400      * The Local Users repository
401      */
402     private UsersRepository fieldLocalUsers;     
403     
404     /**
405      * The DNSService
406      */
407     private DNSService dnsServer;
408     
409     /**
410      * Constructor for POP3mail.
411      */
412     public FetchMail()
413     {
414         super();
415     }
416 
417     /**
418      * Method configure parses and validates the Configuration data and creates
419      * a new <code>ParsedConfiguration</code>, an <code>Account</code> for each
420      * configured static account and a <code>ParsedDynamicAccountParameters</code>
421      * for each dynamic account.
422      * 
423      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
424      */
425     public void configure(Configuration configuration)
426         throws ConfigurationException
427     {
428         // Set any Session parameters passed in the Configuration
429         setSessionParameters(configuration);
430 
431         // Create the ParsedConfiguration used in the delegation chain
432         ParsedConfiguration parsedConfiguration =
433             new ParsedConfiguration(
434                 configuration,
435                 getLogger(),
436                 getServer(),
437                 getLocalUsers(),
438                 getDNSServer());
439         setConfiguration(parsedConfiguration);
440 
441         // Setup the Accounts
442         Configuration[] allAccounts = configuration.getChildren("accounts");
443         if (allAccounts.length < 1)
444             throw new ConfigurationException("Missing <accounts> section.");
445         if (allAccounts.length > 1)
446             throw new ConfigurationException("Too many <accounts> sections, there must be exactly one");
447         Configuration accounts = allAccounts[0];
448 
449         // Create an Account for every configured account
450         Configuration[] accountsChildren = accounts.getChildren();
451         if (accountsChildren.length < 1)
452             throw new ConfigurationException("Missing <account> section.");
453 
454         for (int i = 0; i < accountsChildren.length; i++)
455         {
456             Configuration accountsChild = accountsChildren[i];
457 
458             if ("alllocal".equals(accountsChild.getName()))
459             {
460                 // <allLocal> is dynamic, save the parameters for accounts to
461                 // be created when the task is triggered
462                 getParsedDynamicAccountParameters().add(
463                     new ParsedDynamicAccountParameters(i, accountsChild));
464                 continue;
465             }
466 
467             if ("account".equals(accountsChild.getName()))
468             {
469                 // Create an Account for the named user and
470                 // add it to the list of static accounts
471                 getStaticAccounts().add(
472                     new Account(
473                         i,
474                         parsedConfiguration,
475                         accountsChild.getAttribute("user"),
476                         accountsChild.getAttribute("password"),
477                         accountsChild.getAttribute("recipient"),
478                         accountsChild.getAttributeAsBoolean(
479                             "ignorercpt-header"),
480                         accountsChild.getAttribute("customrcpt-header",""),
481                         getSession()));
482                 continue;
483             }
484 
485             throw new ConfigurationException(
486                 "Illegal token: <"
487                     + accountsChild.getName()
488                     + "> in <accounts>");
489         }
490     }
491 
492     /**
493      * Method target triggered fetches mail for each configured account.
494      * 
495      * @see org.apache.avalon.cornerstone.services.scheduler.Target#targetTriggered(String)
496      */
497     public void targetTriggered(String arg0)
498     {
499         // if we are already fetching then just return
500         if (isFetching())
501         {
502             getLogger().info(
503                 "Triggered fetch cancelled. A fetch is already in progress.");
504             return;
505         }
506 
507         // Enter Fetching State
508         try
509         {
510             setFetching(true);
511             getLogger().info("Fetcher starting fetches");
512 
513             // if debugging, list the JavaMail property key/value pairs
514             // for this Session
515             if (getLogger().isDebugEnabled())
516             {
517                 getLogger().debug("Session properties:");
518                 Properties properties = getSession().getProperties();
519                 Enumeration e = properties.keys();
520                 while (e.hasMoreElements())
521                 {
522                     String key = (String) e.nextElement();
523                     String val = (String) properties.get(key);
524                     if (val.length() > 40)
525                     {
526                         val = val.substring(0, 37) + "...";
527                     }
528                     getLogger().debug(key + "=" + val);
529 
530                 }
531             }
532 
533             // Update the dynamic accounts,
534             // merge with the static accounts and
535             // sort the accounts so they are in the order
536             // they were entered in config.xml
537             updateDynamicAccounts();
538             ArrayList mergedAccounts =
539                 new ArrayList(
540                     getDynamicAccounts().size() + getStaticAccounts().size());
541             mergedAccounts.addAll(getDynamicAccounts().values());
542             mergedAccounts.addAll(getStaticAccounts());
543             Collections.sort(mergedAccounts);
544 
545             StringBuffer logMessage = new StringBuffer(64);
546             logMessage.append("Processing ");
547             logMessage.append(getStaticAccounts().size());
548             logMessage.append(" static accounts and ");
549             logMessage.append(getDynamicAccounts().size());
550             logMessage.append(" dynamic accounts.");
551             getLogger().info(logMessage.toString());
552 
553             // Fetch each account
554             Iterator accounts = mergedAccounts.iterator();
555             while (accounts.hasNext())
556             {
557                 try
558                 {
559                     new StoreProcessor((Account) accounts.next()).process();
560                 }
561                 catch (MessagingException ex)
562                 {
563                     getLogger().error(
564                         "A MessagingException has terminated processing of this Account",
565                         ex);
566                 }
567             }
568         }
569         catch (Exception ex)
570         {
571             getLogger().error("An Exception has terminated this fetch.", ex);
572         }
573         finally
574         {
575             getLogger().info("Fetcher completed fetches");
576 
577             // Exit Fetching State
578             setFetching(false);
579         }
580     }
581 
582     /**
583      * Returns the fetching.
584      * @return boolean
585      */
586     protected boolean isFetching()
587     {
588         return fieldFetching;
589     }
590 
591     /**
592      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
593      */
594     public void service(final ServiceManager manager) throws ServiceException
595     {
596         try
597         {
598             setServer((MailServer) manager.lookup(MailServer.ROLE));
599         }
600         catch (ClassCastException cce)
601         {
602             StringBuffer errorBuffer =
603                 new StringBuffer(128).append("Component ").append(
604                     MailServer.ROLE).append(
605                     "does not implement the required interface.");
606             throw new ServiceException("", errorBuffer.toString());
607         }
608         
609         DNSService dnsServer = (DNSService) manager.lookup(DNSService.ROLE);
610         setDNSServer(dnsServer);
611         
612         UsersRepository usersRepository =
613             (UsersRepository) manager.lookup(UsersRepository.ROLE);
614         setLocalUsers(usersRepository);
615     }
616 
617 
618 
619             
620 
621 
622     /**
623      * Sets the fetching.
624      * @param fetching The fetching to set
625      */
626     protected void setFetching(boolean fetching)
627     {
628         fieldFetching = fetching;
629     }
630 
631     /**
632      * Returns the server.
633      * @return MailServer
634      */
635     protected MailServer getServer()
636     {
637         return fieldServer;
638     }
639 
640     /**
641      * Returns the configuration.
642      * @return ParsedConfiguration
643      */
644     protected ParsedConfiguration getConfiguration()
645     {
646         return fieldConfiguration;
647     }
648 
649     /**
650      * Sets the configuration.
651      * @param configuration The configuration to set
652      */
653     protected void setConfiguration(ParsedConfiguration configuration)
654     {
655         fieldConfiguration = configuration;
656     }
657 
658     /**
659      * Sets the server.
660      * @param server The server to set
661      */
662     protected void setServer(MailServer server)
663     {
664         fieldServer = server;
665     }
666     
667     /**
668      * Returns the localUsers.
669      * @return UsersRepository
670      */
671     protected UsersRepository getLocalUsers()
672     {
673         return fieldLocalUsers;
674     }
675     
676     /**
677      * Sets the localUsers.
678      * @param localUsers The localUsers to set
679      */
680     protected void setLocalUsers(UsersRepository localUsers)
681     {
682         fieldLocalUsers = localUsers;
683     }
684     
685     /**
686      * Returns the DNSService.
687      * @return DNSService 
688      */
689     protected DNSService getDNSServer()
690     {
691         return dnsServer;
692     }
693     
694     /**
695      * Sets the DNSService.
696      * @param dnsServer The DNSService to set
697      */
698     protected void setDNSServer(DNSService dnsServer)
699     {
700         this.dnsServer = dnsServer;
701     }
702 
703 
704     /**
705      * Returns the accounts. Initializes if required.
706      * @return List
707      */
708     protected List getStaticAccounts()
709     {
710         if (null == getStaticAccountsBasic())
711         {
712             updateStaticAccounts();
713             return getStaticAccounts();
714         }   
715         return fieldStaticAccounts;
716     }
717     
718     /**
719      * Returns the staticAccounts.
720      * @return List
721      */
722     private List getStaticAccountsBasic()
723     {
724         return fieldStaticAccounts;
725     }   
726 
727     /**
728      * Sets the accounts.
729      * @param accounts The accounts to set
730      */
731     protected void setStaticAccounts(List accounts)
732     {
733         fieldStaticAccounts = accounts;
734     }
735     
736     /**
737      * Updates the staticAccounts.
738      */
739     protected void updateStaticAccounts()
740     {
741         setStaticAccounts(computeStaticAccounts());
742     }
743     
744     /**
745      * Updates the ParsedDynamicAccountParameters.
746      */
747     protected void updateParsedDynamicAccountParameters()
748     {
749         setParsedDynamicAccountParameters(computeParsedDynamicAccountParameters());
750     }   
751     
752     /**
753      * Updates the dynamicAccounts.
754      */
755     protected void updateDynamicAccounts() throws ConfigurationException
756     {
757         setDynamicAccounts(computeDynamicAccounts());
758     }   
759     
760     /**
761      * Computes the staticAccounts.
762      */
763     protected List computeStaticAccounts()
764     {
765         return new ArrayList();
766     }
767     
768     /**
769      * Computes the ParsedDynamicAccountParameters.
770      */
771     protected List computeParsedDynamicAccountParameters()
772     {
773         return new ArrayList();
774     }   
775     
776     /**
777      * Computes the dynamicAccounts.
778      */
779     protected Map computeDynamicAccounts() throws ConfigurationException
780     {
781         Map newAccounts =
782             new HashMap(
783                 getLocalUsers().countUsers()
784                     * getParsedDynamicAccountParameters().size());
785         Map oldAccounts = getDynamicAccountsBasic();
786         if (null == oldAccounts)
787             oldAccounts = new HashMap(0);
788 
789         Iterator parameterIterator =
790             getParsedDynamicAccountParameters().iterator();
791 
792         // Process each ParsedDynamicParameters
793         while (parameterIterator.hasNext())
794         {
795             Map accounts =
796                 computeDynamicAccounts(
797                     oldAccounts,
798                     (ParsedDynamicAccountParameters) parameterIterator.next());
799             // Remove accounts from oldAccounts.
800             // This avoids an average 2*N increase in heapspace used as the 
801             // newAccounts are created. 
802             Iterator oldAccountsIterator = oldAccounts.keySet().iterator();
803             while (oldAccountsIterator.hasNext())
804             {
805                 if (accounts.containsKey(oldAccountsIterator.next()))
806                     oldAccountsIterator.remove();
807             }
808             // Add this parameter's accounts to newAccounts
809             newAccounts.putAll(accounts);
810         }
811         return newAccounts;
812     }
813     
814     /**
815      * Returns the dynamicAccounts. Initializes if required.
816      * @return Map
817      */
818     protected Map getDynamicAccounts() throws ConfigurationException
819     {
820         if (null == getDynamicAccountsBasic())
821         {
822             updateDynamicAccounts();
823             return getDynamicAccounts();
824         }   
825         return fieldDynamicAccounts;
826     }
827     
828     /**
829      * Returns the dynamicAccounts.
830      * @return Map
831      */
832     private Map getDynamicAccountsBasic()
833     {
834         return fieldDynamicAccounts;
835     }   
836 
837     /**
838      * Sets the dynamicAccounts.
839      * @param dynamicAccounts The dynamicAccounts to set
840      */
841     protected void setDynamicAccounts(Map dynamicAccounts)
842     {
843         fieldDynamicAccounts = dynamicAccounts;
844     }
845     
846     /**
847      * Compute the dynamicAccounts for the passed parameters.
848      * Accounts for existing users are copied and accounts for new users are 
849      * created.
850      * @param oldAccounts
851      * @param parameters
852      * @return Map - The current Accounts
853      * @throws ConfigurationException
854      */
855     protected Map computeDynamicAccounts(
856         Map oldAccounts,
857         ParsedDynamicAccountParameters parameters)
858         throws ConfigurationException
859     {
860         Map accounts = new HashMap(getLocalUsers().countUsers());
861         Iterator usersIterator = getLocalUsers().list();
862         while (usersIterator.hasNext())
863         {
864             String userName = (String) usersIterator.next();
865             DynamicAccountKey key =
866                 new DynamicAccountKey(userName, parameters.getSequenceNumber());
867             Account account = (Account) oldAccounts.get(key);
868             if (null == account)
869             {
870                 // Create a new DynamicAccount
871                 account =
872                     new DynamicAccount(
873                         parameters.getSequenceNumber(),
874                         getConfiguration(),
875                         userName,
876                         parameters.getUserPrefix(),
877                         parameters.getUserSuffix(),
878                         parameters.getPassword(),
879                         parameters.getRecipientPrefix(),
880                         parameters.getRecipientSuffix(),
881                         parameters.isIgnoreRecipientHeader(),
882                         parameters.getCustomRecipientHeader(),
883                         getSession());
884             }
885             accounts.put(key, account);
886         }
887         return accounts;
888     }
889     
890     /**
891      * Resets the dynamicAccounts.
892      */
893     protected void resetDynamicAccounts()
894     {
895         setDynamicAccounts(null);
896     }   
897 
898     /**
899      * Returns the ParsedDynamicAccountParameters.
900      * @return List
901      */
902     protected List getParsedDynamicAccountParameters()
903     {
904         if (null == getParsedDynamicAccountParametersBasic())
905         {
906             updateParsedDynamicAccountParameters();
907             return getParsedDynamicAccountParameters();
908         }   
909         return fieldParsedDynamicAccountParameters;
910     }
911     
912     /**
913      * Returns the ParsedDynamicAccountParameters.
914      * @return List
915      */
916     private List getParsedDynamicAccountParametersBasic()
917     {
918         return fieldParsedDynamicAccountParameters;
919     }   
920 
921     /**
922      * Sets the ParsedDynamicAccountParameters.
923      * @param parsedDynamicAccountParameters The ParsedDynamicAccountParameters to set
924      */
925     protected void setParsedDynamicAccountParameters(List parsedDynamicAccountParameters)
926     {
927         fieldParsedDynamicAccountParameters = parsedDynamicAccountParameters;
928     }
929 
930     /**
931      * Returns the session, lazily initialized if required.
932      * @return Session
933      */
934     protected Session getSession()
935     {
936         Session session = null;
937         if (null == (session = getSessionBasic()))
938         {
939             updateSession();
940             return getSession();
941         }    
942         return session;
943     }
944     
945     /**
946      * Returns the session.
947      * @return Session
948      */
949     private Session getSessionBasic()
950     {
951         return fieldSession;
952     }    
953 
954     /**
955      * Answers a new Session.
956      * @return Session
957      */
958     protected Session computeSession()
959     {
960         return Session.getInstance(System.getProperties());
961     }
962     
963     /**
964      * Updates the current Session.
965      */
966     protected void updateSession()
967     {
968         setSession(computeSession());
969     }    
970 
971     /**
972      * Sets the session.
973      * @param session The session to set
974      */
975     protected void setSession(Session session)
976     {
977         fieldSession = session;
978     }
979     
980     
981     /**
982      * Propogate any Session parameters in the configuration to the Session.
983      * @param configuration The configuration containing the parameters
984      * @throws ConfigurationException
985      */
986     protected void setSessionParameters(Configuration configuration)
987         throws ConfigurationException
988     {
989         Configuration javaMailProperties =
990             configuration.getChild("javaMailProperties", false);
991         if (null != javaMailProperties)
992         {
993             Properties properties = getSession().getProperties();
994             Configuration[] allProperties =
995                 javaMailProperties.getChildren("property");
996             for (int i = 0; i < allProperties.length; i++)
997             {
998                 properties.setProperty(
999                     allProperties[i].getAttribute("name"),
1000                     allProperties[i].getAttribute("value"));
1001                 if (getLogger().isDebugEnabled())
1002                 {
1003                     StringBuffer messageBuffer =
1004                         new StringBuffer("Set property name: ");
1005                     messageBuffer.append(allProperties[i].getAttribute("name"));
1006                     messageBuffer.append(" to: ");
1007                     messageBuffer.append(
1008                         allProperties[i].getAttribute("value"));
1009                     getLogger().debug(messageBuffer.toString());
1010                 }
1011             }
1012         }
1013     }    
1014 
1015 }