Commits

Anonymous committed b667ec4

Split ConfigurationProvider into two interfaces, made package loading pluggable
XW-575 WW-2129

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@1620e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (9)

src/java/com/opensymphony/xwork2/XWorkTestCase.java

     }
     
     protected void loadButAdd(final Class<?> type, final Object impl) {
+        loadButAdd(type, Container.DEFAULT_NAME, impl);
+    }
+    
+    protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
         loadConfigurationProviders(new StubConfigurationProvider() {
             public void register(ContainerBuilder builder,
                     LocatableProperties props) throws ConfigurationException {
-                builder.factory(type, new Factory() {
+                builder.factory(type, name, new Factory() {
                     public Object create(Context context) throws Exception {
                         return impl;
                     }

src/java/com/opensymphony/xwork2/config/Configuration.java

      */
     void destroy();
 
+    /**
+     * @deprecated Since 2.1
+     * @param providers
+     * @throws ConfigurationException
+     */
     void reload(List<ConfigurationProvider> providers) throws ConfigurationException;
+    
+    /**
+     * @since 2.1
+     * @param containerProviders
+     * @throws ConfigurationException
+     */
+    List<PackageProvider> reloadContainer(List<ContainerProvider> containerProviders) throws ConfigurationException;
 
     void removePackageConfig(String name);
 

src/java/com/opensymphony/xwork2/config/ConfigurationManager.java

 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.locks.Lock;
     protected static final Log LOG = LogFactory.getLog(ConfigurationManager.class);
     protected Configuration configuration;
     protected Lock providerLock = new ReentrantLock();
-    private List<ConfigurationProvider> configurationProviders = new CopyOnWriteArrayList<ConfigurationProvider>();
+    private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
+    private List<PackageProvider> packageProviders = new CopyOnWriteArrayList<PackageProvider>();
     protected String defaultFrameworkBeanName;
 
     public ConfigurationManager() {
         if (configuration == null) {
             setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
             try {
-                configuration.reload(getConfigurationProviders());
+                configuration.reloadContainer(getContainerProviders());
             } catch (ConfigurationException e) {
                 setConfiguration(null);
                 throw new ConfigurationException("Unable to load configuration.", e);
     public synchronized void setConfiguration(Configuration configuration) {
         this.configuration = configuration;
     }
+    
+    /**
+     * Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method
+     * will return a list containing only the default ConfigurationProvider, XMLConfigurationProvider.  if a custom
+     * ConfigurationProvider has been added, then the XmlConfigurationProvider must be added by hand.
+     * </p>
+     * <p/>
+     * WARNING: This returns only ContainerProviders that can be cast into ConfigurationProviders
+     *
+     * @return the list of registered ConfigurationProvider objects
+     * @see ConfigurationProvider
+     * @deprecated Since 2.1, use {@link #getContainerProviders()}
+     */
+    public List<ConfigurationProvider> getConfigurationProviders() {
+        List<ContainerProvider> contProviders = getContainerProviders();
+        List<ConfigurationProvider> providers = new ArrayList<ConfigurationProvider>();
+        for (ContainerProvider prov : contProviders) {
+            if (prov instanceof ConfigurationProvider) {
+                providers.add((ConfigurationProvider) prov);
+            }
+        }
+        return providers;
+    }
 
     /**
      * Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method
      * @return the list of registered ConfigurationProvider objects
      * @see ConfigurationProvider
      */
-    public List<ConfigurationProvider> getConfigurationProviders() {
+    public List<ContainerProvider> getContainerProviders() {
         providerLock.lock();
         try {
-            if (configurationProviders.size() == 0) {
-                configurationProviders.add(new XWorkConfigurationProvider());
-                configurationProviders.add(new XmlConfigurationProvider("xwork.xml", false));
+            if (containerProviders.size() == 0) {
+                containerProviders.add(new XWorkConfigurationProvider());
+                containerProviders.add(new XmlConfigurationProvider("xwork.xml", false));
             }
 
-            return configurationProviders;
+            return containerProviders;
         } finally {
             providerLock.unlock();
         }
      * Set the list of configuration providers
      *
      * @param configurationProviders
+     * @deprecated Since 2.1, use {@link #setContainerProvider()}
      */
     public void setConfigurationProviders(List<ConfigurationProvider> configurationProviders) {
+        // Silly copy necessary due to lack of ability to cast generic lists
+        List<ContainerProvider> contProviders = new ArrayList<ContainerProvider>();
+        contProviders.addAll(configurationProviders);
+        
+        setContainerProviders(contProviders);
+    }
+        
+    /**
+     * Set the list of configuration providers
+     *
+     * @param containerProviders
+     */
+    public void setContainerProviders(List<ContainerProvider> containerProviders) {
         providerLock.lock();
         try {
-            this.configurationProviders = new CopyOnWriteArrayList<ConfigurationProvider>(configurationProviders);
+            this.containerProviders = new CopyOnWriteArrayList<ContainerProvider>(containerProviders);
         } finally {
             providerLock.unlock();
         }
      * more than once
      *
      * @param provider the ConfigurationProvider to register
+     * @deprecated Since 2.1, use {@link #addContainerProvider()}
      */
     public void addConfigurationProvider(ConfigurationProvider provider) {
-        if (!configurationProviders.contains(provider)) {
-            configurationProviders.add(provider);
+        addContainerProvider(provider);
+    }
+        
+    /**
+     * adds a configuration provider to the List of ConfigurationProviders.  a given ConfigurationProvider may be added
+     * more than once
+     *
+     * @param provider the ConfigurationProvider to register
+     */
+    public void addContainerProvider(ContainerProvider provider) {
+        if (!containerProviders.contains(provider)) {
+            containerProviders.add(provider);
         }
     }
     
      * ConfigurationProviders
      *
      * @see com.opensymphony.xwork2.config.ConfigurationProvider#destroy
+     * @deprecated Since 2.1, use {@link #clearContainerProviders()}
      */
     public void clearConfigurationProviders() {
-        for (ConfigurationProvider configurationProvider : configurationProviders) {
-        	try {
-        		configurationProvider.destroy();
-        	}
-        	catch(Exception e) {
-        		LOG.warn("error while destroying configuration provider ["+configurationProvider+"]", e);
-        	}
+        clearContainerProviders();
+    }
+    
+    public void clearContainerProviders() {
+        for (ContainerProvider containerProvider : containerProviders) {
+            try {
+                containerProvider.destroy();
+            }
+            catch(Exception e) {
+                LOG.warn("error while destroying container provider ["+containerProvider+"]", e);
+            }
         }
-        configurationProviders.clear();
+        containerProviders.clear();
     }
 
     /**
      */
     public synchronized void destroyConfiguration() {
         clearConfigurationProviders(); // let's destroy the ConfigurationProvider first
-        setConfigurationProviders(new CopyOnWriteArrayList<ConfigurationProvider>());
+        containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
         if (configuration != null)
             configuration.destroy(); // let's destroy it first, before nulling it.
         configuration = null;
 
             reload = false;
 
-            List<ConfigurationProvider> providers = getConfigurationProviders();
-            for (ConfigurationProvider provider : providers) {
+            List<ContainerProvider> providers = getContainerProviders();
+            for (ContainerProvider provider : providers) {
                 if (provider.needsReload()) {
                     if (LOG.isInfoEnabled()) {
-                        LOG.info("Detected configuration provider "+provider+" needs to be reloaded.  Reloading all providers.");
+                        LOG.info("Detected container provider "+provider+" needs to be reloaded.  Reloading all providers.");
                     }
                     reload = true;
 
                     break;
                 }
             }
+            
+            if (packageProviders != null) {
+                for (PackageProvider provider : packageProviders) {
+                    if (provider.needsReload()) {
+                        if (LOG.isInfoEnabled()) {
+                            LOG.info("Detected package provider "+provider+" needs to be reloaded.  Reloading all providers.");
+                        }
+                        reload = true;
+    
+                        break;
+                    }
+                }
+            }
 
             if (reload) {
-            	for (ConfigurationProvider configurationProvider : configurationProviders) {
+            	for (ContainerProvider containerProvider : containerProviders) {
                 	try {
-                		configurationProvider.destroy();
+                		containerProvider.destroy();
                 	}
                 	catch(Exception e) {
-                		LOG.warn("error while destroying configuration provider ["+configurationProvider+"]", e);
+                		LOG.warn("error while destroying configuration provider ["+containerProvider+"]", e);
                 	}
                 }
-                configuration.reload(providers);
+                packageProviders = configuration.reloadContainer(providers);
             }
         }
     }
     
     public synchronized void reload() {
-        getConfiguration().reload(getConfigurationProviders());
+        packageProviders = getConfiguration().reloadContainer(getContainerProviders());
     }
 }

src/java/com/opensymphony/xwork2/config/ConfigurationProvider.java

  */
 package com.opensymphony.xwork2.config;
 
-import com.opensymphony.xwork2.inject.ContainerBuilder;
-import com.opensymphony.xwork2.util.location.LocatableProperties;
-
-
 /**
  * Interface to be implemented by all forms of XWork configuration classes.
- *
- * @author $Author$
- * @version $Revision$
  */
-public interface ConfigurationProvider {
-
-    public void destroy();
-    
-    public void init(Configuration configuration) throws ConfigurationException;
-    
-    public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
-    
-    public void loadPackages() throws ConfigurationException;
-    
-    /**
-     * Tells whether the ConfigurationProvider should reload its configuration
-     *
-     * @return <tt>true</tt>, whether the ConfigurationProvider should reload its configuration, <tt>false</tt>otherwise.
-     */
-    public boolean needsReload();
-    
+public interface ConfigurationProvider extends ContainerProvider, PackageProvider {
 }

src/java/com/opensymphony/xwork2/config/ContainerProvider.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.config;
+
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+
+
+/**
+ * Provides beans and constants/properties for the Container
+ * 
+ * @since 2.1
+ */
+public interface ContainerProvider {
+
+    /**
+     * Called before removed from the configuration manager
+     */
+    public void destroy();
+    
+    /**
+     * Initializes with the configuration
+     * @param configuration The configuration
+     * @throws ConfigurationException If anything goes wrong
+     */
+    public void init(Configuration configuration) throws ConfigurationException;
+    
+    /**
+     * Tells whether the ContainerProvider should reload its configuration
+     *
+     * @return <tt>true</tt>, whether the ContainerProvider should reload its configuration, <tt>false</tt>otherwise.
+     */
+    public boolean needsReload();
+    
+    /**
+     * Registers beans and properties for the Container
+     * 
+     * @param builder The builder to register beans with
+     * @param props The properties to register constants with
+     * @throws ConfigurationException If anything goes wrong
+     */
+    public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
+    
+}

src/java/com/opensymphony/xwork2/config/PackageProvider.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.config;
+
+/**
+ * Provides configuration packages.  The separate init and loadPackages calls are due to the need to 
+ * preserve backwards compatibility with the 2.0 {@link ConfigurationProvider} interface
+ * 
+ * @since 2.1
+ */
+public interface PackageProvider {
+    
+    /**
+     * Initializes with the configuration
+     * @param configuration The configuration
+     * @throws ConfigurationException If anything goes wrong
+     */
+    public void init(Configuration configuration) throws ConfigurationException;
+    
+    /**
+     * Tells whether the PackageProvider should reload its configuration
+     *
+     * @return <tt>true</tt>, whether the PackageProvider should reload its configuration, <tt>false</tt>otherwise.
+     */
+    public boolean needsReload();
+
+    /**
+     * Loads the packages for the configuration.
+     * @throws ConfigurationException
+     */
+    public void loadPackages() throws ConfigurationException;
+    
+}

src/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java

 import com.opensymphony.xwork2.config.Configuration;
 import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.config.ContainerProvider;
+import com.opensymphony.xwork2.config.PackageProvider;
 import com.opensymphony.xwork2.config.RuntimeConfiguration;
 import com.opensymphony.xwork2.config.entities.*;
 import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
     protected static final Log LOG = LogFactory.getLog(DefaultConfiguration.class);
 
 
-    // Programmatic Action Conifigurations
+    // Programmatic Action Configurations
     protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>();
     protected RuntimeConfiguration runtimeConfiguration;
     protected Container container;
     public void rebuildRuntimeConfiguration() {
         runtimeConfiguration = buildRuntimeConfiguration();
     }
-
+    
     /**
      * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
      * buildRuntimeConfiguration().
      * @throws ConfigurationException
      */
     public synchronized void reload(List<ConfigurationProvider> providers) throws ConfigurationException {
+        
+        // Silly copy necessary due to lack of ability to cast generic lists
+        List<ContainerProvider> contProviders = new ArrayList<ContainerProvider>();
+        contProviders.addAll(providers);
+        
+        reloadContainer(contProviders);
+    }
+
+    /**
+     * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
+     * buildRuntimeConfiguration().
+     *
+     * @throws ConfigurationException
+     */
+    public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
         packageContexts.clear();
         loadedFileNames.clear();
+        List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
 
         ContainerProperties props = new ContainerProperties();
         ContainerBuilder builder = new ContainerBuilder();
-        for (ConfigurationProvider configurationProvider : providers)
+        for (final ContainerProvider containerProvider : providers)
         {
-            configurationProvider.init(this);
-            configurationProvider.register(builder, props);
+            containerProvider.init(this);
+            containerProvider.register(builder, props);
+            if (containerProvider instanceof PackageProvider) {
+                builder.factory(PackageProvider.class, containerProvider.toString(), new Factory<PackageProvider>() {
+                    boolean injected = false;
+                    public PackageProvider create(Context context) throws Exception {
+                        if (!injected) {
+                            context.getContainer().inject(containerProvider);
+                            injected = true;
+                        }
+                        return (PackageProvider)containerProvider;
+                    }
+                });
+            }
         }
         props.setConstants(builder);
         
             setContext(container);
             objectFactory = container.getInstance(ObjectFactory.class);
             
-            for (ConfigurationProvider configurationProvider : providers)
-            {
-                container.inject(configurationProvider);
-                configurationProvider.loadPackages();
+            Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
+            for (String name : packageProviderNames) {
+                PackageProvider provider = container.getInstance(PackageProvider.class, name);
+                
+                // Ensure the init method is only called once in the case of ConfigurationProviders
+                if (!providers.contains(provider)) {
+                    provider.init(this);
+                }
+                provider.loadPackages();
+                packageProviders.add(provider);
             }
     
             rebuildRuntimeConfiguration();
         } finally {
             ActionContext.setContext(null);
         }
+        return packageProviders;
     }
     
     protected ActionContext setContext(Container cont) {

src/java/com/opensymphony/xwork2/config/impl/MockConfiguration.java

 import com.opensymphony.xwork2.config.Configuration;
 import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.config.ContainerProvider;
+import com.opensymphony.xwork2.config.PackageProvider;
 import com.opensymphony.xwork2.config.RuntimeConfiguration;
 import com.opensymphony.xwork2.config.entities.PackageConfig;
 import com.opensymphony.xwork2.config.providers.XWorkConfigurationProvider;
     public Set<String> getLoadedFileNames() {
         return loadedFiles;
     }
+
+    public List<PackageProvider> reloadContainer(
+            List<ContainerProvider> containerProviders)
+            throws ConfigurationException {
+        throw new UnsupportedOperationException();
+    }
 }

src/test/com/opensymphony/xwork2/config/ConfigurationTest.java

  */
 package com.opensymphony.xwork2.config;
 
+import com.mockobjects.dynamic.C;
+import com.mockobjects.dynamic.Mock;
 import com.opensymphony.xwork2.*;
 import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.config.entities.InterceptorMapping;
 import com.opensymphony.xwork2.config.providers.MockConfigurationProvider;
 import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.inject.Context;
+import com.opensymphony.xwork2.inject.Factory;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.inject.Scope;
 import com.opensymphony.xwork2.mock.MockInterceptor;
+import com.opensymphony.xwork2.test.StubConfigurationProvider;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
 
 import java.util.HashMap;
 import java.util.Map;
         // check that it has configuration from MockConfigurationProvider
         assertNotNull(configuration.getActionConfig("", MockConfigurationProvider.FOO_ACTION_NAME));
     }
+    
+    public void testMultipleContainerProviders() {
+        Mock mockContainerProvider = new Mock(ContainerProvider.class);
+        mockContainerProvider.expect("init", C.ANY_ARGS);
+        mockContainerProvider.expect("register", C.ANY_ARGS);
+        mockContainerProvider.matchAndReturn("equals", C.ANY_ARGS, false);
+        mockContainerProvider.matchAndReturn("toString", "foo");
+        mockContainerProvider.matchAndReturn("destroy", null);
+        mockContainerProvider.expectAndReturn("needsReload", true);
+        configurationManager.addContainerProvider((ContainerProvider) mockContainerProvider.proxy());
+
+        Configuration config = null;
+        try {
+            config = configurationManager.getConfiguration();
+        } catch (ConfigurationException e) {
+            e.printStackTrace();
+            fail();
+        }
+        
+
+        RuntimeConfiguration configuration = config.getRuntimeConfiguration();
+
+        // check that it has configuration from xml
+        assertNotNull(configuration.getActionConfig("/foo/bar", "Bar"));
+
+        mockContainerProvider.verify();
+    }
+    
+    public void testInitForPackageProviders() {
+        
+        loadConfigurationProviders(new StubConfigurationProvider() {
+            public void register(ContainerBuilder builder,
+                    LocatableProperties props) throws ConfigurationException {
+                builder.factory(PackageProvider.class, "foo", MyPackageProvider.class);
+            }
+        });
+        
+        assertEquals(configuration, MyPackageProvider.getConfiguration());
+    }
+    
+    public void testInitOnceForConfigurationProviders() {
+        
+        loadConfigurationProviders(new StubConfigurationProvider() {
+            boolean called = false;
+            public void init(Configuration config) {
+                if (called) {
+                    fail("Called twice");
+                }
+                called = true;
+            }
+            
+            public void loadPackages() {
+                if (!called) {
+                    fail("Never called");
+                }
+            }
+        });
+    }
 
     public void testMultipleInheritance() {
         try {
         // ensure we're using the default configuration, not simple config
         loadConfigurationProviders(new XmlConfigurationProvider("xwork-sample.xml"));
     }
+    
+    public static class MyPackageProvider implements PackageProvider {
+        static Configuration config;
+        public void loadPackages() throws ConfigurationException {}
+        public boolean needsReload() { return config != null; }
+        
+        public static Configuration getConfiguration() {
+            return config;
+        }
+        public void init(Configuration configuration)
+                throws ConfigurationException {
+            config = configuration;
+        }
+        
+    }
 }