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/10/03 00:45:49 UTC
[5/9] Spring Modularization
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/pom.xml
----------------------------------------------------------------------
diff --git a/framework/pom.xml b/framework/pom.xml
index 4ea2df1..14e3368 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -38,5 +38,7 @@
<module>db</module>
<module>config</module>
<module>managed-context</module>
+ <module>spring/lifecycle</module>
+ <module>spring/module</module>
</modules>
</project>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/pom.xml
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/pom.xml b/framework/spring/lifecycle/pom.xml
new file mode 100644
index 0000000..647101c
--- /dev/null
+++ b/framework/spring/lifecycle/pom.xml
@@ -0,0 +1,34 @@
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>cloud-framework-spring-lifecycle</artifactId>
+ <name>Apache CloudStack Framework - Spring Life Cycle</name>
+ <parent>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-maven-standard</artifactId>
+ <version>4.3.0-SNAPSHOT</version>
+ <relativePath>../../../maven-standard/pom.xml</relativePath>
+ </parent>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-framework-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractBeanCollector.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractBeanCollector.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractBeanCollector.java
new file mode 100644
index 0000000..a3c0d60
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractBeanCollector.java
@@ -0,0 +1,113 @@
+/*
+ * 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;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+/**
+ * This class provides a method to do basically the same as @Inject of a type, but
+ * it will only find the types in the current context and not the parent. This class
+ * should only be used for very specific Spring bootstrap logic. In general @Inject
+ * is infinitely better. Basically you need a very good reason to use this.
+ *
+ */
+public abstract class AbstractBeanCollector extends AbstractSmartLifeCycle implements BeanPostProcessor {
+
+ Class<?>[] typeClasses = new Class<?>[] {};
+ Map<Class<?>, Set<Object>> beans = new HashMap<Class<?>, Set<Object>>();
+
+ @Override
+ public int getPhase() {
+ return 2000;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ for ( Class<?> typeClass : typeClasses ) {
+ if ( typeClass.isAssignableFrom(bean.getClass()) ) {
+ doPostProcessBeforeInitialization(bean, beanName);
+ break;
+ }
+ }
+
+ return bean;
+ }
+
+ protected void doPostProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ }
+
+ protected void doPostProcessAfterInitialization(Object bean, Class<?> typeClass, String beanName) throws BeansException {
+ Set<Object> beansOfType = beans.get(typeClass);
+
+ if ( beansOfType == null ) {
+ beansOfType = new HashSet<Object>();
+ beans.put(typeClass, beansOfType);
+ }
+
+ beansOfType.add(bean);
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ for ( Class<?> typeClass : typeClasses ) {
+ if ( typeClass.isAssignableFrom(bean.getClass()) ) {
+ doPostProcessAfterInitialization(bean, typeClass, beanName);
+ }
+ }
+
+ return bean;
+ }
+
+ protected <T> Set<T> getBeans(Class<T> typeClass) {
+ @SuppressWarnings("unchecked")
+ Set<T> result = (Set<T>) beans.get(typeClass);
+
+ if ( result == null )
+ return Collections.emptySet();
+
+ return result;
+ }
+
+ public Class<?> getTypeClass() {
+ if ( typeClasses == null || typeClasses.length == 0 )
+ return null;
+
+ return typeClasses[0];
+ }
+
+ public void setTypeClass(Class<?> typeClass) {
+ this.typeClasses = new Class<?>[] { typeClass };
+ }
+
+ public Class<?>[] getTypeClasses() {
+ return typeClasses;
+ }
+
+ public void setTypeClasses(Class<?>[] typeClasses) {
+ this.typeClasses = typeClasses;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractSmartLifeCycle.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractSmartLifeCycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractSmartLifeCycle.java
new file mode 100644
index 0000000..071817b
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/AbstractSmartLifeCycle.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import org.springframework.context.SmartLifecycle;
+
+public abstract class AbstractSmartLifeCycle implements SmartLifecycle {
+
+ boolean running = false;
+
+ @Override
+ public void start() {
+ running = true;
+ }
+
+ @Override
+ public void stop() {
+ running = false;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return running;
+ }
+
+ @Override
+ public boolean isAutoStartup() {
+ return true;
+ }
+
+ @Override
+ public void stop(Runnable callback) {
+ stop();
+ callback.run();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java
new file mode 100644
index 0000000..1b7ea51
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java
@@ -0,0 +1,169 @@
+/*
+ * 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;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.naming.ConfigurationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.cloud.utils.component.ComponentLifecycle;
+import com.cloud.utils.component.SystemIntegrityChecker;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.mgmt.JmxUtil;
+import com.cloud.utils.mgmt.ManagementBean;
+
+public class CloudStackExtendedLifeCycle extends AbstractBeanCollector {
+
+ private static final Logger log = LoggerFactory.getLogger(CloudStackExtendedLifeCycle.class);
+
+ Map<Integer, Set<ComponentLifecycle>> sorted = new TreeMap<Integer, Set<ComponentLifecycle>>();
+
+ public CloudStackExtendedLifeCycle() {
+ super();
+ setTypeClasses(new Class<?>[] {
+ ComponentLifecycle.class,
+ SystemIntegrityChecker.class
+ });
+ }
+
+ @Override
+ public void start() {
+ sortBeans();
+ checkIntegrity();
+ configure();
+
+ super.start();
+ }
+
+ protected void checkIntegrity() {
+ for ( SystemIntegrityChecker checker : getBeans(SystemIntegrityChecker.class) ) {
+ log.info("Running system integrity checker {}", checker);
+
+ checker.check();
+ }
+ }
+
+ public void startBeans() {
+ log.info("Starting CloudStack Components");
+
+ with(new WithComponentLifeCycle() {
+ @Override
+ public void with(ComponentLifecycle lifecycle) {
+ lifecycle.start();
+
+ if ( lifecycle instanceof ManagementBean ) {
+ ManagementBean mbean = (ManagementBean)lifecycle;
+ try {
+ JmxUtil.registerMBean(mbean);
+ } catch (MalformedObjectNameException e) {
+ log.warn("Unable to register MBean: " + mbean.getName(), e);
+ } catch (InstanceAlreadyExistsException e) {
+ log.warn("Unable to register MBean: " + mbean.getName(), e);
+ } catch (MBeanRegistrationException e) {
+ log.warn("Unable to register MBean: " + mbean.getName(), e);
+ } catch (NotCompliantMBeanException e) {
+ log.warn("Unable to register MBean: " + mbean.getName(), e);
+ }
+ log.info("Registered MBean: " + mbean.getName());
+ }
+ }
+ });
+
+ log.info("Done Starting CloudStack Components");
+ }
+
+ public void stopBeans() {
+ with(new WithComponentLifeCycle() {
+ @Override
+ public void with(ComponentLifecycle lifecycle) {
+ lifecycle.stop();
+ }
+ });
+ }
+
+ private void configure() {
+ log.info("Configuring CloudStack Components");
+
+ with(new WithComponentLifeCycle() {
+ @Override
+ public void with(ComponentLifecycle lifecycle) {
+ try {
+ lifecycle.configure(lifecycle.getName(), lifecycle.getConfigParams());
+ } catch (ConfigurationException e) {
+ log.error("Failed to configure {}", lifecycle.getName(), e);
+ throw new CloudRuntimeException(e);
+ }
+ }
+ });
+
+ log.info("Done Configuring CloudStack Components");
+ }
+
+ private void sortBeans() {
+ for ( ComponentLifecycle lifecycle : getBeans(ComponentLifecycle.class) ) {
+ Set<ComponentLifecycle> set = sorted.get(lifecycle.getRunLevel());
+
+ if ( set == null ) {
+ set = new HashSet<ComponentLifecycle>();
+ sorted.put(lifecycle.getRunLevel(), set);
+ }
+
+ set.add(lifecycle);
+ }
+ }
+
+ @Override
+ public void stop() {
+ with(new WithComponentLifeCycle() {
+ @Override
+ public void with(ComponentLifecycle lifecycle) {
+ lifecycle.stop();
+ }
+ });
+
+ super.stop();
+ }
+
+ protected void with(WithComponentLifeCycle with) {
+ for ( Set<ComponentLifecycle> lifecycles : sorted.values() ) {
+ for ( ComponentLifecycle lifecycle : lifecycles ) {
+ with.with(lifecycle);
+ }
+ }
+ }
+
+ @Override
+ public int getPhase() {
+ return 2000;
+ }
+
+ private static interface WithComponentLifeCycle {
+ public void with(ComponentLifecycle lifecycle);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycleStart.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycleStart.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycleStart.java
new file mode 100644
index 0000000..33d4aea
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycleStart.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+public class CloudStackExtendedLifeCycleStart extends AbstractSmartLifeCycle implements Runnable {
+
+ CloudStackExtendedLifeCycle lifeCycle;
+
+ @Override
+ public void stop() {
+ lifeCycle.stopBeans();
+ super.stop();
+ }
+
+ @Override
+ public int getPhase() {
+ return 3000;
+ }
+
+ public CloudStackExtendedLifeCycle getLifeCycle() {
+ return lifeCycle;
+ }
+
+ public void setLifeCycle(CloudStackExtendedLifeCycle lifeCycle) {
+ this.lifeCycle = lifeCycle;
+ }
+
+ @Override
+ public void run() {
+ lifeCycle.startBeans();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackLog4jSetup.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackLog4jSetup.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackLog4jSetup.java
new file mode 100644
index 0000000..163703d
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackLog4jSetup.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+import org.springframework.context.SmartLifecycle;
+
+import com.cloud.utils.LogUtils;
+
+public class CloudStackLog4jSetup implements SmartLifecycle {
+
+ @Override
+ public void start() {
+ LogUtils.initLog4j("log4j-cloud.xml");
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public int getPhase() {
+ return 0;
+ }
+
+ @Override
+ public boolean isAutoStartup() {
+ return true;
+ }
+
+ @Override
+ public void stop(Runnable callback) {
+ callback.run();
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/ConfigDepotLifeCycle.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/ConfigDepotLifeCycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/ConfigDepotLifeCycle.java
new file mode 100644
index 0000000..b380028
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/ConfigDepotLifeCycle.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.framework.config.ConfigDepotAdmin;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+public class ConfigDepotLifeCycle implements BeanPostProcessor {
+
+ @Inject
+ ConfigDepotAdmin configDepotAdmin;
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if ( bean instanceof Configurable ) {
+ configDepotAdmin.populateConfiguration((Configurable)bean);
+ }
+
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/DumpRegistry.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/DumpRegistry.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/DumpRegistry.java
new file mode 100644
index 0000000..5614a32
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/DumpRegistry.java
@@ -0,0 +1,77 @@
+/*
+ * 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 java.util.List;
+
+import com.cloud.utils.component.ComponentLifecycleBase;
+import com.cloud.utils.component.Named;
+import com.cloud.utils.component.Registry;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DumpRegistry extends ComponentLifecycleBase {
+
+ private static final Logger log = LoggerFactory.getLogger(DumpRegistry.class);
+
+ List<Registry<?>> registries;
+
+ public List<Registry<?>> getRegistries() {
+ return registries;
+ }
+
+ @Inject
+ public void setRegistries(List<Registry<?>> registries) {
+ this.registries = registries;
+ }
+
+ @Override
+ public boolean start() {
+ for ( Registry<?> registry : registries ) {
+ StringBuilder buffer = new StringBuilder();
+
+ for ( Object o : registry.getRegistered() ) {
+ if ( buffer.length() > 0 )
+ buffer.append(", ");
+
+ buffer.append(getName(o));
+ }
+
+ log.info("Registry [{}] contains [{}]", registry.getName(), buffer);
+ }
+
+ return super.start();
+ }
+
+ protected String getName(Object o) {
+ String name = null;
+ if (o instanceof Named) {
+ name = ((Named) o).getName();
+ }
+
+ if (name == null) {
+ name = o.getClass().getSimpleName();
+ }
+
+ return name;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/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
new file mode 100644
index 0000000..2bd362e
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java
@@ -0,0 +1,245 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.commons.lang.StringUtils;
+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 {
+
+ private static final Logger log = LoggerFactory.getLogger(ExtensionRegistry.class);
+
+ String name;
+ String beanName;
+
+ String orderConfigKey;
+ String orderConfigDefault;
+ ConfigKey<String> orderConfigKeyObj;
+
+ String excludeKey;
+ String excludeDefault;
+ ConfigKey<String> excludeKeyObj;
+
+ String configComponentName;
+ List<Object> preRegistered;
+ List<Object> registered = new CopyOnWriteArrayList<Object>();
+ List<Object> readOnly = Collections.unmodifiableList(registered);
+
+ @Override
+ public boolean register(Object item) {
+ if ( registered.contains(item) )
+ return false;
+
+ String[] order = new String[] {};
+ Set<String> exclude = new HashSet<String>();
+
+ if ( orderConfigKeyObj != null ) {
+ Object value = orderConfigKeyObj.value();
+ if ( value != null && value.toString().trim().length() > 0 ) {
+ order = value.toString().trim().split("\\s*,\\s*");
+ }
+ }
+
+ if ( excludeKeyObj != null ) {
+ Object value = excludeKeyObj.value();
+ if ( value != null && value.toString().trim().length() > 0 ) {
+ for ( String e : value.toString().trim().split("\\s*,\\s*") ) {
+ exclude.add(e);
+ }
+ }
+ }
+
+ String name = getName(item);
+
+ if ( name != null && exclude.size() > 0 && exclude.contains(name) ) {
+ return false;
+ }
+
+ if ( name == null && order.length > 0 ) {
+ throw new RuntimeException("getName() is null for [" + item + "]");
+ }
+
+ int i = 0;
+ for ( String orderTest : order ) {
+ if ( orderTest.equals(name) ) {
+ registered.add(i, item);
+ i = -1;
+ break;
+ }
+
+ if ( registered.size() <= i ) {
+ break;
+ }
+
+ if ( getName(registered.get(i)).equals(orderTest) ) {
+ i++;
+ }
+ }
+
+ if ( i != -1 ) {
+ registered.add(item);
+ }
+
+ log.debug("Registering extension [{}] in [{}]", name, this.name);
+
+ 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) {
+ registered.remove(type);
+ }
+
+ @Override
+ public List<Object> getRegistered() {
+ return readOnly;
+ }
+
+ @Override
+ public String getConfigComponentName() {
+ return configComponentName == null ? this.getClass().getSimpleName() : configComponentName;
+ }
+
+ @Override
+ public ConfigKey<?>[] getConfigKeys() {
+ List<ConfigKey<String>> result = new ArrayList<ConfigKey<String>>();
+
+ if ( orderConfigKey != null && orderConfigKeyObj == null ) {
+ orderConfigKeyObj = new ConfigKey<String>("Advanced", String.class, orderConfigKey, orderConfigDefault,
+ "The order of precedence for the extensions", false);
+ }
+
+ if ( orderConfigKeyObj != null )
+ result.add(orderConfigKeyObj);
+
+ if ( excludeKey != null && excludeKeyObj == null ) {
+ excludeKeyObj = new ConfigKey<String>("Advanced", String.class, excludeKey, excludeDefault,
+ "Extensions to exclude from being registered", false);
+ }
+
+ if ( excludeKeyObj != null ) {
+ result.add(excludeKeyObj);
+ }
+
+ return result.toArray(new ConfigKey[result.size()]);
+ }
+
+ @PostConstruct
+ public void init() {
+ if ( name == null ) {
+ for ( String part : beanName.replaceAll("([A-Z])", " $1").split("\\s+") ) {
+ part = StringUtils.capitalize(part.toLowerCase());;
+
+ name = name == null ? part : name + " " + part;
+ }
+ }
+
+ if ( preRegistered != null ) {
+ for ( Object o : preRegistered ) {
+ register(o);
+ }
+ }
+ }
+
+
+ public String getOrderConfigKey() {
+ return orderConfigKey;
+ }
+
+ public void setOrderConfigKey(String orderConfigKey) {
+ this.orderConfigKey = orderConfigKey;
+ }
+
+ public void setConfigComponentName(String configComponentName) {
+ this.configComponentName = configComponentName;
+ }
+
+ public String getOrderConfigDefault() {
+ return orderConfigDefault;
+ }
+
+ public void setOrderConfigDefault(String orderConfigDefault) {
+ this.orderConfigDefault = orderConfigDefault;
+ }
+
+ public String getExcludeKey() {
+ return excludeKey;
+ }
+
+ public void setExcludeKey(String excludeKey) {
+ this.excludeKey = excludeKey;
+ }
+
+ public String getExcludeDefault() {
+ return excludeDefault;
+ }
+
+ public void setExcludeDefault(String excludeDefault) {
+ this.excludeDefault = excludeDefault;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public void setBeanName(String name) {
+ this.beanName = name;
+ }
+
+ public List<Object> getPreRegistered() {
+ return preRegistered;
+ }
+
+ public void setPreRegistered(List<Object> preRegistered) {
+ this.preRegistered = preRegistered;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/PluggableServiceLifecycle.java
----------------------------------------------------------------------
diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/PluggableServiceLifecycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/PluggableServiceLifecycle.java
new file mode 100644
index 0000000..3eeeed8
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/PluggableServiceLifecycle.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ComponentContext;
+import com.cloud.utils.component.PluggableService;
+
+public class PluggableServiceLifecycle extends RegistryLifecycle {
+
+ @Override
+ public void start() {
+ super.start();
+
+ for (Object obj : beans) {
+ if (obj instanceof PluggableService) {
+ for (Class<?> cmd : ((PluggableService) obj).getCommands()) {
+ ComponentContext.addDelegateContext(cmd, applicationContext);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ for (Object obj : beans) {
+ if (obj instanceof PluggableService) {
+ for (Class<?> cmd : ((PluggableService) obj).getCommands()) {
+ ComponentContext.removeDelegateContext(cmd);
+ }
+ }
+ }
+
+ super.stop();
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/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
new file mode 100644
index 0000000..bd7a033
--- /dev/null
+++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java
@@ -0,0 +1,144 @@
+/*
+ * 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 java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.SmartLifecycle;
+
+import com.cloud.utils.component.Registry;
+
+public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, ApplicationContextAware {
+
+ private static final Logger log = LoggerFactory.getLogger(RegistryLifecycle.class);
+
+ Registry<Object> registry;
+
+ /* The bean name works around circular dependency issues in Spring. This shouldn't be
+ * needed if your beans are already nicely organized. If they look like spaghetti, then you
+ * can use this.
+ */
+ String registryBeanName;
+ Set<Object> beans = new HashSet<Object>();
+ Class<?> typeClass;
+ ApplicationContext applicationContext;
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if ( typeClass.isAssignableFrom(bean.getClass()) )
+ beans.add(bean);
+
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public void start() {
+ Iterator<Object> iter = beans.iterator();
+ Registry<Object> registry = lookupRegistry();
+
+ while ( iter.hasNext() ) {
+ Object next = iter.next();
+ if ( registry.register(next) ) {
+ log.debug("Registered {}", next);
+ } else {
+ iter.remove();
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ Registry<Object> registry = lookupRegistry();
+
+ for ( Object bean : beans ) {
+ registry.unregister(bean);
+ }
+
+ beans.clear();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public int getPhase() {
+ return 2000;
+ }
+
+ @Override
+ public boolean isAutoStartup() {
+ return true;
+ }
+
+ @Override
+ public void stop(Runnable callback) {
+ stop();
+ callback.run();
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Registry<Object> lookupRegistry() {
+ return registry == null ? applicationContext.getBean(registryBeanName, Registry.class) : registry;
+ }
+
+ public Registry<Object> getRegistry() {
+ return registry;
+ }
+
+ public void setRegistry(Registry<Object> registry) {
+ this.registry = registry;
+ }
+
+ public Class<?> getTypeClass() {
+ return typeClass;
+ }
+
+ public void setTypeClass(Class<?> typeClass) {
+ this.typeClass = typeClass;
+ }
+
+ public String getRegistryBeanName() {
+ return registryBeanName;
+ }
+
+ public void setRegistryBeanName(String registryBeanName) {
+ this.registryBeanName = registryBeanName;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/pom.xml
----------------------------------------------------------------------
diff --git a/framework/spring/module/pom.xml b/framework/spring/module/pom.xml
new file mode 100644
index 0000000..b9d95a8
--- /dev/null
+++ b/framework/spring/module/pom.xml
@@ -0,0 +1,50 @@
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>cloud-framework-spring-module</artifactId>
+ <parent>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-maven-standard</artifactId>
+ <version>4.3.0-SNAPSHOT</version>
+ <relativePath>../../../maven-standard/pom.xml</relativePath>
+ </parent>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+</project>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java
new file mode 100644
index 0000000..60d0262
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java
@@ -0,0 +1,55 @@
+/*
+ * 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.context;
+
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.core.io.Resource;
+
+public class ResourceApplicationContext extends AbstractXmlApplicationContext {
+
+ Resource[] configResources;
+ String applicationName = "";
+
+ public ResourceApplicationContext() {
+ }
+
+ public ResourceApplicationContext(Resource... configResources) {
+ super();
+ this.configResources = configResources;
+ }
+
+ @Override
+ protected Resource[] getConfigResources() {
+ return configResources;
+ }
+
+ public void setConfigResources(Resource[] configResources) {
+ this.configResources = configResources;
+ }
+
+ @Override
+ public String getApplicationName() {
+ return applicationName;
+ }
+
+ public void setApplicationName(String applicationName) {
+ this.applicationName = applicationName;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java
new file mode 100644
index 0000000..e624a5b
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java
@@ -0,0 +1,137 @@
+/*
+ * 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.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.cloudstack.spring.module.locator.ModuleDefinitionLocator;
+import org.apache.cloudstack.spring.module.locator.impl.ClasspathModuleDefinitionLocator;
+import org.apache.cloudstack.spring.module.model.ModuleDefinition;
+import org.apache.cloudstack.spring.module.model.ModuleDefinitionSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.io.Resource;
+
+public class CloudStackSpringContext {
+
+ private static final Logger log = LoggerFactory.getLogger(CloudStackSpringContext.class);
+
+ public static final String CLOUDSTACK_CONTEXT = "META-INF/cloudstack";
+ public static final String CLOUDSTACK_BASE = "bootstrap";
+
+ ModuleBasedContextFactory factory = new ModuleBasedContextFactory();
+ ModuleDefinitionLocator loader = new ClasspathModuleDefinitionLocator();
+ ModuleDefinitionSet moduleDefinitionSet;
+ String baseName;
+ String contextName;
+
+ public CloudStackSpringContext(String context, String base) throws IOException {
+ this.baseName = base;
+ this.contextName = context;
+
+ factory = new ModuleBasedContextFactory();
+ loader = new ClasspathModuleDefinitionLocator();
+ init();
+ }
+
+ public CloudStackSpringContext() throws IOException {
+ this(CLOUDSTACK_CONTEXT, CLOUDSTACK_BASE);
+ }
+
+ public void init() throws IOException {
+ Collection<ModuleDefinition> defs = loader.locateModules(contextName);
+
+ if ( defs.size() == 0 )
+ throw new RuntimeException("No modules found to load for Spring");
+
+ moduleDefinitionSet = factory.loadModules(defs, baseName);
+ }
+
+ public void registerShutdownHook() {
+ ApplicationContext base = moduleDefinitionSet.getApplicationContext(baseName);
+
+ if ( base instanceof ConfigurableApplicationContext ) {
+ ((ConfigurableApplicationContext)base).registerShutdownHook();
+ }
+ }
+
+ public ModuleDefinition getModuleDefinitionForWeb(String name) {
+ ModuleDefinition def = moduleDefinitionSet.getModuleDefinition(name);
+
+ if ( def != null ) {
+ return def;
+ }
+
+ /* Grab farthest descendant that is deterministic */
+ def = moduleDefinitionSet.getModuleDefinition(baseName);
+
+ if ( def == null ) {
+ throw new RuntimeException("Failed to find base spring module to extend for web");
+ }
+
+ while ( def.getChildren().size() == 1 ) {
+ def = def.getChildren().iterator().next();
+ }
+
+ return def;
+ }
+
+ public ApplicationContext getApplicationContextForWeb(String name) {
+ ModuleDefinition def = getModuleDefinitionForWeb(name);
+
+ return moduleDefinitionSet.getApplicationContext(def.getName());
+ }
+
+ public String[] getConfigLocationsForWeb(String name, String[] configured) {
+ if ( configured == null )
+ configured = new String[] {};
+
+ ModuleDefinition def = getModuleDefinitionForWeb(name);
+
+ List<Resource> inherited = new ArrayList<Resource>();
+
+ while ( def != null ) {
+ inherited.addAll(def.getInheritableContextLocations());
+ def = moduleDefinitionSet.getModuleDefinition(def.getParentName());
+ }
+
+ List<String> urlList = new ArrayList<String>();
+
+ for ( Resource r : inherited ) {
+ try {
+ String urlString = r.getURL().toExternalForm();
+ urlList.add(urlString);
+ } catch (IOException e) {
+ log.error("Failed to create URL for {}", r.getDescription(), e);
+ }
+ }
+
+ String[] result = new String[urlList.size() + configured.length];
+ result = urlList.toArray(result);
+
+ System.arraycopy(configured, 0, result, urlList.size(), configured.length);
+
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactory.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactory.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactory.java
new file mode 100644
index 0000000..3f89d3a
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactory.java
@@ -0,0 +1,84 @@
+/*
+ * 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.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cloudstack.spring.module.model.ModuleDefinition;
+import org.apache.cloudstack.spring.module.model.ModuleDefinitionSet;
+import org.apache.cloudstack.spring.module.model.impl.DefaultModuleDefinitionSet;
+
+public class ModuleBasedContextFactory {
+
+ public ModuleDefinitionSet loadModules(Collection<ModuleDefinition> defs, String root) throws IOException {
+
+ Map<String, ModuleDefinition> modules = wireUpModules(root, defs);
+
+ DefaultModuleDefinitionSet moduleSet = new DefaultModuleDefinitionSet(modules, root);
+ moduleSet.load();
+
+ return moduleSet;
+ }
+
+ protected Map<String, ModuleDefinition> wireUpModules(String root, Collection<ModuleDefinition> defs) throws IOException {
+ Map<String, ModuleDefinition> modules = new HashMap<String, ModuleDefinition>();
+
+ for ( ModuleDefinition def : defs ) {
+ modules.put(def.getName(), def);
+ }
+
+ ModuleDefinition rootDef = null;
+ Map<String, ModuleDefinition> result = new HashMap<String, ModuleDefinition>();
+
+ for ( ModuleDefinition def : modules.values() ) {
+ if ( def.getName().equals(root) ) {
+ rootDef = def;
+ }
+
+ if ( def.getParentName() != null ) {
+ ModuleDefinition parentDef = modules.get(def.getParentName());
+
+ if ( parentDef != null )
+ parentDef.addChild(def);
+ }
+ }
+
+ return traverse(rootDef, result);
+ }
+
+ protected Map<String, ModuleDefinition> traverse(ModuleDefinition base, Map<String, ModuleDefinition> result) {
+ if ( base == null )
+ return result;
+
+ if ( result.containsKey(base.getName()) ) {
+ throw new RuntimeException("Circular dependency to [" + base.getName() + "] from current set " +
+ result.keySet());
+ }
+
+ result.put(base.getName(), base);
+
+ for ( ModuleDefinition childDef : base.getChildren() )
+ traverse(childDef, result);
+
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/ModuleDefinitionLocator.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/ModuleDefinitionLocator.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/ModuleDefinitionLocator.java
new file mode 100644
index 0000000..6b14e0a
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/ModuleDefinitionLocator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.locator;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.apache.cloudstack.spring.module.model.ModuleDefinition;
+
+/**
+ * Responsible for locating the ModuleDefinition for a given context. The implementation
+ * of this class should take extra care to set the ClassLoader of the ModuleDefinition
+ * properly.
+ *
+ */
+public interface ModuleDefinitionLocator {
+
+ Collection<ModuleDefinition> locateModules(String context) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionLocator.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionLocator.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionLocator.java
new file mode 100644
index 0000000..c9deacc
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionLocator.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.locator.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cloudstack.spring.module.locator.ModuleDefinitionLocator;
+import org.apache.cloudstack.spring.module.model.ModuleDefinition;
+import org.apache.cloudstack.spring.module.model.impl.DefaultModuleDefinition;
+import org.apache.cloudstack.spring.module.util.ModuleLocationUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+public class ClasspathModuleDefinitionLocator implements ModuleDefinitionLocator {
+
+ protected ResourcePatternResolver getResolver() {
+ return new PathMatchingResourcePatternResolver();
+ }
+
+ public Collection<ModuleDefinition> locateModules(String context) throws IOException {
+ ResourcePatternResolver resolver = getResolver();
+
+ Map<String, ModuleDefinition> allModules = discoverModules(context, resolver);
+
+ return allModules.values();
+ }
+
+ protected Map<String, ModuleDefinition> discoverModules(String baseDir, ResourcePatternResolver resolver) throws IOException {
+ Map<String, ModuleDefinition> result = new HashMap<String, ModuleDefinition>();
+
+ for ( Resource r : resolver.getResources(ModuleLocationUtils.getModulesLocation(baseDir)) ) {
+ DefaultModuleDefinition def = new DefaultModuleDefinition(baseDir, r, resolver);
+ def.init();
+
+ if ( def.isValid() )
+ result.put(def.getName(), def);
+ }
+
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinition.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinition.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinition.java
new file mode 100644
index 0000000..b3c4647
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinition.java
@@ -0,0 +1,48 @@
+/*
+ * 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.model;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.core.io.Resource;
+
+public interface ModuleDefinition {
+
+ ClassLoader getClassLoader();
+
+ String getName();
+
+ String getParentName();
+
+ List<Resource> getConfigLocations();
+
+ List<Resource> getContextLocations();
+
+ List<Resource> getInheritableContextLocations();
+
+ List<Resource> getOverrideContextLocations();
+
+ boolean isValid();
+
+ Collection<ModuleDefinition> getChildren();
+
+ void addChild(ModuleDefinition childDef);
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinitionSet.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinitionSet.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinitionSet.java
new file mode 100644
index 0000000..635a7a1
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/ModuleDefinitionSet.java
@@ -0,0 +1,32 @@
+/*
+ * 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.model;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+
+public interface ModuleDefinitionSet {
+
+ ModuleDefinition getModuleDefinition(String name);
+
+ ApplicationContext getApplicationContext(String name);
+
+ Resource[] getConfigResources(String name);
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java
new file mode 100644
index 0000000..6c51808
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java
@@ -0,0 +1,167 @@
+/*
+ * 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.model.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.apache.cloudstack.spring.module.model.ModuleDefinition;
+import org.apache.cloudstack.spring.module.util.ModuleLocationUtils;
+import org.apache.commons.io.IOUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.util.StringUtils;
+
+public class DefaultModuleDefinition implements ModuleDefinition {
+
+ public static final String NAME = "name";
+ public static final String PARENT = "parent";
+
+ String name;
+ String baseDir;
+ String parent;
+ Resource moduleProperties;
+ ResourcePatternResolver resolver;
+ boolean valid;
+
+ List<Resource> configLocations;
+ List<Resource> contextLocations;
+ List<Resource> inheritableContextLocations;
+ List<Resource> overrideContextLocations;
+ Map<String, ModuleDefinition> children = new TreeMap<String, ModuleDefinition>();
+
+ public DefaultModuleDefinition(String baseDir, Resource moduleProperties, ResourcePatternResolver resolver) {
+ this.baseDir = baseDir;
+ this.resolver = resolver;
+ this.moduleProperties = moduleProperties;
+ }
+
+ public void init() throws IOException {
+
+ if ( ! moduleProperties.exists() ) {
+ return;
+ }
+
+ resolveNameAndParent();
+
+ contextLocations = Arrays.asList(resolver.getResources(ModuleLocationUtils.getContextLocation(baseDir, name)));
+ configLocations = Arrays.asList(resolver.getResources(ModuleLocationUtils.getDefaultsLocation(baseDir, name)));
+ inheritableContextLocations = Arrays.asList(resolver.getResources(ModuleLocationUtils.getInheritableContextLocation(baseDir, name)));
+ overrideContextLocations = Arrays.asList(resolver.getResources(ModuleLocationUtils.getOverrideContextLocation(baseDir, name)));
+
+ valid = true;
+ }
+
+ protected void resolveNameAndParent() throws IOException {
+ InputStream is = null;
+
+ try {
+ is = moduleProperties.getInputStream();
+ Properties props = new Properties();
+ props.load(is);
+
+ name = props.getProperty(NAME);
+ parent = props.getProperty(PARENT);
+
+ if ( ! StringUtils.hasText(name) ) {
+ throw new IOException("Missing name property in [" + location() + "]");
+ }
+
+ if ( ! StringUtils.hasText(parent) ) {
+ parent = null;
+ }
+
+ checkNameMatchesSelf();
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ }
+
+ protected void checkNameMatchesSelf() throws IOException {
+ String expectedLocation = ModuleLocationUtils.getModuleLocation(baseDir, name);
+ Resource self = resolver.getResource(expectedLocation);
+
+ if ( ! self.exists() ) {
+ throw new IOException("Resource [" + location() + "] is expected to exist at [" +
+ expectedLocation + "] please ensure the name property is correct");
+ }
+
+ String moduleUrl = moduleProperties.getURL().toExternalForm();
+ String selfUrl = self.getURL().toExternalForm();
+
+ if ( ! moduleUrl.equals(selfUrl) ) {
+ throw new IOException("Resource [" + location() + "] and [" +
+ self.getURL() + "] do not appear to be the same resource, " +
+ "please ensure the name property is correct");
+ }
+ }
+
+ private String location() throws IOException {
+ return moduleProperties.getURL().toString();
+ }
+
+ public void addChild(ModuleDefinition def) {
+ children.put(def.getName(), def);
+ }
+
+ public Collection<ModuleDefinition> getChildren() {
+ return children.values();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getParentName() {
+ return parent;
+ }
+
+ public List<Resource> getConfigLocations() {
+ return configLocations;
+ }
+
+ public List<Resource> getContextLocations() {
+ return contextLocations;
+ }
+
+ public List<Resource> getInheritableContextLocations() {
+ return inheritableContextLocations;
+ }
+
+ @Override
+ public List<Resource> getOverrideContextLocations() {
+ return overrideContextLocations;
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public ClassLoader getClassLoader() {
+ return resolver.getClassLoader();
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/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
new file mode 100644
index 0000000..15df839
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java
@@ -0,0 +1,243 @@
+/*
+ * 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.model.impl;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+
+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_XML = "defaults-context.xml";
+
+ String root;
+ Map<String, ModuleDefinition> modules;
+ Map<String, ApplicationContext> contexts = new HashMap<String, ApplicationContext>();
+ ApplicationContext rootContext = null;
+
+ public DefaultModuleDefinitionSet(Map<String, ModuleDefinition> modules, String root) {
+ super();
+ this.root = root;
+ this.modules = modules;
+ }
+
+ public void load() throws IOException {
+ if ( ! loadRootContext() )
+ return;
+
+ printHierarchy();
+ loadContexts();
+ startContexts();
+ }
+
+ protected boolean loadRootContext() {
+ ModuleDefinition def = modules.get(root);
+
+ if ( def == null )
+ return false;
+
+ ApplicationContext defaultsContext = getDefaultsContext();
+
+ rootContext = loadContext(def, defaultsContext);
+
+ return true;
+ }
+
+ protected void startContexts() {
+ withModule(new WithModule() {
+ public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) {
+ try {
+ ApplicationContext context = getApplicationContext(def.getName());
+ try {
+ Runnable runnable = context.getBean("moduleStartup", Runnable.class);
+ log.info("Starting module [{}]", def.getName());
+ runnable.run();
+ } catch ( BeansException e ) {
+ // Ignore
+ }
+ } catch ( EmptyStackException e ) {
+ // The root context is already loaded, so ignore the exception
+ }
+ }
+ });
+ }
+
+ protected void loadContexts() {
+ withModule(new WithModule() {
+ public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) {
+ try {
+ ApplicationContext parent = getApplicationContext(parents.peek().getName());
+ loadContext(def, parent);
+ } catch ( EmptyStackException e ) {
+ // The root context is already loaded, so ignore the exception
+ }
+ }
+ });
+ }
+ protected ApplicationContext loadContext(ModuleDefinition def, ApplicationContext parent) {
+ ResourceApplicationContext context = new ResourceApplicationContext();
+ context.setApplicationName("/" + def.getName());
+
+ Resource[] resources = getConfigResources(def.getName());
+ context.setConfigResources(resources);
+ context.setParent(parent);
+ context.setClassLoader(def.getClassLoader());
+
+ long start = System.currentTimeMillis();
+ if ( log.isInfoEnabled() ) {
+ for ( Resource resource : resources ) {
+ log.info("Loading module context [{}] from {}", def.getName(), resource);
+ }
+ }
+ context.refresh();
+ log.info("Loaded module context [{}] in {} ms", def.getName(), (System.currentTimeMillis() - start));
+
+ contexts.put(def.getName(), context);
+
+ return context;
+ }
+
+ protected boolean shouldLoad(ModuleDefinition def) {
+ return true;
+ }
+
+ protected ApplicationContext getDefaultsContext() {
+ URL config = DefaultModuleDefinitionSet.class.getResource(DEFAULT_CONFIG_XML);
+
+ ResourceApplicationContext context = new ResourceApplicationContext(new UrlResource(config));
+ context.setApplicationName("/defaults");
+ context.refresh();
+
+ @SuppressWarnings("unchecked")
+ final List<Resource> resources = (List<Resource>) context.getBean(DEFAULT_CONFIG_RESOURCES);
+
+ withModule(new WithModule() {
+ public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) {
+ for ( Resource defaults : def.getConfigLocations() ) {
+ resources.add(defaults);
+ }
+ }
+ });
+
+ return context;
+ }
+
+ protected void printHierarchy() {
+ withModule(new WithModule() {
+ public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) {
+ log.info(String.format("Module Hierarchy:%" + ((parents.size() * 2) + 1) + "s%s", "", def.getName()));
+ }
+ });
+ }
+
+ protected void withModule(WithModule with) {
+ ModuleDefinition rootDef = modules.get(root);
+ withModule(rootDef, new Stack<ModuleDefinition>(), with);
+ }
+
+ protected void withModule(ModuleDefinition def, Stack<ModuleDefinition> parents, WithModule with) {
+ if ( def == null )
+ return;
+
+ if ( ! shouldLoad(def) ) {
+ return;
+ }
+
+ with.with(def, parents);
+
+ parents.push(def);
+
+ for ( ModuleDefinition child : def.getChildren() ) {
+ withModule(child, parents, with);
+ }
+
+ parents.pop();
+ }
+
+ private static interface WithModule {
+ public void with(ModuleDefinition def, Stack<ModuleDefinition> parents);
+ }
+
+ @Configuration
+ public static class ConfigContext {
+
+ List<Resource> resources;
+
+ public ConfigContext(List<Resource> resources) {
+ super();
+ this.resources = resources;
+ }
+
+ @Bean(name = DEFAULT_CONFIG_RESOURCES)
+ public List<Resource> defaultConfigResources() {
+ return new ArrayList<Resource>();
+ }
+ }
+
+ public ApplicationContext getApplicationContext(String name) {
+ return contexts.get(name);
+ }
+
+ public Resource[] getConfigResources(String name) {
+ Set<Resource> resources = new LinkedHashSet<Resource>();
+
+ ModuleDefinition original = null;
+ ModuleDefinition def = original = modules.get(name);
+
+ if ( def == null )
+ return new Resource[] {};
+
+ resources.addAll(def.getContextLocations());
+
+ while ( def != null ) {
+ resources.addAll(def.getInheritableContextLocations());
+ def = modules.get(def.getParentName());
+ }
+
+ resources.addAll(original.getOverrideContextLocations());
+
+ return resources.toArray(new Resource[resources.size()]);
+ }
+
+ public ModuleDefinition getModuleDefinition(String name) {
+ return modules.get(name);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/Main.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/Main.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/Main.java
new file mode 100644
index 0000000..3a9660c
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/Main.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+import org.apache.cloudstack.spring.module.factory.CloudStackSpringContext;
+
+public class Main {
+
+ long start = System.currentTimeMillis();
+
+ public Main() {
+
+ }
+
+ public void start() throws Exception {
+ CloudStackSpringContext context = new CloudStackSpringContext();
+ context.registerShutdownHook();
+
+ if ( Boolean.getBoolean("force.exit") ) {
+ System.exit(0);
+ }
+ }
+
+ public long getTime() {
+ return System.currentTimeMillis() - start;
+ }
+
+
+ public static void main(String... args) {
+ Main main = new Main();
+
+ try {
+ main.start();
+ System.out.println("STARTUP COMPLETE [" + main.getTime() + "] ms");
+ } catch ( Exception e ) {
+ e.printStackTrace();
+ System.out.println("STARTUP FAILED [" + main.getTime() + "] ms");
+ System.err.println("STARTUP FAILED [" + main.getTime() + "] ms");
+ System.exit(1);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/67186429/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/ModuleLocationUtils.java
----------------------------------------------------------------------
diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/ModuleLocationUtils.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/ModuleLocationUtils.java
new file mode 100644
index 0000000..eeab154
--- /dev/null
+++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/util/ModuleLocationUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util;
+
+public class ModuleLocationUtils {
+
+ private static final String ALL_MODULE_PROPERTIES = "classpath*:%s/*/module.properties";
+ private static final String MODULE_PROPERTIES = "classpath:%s/%s/module.properties";
+ private static final String CONTEXT_LOCATION = "classpath*:%s/%s/*context.xml";
+ private static final String INHERTIABLE_CONTEXT_LOCATION = "classpath*:%s/%s/*context-inheritable.xml";
+ private static final String OVERRIDE_CONTEXT_LOCATION = "classpath*:%s/%s/*context-override.xml";
+ private static final String DEFAULTS_LOCATION = "classpath*:%s/%s/*defaults.properties";
+
+ public static String getModulesLocation(String baseDir) {
+ return String.format(ALL_MODULE_PROPERTIES, baseDir);
+ }
+
+ public static String getModuleLocation(String baseDir, String name) {
+ return String.format(MODULE_PROPERTIES, baseDir, name);
+ }
+
+ public static String getContextLocation(String baseDir, String name) {
+ return String.format(CONTEXT_LOCATION, baseDir, name);
+ }
+
+ public static String getInheritableContextLocation(String baseDir, String name) {
+ return String.format(INHERTIABLE_CONTEXT_LOCATION, baseDir, name);
+ }
+
+ public static String getOverrideContextLocation(String baseDir, String name) {
+ return String.format(OVERRIDE_CONTEXT_LOCATION, baseDir, name);
+ }
+
+ public static String getDefaultsLocation(String baseDir, String name) {
+ return String.format(DEFAULTS_LOCATION, baseDir, name);
+ }
+}