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 2bd362eb8e3..38008bb76f2 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, Configurable, BeanNameAware { @@ -81,7 +80,7 @@ public class ExtensionRegistry implements Registry, 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, 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, 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) { 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 bd7a0334a6f..4975c5a4d70 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 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 beans = new HashSet(); Class typeClass; ApplicationContext applicationContext; + Set 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(); + 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; 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 00000000000..8d7e9b68fb6 --- /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(); + } + +} 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 00000000000..74fc068fa74 --- /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[] resources; + + @Override + public Resource[] getObject() throws Exception { + List existing = new ArrayList(); + + 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; + } + +} 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 15df839cf1a..3106ee5234a 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 modules; Map contexts = new HashMap(); ApplicationContext rootContext = null; + Set excludes = new HashSet(); + Properties configProperties = null; public DefaultModuleDefinitionSet(Map 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; } 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 b19833a735b..8c133ec5a94 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"> + + + + + + classpath:modules.properties + + + 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 2947615d597..3cb00bcc714 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 @@ -66,6 +66,16 @@ public class ModuleBasedContextFactoryTest { assertEquals("a string", set.getApplicationContext("child1").getBean("override", String.class)); } + @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(); 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 5114187b9a5..989aa9fbcb9 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 modules = factory.locateModules("testhierarchy"); - assertEquals(5, modules.size()); + assertEquals(8, modules.size()); } } 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 00000000000..abd83684710 --- /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 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 00000000000..57e68ddd595 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + 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 00000000000..2d06f6918f1 --- /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 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 00000000000..55fd9b4810d --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + 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 00000000000..b1f6ab97976 --- /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 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 00000000000..7684deb6c26 --- /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 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 00000000000..6495e1b48b8 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + +