You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2013/11/05 19:08:20 UTC

git commit: updated refs/heads/master to 8081337

Updated Branches:
  refs/heads/master 0ef613519 -> 80813375a


Allow disabling modules and extensions from module.properties

Discovery of modules is based on classpath scanning.  In some situations it
may not be possible or desirable to change the classpath.  To force a
module to not load you can create a file called modules.properties on the
classpath that can exclude specific modules from loading.  Additionally
this same file can be used to exclude a specific extension.  Extension
loading is typically done through global configuration.  If you want to set
up an environment and you don't even want the extension/module loaded on
the first start, then using the config file is appropriate.

Example: modules.properties

modules.exclude=storage-image-s3,storage-volume-solidfire
extensions.exclude=ClusterScopeStoragePoolAllocator,ZoneWideStoragePoolAllocator

Typically you would want to place this file in /etc/cloudstack/management


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/80813375
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/80813375
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/80813375

Branch: refs/heads/master
Commit: 80813375ae9eab84118a4905946ab6073d176d71
Parents: 0ef6135
Author: Darren Shepherd <da...@gmail.com>
Authored: Tue Nov 5 11:02:41 2013 -0700
Committer: Darren Shepherd <da...@gmail.com>
Committed: Tue Nov 5 11:03:06 2013 -0700

----------------------------------------------------------------------
 .../lifecycle/registry/ExtensionRegistry.java   | 15 +----
 .../lifecycle/registry/RegistryLifecycle.java   | 46 ++++++++++++++-
 .../lifecycle/registry/RegistryUtils.java       | 35 +++++++++++
 .../module/factory/QuietLoaderFactory.java      | 62 ++++++++++++++++++++
 .../model/impl/DefaultModuleDefinitionSet.java  | 59 ++++++++++++++++++-
 .../module/model/impl/defaults-context.xml      |  9 +++
 .../factory/ModuleBasedContextFactoryTest.java  | 10 ++++
 ...ClasspathModuleDefinitionSetLocatorTest.java |  2 +-
 .../testhierarchy/excluded/module.properties    | 19 ++++++
 .../testhierarchy/excluded/test-context.xml     | 33 +++++++++++
 .../testhierarchy/excluded2/module.properties   | 19 ++++++
 .../testhierarchy/excluded2/test-context.xml    | 33 +++++++++++
 .../orphan-of-excluded/defaults.properties      | 20 +++++++
 .../orphan-of-excluded/module.properties        | 19 ++++++
 .../orphan-of-excluded/test-context.xml         | 33 +++++++++++
 15 files changed, 396 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java
index 2bd362e..38008bb 100644
--- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java
@@ -34,7 +34,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.BeanNameAware;
 
-import com.cloud.utils.component.Named;
 import com.cloud.utils.component.Registry;
 
 public class ExtensionRegistry implements Registry<Object>, Configurable, BeanNameAware {
@@ -81,7 +80,7 @@ public class ExtensionRegistry implements Registry<Object>, Configurable, BeanNa
             }
         }
         
-        String name = getName(item);
+        String name = RegistryUtils.getName(item);
         
         if ( name != null && exclude.size() > 0 && exclude.contains(name) ) {
             return false;
@@ -103,7 +102,7 @@ public class ExtensionRegistry implements Registry<Object>, Configurable, BeanNa
                 break;
             }
             
-            if ( getName(registered.get(i)).equals(orderTest) ) {
+            if ( RegistryUtils.getName(registered.get(i)).equals(orderTest) ) {
                 i++;
             }
         }
@@ -116,16 +115,6 @@ public class ExtensionRegistry implements Registry<Object>, Configurable, BeanNa
         
         return true;
     }
-    
-    protected String getName(Object object) {
-        if ( object instanceof Named ) {
-            String name = ((Named)object).getName();
-            if ( name != null )
-                return name;
-        }
-        
-        return object == null ? null : object.getClass().getSimpleName();
-    }
 
     @Override
     public void unregister(Object type) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java
index bd7a033..4975c5a 100644
--- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java
@@ -20,6 +20,7 @@ package org.apache.cloudstack.spring.lifecycle.registry;
 
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Properties;
 import java.util.Set;
 
 import org.slf4j.Logger;
@@ -29,6 +30,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.SmartLifecycle;
+import org.springframework.util.StringUtils;
 
 import com.cloud.utils.component.Registry;
 
@@ -36,6 +38,9 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App
 
     private static final Logger log = LoggerFactory.getLogger(RegistryLifecycle.class);
     
+    public static final String EXTENSION_EXCLUDE = "extensions.exclude";
+    public static final String EXTENSION_INCLUDE_PREFIX = "extensions.include.";
+    
     Registry<Object> registry;
     
     /* The bean name works around circular dependency issues in Spring.  This shouldn't be
@@ -46,15 +51,52 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App
     Set<Object> beans = new HashSet<Object>();
     Class<?> typeClass;
     ApplicationContext applicationContext;
+    Set<String> excludes = null;
 
     @Override
     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
-        if ( typeClass.isAssignableFrom(bean.getClass()) )
+        if ( typeClass.isAssignableFrom(bean.getClass())  && ! isExcluded(bean) ) {
             beans.add(bean);
-        
+        }
+
         return bean;
     }
 
+    protected synchronized boolean isExcluded(Object bean) {
+        String name = RegistryUtils.getName(bean);
+
+        if ( excludes == null ) {
+            loadExcluded();
+        }
+
+        boolean result = excludes.contains(name);
+        if ( result ) {
+            log.info("Excluding extension [{}] based on configuration", name); 
+        }
+
+        return result;
+    }
+    
+    protected synchronized void loadExcluded() {
+        Properties props = applicationContext.getBean("DefaultConfigProperties", Properties.class);
+        excludes = new HashSet<String>();
+        for ( String exclude : props.getProperty(EXTENSION_EXCLUDE, "").trim().split("\\s*,\\s*") ) {
+            if ( StringUtils.hasText(exclude) ) {
+                excludes.add(exclude);
+            }
+        }
+
+        for ( String key : props.stringPropertyNames() ) {
+            if ( key.startsWith(EXTENSION_INCLUDE_PREFIX) ) {
+                String module = key.substring(EXTENSION_INCLUDE_PREFIX.length());
+                boolean include = props.getProperty(key).equalsIgnoreCase("true");
+                if ( ! include ) {
+                    excludes.add(module);
+                }
+            }
+        }
+    }
+    
     @Override
     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
         return bean;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.java
new file mode 100644
index 0000000..8d7e9b6
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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.cloudstack.spring.lifecycle.registry;
+
+import com.cloud.utils.component.Named;
+
+public class RegistryUtils {
+
+    public static String getName(Object object) {
+        if ( object instanceof Named ) {
+            String name = ((Named)object).getName();
+            if ( name != null )
+                return name;
+        }
+
+        return object == null ? null : object.getClass().getSimpleName();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.java
new file mode 100644
index 0000000..74fc068
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.java
@@ -0,0 +1,62 @@
+/*
+ * 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.cloudstack.spring.module.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.core.io.Resource;
+
+public class QuietLoaderFactory implements FactoryBean<Resource[]> {
+
+    Resource[] resources;
+
+    @Override
+    public Resource[] getObject() throws Exception {
+        List<Resource> existing = new ArrayList<Resource>();
+
+        for ( Resource resource : resources ) {
+            if ( resource.exists() ) {
+                existing.add(resource);
+            }
+        }
+
+        return existing.toArray(new Resource[existing.size()]);
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return Resource[].class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return false;
+    }
+
+    public Resource[] getResources() {
+        return resources;
+    }
+
+    public void setResources(Resource[] resources) {
+        this.resources = resources;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java
index 15df839..3106ee5 100644
--- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java
@@ -19,19 +19,23 @@
 package org.apache.cloudstack.spring.module.model.impl;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.EmptyStackException;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 import java.util.Stack;
 
 import org.apache.cloudstack.spring.module.context.ResourceApplicationContext;
 import org.apache.cloudstack.spring.module.model.ModuleDefinition;
 import org.apache.cloudstack.spring.module.model.ModuleDefinitionSet;
+import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeansException;
@@ -40,18 +44,25 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.UrlResource;
+import org.springframework.util.StringUtils;
 
 public class DefaultModuleDefinitionSet implements ModuleDefinitionSet {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultModuleDefinitionSet.class);
     
     public static final String DEFAULT_CONFIG_RESOURCES = "DefaultConfigResources";
+    public static final String DEFAULT_CONFIG_PROPERTIES = "DefaultConfigProperties";
+    public static final String MODULES_EXCLUDE = "modules.exclude";
+    public static final String MODULES_INCLUDE_PREFIX = "modules.include.";
+    public static final String MODULE_PROPERITES = "ModuleProperties";
     public static final String DEFAULT_CONFIG_XML = "defaults-context.xml";
     
     String root;
     Map<String, ModuleDefinition> modules;
     Map<String, ApplicationContext> contexts = new HashMap<String, ApplicationContext>();
     ApplicationContext rootContext = null;
+    Set<String> excludes = new HashSet<String>();
+    Properties configProperties = null;
 
     public DefaultModuleDefinitionSet(Map<String, ModuleDefinition> modules, String root) {
         super();
@@ -136,7 +147,7 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet {
     }
     
     protected boolean shouldLoad(ModuleDefinition def) {
-        return true;
+        return ! excludes.contains(def.getName());
     }
     
     protected ApplicationContext getDefaultsContext() {
@@ -156,9 +167,52 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet {
                 }
             }
         });
-        
+
+        configProperties = (Properties) context.getBean(DEFAULT_CONFIG_PROPERTIES);
+        for ( Resource resource : resources ) {
+            load(resource, configProperties);
+        }
+
+        for ( Resource resource : (Resource[])context.getBean(MODULE_PROPERITES) ) {
+            load(resource, configProperties);
+        }
+
+        parseExcludes();
+
         return context;
     }
+
+    protected void parseExcludes() {
+        for ( String exclude : configProperties.getProperty(MODULES_EXCLUDE, "").trim().split("\\s*,\\s*") ) {
+            if ( StringUtils.hasText(exclude) ) {
+                excludes.add(exclude);
+            }
+        }
+
+        for ( String key : configProperties.stringPropertyNames() ) {
+            if ( key.startsWith(MODULES_INCLUDE_PREFIX) ) {
+                String module = key.substring(MODULES_INCLUDE_PREFIX.length());
+                boolean include = configProperties.getProperty(key).equalsIgnoreCase("true");
+                if ( ! include ) {
+                    excludes.add(module);
+                }
+            }
+        }
+    }
+
+    protected void load(Resource resource, Properties props) {
+        InputStream is = null;
+        try {
+            if ( resource.exists() ) {
+                is = resource.getInputStream();
+                props.load(is);
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Failed to load resource [" + resource + "]", e);
+        } finally {
+            IOUtils.closeQuietly(is);
+        }
+    }
     
     protected void printHierarchy() {
         withModule(new WithModule() {
@@ -178,6 +232,7 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet {
             return;
         
         if ( ! shouldLoad(def) ) {
+            log.info("Excluding context [{}] based on configuration", def.getName());
             return;
         }
         

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml b/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml
index b19833a..8c133ec 100644
--- a/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml
+++ b/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml
@@ -24,5 +24,14 @@
                       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
     <bean id="DefaultConfigResources" class="java.util.ArrayList" />
+    <bean id="DefaultConfigProperties" class="java.util.Properties" />
+
+    <bean id="ModuleProperties" class="org.apache.cloudstack.spring.module.factory.QuietLoaderFactory" >
+        <property name="resources">
+            <list>
+                <value>classpath:modules.properties</value>
+            </list>
+        </property>
+    </bean>
 
 </beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java
index 2947615..3cb00bc 100644
--- a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java
+++ b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java
@@ -67,6 +67,16 @@ public class ModuleBasedContextFactoryTest {
     }
     
     @Test
+    public void testExcluded() throws IOException {
+        ModuleBasedContextFactory factory = new ModuleBasedContextFactory();
+        ModuleDefinitionSet set = factory.loadModules(defs, "base");
+
+        assertNull(set.getApplicationContext("excluded"));
+        assertNull(set.getApplicationContext("excluded2"));
+        assertNull(set.getApplicationContext("orphan-of-excluded"));
+    }
+    
+    @Test
     public void testBeans() throws IOException {
         ModuleBasedContextFactory factory = new ModuleBasedContextFactory();
         ModuleDefinitionSet set = factory.loadModules(defs, "base");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java
index 5114187..989aa9f 100644
--- a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java
+++ b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java
@@ -34,7 +34,7 @@ public class ClasspathModuleDefinitionSetLocatorTest {
         
         Collection<ModuleDefinition> modules = factory.locateModules("testhierarchy");
         
-        assertEquals(5, modules.size());
+        assertEquals(8, modules.size());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties b/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties
new file mode 100644
index 0000000..abd8368
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties
@@ -0,0 +1,19 @@
+# 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.
+
+name=excluded
+parent=base
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml b/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml
new file mode 100644
index 0000000..57e68dd
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml
@@ -0,0 +1,33 @@
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+  xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+    <bean id="name" class="java.lang.String" >
+        <constructor-arg value="excluded" />
+    </bean>
+    
+    <bean id="excluded" class="java.lang.String" >
+        <constructor-arg value="excluded" />
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties b/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties
new file mode 100644
index 0000000..2d06f69
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties
@@ -0,0 +1,19 @@
+# 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.
+
+name=excluded2
+parent=base
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml b/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml
new file mode 100644
index 0000000..55fd9b4
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml
@@ -0,0 +1,33 @@
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+  xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+    <bean id="name" class="java.lang.String" >
+        <constructor-arg value="excluded2" />
+    </bean>
+    
+    <bean id="excluded" class="java.lang.String" >
+        <constructor-arg value="excluded2" />
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties
new file mode 100644
index 0000000..b1f6ab9
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties
@@ -0,0 +1,20 @@
+# 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.
+
+modules.include.excluded=false
+modules.include.base=True
+modules.exclude=excluded2,excluded2
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties
new file mode 100644
index 0000000..7684deb
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties
@@ -0,0 +1,19 @@
+# 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.
+
+name=orphan-of-excluded
+parent=excluded
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/80813375/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml
new file mode 100644
index 0000000..6495e1b
--- /dev/null
+++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml
@@ -0,0 +1,33 @@
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+  xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+    <bean id="name" class="java.lang.String" >
+        <constructor-arg value="orphan-of-excluded" />
+    </bean>
+    
+    <bean id="excluded" class="java.lang.String" >
+        <constructor-arg value="orphan-of-excluded" />
+    </bean>
+</beans>