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

org.hibernate.osgi.OsgiClassLoader Maven / Gradle / Ivy

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.osgi;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.hibernate.service.spi.Stoppable;
import org.osgi.framework.Bundle;

/**
 * Custom OSGI ClassLoader helper which knows all the "interesting"
 * class loaders and bundles.  Encapsulates the OSGi related CL capabilities.
 *
 * @author Brett Meyer
 * @author Tim Ward
 */
public class OsgiClassLoader extends ClassLoader implements Stoppable {
	// Leave these as Sets -- addClassLoader or addBundle may be called more
	// than once if a SF or EMF is closed and re-created.
	// HHH-12553: must be thread-safe. Concurrent impl. would be best, but we have to retain insertion-order.
	private Set classLoaders = Collections.synchronizedSet(new LinkedHashSet());
	private Set bundles = Collections.synchronizedSet(new LinkedHashSet());

	private ConcurrentMap> classCache = new ConcurrentHashMap>();
	private ConcurrentMap resourceCache = new ConcurrentHashMap();

	static {
		ClassLoader.registerAsParallelCapable();
	}

	public OsgiClassLoader() {
		// DO NOT use ClassLoader#parent, which is typically the SystemClassLoader for most containers.  Instead,
		// allow the ClassNotFoundException to be thrown.  ClassLoaderServiceImpl will check the SystemClassLoader
		// later on.  This is especially important for embedded OSGi containers, etc.
		super( null );
	}

	/**
	 * Load the class and break on first found match.
	 *
	 * TODO: Should this throw a different exception or warn if multiple
	 * classes were found? Naming collisions can and do happen in OSGi...
	 */
	@Override
	@SuppressWarnings("rawtypes")
	protected Class findClass(String name) throws ClassNotFoundException {
		Class< ? > cachedClass = classCache.get( name );
		if ( cachedClass != null ) {
			return cachedClass;
		}

		synchronized (bundles) {
			for ( Bundle bundle : bundles ) {
				try {
					final Class clazz = bundle.loadClass( name );
					if ( clazz != null ) {
						classCache.put( name, clazz );
						return clazz;
					}
				}
				catch ( Exception ignore ) {
				}
			}
		}

		synchronized (classLoaders) {
			for ( ClassLoader classLoader : classLoaders ) {
				try {
					final Class clazz = classLoader.loadClass( name );
					if ( clazz != null ) {
						classCache.put( name, clazz );
						return clazz;
					}
				}
				catch ( Exception ignore ) {
				}
			}
		}

		throw new ClassNotFoundException( "Could not load requested class : " + name );
	}

	/**
	 * Load the class and break on first found match.
	 *
	 * TODO: Should this throw a different exception or warn if multiple
	 * classes were found? Naming collisions can and do happen in OSGi...
	 */
	@Override
	protected URL findResource(String name) {
		URL cachedResource = resourceCache.get( name );
		if ( cachedResource != null ) {
			return cachedResource;
		}

		synchronized (bundles) {
			for ( Bundle bundle : bundles ) {
				try {
					final URL resource = bundle.getResource( name );
					if ( resource != null ) {
						resourceCache.put( name, resource );
						return resource;
					}
				}
				catch ( Exception ignore ) {
				}
			}
		}

		synchronized (classLoaders) {
			for ( ClassLoader classLoader : classLoaders ) {
				try {
					final URL resource = classLoader.getResource( name );
					if ( resource != null ) {
						resourceCache.put( name, resource );
						return resource;
					}
				}
				catch ( Exception ignore ) {
				}
			}
		}

		// TODO: Error?
		return null;
	}

	/**
	 * Load the class and break on first found match.
	 *
	 * Note: Since they're Enumerations, do not cache these results!
	 *
	 * TODO: Should this throw a different exception or warn if multiple
	 * classes were found? Naming collisions can and do happen in OSGi...
	 */
	@Override
	@SuppressWarnings("unchecked")
	protected Enumeration findResources(String name) {
		final List> enumerations = new ArrayList>();

		synchronized (bundles) {
			for ( Bundle bundle : bundles ) {
				try {
					final Enumeration resources = bundle.getResources( name );
					if ( resources != null ) {
						enumerations.add( resources );
					}
				}
				catch ( Exception ignore ) {
				}
			}
		}

		synchronized (classLoaders) {
			for ( ClassLoader classLoader : classLoaders ) {
				try {
					final Enumeration resources = classLoader.getResources( name );
					if ( resources != null ) {
						enumerations.add( resources );
					}
				}
				catch ( Exception ignore ) {
				}
			}
		}

		final Enumeration aggEnumeration = new Enumeration() {
			@Override
			public boolean hasMoreElements() {
				for ( Enumeration enumeration : enumerations ) {
					if ( enumeration != null && enumeration.hasMoreElements() ) {
						return true;
					}
				}
				return false;
			}

			@Override
			public URL nextElement() {
				for ( Enumeration enumeration : enumerations ) {
					if ( enumeration != null && enumeration.hasMoreElements() ) {
						return enumeration.nextElement();
					}
				}
				throw new NoSuchElementException();
			}
		};

		return aggEnumeration;
	}

	/**
	 * Adds a ClassLoader to the wrapped set of ClassLoaders
	 *
	 * @param classLoader The ClassLoader to add
	 */
	public void addClassLoader( ClassLoader classLoader ) {
		classLoaders.add( classLoader );
	}

	/**
	 * Adds a Bundle to the wrapped set of Bundles
	 *
	 * @param bundle The Bundle to add
	 */
	public void addBundle( Bundle bundle ) {
		bundles.add( bundle );
	}

	@Override
	public void stop() {
		classLoaders.clear();
		bundles.clear();
		classCache.clear();
		resourceCache.clear();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy