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