001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.kernel.config;
019    
020    import java.io.File;
021    import java.io.IOException;
022    import java.net.MalformedURLException;
023    import java.net.URL;
024    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.Iterator;
029    import java.util.LinkedHashMap;
030    import java.util.LinkedHashSet;
031    import java.util.List;
032    import java.util.ListIterator;
033    import java.util.Map;
034    import java.util.Set;
035    import java.util.HashSet;
036    
037    import javax.management.MalformedObjectNameException;
038    import javax.management.ObjectName;
039    
040    import org.apache.commons.logging.Log;
041    import org.apache.commons.logging.LogFactory;
042    import org.apache.geronimo.gbean.AbstractName;
043    import org.apache.geronimo.gbean.AbstractNameQuery;
044    import org.apache.geronimo.gbean.GBeanData;
045    import org.apache.geronimo.gbean.GBeanInfo;
046    import org.apache.geronimo.gbean.GBeanInfoBuilder;
047    import org.apache.geronimo.gbean.GBeanLifecycle;
048    import org.apache.geronimo.gbean.ReferencePatterns;
049    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
050    import org.apache.geronimo.kernel.GBeanNotFoundException;
051    import org.apache.geronimo.kernel.Naming;
052    import org.apache.geronimo.kernel.classloader.JarFileClassLoader;
053    import org.apache.geronimo.kernel.repository.Artifact;
054    import org.apache.geronimo.kernel.repository.Dependency;
055    import org.apache.geronimo.kernel.repository.Environment;
056    import org.apache.geronimo.kernel.repository.ImportType;
057    import org.apache.geronimo.kernel.repository.MissingDependencyException;
058    
059    /**
060     * A Configuration represents a collection of runnable services that can be
061     * loaded into a Geronimo Kernel and brought online. The primary components in
062     * a Configuration are a codebase, represented by a collection of URLs that
063     * is used to locate classes, and a collection of GBean instances that define
064     * its state.
065     * <p/>
066     * The persistent attributes of the Configuration are:
067     * <ul>
068     * <li>its unique configId used to identify this specific config</li>
069     * <li>the configId of a parent Configuration on which this one is dependent</li>
070     * <li>a List<URI> of code locations (which may be absolute or relative to a baseURL)</li>
071     * <li>a byte[] holding the state of the GBeans instances in Serialized form</li>
072     * </ul>
073     * When a configuration is started, it converts the URIs into a set of absolute
074     * URLs by resolving them against the specified baseURL (this would typically
075     * be the root of the CAR file which contains the configuration) and then
076     * constructs a ClassLoader for that codebase. That ClassLoader is then used
077     * to de-serialize the persisted GBeans, ensuring the GBeans can be recycled
078     * as necessary. Once the GBeans have been restored, they are brought online
079     * by registering them with the MBeanServer.
080     * <p/>
081     * A dependency on the Configuration is created for every GBean it loads. As a
082     * result, a startRecursive() operation on the configuration will result in
083     * a startRecursive() for all the GBeans it contains. Similarly, if the
084     * Configuration is stopped then all of its GBeans will be stopped as well.
085     *
086     * @version $Rev:385718 $ $Date: 2009-02-16 18:20:58 +0800 (Mon, 16 Feb 2009) $
087     */
088    public class Configuration implements GBeanLifecycle, ConfigurationParent {
089        private static final Log log = LogFactory.getLog(Configuration.class);
090    
091        /**
092         * Converts an Artifact to an AbstractName for a configuration.  Does not
093         * validate that this is a reasonable or resolved Artifact, or that it
094         * corresponds to an actual Configuration.
095         */
096        public static AbstractName getConfigurationAbstractName(Artifact configId) throws InvalidConfigException {
097            return new AbstractName(configId, Collections.singletonMap("configurationName", configId.toString()), getConfigurationObjectName(configId));
098        }
099    
100        public static boolean isConfigurationObjectName(ObjectName name) {
101            return name.getDomain().equals("geronimo.config") && name.getKeyPropertyList().size() == 1 && name.getKeyProperty("name") != null;
102        }
103    
104        public static Artifact getConfigurationID(ObjectName objectName) {
105            if (isConfigurationObjectName(objectName)) {
106                String name = ObjectName.unquote(objectName.getKeyProperty("name"));
107                return Artifact.create(name);
108            } else {
109                throw new IllegalArgumentException("ObjectName " + objectName + " is not a Configuration name");
110            }
111        }
112    
113        private static ObjectName getConfigurationObjectName(Artifact configId) throws InvalidConfigException {
114            try {
115                return new ObjectName("geronimo.config:name=" + ObjectName.quote(configId.toString()));
116            } catch (MalformedObjectNameException e) {
117                throw new InvalidConfigException("Could not construct object name for configuration", e);
118            }
119        }
120    
121        /**
122         * The artifact id for this configuration.
123         */
124        private final Artifact id;
125    
126        /**
127         * The registered abstractName for this configuraion.
128         */
129        private final AbstractName abstractName;
130    
131        /**
132         * Defines the environment requred for this configuration.
133         */
134        private final Environment environment;
135    
136        /**
137         * Used to resolve dependecies and paths
138         */
139        private final ConfigurationResolver configurationResolver;
140    
141        /**
142         * Parent configurations used for class loader.
143         */
144        private final List<Configuration> classParents = new ArrayList<Configuration>();
145    
146        /**
147         * Parent configuations used for service resolution.
148         */
149        private final List<Configuration> serviceParents = new ArrayList<Configuration>();
150    
151        /**
152         * All service parents depth first
153         */
154        private final List<Configuration> allServiceParents = new ArrayList<Configuration>();
155    
156        /**
157         * Artifacts added to the class loader (non-configuation artifacts).
158         */
159        private final LinkedHashSet<Artifact> dependencies = new LinkedHashSet<Artifact>();
160    
161        /**
162         * The GBeanData objects by ObjectName
163         */
164        private final Map<AbstractName, GBeanData> gbeans = new LinkedHashMap<AbstractName, GBeanData>();
165    
166        /**
167         * The classloader used to load the child GBeans contained in this configuration.
168         */
169        private final MultiParentClassLoader configurationClassLoader;
170    
171        /**
172         * The relative class path (URI) of this configuation.
173         */
174        private final LinkedHashSet<String> classPath;
175    
176        /**
177         * Naming system used when generating a name for a new gbean
178         */
179        private final Naming naming;
180    
181        /**
182         * Environment, classpath, gbeans and other data for this configuration.
183         */
184        private ConfigurationData configurationData;
185    
186        /**
187         * The nested configurations of this configuration.
188         */
189        List<Configuration> children = new ArrayList<Configuration>();
190    
191        /**
192         * The parent of this configuration;
193         */
194        private Configuration parent = null;
195        
196        /**
197         * Manageable Attribute Store containing overrides to this configuration.
198         */
199        private ManageableAttributeStore attributeStore = null;
200    
201        /**
202         * Only used to allow declaration as a reference.
203         */
204        public Configuration() {
205            id = null;
206            abstractName = null;
207            environment = null;
208            classPath = null;
209            configurationResolver = null;
210            configurationClassLoader = null;
211            naming = null;
212        }
213    
214        /**
215         * Creates a configuration.
216         * @param parents parents of this configuation (not ordered)
217         * @param configurationData the module type, environment and classpath of the configuration
218         * @param configurationResolver used to resolve dependecies and paths
219         */
220        public Configuration(Collection<Configuration> parents,
221                ConfigurationData configurationData,
222                ConfigurationResolver configurationResolver,
223                ManageableAttributeStore attributeStore) throws MissingDependencyException, MalformedURLException, NoSuchConfigException, InvalidConfigException {
224            if (parents == null) parents = Collections.EMPTY_SET;
225            if (configurationData == null) throw new NullPointerException("configurationData is null");
226            if (configurationResolver == null) throw new NullPointerException("configurationResolver is null");
227    
228            this.configurationData = configurationData;
229            this.environment = configurationData.getEnvironment();
230            this.configurationResolver = configurationResolver;
231            this.classPath = new LinkedHashSet<String>(configurationData.getClassPath());
232            this.naming = configurationData.getNaming();
233            this.attributeStore = attributeStore;
234            this.id = environment.getConfigId();
235            abstractName = getConfigurationAbstractName(id);
236    
237            //
238            // Transitively resolve all the dependencies in the environment
239            //
240            List<Dependency> transitiveDependencies = configurationResolver.resolveTransitiveDependencies(parents, environment.getDependencies());
241    
242            //
243            // Process transtive dependencies splitting it into classParents, serviceParents and artifactDependencies
244            //
245            Map<Artifact, Configuration> parentsById = new HashMap<Artifact, Configuration>();
246            for (Configuration configuration : parents) {
247                Artifact id = configuration.getId();
248                parentsById.put(id, configuration);
249            }
250    
251            for (Dependency dependency : transitiveDependencies) {
252                Artifact artifact = dependency.getArtifact();
253                if (parentsById.containsKey(artifact)) {
254                    Configuration parent = parentsById.get(artifact);
255                    if (dependency.getImportType() == ImportType.CLASSES || dependency.getImportType() == ImportType.ALL) {
256                        classParents.add(parent);
257                    }
258                    if (dependency.getImportType() == ImportType.SERVICES || dependency.getImportType() == ImportType.ALL) {
259                        serviceParents.add(parent);
260                    }
261                } else if (dependency.getImportType() == ImportType.SERVICES) {
262                    throw new IllegalStateException("Could not find parent " + artifact + " in the parents collection");
263                } else {
264                    dependencies.add(artifact);
265                }
266            }
267    
268            try {
269                //
270                // Build the configuration class loader
271                //
272                configurationClassLoader = createConfigurationClasssLoader(parents, environment, classPath);
273    
274                //
275                // Get all service parents in depth first order
276                //
277    
278                addDepthFirstServiceParents(this, allServiceParents, new HashSet<Artifact>());
279    
280                //
281                // Deserialize the GBeans in the configurationData
282                //
283                Collection<GBeanData> gbeans = configurationData.getGBeans(configurationClassLoader);
284                if (attributeStore != null) {
285                    gbeans = attributeStore.applyOverrides(id, gbeans, configurationClassLoader);
286                }
287                for (GBeanData gbeanData : gbeans) {
288                    this.gbeans.put(gbeanData.getAbstractName(), gbeanData);
289                }
290    
291                //
292                // Create child configurations
293                //
294                LinkedHashSet<Configuration> childParents = new LinkedHashSet<Configuration>(parents);
295                childParents.add(this);
296                for (Iterator iterator = configurationData.getChildConfigurations().entrySet().iterator(); iterator.hasNext();) {
297                    Map.Entry entry = (Map.Entry) iterator.next();
298                    String moduleName = (String) entry.getKey();
299                    ConfigurationData childConfigurationData = (ConfigurationData) entry.getValue();
300                    Configuration childConfiguration = new Configuration(childParents, childConfigurationData, configurationResolver.createChildResolver(moduleName), attributeStore);
301                    childConfiguration.parent = this;
302                    children.add(childConfiguration);
303                }
304            } catch (RuntimeException e) {
305                shutdown();
306                throw e;
307            } catch (Error e) {
308                shutdown();
309                throw e;
310            } catch (MissingDependencyException e) {
311                shutdown();
312                throw e;
313            } catch (MalformedURLException e) {
314                shutdown();
315                throw e;
316            } catch (NoSuchConfigException e) {
317                shutdown();
318                throw e;
319            } catch (InvalidConfigException e) {
320                shutdown();
321                throw e;
322            }
323        }
324    
325        private MultiParentClassLoader createConfigurationClasssLoader(Collection<Configuration> parents, Environment environment, LinkedHashSet<String> classPath) throws MalformedURLException, MissingDependencyException, NoSuchConfigException {
326            // create the URL list
327            URL[] urls = buildClassPath(classPath);
328    
329            // parents
330            ClassLoader[] parentClassLoaders;
331            if (parents.size() == 0 && classParents.size() == 0) {
332                // no explicit parent set, so use the class loader of this class as
333                // the parent... this class should be in the root geronimo classloader,
334                // which is normally the system class loader but not always, so be safe
335                parentClassLoaders = new ClassLoader[] {getClass().getClassLoader()};
336            } else {
337                parentClassLoaders = new ClassLoader[classParents.size()];
338                for (ListIterator iterator = classParents.listIterator(); iterator.hasNext();) {
339                    Configuration configuration = (Configuration) iterator.next();
340                    parentClassLoaders[iterator.previousIndex()] = configuration.getConfigurationClassLoader();
341                }
342            }
343    
344            // hidden classes
345            Set<String> hiddenClassesSet = environment.getHiddenClasses();
346            String[] hiddenClasses = hiddenClassesSet.toArray(new String[hiddenClassesSet.size()]);
347    
348            // we need to propagate the non-overrideable classes from parents
349            LinkedHashSet<String> nonOverridableSet = new LinkedHashSet<String>(environment.getNonOverrideableClasses());
350            for (Configuration parent : classParents) {
351    
352                Environment parentEnvironment = parent.getEnvironment();
353                nonOverridableSet.addAll(parentEnvironment.getNonOverrideableClasses());
354            }
355            String[] nonOverridableClasses = nonOverridableSet.toArray(new String[nonOverridableSet.size()]);
356    
357            if (log.isDebugEnabled()) {
358                StringBuffer buf = new StringBuffer("ClassLoader structure for configuration ").append(id).append("\n");
359                buf.append("Parent configurations:\n");
360                for (Configuration configuration : classParents) {
361                    buf.append("     ").append(configuration.getId()).append("\n");
362                }
363                buf.append("ClassPath:\n");
364                for (URL url : urls) {
365                    buf.append("     ").append(url).append("\n");
366                }
367                log.debug(buf.toString());
368            }
369    
370            // The JarFileClassLoader was created to address a locking problem seen only on Windows platforms.
371            // It carries with it a slight performance penalty that needs to be addressed.  Rather than make
372            // *nix OSes carry this burden we'll engage the JarFileClassLoader for Windows or if the user 
373            // specifically requests it.  We'll look more at this issue in the future.
374            boolean useJarFileClassLoader = false;
375            if (System.getProperty("Xorg.apache.geronimo.JarFileClassLoader") == null) {
376                useJarFileClassLoader = System.getProperty("os.name").startsWith("Windows");
377            } else {
378                useJarFileClassLoader = Boolean.getBoolean("Xorg.apache.geronimo.JarFileClassLoader");
379            }
380            if (useJarFileClassLoader) {
381                return new JarFileClassLoader(environment.getConfigId(),
382                        urls,
383                        parentClassLoaders,
384                        environment.isInverseClassLoading(),
385                        hiddenClasses,
386                        nonOverridableClasses);
387            } else {
388                return new MultiParentClassLoader(environment.getConfigId(),
389                        urls,
390                        parentClassLoaders,
391                        environment.isInverseClassLoading(),
392                        hiddenClasses,
393                        nonOverridableClasses);
394            }
395        }
396    
397        private void addDepthFirstServiceParents(Configuration configuration, List<Configuration> ancestors, Set<Artifact> ids) {
398            if (!ids.contains(configuration.getId())) {
399                ancestors.add(configuration);
400                ids.add(configuration.getId());
401                for (Configuration parent : configuration.getServiceParents()) {
402                    addDepthFirstServiceParents(parent, ancestors, ids);
403                }
404            }
405        }
406    
407        private URL[] buildClassPath(LinkedHashSet<String> classPath) throws MalformedURLException, MissingDependencyException, NoSuchConfigException {
408            List<URL> urls = new ArrayList<URL>();
409            for (Artifact artifact : dependencies) {
410                File file = configurationResolver.resolve(artifact);
411                urls.add(file.toURL());
412            }
413            if (classPath != null) {
414                for (String pattern : classPath) {
415                    Set<URL> matches = configurationResolver.resolve(pattern);
416                    for (URL url : matches) {
417                        urls.add(url);
418                    }
419                }
420            }
421            return urls.toArray(new URL[urls.size()]);
422        }
423    
424        /**
425         * Return the unique Id
426         * @return the unique Id
427         */
428        public Artifact getId() {
429            return id;
430        }
431    
432        /**
433         * Gets the unique name of this configuration within the kernel.
434         * @return the unique name of this configuration
435         */
436        public String getObjectName() {
437            try {
438                return getConfigurationObjectName(id).getCanonicalName();
439            } catch (InvalidConfigException e) {
440                throw new AssertionError(e);
441            }
442        }
443    
444        public AbstractName getAbstractName() {
445            return abstractName;
446        }
447    
448        /**
449         * Gets the parent configurations used for class loading.
450         * @return the parents of this configuration used for class loading
451         */
452        public List<Configuration> getClassParents() {
453            return classParents;
454        }
455    
456        /**
457         * Gets the parent configurations used for service resolution.
458         * @return the parents of this configuration used for service resolution
459         */
460        public List<Configuration> getServiceParents() {
461            return serviceParents;
462        }
463    
464        /**
465         * Gets the artifact dependencies of this configuration.
466         * @return the artifact dependencies of this configuration
467         */
468        public LinkedHashSet<Artifact> getDependencies() {
469            return dependencies;
470        }
471    
472        /**
473         * Gets the declaration of the environment in which this configuration runs.
474         * @return the environment of this configuration
475         */
476        public Environment getEnvironment() {
477            return environment;
478        }
479    
480        /**
481         * This is used by the configuration manager to restart an existing configuation.
482         * Do not modify the configuration data.
483         * @return the configuration data for this configuration; do not modify
484         */
485        ConfigurationData getConfigurationData() {
486            return configurationData;
487        }
488    
489        public File getConfigurationDir() {
490            return configurationData.getConfigurationDir();
491        }
492    
493        /**
494         * @deprecated this is only exposed temporarily for configuration manager
495         */
496        public ConfigurationResolver getConfigurationResolver() {
497            return configurationResolver;
498        }
499    
500        /**
501         * Gets the relative class path (URIs) of this configuration.
502         * @return the relative class path of this configuation
503         */
504        public List<String> getClassPath() {
505            return new ArrayList<String>(classPath);
506        }
507    
508        public void addToClassPath(String pattern) throws IOException {
509            if (!classPath.contains(pattern)) {
510                try {
511                    Set<URL> matches = configurationResolver.resolve(pattern);
512                    for (URL url : matches) {
513                        configurationClassLoader.addURL(url);
514                    }
515                    classPath.add(pattern);
516                } catch (Exception e) {
517                    throw (IOException)new IOException("Unable to extend classpath with " + pattern).initCause(e);
518                }
519            }
520        }
521    
522        /**
523         * Gets the type of the configuration (WAR, RAR et cetera)
524         * @return Type of the configuration.
525         */
526        public ConfigurationModuleType getModuleType() {
527            return configurationData.getModuleType();
528        }
529    
530        /**
531         * Gets the time at which this configuration was created (or deployed).
532         * @return the time at which this configuration was created (or deployed)
533         */
534        public long getCreated() {
535            return configurationData.getCreated();
536        }
537    
538        /**
539         * Gets the class loader for this configuration.
540         * @return the class loader for this configuration
541         */
542        public ClassLoader getConfigurationClassLoader() {
543            return configurationClassLoader;
544        }
545    
546        /**
547         * Gets the nested configurations of this configuration.  That is, the
548         * configurations within this one as a WAR can be within an EAR; not
549         * including wholly separate configurations that just depend on this
550         * one as a parent.
551         * 
552         * @return the nested configuration of this configuration
553         */
554        public List<Configuration> getChildren() {
555            return Collections.unmodifiableList(children);
556        }
557    
558        /**
559         * Gets the configurations owned by this configuration.  This is only used for cascade-uninstall.
560         * @return the configurations owned by this configuration
561         */
562        public Set<Artifact> getOwnedConfigurations() {
563            return configurationData.getOwnedConfigurations();
564        }
565    
566        /**
567         * Gets an unmodifiable collection of the GBeanDatas for the GBeans in this configuration.
568         * @return the GBeans in this configuration
569         */
570        public Map<AbstractName, GBeanData> getGBeans() {
571            return Collections.unmodifiableMap(gbeans);
572        }
573    
574        /**
575         * Determines of this configuration constains the specified GBean.
576         * @param gbean the name of the GBean
577         * @return true if this configuration contains the specified GBean; false otherwise
578         */
579        public synchronized boolean containsGBean(AbstractName gbean) {
580            return gbeans.containsKey(gbean);
581        }
582    
583        /**
584         * Gets the enclosing configuration of this one (e.g. the EAR for a WAR),
585         * or null if it has none.
586         * @return enclosing configuration, if any
587         */
588        public Configuration getEnclosingConfiguration() {
589            return parent;
590        }
591    
592        /**
593         * Gets the manageable attribute store for this configuration.
594         * This is used in the configuration manager to apply overrides
595         * @return
596         */
597        ManageableAttributeStore getManageableAttributeStore(){
598            return attributeStore;
599        }
600    
601        public synchronized AbstractName addGBean(String name, GBeanData gbean) throws GBeanAlreadyExistsException {
602            AbstractName abstractName = gbean.getAbstractName();
603            if (abstractName != null) {
604                throw new IllegalArgumentException("gbean already has an abstract name: " + abstractName);
605            }
606    
607            String j2eeType = gbean.getGBeanInfo().getJ2eeType();
608            if (j2eeType == null) j2eeType = "GBean";
609            abstractName = naming.createRootName(id, name, j2eeType);
610            gbean.setAbstractName(abstractName);
611    
612            if (gbeans.containsKey(abstractName)) {
613                throw new GBeanAlreadyExistsException(gbean.getAbstractName().toString());
614            }
615            gbeans.put(abstractName, gbean);
616            return abstractName;
617        }
618    
619        public synchronized void addGBean(GBeanData gbean) throws GBeanAlreadyExistsException {
620            if (gbeans.containsKey(gbean.getAbstractName())) {
621                throw new GBeanAlreadyExistsException(gbean.getAbstractName().toString());
622            }
623            gbeans.put(gbean.getAbstractName(), gbean);
624        }
625    
626        public synchronized void removeGBean(AbstractName name) throws GBeanNotFoundException {
627            if (!gbeans.containsKey(name)) {
628                throw new GBeanNotFoundException(name);
629            }
630            gbeans.remove(name);
631        }
632    
633        public AbstractName findGBean(AbstractNameQuery pattern) throws GBeanNotFoundException {
634            if (pattern == null) throw new NullPointerException("pattern is null");
635            return findGBean(Collections.singleton(pattern));
636        }
637    
638        public GBeanData findGBeanData(AbstractNameQuery pattern) throws GBeanNotFoundException {
639            if (pattern == null) throw new NullPointerException("pattern is null");
640            return findGBeanData(Collections.singleton(pattern));
641        }
642    
643        public AbstractName findGBean(ReferencePatterns referencePatterns) throws GBeanNotFoundException {
644            if (referencePatterns == null) throw new NullPointerException("referencePatterns is null");
645            if (referencePatterns.isResolved()) {
646                return referencePatterns.getAbstractName();
647            }
648    
649            // check the local config
650            Set<AbstractNameQuery> patterns = referencePatterns.getPatterns();
651            return findGBean(patterns);
652        }
653    
654        public AbstractName findGBean(Set<AbstractNameQuery> patterns) throws GBeanNotFoundException {
655            if (patterns == null) throw new NullPointerException("patterns is null");
656            return findGBeanData(patterns).getAbstractName();
657        }
658    
659        public GBeanData findGBeanData(Set<AbstractNameQuery> patterns) throws GBeanNotFoundException {
660            if (patterns == null) throw new NullPointerException("patterns is null");
661            Set<GBeanData> result = findGBeanDatas(this, patterns);
662            if (result.size() > 1) {
663                throw new GBeanNotFoundException("More than one match to referencePatterns in local configuration", patterns, mapToNames(result));
664            } else if (result.size() == 1) {
665                return (GBeanData) result.iterator().next();
666            }
667    
668            // search all parents
669            for (Configuration configuration : allServiceParents) {
670                result.addAll(findGBeanDatas(configuration, patterns));
671    
672            }
673            // if we already found a match we have an ambiguous query
674            if (result.size() > 1) {
675                List<AbstractName> names = new ArrayList<AbstractName>(result.size());
676                for (GBeanData gBeanData : result) {
677                    names.add(gBeanData.getAbstractName());
678                }
679                throw new GBeanNotFoundException("More than one match to referencePatterns in parent configurations: " + names.toString(), patterns, mapToNames(result));
680            }
681    
682            if (result.isEmpty()) {
683                throw new GBeanNotFoundException("No matches for referencePatterns", patterns, null);
684            }
685    
686            return result.iterator().next();
687        }
688    
689        private Set<AbstractName> mapToNames(Set<GBeanData> datas) {
690            Set<AbstractName> names = new HashSet<AbstractName>(datas.size());
691            for (GBeanData gBeanData: datas) {
692                names.add(gBeanData.getAbstractName());
693            }
694            return names;
695        }
696    
697        public LinkedHashSet<AbstractName> findGBeans(AbstractNameQuery pattern) {
698            if (pattern == null) throw new NullPointerException("pattern is null");
699            return findGBeans(Collections.singleton(pattern));
700        }
701    
702        public LinkedHashSet<AbstractName> findGBeans(ReferencePatterns referencePatterns) {
703            if (referencePatterns == null) throw new NullPointerException("referencePatterns is null");
704            if (referencePatterns.getAbstractName() != null) {
705                // this pattern is already resolved
706                LinkedHashSet<AbstractName> result = new LinkedHashSet<AbstractName>();
707                result.add(referencePatterns.getAbstractName());
708                return result;
709            }
710    
711            // check the local config
712            Set<AbstractNameQuery> patterns = referencePatterns.getPatterns();
713            return findGBeans(patterns);
714        }
715    
716        public LinkedHashSet<AbstractName> findGBeans(Set<AbstractNameQuery> patterns) {
717            if (patterns == null) throw new NullPointerException("patterns is null");
718            LinkedHashSet<GBeanData> datas = findGBeanDatas(patterns);
719            LinkedHashSet<AbstractName> result = new LinkedHashSet<AbstractName>(datas.size());
720            for (GBeanData gBeanData : datas) {
721                result.add(gBeanData.getAbstractName());
722            }
723    
724            return result;
725        }
726    
727        public LinkedHashSet<GBeanData> findGBeanDatas(Set<AbstractNameQuery> patterns) {
728            if (patterns == null) throw new NullPointerException("patterns is null");
729            LinkedHashSet<GBeanData> datas = findGBeanDatas(this, patterns);
730    
731            // search all parents
732            for (Configuration configuration : allServiceParents) {
733                Set<GBeanData> match = findGBeanDatas(configuration, patterns);
734                datas.addAll(match);
735            }
736            return datas;
737        }
738    
739        /**
740         * Find the gbeanDatas matching the patterns in this configuration only, ignoring parents.
741         *
742         * @param configuration configuration to look in
743         * @param patterns patterns to look for
744         * @return set of gbeandatas matching one of the patterns from this configuration only, not including parents.
745         */
746        public LinkedHashSet<GBeanData> findGBeanDatas(Configuration configuration, Set<AbstractNameQuery> patterns) {
747            LinkedHashSet<GBeanData> result = new LinkedHashSet<GBeanData>();
748    
749            Set<Map.Entry<AbstractName, GBeanData>> gbeanNames = configuration.getGBeans().entrySet();
750            for (AbstractNameQuery abstractNameQuery : patterns) {
751                Artifact queryArtifact = abstractNameQuery.getArtifact();
752    
753                // Does this query apply to this configuration
754                if (queryArtifact == null || queryArtifact.matches(configuration.getId())) {
755    
756                    // Search the GBeans
757                    for (Map.Entry<AbstractName, GBeanData> entry : gbeanNames) {
758                        AbstractName abstractName = entry.getKey();
759                        GBeanData gbeanData = entry.getValue();
760                        if (abstractNameQuery.matches(abstractName, gbeanData.getGBeanInfo().getInterfaces())) {
761                            result.add(gbeanData);
762                        }
763                    }
764                }
765            }
766            return result;
767        }
768    
769        public void doStart() throws Exception {
770            log.debug("Started configuration " + id);
771        }
772    
773        public synchronized void doStop() throws Exception {
774            log.debug("Stopping configuration " + id);
775            shutdown();
776    
777        }
778    
779        public void doFail() {
780            log.debug("Failed configuration " + id);
781            shutdown();
782        }
783    
784        private void shutdown() {
785            for (Configuration configuration : children) {
786                configuration.shutdown();
787            }
788    
789            // clear references to GBeanDatas
790            gbeans.clear();
791    
792            // destroy the class loader
793            if (configurationClassLoader != null) {
794                configurationClassLoader.destroy();
795            }
796        }
797    
798        public static final GBeanInfo GBEAN_INFO;
799    
800        static {
801            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(Configuration.class);//does not use jsr-77 naming
802            infoFactory.addReference("Parents", Configuration.class);
803            infoFactory.addAttribute("configurationData", ConfigurationData.class, true, false);
804            infoFactory.addAttribute("configurationResolver", ConfigurationResolver.class, true);
805            infoFactory.addAttribute("managedAttributeStore", ManageableAttributeStore.class, true);
806    
807            infoFactory.addInterface(Configuration.class);
808    
809            infoFactory.setConstructor(new String[]{
810                    "Parents",
811                    "configurationData",
812                    "configurationResolver",
813                    "managedAttributeStore"
814            });
815    
816            GBEAN_INFO = infoFactory.getBeanInfo();
817        }
818    
819        public static GBeanInfo getGBeanInfo() {
820            return GBEAN_INFO;
821        }
822    }