FileDocCategorySizeDatePackage
JDBCMailRepository.javaAPI DocApache James 2.3.145872Fri Jan 12 12:56:24 GMT 2007org.apache.james.mailrepository

JDBCMailRepository

public class JDBCMailRepository extends org.apache.avalon.framework.logger.AbstractLogEnabled implements org.apache.avalon.framework.service.Serviceable, org.apache.avalon.framework.activity.Initializable, org.apache.avalon.framework.context.Contextualizable, org.apache.avalon.framework.configuration.Configurable, org.apache.james.services.MailRepository
Implementation of a MailRepository on a database.

Requires a configuration element in the .conf.xml file of the form:
<repository destinationURL="db://<datasource>/<table_name>/<repository_name>"
type="MAIL"
model="SYNCHRONOUS"/>
</repository>

destinationURL specifies..(Serge??)
Type can be SPOOL or MAIL
Model is currently not used and may be dropped

Requires a logger called MailRepository.

version
CVS $Revision: 494012 $ $Date: 2007-01-08 11:23:58 +0100 (Mo, 08 Jan 2007) $

Fields Summary
private static final boolean
DEEP_DEBUG
Whether 'deep debugging' is turned on.
private org.apache.avalon.framework.service.ServiceManager
componentManager
The Avalon componentManager used by the instance
protected org.apache.avalon.framework.context.Context
context
The Avalon context used by the instance
private org.apache.james.util.Lock
lock
A lock used to control access to repository elements, locking access based on the key
protected String
tableName
The table name parsed from the destination URL
protected String
repositoryName
The repository name parsed from the destination URL
private String
sqlFileName
The name of the SQL configuration file to be used to configure this repository.
private org.apache.avalon.cornerstone.services.store.StreamRepository
sr
The stream repository used in dbfile mode
protected org.apache.avalon.cornerstone.services.datasources.DataSourceSelector
datasources
The selector used to obtain the JDBC datasource
protected org.apache.avalon.excalibur.datasource.DataSourceComponent
datasource
The JDBC datasource that provides the JDBC connection
protected String
datasourceName
The name of the datasource used by this repository
protected org.apache.james.util.SqlResources
sqlQueries
Contains all of the sql strings for this component.
protected org.apache.james.util.JDBCUtil
theJDBCUtil
The JDBCUtil helper class
protected boolean
jdbcMailAttributesReady
"Support for Mail Attributes under JDBC repositories is ready" indicator.
private int
inMemorySizeLimit
The size threshold for in memory handling of storing operations
Constructors Summary
Methods Summary
protected voidcheckJdbcAttributesSupport(java.sql.DatabaseMetaData dbMetaData)
Checks whether support for JDBC Mail atributes is activated for this repository and if everything is consistent. Looks for both the "updateMessageAttributesSQL" and "retrieveMessageAttributesSQL" statements in sqlResources and for a table column named "message_attributes".

param
dbMetaData the database metadata to be used to look up the column
throws
SQLException if a fatal situation is met

        String attributesColumnName = "message_attributes";
        boolean hasUpdateMessageAttributesSQL = false;
        boolean hasRetrieveMessageAttributesSQL = false;
        
        boolean hasMessageAttributesColumn = theJDBCUtil.columnExists(dbMetaData, tableName, attributesColumnName);
        
        StringBuffer logBuffer = new StringBuffer(64)
                                    .append("JdbcMailRepository '"
                                            + repositoryName
                                            + ", table '"
                                            + tableName
                                            + "': ");
        
        //Determine whether attributes are used and available for storing
        //Do we have updateMessageAttributesSQL?
        String updateMessageAttrSql =
            sqlQueries.getSqlString("updateMessageAttributesSQL", false);
        if (updateMessageAttrSql!=null) {
            hasUpdateMessageAttributesSQL = true;
        }
        
        //Determine whether attributes are used and retrieve them
        //Do we have retrieveAttributesSQL?
        String retrieveMessageAttrSql =
            sqlQueries.getSqlString("retrieveMessageAttributesSQL", false);
        if (retrieveMessageAttrSql!=null) {
            hasRetrieveMessageAttributesSQL = true;
        }
        
        if (hasUpdateMessageAttributesSQL && !hasRetrieveMessageAttributesSQL) {
            logBuffer.append("JDBC Mail Attributes support was activated for update but not for retrieval"
                             + "(found 'updateMessageAttributesSQL' but not 'retrieveMessageAttributesSQL'"
                             + "in table '"
                             + tableName
                             + "').");
            getLogger().fatalError(logBuffer.toString());
            throw new SQLException(logBuffer.toString());
        }
        if (!hasUpdateMessageAttributesSQL && hasRetrieveMessageAttributesSQL) {
            logBuffer.append("JDBC Mail Attributes support was activated for retrieval but not for update"
                             + "(found 'retrieveMessageAttributesSQL' but not 'updateMessageAttributesSQL'"
                             + "in table '"
                             + tableName
                             + "'.");
            getLogger().fatalError(logBuffer.toString());
            throw new SQLException(logBuffer.toString());
        }
        if (!hasMessageAttributesColumn
            && (hasUpdateMessageAttributesSQL || hasRetrieveMessageAttributesSQL)
            ) {
                logBuffer.append("JDBC Mail Attributes support was activated but column '"
                                 + attributesColumnName
                                 + "' is missing in table '"
                                 + tableName
                                 + "'.");
                getLogger().fatalError(logBuffer.toString());
                throw new SQLException(logBuffer.toString());
        }
        if (hasUpdateMessageAttributesSQL && hasRetrieveMessageAttributesSQL) {
            jdbcMailAttributesReady = true;
            if (getLogger().isInfoEnabled()) {
                logBuffer.append("JDBC Mail Attributes support ready.");
                getLogger().info(logBuffer.toString());
            }
        } else {
            jdbcMailAttributesReady = false;
            logBuffer.append("JDBC Mail Attributes support not activated. "
                             + "Missing both 'updateMessageAttributesSQL' "
                             + "and 'retrieveMessageAttributesSQL' "
                             + "statements for table '"
                             + tableName
                             + "' in sqlResources.xml. "
                             + "Will not persist in the repository '"
                             + repositoryName
                             + "'.");
            getLogger().warn(logBuffer.toString());
        }
    
public voidconfigure(org.apache.avalon.framework.configuration.Configuration conf)

see
org.apache.avalon.framework.configuration.Configurable#configure(Configuration)

        if (getLogger().isDebugEnabled()) {
            getLogger().debug(this.getClass().getName() + ".configure()");
        }

        String destination = conf.getAttribute("destinationURL");
        // normalize the destination, to simplify processing.
        if ( ! destination.endsWith("/") ) {
            destination += "/";
        }
        // Parse the DestinationURL for the name of the datasource,
        // the table to use, and the (optional) repository Key.
        // Split on "/", starting after "db://"
        List urlParams = new ArrayList();
        int start = 5;
        if (destination.startsWith("dbfile")) {
            //this is dbfile:// instead of db://
            start += 4;
        }
        int end = destination.indexOf('/", start);
        while ( end > -1 ) {
            urlParams.add(destination.substring(start, end));
            start = end + 1;
            end = destination.indexOf('/", start);
        }

        // Build SqlParameters and get datasource name from URL parameters
        if (urlParams.size() == 0) {
            StringBuffer exceptionBuffer =
                new StringBuffer(256)
                        .append("Malformed destinationURL - Must be of the format '")
                        .append("db://<data-source>[/<table>[/<repositoryName>]]'.  Was passed ")
                        .append(conf.getAttribute("destinationURL"));
            throw new ConfigurationException(exceptionBuffer.toString());
        }
        if (urlParams.size() >= 1) {
            datasourceName = (String)urlParams.get(0);
        }
        if (urlParams.size() >= 2) {
            tableName = (String)urlParams.get(1);
        }
        if (urlParams.size() >= 3) {
            repositoryName = "";
            for (int i = 2; i < urlParams.size(); i++) {
                if (i >= 3) {
                    repositoryName += '/";
                }
                repositoryName += (String)urlParams.get(i);
            }
        }

        if (getLogger().isDebugEnabled()) {
            StringBuffer logBuffer =
                new StringBuffer(128)
                        .append("Parsed URL: table = '")
                        .append(tableName)
                        .append("', repositoryName = '")
                        .append(repositoryName)
                        .append("'");
            getLogger().debug(logBuffer.toString());
        }
        
        inMemorySizeLimit = conf.getChild("inMemorySizeLimit").getValueAsInteger(409600000); 

        String filestore = conf.getChild("filestore").getValue(null);
        sqlFileName = conf.getChild("sqlFile").getValue();
        if (!sqlFileName.startsWith("file://")) {
            throw new ConfigurationException
                ("Malformed sqlFile - Must be of the format 'file://<filename>'.");
        }
        try {
            if (filestore != null) {
                Store store = (Store)componentManager.lookup(Store.ROLE);
                //prepare Configurations for stream repositories
                DefaultConfiguration streamConfiguration
                    = new DefaultConfiguration( "repository",
                                                "generated:JDBCMailRepository.configure()" );

                streamConfiguration.setAttribute( "destinationURL", filestore );
                streamConfiguration.setAttribute( "type", "STREAM" );
                streamConfiguration.setAttribute( "model", "SYNCHRONOUS" );
                sr = (StreamRepository) store.select(streamConfiguration);

                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Got filestore for JdbcMailRepository: " + filestore);
                }
            }

            lock = new Lock();
            if (getLogger().isDebugEnabled()) {
                StringBuffer logBuffer =
                    new StringBuffer(128)
                            .append(this.getClass().getName())
                            .append(" created according to ")
                            .append(destination);
                getLogger().debug(logBuffer.toString());
            }
        } catch (Exception e) {
            final String message = "Failed to retrieve Store component:" + e.getMessage();
            getLogger().error(message, e);
            throw new ConfigurationException(message, e);
        }
    
public voidcontextualize(org.apache.avalon.framework.context.Context context)

see
org.apache.avalon.framework.context.Contextualizable#contextualize(Context)


           
        
              
        this.context = context;
    
public booleanequals(java.lang.Object obj)

see
java.lang.Object#equals(Object)

        if (!(obj instanceof JDBCMailRepository)) {
            return false;
        }
        // TODO: Figure out whether other instance variables should be part of
        // the equals equation
        JDBCMailRepository repository = (JDBCMailRepository)obj;
        return  ((repository.tableName == tableName) || ((repository.tableName != null) && repository.tableName.equals(tableName))) && 
                ((repository.repositoryName == repositoryName) || ((repository.repositoryName != null) && repository.repositoryName.equals(repositoryName)));
    
protected java.sql.ConnectiongetConnection()
Gets the SQL connection to be used by this JDBCMailRepository

return
the connection
throws
SQLException if there is an issue with getting the connection

        return datasource.getConnection();
    
private intgetNumberOfParameters(java.lang.String sqlstring)
This method calculates number of parameters in a prepared statement SQL String. It does so by counting the number of '?' in the string

param
sqlstring to return parameter count for
return
number of parameters

        //it is alas a java 1.4 feature to be able to call
        //getParameterMetaData which could provide us with the parameterCount
        char[] chars = sqlstring.toCharArray();
        int count = 0;
        for (int i = 0; i < chars.length; i++) {
            count += chars[i]=='?" ? 1 : 0;
        }
        return count;
    
public inthashCode()
Provide a hash code that is consistent with equals for this class

return
the hash code

        int result = 17;
        if (tableName != null) {
            result = 37 * tableName.hashCode();
        }
        if (repositoryName != null) {
            result = 37 * repositoryName.hashCode();
        }
        return result;
     
public voidinitialize()
Initialises the JDBC repository. 1) Tests the connection to the database. 2) Loads SQL strings from the SQL definition file, choosing the appropriate SQL for this connection, and performing paramter substitution, 3) Initialises the database with the required tables, if necessary.

throws
Exception if an error occurs

        StringBuffer logBuffer = null;
        if (getLogger().isDebugEnabled()) {
            getLogger().debug(this.getClass().getName() + ".initialize()");
        }

        theJDBCUtil =
            new JDBCUtil() {
                protected void delegatedLog(String logString) {
                    JDBCMailRepository.this.getLogger().warn("JDBCMailRepository: " + logString);
                }
            };
        // Get the data-source required.
        datasource = (DataSourceComponent)datasources.select(datasourceName);

        // Test the connection to the database, by getting the DatabaseMetaData.
        Connection conn = datasource.getConnection();
        PreparedStatement createStatement = null;

        try {
            // Initialise the sql strings.

            File sqlFile = null;
            try {
                sqlFile = AvalonContextUtilities.getFile(context, sqlFileName);
                sqlFileName = null;
            } catch (Exception e) {
                getLogger().fatalError(e.getMessage(), e);
                throw e;
            }

            if (getLogger().isDebugEnabled()) {
                logBuffer =
                    new StringBuffer(128)
                            .append("Reading SQL resources from file: ")
                            .append(sqlFile.getAbsolutePath())
                            .append(", section ")
                            .append(this.getClass().getName())
                            .append(".");
                getLogger().debug(logBuffer.toString());
            }

            // Build the statement parameters
            Map sqlParameters = new HashMap();
            if (tableName != null) {
                sqlParameters.put("table", tableName);
            }
            if (repositoryName != null) {
                sqlParameters.put("repository", repositoryName);
            }

            sqlQueries = new SqlResources();
            sqlQueries.init(sqlFile, this.getClass().getName(),
                            conn, sqlParameters);

            // Check if the required table exists. If not, create it.
            DatabaseMetaData dbMetaData = conn.getMetaData();
            // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
            // Try UPPER, lower, and MixedCase, to see if the table is there.
            if (!(theJDBCUtil.tableExists(dbMetaData, tableName))) {
                // Users table doesn't exist - create it.
                createStatement =
                    conn.prepareStatement(sqlQueries.getSqlString("createTable", true));
                createStatement.execute();

                if (getLogger().isInfoEnabled()) {
                    logBuffer =
                        new StringBuffer(64)
                                .append("JdbcMailRepository: Created table '")
                                .append(tableName)
                                .append("'.");
                    getLogger().info(logBuffer.toString());
                }
            }
            
            checkJdbcAttributesSupport(dbMetaData);

        } finally {
            theJDBCUtil.closeJDBCStatement(createStatement);
            theJDBCUtil.closeJDBCConnection(conn);
        }
    
public java.util.Iteratorlist()
Gets a list of message keys stored in this repository.

return
an Iterator of the message keys

        //System.err.println("listing messages");
        Connection conn = null;
        PreparedStatement listMessages = null;
        ResultSet rsListMessages = null;
        try {
            conn = datasource.getConnection();
            listMessages =
                conn.prepareStatement(sqlQueries.getSqlString("listMessagesSQL", true));
            listMessages.setString(1, repositoryName);
            rsListMessages = listMessages.executeQuery();

            List messageList = new ArrayList();
            while (rsListMessages.next() && !Thread.currentThread().isInterrupted()) {
                messageList.add(rsListMessages.getString(1));
            }
            return messageList.iterator();
        } catch (Exception me) {
            throw new MessagingException("Exception while listing mail: " + me.getMessage());
        } finally {
            theJDBCUtil.closeJDBCResultSet(rsListMessages);
            theJDBCUtil.closeJDBCStatement(listMessages);
            theJDBCUtil.closeJDBCConnection(conn);
        }
    
public booleanlock(java.lang.String key)
Obtains a lock on a message identified by a key

param
key the key of the message to be locked
return
true if successfully obtained the lock, false otherwise

        if (lock.lock(key)) {
            if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
                StringBuffer debugBuffer =
                    new StringBuffer(256)
                            .append("Locked ")
                            .append(key)
                            .append(" for ")
                            .append(Thread.currentThread().getName())
                            .append(" @ ")
                            .append(new java.util.Date(System.currentTimeMillis()));
                getLogger().debug(debugBuffer.toString());
            }
            return true;
        } else {
            return false;
        }
    
public voidremove(org.apache.mailet.Mail mail)
Removes a specified message

param
mail the message to be removed from the repository

        remove(mail.getName());
    
public voidremove(java.util.Collection mails)
Removes a Collection of mails from the repository

param
mails The Collection of MailImpl's to delete
throws
MessagingException
since
2.2.0

        Iterator delList = mails.iterator();
        while (delList.hasNext()) {
            remove((Mail)delList.next());
        }
    
public voidremove(java.lang.String key)
Removes a message identified by a key.

param
key the key of the message to be removed from the repository

        //System.err.println("removing " + key);
        if (lock(key)) {
            Connection conn = null;
            PreparedStatement removeMessage = null;
            try {
                conn = datasource.getConnection();
                removeMessage =
                    conn.prepareStatement(sqlQueries.getSqlString("removeMessageSQL", true));
                removeMessage.setString(1, key);
                removeMessage.setString(2, repositoryName);
                removeMessage.execute();

                if (sr != null) {
                    sr.remove(key);
                }
            } catch (Exception me) {
                throw new MessagingException("Exception while removing mail: " + me.getMessage());
            } finally {
                theJDBCUtil.closeJDBCStatement(removeMessage);
                theJDBCUtil.closeJDBCConnection(conn);
                unlock(key);
            }
        }
    
public org.apache.mailet.Mailretrieve(java.lang.String key)
Retrieves a message given a key. At the moment, keys can be obtained from list()

param
key the key of the message to retrieve
return
the mail corresponding to this key, null if none exists

        if (DEEP_DEBUG) {
            System.err.println("retrieving " + key);
        }
        Connection conn = null;
        PreparedStatement retrieveMessage = null;
        ResultSet rsMessage = null;
        try {
            conn = datasource.getConnection();
            if (DEEP_DEBUG) {
                System.err.println("got a conn " + key);
            }

            retrieveMessage =
                conn.prepareStatement(sqlQueries.getSqlString("retrieveMessageSQL", true));
            retrieveMessage.setString(1, key);
            retrieveMessage.setString(2, repositoryName);
            rsMessage = retrieveMessage.executeQuery();
            if (DEEP_DEBUG) {
                System.err.println("ran the query " + key);
            }
            if (!rsMessage.next()) {
                if (getLogger().isDebugEnabled()) {
                    StringBuffer debugBuffer =
                        new StringBuffer(64)
                                .append("Did not find a record ")
                                .append(key)
                                .append(" in ")
                                .append(repositoryName);
                    getLogger().debug(debugBuffer.toString());
                }
                return null;
            }
            //Determine whether attributes are used and retrieve them
            PreparedStatement retrieveMessageAttr = null;
            HashMap attributes = null;
            if (jdbcMailAttributesReady) {
                String retrieveMessageAttrSql =
                    sqlQueries.getSqlString("retrieveMessageAttributesSQL", false);
                ResultSet rsMessageAttr = null;
                try {
                    retrieveMessageAttr =
                        conn.prepareStatement(retrieveMessageAttrSql);
                    
                    retrieveMessageAttr.setString(1, key);
                    retrieveMessageAttr.setString(2, repositoryName);
                    rsMessageAttr = retrieveMessageAttr.executeQuery();
                    
                    if (rsMessageAttr.next()) {
                        try {
                            byte[] serialized_attr = null;
                            String getAttributesOption = sqlQueries.getDbOption("getAttributes");
                            if (getAttributesOption != null && (getAttributesOption.equalsIgnoreCase("useBlob") || getAttributesOption.equalsIgnoreCase("useBinaryStream"))) {
                                Blob b = rsMessageAttr.getBlob(1);
                                serialized_attr = b.getBytes(1, (int)b.length());
                            } else {
                                serialized_attr = rsMessageAttr.getBytes(1);
                            }
                            // this check is for better backwards compatibility
                            if (serialized_attr != null) {
                                ByteArrayInputStream bais = new ByteArrayInputStream(serialized_attr);
                                ObjectInputStream ois = new ObjectInputStream(bais);
                                attributes = (HashMap)ois.readObject();
                                ois.close();
                            }
                        } catch (IOException ioe) {
                            if (getLogger().isDebugEnabled()) {
                                StringBuffer debugBuffer =
                                    new StringBuffer(64)
                                    .append("Exception reading attributes ")
                                    .append(key)
                                    .append(" in ")
                                    .append(repositoryName);
                                getLogger().debug(debugBuffer.toString(), ioe);
                            }
                        }
                    } else {
                        if (getLogger().isDebugEnabled()) {
                            StringBuffer debugBuffer =
                                new StringBuffer(64)
                                .append("Did not find a record (attributes) ")
                                .append(key)
                                .append(" in ")
                            .append(repositoryName);
                            getLogger().debug(debugBuffer.toString());
                        }
                    }
                } catch (SQLException sqle) {
                    StringBuffer errorBuffer =  new StringBuffer(256)
                                                .append("Error retrieving message")
                                                .append(sqle.getMessage())
                                                .append(sqle.getErrorCode())
                                                .append(sqle.getSQLState())
                                                .append(sqle.getNextException());
                    getLogger().error(errorBuffer.toString());
                } finally {
                    theJDBCUtil.closeJDBCResultSet(rsMessageAttr);
                    theJDBCUtil.closeJDBCStatement(retrieveMessageAttr);
                }
            }

            MailImpl mc = new MailImpl();
            mc.setAttributesRaw (attributes);
            mc.setName(key);
            mc.setState(rsMessage.getString(1));
            mc.setErrorMessage(rsMessage.getString(2));
            String sender = rsMessage.getString(3);
            if (sender == null) {
                mc.setSender(null);
            } else {
                mc.setSender(new MailAddress(sender));
            }
            StringTokenizer st = new StringTokenizer(rsMessage.getString(4), "\r\n", false);
            Set recipients = new HashSet();
            while (st.hasMoreTokens()) {
                recipients.add(new MailAddress(st.nextToken()));
            }
            mc.setRecipients(recipients);
            mc.setRemoteHost(rsMessage.getString(5));
            mc.setRemoteAddr(rsMessage.getString(6));
            mc.setLastUpdated(rsMessage.getTimestamp(7));

            MimeMessageJDBCSource source = new MimeMessageJDBCSource(this, key, sr);
            MimeMessageCopyOnWriteProxy message = new MimeMessageCopyOnWriteProxy(source);
            mc.setMessage(message);
            return mc;
        } catch (SQLException sqle) {
            StringBuffer errorBuffer =  new StringBuffer(256)
                                        .append("Error retrieving message")
                                        .append(sqle.getMessage())
                                        .append(sqle.getErrorCode())
                                        .append(sqle.getSQLState())
                                        .append(sqle.getNextException());
            getLogger().error(errorBuffer.toString());
            throw new MessagingException("Exception while retrieving mail: " + sqle.getMessage());
        } catch (Exception me) {
            throw new MessagingException("Exception while retrieving mail: " + me.getMessage());
        } finally {
            theJDBCUtil.closeJDBCResultSet(rsMessage);
            theJDBCUtil.closeJDBCStatement(retrieveMessage);
            theJDBCUtil.closeJDBCConnection(conn);
        }
    
public voidservice(org.apache.avalon.framework.service.ServiceManager componentManager)

see
org.apache.avalon.framework.service.Servicable#service(ServiceManager)

        StringBuffer logBuffer = null;
        if (getLogger().isDebugEnabled()) {
            logBuffer =
                new StringBuffer(64)
                        .append(this.getClass().getName())
                        .append(".compose()");
            getLogger().debug(logBuffer.toString());
        }
        // Get the DataSourceSelector service
        datasources = (DataSourceSelector)componentManager.lookup( DataSourceSelector.ROLE );
        this.componentManager = componentManager;

    
public voidstore(org.apache.mailet.Mail mc)
Store this message to the database. Optionally stores the message body to the filesystem and only writes the headers to the database.

        Connection conn = null;
        boolean wasLocked = true;
        String key = mc.getName();
        try {
            synchronized(this) {
                  wasLocked = lock.isLocked(key);
    
                  if (!wasLocked) {
                      //If it wasn't locked, we want a lock during the store
                      lock(key);
                  }
            }
            conn = datasource.getConnection();

            //Need to determine whether need to insert this record, or update it.

            //Begin a transaction
            conn.setAutoCommit(false);

            PreparedStatement checkMessageExists = null;
            ResultSet rsExists = null;
            boolean exists = false;
            try {
                checkMessageExists = 
                    conn.prepareStatement(sqlQueries.getSqlString("checkMessageExistsSQL", true));
                checkMessageExists.setString(1, mc.getName());
                checkMessageExists.setString(2, repositoryName);
                rsExists = checkMessageExists.executeQuery();
                exists = rsExists.next() && rsExists.getInt(1) > 0;
            } finally {
                theJDBCUtil.closeJDBCResultSet(rsExists);
                theJDBCUtil.closeJDBCStatement(checkMessageExists);
            }

            if (exists) {
                //Update the existing record
                PreparedStatement updateMessage = null;

                try {
                    updateMessage =
                        conn.prepareStatement(sqlQueries.getSqlString("updateMessageSQL", true));
                    updateMessage.setString(1, mc.getState());
                    updateMessage.setString(2, mc.getErrorMessage());
                    if (mc.getSender() == null) {
                        updateMessage.setNull(3, java.sql.Types.VARCHAR);
                    } else {
                        updateMessage.setString(3, mc.getSender().toString());
                    }
                    StringBuffer recipients = new StringBuffer();
                    for (Iterator i = mc.getRecipients().iterator(); i.hasNext(); ) {
                        recipients.append(i.next().toString());
                        if (i.hasNext()) {
                            recipients.append("\r\n");
                        }
                    }
                    updateMessage.setString(4, recipients.toString());
                    updateMessage.setString(5, mc.getRemoteHost());
                    updateMessage.setString(6, mc.getRemoteAddr());
                    updateMessage.setTimestamp(7, new java.sql.Timestamp(mc.getLastUpdated().getTime()));
                    updateMessage.setString(8, mc.getName());
                    updateMessage.setString(9, repositoryName);
                    updateMessage.execute();
                } finally {
                    Statement localUpdateMessage = updateMessage;
                    // Clear reference to statement
                    updateMessage = null;
                    theJDBCUtil.closeJDBCStatement(localUpdateMessage);
                }

                //Determine whether attributes are used and available for storing
                if (jdbcMailAttributesReady && mc.hasAttributes()) {
                    String updateMessageAttrSql =
                        sqlQueries.getSqlString("updateMessageAttributesSQL", false);
                    PreparedStatement updateMessageAttr = null;
                    try {
                        updateMessageAttr =
                            conn.prepareStatement(updateMessageAttrSql);
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ObjectOutputStream oos = new ObjectOutputStream(baos);
                        try {
                            if (mc instanceof MailImpl) {
                            oos.writeObject(((MailImpl)mc).getAttributesRaw());
                            } else {
                                HashMap temp = new HashMap();
                                for (Iterator i = mc.getAttributeNames(); i.hasNext(); ) {
                                    String hashKey = (String) i.next();
                                    temp.put(hashKey,mc.getAttribute(hashKey));
                                }
                                oos.writeObject(temp);
                            }
                            oos.flush();
                            ByteArrayInputStream attrInputStream =
                                new ByteArrayInputStream(baos.toByteArray());
                            updateMessageAttr.setBinaryStream(1, attrInputStream, baos.size());
                        } finally {
                            try {
                                if (oos != null) {
                                    oos.close();
                                }
                            } catch (IOException ioe) {
                                getLogger().debug("JDBCMailRepository: Unexpected exception while closing output stream.",ioe);
                            }
                        }
                        updateMessageAttr.setString(2, mc.getName());
                        updateMessageAttr.setString(3, repositoryName);
                        updateMessageAttr.execute();
                    } catch (SQLException sqle) {
                        getLogger().info("JDBCMailRepository: Trying to update mail attributes failed.",sqle);
                        
                    } finally {
                        theJDBCUtil.closeJDBCStatement(updateMessageAttr);
                    }
                }

                //Determine whether the message body has changed, and possibly avoid
                //  updating the database.
                MimeMessage messageBody = mc.getMessage();
                boolean saveBody = false;
                // if the message is a CopyOnWrite proxy we check the modified wrapped object.
                if (messageBody instanceof MimeMessageCopyOnWriteProxy) {
                    MimeMessageCopyOnWriteProxy messageCow = (MimeMessageCopyOnWriteProxy) messageBody;
                    messageBody = messageCow.getWrappedMessage();
                }
                if (messageBody instanceof MimeMessageWrapper) {
                    MimeMessageWrapper message = (MimeMessageWrapper)messageBody;
                    saveBody = message.isModified();
                } else {
                    saveBody = true;
                }
                
                if (saveBody) {
                    PreparedStatement updateMessageBody = 
                        conn.prepareStatement(sqlQueries.getSqlString("updateMessageBodySQL", true));
                    try {
                        MessageInputStream is = new MessageInputStream(mc,sr,inMemorySizeLimit);
                        updateMessageBody.setBinaryStream(1,is,(int) is.getSize());
                        updateMessageBody.setString(2, mc.getName());
                        updateMessageBody.setString(3, repositoryName);
                        updateMessageBody.execute();
                        
                    } finally {
                        theJDBCUtil.closeJDBCStatement(updateMessageBody);
                    }
                }
                

            } else {
                //Insert the record into the database
                PreparedStatement insertMessage = null;
                try {
                    String insertMessageSQL = sqlQueries.getSqlString("insertMessageSQL", true);
                    int number_of_parameters = getNumberOfParameters (insertMessageSQL);
                    insertMessage =
                        conn.prepareStatement(insertMessageSQL);
                    insertMessage.setString(1, mc.getName());
                    insertMessage.setString(2, repositoryName);
                    insertMessage.setString(3, mc.getState());
                    insertMessage.setString(4, mc.getErrorMessage());
                    if (mc.getSender() == null) {
                        insertMessage.setNull(5, java.sql.Types.VARCHAR);
                    } else {
                        insertMessage.setString(5, mc.getSender().toString());
                    }
                    StringBuffer recipients = new StringBuffer();
                    for (Iterator i = mc.getRecipients().iterator(); i.hasNext(); ) {
                        recipients.append(i.next().toString());
                        if (i.hasNext()) {
                            recipients.append("\r\n");
                        }
                    }
                    insertMessage.setString(6, recipients.toString());
                    insertMessage.setString(7, mc.getRemoteHost());
                    insertMessage.setString(8, mc.getRemoteAddr());
                    insertMessage.setTimestamp(9, new java.sql.Timestamp(mc.getLastUpdated().getTime()));

                    MessageInputStream is = new MessageInputStream(mc, sr, inMemorySizeLimit);

                    insertMessage.setBinaryStream(10, is, (int) is.getSize());
                    
                    //Store attributes
                    if (number_of_parameters > 10) {
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ObjectOutputStream oos = new ObjectOutputStream(baos);
                        try {
                            if (mc instanceof MailImpl) {
                            oos.writeObject(((MailImpl)mc).getAttributesRaw());
                            } else {
                                HashMap temp = new HashMap();
                                for (Iterator i = mc.getAttributeNames(); i.hasNext(); ) {
                                    String hashKey = (String) i.next();
                                    temp.put(hashKey,mc.getAttribute(hashKey));
                                }
                                oos.writeObject(temp);
                            }
                            oos.flush();
                            ByteArrayInputStream attrInputStream =
                                new ByteArrayInputStream(baos.toByteArray());
                            insertMessage.setBinaryStream(11, attrInputStream, baos.size());
                        } finally {
                            try {
                                if (oos != null) {
                                    oos.close();
                                }
                            } catch (IOException ioe) {
                                getLogger().debug("JDBCMailRepository: Unexpected exception while closing output stream.",ioe);
                            }
                        }                        
                    }
                    
                    insertMessage.execute();
                } finally {
                    theJDBCUtil.closeJDBCStatement(insertMessage);
                }
            }


            conn.commit();
            conn.setAutoCommit(true);

        } catch (Exception e) {
            getLogger().error("Exception caught while storing mail Container",e);
            throw new MessagingException("Exception caught while storing mail Container: ",e);
        } finally {
            theJDBCUtil.closeJDBCConnection(conn);
            if (!wasLocked) {
                // If it wasn't locked, we need to unlock now
                unlock(key);
                synchronized (this) {
                    notify();
                }
            }
        }
    
public booleanunlock(java.lang.String key)
Releases a lock on a message identified by a key

param
key the key of the message to be unlocked
return
true if successfully released the lock, false otherwise

        if (lock.unlock(key)) {
            if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
                StringBuffer debugBuffer =
                    new StringBuffer(256)
                            .append("Unlocked ")
                            .append(key)
                            .append(" for ")
                            .append(Thread.currentThread().getName())
                            .append(" @ ")
                            .append(new java.util.Date(System.currentTimeMillis()));
                getLogger().debug(debugBuffer.toString());
            }
            return true;
        } else {
            return false;
        }