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

org.drools.util.CompositeClassLoader Maven / Gradle / Ivy

There is a newer version: 5.1.1
Show newest version
/**
 * Copyright 2010 JBoss Inc
 *
 * 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.drools.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;

public class CompositeClassLoader extends ClassLoader {
    /* Assumption: modifications are really rare, but iterations are frequent. */
    private final List       classLoaders = new CopyOnWriteArrayList();
    private final AtomicReference loader       = new AtomicReference();

    public CompositeClassLoader(final ClassLoader parentClassLoader) {
        super( null );
        loader.set( new DefaultLoader() );
    }

    public synchronized void setCachingEnabled(boolean enabled) {
        if ( enabled && loader.get() instanceof DefaultLoader ) {
            loader.set( new CachingLoader() );
        } else if ( !enabled && loader.get() instanceof CachingLoader ) {
            loader.set( DefaultLoader.INSTANCE );
        }
    }

    public synchronized void addClassLoader(final ClassLoader classLoader) {
        /* NB: we need synchronized here even though we use a COW list:
         *     two threads may try to add the same new class loader, so we need
         *     to protect over a bigger area than just a single iteration.
         */
        // don't add duplicate ClassLoaders;
        for ( final ClassLoader cl : this.classLoaders ) {
            if ( cl == classLoader ) {
                return;
            }
        }
        this.classLoaders.add( classLoader );
        this.loader.get().reset();
    }

    public synchronized void removeClassLoader(final ClassLoader classLoader) {
        /* synchronized to protect against concurrent runs of 
         * addClassLoader(x) and removeClassLoader(x).
         */
        classLoaders.remove( classLoader );
        this.loader.get().reset();
    }

    /**
     * This ClassLoader never has classes of it's own, so only search the child ClassLoaders
     * and the parent ClassLoader if one is provided
     */
    public Class< ? > loadClass(final String name,
                                final boolean resolve) throws ClassNotFoundException {
        return loader.get().load( this,
                                  name,
                                  resolve );
    }

    /**
     * This ClassLoader never has classes of it's own, so only search the child ClassLoaders
     * and the parent ClassLoader if one is provided
     */
    public InputStream getResourceAsStream(final String name) {
        for ( final ClassLoader classLoader : this.classLoaders ) {
            InputStream stream = classLoader.getResourceAsStream( name );
            if ( stream != null ) {
                return stream;
            }
        }

        return null;
    }

    @Override
    public URL getResource(String name) {
        for ( final ClassLoader classLoader : this.classLoaders ) {
            URL url = classLoader.getResource( name );
            if ( url != null ) {
                return url;
            }
        }

        return null;
    }

    @Override
    public Enumeration getResources(String name) throws IOException {
        CompositeEnumeration enumerations = new CompositeEnumeration();

        for ( final ClassLoader classLoader : this.classLoaders ) {
            Enumeration e = classLoader.getResources( name );
            if ( e != null ) {
                enumerations.addEnumeration( e );
            }
        }

        if ( enumerations.size() == 0 ) {
            return null;
        } else {
            return enumerations;
        }
    }

    public void dumpStats() {
        System.out.println( loader.toString() );
    }

    private static interface Loader {
        public Class< ? > load(final CompositeClassLoader cl,
                               final String name,
                               final boolean resolve);

        public void reset();
    }

    private static class DefaultLoader
        implements
        Loader {

        // this class is stateless, so lets make a singleton of it
        public static final DefaultLoader INSTANCE = new DefaultLoader();

        private DefaultLoader() {
        }

        public Class< ? > load(final CompositeClassLoader cl,
                               final String name,
                               final boolean resolve) {
            // search the child ClassLoaders
            Class< ? > cls = null;

            for ( final ClassLoader classLoader : cl.classLoaders ) {
                try {
                    cls = Class.forName( name,
                                         true,
                                         classLoader );
                } catch ( ClassNotFoundException e ) {
                    // swallow as we need to check more classLoaders
                }
                if ( cls != null ) {
                    break;
                }
            }

            if ( resolve ) {
                cl.resolveClass( cls );
            }

            return cls;
        }

        public void reset() {
            // nothing to do
        }
    }

    private static class CachingLoader
        implements
        Loader {

        private final Map classLoaderResultMap = new HashMap();
        public long                       successfulCalls      = 0;
        public long                       failedCalls          = 0;
        public long                       cacheHits            = 0;

        public Class< ? > load(final CompositeClassLoader cl,
                               final String name,
                               final boolean resolve) {
            if ( classLoaderResultMap.containsKey( name ) ) {
                cacheHits++;
                return (Class< ? >) classLoaderResultMap.get( name );
            }
            // search the child ClassLoaders
            Class< ? > cls = null;

            for ( final ClassLoader classLoader : cl.classLoaders ) {
                try {
                    cls = Class.forName( name,
                                         true,
                                         classLoader );
                } catch ( ClassNotFoundException e ) {
                    // swallow as we need to check more classLoaders
                }
                if ( cls != null ) {
                    break;
                }
            }

            if ( resolve ) {
                cl.resolveClass( cls );
            }

            classLoaderResultMap.put( name,
                                      cls );
            if ( cls != null ) {
                this.successfulCalls++;
            } else {
                this.failedCalls++;
            }

            return cls;
        }

        public void reset() {
            this.successfulCalls = this.failedCalls = this.cacheHits = 0;
        }

        public String toString() {
            return new StringBuilder().append( "TotalCalls: " ).append( successfulCalls + failedCalls + cacheHits ).append( " CacheHits: " ).append( cacheHits ).append( " successfulCalls: " ).append( successfulCalls ).append( " FailedCalls: " ).append( failedCalls ).toString();
        }
    }

    private static class CompositeEnumeration
        implements
        Enumeration {
        private List     list;
        private Iterator it;

        public void addEnumeration(Enumeration enumeration) {
            if ( !enumeration.hasMoreElements() ) {
                // don't add it, if it's empty
                return;
            }

            if ( this.it != null ) {
                throw new IllegalStateException( "cannot add more enumerations while iterator" );
            }

            if ( this.list == null ) {
                this.list = new ArrayList();
            }

            while ( enumeration.hasMoreElements() ) {
                this.list.add( enumeration.nextElement() );
            }
        }

        public int size() {
            if ( this.list == null ) {
                return 0;
            } else {
                return this.list.size();
            }
        }

        public boolean hasMoreElements() {
            if ( this.it == null ) {
                if ( this.list == null ) {
                    return false;
                } else {
                    this.it = this.list.iterator();
                }
            }
            return it.hasNext();
        }

        public URL nextElement() {
            if ( this.it == null ) {
                if ( this.list == null ) {
                    throw new NoSuchElementException();
                } else {
                    this.it = this.list.iterator();
                }
            }
            return it.next();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy