View Javadoc

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