All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geronimo.kernel.config.Configuration Maven / Gradle / Ivy

The newest version!
/**
 *  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.geronimo.kernel.config;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.AbstractNameQuery;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.ReferencePatterns;
import org.apache.geronimo.gbean.annotation.GBean;
import org.apache.geronimo.gbean.annotation.ParamAttribute;
import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Naming;
import org.apache.geronimo.kernel.repository.Artifact;
import org.apache.geronimo.kernel.repository.Environment;
import org.apache.geronimo.kernel.repository.MissingDependencyException;
import org.apache.xbean.osgi.bundle.util.BundleUtils;
import org.apache.xbean.osgi.bundle.util.DelegatingBundle;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Configuration represents a collection of runnable services that can be
 * loaded into a Geronimo Kernel and brought online. The primary components in
 * a Configuration are a codebase, represented by a collection of URLs that
 * is used to locate classes, and a collection of GBean instances that define
 * its state.
 * 

* The persistent attributes of the Configuration are: *

    *
  • its unique configId used to identify this specific config
  • *
  • the configId of a parent Configuration on which this one is dependent
  • *
  • a List of code locations (which may be absolute or relative to a baseURL)
  • *
  • a byte[] holding the state of the GBeans instances in Serialized form
  • *
* When a configuration is started, it converts the URIs into a set of absolute * URLs by resolving them against the specified baseURL (this would typically * be the root of the CAR file which contains the configuration) and then * constructs a ClassLoader for that codebase. That ClassLoader is then used * to de-serialize the persisted GBeans, ensuring the GBeans can be recycled * as necessary. Once the GBeans have been restored, they are brought online * by registering them with the MBeanServer. *

* A dependency on the Configuration is created for every GBean it loads. As a * result, a startRecursive() operation on the configuration will result in * a startRecursive() for all the GBeans it contains. Similarly, if the * Configuration is stopped then all of its GBeans will be stopped as well. * * @version $Rev:385718 $ $Date: 2012-04-28 10:17:12 +0800 (Sat, 28 Apr 2012) $ */ @GBean public class Configuration implements GBeanLifecycle, ConfigurationParent { private static final Logger log = LoggerFactory.getLogger(Configuration.class); /** * Converts an Artifact to an AbstractName for a configuration. Does not * validate that this is a reasonable or resolved Artifact, or that it * corresponds to an actual Configuration. * @param configId id for configuration * @return abstract name constructed from supplied id * @throws InvalidConfigException if the ObjectName could not be constructed */ public static AbstractName getConfigurationAbstractName(Artifact configId) throws InvalidConfigException { return new AbstractName(configId, Collections.singletonMap("configurationName", configId.toString()), getConfigurationObjectName(configId)); } public static boolean isConfigurationObjectName(ObjectName name) { return name.getDomain().equals("geronimo.config") && name.getKeyPropertyList().size() == 1 && name.getKeyProperty("name") != null; } public static Artifact getConfigurationID(ObjectName objectName) { if (isConfigurationObjectName(objectName)) { String name = ObjectName.unquote(objectName.getKeyProperty("name")); return Artifact.create(name); } else { throw new IllegalArgumentException("ObjectName " + objectName + " is not a Configuration name"); } } private static ObjectName getConfigurationObjectName(Artifact configId) throws InvalidConfigException { try { return new ObjectName("geronimo.config:name=" + ObjectName.quote(configId.toString())); } catch (MalformedObjectNameException e) { throw new InvalidConfigException("Could not construct object name for configuration", e); } } /** * The registered abstractName for this configuraion. */ private final AbstractName abstractName; /** * Used to resolve dependecies and paths */ private final ConfigurationResolver configurationResolver; /** * Contains ids of class and service dependencies for parent configurations */ private final DependencyNode dependencyNode; /** * All service parents depth first */ private final List allServiceParents; /** * The GBeanData objects by ObjectName */ private final Map gbeans = new LinkedHashMap(); /** * Naming system used when generating a name for a new gbean */ private final Naming naming; /** * Environment, classpath, gbeans and other data for this configuration. */ private final ConfigurationData configurationData; /** * The nested configurations of this configuration. */ private final List children = new ArrayList(); /** * The enclosing parent of this configuration (e.g. if this is for a war file, an enclosing ear file's configuration); */ private Configuration parent = null; /** * Manageable Attribute Store containing overrides to this configuration. */ private ManageableAttributeStore attributeStore = null; private Bundle bundle; /** * Creates a configuration. * // * @param classLoaderHolder Classloaders for this configuration * @param configurationData the module type, environment and classpath of the configuration * @param dependencyNode Class and Service parent ids * @param allServiceParents ordered list of transitive closure of service parents for gbean searches * @param attributeStore Customization info for gbeans * @param configurationResolver (there should be a better way) Where this configuration is actually located in file system * @throws InvalidConfigException if this configuration turns out to have a problem. */ public Configuration( @ParamAttribute(name = "configurationData") ConfigurationData configurationData, @ParamAttribute(name = "dependencyNode") DependencyNode dependencyNode, @ParamAttribute(name = "allServiceParents") List allServiceParents, @ParamAttribute(name = "attributeStore") ManageableAttributeStore attributeStore, @ParamAttribute(name = "configurationResolver") ConfigurationResolver configurationResolver, @ParamAttribute(name = "configurationManager") ConfigurationManager configurationManager) throws InvalidConfigException { if (configurationData == null) { throw new NullPointerException("configurationData is null"); } this.configurationData = configurationData; this.naming = configurationData.getNaming(); this.attributeStore = attributeStore; this.dependencyNode = dependencyNode; this.allServiceParents = allServiceParents; this.configurationResolver = configurationResolver; this.abstractName = getConfigurationAbstractName(dependencyNode.getId()); this.bundle = configurationData.getBundleContext().getBundle(); if (configurationData.isUseEnvironment() && configurationManager != null) { try { Collection bundles = getBundles(configurationData, configurationResolver, configurationManager); this.bundle = new DelegatingBundle(bundles); } catch (Exception e) { log.debug("Failed to identify bundle parents for " + configurationData.getId(), e); } } try { // Deserialize the GBeans in the configurationData Collection gbeans = configurationData.getGBeans(bundle); if (attributeStore != null) { gbeans = attributeStore.applyOverrides(dependencyNode.getId(), gbeans, bundle); } for (GBeanData gbeanData : gbeans) { this.gbeans.put(gbeanData.getAbstractName(), gbeanData); } } catch (RuntimeException e) { shutdown(); throw e; } catch (Error e) { shutdown(); throw e; } catch (InvalidConfigException e) { shutdown(); throw e; } } private Collection getBundles(ConfigurationData configurationData, ConfigurationResolver configurationResolver, ConfigurationManager configurationManager) throws MissingDependencyException, InvalidConfigException { Set bundles = new LinkedHashSet(); bundles.add(configurationData.getBundleContext().getBundle()); getAllBundles(configurationData, configurationManager, new HashSet(), bundles); return bundles; } /* * Gets all dependent non-configuration bundles. */ private void getAllBundles(ConfigurationData configurationData, ConfigurationManager configurationManager, Set artifacts, Collection bundles) throws MissingDependencyException, InvalidConfigException { LinkedHashSet parents = configurationManager.resolveParentIds(configurationData); for (Artifact parent : parents) { if (artifacts.contains(parent)) { continue; } artifacts.add(parent); boolean isConfiguration = configurationManager.isConfiguration(parent); if (isConfiguration) { Configuration configuration = configurationManager.getConfiguration(parent); getAllBundles(configuration.getConfigurationData(), configurationManager, artifacts, bundles); } else { String location = getBundleLocation(configurationResolver, parent); Bundle bundle = getBundleByLocation(configurationData.getBundleContext(), location); if (bundle != null) { bundles.add(bundle); } } } } private static String getBundleLocation(ConfigurationResolver configurationResolver, Artifact configurationId) { if (configurationResolver == null) { throw new NullPointerException("ConfigurationResolver is null"); } try { File file = configurationResolver.resolve(configurationId); return BundleUtils.toReferenceFileLocation(file); } catch (MissingDependencyException e) { return null; } catch (IOException e) { return null; } } private static Bundle getBundleByLocation(BundleContext bundleContext, String location) { for (Bundle bundle: bundleContext.getBundles()) { if (location.equals(bundle.getLocation())) { return bundle; } } return null; } /** * Add a contained configuration, such as for a war inside an ear * @param child contained configuration */ void addChild(Configuration child) { children.add(child); child.parent = this; } /** * Return the unique Id * * @return the unique Id */ public Artifact getId() { return dependencyNode.getId(); } /** * Gets the unique name of this configuration within the kernel. * * @return the unique name of this configuration */ public String getObjectName() { try { return getConfigurationObjectName(getId()).getCanonicalName(); } catch (InvalidConfigException e) { throw new AssertionError(e); } } public AbstractName getAbstractName() { return abstractName; } // public ClassLoaderHolder getClassLoaderHolder() { // return classLoaderHolder; // } /** * Gets the parent configurations used for class loading. * @return the parents of this configuration used for class loading */ // public List getClassParents() { // return classParents; // } /** * Gets the parent configurations used for service resolution. * * @return the parents of this configuration used for service resolution */ // public List getServiceParents() { // return serviceParents; // } public DependencyNode getDependencyNode() { return dependencyNode; } /** * Gets the artifact dependencies of this configuration. * @return the artifact dependencies of this configuration */ // public LinkedHashSet getDependencies() { // return dependencies; // } /** * Gets the declaration of the environment in which this configuration runs. * * @return the environment of this configuration */ public Environment getEnvironment() { return configurationData.getEnvironment(); } /** * This is used by the configuration manager to restart an existing configuation. * Do not modify the configuration data. * * @return the configuration data for this configuration; do not modify */ ConfigurationData getConfigurationData() { return configurationData; } public File getConfigurationDir() { return configurationData.getConfigurationDir(); } /** * Provide a way to locate where this configuration is for web apps and persistence units * @return the ConfigurationResolver for this configuration */ public ConfigurationResolver getConfigurationResolver() { return configurationResolver; } /** * Gets the type of the configuration (WAR, RAR et cetera) * * @return Type of the configuration. */ public ConfigurationModuleType getModuleType() { return configurationData.getModuleType(); } /** * Gets the time at which this configuration was created (or deployed). * * @return the time at which this configuration was created (or deployed) */ public long getCreated() { return configurationData.getCreated(); } /** * Gets the class loader for this configuration. * * @return the bundle for this configuration */ public Bundle getBundle() { return bundle; } public BundleContext getBundleContext() { return bundle.getBundleContext(); } /** * Gets the nested configurations of this configuration. That is, the * configurations within this one as a WAR can be within an EAR; not * including wholly separate configurations that just depend on this * one as a parent. * * @return the nested configuration of this configuration */ public List getChildren() { return Collections.unmodifiableList(children); } /** * Gets the configurations owned by this configuration. This is only used for cascade-uninstall. * * @return the configurations owned by this configuration */ public Set getOwnedConfigurations() { return configurationData.getOwnedConfigurations(); } /** * Gets an unmodifiable collection of the GBeanDatas for the GBeans in this configuration. * * @return the GBeans in this configuration */ public Map getGBeans() { return Collections.unmodifiableMap(gbeans); } /** * Determines of this configuration constains the specified GBean. * * @param gbean the name of the GBean * @return true if this configuration contains the specified GBean; false otherwise */ public synchronized boolean containsGBean(AbstractName gbean) { return gbeans.containsKey(gbean); } /** * Gets the enclosing configuration of this one (e.g. the EAR for a WAR), * or null if it has none. * * @return enclosing configuration, if any */ public Configuration getEnclosingConfiguration() { return parent; } /** * Gets the manageable attribute store for this configuration. * This is used in the configuration manager to apply overrides * * @return customization source for gbeans */ ManageableAttributeStore getManageableAttributeStore() { return attributeStore; } public synchronized AbstractName addGBean(String name, GBeanData gbean) throws GBeanAlreadyExistsException { AbstractName abstractName = gbean.getAbstractName(); if (abstractName != null) { throw new IllegalArgumentException("gbean already has an abstract name: " + abstractName); } String j2eeType = gbean.getGBeanInfo().getJ2eeType(); if (j2eeType == null) j2eeType = "GBean"; abstractName = naming.createRootName(getId(), name, j2eeType); gbean.setAbstractName(abstractName); if (gbeans.containsKey(abstractName)) { throw new GBeanAlreadyExistsException(gbean.getAbstractName().toString()); } gbeans.put(abstractName, gbean); return abstractName; } public synchronized void addGBean(GBeanData gbean) throws GBeanAlreadyExistsException { if (gbeans.containsKey(gbean.getAbstractName())) { throw new GBeanAlreadyExistsException(gbean.getAbstractName().toString()); } gbeans.put(gbean.getAbstractName(), gbean); } public synchronized void removeGBean(AbstractName name) throws GBeanNotFoundException { if (!gbeans.containsKey(name)) { throw new GBeanNotFoundException(name); } gbeans.remove(name); } public AbstractName findGBean(AbstractNameQuery pattern) throws GBeanNotFoundException { if (pattern == null) throw new NullPointerException("pattern is null"); return findGBean(Collections.singleton(pattern)); } public GBeanData findGBeanData(AbstractNameQuery pattern) throws GBeanNotFoundException { if (pattern == null) throw new NullPointerException("pattern is null"); return findGBeanData(Collections.singleton(pattern)); } public AbstractName findGBean(ReferencePatterns referencePatterns) throws GBeanNotFoundException { if (referencePatterns == null) throw new NullPointerException("referencePatterns is null"); if (referencePatterns.isResolved()) { return referencePatterns.getAbstractName(); } // check the local config Set patterns = referencePatterns.getPatterns(); return findGBean(patterns); } public AbstractName findGBean(Set patterns) throws GBeanNotFoundException { if (patterns == null) throw new NullPointerException("patterns is null"); return findGBeanData(patterns).getAbstractName(); } public GBeanData findGBeanData(Set patterns) throws GBeanNotFoundException { if (patterns == null) throw new NullPointerException("patterns is null"); Set result = findGBeanDatas(this, patterns); if (result.size() > 1) { throw new GBeanNotFoundException("More than one match to referencePatterns in local configuration", patterns, mapToNames(result)); } else if (result.size() == 1) { return result.iterator().next(); } // search all parents for (Configuration configuration : allServiceParents) { result.addAll(findGBeanDatas(configuration, patterns)); } // if we already found a match we have an ambiguous query if (result.size() > 1) { List names = new ArrayList(result.size()); for (GBeanData gBeanData : result) { names.add(gBeanData.getAbstractName()); } throw new GBeanNotFoundException("More than one match to referencePatterns in parent configurations: " + names.toString(), patterns, mapToNames(result)); } if (result.isEmpty()) { throw new GBeanNotFoundException("No matches for referencePatterns", patterns, null); } return result.iterator().next(); } private Set mapToNames(Set datas) { Set names = new HashSet(datas.size()); for (GBeanData gBeanData : datas) { names.add(gBeanData.getAbstractName()); } return names; } public LinkedHashSet findGBeans(AbstractNameQuery pattern) { if (pattern == null) throw new NullPointerException("pattern is null"); return findGBeans(Collections.singleton(pattern)); } public LinkedHashSet findGBeans(ReferencePatterns referencePatterns) { if (referencePatterns == null) throw new NullPointerException("referencePatterns is null"); if (referencePatterns.getAbstractName() != null) { // this pattern is already resolved LinkedHashSet result = new LinkedHashSet(); result.add(referencePatterns.getAbstractName()); return result; } // check the local config Set patterns = referencePatterns.getPatterns(); return findGBeans(patterns); } public LinkedHashSet findGBeans(Set patterns) { if (patterns == null) throw new NullPointerException("patterns is null"); LinkedHashSet datas = findGBeanDatas(patterns); LinkedHashSet result = new LinkedHashSet(datas.size()); for (GBeanData gBeanData : datas) { result.add(gBeanData.getAbstractName()); } return result; } public LinkedHashSet findGBeanDatas(Set patterns) { if (patterns == null) throw new NullPointerException("patterns is null"); LinkedHashSet datas = findGBeanDatas(this, patterns); // search all parents for (Configuration configuration : allServiceParents) { Set match = findGBeanDatas(configuration, patterns); datas.addAll(match); } return datas; } /** * Find the gbeanDatas matching the patterns in this configuration only, ignoring parents. * * @param configuration configuration to look in * @param patterns patterns to look for * @return set of gbeandatas matching one of the patterns from this configuration only, not including parents. */ public LinkedHashSet findGBeanDatas(Configuration configuration, Set patterns) { LinkedHashSet result = new LinkedHashSet(); Set> gbeanNames = configuration.getGBeans().entrySet(); for (AbstractNameQuery abstractNameQuery : patterns) { Artifact queryArtifact = abstractNameQuery.getArtifact(); // Does this query apply to this configuration if (queryArtifact == null || queryArtifact.matches(configuration.getId())) { // Search the GBeans for (Map.Entry entry : gbeanNames) { AbstractName abstractName = entry.getKey(); GBeanData gbeanData = entry.getValue(); if (abstractNameQuery.matches(abstractName, gbeanData.getGBeanInfo().getInterfaces())) { result.add(gbeanData); } } } } return result; } public void doStart() throws Exception { log.debug("Started configuration {}", getId()); } public synchronized void doStop() throws Exception { log.debug("Stopping configuration {}", getId()); shutdown(); } public void doFail() { log.debug("Failed configuration {}", getId()); shutdown(); } private void shutdown() { for (Configuration configuration : children) { configuration.shutdown(); } // clear references to GBeanDatas gbeans.clear(); // classLoaderHolder.destroy(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy