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