KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > mailrepository > AvalonMailRepository


1 /***********************************************************************
2  * Copyright (c) 1999-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.mailrepository;
19
20 import org.apache.avalon.cornerstone.services.store.ObjectRepository;
21 import org.apache.avalon.cornerstone.services.store.Store;
22 import org.apache.avalon.cornerstone.services.store.StreamRepository;
23 import org.apache.avalon.framework.activity.Initializable;
24 import org.apache.avalon.framework.component.Component;
25 import org.apache.avalon.framework.component.ComponentException;
26 import org.apache.avalon.framework.component.ComponentManager;
27 import org.apache.avalon.framework.component.Composable;
28 import org.apache.avalon.framework.configuration.Configurable;
29 import org.apache.avalon.framework.configuration.Configuration;
30 import org.apache.avalon.framework.configuration.ConfigurationException;
31 import org.apache.avalon.framework.configuration.DefaultConfiguration;
32 import org.apache.avalon.framework.logger.AbstractLogEnabled;
33 import org.apache.james.core.MailImpl;
34 import org.apache.james.core.MimeMessageWrapper;
35 import org.apache.james.services.MailRepository;
36 import org.apache.james.util.Lock;
37
38 import java.io.OutputStream JavaDoc;
39 import java.util.*;
40 import javax.mail.MessagingException JavaDoc;
41
42 /**
43  * Implementation of a MailRepository on a FileSystem.
44  *
45  * Requires a configuration element in the .conf.xml file of the form:
46  * <repository destinationURL="file://path-to-root-dir-for-repository"
47  * type="MAIL"
48  * model="SYNCHRONOUS"/>
49  * Requires a logger called MailRepository.
50  *
51  * @version 1.0.0, 24/04/1999
52  */

53 public class AvalonMailRepository
54     extends AbstractLogEnabled
55     implements MailRepository, Component, Configurable, Composable, Initializable {
56
57     /**
58      * Whether 'deep debugging' is turned on.
59      */

60     protected final static boolean DEEP_DEBUG = false;
61
62     private static final String JavaDoc TYPE = "MAIL";
63
64     private Lock lock;
65     private Store store;
66     private StreamRepository sr;
67     private ObjectRepository or;
68     private String JavaDoc destination;
69     private Set keys;
70     private boolean fifo;
71
72     /**
73      * @see org.apache.avalon.framework.component.Composable#compose(ComponentManager)
74      */

75     public void compose( final ComponentManager componentManager )
76             throws ComponentException {
77         store = (Store)componentManager.
78         lookup( "org.apache.avalon.cornerstone.services.store.Store" );
79     }
80
81     /**
82      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
83      */

84     public void configure(Configuration conf) throws ConfigurationException {
85         destination = conf.getAttribute("destinationURL");
86         if (getLogger().isDebugEnabled()) {
87             getLogger().debug("AvalonMailRepository.destinationURL: " + destination);
88         }
89         String JavaDoc checkType = conf.getAttribute("type");
90         if (! (checkType.equals("MAIL") || checkType.equals("SPOOL")) ) {
91             String JavaDoc exceptionString = "Attempt to configure AvalonMailRepository as " +
92                                      checkType;
93             if (getLogger().isWarnEnabled()) {
94                 getLogger().warn(exceptionString);
95             }
96             throw new ConfigurationException(exceptionString);
97         }
98         fifo = conf.getAttributeAsBoolean("FIFO", false);
99         // ignore model
100
}
101
102     /**
103      * @see org.apache.avalon.framework.activity.Initializable#initialize()
104      */

105     public void initialize()
106             throws Exception JavaDoc {
107         try {
108             //prepare Configurations for object and stream repositories
109
DefaultConfiguration objectConfiguration
110                 = new DefaultConfiguration( "repository",
111                                             "generated:AvalonFileRepository.compose()" );
112
113             objectConfiguration.setAttribute("destinationURL", destination);
114             objectConfiguration.setAttribute("type", "OBJECT");
115             objectConfiguration.setAttribute("model", "SYNCHRONOUS");
116
117             DefaultConfiguration streamConfiguration
118                 = new DefaultConfiguration( "repository",
119                                             "generated:AvalonFileRepository.compose()" );
120
121             streamConfiguration.setAttribute( "destinationURL", destination );
122             streamConfiguration.setAttribute( "type", "STREAM" );
123             streamConfiguration.setAttribute( "model", "SYNCHRONOUS" );
124
125             sr = (StreamRepository) store.select(streamConfiguration);
126             or = (ObjectRepository) store.select(objectConfiguration);
127             lock = new Lock();
128             keys = Collections.synchronizedSet(new HashSet());
129
130
131             //Finds non-matching pairs and deletes the extra files
132
HashSet streamKeys = new HashSet();
133             for (Iterator i = sr.list(); i.hasNext(); ) {
134                 streamKeys.add(i.next());
135             }
136             HashSet objectKeys = new HashSet();
137             for (Iterator i = or.list(); i.hasNext(); ) {
138                 objectKeys.add(i.next());
139             }
140
141             Collection strandedStreams = (Collection)streamKeys.clone();
142             strandedStreams.removeAll(objectKeys);
143             for (Iterator i = strandedStreams.iterator(); i.hasNext(); ) {
144                 String JavaDoc key = (String JavaDoc)i.next();
145                 remove(key);
146             }
147
148             Collection strandedObjects = (Collection)objectKeys.clone();
149             strandedObjects.removeAll(streamKeys);
150             for (Iterator i = strandedObjects.iterator(); i.hasNext(); ) {
151                 String JavaDoc key = (String JavaDoc)i.next();
152                 remove(key);
153             }
154
155             //Next get a list from the object repository
156
// and use that for the list of keys
157
keys.clear();
158             for (Iterator i = or.list(); i.hasNext(); ) {
159                 keys.add(i.next());
160             }
161             if (getLogger().isDebugEnabled()) {
162                 StringBuffer JavaDoc logBuffer =
163                     new StringBuffer JavaDoc(128)
164                             .append(this.getClass().getName())
165                             .append(" created in ")
166                             .append(destination);
167                 getLogger().debug(logBuffer.toString());
168             }
169         } catch (Exception JavaDoc e) {
170             final String JavaDoc message = "Failed to retrieve Store component:" + e.getMessage();
171             getLogger().error( message, e );
172             throw e;
173         }
174     }
175
176     /**
177      * Releases a lock on a message identified by a key
178      *
179      * @param key the key of the message to be unlocked
180      *
181      * @return true if successfully released the lock, false otherwise
182      */

183     public boolean unlock(String JavaDoc key) {
184         if (lock.unlock(key)) {
185             if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
186                 StringBuffer JavaDoc debugBuffer =
187                     new StringBuffer JavaDoc(256)
188                             .append("Unlocked ")
189                             .append(key)
190                             .append(" for ")
191                             .append(Thread.currentThread().getName())
192                             .append(" @ ")
193                             .append(new java.util.Date JavaDoc(System.currentTimeMillis()));
194                 getLogger().debug(debugBuffer.toString());
195             }
196 // synchronized (this) {
197
// notifyAll();
198
// }
199
return true;
200         } else {
201             return false;
202         }
203     }
204
205     /**
206      * Obtains a lock on a message identified by a key
207      *
208      * @param key the key of the message to be locked
209      *
210      * @return true if successfully obtained the lock, false otherwise
211      */

212     public boolean lock(String JavaDoc key) {
213         if (lock.lock(key)) {
214             if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
215                 StringBuffer JavaDoc debugBuffer =
216                     new StringBuffer JavaDoc(256)
217                             .append("Locked ")
218                             .append(key)
219                             .append(" for ")
220                             .append(Thread.currentThread().getName())
221                             .append(" @ ")
222                             .append(new java.util.Date JavaDoc(System.currentTimeMillis()));
223                 getLogger().debug(debugBuffer.toString());
224             }
225 // synchronized (this) {
226
// notifyAll();
227
// }
228
return true;
229         } else {
230             return false;
231         }
232     }
233
234     /**
235      * Stores a message in this repository. Shouldn't this return the key
236      * under which it is stored?
237      *
238      * @param mc the mail message to store
239      */

240     public void store(MailImpl mc) throws MessagingException JavaDoc {
241         try {
242             String JavaDoc key = mc.getName();
243             //Remember whether this key was locked
244
boolean wasLocked = lock.isLocked(key);
245
246             if (!wasLocked) {
247                 //If it wasn't locked, we want a lock during the store
248
lock.lock(key);
249             }
250             try {
251                 if (!keys.contains(key)) {
252                     keys.add(key);
253                 }
254                 boolean saveStream = true;
255
256                 if (mc.getMessage() instanceof MimeMessageWrapper) {
257                     MimeMessageWrapper wrapper = (MimeMessageWrapper) mc.getMessage();
258                     if (DEEP_DEBUG) {
259                         System.out.println("Retrieving from: " + wrapper.getSourceId());
260                         StringBuffer JavaDoc debugBuffer =
261                             new StringBuffer JavaDoc(64)
262                                     .append("Saving to: ")
263                                     .append(destination)
264                                     .append("/")
265                                     .append(mc.getName());
266                         System.out.println(debugBuffer.toString());
267                         System.out.println("Modified: " + wrapper.isModified());
268                     }
269                     StringBuffer JavaDoc destinationBuffer =
270                         new StringBuffer JavaDoc(128)
271                             .append(destination)
272                             .append("/")
273                             .append(mc.getName());
274                     if (wrapper.getSourceId().equals(destinationBuffer.toString()) && !wrapper.isModified()) {
275                         //We're trying to save to the same place, and it's not modified... we shouldn't save.
276
//More importantly, if we try to save, we will create a 0-byte file since we're
277
//retrying to retrieve from a file we'll be overwriting.
278
saveStream = false;
279                     }
280                 }
281                 if (saveStream) {
282                     OutputStream JavaDoc out = null;
283                     try {
284                         out = sr.put(key);
285                         mc.writeMessageTo(out);
286                     } finally {
287                         if (out != null) out.close();
288                     }
289                 }
290                 //Always save the header information
291
or.put(key, mc);
292             } finally {
293                 if (!wasLocked) {
294                     //If it wasn't locked, we need to now unlock
295
lock.unlock(key);
296                 }
297             }
298
299             if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
300                 StringBuffer JavaDoc logBuffer =
301                     new StringBuffer JavaDoc(64)
302                             .append("Mail ")
303                             .append(key)
304                             .append(" stored.");
305                 getLogger().debug(logBuffer.toString());
306             }
307
308             synchronized (this) {
309 // notifyAll();
310
notify();
311             }
312         } catch (Exception JavaDoc e) {
313             getLogger().error("Exception storing mail: " + e);
314             throw new MessagingException JavaDoc("Exception caught while storing Message Container: " + e);
315         }
316     }
317
318     /**
319      * Retrieves a message given a key. At the moment, keys can be obtained
320      * from list() in superinterface Store.Repository
321      *
322      * @param key the key of the message to retrieve
323      * @return the mail corresponding to this key, null if none exists
324      */

325     public MailImpl retrieve(String JavaDoc key) throws MessagingException JavaDoc {
326         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
327             getLogger().debug("Retrieving mail: " + key);
328         }
329         try {
330             MailImpl mc = null;
331             try {
332                 mc = (MailImpl) or.get(key);
333             } catch (RuntimeException JavaDoc re) {
334                 StringBuffer JavaDoc exceptionBuffer =
335                     new StringBuffer JavaDoc(128)
336                             .append("Exception retrieving mail: ")
337                             .append(re.toString())
338                             .append(", so we're deleting it... good riddance!");
339                 getLogger().debug(exceptionBuffer.toString());
340                 remove(key);
341                 return null;
342             }
343             MimeMessageAvalonSource source = new MimeMessageAvalonSource(sr, destination, key);
344             mc.setMessage(new MimeMessageWrapper(source));
345
346             return mc;
347         } catch (Exception JavaDoc me) {
348             getLogger().error("Exception retrieving mail: " + me);
349             throw new MessagingException JavaDoc("Exception while retrieving mail: " + me.getMessage());
350         }
351     }
352
353     /**
354      * Removes a specified message
355      *
356      * @param mail the message to be removed from the repository
357      */

358     public void remove(MailImpl mail) throws MessagingException JavaDoc {
359         remove(mail.getName());
360     }
361
362
363     /**
364      * Removes a Collection of mails from the repository
365      * @param mails The Collection of <code>MailImpl</code>'s to delete
366      * @throws MessagingException
367      * @since 2.2.0
368      */

369     public void remove(Collection mails) throws MessagingException JavaDoc {
370         Iterator delList = mails.iterator();
371         while (delList.hasNext()) {
372             remove((MailImpl)delList.next());
373         }
374     }
375
376     /**
377      * Removes a message identified by key.
378      *
379      * @param key the key of the message to be removed from the repository
380      */

381     public void remove(String JavaDoc key) throws MessagingException JavaDoc {
382         if (lock(key)) {
383             try {
384                 keys.remove(key);
385                 sr.remove(key);
386                 or.remove(key);
387             } finally {
388                 unlock(key);
389             }
390         } else {
391             StringBuffer JavaDoc exceptionBuffer =
392                 new StringBuffer JavaDoc(64)
393                         .append("Cannot lock ")
394                         .append(key)
395                         .append(" to remove it");
396             throw new MessagingException JavaDoc(exceptionBuffer.toString());
397         }
398     }
399
400     /**
401      * List string keys of messages in repository.
402      *
403      * @return an <code>Iterator</code> over the list of keys in the repository
404      *
405      */

406     public Iterator list() {
407         // Fix ConcurrentModificationException by cloning
408
// the keyset before getting an iterator
409
final ArrayList clone;
410         synchronized(keys) {
411             clone = new ArrayList(keys);
412         }
413         if (fifo) Collections.sort(clone); // Keys is a HashSet; impose FIFO for apps that need it
414
return clone.iterator();
415     }
416 }
417
Popular Tags