diff --git a/greenmail-core/pom.xml b/greenmail-core/pom.xml index e9d8e2ab82..cff7efa2cc 100644 --- a/greenmail-core/pom.xml +++ b/greenmail-core/pom.xml @@ -61,6 +61,21 @@ org.hamcrest hamcrest-all + + org.easymock + easymock + test + + + org.powermock + powermock-module-junit4 + test + + + org.powermock + powermock-api-easymock + test + diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/Managers.java b/greenmail-core/src/main/java/com/icegreen/greenmail/Managers.java index c70f1470a1..47ee069037 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/Managers.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/Managers.java @@ -8,6 +8,7 @@ import com.icegreen.greenmail.imap.ImapHostManagerImpl; import com.icegreen.greenmail.smtp.SmtpManager; import com.icegreen.greenmail.store.InMemoryStore; +import com.icegreen.greenmail.store.Store; import com.icegreen.greenmail.user.UserManager; /** @@ -16,19 +17,39 @@ * @since Jan 27, 2006 */ public class Managers { - private ImapHostManager imapHostManager = new ImapHostManagerImpl(new InMemoryStore()); - private UserManager userManager = new UserManager(imapHostManager); - private SmtpManager smtpManager = new SmtpManager(imapHostManager, userManager); + private ImapHostManager imapHostManager; + private UserManager userManager; + private SmtpManager smtpManager; - public SmtpManager getSmtpManager() { + public Managers() { + this(new InMemoryStore()); + } + + protected Managers(final Store imapHostManagerStore) { + imapHostManager = new ImapHostManagerImpl(imapHostManagerStore); + userManager = new UserManager(imapHostManager); + smtpManager = new SmtpManager(imapHostManager, userManager); + } + + public final SmtpManager getSmtpManager() { return smtpManager; } - public UserManager getUserManager() { + public final UserManager getUserManager() { return userManager; } - public ImapHostManager getImapHostManager() { + public final ImapHostManager getImapHostManager() { return imapHostManager; } + + public final void reset() { + imapHostManager = new ImapHostManagerImpl(createNewStore()); + userManager = new UserManager(imapHostManager); + smtpManager = new SmtpManager(imapHostManager, userManager); + } + + protected Store createNewStore() { + return new InMemoryStore(); + } } diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/MemorySafeManagers.java b/greenmail-core/src/main/java/com/icegreen/greenmail/MemorySafeManagers.java new file mode 100644 index 0000000000..2f31616ac9 --- /dev/null +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/MemorySafeManagers.java @@ -0,0 +1,29 @@ +/* + * ------------------------------------------------------------------- + * This software is released under the Apache license 2.0 + * ------------------------------------------------------------------- + * / + */ + +package com.icegreen.greenmail; + +import com.icegreen.greenmail.store.InMemoryStore; +import com.icegreen.greenmail.store.Store; +import com.icegreen.greenmail.store.StoredMessageCollectionFactory; + +/** + * {@link Managers} which uses the {@link StoredMessageCollectionFactory#MAP_BASED_FACTORY MAP_BASED_FACTORY} for + * instantiating the {@link InMemoryStore}. + * + * @author Raimund Klein + */ +public class MemorySafeManagers extends Managers { + public MemorySafeManagers() { + super(new InMemoryStore(StoredMessageCollectionFactory.MAP_BASED_FACTORY)); + } + + @Override + protected Store createNewStore() { + return new InMemoryStore(StoredMessageCollectionFactory.MAP_BASED_FACTORY); + } +} diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/ConfiguredGreenMail.java b/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/ConfiguredGreenMail.java index b01b64dee1..5020fa79ca 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/ConfiguredGreenMail.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/ConfiguredGreenMail.java @@ -1,13 +1,36 @@ package com.icegreen.greenmail.configuration; +import com.icegreen.greenmail.Managers; import com.icegreen.greenmail.base.GreenMailOperations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A version of GreenMailOperations that implements the configure() method. */ public abstract class ConfiguredGreenMail implements GreenMailOperations { + protected final Logger log = LoggerFactory.getLogger(getClass()); private GreenMailConfiguration config; + protected final Managers createManagers() { + if (config == null) + config = GreenMailConfiguration.aConfig(); + Class configuredManagersClass = config.getManagersClass(); + try { + return configuredManagersClass.newInstance(); + } catch (InstantiationException e) { + log.warn(String.format("Could not instantiate Managers class '%s'. Will run with default class '%s'.", + configuredManagersClass, Managers.class), e); + } catch (IllegalAccessException e) { + log.warn(String.format("Illegal access while instantiating Managers class '%s'. Will run with default " + + "class '%s'.", configuredManagersClass, Managers.class), e); + } catch (Exception e) { + log.warn(String.format("General exception while instantiating Managers class '%s'. Will run with default " + + "class '%s'.", configuredManagersClass, Managers.class), e); + } + return new Managers(); + } + @Override public ConfiguredGreenMail withConfiguration(GreenMailConfiguration config) { this.config = config; diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/GreenMailConfiguration.java b/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/GreenMailConfiguration.java index ad6fbf1519..9381dfabfd 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/GreenMailConfiguration.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/configuration/GreenMailConfiguration.java @@ -1,5 +1,7 @@ package com.icegreen.greenmail.configuration; +import com.icegreen.greenmail.Managers; + import java.util.ArrayList; import java.util.List; @@ -8,6 +10,14 @@ */ public class GreenMailConfiguration { private final List usersToCreate = new ArrayList(); + private Class managersClass = Managers.class; + + /** + * @return New GreenMail configuration + */ + public static GreenMailConfiguration aConfig() { + return new GreenMailConfiguration(); + } /** * The given {@link com.icegreen.greenmail.user.GreenMailUser} will be created when servers will start @@ -33,11 +43,12 @@ public GreenMailConfiguration withUser(final String email, final String login, f return this; } - /** - * @return New GreenMail configuration - */ - public static GreenMailConfiguration aConfig() { - return new GreenMailConfiguration(); + public GreenMailConfiguration withManagersClass(final Class managersClass) { + if (managersClass == null) { + throw new NullPointerException("managersClass may not be null."); + } + this.managersClass = managersClass; + return this; } /** @@ -46,4 +57,11 @@ public static GreenMailConfiguration aConfig() { public List getUsersToCreate() { return usersToCreate; } + + /** + * @return The {@link Managers} class to should be used + */ + public Class getManagersClass() { + return managersClass; + } } diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/imap/ImapHostManagerImpl.java b/greenmail-core/src/main/java/com/icegreen/greenmail/imap/ImapHostManagerImpl.java index 172ce3bc09..5c82fc722d 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/imap/ImapHostManagerImpl.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/imap/ImapHostManagerImpl.java @@ -28,8 +28,7 @@ public class ImapHostManagerImpl * Hack constructor which creates an in-memory store, and creates a console logger. */ public ImapHostManagerImpl() { - store = new InMemoryStore(); - subscriptions = new MailboxSubscriptions(); + this(new InMemoryStore()); } public ImapHostManagerImpl(Store store) { @@ -294,6 +293,10 @@ private String getQualifiedMailboxName(GreenMailUser user, String mailboxName) { } } + public Store getStore() { + return store; + } + /** * Handles all user subscriptions. * TODO make this a proper class @@ -348,9 +351,4 @@ private List getUserSubs(GreenMailUser user) { return subs; } } - - @Override - public Store getStore() { - return store; - } } diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/mail/MovingMessage.java b/greenmail-core/src/main/java/com/icegreen/greenmail/mail/MovingMessage.java index fbbfd15d04..cc1e748b02 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/mail/MovingMessage.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/mail/MovingMessage.java @@ -37,14 +37,14 @@ public class MovingMessage { private MimeMessage message; private int _references = 0; - public List getToAddresses() { - return toAddresses; - } - public MovingMessage(Workspace workspace) { _workspace = workspace; } + public List getToAddresses() { + return toAddresses; + } + public MimeMessage getMessage() { return message; } diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/server/AbstractServer.java b/greenmail-core/src/main/java/com/icegreen/greenmail/server/AbstractServer.java index 815cfc75e5..f3076b3682 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/server/AbstractServer.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/server/AbstractServer.java @@ -26,13 +26,13 @@ public abstract class AbstractServer extends Thread implements Service { protected final Logger log = LoggerFactory.getLogger(getClass()); protected final InetAddress bindTo; + private final List handlers = Collections.synchronizedList(new ArrayList()); + private final Object startupMonitor = new Object(); protected ServerSocket serverSocket = null; protected Managers managers; protected ServerSetup setup; - private final List handlers = Collections.synchronizedList(new ArrayList()); private volatile boolean keepRunning = false; private volatile boolean running = false; - private final Object startupMonitor = new Object(); protected AbstractServer(ServerSetup setup, Managers managers) { this.setup = setup; diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/store/HierarchicalFolder.java b/greenmail-core/src/main/java/com/icegreen/greenmail/store/HierarchicalFolder.java index 8dbc2bdb7f..f5006bdc82 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/store/HierarchicalFolder.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/store/HierarchicalFolder.java @@ -30,7 +30,8 @@ class HierarchicalFolder implements MailFolder, UIDFolder { PERMANENT_FLAGS.add(Flags.Flag.SEEN); } - private final StoredMessageCollection mailMessages = new ListBasedStoredMessageCollection(); + private final StoredMessageCollectionFactory storedMessageCollectionFactory; + private final StoredMessageCollection mailMessages; private final List _mailboxListeners = Collections.synchronizedList(new ArrayList()); protected String name; private Collection children; @@ -39,14 +40,20 @@ class HierarchicalFolder implements MailFolder, UIDFolder { private long nextUid = 1; private long uidValidity; - public HierarchicalFolder(HierarchicalFolder parent, - String name) { + protected HierarchicalFolder(final StoredMessageCollectionFactory storedMessageCollectionFactory, + HierarchicalFolder parent, String name) { + this.storedMessageCollectionFactory = storedMessageCollectionFactory; + mailMessages = storedMessageCollectionFactory.createCollection(); this.name = name; this.children = new ArrayList(); this.parent = parent; this.uidValidity = System.currentTimeMillis(); } + public HierarchicalFolder(final HierarchicalFolder castParent, final String mailboxName) { + this(castParent.storedMessageCollectionFactory, castParent, mailboxName); + } + public Collection getChildren() { return children; } diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/store/InMemoryStore.java b/greenmail-core/src/main/java/com/icegreen/greenmail/store/InMemoryStore.java index 5630f710a2..5b6bd0e4e9 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/store/InMemoryStore.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/store/InMemoryStore.java @@ -21,10 +21,18 @@ */ public class InMemoryStore implements Store, ImapConstants { + private final RootFolder rootMailbox; boolean quotaSupported = true; - private RootFolder rootMailbox = new RootFolder(); private Map> quotaMap = new HashMap>(); + public InMemoryStore() { + this(StoredMessageCollectionFactory.LIST_BASED_FACTORY); + } + + public InMemoryStore(final StoredMessageCollectionFactory storedMessageCollectionFactory) { + this.rootMailbox = new RootFolder(storedMessageCollectionFactory); + } + @Override public MailFolder getMailbox(String absoluteMailboxName) { StringTokenizer tokens = new StringTokenizer(absoluteMailboxName, HIERARCHY_DELIMITER); diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/store/RootFolder.java b/greenmail-core/src/main/java/com/icegreen/greenmail/store/RootFolder.java index 58360a4700..bc0af12701 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/store/RootFolder.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/store/RootFolder.java @@ -10,8 +10,8 @@ * @author Raimund Klein */ class RootFolder extends HierarchicalFolder { - public RootFolder() { - super(null, ImapConstants.USER_NAMESPACE); + RootFolder(final StoredMessageCollectionFactory storedMessageCollectionFactory) { + super(storedMessageCollectionFactory, null, ImapConstants.USER_NAMESPACE); } @Override diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/store/StoredMessageCollectionFactory.java b/greenmail-core/src/main/java/com/icegreen/greenmail/store/StoredMessageCollectionFactory.java new file mode 100644 index 0000000000..209681d5f8 --- /dev/null +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/store/StoredMessageCollectionFactory.java @@ -0,0 +1,75 @@ +/* ------------------------------------------------------------------- +* This software is released under the Apache license 2.0 +* ------------------------------------------------------------------- +*/ +package com.icegreen.greenmail.store; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Raimund Klein + */ +public enum StoredMessageCollectionFactory { + LIST_BASED_FACTORY { + @Override + StoredMessageCollection createCollection() { + return new ListBasedStoredMessageCollection(); + } + }, + MAP_BASED_FACTORY { + private static final int DEFAULT_MAXIMUM_MAP_SIZE = 5000; + + @Override + protected Set getAcceptedConfigurationKeys() { + final Set acceptedKeys = super.getAcceptedConfigurationKeys(); + acceptedKeys.add(Constants.CONFIGURATION_KEY_MAXIMUM_MAP_SIZE); + return acceptedKeys; + } + + @Override + StoredMessageCollection createCollection() { + final int maximumMapSize = hasConfigurationFor(Constants.CONFIGURATION_KEY_MAXIMUM_MAP_SIZE) ? + (Integer) getConfiguredValue + (Constants.CONFIGURATION_KEY_MAXIMUM_MAP_SIZE) : DEFAULT_MAXIMUM_MAP_SIZE; + return new MapBasedStoredMessageCollection(maximumMapSize); + } + }; + + private final Map configuration; + + StoredMessageCollectionFactory() { + configuration = new ConcurrentHashMap(); + } + + abstract StoredMessageCollection createCollection(); + + protected Set getAcceptedConfigurationKeys() { + return new HashSet(); + } + + protected final Object getConfiguredValue(final String configurationKey) { + return configuration.get(configurationKey); + } + + protected final boolean hasConfigurationFor(final String configurationKey) { + return configuration.containsKey(configurationKey); + } + + public final StoredMessageCollectionFactory withConfigurationValue(final String key, final Object value) { + final Set acceptedKeys = getAcceptedConfigurationKeys(); + if (acceptedKeys.contains(key)) { + configuration.put(key, value); + return this; + } + + throw new IllegalArgumentException(String.format("Configuration key '%s' unknown. Accepted keys are:%s", key, + acceptedKeys)); + } + + public static class Constants { + public static final String CONFIGURATION_KEY_MAXIMUM_MAP_SIZE = "CONFIGURATION_KEY_MAXIMUM_MAP_SIZE"; + } +} diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/user/UserImpl.java b/greenmail-core/src/main/java/com/icegreen/greenmail/user/UserImpl.java index ec2d66602e..8c309baf9d 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/user/UserImpl.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/user/UserImpl.java @@ -16,9 +16,9 @@ public class UserImpl implements GreenMailUser { - String email; private final int cachedHashCode; private final String cachedHashCodeAsString; + String email; String login; String password; private ImapHostManager imapHostManager; @@ -46,9 +46,9 @@ public void delete() { try { imapHostManager.deleteMailbox(this, ImapConstants.INBOX_NAME); } catch (FolderException e) { - throw new IllegalStateException("Can not delete user "+this, e); + throw new IllegalStateException("Can not delete user " + this, e); } catch (AuthorizationException e) { - throw new IllegalStateException("Can not delete user "+this, e); + throw new IllegalStateException("Can not delete user " + this, e); } } diff --git a/greenmail-core/src/main/java/com/icegreen/greenmail/util/GreenMail.java b/greenmail-core/src/main/java/com/icegreen/greenmail/util/GreenMail.java index 1c5354f2c9..c901dc9980 100644 --- a/greenmail-core/src/main/java/com/icegreen/greenmail/util/GreenMail.java +++ b/greenmail-core/src/main/java/com/icegreen/greenmail/util/GreenMail.java @@ -58,12 +58,36 @@ public GreenMail(ServerSetup[] config) { init(); } + /** + * Create the required services according to the server setup + * + * @param config Service configuration + * @return Services map + */ + private static Map createServices(ServerSetup[] config, Managers mgr) { + Map srvc = new HashMap(); + for (ServerSetup setup : config) { + if (srvc.containsKey(setup.getProtocol())) { + throw new IllegalArgumentException("Server '" + setup.getProtocol() + "' was found at least twice in the array"); + } + final String protocol = setup.getProtocol(); + if (protocol.startsWith(ServerSetup.PROTOCOL_SMTP)) { + srvc.put(protocol, new SmtpServer(setup, mgr)); + } else if (protocol.startsWith(ServerSetup.PROTOCOL_POP3)) { + srvc.put(protocol, new Pop3Server(setup, mgr)); + } else if (protocol.startsWith(ServerSetup.PROTOCOL_IMAP)) { + srvc.put(protocol, new ImapServer(setup, mgr)); + } + } + return srvc; + } + /** * Initialize */ private void init() { if (managers == null) { - managers = new Managers(); + managers = createManagers(); } if(services == null) { services = createServices(config, managers); @@ -118,7 +142,7 @@ public synchronized void stop() { service.stopService(); } } - managers = new Managers(); + managers.reset(); services = null; } @@ -128,30 +152,6 @@ public void reset() { start(); } - /** - * Create the required services according to the server setup - * - * @param config Service configuration - * @return Services map - */ - private static Map createServices(ServerSetup[] config, Managers mgr) { - Map srvc = new HashMap(); - for (ServerSetup setup : config) { - if (srvc.containsKey(setup.getProtocol())) { - throw new IllegalArgumentException("Server '" + setup.getProtocol() + "' was found at least twice in the array"); - } - final String protocol = setup.getProtocol(); - if (protocol.startsWith(ServerSetup.PROTOCOL_SMTP)) { - srvc.put(protocol, new SmtpServer(setup, mgr)); - } else if (protocol.startsWith(ServerSetup.PROTOCOL_POP3)) { - srvc.put(protocol, new Pop3Server(setup, mgr)); - } else if (protocol.startsWith(ServerSetup.PROTOCOL_IMAP)) { - srvc.put(protocol, new ImapServer(setup, mgr)); - } - } - return srvc; - } - @Override public SmtpServer getSmtp() { return (SmtpServer) services.get(ServerSetup.PROTOCOL_SMTP); diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/ManagersTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/ManagersTest.java new file mode 100644 index 0000000000..32864ce59b --- /dev/null +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/ManagersTest.java @@ -0,0 +1,126 @@ +/* + * ------------------------------------------------------------------- + * This software is released under the Apache license 2.0 + * ------------------------------------------------------------------- + * / + */ + +package com.icegreen.greenmail; + +import com.icegreen.greenmail.imap.ImapHostManagerImpl; +import com.icegreen.greenmail.smtp.SmtpManager; +import com.icegreen.greenmail.store.InMemoryStore; +import com.icegreen.greenmail.store.Store; +import com.icegreen.greenmail.user.UserManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.powermock.api.easymock.PowerMock.*; + +/** + * @author Raimund Klein + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(Managers.class) +public class ManagersTest { + + @Test + public void shouldUseDefaultInMemoryStoreIfNoneGiven() throws Exception { + // Given + final InMemoryStore mockStore = createMockAndExpectNew(InMemoryStore.class); + replay(mockStore, InMemoryStore.class); + + // When + new Managers(); + + // Then + verify(mockStore, InMemoryStore.class); + } + + @Test + public void shouldCreateNewImapHostManagerImpl() throws Exception { + // Given + final Store store = new InMemoryStore(); + final ImapHostManagerImpl mockHostManager = createMockAndExpectNew(ImapHostManagerImpl.class, store); + replay(mockHostManager, ImapHostManagerImpl.class); + + // When + new Managers(store); + + // Then + verify(mockHostManager, ImapHostManagerImpl.class); + } + + @Test + public void shouldCreateNewUserManager() throws Exception { + // Given + final Store store = new InMemoryStore(); + final ImapHostManagerImpl mockHostManager = createMock(ImapHostManagerImpl.class, store); + expectNew(ImapHostManagerImpl.class, store).andReturn(mockHostManager); + final UserManager mockUserManager = createMockAndExpectNew(UserManager.class, mockHostManager); + replay(mockHostManager, ImapHostManagerImpl.class, mockUserManager, UserManager.class); + + // When + new Managers(store); + + // Then + verify(mockUserManager, UserManager.class); + } + + @Test + public void shouldCreateNewSmtpManager() throws Exception { + // Given + final Store store = new InMemoryStore(); + final ImapHostManagerImpl mockHostManager = createMock(ImapHostManagerImpl.class, store); + expectNew(ImapHostManagerImpl.class, store).andReturn(mockHostManager); + final UserManager mockUserManager = createMock(UserManager.class, mockHostManager); + expectNew(UserManager.class, mockHostManager).andReturn(mockUserManager); + final SmtpManager mockSmtpManager = createMock(SmtpManager.class, + mockHostManager, mockUserManager); + replay(mockHostManager, ImapHostManagerImpl.class, mockUserManager, UserManager.class, mockSmtpManager, + SmtpManager.class); + + // When + new Managers(store); + + // Then + verify(mockSmtpManager, SmtpManager.class); + } + + @Test + public void shouldCreateDefaultInMemoryStore() throws Exception { + // Given + final Managers managers = new Managers(); + final Store mockStore = createMockAndExpectNew(InMemoryStore.class); + replay(mockStore, InMemoryStore.class); + + // When + final Store createdStore = managers.createNewStore(); + + // Then + verify(mockStore, InMemoryStore.class); + assertThat(createdStore, is(mockStore)); + } + + @Test + public void shouldRecreateStoreAndAllSubManagers() throws Exception { + // Given + final Managers managers = new Managers(); + final InMemoryStore mockStore = createMockAndExpectNew(InMemoryStore.class); + final ImapHostManagerImpl mockHostManager = createMockAndExpectNew(ImapHostManagerImpl.class, mockStore); + final UserManager mockUserManager = createMockAndExpectNew(UserManager.class, mockHostManager); + replay(mockStore, InMemoryStore.class, mockHostManager, ImapHostManagerImpl.class, mockUserManager, + UserManager.class); + + // When + managers.reset(); + + // Then + verify(mockStore, InMemoryStore.class, mockHostManager, ImapHostManagerImpl.class, mockUserManager, + UserManager.class); + } +} \ No newline at end of file diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/MemorySafeManagersTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/MemorySafeManagersTest.java new file mode 100644 index 0000000000..0b36092154 --- /dev/null +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/MemorySafeManagersTest.java @@ -0,0 +1,55 @@ +/* + * ------------------------------------------------------------------- + * This software is released under the Apache license 2.0 + * ------------------------------------------------------------------- + * / + */ + +package com.icegreen.greenmail; + +import com.icegreen.greenmail.store.InMemoryStore; +import com.icegreen.greenmail.store.Store; +import com.icegreen.greenmail.store.StoredMessageCollectionFactory; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.powermock.api.easymock.PowerMock.*; + +/** + * @author Raimund Klein + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(MemorySafeManagers.class) +public class MemorySafeManagersTest { + @Test + public void shouldUseMapBasedFactoryInConstructor() throws Exception { + // Given + final InMemoryStore mockStore = createMockAndExpectNew(InMemoryStore.class, StoredMessageCollectionFactory.MAP_BASED_FACTORY); + replay(mockStore, InMemoryStore.class); + + // When + new MemorySafeManagers(); + + // Then + verify(mockStore, InMemoryStore.class); + } + + @Test + public void shouldCreateInMemoryStoreWithMapBasedFactory() throws Exception { + // Given + final Managers managers = new MemorySafeManagers(); + final Store mockStore = createMockAndExpectNew(InMemoryStore.class, StoredMessageCollectionFactory.MAP_BASED_FACTORY); + replay(mockStore, InMemoryStore.class); + + // When + final Store createdStore = managers.createNewStore(); + + // Then + verify(mockStore, InMemoryStore.class); + assertThat(createdStore, is(mockStore)); + } +} \ No newline at end of file diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTest.java index 6452215a12..f4f7d61573 100644 --- a/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTest.java +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTest.java @@ -1,10 +1,14 @@ package com.icegreen.greenmail.configuration; +import com.icegreen.greenmail.Managers; +import com.icegreen.greenmail.MemorySafeManagers; import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.ServerSetupTest; import org.junit.Test; +import static com.icegreen.greenmail.configuration.GreenMailConfigurationTestBase.testConfigWithMemorySafeManagers; import static com.icegreen.greenmail.configuration.GreenMailConfigurationTestBase.testUsersAccessibleConfig; +import static org.junit.Assert.assertSame; /** * Tests if the configuration is applied correctly to GreenMail instances. @@ -23,4 +27,28 @@ public void testUsersAccessible() { } } + @Test + public void shouldCreateManagersClassByDefault() { + // Given + final GreenMail greenMail = new GreenMail(ServerSetupTest.IMAP); + + // When + final Managers managers = greenMail.createManagers(); + + // Then + assertSame(Managers.class, managers.getClass()); + } + + @Test + public void shouldCreateMemorySafeManagersWhenConfigured() { + // Given + final GreenMail greenMail = new GreenMail(ServerSetupTest.IMAP).withConfiguration + (testConfigWithMemorySafeManagers()); + + // When + final Managers managers = greenMail.createManagers(); + + // Then + assertSame(MemorySafeManagers.class, managers.getClass()); + } } \ No newline at end of file diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTestBase.java b/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTestBase.java index a1788e7e82..04c2ddb74d 100644 --- a/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTestBase.java +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/configuration/GreenMailConfigurationTestBase.java @@ -1,5 +1,6 @@ package com.icegreen.greenmail.configuration; +import com.icegreen.greenmail.MemorySafeManagers; import com.icegreen.greenmail.base.GreenMailOperations; import com.icegreen.greenmail.util.Retriever; @@ -45,4 +46,11 @@ public static GreenMailConfiguration testUsersAccessibleConfig() { .withUser("user@localhost", "password") .withUser("secondUser@localhost", "secondUserLogin", "password2"); } + + /** + * @return A {@link GreenMailConfiguration} with {@link MemorySafeManagers} as Managers class. + */ + public static GreenMailConfiguration testConfigWithMemorySafeManagers() { + return aConfig().withManagersClass(MemorySafeManagers.class); + } } diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/imap/ImapHostManagerImplTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/imap/ImapHostManagerImplTest.java new file mode 100644 index 0000000000..f786c54c02 --- /dev/null +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/imap/ImapHostManagerImplTest.java @@ -0,0 +1,34 @@ +/* ------------------------------------------------------------------- +* This software is released under the Apache license 2.0 +* ------------------------------------------------------------------- +*/ +package com.icegreen.greenmail.imap; + +import com.icegreen.greenmail.store.InMemoryStore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.powermock.api.easymock.PowerMock.*; + +/** + * @author Raimund Klein + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(ImapHostManagerImpl.class) +public class ImapHostManagerImplTest { + + @Test + public void shouldUseDefaultInMemoryStoreIfNoneGiven() throws Exception { + // Given + final InMemoryStore mockStore = createMockAndExpectNew(InMemoryStore.class); + replay(mockStore, InMemoryStore.class); + + // When + new ImapHostManagerImpl(); + + // Then + verify(mockStore, InMemoryStore.class); + } +} \ No newline at end of file diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/store/InMemoryStoreTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/store/InMemoryStoreTest.java new file mode 100644 index 0000000000..7b0d3a12df --- /dev/null +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/store/InMemoryStoreTest.java @@ -0,0 +1,49 @@ +/* ------------------------------------------------------------------- +* This software is released under the Apache license 2.0 +* ------------------------------------------------------------------- +*/ +package com.icegreen.greenmail.store; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.powermock.api.easymock.PowerMock.*; + +/** + * @author Raimund Klein + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(InMemoryStore.class) +public class InMemoryStoreTest { + @Test + public void shouldUsedListBasedFactoryByDefault() throws Exception { + // Given + final RootFolder mockFolder = createMockAndExpectNew(RootFolder.class, StoredMessageCollectionFactory + .LIST_BASED_FACTORY); + replay(mockFolder, RootFolder.class); + + // When + new InMemoryStore(); + + // Then + verify(mockFolder, RootFolder.class); + } + + @Test + public void shouldForwardGivenFactory() throws Exception { + for (final StoredMessageCollectionFactory factory : StoredMessageCollectionFactory.values()) { + // Given + final RootFolder mockFolder = createMockAndExpectNew(RootFolder.class, factory); + replay(mockFolder, RootFolder.class); + + // When + new InMemoryStore(factory); + + // Then + verify(mockFolder, RootFolder.class); + reset(RootFolder.class); + } + } +} \ No newline at end of file diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/store/StoredMessageCollectionFactoryPowerMockTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/store/StoredMessageCollectionFactoryPowerMockTest.java new file mode 100644 index 0000000000..50a40ab791 --- /dev/null +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/store/StoredMessageCollectionFactoryPowerMockTest.java @@ -0,0 +1,66 @@ +/* + * ------------------------------------------------------------------- + * This software is released under the Apache license 2.0 + * ------------------------------------------------------------------- + * / + */ + +package com.icegreen.greenmail.store; + +import org.hamcrest.Matchers; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.powermock.api.easymock.PowerMock.*; + +/** + * @author Raimund Klein + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(StoredMessageCollectionFactory.class) +public class StoredMessageCollectionFactoryPowerMockTest { + // The following tests require a fix for PowerMock issue #552: + // https://code.google.com/p/powermock/issues/detail?id=552 + + @Test + @Ignore + public void shouldCreateMapBasedCollectionWithSize5000ByDefault() throws Exception { + // Given + final MapBasedStoredMessageCollection mockCollection = createMockAndExpectNew(MapBasedStoredMessageCollection + .class, new Class[]{int.class}, 5000); + replay(mockCollection, MapBasedStoredMessageCollection.class); + + // When + final StoredMessageCollection createdCollection = StoredMessageCollectionFactory.MAP_BASED_FACTORY + .createCollection(); + + // Then + assertThat(createdCollection, is(Matchers.sameInstance(mockCollection))); + verify(mockCollection, MapBasedStoredMessageCollection.class); + } + + @Test + @Ignore + public void shouldCreateMapBasedCollectionWithGivenSizeIfPropertyIsSet() throws Exception { + // Given + final int expectedMapSize = 200; + StoredMessageCollectionFactory.MAP_BASED_FACTORY.withConfigurationValue(StoredMessageCollectionFactory + .Constants.CONFIGURATION_KEY_MAXIMUM_MAP_SIZE, expectedMapSize); + final MapBasedStoredMessageCollection mockCollection = createMockAndExpectNew(MapBasedStoredMessageCollection + .class, new Class[]{int.class}, expectedMapSize); + replay(mockCollection, MapBasedStoredMessageCollection.class); + + // When + final StoredMessageCollection createdCollection = StoredMessageCollectionFactory.MAP_BASED_FACTORY + .createCollection(); + + // Then + assertThat(createdCollection, is(Matchers.sameInstance(mockCollection))); + verify(mockCollection, MapBasedStoredMessageCollection.class); + } +} diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/store/StoredMessageCollectionFactoryTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/store/StoredMessageCollectionFactoryTest.java new file mode 100644 index 0000000000..bef9bd198c --- /dev/null +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/store/StoredMessageCollectionFactoryTest.java @@ -0,0 +1,60 @@ +/* ------------------------------------------------------------------- +* This software is released under the Apache license 2.0 +* ------------------------------------------------------------------- +*/ +package com.icegreen.greenmail.store; + +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * @author Raimund Klein + */ +@RunWith(Theories.class) +public class StoredMessageCollectionFactoryTest { + @DataPoints + public static FactoryWithMatchingClass[] createFactoryPoints() { + return new FactoryWithMatchingClass[]{ + new FactoryWithMatchingClass(StoredMessageCollectionFactory.LIST_BASED_FACTORY, ListBasedStoredMessageCollection.class), + new FactoryWithMatchingClass(StoredMessageCollectionFactory.MAP_BASED_FACTORY, MapBasedStoredMessageCollection.class)}; + } + + @Theory + public void shouldCreateMatchingCollection(final FactoryWithMatchingClass factoryWithMatchingClass) { + // When + final StoredMessageCollection collection = factoryWithMatchingClass.factory.createCollection(); + + // Then + assertThat(collection, is(Matchers.instanceOf(factoryWithMatchingClass.collectionClass))); + } + + private static class FactoryWithMatchingClass { + private final StoredMessageCollectionFactory factory; + private final Class collectionClass; + + private FactoryWithMatchingClass(StoredMessageCollectionFactory factory, Class collectionClass) { + this.factory = factory; + this.collectionClass = collectionClass; + } + } + + @Test + public void shouldRejectUnknownProperty() { + for (final StoredMessageCollectionFactory factory : StoredMessageCollectionFactory.values()) { + try { + factory.withConfigurationValue("foo", "bar"); + fail("Should have thrown exception"); + } catch (IllegalArgumentException e) { + // This is expected. + } + } + } +} \ No newline at end of file diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/test/GreenMailUtilTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/test/GreenMailUtilTest.java index 42888c1086..e155f7f095 100644 --- a/greenmail-core/src/test/java/com/icegreen/greenmail/test/GreenMailUtilTest.java +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/test/GreenMailUtilTest.java @@ -25,6 +25,26 @@ * @since Jan 29, 2006 */ public class GreenMailUtilTest { + final static String SAMPLE_EMAIL = "From - Thu Jan 19 00:30:34 2006\r\n" + + "X-Account-Key: account245\r\n" + + "X-UIDL: 11332317636080.2607.mail5,S=833\r\n" + + "X-Mozilla-Status: 0001\r\n" + + "X-Mozilla-Status2: 00000000\r\n" + + "Return-Path: \r\n" + + "Delivered-To: eivar@blastigen.com\r\n" + + "Received: (qmail 2376 invoked from network); 19 Jan 2006 02:01:05 -0000\r\n" + + "Received: from unknown (HELO [192.168.0.5]) (hej@66.245.216.76)\r\n" + + "\tby mail5.hotmail.com with (RC4-MD5 encrypted) SMTP; Wed, 18 Jan 2006 18:01:05 -0800\r\n" + + "Message-ID: <43CEF322.7080702@hotmail.com>\r\n" + + "Date: Wed, 18 Jan 2006 18:02:10 -0800\r\n" + + "From: Wael Chatila \r\n" + + "User-Agent: Mozilla Thunderbird 1.0.7 (Windows/20050923)\r\n" + + "X-Accept-Language: en-us, en\r\n" + "MIME-Version: 1.0\r\n" + + "To: Bertil \r\n" + "Subject: wassup\r\n" + + "Content-Type: text/plain; charset=ISO-8859-1; format=flowed\r\n" + + "Content-Transfer-Encoding: 7bit\r\n" + "\r\n" + + "Yo wassup Bertil\r\n"; + @Test public void testMimeMessageLoading() throws MessagingException { MimeMessage message = GreenMailUtil.newMimeMessage(SAMPLE_EMAIL); @@ -101,24 +121,4 @@ public void testSetAndGetQuota() throws MessagingException { greenMail.stop(); } } - - final static String SAMPLE_EMAIL = "From - Thu Jan 19 00:30:34 2006\r\n" - + "X-Account-Key: account245\r\n" - + "X-UIDL: 11332317636080.2607.mail5,S=833\r\n" - + "X-Mozilla-Status: 0001\r\n" - + "X-Mozilla-Status2: 00000000\r\n" - + "Return-Path: \r\n" - + "Delivered-To: eivar@blastigen.com\r\n" - + "Received: (qmail 2376 invoked from network); 19 Jan 2006 02:01:05 -0000\r\n" - + "Received: from unknown (HELO [192.168.0.5]) (hej@66.245.216.76)\r\n" - + "\tby mail5.hotmail.com with (RC4-MD5 encrypted) SMTP; Wed, 18 Jan 2006 18:01:05 -0800\r\n" - + "Message-ID: <43CEF322.7080702@hotmail.com>\r\n" - + "Date: Wed, 18 Jan 2006 18:02:10 -0800\r\n" - + "From: Wael Chatila \r\n" - + "User-Agent: Mozilla Thunderbird 1.0.7 (Windows/20050923)\r\n" - + "X-Accept-Language: en-us, en\r\n" + "MIME-Version: 1.0\r\n" - + "To: Bertil \r\n" + "Subject: wassup\r\n" - + "Content-Type: text/plain; charset=ISO-8859-1; format=flowed\r\n" - + "Content-Transfer-Encoding: 7bit\r\n" + "\r\n" - + "Yo wassup Bertil\r\n"; } diff --git a/greenmail-core/src/test/java/com/icegreen/greenmail/test/ServerStartStopTest.java b/greenmail-core/src/test/java/com/icegreen/greenmail/test/ServerStartStopTest.java index ec02192e26..54c4e97dff 100644 --- a/greenmail-core/src/test/java/com/icegreen/greenmail/test/ServerStartStopTest.java +++ b/greenmail-core/src/test/java/com/icegreen/greenmail/test/ServerStartStopTest.java @@ -53,7 +53,7 @@ public void testServerStartupTimeout() { } // Set large startup timeout - for(ServerSetup s: setups) { + for (ServerSetup s : setups) { s.setServerStartupTimeout(3000L); } service = new GreenMail(setups); diff --git a/greenmail-core/src/test/resources/log4j.xml b/greenmail-core/src/test/resources/log4j.xml index 2d7728a79c..553a61dd01 100644 --- a/greenmail-core/src/test/resources/log4j.xml +++ b/greenmail-core/src/test/resources/log4j.xml @@ -26,7 +26,7 @@ - + diff --git a/greenmail-site/index.html b/greenmail-site/index.html index 77a77d4c47..b2cfc7f26f 100644 --- a/greenmail-site/index.html +++ b/greenmail-site/index.html @@ -745,16 +745,18 @@

How can I use IMAP quotas?

ImapServerTest.java quota test

How can I create or delete a mail user?

+


-GreenMail greenMail = ...
-// Create user with login id equals email
-GreenMailUser user1 = greenMail.setUser("foo@localhost", "some secret pwd");
-// Create user with login id different than email
-GreenMailUser user2 = greenMail.setUser("foo@localhost", "login-id", "some secret pwd");
-...
-greenMail.getManagers().getUserManager().deleteUser(user1); // Delete user
-
+ GreenMail greenMail = ... + // Create user with login id equals email + GreenMailUser user1 = greenMail.setUser("foo@localhost", "some secret pwd"); + // Create user with login id different than email + GreenMailUser user2 = greenMail.setUser("foo@localhost", "login-id", "some secret pwd"); + ... + greenMail.getManagers().getUserManager().deleteUser(user1); // Delete user + +

diff --git a/pom.xml b/pom.xml index c613cef6ca..6a1ae80b02 100644 --- a/pom.xml +++ b/pom.xml @@ -253,6 +253,7 @@ 1.7.12 1.3 + 1.6.3 4.1.7.RELEASE UTF-8 UTF-8 @@ -365,6 +366,18 @@ 3.2 test + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-easymock + ${powermock.version} + test + org.hamcrest hamcrest-core