KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > core > AvalonMailStore


1 /***********************************************************************
2  * Copyright (c) 2000-2004 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.core;
19
20 import org.apache.avalon.framework.activity.Initializable;
21 import org.apache.avalon.framework.component.Component;
22 import org.apache.avalon.framework.component.ComponentException;
23 import org.apache.avalon.framework.component.ComponentManager;
24 import org.apache.avalon.framework.component.Composable;
25 import org.apache.avalon.framework.configuration.Configurable;
26 import org.apache.avalon.framework.configuration.Configuration;
27 import org.apache.avalon.framework.configuration.ConfigurationException;
28 import org.apache.avalon.framework.configuration.DefaultConfiguration;
29 import org.apache.avalon.framework.context.Context;
30 import org.apache.avalon.framework.context.ContextException;
31 import org.apache.avalon.framework.context.Contextualizable;
32 import org.apache.avalon.framework.logger.AbstractLogEnabled;
33 import org.apache.avalon.framework.logger.LogEnabled;
34 import org.apache.james.services.MailRepository;
35 import org.apache.james.services.MailStore;
36 import org.apache.james.services.SpoolRepository;
37
38 import java.util.HashMap JavaDoc;
39
40 /**
41  * Provides a registry of mail repositories. A mail repository is uniquely
42  * identified by its destinationURL, type and model.
43  *
44  */

45 public class AvalonMailStore
46     extends AbstractLogEnabled
47     implements Contextualizable, Composable, Configurable, Initializable, MailStore {
48
49     // Prefix for repository names
50
private static final String JavaDoc REPOSITORY_NAME = "Repository";
51
52     // Static variable used to name individual repositories. Should only
53
// be accessed when a lock on the AvalonMailStore.class is held
54
private static long id;
55
56     // map of [destinationURL + type]->Repository
57
private HashMap JavaDoc repositories;
58
59     // map of [protocol(destinationURL) + type ]->classname of repository;
60
private HashMap JavaDoc classes;
61
62     // map of [protocol(destinationURL) + type ]->default config for repository.
63
private HashMap JavaDoc defaultConfigs;
64
65     /**
66      * The Avalon context used by the instance
67      */

68     protected Context context;
69
70     /**
71      * The Avalon configuration used by the instance
72      */

73     protected Configuration configuration;
74
75     /**
76      * The Avalon component manager used by the instance
77      */

78     protected ComponentManager componentManager;
79
80     private SpoolRepository inboundSpool;
81
82     /**
83      * @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
84      */

85     public void contextualize(final Context context)
86             throws ContextException {
87         this.context = context;
88     }
89
90     /**
91      * @see org.apache.avalon.framework.component.Composable#compose(ComponentManager)
92      */

93     public void compose( final ComponentManager componentManager )
94         throws ComponentException
95     {
96         this.componentManager = componentManager;
97     }
98
99     /**
100      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
101      */

102     public void configure( final Configuration configuration )
103         throws ConfigurationException
104     {
105         this.configuration = configuration;
106     }
107
108     /**
109      * @see org.apache.avalon.framework.activity.Initializable#initialize()
110      */

111     public void initialize()
112         throws Exception JavaDoc {
113
114         getLogger().info("JamesMailStore init...");
115         repositories = new HashMap JavaDoc();
116         classes = new HashMap JavaDoc();
117         defaultConfigs = new HashMap JavaDoc();
118         Configuration[] registeredClasses
119             = configuration.getChild("repositories").getChildren("repository");
120         for ( int i = 0; i < registeredClasses.length; i++ )
121         {
122             registerRepository((Configuration) registeredClasses[i]);
123         }
124
125
126         Configuration spoolRepConf
127           = configuration.getChild("spoolRepository").getChild("repository");
128         try {
129            inboundSpool = (SpoolRepository) select(spoolRepConf);
130         } catch (Exception JavaDoc e) {
131             getLogger().error("Cannot open private SpoolRepository");
132             throw e;
133         }
134         if (getLogger().isInfoEnabled()) {
135             getLogger().info("SpoolRepository inboundSpool opened: "
136                               + inboundSpool.hashCode());
137             getLogger().info("James MailStore ...init");
138         }
139     }
140
141     /**
142      * <p>Registers a new mail repository type in the mail store's
143      * registry based upon a passed in <code>Configuration</code> object.</p>
144      *
145      * <p>This is presumably synchronized to prevent corruption of the
146      * internal registry.</p>
147      *
148      * @param repConf the Configuration object used to register the
149      * repository
150      *
151      * @throws ConfigurationException if an error occurs accessing the
152      * Configuration object
153      */

154     public synchronized void registerRepository(Configuration repConf)
155         throws ConfigurationException {
156         String JavaDoc className = repConf.getAttribute("class");
157         boolean infoEnabled = getLogger().isInfoEnabled();
158         Configuration[] protocols
159             = repConf.getChild("protocols").getChildren("protocol");
160         Configuration[] types = repConf.getChild("types").getChildren("type");
161         for ( int i = 0; i < protocols.length; i++ )
162         {
163             String JavaDoc protocol = protocols[i].getValue();
164
165             // Get the default configuration for these protocol/type combinations.
166
Configuration defConf = repConf.getChild("config");
167
168             for ( int j = 0; j < types.length; j++ )
169             {
170                 String JavaDoc type = types[j].getValue();
171                 String JavaDoc key = protocol + type ;
172                 if (infoEnabled) {
173                     StringBuffer JavaDoc infoBuffer =
174                         new StringBuffer JavaDoc(128)
175                             .append("Registering Repository instance of class ")
176                             .append(className)
177                             .append(" to handle ")
178                             .append(protocol)
179                             .append(" protocol requests for repositories of type ")
180                             .append(type);
181                     getLogger().info(infoBuffer.toString());
182                 }
183                 if (classes.get(key) != null) {
184                     throw new ConfigurationException("The combination of protocol and type comprise a unique key for repositories. This constraint has been violated. Please check your repository configuration.");
185                 }
186                 classes.put(key, className);
187                 if (defConf != null) {
188                     defaultConfigs.put(key, defConf);
189                 }
190             }
191         }
192
193     }
194
195     /**
196      * This method accept a Configuration object as hint and return the
197      * corresponding MailRepository.
198      * The Configuration must be in the form of:
199      * <repository destinationURL="[URL of this mail repository]"
200      * type="[repository type ex. OBJECT or STREAM or MAIL etc.]"
201      * model="[repository model ex. PERSISTENT or CACHE etc.]">
202      * [addition configuration]
203      * </repository>
204      *
205      * @param hint the Configuration object used to look up the repository
206      *
207      * @return the selected repository
208      *
209      * @throws ComponentException if any error occurs while parsing the
210      * Configuration or retrieving the
211      * MailRepository
212      */

213     public synchronized Component select(Object JavaDoc hint) throws ComponentException {
214         Configuration repConf = null;
215         try {
216             repConf = (Configuration) hint;
217         } catch (ClassCastException JavaDoc cce) {
218             throw new ComponentException(
219                 "hint is of the wrong type. Must be a Configuration", cce);
220         }
221         String JavaDoc destination = null;
222         String JavaDoc protocol = null;
223         try {
224             destination = repConf.getAttribute("destinationURL");
225             int idx = destination.indexOf(':');
226             if ( idx == -1 )
227                 throw new ComponentException(
228                     "destination is malformed. Must be a valid URL: "
229                     + destination);
230             protocol = destination.substring(0,idx);
231         } catch (ConfigurationException ce) {
232             throw new ComponentException(
233                 "Malformed configuration has no destinationURL attribute", ce);
234         }
235
236         try
237         {
238             String JavaDoc type = repConf.getAttribute("type");
239             String JavaDoc repID = destination + type;
240             MailRepository reply = (MailRepository) repositories.get(repID);
241             StringBuffer JavaDoc logBuffer = null;
242             if (reply != null) {
243                 if (getLogger().isDebugEnabled()) {
244                     logBuffer =
245                         new StringBuffer JavaDoc(128)
246                                 .append("obtained repository: ")
247                                 .append(repID)
248                                 .append(",")
249                                 .append(reply.getClass());
250                     getLogger().debug(logBuffer.toString());
251                 }
252                 return (Component)reply;
253             } else {
254                 String JavaDoc key = protocol + type;
255                 String JavaDoc repClass = (String JavaDoc) classes.get( key );
256
257                 if (getLogger().isDebugEnabled()) {
258                     logBuffer =
259                         new StringBuffer JavaDoc(128)
260                                 .append("obtained repository: ")
261                                 .append(repClass)
262                                 .append(" to handle: ")
263                                 .append(protocol)
264                                 .append(",")
265                                 .append(type);
266                     getLogger().debug( logBuffer.toString() );
267                 }
268
269                 // If default values have been set, create a new repository
270
// configuration element using the default values
271
// and the values in the selector.
272
// If no default values, just use the selector.
273
Configuration config;
274                 Configuration defConf = (Configuration)defaultConfigs.get(key);
275                 if ( defConf == null) {
276                     config = repConf;
277                 }
278                 else {
279                     config = new DefaultConfiguration(repConf.getName(),
280                                                       repConf.getLocation());
281                     copyConfig(defConf, (DefaultConfiguration)config);
282                     copyConfig(repConf, (DefaultConfiguration)config);
283                 }
284
285                 try {
286                     reply = (MailRepository) this.getClass().getClassLoader().loadClass(repClass).newInstance();
287                     if (reply instanceof LogEnabled) {
288                        setupLogger(reply);
289                     }
290                     if (reply instanceof Contextualizable) {
291                         ((Contextualizable) reply).contextualize(context);
292                     }
293                     if (reply instanceof Composable) {
294                         ((Composable) reply).compose( componentManager );
295                     }
296                     if (reply instanceof Configurable) {
297                         ((Configurable) reply).configure(config);
298                     }
299                     if (reply instanceof Initializable) {
300                         ((Initializable) reply).initialize();
301                     }
302                     repositories.put(repID, reply);
303                     if (getLogger().isInfoEnabled()) {
304                         logBuffer =
305                             new StringBuffer JavaDoc(128)
306                                 .append("added repository: ")
307                                 .append(repID)
308                                 .append("->")
309                                 .append(repClass);
310                         getLogger().info(logBuffer.toString());
311                     }
312                     return (Component)reply;
313                 } catch (Exception JavaDoc e) {
314                     if (getLogger().isWarnEnabled()) {
315                         getLogger().warn( "Exception while creating repository:" +
316                                           e.getMessage(), e );
317                     }
318                     throw new
319                         ComponentException("Cannot find or init repository",
320                                            e);
321                 }
322             }
323         } catch( final ConfigurationException ce ) {
324             throw new ComponentException( "Malformed configuration", ce );
325         }
326     }
327
328     /**
329      * <p>Returns a new name for a repository.</p>
330      *
331      * <p>Synchronized on the AvalonMailStore.class object to ensure
332      * against duplication of the repository name</p>
333      *
334      * @return a new repository name
335      */

336     public static final String JavaDoc getName() {
337         synchronized (AvalonMailStore.class) {
338             return REPOSITORY_NAME + id++;
339         }
340     }
341
342     /**
343      * Returns the mail spool associated with this AvalonMailStore
344      *
345      * @return the mail spool
346      *
347      * @throws IllegalStateException if the inbound spool has not
348      * yet been set
349      */

350     public SpoolRepository getInboundSpool() {
351         if (inboundSpool != null) {
352             return inboundSpool;
353         } else {
354             throw new IllegalStateException JavaDoc("Inbound spool not defined");
355         }
356     }
357
358     /**
359      * Returns whether the mail store has a repository corresponding to
360      * the passed in hint.
361      *
362      * @param hint the Configuration object used to look up the repository
363      *
364      * @return whether the mail store has a repository corresponding to this hint
365      */

366     public boolean hasComponent( Object JavaDoc hint ) {
367         Component comp = null;
368         try {
369             comp = select(hint);
370         } catch(ComponentException ex) {
371             if (getLogger().isErrorEnabled()) {
372                 getLogger().error("Exception AvalonMailStore.hasComponent-" + ex.toString());
373             }
374         }
375         return (comp != null);
376     }
377
378     /**
379      * Copies values from one config into another, overwriting duplicate attributes
380      * and merging children.
381      *
382      * @param fromConfig the Configuration to be copied
383      * @param toConfig the Configuration to which data is being copied
384      */

385     private void copyConfig(Configuration fromConfig, DefaultConfiguration toConfig)
386     {
387         // Copy attributes
388
String JavaDoc[] attrs = fromConfig.getAttributeNames();
389         for ( int i = 0; i < attrs.length; i++ ) {
390             String JavaDoc attrName = attrs[i];
391             String JavaDoc attrValue = fromConfig.getAttribute(attrName, null);
392             toConfig.setAttribute(attrName, attrValue);
393         }
394
395         // Copy children
396
Configuration[] children = fromConfig.getChildren();
397         for ( int i = 0; i < children.length; i++ ) {
398             Configuration child = children[i];
399             String JavaDoc childName = child.getName();
400             Configuration existingChild = toConfig.getChild(childName, false);
401             if ( existingChild == null ) {
402                 toConfig.addChild(child);
403             }
404             else {
405                 copyConfig(child, (DefaultConfiguration)existingChild);
406             }
407         }
408
409         // Copy value
410
String JavaDoc val = fromConfig.getValue(null);
411         if ( val != null ) {
412             toConfig.setValue(val);
413         }
414     }
415
416     /**
417      * Return the <code>Component</code> when you are finished with it. In this
418      * implementation it does nothing
419      *
420      * @param component The Component we are releasing.
421      */

422     public void release(Component component) {}
423 }
424
Popular Tags