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: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
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         * Only used to allow declaration as a reference.
198         */
199        public Configuration() {
200            id = null;
201            abstractName = null;
202            environment = null;
203            classPath = null;
204            configurationResolver = null;
205            configurationClassLoader = null;
206            naming = null;
207        }
208    
209        /**
210         * Creates a configuration.
211         * @param parents parents of this configuation (not ordered)
212         * @param configurationData the module type, environment and classpath of the configuration
213         * @param configurationResolver used to resolve dependecies and paths
214         */
215        public Configuration(Collection<Configuration> parents,
216                ConfigurationData configurationData,
217                ConfigurationResolver configurationResolver,
218                ManageableAttributeStore attributeStore) throws MissingDependencyException, MalformedURLException, NoSuchConfigException, InvalidConfigException {
219            if (parents == null) parents = Collections.EMPTY_SET;
220            if (configurationData == null) throw new NullPointerException("configurationData is null");
221            if (configurationResolver == null) throw new NullPointerException("configurationResolver is null");
222    
223            this.configurationData = configurationData;
224            this.environment = configurationData.getEnvironment();
225            this.configurationResolver = configurationResolver;
226            this.classPath = new LinkedHashSet<String>(configurationData.getClassPath());
227            this.naming = configurationData.getNaming();
228    
229            this.id = environment.getConfigId();
230            abstractName = getConfigurationAbstractName(id);
231    
232            //
233            // Transitively resolve all the dependencies in the environment
234            //
235            List<Dependency> transitiveDependencies = configurationResolver.resolveTransitiveDependencies(parents, environment.getDependencies());
236    
237            //
238            // Process transtive dependencies splitting it into classParents, serviceParents and artifactDependencies
239            //
240            Map<Artifact, Configuration> parentsById = new HashMap<Artifact, Configuration>();
241            for (Configuration configuration : parents) {
242                Artifact id = configuration.getId();
243                parentsById.put(id, configuration);
244            }
245    
246            for (Dependency dependency : transitiveDependencies) {
247                Artifact artifact = dependency.getArtifact();
248                if (parentsById.containsKey(artifact)) {
249                    Configuration parent = parentsById.get(artifact);
250                    if (dependency.getImportType() == ImportType.CLASSES || dependency.getImportType() == ImportType.ALL) {
251                        classParents.add(parent);
252                    }
253                    if (dependency.getImportType() == ImportType.SERVICES || dependency.getImportType() == ImportType.ALL) {
254                        serviceParents.add(parent);
255                    }
256                } else if (dependency.getImportType() == ImportType.SERVICES) {
257                    throw new IllegalStateException("Could not find parent " + artifact + " in the parents collection");
258                } else {
259                    dependencies.add(artifact);
260                }
261            }
262    
263            try {
264                //
265                // Build the configuration class loader
266                //
267                configurationClassLoader = createConfigurationClasssLoader(parents, environment, classPath);
268    
269                //
270                // Get all service parents in depth first order
271                //
272    
273                addDepthFirstServiceParents(this, allServiceParents, new HashSet<Artifact>());
274    
275                //
276                // Deserialize the GBeans in the configurationData
277                //
278                Collection<GBeanData> gbeans = configurationData.getGBeans(configurationClassLoader);
279                if (attributeStore != null) {
280                    gbeans = attributeStore.applyOverrides(id, gbeans, configurationClassLoader);
281                }
282                for (GBeanData gbeanData : gbeans) {
283                    this.gbeans.put(gbeanData.getAbstractName(), gbeanData);
284                }
285    
286                //
287                // Create child configurations
288                //
289                LinkedHashSet<Configuration> childParents = new LinkedHashSet<Configuration>(parents);
290                childParents.add(this);
291                for (Iterator iterator = configurationData.getChildConfigurations().entrySet().iterator(); iterator.hasNext();) {
292                    Map.Entry entry = (Map.Entry) iterator.next();
293                    String moduleName = (String) entry.getKey();
294                    ConfigurationData childConfigurationData = (ConfigurationData) entry.getValue();
295                    Configuration childConfiguration = new Configuration(childParents, childConfigurationData, configurationResolver.createChildResolver(moduleName), attributeStore);
296                    childConfiguration.parent = this;
297                    children.add(childConfiguration);
298                }
299            } catch (RuntimeException e) {
300                shutdown();
301                throw e;
302            } catch (Error e) {
303                shutdown();
304                throw e;
305            } catch (MissingDependencyException e) {
306                shutdown();
307                throw e;
308            } catch (MalformedURLException e) {
309                shutdown();
310                throw e;
311            } catch (NoSuchConfigException e) {
312                shutdown();
313                throw e;
314            } catch (InvalidConfigException e) {
315                shutdown();
316                throw e;
317            }
318        }
319    
320        private MultiParentClassLoader createConfigurationClasssLoader(Collection<Configuration> parents, Environment environment, LinkedHashSet<String> classPath) throws MalformedURLException, MissingDependencyException, NoSuchConfigException {
321            // create the URL list
322            URL[] urls = buildClassPath(classPath);
323    
324            // parents
325            ClassLoader[] parentClassLoaders;
326            if (parents.size() == 0 && classParents.size() == 0) {
327                // no explicit parent set, so use the class loader of this class as
328                // the parent... this class should be in the root geronimo classloader,
329                // which is normally the system class loader but not always, so be safe
330                parentClassLoaders = new ClassLoader[] {getClass().getClassLoader()};
331            } else {
332                parentClassLoaders = new ClassLoader[classParents.size()];
333                for (ListIterator iterator = classParents.listIterator(); iterator.hasNext();) {
334                    Configuration configuration = (Configuration) iterator.next();
335                    parentClassLoaders[iterator.previousIndex()] = configuration.getConfigurationClassLoader();
336                }
337            }
338    
339            // hidden classes
340            Set<String> hiddenClassesSet = environment.getHiddenClasses();
341            String[] hiddenClasses = hiddenClassesSet.toArray(new String[hiddenClassesSet.size()]);
342    
343            // we need to propagate the non-overrideable classes from parents
344            LinkedHashSet<String> nonOverridableSet = new LinkedHashSet<String>(environment.getNonOverrideableClasses());
345            for (Configuration parent : classParents) {
346    
347                Environment parentEnvironment = parent.getEnvironment();
348                nonOverridableSet.addAll(parentEnvironment.getNonOverrideableClasses());
349            }
350            String[] nonOverridableClasses = nonOverridableSet.toArray(new String[nonOverridableSet.size()]);
351    
352            if (log.isDebugEnabled()) {
353                StringBuffer buf = new StringBuffer("ClassLoader structure for configuration ").append(id).append("\n");
354                buf.append("Parent configurations:\n");
355                for (Configuration configuration : classParents) {
356                    buf.append("     ").append(configuration.getId()).append("\n");
357                }
358                buf.append("ClassPath:\n");
359                for (URL url : urls) {
360                    buf.append("     ").append(url).append("\n");
361                }
362                log.debug(buf.toString());
363            }
364    
365            // The JarFileClassLoader was created to address a locking problem seen only on Windows platforms.
366            // It carries with it a slight performance penalty that needs to be addressed.  Rather than make
367            // *nix OSes carry this burden we'll engage the JarFileClassLoader for Windows or if the user 
368            // specifically requests it.  We'll look more at this issue in the future.
369            boolean useJarFileClassLoader = false;
370            if (System.getProperty("Xorg.apache.geronimo.JarFileClassLoader") == null) {
371                useJarFileClassLoader = System.getProperty("os.name").startsWith("Windows");
372            } else {
373                useJarFileClassLoader = Boolean.getBoolean("Xorg.apache.geronimo.JarFileClassLoader");
374            }
375            if (useJarFileClassLoader) {
376                return new JarFileClassLoader(environment.getConfigId(),
377                        urls,
378                        parentClassLoaders,
379                        environment.isInverseClassLoading(),
380                        hiddenClasses,
381                        nonOverridableClasses);
382            } else {
383                return new MultiParentClassLoader(environment.getConfigId(),
384                        urls,
385                        parentClassLoaders,
386                        environment.isInverseClassLoading(),
387                        hiddenClasses,
388                        nonOverridableClasses);
389            }
390        }
391    
392        private void addDepthFirstServiceParents(Configuration configuration, List<Configuration> ancestors, Set<Artifact> ids) {
393            if (!ids.contains(configuration.getId())) {
394                ancestors.add(configuration);
395                ids.add(configuration.getId());
396                for (Configuration parent : configuration.getServiceParents()) {
397                    addDepthFirstServiceParents(parent, ancestors, ids);
398                }
399            }
400        }
401    
402        private URL[] buildClassPath(LinkedHashSet<String> classPath) throws MalformedURLException, MissingDependencyException, NoSuchConfigException {
403            List<URL> urls = new ArrayList<URL>();
404            for (Artifact artifact : dependencies) {
405                File file = configurationResolver.resolve(artifact);
406                urls.add(file.toURL());
407            }
408            if (classPath != null) {
409                for (String pattern : classPath) {
410                    Set<URL> matches = configurationResolver.resolve(pattern);
411                    for (URL url : matches) {
412                        urls.add(url);
413                    }
414                }
415            }
416            return urls.toArray(new URL[urls.size()]);
417        }
418    
419        /**
420         * Return the unique Id
421         * @return the unique Id
422         */
423        public Artifact getId() {
424            return id;
425        }
426    
427        /**
428         * Gets the unique name of this configuration within the kernel.
429         * @return the unique name of this configuration
430         */
431        public String getObjectName() {
432            try {
433                return getConfigurationObjectName(id).getCanonicalName();
434            } catch (InvalidConfigException e) {
435                throw new AssertionError(e);
436            }
437        }
438    
439        public AbstractName getAbstractName() {
440            return abstractName;
441        }
442    
443        /**
444         * Gets the parent configurations used for class loading.
445         * @return the parents of this configuration used for class loading
446         */
447        public List<Configuration> getClassParents() {
448            return classParents;
449        }
450    
451        /**
452         * Gets the parent configurations used for service resolution.
453         * @return the parents of this configuration used for service resolution
454         */
455        public List<Configuration> getServiceParents() {
456            return serviceParents;
457        }
458    
459        /**
460         * Gets the artifact dependencies of this configuration.
461         * @return the artifact dependencies of this configuration
462         */
463        public LinkedHashSet<Artifact> getDependencies() {
464            return dependencies;
465        }
466    
467        /**
468         * Gets the declaration of the environment in which this configuration runs.
469         * @return the environment of this configuration
470         */
471        public Environment getEnvironment() {
472            return environment;
473        }
474    
475        /**
476         * This is used by the configuration manager to restart an existing configuation.
477         * Do not modify the configuration data.
478         * @return the configuration data for this configuration; do not modify
479         */
480        ConfigurationData getConfigurationData() {
481            return configurationData;
482        }
483    
484        public File getConfigurationDir() {
485            return configurationData.getConfigurationDir();
486        }
487    
488        /**
489         * @deprecated this is only exposed temporarily for configuration manager
490         */
491        public ConfigurationResolver getConfigurationResolver() {
492            return configurationResolver;
493        }
494    
495        /**
496         * Gets the relative class path (URIs) of this configuration.
497         * @return the relative class path of this configuation
498         */
499        public List<String> getClassPath() {
500            return new ArrayList<String>(classPath);
501        }
502    
503        public void addToClassPath(String pattern) throws IOException {
504            if (!classPath.contains(pattern)) {
505                try {
506                    Set<URL> matches = configurationResolver.resolve(pattern);
507                    for (URL url : matches) {
508                        configurationClassLoader.addURL(url);
509                    }
510                    classPath.add(pattern);
511                } catch (Exception e) {
512                    throw (IOException)new IOException("Unable to extend classpath with " + pattern).initCause(e);
513                }
514            }
515        }
516    
517        /**
518         * Gets the type of the configuration (WAR, RAR et cetera)
519         * @return Type of the configuration.
520         */
521        public ConfigurationModuleType getModuleType() {
522            return configurationData.getModuleType();
523        }
524    
525        /**
526         * Gets the time at which this configuration was created (or deployed).
527         * @return the time at which this configuration was created (or deployed)
528         */
529        public long getCreated() {
530            return configurationData.getCreated();
531        }
532    
533        /**
534         * Gets the class loader for this configuration.
535         * @return the class loader for this configuration
536         */
537        public ClassLoader getConfigurationClassLoader() {
538            return configurationClassLoader;
539        }
540    
541        /**
542         * Gets the nested configurations of this configuration.  That is, the
543         * configurations within this one as a WAR can be within an EAR; not
544         * including wholly separate configurations that just depend on this
545         * one as a parent.
546         * 
547         * @return the nested configuration of this configuration
548         */
549        public List<Configuration> getChildren() {
550            return Collections.unmodifiableList(children);
551        }
552    
553        /**
554         * Gets the configurations owned by this configuration.  This is only used for cascade-uninstall.
555         * @return the configurations owned by this configuration
556         */
557        public Set<Artifact> getOwnedConfigurations() {
558            return configurationData.getOwnedConfigurations();
559        }
560    
561        /**
562         * Gets an unmodifiable collection of the GBeanDatas for the GBeans in this configuration.
563         * @return the GBeans in this configuration
564         */
565        public Map<AbstractName, GBeanData> getGBeans() {
566            return Collections.unmodifiableMap(gbeans);
567        }
568    
569        /**
570         * Determines of this configuration constains the specified GBean.
571         * @param gbean the name of the GBean
572         * @return true if this configuration contains the specified GBean; false otherwise
573         */
574        public synchronized boolean containsGBean(AbstractName gbean) {
575            return gbeans.containsKey(gbean);
576        }
577    
578        /**
579         * Gets the enclosing configuration of this one (e.g. the EAR for a WAR),
580         * or null if it has none.
581         * @return enclosing configuration, if any
582         */
583        public Configuration getEnclosingConfiguration() {
584            return parent;
585        }
586    
587        public synchronized AbstractName addGBean(String name, GBeanData gbean) throws GBeanAlreadyExistsException {
588            AbstractName abstractName = gbean.getAbstractName();
589            if (abstractName != null) {
590                throw new IllegalArgumentException("gbean already has an abstract name: " + abstractName);
591            }
592    
593            String j2eeType = gbean.getGBeanInfo().getJ2eeType();
594            if (j2eeType == null) j2eeType = "GBean";
595            abstractName = naming.createRootName(id, name, j2eeType);
596            gbean.setAbstractName(abstractName);
597    
598            if (gbeans.containsKey(abstractName)) {
599                throw new GBeanAlreadyExistsException(gbean.getAbstractName().toString());
600            }
601            gbeans.put(abstractName, gbean);
602            return abstractName;
603        }
604    
605        public synchronized void addGBean(GBeanData gbean) throws GBeanAlreadyExistsException {
606            if (gbeans.containsKey(gbean.getAbstractName())) {
607                throw new GBeanAlreadyExistsException(gbean.getAbstractName().toString());
608            }
609            gbeans.put(gbean.getAbstractName(), gbean);
610        }
611    
612        public synchronized void removeGBean(AbstractName name) throws GBeanNotFoundException {
613            if (!gbeans.containsKey(name)) {
614                throw new GBeanNotFoundException(name);
615            }
616            gbeans.remove(name);
617        }
618    
619        public AbstractName findGBean(AbstractNameQuery pattern) throws GBeanNotFoundException {
620            if (pattern == null) throw new NullPointerException("pattern is null");
621            return findGBean(Collections.singleton(pattern));
622        }
623    
624        public GBeanData findGBeanData(AbstractNameQuery pattern) throws GBeanNotFoundException {
625            if (pattern == null) throw new NullPointerException("pattern is null");
626            return findGBeanData(Collections.singleton(pattern));
627        }
628    
629        public AbstractName findGBean(ReferencePatterns referencePatterns) throws GBeanNotFoundException {
630            if (referencePatterns == null) throw new NullPointerException("referencePatterns is null");
631            if (referencePatterns.isResolved()) {
632                return referencePatterns.getAbstractName();
633            }
634    
635            // check the local config
636            Set<AbstractNameQuery> patterns = referencePatterns.getPatterns();
637            return findGBean(patterns);
638        }
639    
640        public AbstractName findGBean(Set<AbstractNameQuery> patterns) throws GBeanNotFoundException {
641            if (patterns == null) throw new NullPointerException("patterns is null");
642            return findGBeanData(patterns).getAbstractName();
643        }
644    
645        public GBeanData findGBeanData(Set<AbstractNameQuery> patterns) throws GBeanNotFoundException {
646            if (patterns == null) throw new NullPointerException("patterns is null");
647            Set<GBeanData> result = findGBeanDatas(this, patterns);
648            if (result.size() > 1) {
649                throw new GBeanNotFoundException("More than one match to referencePatterns in local configuration", patterns, mapToNames(result));
650            } else if (result.size() == 1) {
651                return (GBeanData) result.iterator().next();
652            }
653    
654            // search all parents
655            for (Configuration configuration : allServiceParents) {
656                result.addAll(findGBeanDatas(configuration, patterns));
657    
658            }
659            // if we already found a match we have an ambiguous query
660            if (result.size() > 1) {
661                List<AbstractName> names = new ArrayList<AbstractName>(result.size());
662                for (GBeanData gBeanData : result) {
663                    names.add(gBeanData.getAbstractName());
664                }
665                throw new GBeanNotFoundException("More than one match to referencePatterns in parent configurations: " + names.toString(), patterns, mapToNames(result));
666            }
667    
668            if (result.isEmpty()) {
669                throw new GBeanNotFoundException("No matches for referencePatterns", patterns, null);
670            }
671    
672            return result.iterator().next();
673        }
674    
675        private Set<AbstractName> mapToNames(Set<GBeanData> datas) {
676            Set<AbstractName> names = new HashSet<AbstractName>(datas.size());
677            for (GBeanData gBeanData: datas) {
678                names.add(gBeanData.getAbstractName());
679            }
680            return names;
681        }
682    
683        public LinkedHashSet<AbstractName> findGBeans(AbstractNameQuery pattern) {
684            if (pattern == null) throw new NullPointerException("pattern is null");
685            return findGBeans(Collections.singleton(pattern));
686        }
687    
688        public LinkedHashSet<AbstractName> findGBeans(ReferencePatterns referencePatterns) {
689            if (referencePatterns == null) throw new NullPointerException("referencePatterns is null");
690            if (referencePatterns.getAbstractName() != null) {
691                // this pattern is already resolved
692                LinkedHashSet<AbstractName> result = new LinkedHashSet<AbstractName>();
693                result.add(referencePatterns.getAbstractName());
694                return result;
695            }
696    
697            // check the local config
698            Set<AbstractNameQuery> patterns = referencePatterns.getPatterns();
699            return findGBeans(patterns);
700        }
701    
702        public LinkedHashSet<AbstractName> findGBeans(Set<AbstractNameQuery> patterns) {
703            if (patterns == null) throw new NullPointerException("patterns is null");
704            LinkedHashSet<GBeanData> datas = findGBeanDatas(patterns);
705            LinkedHashSet<AbstractName> result = new LinkedHashSet<AbstractName>(datas.size());
706            for (GBeanData gBeanData : datas) {
707                result.add(gBeanData.getAbstractName());
708            }
709    
710            return result;
711        }
712    
713        public LinkedHashSet<GBeanData> findGBeanDatas(Set<AbstractNameQuery> patterns) {
714            if (patterns == null) throw new NullPointerException("patterns is null");
715            LinkedHashSet<GBeanData> datas = findGBeanDatas(this, patterns);
716    
717            // search all parents
718            for (Configuration configuration : allServiceParents) {
719                Set<GBeanData> match = findGBeanDatas(configuration, patterns);
720                datas.addAll(match);
721            }
722            return datas;
723        }
724    
725        /**
726         * Find the gbeanDatas matching the patterns in this configuration only, ignoring parents.
727         *
728         * @param configuration configuration to look in
729         * @param patterns patterns to look for
730         * @return set of gbeandatas matching one of the patterns from this configuration only, not including parents.
731         */
732        public LinkedHashSet<GBeanData> findGBeanDatas(Configuration configuration, Set<AbstractNameQuery> patterns) {
733            LinkedHashSet<GBeanData> result = new LinkedHashSet<GBeanData>();
734    
735            Set<Map.Entry<AbstractName, GBeanData>> gbeanNames = configuration.getGBeans().entrySet();
736            for (AbstractNameQuery abstractNameQuery : patterns) {
737                Artifact queryArtifact = abstractNameQuery.getArtifact();
738    
739                // Does this query apply to this configuration
740                if (queryArtifact == null || queryArtifact.matches(configuration.getId())) {
741    
742                    // Search the GBeans
743                    for (Map.Entry<AbstractName, GBeanData> entry : gbeanNames) {
744                        AbstractName abstractName = entry.getKey();
745                        GBeanData gbeanData = entry.getValue();
746                        if (abstractNameQuery.matches(abstractName, gbeanData.getGBeanInfo().getInterfaces())) {
747                            result.add(gbeanData);
748                        }
749                    }
750                }
751            }
752            return result;
753        }
754    
755        public void doStart() throws Exception {
756            log.debug("Started configuration " + id);
757        }
758    
759        public synchronized void doStop() throws Exception {
760            log.debug("Stopping configuration " + id);
761            shutdown();
762    
763        }
764    
765        public void doFail() {
766            log.debug("Failed configuration " + id);
767            shutdown();
768        }
769    
770        private void shutdown() {
771            for (Configuration configuration : children) {
772                configuration.shutdown();
773            }
774    
775            // clear references to GBeanDatas
776            gbeans.clear();
777    
778            // destroy the class loader
779            if (configurationClassLoader != null) {
780                configurationClassLoader.destroy();
781            }
782        }
783    
784        public static final GBeanInfo GBEAN_INFO;
785    
786        static {
787            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(Configuration.class);//does not use jsr-77 naming
788            infoFactory.addReference("Parents", Configuration.class);
789            infoFactory.addAttribute("configurationData", ConfigurationData.class, true, false);
790            infoFactory.addAttribute("configurationResolver", ConfigurationResolver.class, true);
791            infoFactory.addAttribute("managedAttributeStore", ManageableAttributeStore.class, true);
792    
793            infoFactory.addInterface(Configuration.class);
794    
795            infoFactory.setConstructor(new String[]{
796                    "Parents",
797                    "configurationData",
798                    "configurationResolver",
799                    "managedAttributeStore"
800            });
801    
802            GBEAN_INFO = infoFactory.getBeanInfo();
803        }
804    
805        public static GBeanInfo getGBeanInfo() {
806            return GBEAN_INFO;
807        }
808    }