You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-user@james.apache.org by Norman <nm...@spam-box.de> on 2006/12/17 10:39:35 UTC

****-SPAM-**** Re: mboxrd support

Hi Philip,

i opened a bug report:

http://issues.apache.org/jira/browse/JAMES-744

Thx for reporting.

bye
Norman

Philip Tomlinson schrieb:
> Hi,
>
> I think I've answered my own question and discovered a bug with
> MBoxMailRepository and a fix is below.
>
> Line 283
> was:
> prevMessageStart = ins.getFilePointer() - line.length() ;
> should be:
> prevMessageStart = ins.getFilePointer() - line.length() - 2;
> This is the case because we need to subtract -1 to back up in front of
> the character we are looking for and -1 because the readLine doesnt
> return the carriage return
> Now the preMessageStart points to the F character in the pattern From
> .......
>
> and line 413
> was:
> ins.seek(messageStart -1);
> should be:
> ins.seek(messageStart );
> Given that the messageStart now points at the F character there is no
> longer a need to back up one character.
>
> The full file is attached.
>
> Now I can parse Thunderbird mail files without a problem
>
> Rgds,
> Phil
>
>> I've been trying to use the MBoxMailRepository to parse a Thunderbird
>> mail file.
>> It doesnt seem to work propely. (After loading the messages, the
>> retrieve operation quite often doesnt return a message for a key).
>>
>> Is this because the MBoxMailRepository class doesnt support mboxrd?
>>
> -------------------------
> Email: phil@ez123.co.nz
>
>
> ------------------------------------------------------------------------
>
> /****************************************************************
>  * Licensed to the Apache Software Foundation (ASF) under one   *
>  * or more contributor license agreements.  See the NOTICE file *
>  * distributed with this work for additional information        *
>  * regarding copyright ownership.  The ASF licenses this file   *
>  * to you under the Apache License, Version 2.0 (the            *
>  * "License"); you may not use this file except in compliance   *
>  * with the License.  You may obtain a copy of the License at   *
>  *                                                              *
>  *   http://www.apache.org/licenses/LICENSE-2.0                 *
>  *                                                              *
>  * Unless required by applicable law or agreed to in writing,   *
>  * software distributed under the License is distributed on an  *
>  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
>  * KIND, either express or implied.  See the License for the    *
>  * specific language governing permissions and limitations      *
>  * under the License.                                           *
>  ****************************************************************/
>
>
>
> /* TODO:
>  *
>  * 1. Currently, iterating through the message collection does not
>  *    preserve the order in the file.  Change this with some form of
>  *    OrderedMap.  There is a suitable class in Jakarta Commons
>  *    Collections.
>  *
>  * 2. Optimize the remove operation.
>  *
>  * 3. Don't load entire message into memory.  This would mean computing
>  *    the hash during I/O streaming, rather than loading entire message
>  *    into memory, and using a MimeMessageWrapper with a suitable data
>  *    source.  As a strawman, the interface to MessageAction would
>  *    carry the hash, along with a size-limited stream providing the
>  *    message body.
>  *
>  * 4. Decide what to do when there are IDENTICAL messages in the file.
>  *    Right now only the last one will ever be processed, due to key
>  *    collissions.
>  *
>  * 5. isComplete()  - DONE.
>  *
>  * 6. Buffered I/O. - Partially done, and optional.
>  *
>  */
>
> package org.apache.james.mailrepository;
>
> import org.apache.avalon.framework.configuration.Configurable;
> import org.apache.avalon.framework.configuration.Configuration;
> import org.apache.avalon.framework.configuration.ConfigurationException;
> import org.apache.avalon.framework.logger.AbstractLogEnabled;
> import org.apache.james.core.MailImpl;
> import org.apache.james.services.MailRepository;
> import org.apache.mailet.Mail;
> import org.apache.oro.text.regex.MalformedPatternException;
> import org.apache.oro.text.regex.Perl5Compiler;
> import org.apache.oro.text.regex.Pattern;
> import org.apache.oro.text.regex.Perl5Matcher;
>
> import javax.mail.MessagingException;
> import javax.mail.Session;
> import javax.mail.internet.MimeMessage;
>
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.File;
> import java.io.FileNotFoundException;
> import java.io.IOException;
> import java.io.RandomAccessFile;
> import java.security.NoSuchAlgorithmException;
> import java.security.MessageDigest;
> import java.text.SimpleDateFormat;
> import java.util.ArrayList;
> import java.util.Calendar;
> import java.util.Collection;
> import java.util.Collections;
> import java.util.Hashtable;
> import java.util.Iterator;
> import java.util.Locale;
> import java.util.Properties;
> import java.util.Vector;
>
> /**
>  * Implementation of a MailRepository using UNIX mbox files.
>  *
>  * <p>Requires a configuration element in the .conf.xml file of the form:
>  *  <br>&lt;repository destinationURL="mbox://&lt;directory&gt;"
>  *  <br>            type="MAIL"
>  *  <br>&lt;/directory&gt; is where the individual mbox files are read from/written to
>  * <br>Type can ONLY be MAIL (SPOOL is NOT supported)
>  *
>  * <p>Requires a logger called MailRepository.
>  *
>  * <p> Implementation notes:
>  * <p>
>  * This class keeps an internal store of the mbox file
>  * When the internal mbox file is updated (added/deleted)
>  * then the file will be re-read from disk and then written back.
>  * This is a bit inefficent but means that the file on disk
>  * should be correct.
>  * <p>
>  * The mbox store is mainly meant to be used as a one-way street.
>  * Storing new emails is very fast (append to file) whereas reading them (via POP3) is
>  * slower (read from disk and parse).
>  * Therefore this implementation is best suited to people who wish to use the mbox format
>  * for taking data out of James and into something else (IMAP server or mail list displayer)
>  *
>  * @version CVS $Revision: 453945 $
>  */
>
>
> public class MBoxMailRepository
>         extends AbstractLogEnabled
>             implements MailRepository, Configurable {
>
>
>     static final SimpleDateFormat dy = new SimpleDateFormat("EE MMM dd HH:mm:ss yyyy", Locale.US);
>     static final String LOCKEXT = ".lock";
>     static final String WORKEXT = ".work";
>     static final int LOCKSLEEPDELAY = 2000; // 2 second back off in the event of a problem with the lock file
>     static final int MAXSLEEPTIMES = 100; //
>     static final long MLISTPRESIZEFACTOR = 10 * 1024;  // The hash table will be loaded with a initial capacity of  filelength/MLISTPRESIZEFACTOR
>     static final long DEFAULTMLISTCAPACITY = 20; // Set up a hashtable to have a meaningful default
>
>     /**
>      * Whether line buffering is turned used.
>      */
>     private static boolean BUFFERING = true;
>
>     /**
>      * Whether 'deep debugging' is turned on.
>      */
>     private static final boolean DEEP_DEBUG = true;
>
>     /**
>      * The internal list of the emails
>      * The key is an adapted MD5 checksum of the mail
>      */
>     private Hashtable mList = null;
>     /**
>      * The filename to read & write the mbox from/to
>      */
>     private String mboxFile;
>
>     private boolean fifo;
>     
>     /**
>      * A callback used when a message is read from the mbox file
>      */
>     public interface MessageAction {
>         public boolean isComplete();  // *** Not valid until AFTER each call to messageAction(...)!
>         public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart);
>     }
>
>
>     /**
>      * Convert a MimeMessage into raw text
>      * @param mc The mime message to convert
>      * @return A string representation of the mime message
>      * @throws IOException
>      * @throws MessagingException
>      */
>     private String getRawMessage(MimeMessage mc) throws IOException, MessagingException {
>
>         ByteArrayOutputStream rawMessage = new ByteArrayOutputStream();
>         mc.writeTo(rawMessage);
>         return rawMessage.toString();
>     }
>
>     /**
>      * Parse a text block as an email and convert it into a mime message
>      * @param emailBody The headers and body of an email. This will be parsed into a mime message and stored
>      */
>     private MimeMessage convertTextToMimeMessage(String emailBody) {
>         //this.emailBody = emailBody;
>         MimeMessage mimeMessage = null;
>         // Parse the mime message as we have the full message now (in string format)
>         ByteArrayInputStream mb = new ByteArrayInputStream(emailBody.getBytes());
>         Properties props = System.getProperties();
>         Session session = Session.getDefaultInstance(props);
>         try {
>             mimeMessage = new MimeMessage(session, mb);
>
>
>         } catch (MessagingException e) {
>             getLogger().error("Unable to parse mime message!", e);
>         }
>
>         if (mimeMessage == null && getLogger().isDebugEnabled()) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" Mime message is null");
>             getLogger().debug(logBuffer.toString());
>         }
>
>         /*
>         String toAddr = null;
>         try {
>             // Attempt to read the TO field and see if it errors
>             toAddr = mimeMessage.getRecipients(javax.mail.Message.RecipientType.TO).toString();
>         } catch (Exception e) {
>             // It has errored, so time for plan B
>             // use the from field I suppose
>             try {
>                 mimeMessage.setRecipients(javax.mail.Message.RecipientType.TO, mimeMessage.getFrom());
>                 if (getLogger().isDebugEnabled()) {
>                     StringBuffer logBuffer =
>                             new StringBuffer(128)
>                             .append(this.getClass().getName())
>                             .append(" Patching To: field for message ")
>                             .append(" with  From: field");
>                     getLogger().debug(logBuffer.toString());
>                 }
>             } catch (MessagingException e1) {
>                 getLogger().error("Unable to set to: field to from: field", e);
>             }
>         } */
>         return mimeMessage;
>     }
>
>     /**
>      * Generate a hex representation of an MD5 checksum on the emailbody
>      * @param emailBody
>      * @return A hex representation of the text
>      * @throws NoSuchAlgorithmException
>      */
>     private String generateKeyValue(String emailBody) throws NoSuchAlgorithmException {
>         // MD5 the email body for a reilable (ha ha) key
>         byte[] digArray = MessageDigest.getInstance("MD5").digest(emailBody.getBytes());
>         StringBuffer digest = new StringBuffer();
>         for (int i = 0; i < digArray.length; i++) {
>             digest.append(Integer.toString(digArray[i], Character.MAX_RADIX).toUpperCase(Locale.US));
>         }
>         return digest.toString();
>     }
>
>     /**
>      * Parse the mbox file.
>      * @param ins The random access file to load. Note that the file may or may not start at offset 0 in the file
>      * @param messAct The action to take when a message is found
>      */
>     private MimeMessage parseMboxFile(RandomAccessFile ins, MessageAction messAct) {
>         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" Start parsing ")
>                     .append(mboxFile);
>
>             getLogger().debug(logBuffer.toString());
>         }
>         try {
>
>             Perl5Compiler sepMatchCompiler = new Perl5Compiler();
>             Pattern sepMatchPattern = sepMatchCompiler.compile("^From (.*) (.*):(.*):(.*)$");
>             Perl5Matcher sepMatch = new Perl5Matcher();
>
>             int c;
>             boolean inMessage = false;
>             StringBuffer messageBuffer = new StringBuffer();
>             String previousMessageSeparator = null;
>             boolean foundSep = false;
>
>             long prevMessageStart = ins.getFilePointer();
>             if (BUFFERING) {
>             String line = null;
>             while ((line = ins.readLine()) != null) {
>                 foundSep = sepMatch.contains(line + "\n", sepMatchPattern);
>
>                 if (foundSep && inMessage) {
> //                    if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> //                        getLogger().debug(this.getClass().getName() + " Invoking " + messAct.getClass() + " at " + prevMessageStart);
> //                    }
>                     MimeMessage endResult = messAct.messageAction(previousMessageSeparator, messageBuffer.toString(), prevMessageStart);
>                     if (messAct.isComplete()) {
>                         // I've got what I want so just exit
>                         return endResult;
>                     }
>                     previousMessageSeparator = line;
>                     prevMessageStart = ins.getFilePointer() - line.length() - 2;
>                     messageBuffer = new StringBuffer();
>                     inMessage = true;
>                 }
>                 // Only done at the start (first header)
>                 if (foundSep && !inMessage) {
>                     previousMessageSeparator = line.toString();
>                     inMessage = true;
>                 }
>                 if (!foundSep && inMessage) {
>                     messageBuffer.append(line).append("\n");
>                 }
>             }
>             } else {
>             StringBuffer line = new StringBuffer();
>             while ((c = ins.read()) != -1) {
>                 if (c == 10) {
>                     foundSep = sepMatch.contains(line.toString(), sepMatchPattern);
>                     if (foundSep && inMessage) {
> //                        if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> //                            getLogger().debug(this.getClass().getName() + " Invoking " + messAct.getClass() + " at " + prevMessageStart);
> //                        }
>                         MimeMessage endResult = messAct.messageAction(previousMessageSeparator, messageBuffer.toString(), prevMessageStart);
>                         if (messAct.isComplete()) {
>                             // I've got what I want so just exit
>                             return endResult;
>                         }
>                         previousMessageSeparator = line.toString();
>                         prevMessageStart = ins.getFilePointer() - line.length();
>                         messageBuffer = new StringBuffer();
>                         inMessage = true;
>                     }
>                     // Only done at the start (first header)
>                     if (foundSep && inMessage == false) {
>                         previousMessageSeparator = line.toString();
>                         inMessage = true;
>                     }
>                     if (!foundSep) {
>                         messageBuffer.append(line).append((char) c);
>                     }
>                     line = new StringBuffer(); // Reset buffer
>                 } else {
>                     line.append((char) c);
>                 }
>             }
>             }
>
>             if (messageBuffer.length() != 0) {
>                 // process last message
>                 return messAct.messageAction(previousMessageSeparator, messageBuffer.toString(), prevMessageStart);
>             }
>         } catch (IOException ioEx) {
>             getLogger().error("Unable to write file (General I/O problem) " + mboxFile, ioEx);
>         } catch (MalformedPatternException e) {
>             getLogger().error("Bad regex passed " + mboxFile, e);
>         } finally {
>             if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>                 StringBuffer logBuffer =
>                         new StringBuffer(128)
>                         .append(this.getClass().getName())
>                         .append(" Finished parsing ")
>                         .append(mboxFile);
>
>                 getLogger().debug(logBuffer.toString());
>             }
>         }
>         return null;
>     }
>
>     /**
>      * Find a given message
>      * This method will first use selectMessage(key) to see if the key/offset combination allows us to skip
>      * parts of the file and only load the message we are interested in
>      *
>      * @param key The key of the message to find
>      */
>     private MimeMessage findMessage(String key) {
>         MimeMessage foundMessage = null;
>
>         // See if we can get the message by using the cache position first
>         foundMessage = selectMessage(key);
>         if (foundMessage == null) {
>             // If the message is not found something has changed from
>             // the cache.  The cache may have been invalidated by
>             // another method, or the file may have been replaced from
>             // underneath us.  Reload the cache, and try again.
>             mList = null;
>             loadKeys();
>             foundMessage = selectMessage(key);
>         }
>         return foundMessage;
>     }
>
>     /**
>      * Quickly find a message by using the stored message offsets
>      * @param key  The key of the message to find
>      */
>     private MimeMessage selectMessage(final String key) {
>         MimeMessage foundMessage = null;
>         // Can we find the key first
>         if (mList == null || !mList.containsKey(key)) {
>             // Not initiailised so no point looking
>             if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>                 StringBuffer logBuffer =
>                         new StringBuffer(128)
>                         .append(this.getClass().getName())
>                         .append(" mList - key not found ")
>                         .append(mboxFile);
>
>                 getLogger().debug(logBuffer.toString());
>             }
>             return foundMessage;
>         }
>         long messageStart = ((Long) mList.get(key)).longValue();
>         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" Load message starting at offset ")
>                     .append(messageStart)
>                     .append(" from file ")
>                     .append(mboxFile);
>
>             getLogger().debug(logBuffer.toString());
>         }
>         // Now try and find the position in the file
>         RandomAccessFile ins = null;
>         try {
>             ins = new RandomAccessFile(mboxFile, "r");
>             if (messageStart != 0) {
>                 ins.seek(messageStart );
>             }
>             MessageAction op = new MessageAction() {
>                 public boolean isComplete() { return true; }
>                 public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart) {
>                     try {
>                         if (key.equals(generateKeyValue(bodyText))) {
>                             getLogger().debug(this.getClass().getName() + " Located message. Returning MIME message");
>                             return convertTextToMimeMessage(bodyText);
>                         }
>                     } catch (NoSuchAlgorithmException e) {
>                         getLogger().error("MD5 not supported! ",e);
>                     }
>                     return null;
>                 }
>             };
>             foundMessage = this.parseMboxFile(ins, op);
>         } catch (FileNotFoundException e) {
>             getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
>         } catch (IOException e) {
>             getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
>         } finally {
>             if (foundMessage == null) {
>                 if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>                     StringBuffer logBuffer =
>                             new StringBuffer(128)
>                             .append(this.getClass().getName())
>                             .append(" select - message not found ")
>                             .append(mboxFile);
>
>                     getLogger().debug(logBuffer.toString());
>                 }
>             }
>             if (ins != null) try { ins.close(); } catch (IOException e) { getLogger().error("Unable to close file (General I/O problem) " + mboxFile, e); }
>         }
>         return foundMessage;
>     }
>
>     /**
>      * Load the message keys and file pointer offsets from disk
>      */
>     private synchronized void loadKeys() {
>         if (mList!=null) {
>             return;
>         }
>         RandomAccessFile ins = null;
>         try {
>             ins = new RandomAccessFile(mboxFile, "r");
>             long initialCapacity = (ins.length() >  MLISTPRESIZEFACTOR ? ins.length() /MLISTPRESIZEFACTOR  : 0);
>             if (initialCapacity < DEFAULTMLISTCAPACITY ) {
>                 initialCapacity =  DEFAULTMLISTCAPACITY;
>             }
>             if (initialCapacity > Integer.MAX_VALUE) {
>                 initialCapacity = Integer.MAX_VALUE - 1;
>             }
>             this.mList = new Hashtable((int)initialCapacity);
>             this.parseMboxFile(ins, new MessageAction() {
>                 public boolean isComplete() { return false; }
>                 public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart) {
>                     try {
>                         String key = generateKeyValue(bodyText);
>                         mList.put(key, new Long(messageStart));
>                         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>                             getLogger().debug(this.getClass().getName() + " Key " + key + " at " + messageStart);
>                         }
>                         
>                     } catch (NoSuchAlgorithmException e) {
>                         getLogger().error("MD5 not supported! ",e);
>                     }
>                     return null;
>                 }
>             });
>             //System.out.println("Done Load keys!");
>         } catch (FileNotFoundException e) {
>             getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
>             this.mList = new Hashtable((int)DEFAULTMLISTCAPACITY);
>         } catch (IOException e) {
>             getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
>         } finally {
>             if (ins != null) try { ins.close(); } catch (IOException e) { getLogger().error("Unable to close file (General I/O problem) " + mboxFile, e); }
>         }
>     }
>
>
>     /**
>      * @see org.apache.james.services.MailRepository#store(Mail)
>      */
>     public void store(Mail mc) {
>
>         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" Will store message to file ")
>                     .append(mboxFile);
>
>             getLogger().debug(logBuffer.toString());
>         }
>         this.mList = null;
>         // Now make up the from header
>         String fromHeader = null;
>         String message = null;
>         try {
>             message = getRawMessage(mc.getMessage());
>             // check for nullsender
>             if (mc.getMessage().getFrom() == null) {
>             fromHeader = "From   " + dy.format(Calendar.getInstance().getTime());
>             } else {
>             fromHeader = "From " + mc.getMessage().getFrom()[0] + " " + dy.format(Calendar.getInstance().getTime());
>             }
>             
>         } catch (IOException e) {
>             getLogger().error("Unable to parse mime message for " + mboxFile, e);
>         } catch (MessagingException e) {
>             getLogger().error("Unable to parse mime message for " + mboxFile, e);
>         }
>         // And save only the new stuff to disk
>         RandomAccessFile saveFile = null;
>         try {
>             saveFile = new RandomAccessFile(mboxFile, "rw");
>             saveFile.seek(saveFile.length()); // Move to the end
>             saveFile.writeBytes((fromHeader + "\n"));
>             saveFile.writeBytes((message + "\n"));
>             saveFile.close();
>
>         } catch (FileNotFoundException e) {
>             getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
>         } catch (IOException e) {
>             getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
>         }
>     }
>
>
>     /**
>      * @see org.apache.james.services.MailRepository#list()
>      */
>     public Iterator list() {
>         loadKeys();
>         ArrayList keys =  new ArrayList(mList.keySet());
>         
>         if (keys.isEmpty() == false) {
>             // find the first message.  This is a trick to make sure that if
>             // the file is changed out from under us, we will detect it and
>             // correct for it BEFORE we return the iterator.
>             findMessage((String) keys.iterator().next());
>         }
>         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" ")
>                     .append(keys.size())
>                     .append(" keys to be iterated over.");
>
>             getLogger().debug(logBuffer.toString());
>         }
>         if (fifo) Collections.sort(keys); // Keys is a HashSet; impose FIFO for apps that need it
>         return keys.iterator();
>     }
>
>     /**
>      * @see org.apache.james.services.MailRepository#retrieve(String)
>      */
>     public Mail retrieve(String key) {
>
>         loadKeys();
>         MailImpl res = null;
>         try {
>             MimeMessage foundMessage = findMessage(key);
>             if (foundMessage == null) {
>                 getLogger().error("found message is null!");
>                 return null;
>             }
>             res = new MailImpl(foundMessage);
>             res.setName(key);
>             if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>                 StringBuffer logBuffer =
>                         new StringBuffer(128)
>                         .append(this.getClass().getName())
>                         .append(" Retrieving entry for key ")
>                         .append(key);
>
>                 getLogger().debug(logBuffer.toString());
>             }
>         } catch (MessagingException e) {
>             getLogger().error("Unable to parse mime message for " + mboxFile + "\n" + e.getMessage(), e);
>         }
>         return res;
>     }
>
>     /**
>      * @see org.apache.james.services.MailRepository#remove(Mail)
>      */
>     public void remove(Mail mail) {
>         // Convert the message into a key
>         Vector delVec = new Vector();
>         delVec.addElement(mail);
>         remove(delVec);
>     }
>
>     /**
>      * Attempt to get a lock on the mbox by creating
>      * the file mboxname.lock
>      * @throws Exception
>      */
>     private void lockMBox() throws Exception {
>         // Create the lock file (if possible)
>         String lockFileName = mboxFile + LOCKEXT;
>         int sleepCount = 0;
>         File mBoxLock = new File(lockFileName);
>         if (!mBoxLock.createNewFile()) {
>             // This is not good, somebody got the lock before me
>             // So wait for a file
>             while (!mBoxLock.createNewFile() && sleepCount < MAXSLEEPTIMES) {
>                 try {
>                     if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>                         StringBuffer logBuffer =
>                                 new StringBuffer(128)
>                                 .append(this.getClass().getName())
>                                 .append(" Waiting for lock on file ")
>                                 .append(mboxFile);
>
>                         getLogger().debug(logBuffer.toString());
>                     }
>
>                     Thread.sleep(LOCKSLEEPDELAY);
>                     sleepCount++;
>                 } catch (InterruptedException e) {
>                     getLogger().error("File lock wait for " + mboxFile + " interrupted!",e);
>
>                 }
>             }
>             if (sleepCount >= MAXSLEEPTIMES) {
>                 throw new Exception("Unable to get lock on file " + mboxFile);
>             }
>         }
>     }
>
>     /**
>      * Unlock a previously locked mbox file
>      */
>     private void unlockMBox() {
>         // Just delete the MBOX file
>         String lockFileName = mboxFile + LOCKEXT;
>         File mBoxLock = new File(lockFileName);
>         if (!mBoxLock.delete()) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" Failed to delete lock file ")
>                     .append(lockFileName);
>             getLogger().error(logBuffer.toString());
>         }
>     }
>
>
>
>     /**
>      * @see org.apache.james.services.MailRepository#remove(Collection)
>      */
>     public void remove(final Collection mails)
>     {
>         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
>             StringBuffer logBuffer =
>                     new StringBuffer(128)
>                     .append(this.getClass().getName())
>                     .append(" Removing entry for key ")
>                     .append(mails);
>
>             getLogger().debug(logBuffer.toString());
>         }
>         // The plan is as follows:
>         // Attempt to locate the message in the file
>         // by reading through the
>         // once we've done that then seek to the file
>         try {
>             RandomAccessFile ins = new RandomAccessFile(mboxFile, "r"); // The source
>             final RandomAccessFile outputFile = new RandomAccessFile(mboxFile + WORKEXT, "rw"); // The destination
>             parseMboxFile(ins, new MessageAction() {
>                 public boolean isComplete() { return false; }
>                 public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart) {
>                     // Write out the messages as we go, until we reach the key we want
>                     try {
>                         String currentKey=generateKeyValue(bodyText);
>                         boolean foundKey=false;
>                         Iterator mailList = mails.iterator();
>                         String key;
>                         while (mailList.hasNext()) {
>                             // Attempt to find the current key in the array
>                             key = ((Mail)mailList.next()).getName();
>                             if (key.equals(currentKey)) {
>                                 // Don't write the message to disk
>                                 foundKey = true;
>                                 break;
>                             }
>                         }
>                         if (foundKey == false)
>                         {
>                             // We didn't find the key in the array so we will keep it
>                             outputFile.writeBytes(messageSeparator + "\n");
>                             outputFile.writeBytes(bodyText);
>
>                         }
>                     } catch (NoSuchAlgorithmException e) {
>                         getLogger().error("MD5 not supported! ",e);
>                     } catch (IOException e) {
>                         getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
>                     }
>                     return null;
>                 }
>             });
>             ins.close();
>             outputFile.close();
>             // Delete the old mbox file
>             File mbox = new File(mboxFile);
>             mbox.delete();
>             // And rename the lock file to be the new mbox
>             mbox = new File(mboxFile + WORKEXT);
>             if (!mbox.renameTo(new File(mboxFile)))
>             {
>                  System.out.println("Failed to rename file!");
>             }
>
>             // Now delete the keys in mails from the main hash
>             Iterator mailList = mails.iterator();
>             String key;
>             while (mailList.hasNext()) {
>                 // Attempt to find the current key in the array
>                 key = ((Mail)mailList.next()).getName();
>                 mList.remove(key);
>             }
>
>
>         } catch (FileNotFoundException e) {
>             getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
>         } catch (IOException e) {
>             getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
>         }
>     }
>
>     /**
>      * @see org.apache.james.services.MailRepository#remove(String)
>      */
>     public void remove(String key) {
>         loadKeys();
>         try {
>             lockMBox();
>         } catch (Exception e) {
>             getLogger().error("Lock failed!",e);
>             return; // No lock, so exit
>         }
>         ArrayList keys = new ArrayList();
>         keys.add(key);
>
>         this.remove(keys);
>         unlockMBox();
>     }
>
>     /**
>      * @see org.apache.james.services.MailRepository#lock(String)
>      */
>     public boolean lock(String key) {
>         return false;
>     }
>
>     /**
>      * @see org.apache.james.services.MailRepository#unlock(String)
>      */
>     public boolean unlock(String key) {
>         return false;
>     }
>
>     /**
>      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
>      */
>     public void configure(Configuration conf) throws ConfigurationException {
>         String destination;
>         this.mList = null;
>         BUFFERING = conf.getAttributeAsBoolean("BUFFERING", true);
>         fifo = conf.getAttributeAsBoolean("FIFO", false);
>         destination = conf.getAttribute("destinationURL");
>         if (destination.charAt(destination.length() - 1) == '/') {
>             // Remove the trailing / as well as the protocol marker
>             mboxFile = destination.substring("mbox://".length(), destination.lastIndexOf("/"));
>         } else {
>             mboxFile = destination.substring("mbox://".length());
>         }
>
>         if (getLogger().isDebugEnabled()) {
>             getLogger().debug("MBoxMailRepository.destinationURL: " + destination);
>         }
>
>         String checkType = conf.getAttribute("type");
>         if (!(checkType.equals("MAIL") || checkType.equals("SPOOL"))) {
>             String exceptionString = "Attempt to configure MboxMailRepository as " + checkType;
>             if (getLogger().isWarnEnabled()) {
>                 getLogger().warn(exceptionString);
>             }
>             throw new ConfigurationException(exceptionString);
>         }
>     }
>
> }
>
>   
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: server-user-unsubscribe@james.apache.org
> For additional commands, e-mail: server-user-help@james.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: server-user-unsubscribe@james.apache.org
For additional commands, e-mail: server-user-help@james.apache.org