<commit_before>/**
 *
 * Copyright 2003-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.geronimo.kernel.config;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.AbstractNameQuery;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.ReferencePatterns;
import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Naming;
import org.apache.geronimo.kernel.classloader.JarFileClassLoader;
import org.apache.geronimo.kernel.repository.Artifact;
import org.apache.geronimo.kernel.repository.Dependency;
import org.apache.geronimo.kernel.repository.Environment;
import org.apache.geronimo.kernel.repository.ImportType;
import org.apache.geronimo.kernel.repository.MissingDependencyException;

/**
 * A Configuration represents a collection of runnable services that can be
 * loaded into a Geronimo Kernel and brought online. The primary components in
 * a Configuration are a codebase, represented by a collection of URLs that
 * is used to locate classes, and a collection of GBean instances that define
 * its state.
 * <p/>
 * The persistent attributes of the Configuration are:
 * <ul>
 * <li>its unique configId used to identify this specific config</li>
 * <li>the configId of a parent Configuration on which this one is dependent</li>
 * <li>a List<URI> of code locations (which may be absolute or relative to a baseURL)</li>
 * <li>a byte[] holding the state of the GBeans instances in Serialized form</li>
 * </ul>
 * When a configuration is started, it converts the URIs into a set of absolute
 * URLs by resolving them against the specified baseURL (this would typically
 * be the root of the CAR file which contains the configuration) and then
 * constructs a ClassLoader for that codebase. That ClassLoader is then used
 * to de-serialize the persisted GBeans, ensuring the GBeans can be recycled
 * as necessary. Once the GBeans have been restored, they are brought online
 * by registering them with the MBeanServer.
 * <p/>
 * A dependency on the Configuration is created for every GBean it loads. As a
 * result, a startRecursive() operation on the configuration will result in
 * a startRecursive() for all the GBeans it contains. Similarly, if the
 * Configuration is stopped then all of its GBeans will be stopped as well.
 *
 * @version $Rev:385718 $ $Date$
 */
public class Configuration implements GBeanLifecycle, ConfigurationParent {
    private static final Log log = LogFactory.getLog(Configuration.class);
    public static final Object JSR77_BASE_NAME_PROPERTY = "org.apache.geronimo.name.javax.management.j2ee.BaseName";

    /**
     * Converts an Artifact to an AbstractName for a configuration.  Does not
     * validate that this is a reasonable or resolved Artifact, or that it
     * corresponds to an actual Configuration.
     */
    public static AbstractName getConfigurationAbstractName(Artifact configId) throws InvalidConfigException {
        return new AbstractName(configId, Collections.singletonMap("configurationName", configId.toString()), getConfigurationObjectName(configId));
    }

    public static boolean isConfigurationObjectName(ObjectName name) {
        return name.getDomain().equals("geronimo.config") && name.getKeyPropertyList().size() == 1 && name.getKeyProperty("name") != null;
    }

    public static Artifact getConfigurationID(ObjectName objectName) {
        if (isConfigurationObjectName(objectName)) {
            String name = ObjectName.unquote(objectName.getKeyProperty("name"));
            return Artifact.create(name);
        } else {
            throw new IllegalArgumentException("ObjectName " + objectName + " is not a Configuration name");
        }
    }

    private static ObjectName getConfigurationObjectName(Artifact configId) throws InvalidConfigException {
        try {
            return new ObjectName("geronimo.config:name=" + ObjectName.quote(configId.toString()));
        } catch (MalformedObjectNameException e) {
            throw new InvalidConfigException("Could not construct object name for configuration", e);
        }
    }

    /**
     * The artifact id for this configuration.
     */
    private final Artifact id;

    /**
     * The registered abstractName for this configuraion.
     */
    private final AbstractName abstractName;

    /**
     * Defines the environment requred for this configuration.
     */
    private final Environment environment;

    /**
     * Used to resolve dependecies and paths
     */
    private final ConfigurationResolver configurationResolver;

    /**
     * Parent configurations used for class loader.
     */
    private final List classParents = new ArrayList();

    /**
     * Parent configuations used for service resolution.
     */
    private final List serviceParents = new ArrayList();

    /**
     * All service parents depth first
     */
    private final List allServiceParents = new ArrayList();

    /**
     * Artifacts added to the class loader (non-configuation artifacts).
     */
    private final LinkedHashSet dependencies = new LinkedHashSet();

    /**
     * The GBeanData objects by ObjectName
     */
    private final Map gbeans = new HashMap();

    /**
     * The classloader used to load the child GBeans contained in this configuration.
     */
    private final MultiParentClassLoader configurationClassLoader;

    /**
     * The relative class path (URI) of this configuation.
     */
    private final LinkedHashSet classPath;

    /**
     * Naming system used when generating a name for a new gbean
     */
    private final Naming naming;

    /**
     * Environment, classpath, gbeans and other data for this configuration.
     */
    private ConfigurationData configurationData;

    /**
     * The nested configurations of this configuration.
     */
    List children = new ArrayList();

    /**
     * The parent of this configuration;
     */
    private Configuration parent = null;

    /**
     * Only used to allow declaration as a reference.
     */
    public Configuration() {
        id = null;
        abstractName = null;
        environment = null;
        classPath = null;
        configurationResolver = null;
        configurationClassLoader = null;
        naming = null;
    }

    /**
     * Creates a configuration.
     * @param parents parents of this configuation (not ordered)
     * @param configurationData the module type, environment and classpath of the configuration
     * @param configurationResolver used to resolve dependecies and paths
     */
    public Configuration(Collection parents,
            ConfigurationData configurationData,
            ConfigurationResolver configurationResolver,
            ManageableAttributeStore attributeStore) throws MissingDependencyException, MalformedURLException, NoSuchConfigException, InvalidConfigException {
        if (parents == null) parents = Collections.EMPTY_SET;
        if (configurationData == null) throw new NullPointerException("configurationData is null");
        if (configurationResolver == null) throw new NullPointerException("configurationResolver is null");

        this.configurationData = configurationData;
        this.environment = configurationData.getEnvironment();
        this.configurationResolver = configurationResolver;
        this.classPath = new LinkedHashSet(configurationData.getClassPath());
        this.naming = configurationData.getNaming();

        this.id = environment.getConfigId();
        abstractName = getConfigurationAbstractName(id);

        //
        // Transitively resolve all the dependencies in the environment
        //
        List transtiveDependencies = configurationResolver.resolveTransitiveDependencies(parents, environment.getDependencies());

        //
        // Process transtive dependencies splitting it into classParents, serviceParents and artifactDependencies
        //
        Map parentsById = new HashMap();
        for (Iterator iterator = parents.iterator(); iterator.hasNext();) {
            Configuration configuration = (Configuration) iterator.next();
            Artifact id = configuration.getId();
            parentsById.put(id, configuration);
        }

        for (Iterator iterator = transtiveDependencies.iterator(); iterator.hasNext();) {
            Dependency dependency = (Dependency) iterator.next();
            Artifact artifact = dependency.getArtifact();
            if (parentsById.containsKey(artifact)) {
                Configuration parent = (Configuration) parentsById.get(artifact);
                if (dependency.getImportType() == ImportType.CLASSES || dependency.getImportType() == ImportType.ALL) {
                    classParents.add(parent);
                }
                if (dependency.getImportType() == ImportType.SERVICES || dependency.getImportType() == ImportType.ALL) {
                    serviceParents.add(parent);
                }
            } else if (dependency.getImportType() == ImportType.SERVICES) {
                throw new IllegalStateException("Could not find parent " + artifact + " in the parents collection");
            } else {
                dependencies.add(artifact);
            }
        }

        try {
            //
            // Build the configuration class loader
            //
            configurationClassLoader = createConfigurationClasssLoader(parents, environment, classPath);

            //
            // Get all service parents in depth first order
            //
            addDepthFirstServiceParents(this, allServiceParents);

            //
            // Deserialize the GBeans in the configurationData
            //
            Collection gbeans = configurationData.getGBeans(configurationClassLoader);
            if (attributeStore != null) {
                gbeans = attributeStore.applyOverrides(id, gbeans, configurationClassLoader);
            }
            for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) {
                GBeanData gbeanData = (GBeanData) iterator.next();
                this.gbeans.put(gbeanData.getAbstractName(), gbeanData);
            }

            //
            // Create child configurations
            //
            LinkedHashSet childParents = new LinkedHashSet(parents);
            childParents.add(this);
            for (Iterator iterator = configurationData.getChildConfigurations().entrySet().iterator(); iterator.hasNext();) {
                Map.Entry entry = (Map.Entry) iterator.next();
                String moduleName = (String) entry.getKey();
                ConfigurationData childConfigurationData = (ConfigurationData) entry.getValue();
                Configuration childConfiguration = new Configuration(childParents, childConfigurationData, configurationResolver.createChildResolver(moduleName), attributeStore);
                childConfiguration.parent = this;
                children.add(childConfiguration);
            }
        } catch (RuntimeException e) {
            shutdown();
            throw e;
        } catch (Error e) {
            shutdown();
            throw e;
        } catch (MissingDependencyException e) {
            shutdown();
            throw e;
        } catch (MalformedURLException e) {
            shutdown();
            throw e;
        } catch (NoSuchConfigException e) {
            shutdown();
            throw e;
        } catch (InvalidConfigException e) {
            shutdown();
            throw e;
        }
    }

    private MultiParentClassLoader createConfigurationClasssLoader(Collection parents, Environment environment, LinkedHashSet classPath) throws MalformedURLException, MissingDependencyException, NoSuchConfigException {
        // create the URL list
        URL[] urls = buildClassPath(classPath);

        // parents
        ClassLoader[] parentClassLoaders;
        if (parents.size() == 0 && classParents.size() == 0) {
            // no explicit parent set, so use the class loader of this class as
            // the parent... this class should be in the root geronimo classloader,
            // which is normally the system class loader but not always, so be safe
            parentClassLoaders = new ClassLoader[] {getClass().getClassLoader()};
        } else {
            parentClassLoaders = new ClassLoader[classParents.size()];
            for (ListIterator iterator = classParents.listIterator(); iterator.hasNext();) {
                Configuration configuration = (Configuration) iterator.next();
                parentClassLoaders[iterator.previousIndex()] = configuration.getConfigurationClassLoader();
            }
        }

        // hidden classes
        Set hiddenClassesSet = environment.getHiddenClasses();
        String[] hiddenClasses = (String[]) hiddenClassesSet.toArray(new String[hiddenClassesSet.size()]);

        // we need to propagate the non-overrideable classes from parents
        LinkedHashSet nonOverridableSet = new LinkedHashSet();
        for (Iterator iterator = classParents.iterator(); iterator.hasNext();) {
            Configuration parent = (Configuration) iterator.next();

            Environment parentEnvironment = parent.getEnvironment();
            nonOverridableSet.addAll(parentEnvironment.getNonOverrideableClasses());
        }
        String[] nonOverridableClasses = (String[]) nonOverridableSet.toArray(new String[nonOverridableSet.size()]);

        log.debug("ClassPath for " + id + " resolved to " + Arrays.asList(urls));

        if (Boolean.getBoolean("Xorg.apache.geronimo.NewClassLoader")) {
            return new JarFileClassLoader(environment.getConfigId(),
                    urls,
                    parentClassLoaders,
                    environment.isInverseClassLoading(),
                    hiddenClasses,
                    nonOverridableClasses);
        } else {
            return new MultiParentClassLoader(environment.getConfigId(),<commit_msg>Default to use the new JarFileClassLoader. To use the old MultiParentClassLoader, must specify the property -DXorg.apache.geronimo.OldClassLoader=true<commit_after>/**
 *
 * Copyright 2003-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.geronimo.kernel.config;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.AbstractNameQuery;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.ReferencePatterns;
import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Naming;
import org.apache.geronimo.kernel.classloader.JarFileClassLoader;
import org.apache.geronimo.kernel.repository.Artifact;
import org.apache.geronimo.kernel.repository.Dependency;
import org.apache.geronimo.kernel.repository.Environment;
import org.apache.geronimo.kernel.repository.ImportType;
import org.apache.geronimo.kernel.repository.MissingDependencyException;

/**
 * A Configuration represents a collection of runnable services that can be
 * loaded into a Geronimo Kernel and brought online. The primary components in
 * a Configuration are a codebase, represented by a collection of URLs that
 * is used to locate classes, and a collection of GBean instances that define
 * its state.
 * <p/>
 * The persistent attributes of the Configuration are:
 * <ul>
 * <li>its unique configId used to identify this specific config</li>
 * <li>the configId of a parent Configuration on which this one is dependent</li>
 * <li>a List<URI> of code locations (which may be absolute or relative to a baseURL)</li>
 * <li>a byte[] holding the state of the GBeans instances in Serialized form</li>
 * </ul>
 * When a configuration is started, it converts the URIs into a set of absolute
 * URLs by resolving them against the specified baseURL (this would typically
 * be the root of the CAR file which contains the configuration) and then
 * constructs a ClassLoader for that codebase. That ClassLoader is then used
 * to de-serialize the persisted GBeans, ensuring the GBeans can be recycled
 * as necessary. Once the GBeans have been restored, they are brought online
 * by registering them with the MBeanServer.
 * <p/>
 * A dependency on the Configuration is created for every GBean it loads. As a
 * result, a startRecursive() operation on the configuration will result in
 * a startRecursive() for all the GBeans it contains. Similarly, if the
 * Configuration is stopped then all of its GBeans will be stopped as well.
 *
 * @version $Rev:385718 $ $Date$
 */
public class Configuration implements GBeanLifecycle, ConfigurationParent {
    private static final Log log = LogFactory.getLog(Configuration.class);
    public static final Object JSR77_BASE_NAME_PROPERTY = "org.apache.geronimo.name.javax.management.j2ee.BaseName";

    /**
     * Converts an Artifact to an AbstractName for a configuration.  Does not
     * validate that this is a reasonable or resolved Artifact, or that it
     * corresponds to an actual Configuration.
     */
    public static AbstractName getConfigurationAbstractName(Artifact configId) throws InvalidConfigException {
        return new AbstractName(configId, Collections.singletonMap("configurationName", configId.toString()), getConfigurationObjectName(configId));
    }

    public static boolean isConfigurationObjectName(ObjectName name) {
        return name.getDomain().equals("geronimo.config") && name.getKeyPropertyList().size() == 1 && name.getKeyProperty("name") != null;
    }

    public static Artifact getConfigurationID(ObjectName objectName) {
        if (isConfigurationObjectName(objectName)) {
            String name = ObjectName.unquote(objectName.getKeyProperty("name"));
            return Artifact.create(name);
        } else {
            throw new IllegalArgumentException("ObjectName " + objectName + " is not a Configuration name");
        }
    }

    private static ObjectName getConfigurationObjectName(Artifact configId) throws InvalidConfigException {
        try {
            return new ObjectName("geronimo.config:name=" + ObjectName.quote(configId.toString()));
        } catch (MalformedObjectNameException e) {
            throw new InvalidConfigException("Could not construct object name for configuration", e);
        }
    }

    /**
     * The artifact id for this configuration.
     */
    private final Artifact id;

    /**
     * The registered abstractName for this configuraion.
     */
    private final AbstractName abstractName;

    /**
     * Defines the environment requred for this configuration.
     */
    private final Environment environment;

    /**
     * Used to resolve dependecies and paths
     */
    private final ConfigurationResolver configurationResolver;

    /**
     * Parent configurations used for class loader.
     */
    private final List classParents = new ArrayList();

    /**
     * Parent configuations used for service resolution.
     */
    private final List serviceParents = new ArrayList();

    /**
     * All service parents depth first
     */
    private final List allServiceParents = new ArrayList();

    /**
     * Artifacts added to the class loader (non-configuation artifacts).
     */
    private final LinkedHashSet dependencies = new LinkedHashSet();

    /**
     * The GBeanData objects by ObjectName
     */
    private final Map gbeans = new HashMap();

    /**
     * The classloader used to load the child GBeans contained in this configuration.
     */
    private final MultiParentClassLoader configurationClassLoader;

    /**
     * The relative class path (URI) of this configuation.
     */
    private final LinkedHashSet classPath;

    /**
     * Naming system used when generating a name for a new gbean
     */
    private final Naming naming;

    /**
     * Environment, classpath, gbeans and other data for this configuration.
     */
    private ConfigurationData configurationData;

    /**
     * The nested configurations of this configuration.
     */
    List children = new ArrayList();

    /**
     * The parent of this configuration;
     */
    private Configuration parent = null;

    /**
     * Only used to allow declaration as a reference.
     */
    public Configuration() {
        id = null;
        abstractName = null;
        environment = null;
        classPath = null;
        configurationResolver = null;
        configurationClassLoader = null;
        naming = null;
    }

    /**
     * Creates a configuration.
     * @param parents parents of this configuation (not ordered)
     * @param configurationData the module type, environment and classpath of the configuration
     * @param configurationResolver used to resolve dependecies and paths
     */
    public Configuration(Collection parents,
            ConfigurationData configurationData,
            ConfigurationResolver configurationResolver,
            ManageableAttributeStore attributeStore) throws MissingDependencyException, MalformedURLException, NoSuchConfigException, InvalidConfigException {
        if (parents == null) parents = Collections.EMPTY_SET;
        if (configurationData == null) throw new NullPointerException("configurationData is null");
        if (configurationResolver == null) throw new NullPointerException("configurationResolver is null");

        this.configurationData = configurationData;
        this.environment = configurationData.getEnvironment();
        this.configurationResolver = configurationResolver;
        this.classPath = new LinkedHashSet(configurationData.getClassPath());
        this.naming = configurationData.getNaming();

        this.id = environment.getConfigId();
        abstractName = getConfigurationAbstractName(id);

        //
        // Transitively resolve all the dependencies in the environment
        //
        List transtiveDependencies = configurationResolver.resolveTransitiveDependencies(parents, environment.getDependencies());

        //
        // Process transtive dependencies splitting it into classParents, serviceParents and artifactDependencies
        //
        Map parentsById = new HashMap();
        for (Iterator iterator = parents.iterator(); iterator.hasNext();) {
            Configuration configuration = (Configuration) iterator.next();
            Artifact id = configuration.getId();
            parentsById.put(id, configuration);
        }

        for (Iterator iterator = transtiveDependencies.iterator(); iterator.hasNext();) {
            Dependency dependency = (Dependency) iterator.next();
            Artifact artifact = dependency.getArtifact();
            if (parentsById.containsKey(artifact)) {
                Configuration parent = (Configuration) parentsById.get(artifact);
                if (dependency.getImportType() == ImportType.CLASSES || dependency.getImportType() == ImportType.ALL) {
                    classParents.add(parent);
                }
                if (dependency.getImportType() == ImportType.SERVICES || dependency.getImportType() == ImportType.ALL) {
                    serviceParents.add(parent);
                }
            } else if (dependency.getImportType() == ImportType.SERVICES) {
                throw new IllegalStateException("Could not find parent " + artifact + " in the parents collection");
            } else {
                dependencies.add(artifact);
            }
        }

        try {
            //
            // Build the configuration class loader
            //
            configurationClassLoader = createConfigurationClasssLoader(parents, environment, classPath);

            //
            // Get all service parents in depth first order
            //
            addDepthFirstServiceParents(this, allServiceParents);

            //
            // Deserialize the GBeans in the configurationData
            //
            Collection gbeans = configurationData.getGBeans(configurationClassLoader);
            if (attributeStore != null) {
                gbeans = attributeStore.applyOverrides(id, gbeans, configurationClassLoader);
            }
            for (Iterator iterator = gbeans.iterator(); iterator.hasNext();) {
                GBeanData gbeanData = (GBeanData) iterator.next();
                this.gbeans.put(gbeanData.getAbstractName(), gbeanData);
            }

            //
            // Create child configurations
            //
            LinkedHashSet childParents = new LinkedHashSet(parents);
            childParents.add(this);
            for (Iterator iterator = configurationData.getChildConfigurations().entrySet().iterator(); iterator.hasNext();) {
                Map.Entry entry = (Map.Entry) iterator.next();
                String moduleName = (String) entry.getKey();
                ConfigurationData childConfigurationData = (ConfigurationData) entry.getValue();
                Configuration childConfiguration = new Configuration(childParents, childConfigurationData, configurationResolver.createChildResolver(moduleName), attributeStore);
                childConfiguration.parent = this;
                children.add(childConfiguration);
            }
        } catch (RuntimeException e) {
            shutdown();
            throw e;
        } catch (Error e) {
            shutdown();
            throw e;
        } catch (MissingDependencyException e) {
            shutdown();
            throw e;
        } catch (MalformedURLException e) {
            shutdown();
            throw e;
        } catch (NoSuchConfigException e) {
            shutdown();
            throw e;
        } catch (InvalidConfigException e) {
            shutdown();
            throw e;
        }
    }

    private MultiParentClassLoader createConfigurationClasssLoader(Collection parents, Environment environment, LinkedHashSet classPath) throws MalformedURLException, MissingDependencyException, NoSuchConfigException {
        // create the URL list
        URL[] urls = buildClassPath(classPath);

        // parents
        ClassLoader[] parentClassLoaders;
        if (parents.size() == 0 && classParents.size() == 0) {
            // no explicit parent set, so use the class loader of this class as
            // the parent... this class should be in the root geronimo classloader,
            // which is normally the system class loader but not always, so be safe
            parentClassLoaders = new ClassLoader[] {getClass().getClassLoader()};
        } else {
            parentClassLoaders = new ClassLoader[classParents.size()];
            for (ListIterator iterator = classParents.listIterator(); iterator.hasNext();) {
                Configuration configuration = (Configuration) iterator.next();
                parentClassLoaders[iterator.previousIndex()] = configuration.getConfigurationClassLoader();
            }
        }

        // hidden classes
        Set hiddenClassesSet = environment.getHiddenClasses();
        String[] hiddenClasses = (String[]) hiddenClassesSet.toArray(new String[hiddenClassesSet.size()]);

        // we need to propagate the non-overrideable classes from parents
        LinkedHashSet nonOverridableSet = new LinkedHashSet();
        for (Iterator iterator = classParents.iterator(); iterator.hasNext();) {
            Configuration parent = (Configuration) iterator.next();

            Environment parentEnvironment = parent.getEnvironment();
            nonOverridableSet.addAll(parentEnvironment.getNonOverrideableClasses());
        }
        String[] nonOverridableClasses = (String[]) nonOverridableSet.toArray(new String[nonOverridableSet.size()]);

        log.debug("ClassPath for " + id + " resolved to " + Arrays.asList(urls));

        if (Boolean.getBoolean("Xorg.apache.geronimo.OldClassLoader")) {
            return new MultiParentClassLoader(environment.getConfigId(),
                    urls,
                    parentClassLoaders,
                    environment.isInverseClassLoading(),
                    hiddenClasses,
                    nonOverridableClasses);
        } else {
            return new JarFileClassLoader(environment.getConfigId(),
{'fraction_non_alphanumeric': 0.0837698038322652, 'fraction_numerical': 0.0015615342073587299, 'mean_word_length': 2.8477907122293153, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7592439', 'n_tokens_mistral': 7443, 'n_tokens_neox': 7249, 'n_words': 2292}