[prev in list] [next in list] [prev in thread] [next in thread] 

List:       james-user
Subject:    Re: mboxrd support
From:       Philip Tomlinson <phil () ez123 ! co ! nz>
Date:       2006-12-17 1:42:16
Message-ID: 4584A078.7080702 () ez123 ! co ! nz
[Download RAW message or body]

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



["MBoxMailRepository.java" (text/plain)]

/****************************************************************
 * 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

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic