You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2006/09/16 22:12:02 UTC
svn commit: r446936 -
/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/
Author: norman
Date: Sat Sep 16 13:12:01 2006
New Revision: 446936
URL: http://svn.apache.org/viewvc?view=rev&rev=446936
Log:
Commit javamailstore mailrepository contributed by Joachim Draeger. Thx for the great work!
Added:
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/AbstractJavamailStoreMailRepository.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderAdapter.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeper.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeperImpl.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderInterface.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/HashJavamailStoreMailRepository.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockAdapter.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockInterface.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/StoreAware.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolder.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderAdapter.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderMailRepository.java
james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UidToKeyBidiMap.java
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/AbstractJavamailStoreMailRepository.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/AbstractJavamailStoreMailRepository.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/AbstractJavamailStoreMailRepository.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/AbstractJavamailStoreMailRepository.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,348 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+package org.apache.james.mailrepository.javamail;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Random;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.NoSuchProviderException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+
+import org.apache.avalon.framework.activity.Initializable;
+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.avalon.framework.logger.Logger;
+import org.apache.james.services.MailRepository;
+import org.apache.mailet.Mail;
+
+/**
+ * MailRepository implementation to store mail in a Javamail store
+ *
+ * This implementation should be considered as EXPERIMENTAL. <br>
+ * <br>
+ * TODO examine for thread-safety
+ */
+
+public abstract class AbstractJavamailStoreMailRepository extends
+ AbstractLogEnabled implements MailRepository, StoreAware, Configurable,
+ Initializable {
+
+ /**
+ * Whether 'deep debugging' is turned on.
+ */
+ protected final static boolean DEEP_DEBUG = true;
+
+ private static final String TYPE = "MAIL";
+
+ /**
+ * Assembled JavaMail destinationURL, only kept here for debugging via
+ * getDestination()
+ */
+ private String destination;
+
+ protected Logger log;
+
+ /**
+ * The underlaying Store can be used externaly via the StoreAware.getStore()
+ * Method
+ */
+ private javax.mail.Store mailStore = null;
+
+ /**
+ * used internally to generate keys
+ */
+ private static Random random;
+
+ /**
+ * this has not been tested yet, so it is not configurable. But it is likely
+ * that there would be memory leaks
+ */
+ protected boolean cacheMessages = false;
+
+ /**
+ * A lock used to control access to repository elements, locking access
+ * based on the key
+ */
+ private LockInterface lock;
+
+ /**
+ * inbox only accessable through a FolderGateKeeper
+ */
+ private FolderGateKeeper folderGateKeeper;
+
+ /**
+ * builds destination from attributes destinationURL and postfix.
+ * at the moment james does not hand over additional parameters like postfix.
+ */
+ public void configure(Configuration conf) throws ConfigurationException {
+ log.debug("JavamailStoreMailRepository configure");
+ destination = conf.getAttribute("destinationURL");
+ log.debug("JavamailStoreMailRepository.destinationURL: " + destination);
+ if (!destination.endsWith("/")) {
+ destination += "/";
+ }
+ String postfix = conf.getAttribute("postfix", "");
+ if (postfix.length() > 0) {
+ if (postfix.startsWith("/")) {
+ postfix = postfix.substring(1);
+ }
+ if (!postfix.endsWith("/")) {
+ postfix += "/";
+ }
+ destination += postfix;
+
+ }
+
+ /*
+ * Ugly hack to get the right folder to store the maildir I guess this
+ * could be also configured in config.xml
+ */
+ final int pi = destination.indexOf(':');
+ if (pi < 0) {
+ throw new ConfigurationException("protocol prefix is missing "
+ + destination);
+ }
+ String withoutProtocol = destination.substring(pi);
+ final String protocol = destination.substring(0, pi);
+ if (!withoutProtocol.startsWith(":///")) {
+ withoutProtocol = ":///../apps/james/" + withoutProtocol.substring(3);
+
+ }
+ destination = protocol + withoutProtocol;
+ log.debug("destination: " + destination);
+ Properties mailSessionProps = new Properties();
+
+ // That seems to be deprecated
+ // mailSessionProps.put("mail.store.maildir.autocreatedir", "true");
+ Session mailSession = Session.getDefaultInstance(mailSessionProps);
+ try {
+ mailStore = mailSession.getStore(new URLName(destination));
+ } catch (NoSuchProviderException e) {
+ throw new ConfigurationException("cannot find store provider for "
+ + destination, e);
+ }
+
+ String checkType = conf.getAttribute("type");
+ if (!checkType.equals(TYPE)) {
+ String exceptionString = "Attempt to configure JavaMailStoreMailRepository as "
+ + checkType;
+ log.warn(exceptionString);
+ throw new ConfigurationException(exceptionString);
+ }
+ log.debug("JavaMailStoreMailRepository configured");
+ }
+
+ /**
+ * connect the mailStore
+ * @see Initializable#initialize()
+ */
+ public void initialize() throws Exception {
+ mailStore.connect();
+ log.debug("JavaMailStoreMailRepository initialized");
+ }
+
+ /**
+ * gets the Lock and creates it, if not present. LockInterface offers functionality
+ * of org.apache.james.util.Lock
+ */
+ protected LockInterface getLock() {
+ if (lock==null) {
+ lock = new LockAdapter();
+ }
+ return lock;
+ }
+
+ /**
+ * possibility to replace Lock implementation. At the moment only used for testing
+ */
+ void setLock(LockInterface lock) {
+ this.lock=lock;
+ }
+
+ /**
+ * used in unit tests
+ */
+ String getDestination() {
+ return destination;
+ }
+
+ /**
+ * 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
+ */
+ public boolean lock(String key) {
+ if (getLock().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;
+ }
+ }
+
+ /**
+ * 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
+ */
+ public boolean unlock(String key) {
+ if (getLock().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;
+ }
+ }
+
+ /**
+ * Removes a specified message
+ *
+ * @param mail
+ * the message to be removed from the repository
+ * @throws MessagingException
+ */
+ public void remove(Mail mail) throws MessagingException {
+ log.debug("UIDPlusFolder remove by Mail");
+ remove(mail.getName());
+ }
+
+ /**
+ * Remove a list of messages from disk The collection is simply a list of
+ * mails to delete
+ *
+ * @param mails
+ * @throws MessagingException
+ */
+ public void remove(final Collection mails) throws MessagingException {
+ log.debug("UIDPlusFolder remove by Collection " + mails.size());
+ 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());
+ }
+ Iterator mailList = mails.iterator();
+
+ /*
+ * remove every email from the Collection
+ */
+ while (mailList.hasNext()) {
+ remove(((Mail) mailList.next()).getName());
+ }
+ }
+
+ /**
+ * offers the underlaying Store for external use
+ */
+ public Store getStore() {
+ return mailStore;
+ }
+
+ /**
+ * lazy-loads random
+ */
+ protected static synchronized Random getRandom() {
+ if (random == null) {
+ random = new Random();
+ }
+ return new Random();
+
+ }
+
+ /**
+ * sets log
+ */
+ public void enableLogging(Logger log) {
+ super.enableLogging(log);
+ this.log=log;
+
+ }
+ /**
+ * possibility to replace FolderGateKeeper implementation. Only used for
+ * testing
+ */
+ void setFolderGateKeeper(FolderGateKeeper gk) {
+ this.folderGateKeeper=gk;
+
+ }
+
+ /**
+ * used by getFolderGateKeeper to get the right FolderInterface implementation
+ *
+ * @param folder JavaMail folder
+ * @return
+ * @throws NoSuchMethodException if the Folder is not suitable
+ */
+ protected abstract FolderInterface createAdapter(Folder folder) throws NoSuchMethodException;
+ /**
+ * Lazy-load FolderGateKeeper with inbox folder. Inbox folder is created if
+ * not present
+ * @return FolderGateKeeper offering inbox folder for this mail repository
+ */
+ protected FolderGateKeeper getFolderGateKeeper() {
+ if (folderGateKeeper == null) {
+ try {
+ Folder inbox = getStore().getFolder("INBOX");
+ FolderInterface myInbox=createAdapter(inbox);
+ folderGateKeeper = new FolderGateKeeperImpl(myInbox);
+
+ /*
+ * Check whether the folder exists, if not create it
+ */
+ if (inbox.exists() == false) {
+ inbox.create(Folder.HOLDS_MESSAGES);
+ }
+ } catch (MessagingException e) {
+ throw new RuntimeException(
+ "cannot retrieve inbox for this repository", e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(
+ "Folder implementation is not suitable", e);
+ }
+ }
+ return folderGateKeeper;
+ }
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderAdapter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderAdapter.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderAdapter.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderAdapter.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,256 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+package org.apache.james.mailrepository.javamail;
+
+import javax.mail.FetchProfile;
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Store;
+import javax.mail.URLName;
+import javax.mail.event.ConnectionListener;
+import javax.mail.event.FolderListener;
+import javax.mail.event.MessageChangedListener;
+import javax.mail.event.MessageCountListener;
+import javax.mail.search.SearchTerm;
+
+/**
+ * Simple 1:1 wrapper that holds a javax.mail.Folder internally to be accessed
+ * through the FolderInterface
+ */
+
+public class FolderAdapter implements FolderInterface {
+
+ protected Folder folder;
+
+ public FolderAdapter(Folder folder) {
+ this.folder=folder;
+ }
+
+ public void addConnectionListener(ConnectionListener arg0) {
+ folder.addConnectionListener(arg0);
+ }
+
+ public void addFolderListener(FolderListener arg0) {
+ folder.addFolderListener(arg0);
+ }
+
+ public void addMessageChangedListener(MessageChangedListener arg0) {
+ folder.addMessageChangedListener(arg0);
+ }
+
+ public void addMessageCountListener(MessageCountListener arg0) {
+ folder.addMessageCountListener(arg0);
+ }
+
+ public void appendMessages(Message[] arg0) throws MessagingException {
+ folder.appendMessages(arg0);
+ }
+
+ public void close(boolean arg0) throws MessagingException {
+ folder.close(arg0);
+ }
+
+ public void copyMessages(Message[] arg0, Folder arg1) throws MessagingException {
+ folder.copyMessages(arg0, arg1);
+ }
+
+ public boolean create(int arg0) throws MessagingException {
+ return folder.create(arg0);
+ }
+
+ public boolean delete(boolean arg0) throws MessagingException {
+ return folder.delete(arg0);
+ }
+
+ public boolean equals(Object arg0) {
+ return folder.equals(arg0);
+ }
+
+ public boolean exists() throws MessagingException {
+ return folder.exists();
+ }
+
+ public Message[] expunge() throws MessagingException {
+ return folder.expunge();
+ }
+
+ public void fetch(Message[] arg0, FetchProfile arg1) throws MessagingException {
+ folder.fetch(arg0, arg1);
+ }
+
+ public int getDeletedMessageCount() throws MessagingException {
+ return folder.getDeletedMessageCount();
+ }
+
+ public Folder getFolder(String arg0) throws MessagingException {
+ return folder.getFolder(arg0);
+ }
+
+ public String getFullName() {
+ return folder.getFullName();
+ }
+
+ public Message getMessage(int arg0) throws MessagingException {
+ return folder.getMessage(arg0);
+ }
+
+ public int getMessageCount() throws MessagingException {
+ return folder.getMessageCount();
+ }
+
+ public Message[] getMessages() throws MessagingException {
+ return folder.getMessages();
+ }
+
+ public Message[] getMessages(int arg0, int arg1) throws MessagingException {
+ return folder.getMessages(arg0, arg1);
+ }
+
+ public Message[] getMessages(int[] arg0) throws MessagingException {
+ return folder.getMessages(arg0);
+ }
+
+ public int getMode() {
+ return folder.getMode();
+ }
+
+ public String getName() {
+ return folder.getName();
+ }
+
+ public int getNewMessageCount() throws MessagingException {
+ return folder.getNewMessageCount();
+ }
+
+ public Folder getParent() throws MessagingException {
+ return folder.getParent();
+ }
+
+ public Flags getPermanentFlags() {
+ return folder.getPermanentFlags();
+ }
+
+ public char getSeparator() throws MessagingException {
+ return folder.getSeparator();
+ }
+
+ public Store getStore() {
+ return folder.getStore();
+ }
+
+ public int getType() throws MessagingException {
+ return folder.getType();
+ }
+
+ public int getUnreadMessageCount() throws MessagingException {
+ return folder.getUnreadMessageCount();
+ }
+
+ public URLName getURLName() throws MessagingException {
+ return folder.getURLName();
+ }
+
+ public int hashCode() {
+ return folder.hashCode();
+ }
+
+ public boolean hasNewMessages() throws MessagingException {
+ return folder.hasNewMessages();
+ }
+
+ public boolean isOpen() {
+ return folder.isOpen();
+ }
+
+ public boolean isSubscribed() {
+ return folder.isSubscribed();
+ }
+
+ public Folder[] list() throws MessagingException {
+ return folder.list();
+ }
+
+ public Folder[] list(String arg0) throws MessagingException {
+ return folder.list(arg0);
+ }
+
+ public Folder[] listSubscribed() throws MessagingException {
+ return folder.listSubscribed();
+ }
+
+ public Folder[] listSubscribed(String arg0) throws MessagingException {
+ return folder.listSubscribed(arg0);
+ }
+
+ public void open(int arg0) throws MessagingException {
+ folder.open(arg0);
+ }
+
+ public void removeConnectionListener(ConnectionListener arg0) {
+ folder.removeConnectionListener(arg0);
+ }
+
+ public void removeFolderListener(FolderListener arg0) {
+ folder.removeFolderListener(arg0);
+ }
+
+ public void removeMessageChangedListener(MessageChangedListener arg0) {
+ folder.removeMessageChangedListener(arg0);
+ }
+
+ public void removeMessageCountListener(MessageCountListener arg0) {
+ folder.removeMessageCountListener(arg0);
+ }
+
+ public boolean renameTo(Folder arg0) throws MessagingException {
+ return folder.renameTo(arg0);
+ }
+
+ public Message[] search(SearchTerm arg0, Message[] arg1) throws MessagingException {
+ return folder.search(arg0, arg1);
+ }
+
+ public Message[] search(SearchTerm arg0) throws MessagingException {
+ return folder.search(arg0);
+ }
+
+ public void setFlags(int arg0, int arg1, Flags arg2, boolean arg3) throws MessagingException {
+ folder.setFlags(arg0, arg1, arg2, arg3);
+ }
+
+ public void setFlags(int[] arg0, Flags arg1, boolean arg2) throws MessagingException {
+ folder.setFlags(arg0, arg1, arg2);
+ }
+
+ public void setFlags(Message[] arg0, Flags arg1, boolean arg2) throws MessagingException {
+ folder.setFlags(arg0, arg1, arg2);
+ }
+
+ public void setSubscribed(boolean arg0) throws MessagingException {
+ folder.setSubscribed(arg0);
+ }
+
+ public String toString() {
+ return folder.toString();
+ }
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeper.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeper.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeper.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeper.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,94 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+package org.apache.james.mailrepository.javamail;
+
+import javax.mail.MessagingException;
+
+/**
+ * offers access to an underlaying Folder and manages open/close operations.<br>
+ * The FolderGateKeeper can be handed over to different threads.
+ * <br>
+ * Clients have to call use() one time before and free() one time after they are
+ * operating on the folder. When use() has been called free() has to be called
+ * afterwards in any circumstance usally in a finally block.<br>
+ *
+ * <pre>
+ * try {
+ * use();
+ * getFolder().doSomething();
+ * getFolder().doSomething();
+ * } finally {
+ * free();
+ * }
+ * </pre>
+ *
+ * It is not allowed to open/close Folder from outside.
+ */
+
+public interface FolderGateKeeper {
+
+ /**
+ * increments count of users
+ */
+ public void use();
+
+ /**
+ * decrements count of users and closes folder if 0 and folder is open.
+ *
+ * @throws MessagingException
+ * if something went wrong closing the Folder
+ * @throws IllegalStateException
+ * if there are already 0 users
+ * @throws IllegalStateException
+ * if the state of the folder differs from the last known
+ */
+ public void free() throws MessagingException;
+
+ /**
+ * Gets the Folder and opens it, if necessary
+ *
+ * @return an open Folder
+ * @throws MessagingException
+ * if something went wron opening the Folder
+ * @throws IllegalStateException
+ * if the state of the folder differs from the last known
+ * @throws IllegalStateException
+ * if there are only 0 users
+ */
+ public FolderInterface getOpenFolder() throws MessagingException;
+
+ /**
+ * used to test whether everyone has freed it
+ *
+ * @return number of users
+ */
+ public int getUseCount();
+
+ /**
+ * Gets the Folder and don't care whether it is open or closed.
+ *
+ * @return a open or closed Folder
+ * @throws IllegalStateException
+ * if the state of the folder differs from the last known
+ * @throws IllegalStateException
+ * if there are only 0 users
+ */
+ public FolderInterface getFolder();
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeperImpl.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeperImpl.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeperImpl.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderGateKeeperImpl.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,112 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+
+
+/**
+ * offers access to an underlaying Folder and manages open/close operations.<br>
+ * The FolderGateKeeper can be handed over to different threads.
+ *
+ * @sse FolderGateKeeper
+ *
+ */
+public class FolderGateKeeperImpl implements FolderGateKeeper {
+
+ private FolderInterface folder;
+
+ private int inUse = 0;
+
+ boolean open = false;
+
+ public FolderGateKeeperImpl(FolderInterface folder) {
+ if (folder.isOpen()) {
+ throw new IllegalStateException(
+ "FolderGateKeeper: initial state must be closed");
+ }
+ this.folder = folder;
+ }
+
+ public synchronized void use() {
+ inUse++;
+ }
+
+ public synchronized void free() throws MessagingException {
+ if (inUse < 1) {
+ throw new IllegalStateException(
+ "called free() but FolderGateKeeper is not in use");
+ } else {
+ inUse--;
+ if (inUse == 0) {
+ if (open != folder.isOpen()) {
+ throw new IllegalStateException(
+ "free(): folder state not equals last known state: open="
+ + open);
+ }
+ if (open) {
+ // TODO expunge should be configurable
+ folder.close(true);
+ open = false;
+ }
+ }
+ }
+
+ }
+
+ public synchronized FolderInterface getOpenFolder() throws MessagingException {
+ if (inUse < 1) {
+ throw new IllegalStateException(
+ "called getFolder() but folder is not in use");
+ }
+ if (open != folder.isOpen()) {
+ throw new IllegalStateException(
+ "getFolder() folder state not equals last known state: open="
+ + open);
+ }
+ if (!open) {
+ folder.open(Folder.READ_WRITE);
+ open = true;
+ }
+ return folder;
+
+ }
+
+ public synchronized int getUseCount() {
+ return inUse;
+ }
+
+ public synchronized FolderInterface getFolder() {
+ if (inUse < 1) {
+ throw new IllegalStateException(
+ "called getFolder() but folder is not in use");
+ }
+ if (open != folder.isOpen()) {
+ throw new IllegalStateException(
+ "getFolder() folder state not equals last known state: open="
+ + open);
+ }
+ return folder;
+ }
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderInterface.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderInterface.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderInterface.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/FolderInterface.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,48 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+
+/**
+ * Interface to javax.mail.Folder functionality to be able to replace
+ * implementation or using Mocks when testing
+ */
+
+public interface FolderInterface {
+
+ public Message[] getMessages() throws MessagingException;
+
+ public Message getMessage(int no) throws MessagingException;
+
+ public int getMessageCount() throws MessagingException;
+
+ public void appendMessages(Message[] messages) throws MessagingException;
+
+ public boolean isOpen();
+
+ public void open(int status) throws MessagingException;
+
+ public void close(boolean b) throws MessagingException;
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/HashJavamailStoreMailRepository.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/HashJavamailStoreMailRepository.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/HashJavamailStoreMailRepository.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/HashJavamailStoreMailRepository.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,557 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.UIDFolder;
+import javax.mail.Flags.Flag;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.core.MailImpl;
+import org.apache.james.mailrepository.javamail.HashJavamailStoreMailRepository.KeyToMsgMap.MsgObj;
+import org.apache.mailet.Mail;
+
+import com.sun.mail.util.CRLFOutputStream;
+
+/**
+ * MailRepository implementation to store mail in a Javamail store. <br>
+ * should work with every JavamailStore implementation that has deterministic
+ * message content. (checksum save). This implementation should be considered as
+ * EXPERIMENTAL.
+ *
+ * @author Joachim Draeger <jd at joachim-draeger.de>
+ *
+ * TODO examine for thread-safety
+ */
+public class HashJavamailStoreMailRepository extends
+ AbstractJavamailStoreMailRepository {
+
+ /**
+ * tridirectional map of messages key, hash and number saved in internaly
+ * used class MsgObj
+ */
+ protected KeyToMsgMap keyToMsgMap = null;
+
+
+ private boolean getMessageCountOnClosed =true;
+
+
+ protected int getMessageCount() throws MessagingException {
+ try {
+ getFolderGateKeeper().use();
+ int n=-1;
+ if (getMessageCountOnClosed) {
+ n=getFolderGateKeeper().getFolder().getMessageCount();
+ if (n==-1) {
+ getMessageCountOnClosed=false;
+ }
+ }
+ if (!getMessageCountOnClosed) {
+ n=getFolderGateKeeper().getOpenFolder().getMessageCount();
+ }
+ return n;
+ } finally {
+ getFolderGateKeeper().free();
+ }
+ }
+
+ /**
+ * Stores a message by Javamails appendMessages method. Tries to guess
+ * resulting messagenumber and saves result in keyToMsgMap. If Folder
+ * supports getMessageCount on closed folder, this could be quite efficient
+ */
+ public synchronized void store(Mail mc) throws MessagingException {
+
+ final String key = mc.getName();
+ boolean wasLocked = true;
+ log.debug("Store: (hash) " + key);
+ if (!mc.getMessage().isSet(Flag.RECENT)) {
+ log.debug("Message didn't have RECENT flag");
+ mc.getMessage().setFlag(Flag.RECENT,true);
+ }
+ // because we use/free/use we need to know the state in finally
+ boolean use=false;
+ try {
+
+ // Shouldn't we care when another Thread has locked this key and
+ // stop here?
+ synchronized (this) {
+ wasLocked = getLock().isLocked(key);
+ if (!wasLocked) {
+ // If it wasn't locked, we want a lock during the store
+ lock(key);
+ }
+ }
+
+
+ // Yes, appendMessages works on a closed inbox. But we need
+ // getMessageCount() and that is allowed
+ // to be -1 on a closed Folder when counting messages is expensive
+
+ int countBefore = getMessageCount();
+
+ getFolderGateKeeper().use();
+ use=true;
+ // insert or update, don't call remove(key) because of locking
+ if (getKeyToMsgMap().contains(key)) {
+ log.debug("store is a update");
+ Message mm = getMessageFromInbox(key);
+ if (mm != null) {
+ countBefore--;
+ mm.setFlag(Flags.Flag.DELETED, true);
+ mc.getMessage().setFlag(Flags.Flag.RECENT, false);
+ }
+ getKeyToMsgMap().removeByKey(key, true);
+ }
+ getFolderGateKeeper().getFolder().appendMessages(new Message[] { mc.getMessage() });
+ use=false;
+ getFolderGateKeeper().free();
+
+ // Try to guess resulting message number
+ int no = -1;
+ int count=getMessageCount();
+ if (count - countBefore == 1) {
+ no = count;
+ log.debug("Assigned message number "+ count);
+ } else {
+ log.debug("count - countBefore = "+ (count - countBefore ));
+ }
+
+ getKeyToMsgMap().put(mc.getMessage(), mc.getName(), no);
+ } catch (MessagingException e) {
+ log.error("Exception in HashJavamailStore: ", e);
+ throw e;
+ } finally {
+ if (!wasLocked) {
+ // If it wasn't locked, we need to unlock now
+ unlock(key);
+ synchronized (this) {
+ notify();
+ }
+ }
+ if (use) {
+ getFolderGateKeeper().free();
+ }
+ log.debug("closed.");
+ }
+ log.debug("store finished");
+ }
+
+ /**
+ * calls rehash and uses stored keys in KeyToMsgMap
+ */
+ public Iterator list() throws MessagingException {
+ try {
+ getFolderGateKeeper().use();
+ log.debug("list()");
+ rehash(null);
+ final String[] keys = getKeyToMsgMap().getKeys();
+ final Iterator it = Arrays.asList(keys).iterator();
+ return it;
+ } catch (MessagingException e) {
+ throw e;
+ } finally {
+ getFolderGateKeeper().free();
+ }
+
+ }
+
+ /**
+ * uses getMessageFromInbox, returns null if not found
+ */
+ public Mail retrieve(String key) throws MessagingException {
+ log.debug("retrieve: " + key);
+ Mail m = null;
+ try {
+ getFolderGateKeeper().use();
+ MimeMessage mm = getMessageFromInbox(key);
+ if (mm != null) {
+ m = new MailImpl(mm);
+ m.setName(key);
+ } else {
+ log.debug("could not retrieve a MimeMessage from folder");
+ }
+
+ } catch (MessagingException e) {
+ throw e;
+ } finally {
+ getFolderGateKeeper().free();
+ }
+ return m;
+ }
+
+ /**
+ * Removes a message identified by key. uses getMessageFromInbox and does a
+ * setFlag(Flags.Flag.DELETED, true); on message. removes message from
+ * KeyToMsgMap. Messagenumbers are recalculated for next guesses.
+ *
+ * @param key
+ * the key of the message to be removed from the repository
+ */
+ public synchronized void remove(String key) throws MessagingException {
+ log.debug("HashJavamailStore remove key:" + key);
+ if (lock(key)) {
+ try {
+ getFolderGateKeeper().use();
+ Message mm = getMessageFromInbox(key);
+ if (mm != null) {
+ // will be deleted on expunge
+ mm.setFlag(Flags.Flag.DELETED, true);
+ }
+ getKeyToMsgMap().removeByKey(key, true);
+ } catch (MessagingException e) {
+ throw e;
+ } finally {
+ unlock(key);
+ getFolderGateKeeper().free();
+ }
+ } else {
+ log.debug("could not optain lock");
+ throw new MessagingException("could not optain lock for remove");
+ }
+ }
+
+ /**
+ * Calls getMessages(); on Folder and rehashes messages an renews message
+ * numbers calls retainAllListedAndAddedByKeys on KeyToMsgMap to remove keys
+ * not found in Folder
+ *
+ * @param filterkey
+ * key of message that should be returned, can be null
+ * @return a message if found by filterkey
+ * @throws MessagingException
+ */
+ protected MimeMessage rehash(String filterkey) throws MessagingException {
+ if (DEEP_DEBUG)
+ log.debug("doing rehash");
+ String[] keysBefore = getKeyToMsgMap().getKeys();
+ MimeMessage mm = null;
+ Message[] msgs = getFolderGateKeeper().getOpenFolder().getMessages();
+ String[] keys = new String[msgs.length];
+ for (int i = 0; i < msgs.length; i++) {
+ Message message = msgs[i];
+ MsgObj mo = getKeyToMsgMap()
+ .put((MimeMessage) message, null, i + 1);
+ keys[i] = mo.key;
+ if (DEEP_DEBUG)
+ log.debug("No " + mo.no + " key:" + mo.key);
+ if (mo.key.equals(filterkey)) {
+ if (DEEP_DEBUG)
+ log.debug("Found message!");
+ mm = (MimeMessage) message;
+ }
+ }
+ getKeyToMsgMap().retainAllListedAndAddedByKeys(keysBefore, keys);
+ return mm;
+ }
+
+ /**
+ * Fetches a message from inbox. Fast fails if key is unknown in
+ * KeyToMsgMap. Tries to get message at last known position, if that was not
+ * successfull calls rehash
+ *
+ * @param key
+ * message key
+ * @return message if found, otherwise null
+ * @throws MessagingException
+ */
+ protected MimeMessage getMessageFromInbox(String key)
+ throws MessagingException {
+ MsgObj mo = getKeyToMsgMap().getByKey(key);
+ if (mo == null) {
+ log.debug("Key not found");
+ return null;
+ }
+ MimeMessage mm = null;
+ if (cacheMessages && mo.message != null) {
+ // not used at the moment
+ mm = mo.message;
+ } else {
+ try {
+ getFolderGateKeeper().use();
+ Object hash = null;
+ if (mo.no >= 0) {
+ try {
+ mm = (MimeMessage) getFolderGateKeeper().getOpenFolder()
+ .getMessage(mo.no);
+ hash = calcMessageHash(mm);
+ if (!hash.equals(mo.hash)) {
+ log
+ .debug("Message at guessed position does not match "
+ + mo.no);
+ mm = null;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ log.debug("no Message found at guessed position "
+ + mo.no);
+ }
+ } else {
+ log.debug("cannot guess message number");
+ }
+ if (mm == null) {
+ mm = rehash(mo.key);
+ if (mm == null)
+ log.debug("rehashing was fruitless");
+ }
+ } finally {
+ getFolderGateKeeper().free();
+ }
+ }
+ return mm;
+ }
+
+ /**
+ * lazy loads KeyToMsgMap
+ *
+ * @return
+ */
+ protected KeyToMsgMap getKeyToMsgMap() {
+ if (keyToMsgMap == null) {
+ keyToMsgMap = new KeyToMsgMap();
+ }
+ return keyToMsgMap;
+ }
+
+ protected class KeyToMsgMap {
+ protected SortedMap noToMsgObj;
+
+ protected Map keyToMsgObj;
+
+ protected Map hashToMsgObj;
+
+ protected KeyToMsgMap() {
+ noToMsgObj = new TreeMap();
+ keyToMsgObj = new HashMap();
+ hashToMsgObj = new HashMap();
+ }
+
+ public synchronized boolean contains(String key) {
+ return keyToMsgObj.containsKey(key);
+ }
+
+ /**
+ * Cleans up database after rehash. Only keys listed by rehash or added
+ * in the meantime are retained
+ *
+ * @param keysBefore
+ * keys that have exist before rehash was called
+ * @param listed
+ * keys that have listed by rehash
+ */
+ public synchronized void retainAllListedAndAddedByKeys(
+ String[] keysBefore, String[] listed) {
+ if (DEEP_DEBUG)
+ log.debug("stat before retain: " + getStat());
+ Set added = new HashSet(keyToMsgObj.keySet());
+ added.removeAll(Arrays.asList(keysBefore));
+
+ Set retain = new HashSet(Arrays.asList(listed));
+ retain.addAll(added);
+
+ Collection remove = new HashSet(keyToMsgObj.keySet());
+ remove.removeAll(retain);
+ // huh, are we turning in circles? :-)
+
+ for (Iterator iter = remove.iterator(); iter.hasNext();) {
+ removeByKey((String) iter.next(), false);
+ }
+ if (DEEP_DEBUG)
+ log.debug("stat after retain: " + getStat());
+ }
+
+ /**
+ * only used for debugging
+ *
+ * @return a String representing the sizes of the internal maps
+ */
+ public String getStat() {
+ String s = "keyToMsgObj:" + keyToMsgObj.size() + " hashToMsgObj:"
+ + hashToMsgObj.size() + " noToMsgObj:" + noToMsgObj.size();
+ return s;
+ }
+
+ /**
+ * removes a message from the maps.
+ *
+ * @param key
+ * key of message
+ * @param decrease
+ * if true, all message number greater than this are
+ * decremented
+ */
+ public synchronized void removeByKey(String key, boolean decrement) {
+ MsgObj mo = getByKey(key);
+ keyToMsgObj.remove(mo.key);
+ noToMsgObj.remove(new Integer(mo.no));
+ hashToMsgObj.remove(mo.hash);
+ if (decrement) {
+ // tailMap finds all entries that have message number greater
+ // than removed one and decrements them
+ MsgObj[] dmos = (MsgObj[]) noToMsgObj.tailMap(
+ new Integer(mo.no)).values().toArray(new MsgObj[0]);
+ for (int i = 0; i < dmos.length; i++) {
+ MsgObj dmo = dmos[i];
+ noToMsgObj.remove(new Integer(dmo.no));
+ dmo.no--;
+ noToMsgObj.put(new Integer(dmo.no), dmo);
+ }
+ }
+ }
+
+ public synchronized String[] getKeys() {
+ return (String[]) keyToMsgObj.keySet().toArray(new String[0]);
+ }
+
+ public synchronized MsgObj getByKey(String key) {
+ return (MsgObj) keyToMsgObj.get(key);
+ }
+
+ /**
+ * At first it tries to lookup message by key otherwise by hash or
+ * stores it as new
+ *
+ * @param mm
+ * message
+ * @param key
+ * if null it will be generated when not found by hash
+ * @param no
+ * current number of this message
+ * @return fetched/created MsgObj
+ * @throws MessagingException
+ */
+
+ public synchronized MsgObj put(final MimeMessage mm, String key,
+ final int no) throws MessagingException {
+ final Object hash = calcMessageHash(mm);
+ MsgObj mo;
+ if (key != null) {
+ mo = getMsgObj(key);
+ } else {
+ mo = (MsgObj) hashToMsgObj.get(hash);
+ if (mo == null) {
+ key = generateKey(hash.toString());
+ }
+ }
+ if (mo == null) {
+ mo = new MsgObj();
+ keyToMsgObj.put(key, mo);
+ mo.key = key;
+ }
+ if (!hash.equals(mo.hash)) {
+ if (mo.hash != null) {
+ hashToMsgObj.remove(mo.hash);
+ }
+ mo.hash = hash;
+ hashToMsgObj.put(hash, mo);
+ }
+ if (no != mo.no) {
+ if (mo.no > -1) {
+ noToMsgObj.remove(new Integer(mo.no));
+ }
+ mo.no = no;
+ noToMsgObj.put(new Integer(no), mo);
+ }
+ if (cacheMessages) {
+ mo.message = mm;
+ }
+ return mo;
+
+ }
+
+ public synchronized MsgObj getMsgObj(String key) {
+ return (MsgObj) keyToMsgObj.get(key);
+ }
+
+ /**
+ * used to internal represent a message
+ *
+ */
+ protected class MsgObj {
+ MimeMessage message;
+
+ int no = -1;
+
+ Object hash;
+
+ String key;
+ }
+
+ }
+
+ /**
+ * currently uses Arrays.hashCode to build an Integer. Resulting Method
+ * should provide a good hashCode and a correct equals method
+ *
+ * @param mm
+ * message to hash
+ * @return an Object provides and correct equals method.
+ * @throws MessagingException
+ */
+ protected static Object calcMessageHash(MimeMessage mm)
+ throws MessagingException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ mm.writeTo(new CRLFOutputStream(baos));
+ } catch (IOException e) {
+ throw new MessagingException("error while calculating hash ", e);
+ }
+ Integer i = new Integer(Arrays.hashCode(baos.toByteArray()));
+ return i;
+ }
+
+ /**
+ * builds a key for unknow messages
+ *
+ * @param hash Hash to be included in key
+ * @return contains "james-hashed:", the hash, the time and a random long
+ */
+ protected static String generateKey(String hash) {
+ String key = "james-hashed:" + hash + ";" + System.currentTimeMillis()
+ + ";" + getRandom().nextLong();
+ return key;
+ }
+
+ /**
+ * just uses a FolderAdapter to wrap around folder
+ *
+ */
+ protected FolderInterface createAdapter(Folder folder)
+ throws NoSuchMethodException {
+ return new FolderAdapter(folder);
+ }
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockAdapter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockAdapter.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockAdapter.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockAdapter.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,48 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import org.apache.james.util.Lock;
+/**
+ * Just a simple 1:1 wrapper for org.apache.james.util.Lock that implements the LockInterface
+ *
+ * @see org.apache.james.util.Lock
+ * @see org.apache.james.mailrepository.javamail.LockInterface
+ */
+public class LockAdapter implements LockInterface {
+ Lock lock= new Lock();
+
+ public boolean isLocked(Object key) {
+ return lock.isLocked(key);
+ }
+
+ public boolean lock(Object key) {
+ return lock.lock(key);
+ }
+
+ public boolean unlock(Object key) {
+ return lock.unlock(key);
+ }
+
+
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockInterface.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockInterface.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockInterface.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/LockInterface.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,56 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+/**
+ * Interface for org.apache.james.util.Lock functionality to be able to replace
+ * implementation or using Mock-objects at tests
+ *
+ * @see org.apache.james.util.Lock
+ */
+public interface LockInterface {
+
+ /**
+ * Check to see if the object is locked
+ *
+ * @param key the Object on which to check the lock
+ * @return true if the object is locked, false otherwise
+ */
+ public boolean isLocked(final Object key);
+
+ /**
+ * Lock on a given object.
+ *
+ * @param key the Object on which to lock
+ * @return true if the locking was successful, false otherwise
+ */
+ public boolean lock(final Object key);
+
+ /**
+ * Release the lock on a given object.
+ *
+ * @param key the Object on which the lock is held
+ * @return true if the unlocking was successful, false otherwise
+ */
+ public boolean unlock(final Object key);
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/StoreAware.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/StoreAware.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/StoreAware.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/StoreAware.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,37 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import javax.mail.Store;
+
+/**
+ * Bridge, that allows JavamailStoreMailRepository implementations to offer the
+ * underlaying Javamail Store
+ *
+ * @author Joachim Draeger <jd at joachim-draeger.de>
+ */
+
+public interface StoreAware {
+
+ public Store getStore();
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolder.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolder.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolder.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolder.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.UIDFolder;
+
+/**
+ * Interim interface to provide access to UID PLUS methods reflecting RFC 2359,
+ * until official Javamail API offers this.
+ */
+
+public interface UIDPlusFolder extends UIDFolder, FolderInterface {
+ /**
+ * Appends the given messages to the folder and returns corresponding uids.<br>
+ * Implementations may require the folder to be open.
+ *
+ * @see javax.mail.Folder#appendMessages(javax.mail.Message[])
+ *
+ * @param msgs
+ * messages to append
+ * @return array of same size and sequenze of msgs containing corresponding
+ * uids or -1, if something went wrong
+ * @throws MessagingException
+ * @throws IllegalStateException when folder has to be open
+ */
+ public long[] addUIDMessages(Message[] msgs) throws MessagingException;
+
+ /**
+ * Appends the given messages to the folder and returns corresponding
+ * instances of the appended messages.<br>
+ * Implementations may require the folder to be open.
+ *
+ * @see javax.mail.Folder#appendMessages(javax.mail.Message[])
+ *
+ * @param msgs
+ * messages to append
+ * @return array of same size and sequenze of msgs containing corresponding
+ * added messages or null, if something went wrong
+ * @throws MessagingException
+ * @throws IllegalStateException when folder has to be open
+ */
+ public Message[] addMessages(Message[] msgs) throws MessagingException;
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderAdapter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderAdapter.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderAdapter.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderAdapter.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,93 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.UIDFolder;
+
+/**
+ * Simple 1:1 wrapper for original JavaMail Folder and UIDFolder. uses
+ * reflection to call UIDPlusFolder methods on a JavaMail Folder.
+ */
+public class UIDPlusFolderAdapter extends FolderAdapter implements UIDPlusFolder {
+
+ private Method addUIDMessagesMethod;
+
+ public UIDPlusFolderAdapter(Folder folder) throws NoSuchMethodException {
+ super(folder);
+ try {
+ addUIDMessagesMethod=folder.getClass().getMethod("addUIDMessages",
+ new Class[] { Message[].class });
+ } catch (SecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public long[] addUIDMessages(Message[] msgs) throws MessagingException {
+ try {
+ return (long[]) addUIDMessagesMethod.invoke(folder, new Object[] { msgs });
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ Throwable cause=e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException)cause;
+ }
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Message[] addMessages(Message[] msgs) throws MessagingException {
+ throw new RuntimeException("Method addMessages(Message[] msgs) not implemented");
+ }
+
+ public long getUIDValidity() throws MessagingException {
+ return ((UIDFolder)folder).getUIDValidity();
+ }
+
+ public Message getMessageByUID(long arg0) throws MessagingException {
+ return ((UIDFolder)folder).getMessageByUID(arg0);
+ }
+
+ public Message[] getMessagesByUID(long arg0, long arg1) throws MessagingException {
+ return ((UIDFolder)folder).getMessagesByUID(arg0, arg1);
+ }
+
+ public Message[] getMessagesByUID(long[] arg0) throws MessagingException {
+ return ((UIDFolder)folder).getMessagesByUID(arg0);
+ }
+
+ public long getUID(Message arg0) throws MessagingException {
+ return ((UIDFolder)folder).getUID(arg0);
+ }
+
+
+
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderMailRepository.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderMailRepository.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderMailRepository.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UIDPlusFolderMailRepository.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,379 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.UIDFolder;
+import javax.mail.Flags.Flag;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.core.MailImpl;
+import org.apache.mailet.Mail;
+
+/**
+ * MailRepository implementation to store mail in a Javamail store which
+ * provides the UID Plus method public long[] addUIDMessages.<br>
+ * <br>
+ * This implementation should be considered as EXPERIMENTAL.
+ *
+ * TODO examine for thread-safety
+ */
+public class UIDPlusFolderMailRepository extends
+ AbstractJavamailStoreMailRepository {
+
+ /**
+ * used to map keys to uid and vice versa
+ */
+ private UidToKeyBidiMap uidToKeyBidiMap = null;
+
+ public static final int DELIVERY_MODE_CLOSED = 0;
+ public static final int DELIVERY_MODE_TRY = 1;
+ public static final int DELIVERY_MODE_OPEN = 2;
+
+ private int deliveryMode=DELIVERY_MODE_TRY;
+
+ protected long addUIDMessage(Message message) throws MessagingException {
+ try {
+ getFolderGateKeeper().use();
+ long[] uids = null;
+ if (deliveryMode != DELIVERY_MODE_OPEN) {
+ try {
+ log.debug("Doing a addUIDMessages on maybe closed Folder: isopen="+getFolderGateKeeper().getFolder().isOpen());
+ uids = ((UIDPlusFolder) getFolderGateKeeper().getFolder())
+ .addUIDMessages(new Message[] { message });
+ } catch (IllegalStateException e) {
+ if (deliveryMode == DELIVERY_MODE_CLOSED) {
+ log.error("deliveryMode=DELIVERY_MODE_CLOSED",e);
+ throw e;
+ } else {
+ log.debug("switching to DELIVERY_MODE_OPEN",e);
+ deliveryMode = DELIVERY_MODE_OPEN;
+ }
+
+ }
+ }
+ if (deliveryMode == DELIVERY_MODE_OPEN) {
+ log.debug("Doing a addUIDMessages on a open Folder");
+ uids = ((UIDPlusFolder) getFolderGateKeeper().getOpenFolder())
+ .addUIDMessages(new Message[] { message });
+ }
+ if (uids == null || uids.length != 1) {
+ throw new RuntimeException(
+ "Fatal error while storing Message Container: Message was not Appendet");
+ }
+ return uids[0];
+ } finally {
+ getFolderGateKeeper().free();
+ }
+
+ }
+
+
+
+ /**
+ * Stores a message in this repository.
+ *
+ * @param mc
+ * the mail message to store
+ * @throws MessagingException
+ */
+ public void store(Mail mc) throws MessagingException {
+
+ log.debug("UIDPlusFolder store key:" + mc.getName());
+ if (!mc.getMessage().isSet(Flag.RECENT)) {
+ log.debug("Message didn't have RECENT flag");
+ mc.getMessage().setFlag(Flag.RECENT,true);
+ }
+ String key = mc.getName();
+ boolean wasLocked = true;
+ try {
+ getFolderGateKeeper().use();
+ MimeMessage message = mc.getMessage();
+ synchronized (this) {
+ wasLocked = getLock().isLocked(key);
+ if (!wasLocked) {
+ // If it wasn't locked, we want a lock during the store
+ lock(key);
+ }
+ }
+
+ // insert or update, don't call remove(key) because of locking
+ if (getUidToKeyBidiMap().containsKey(key)) {
+ // message gets updated an folder stays open.
+ Message mm = getMessageFromInbox(key,
+ (UIDFolder) getFolderGateKeeper().getOpenFolder());
+ if (mm != null) {
+ mm.setFlag(Flags.Flag.DELETED, true);
+ message.setFlag(Flags.Flag.RECENT, false);
+ }
+ }
+ long uid = addUIDMessage(message);
+ getUidToKeyBidiMap().put(key, uid);
+
+ log.info("message stored: UID: " + uid + " Key:" + mc.getName());
+ } finally {
+ getFolderGateKeeper().free();
+ if (!wasLocked) {
+ // If it wasn't locked, we need to unlock now
+ unlock(key);
+ synchronized (this) {
+ notify();
+ }
+ }
+
+ }
+ }
+
+ /**
+ * lazy loads UidToKeyBidiMap
+ * @return
+ */
+ protected UidToKeyBidiMap getUidToKeyBidiMap() {
+ if (uidToKeyBidiMap == null) {
+ uidToKeyBidiMap = new UidToKeyBidiMapImpl();
+ }
+ return uidToKeyBidiMap;
+ }
+
+ /**
+ * Used for testing
+ *
+ * @param uidToKeyBidiMap
+ */
+ void setUidToKeyBidiMap(UidToKeyBidiMap uidToKeyBidiMap) {
+ this.uidToKeyBidiMap = uidToKeyBidiMap;
+ }
+
+ /**
+ * Retrieves a message given a key. At the moment, keys can be obtained from
+ * list() in superinterface Store.Repository
+ *
+ * @param key
+ * the key of the message to retrieve
+ * @return the mail corresponding to this key, null if none exists
+ * @throws MessagingException
+ */
+
+ public Mail retrieve(String key) throws MessagingException {
+ log.debug("UIDPlusFolder retrieve " + key);
+ try {
+ getFolderGateKeeper().use();
+ MimeMessage mm = getMessageFromInbox(key,
+ (UIDFolder) getFolderGateKeeper().getOpenFolder());
+ if (mm == null)
+ return null;
+ Mail mail = new MailImpl(mm);
+ mail.setName(key);
+ return mail;
+ } finally {
+ getFolderGateKeeper().free();
+ }
+ }
+
+ /**
+ * Removes a message identified by key.
+ *
+ * @param key
+ * the key of the message to be removed from the repository
+ */
+ public void remove(String key) throws MessagingException {
+
+ log.debug("UIDFolder remove key:" + key);// , new Throwable());
+ if (lock(key)) {
+ getFolderGateKeeper().use();
+ try {
+ Message mm = getMessageFromInbox(key,
+ (UIDFolder) getFolderGateKeeper().getOpenFolder());
+ if (mm != null) {
+ mm.setFlag(Flags.Flag.DELETED, true);
+ }
+ getUidToKeyBidiMap().removeByKey(key);
+ } finally {
+ unlock(key);
+ getFolderGateKeeper().free();
+ }
+ } else {
+ log.debug("could not optain lock");
+ throw new MessagingException("could not optain lock for remove");
+ }
+ }
+
+ /**
+ * List string keys of messages in repository.
+ *
+ * @return an <code>Iterator</code> over the list of keys in the
+ * repository
+ * @throws MessagingException
+ *
+ */
+ public Iterator list() throws MessagingException {
+ log.debug("UIDPlusFolder list");
+ try {
+ getFolderGateKeeper().use();
+ // needed for retainAllListedAndAddedByKeys(String[], Collection)
+ String[] keysBefore = getUidToKeyBidiMap().getKeys();
+ // get the messages
+ Message[] msgs = getFolderGateKeeper().getOpenFolder().getMessages();
+ Collection keys = new ArrayList(msgs.length);
+ if (msgs == null)
+ throw new RuntimeException("inbox.getMessages returned null");
+ for (int i = 0; i < msgs.length; i++) {
+ try {
+ long uidvalidity = ((UIDFolder) getFolderGateKeeper().getOpenFolder()).getUIDValidity();
+ // lookup uid
+ long uid = ((UIDFolder) getFolderGateKeeper().getOpenFolder()).getUID(msgs[i]);
+ String key = getUidToKeyBidiMap().getByUid(uid);
+ if (key == null) {
+ // generate new key
+ key = "james-uid:" + uidvalidity + ";" + uid + ";"
+ + System.currentTimeMillis() + ";"
+ + getRandom().nextLong();
+ getUidToKeyBidiMap().put(key, uid);
+ }
+ keys.add(key);
+ log.info("list: UID: " + uid + " Key:" + key);
+ } catch (NoSuchElementException e) {
+ // no problem, messages could have been deleted in the
+ // meantime
+ }
+ }
+ // retain only listed keys, and keys added in the meantime (it would
+ // be fatal to loose those)
+ // I don't care about meanwhile removed, those will fail on next
+ // access
+ // it's a good idea to keep count of cached small
+ getUidToKeyBidiMap()
+ .retainAllListedAndAddedByKeys(keysBefore, keys);
+ return keys.iterator();
+ } catch (MessagingException e) {
+ throw new RuntimeException(e);
+ } finally {
+ getFolderGateKeeper().free();
+ }
+ }
+
+ private MimeMessage getMessageFromInbox(String key, UIDFolder inbox)
+ throws MessagingException {
+
+ long uid = getUidToKeyBidiMap().getByKey(key);
+ if (uid < 0) {
+ return null;
+ }
+
+ MimeMessage mm = (MimeMessage) inbox.getMessageByUID(uid);
+ log.info("getMessageFromInbox: UID: " + uid + " Key:" + key);
+ if (mm == null) {
+ getUidToKeyBidiMap().removeByKey(key);
+ log.info("Message not Found");
+ }
+ return mm;
+ }
+
+ /**
+ *
+ * maybe it could be replaced by BidiMap from commons-collections 3.0+
+ *
+ * @author Joachim Draeger <jd at joachim-draeger.de>
+ */
+ private class UidToKeyBidiMapImpl implements UidToKeyBidiMap {
+
+ private Map keyToUid;
+
+ private Map uidToKey;
+
+ public UidToKeyBidiMapImpl() {
+ keyToUid = new HashMap();
+ uidToKey = new HashMap();
+ }
+
+ public synchronized String[] getKeys() {
+ final ArrayList al = new ArrayList(keyToUid.keySet());
+ final String[] keys = (String[]) al.toArray(new String[0]);
+ return keys;
+ }
+
+ public synchronized void retainAllListedAndAddedByKeys(
+ final String[] before, final Collection listed) {
+ Collection added = new HashSet(keyToUid.keySet());
+ added.removeAll(Arrays.asList(before));
+ Collection retain = new HashSet(listed);
+ retain.addAll(added);
+ keyToUid.keySet().retainAll(retain);
+ uidToKey.keySet().retainAll(keyToUid.values());
+ }
+
+ public synchronized void removeByKey(String key) {
+ long uid = getByKey(key);
+ if (uid > -1) {
+ uidToKey.remove(new Long(uid));
+ }
+ keyToUid.remove(key);
+ }
+
+ public synchronized long getByKey(String key) {
+ Long lo = (Long) keyToUid.get(key);
+ long l = -1;
+ if (lo != null) {
+ l = lo.longValue();
+ }
+ return l;
+ }
+
+ public synchronized String getByUid(long uid) {
+
+ return (String) uidToKey.get(new Long(uid));
+ }
+
+ public synchronized boolean containsKey(String key) {
+ return keyToUid.containsKey(key);
+ }
+
+ public synchronized void put(String key, long uid) {
+ keyToUid.put(key, new Long(uid));
+ uidToKey.put(new Long(uid), key);
+ }
+
+ }
+
+ /**
+ * returns a UIDPlusFolderAdapter wrapper
+ *
+ * @see UIDPlusFolderAdapter
+ */
+ protected FolderInterface createAdapter(Folder folder) throws NoSuchMethodException {
+ return new UIDPlusFolderAdapter(folder);
+ }
+
+
+}
Added: james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UidToKeyBidiMap.java
URL: http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UidToKeyBidiMap.java?view=auto&rev=446936
==============================================================================
--- james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UidToKeyBidiMap.java (added)
+++ james/server/trunk/src/java/org/apache/james/mailrepository/javamail/UidToKeyBidiMap.java Sat Sep 16 13:12:01 2006
@@ -0,0 +1,47 @@
+/****************************************************************
+ * 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. *
+ ****************************************************************/
+
+
+
+package org.apache.james.mailrepository.javamail;
+
+import java.util.Collection;
+
+/**
+ * Used to map uids to keys and keys to uids
+ *
+ */
+
+public interface UidToKeyBidiMap {
+
+ boolean containsKey(String key);
+
+ void put(String key, long uid);
+
+ void removeByKey(String key);
+
+ String[] getKeys();
+
+ String getByUid(long uid);
+
+ void retainAllListedAndAddedByKeys(String[] keysBefore, Collection keys);
+
+ long getByKey(String key);
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org