| /* |
| * 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.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.commons.io.IOUtils; |
| import org.apache.log4j.Logger; |
| 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; |
| import org.springframework.util.StringUtils; |
| |
| import org.apache.cloudstack.spring.module.context.ResourceApplicationContext; |
| import org.apache.cloudstack.spring.module.model.ModuleDefinition; |
| import org.apache.cloudstack.spring.module.model.ModuleDefinitionSet; |
| |
| public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { |
| |
| private static final Logger log = Logger.getLogger(DefaultModuleDefinitionSet.class); |
| |
| public static final String DEFAULT_CONFIG_RESOURCES = "DefaultConfigResources"; |
| public static final String DEFAULT_CONFIG_PROPERTIES = "DefaultConfigProperties"; |
| public static final String MODULES_EXCLUDE = "modules.exclude"; |
| public static final String MODULES_INCLUDE_PREFIX = "modules.include."; |
| public static final String MODULE_PROPERITES = "ModuleProperties"; |
| public static final String DEFAULT_CONFIG_XML = "defaults-context.xml"; |
| |
| String root; |
| Map<String, ModuleDefinition> modules; |
| Map<String, ApplicationContext> contexts = new HashMap<String, ApplicationContext>(); |
| ApplicationContext rootContext = null; |
| Set<String> excludes = new HashSet<String>(); |
| Properties configProperties = null; |
| |
| public DefaultModuleDefinitionSet(Map<String, ModuleDefinition> modules, String root) { |
| super(); |
| 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() { |
| @Override |
| public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) { |
| try { |
| String moduleDefinitionName = def.getName(); |
| log.debug(String.format("Trying to obtain module [%s] context.", moduleDefinitionName)); |
| ApplicationContext context = getApplicationContext(moduleDefinitionName); |
| try { |
| if (context.containsBean("moduleStartup")) { |
| Runnable runnable = context.getBean("moduleStartup", Runnable.class); |
| log.info(String.format("Starting module [%s].", moduleDefinitionName)); |
| runnable.run(); |
| } else { |
| log.debug(String.format("Could not get module [%s] context bean.", moduleDefinitionName)); |
| } |
| } catch (BeansException e) { |
| log.warn(String.format("Failed to start module [%s] due to: [%s].", moduleDefinitionName, e.getMessage())); |
| if (log.isDebugEnabled()) { |
| log.debug(String.format("module start failure of module [%s] was due to: ", moduleDefinitionName), e); |
| } |
| } |
| } catch (EmptyStackException e) { |
| log.warn(String.format("Failed to obtain module context due to [%s]. Using root context instead.", e.getMessage())); |
| if (log.isDebugEnabled()) { |
| log.debug("Failed to obtain module context: ", e); |
| } |
| } |
| } |
| }); |
| } |
| |
| protected void loadContexts() { |
| withModule(new WithModule() { |
| @Override |
| public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) { |
| try { |
| String moduleDefinitionName = def.getName(); |
| if (parents.isEmpty()) { |
| log.debug(String.format("Could not find module [%s] context as they have no parents.", moduleDefinitionName)); |
| return; |
| } |
| log.debug(String.format("Trying to obtain module [%s] context.", moduleDefinitionName)); |
| ApplicationContext parent = getApplicationContext(parents.peek().getName()); |
| log.debug(String.format("Trying to load module [%s] context.", moduleDefinitionName)); |
| loadContext(def, parent); |
| } catch (EmptyStackException e) { |
| log.warn(String.format("Failed to obtain module context due to [%s]. Using root context instead.", e.getMessage())); |
| if (log.isDebugEnabled()) { |
| log.debug("Failed to obtain module context: ", e); |
| } |
| } catch (BeansException e) { |
| log.warn(String.format("Failed to start module [%s] due to: [%s].", def.getName(), e.getMessage())); |
| if (log.isDebugEnabled()) { |
| log.debug(String.format("module start failure of module [%s] was due to: ", def.getName()), e); |
| } |
| } |
| } |
| }); |
| } |
| |
| 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 [" + def.getName() + "] from " + resource); |
| } |
| } |
| context.refresh(); |
| log.info("Loaded module context [" + def.getName() + "] in " + (System.currentTimeMillis() - start) + " ms"); |
| |
| contexts.put(def.getName(), context); |
| |
| return context; |
| } |
| |
| protected boolean shouldLoad(ModuleDefinition def) { |
| return !excludes.contains(def.getName()); |
| } |
| |
| 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() { |
| @Override |
| public void with(ModuleDefinition def, Stack<ModuleDefinition> parents) { |
| for (Resource defaults : def.getConfigLocations()) { |
| resources.add(defaults); |
| } |
| } |
| }); |
| |
| 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() { |
| @Override |
| 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)) { |
| log.info("Excluding context [" + def.getName() + "] based on configuration"); |
| 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>(); |
| } |
| } |
| |
| @Override |
| public ApplicationContext getApplicationContext(String name) { |
| return contexts.get(name); |
| } |
| |
| @Override |
| public Map<String, ApplicationContext> getContextMap() { |
| return contexts; |
| } |
| |
| @Override |
| 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()]); |
| } |
| |
| @Override |
| public ModuleDefinition getModuleDefinition(String name) { |
| return modules.get(name); |
| } |
| } |