diff --git a/core/src/main/java/org/springframework/security/config/ApacheDSStartStopBean.java b/core/src/main/java/org/springframework/security/config/ApacheDSStartStopBean.java index 2b6797539f..f7430abe7d 100644 --- a/core/src/main/java/org/springframework/security/config/ApacheDSStartStopBean.java +++ b/core/src/main/java/org/springframework/security/config/ApacheDSStartStopBean.java @@ -13,9 +13,11 @@ import org.apache.directory.server.configuration.MutableServerStartupConfigurati import org.apache.directory.server.jndi.ServerContextFactory; import org.apache.directory.server.protocol.shared.store.LdifFileLoader; import org.apache.directory.server.core.configuration.ShutdownConfiguration; +import org.apache.directory.server.core.DirectoryService; import javax.naming.Context; import javax.naming.InitialContext; +import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import java.util.Properties; @@ -23,7 +25,15 @@ import java.io.File; /** * Starts and stops the embedded apacheDS server defined by the supplied configuration. - * Used by {@link LdapBeanDefinitionParser}. + * Used by {@link LdapBeanDefinitionParser}. An instance will be stored in the context for + * each embedded server instance and its InitializingBean and DisposableBean implementations + * used to start and stop the server, respectively. + * + *

+ * If used repeatedly in a single JVM process with the same configuration (for example, when + * repeatedly loading an application context during testing), it's important that the + * application context is closed to allow the bean to be disposed of and the server shutdown + * prior to attempting to start it again. * * @author Luke Taylor * @version $Id$ @@ -34,13 +44,17 @@ class ApacheDSStartStopBean implements InitializingBean, DisposableBean, Applica private MutableServerStartupConfiguration configuration; private ApplicationContext ctxt; private File workingDir; + /** The instance Id of the Apache DS DirectoryServer instance */ + private String instanceId; - public ApacheDSStartStopBean(MutableServerStartupConfiguration configuration) { + private ContextSource contextSource; + + public ApacheDSStartStopBean(MutableServerStartupConfiguration configuration, ContextSource contextSource) { this.configuration = configuration; + this.contextSource = contextSource; } public void afterPropertiesSet() throws Exception { - Properties env = new Properties(); String apacheWorkDir = System.getProperty("apacheDSWorkDir"); if (apacheWorkDir == null) { @@ -49,26 +63,20 @@ class ApacheDSStartStopBean implements InitializingBean, DisposableBean, Applica workingDir = new File(apacheWorkDir); -// if (workingDir.exists()) { -// logger.info("Deleting existing working directory " + workingDir.getAbsolutePath()); -// deleteDir(workingDir); -// } - configuration.setWorkingDirectory(workingDir); - env.put(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName()); - env.setProperty(Context.SECURITY_AUTHENTICATION, "simple"); - env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); - env.setProperty(Context.SECURITY_CREDENTIALS, "secret"); - env.putAll(configuration.toJndiEnvironment()); + // We need this for shutdown + instanceId = configuration.getInstanceId(); - DirContext serverContext = new InitialDirContext(env); + startDirectoryService(); // Import any ldif files Resource[] ldifs = ctxt.getResources("classpath:*.ldif"); - - DirContext dirContext = ((ContextSource)ctxt.getBean("contextSource")).getReadWriteContext(); + // Note that we can't just import using the ServerContext returned + // from starting Apace DS, apparently because of the long-running issue DIRSERVER-169. + // We need a standard context. + DirContext dirContext = contextSource.getReadWriteContext(); if(ldifs != null && ldifs.length > 0) { try { @@ -82,6 +90,25 @@ class ApacheDSStartStopBean implements InitializingBean, DisposableBean, Applica } + private void startDirectoryService() throws NamingException { + DirectoryService ds = DirectoryService.getInstance(instanceId); + + if (ds.isStarted()) { + throw new IllegalStateException("A DirectoryService with Id '" + instanceId + "' is already running."); + } + + logger.info("Starting directory server with Id '" + instanceId + "'"); + Properties env = new Properties(); + + env.setProperty(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName()); + env.setProperty(Context.SECURITY_AUTHENTICATION, "simple"); + env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); + env.setProperty(Context.SECURITY_CREDENTIALS, "secret"); + env.putAll(configuration.toJndiEnvironment()); + + new InitialDirContext(env); + } + public void destroy() throws Exception { Properties env = new Properties(); env.setProperty(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName()); @@ -89,14 +116,14 @@ class ApacheDSStartStopBean implements InitializingBean, DisposableBean, Applica env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system"); env.setProperty(Context.SECURITY_CREDENTIALS, "secret"); - ShutdownConfiguration shutdown = new ShutdownConfiguration(); + ShutdownConfiguration shutdown = new ShutdownConfiguration(instanceId); env.putAll(shutdown.toJndiEnvironment()); - logger.info("Shutting down server..."); + logger.info("Shutting down directory server with Id '" + instanceId + "'"); new InitialContext(env); if (workingDir.exists()) { - logger.info("Deleting working directory after shutting down " + workingDir.getAbsolutePath()); + logger.info("Deleting working directory " + workingDir.getAbsolutePath()); deleteDir(workingDir); } diff --git a/core/src/main/java/org/springframework/security/config/LdapBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/LdapBeanDefinitionParser.java index 42485f8b4e..99ba97e227 100644 --- a/core/src/main/java/org/springframework/security/config/LdapBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/LdapBeanDefinitionParser.java @@ -4,7 +4,6 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.util.StringUtils; import org.springframework.security.ldap.DefaultInitialDirContextFactory; @@ -113,6 +112,8 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser { * @param parserContext * * @return the BeanDefinition for the ContextSource for the embedded server. + * + * @see ApacheDSStartStopBean */ private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) { MutableServerStartupConfiguration configuration = new MutableServerStartupConfiguration(); @@ -145,6 +146,10 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser { //TODO: Allow port configuration configuration.setLdapPort(3389); + // We shut down the server ourself when the app context is closed so we don't need + // the extra shutdown hook from apache DS itself. + configuration.setShutdownHookEnabled(false); + configuration.setExitVmOnShutdown(false); configuration.setContextPartitionConfigurations(partitions); RootBeanDefinition initialDirContextFactory = new RootBeanDefinition(DefaultInitialDirContextFactory.class); @@ -155,6 +160,7 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser { RootBeanDefinition apacheDSStartStop = new RootBeanDefinition(ApacheDSStartStopBean.class); apacheDSStartStop.getConstructorArgumentValues().addGenericArgumentValue(configuration); + apacheDSStartStop.getConstructorArgumentValues().addGenericArgumentValue(initialDirContextFactory); if (parserContext.getRegistry().containsBeanDefinition("_apacheDSStartStopBean")) { //TODO: Appropriate exception @@ -163,7 +169,6 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser { parserContext.getRegistry().registerBeanDefinition("_apacheDSStartStopBean", apacheDSStartStop); - return initialDirContextFactory; }