HHH-8096 Re-work the OSGi class loading concepts

This commit is contained in:
Brett Meyer 2013-03-25 11:34:22 -04:00 committed by Brett Meyer
parent 206a846934
commit 1df99fc066
8 changed files with 362 additions and 211 deletions

View File

@ -912,7 +912,6 @@ public class Ejb3Configuration implements Serializable, Referenceable {
thread = Thread.currentThread();
contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader( overridenClassLoader );
builder.with( overridenClassLoader );
}
try {

View File

@ -42,16 +42,6 @@ public class HibernatePersistence extends AvailableSettings implements Persisten
private final PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache();
/**
* Used for environment-supplied properties. Ex: hibernate-osgi's
* HibernateBundleActivator needs to set a custom JtaPlatform.
*/
private Map environmentProperties;
public void setEnvironmentProperties( Map environmentProperties ) {
this.environmentProperties = environmentProperties;
}
/**
* Get an entity manager factory by its entity manager name, using the specified
* properties (they override any found in the peristence.xml file).
@ -64,9 +54,6 @@ public class HibernatePersistence extends AvailableSettings implements Persisten
* @return initialized EntityManagerFactory
*/
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) {
if ( environmentProperties != null ) {
properties.putAll( environmentProperties );
}
Ejb3Configuration cfg = new Ejb3Configuration();
Ejb3Configuration configured = cfg.configure( persistenceUnitName, properties );
return configured != null ? configured.buildEntityManagerFactory() : null;
@ -84,9 +71,6 @@ public class HibernatePersistence extends AvailableSettings implements Persisten
* @return initialized EntityManagerFactory
*/
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
if ( environmentProperties != null ) {
properties.putAll( environmentProperties );
}
Ejb3Configuration cfg = new Ejb3Configuration();
Ejb3Configuration configured = cfg.configure( info, properties );
return configured != null ? configured.buildEntityManagerFactory() : null;

View File

@ -1,94 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.osgi;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.osgi.framework.Bundle;
/**
* Integrates a Bundle, its key, and classes/resources that have been found
* through its ClassLoader. Primarily used to clear the OsgiClassLoader
* caches once the Bundle is deactivated.
*
* @author Brett Meyer
*/
public class CachedBundle {
private Bundle bundle;
private String key;
private List<String> classNames = new ArrayList<String>();
private List<String> resourceNames = new ArrayList<String>();
private List<String> resourceListNames = new ArrayList<String>();
public CachedBundle( Bundle bundle, String key ) {
this.bundle = bundle;
this.key = key;
}
public Class loadClass(String name) throws ClassNotFoundException {
Class clazz = bundle.loadClass( name );
if ( clazz != null ) {
classNames.add( name );
}
return clazz;
}
public URL getResource(String name) {
URL resource = bundle.getResource( name );
if ( resource != null ) {
resourceNames.add( name );
}
return resource;
}
public Enumeration getResources(String name) throws IOException {
Enumeration resourceList = bundle.getResources( name );
if ( resourceList != null ) {
resourceListNames.add( name );
}
return resourceList;
}
public String getKey() {
return key;
}
public List<String> getClassNames() {
return classNames;
}
public List<String> getResourceNames() {
return resourceNames;
}
public List<String> getResourceListNames() {
return resourceListNames;
}
}

View File

@ -23,75 +23,68 @@
*/
package org.hibernate.osgi;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.persistence.spi.PersistenceProvider;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernatePersistence;
import org.hibernate.internal.util.ClassLoaderHelper;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkUtil;
/**
* This BundleActivator provides three different uses of Hibernate in OSGi
* environments:
*
* 1.) Enterprise OSGi JPA: The OSGi container/runtime is in charge of managing
* all of the client bundles' persistence units. The container/runtime is
* also in charge of creating and managing the EntityManagerFactory through a
* registered PersistenceProvider (this).
* 2.) Un-managed OSGI JPA: Same as #1, but the container does not manage
* the persistence units. Client bundles identify a typical
* PersistenceProvider, registered by this activator.
* 3.) Client bundles create and manage their own SessionFactory. A
* SessionFactory is registered as an OSGi ServiceFactory -- each requesting
* bundle gets its own instance of a SessionFactory. The use of services,
* rather than direct use of Configuration, is necessary to shield users
* from ClassLoader issues. See {@link #OsgiSessionFactoryService} for more
* information.
*
* @author Brett Meyer
* @author Martin Neimeier
* @author Tim Ward
*/
public class HibernateBundleActivator
implements BundleActivator, /*ServiceListener,*/ BundleListener {
public class HibernateBundleActivator implements BundleActivator {
private OsgiClassLoader osgiClassLoader;
@Override
public void start(BundleContext context) throws Exception {
// register this instance as a bundle listener to get informed about all
// bundle live cycle events
context.addBundleListener(this);
osgiClassLoader = new OsgiClassLoader();
ClassLoaderHelper.overridenClassLoader = osgiClassLoader;
private OsgiJtaPlatform osgiJtaPlatform;
for ( Bundle bundle : context.getBundles() ) {
handleBundleChange( bundle );
}
HibernatePersistence hp = new HibernatePersistence();
Map map = new HashMap();
map.put( AvailableSettings.JTA_PLATFORM, new OsgiJtaPlatform( context ) );
hp.setEnvironmentProperties( map );
Properties properties = new Properties();
properties.put( "javax.persistence.provider", HibernatePersistence.class.getName() );
context.registerService( PersistenceProvider.class.getName(), hp, properties );
}
@Override
public void start(BundleContext context) throws Exception {
@Override
public void stop(BundleContext context) throws Exception {
context.removeBundleListener(this);
// Nothing else to do. When re-activated, this Activator will be
// re-started and the EMF re-created.
}
osgiClassLoader = new OsgiClassLoader();
osgiClassLoader.addBundle( FrameworkUtil.getBundle( Session.class ) );
osgiClassLoader.addBundle( FrameworkUtil.getBundle( HibernatePersistence.class ) );
ClassLoaderHelper.overridenClassLoader = osgiClassLoader;
@Override
public void bundleChanged(BundleEvent event) {
handleBundleChange( event.getBundle() );
osgiJtaPlatform = new OsgiJtaPlatform( context );
}
private void handleBundleChange( Bundle bundle ) {
if ( bundle.getState() == Bundle.ACTIVE ) {
osgiClassLoader.registerBundle(bundle);
} else {
osgiClassLoader.unregisterBundle(bundle);
}
}
Properties properties = new Properties();
// In order to support existing persistence.xml files, register
// using the legacy provider name.
properties.put( "javax.persistence.provider", HibernatePersistence.class.getName() );
context.registerService( PersistenceProvider.class.getName(),
new OsgiPersistenceProviderService( osgiClassLoader, osgiJtaPlatform ), properties );
context.registerService( SessionFactory.class.getName(),
new OsgiSessionFactoryService( osgiClassLoader, osgiJtaPlatform ), new Properties());
}
}
@Override
public void stop(BundleContext context) throws Exception {
// Nothing else to do?
}
}

View File

@ -24,22 +24,27 @@
package org.hibernate.osgi;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.osgi.framework.Bundle;
/**
* Custom OSGI ClassLoader helper which knows all the "interesting" bundles and
* encapsulates the OSGi related capabilities.
* 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 {
private Map<String, CachedBundle> bundles = new HashMap<String, CachedBundle>();
private List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
private List<Bundle> bundles = new ArrayList<Bundle>();
private Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
@ -59,7 +64,7 @@ public class OsgiClassLoader extends ClassLoader {
return classCache.get( name );
}
for ( CachedBundle bundle : bundles.values() ) {
for ( Bundle bundle : bundles ) {
try {
Class clazz = bundle.loadClass( name );
if ( clazz != null ) {
@ -70,6 +75,18 @@ public class OsgiClassLoader extends ClassLoader {
catch ( Exception ignore ) {
}
}
for ( ClassLoader classLoader : classLoaders ) {
try {
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 );
}
@ -85,7 +102,7 @@ public class OsgiClassLoader extends ClassLoader {
return resourceCache.get( name );
}
for ( CachedBundle bundle : bundles.values() ) {
for ( Bundle bundle : bundles ) {
try {
URL resource = bundle.getResource( name );
if ( resource != null ) {
@ -96,6 +113,19 @@ public class OsgiClassLoader extends ClassLoader {
catch ( Exception ignore ) {
}
}
for ( ClassLoader classLoader : classLoaders ) {
try {
URL resource = classLoader.getResource( name );
if ( resource != null ) {
resourceCache.put( name, resource );
return resource;
}
}
catch ( Exception ignore ) {
}
}
// TODO: Error?
return null;
}
@ -112,67 +142,69 @@ public class OsgiClassLoader extends ClassLoader {
return resourceListCache.get( name );
}
for ( CachedBundle bundle : bundles.values() ) {
final List<Enumeration<URL>> enumerations = new ArrayList<Enumeration<URL>>();
for ( Bundle bundle : bundles ) {
try {
Enumeration<URL> resources = bundle.getResources( name );
if ( resources != null ) {
resourceListCache.put( name, resources );
return resources;
enumerations.add( resources );
}
}
catch ( Exception ignore ) {
}
}
// TODO: Error?
return null;
}
/**
* Register the bundle with this class loader
*/
public void registerBundle(Bundle bundle) {
if ( bundle != null ) {
synchronized ( bundles ) {
String key = getBundleKey( bundle );
if ( !bundles.containsKey( key ) ) {
bundles.put( key, new CachedBundle( bundle, key ) );
for ( ClassLoader classLoader : classLoaders ) {
try {
Enumeration<URL> resources = classLoader.getResources( name );
if ( resources != null ) {
enumerations.add( resources );
}
}
}
}
/**
* Unregister the bundle from this class loader
*/
public void unregisterBundle(Bundle bundle) {
if ( bundle != null ) {
synchronized ( bundles ) {
String key = getBundleKey( bundle );
if ( bundles.containsKey( key ) ) {
CachedBundle cachedBundle = bundles.remove( key );
clearCache( classCache, cachedBundle.getClassNames() );
clearCache( resourceCache, cachedBundle.getResourceNames() );
clearCache( resourceListCache, cachedBundle.getResourceListNames() );
}
catch ( Exception ignore ) {
}
}
Enumeration<URL> aggEnumeration = new Enumeration<URL>() {
@Override
public boolean hasMoreElements() {
for ( Enumeration<URL> enumeration : enumerations ) {
if ( enumeration != null && enumeration.hasMoreElements() ) {
return true;
}
}
return false;
}
@Override
public URL nextElement() {
for ( Enumeration<URL> enumeration : enumerations ) {
if ( enumeration != null && enumeration.hasMoreElements() ) {
return enumeration.nextElement();
}
}
throw new NoSuchElementException();
}
};
resourceListCache.put( name, aggEnumeration );
return aggEnumeration;
}
private void clearCache( Map cache, List<String> names ) {
for ( String name : names ) {
cache.remove( name );
}
public void addClassLoader( ClassLoader classLoader ) {
classLoaders.add( classLoader );
}
public void addBundle( Bundle bundle ) {
bundles.add( bundle );
}
public void clear() {
bundles.clear();
classCache.clear();
resourceCache.clear();
resourceListCache.clear();
}
protected static String getBundleKey(Bundle bundle) {
return bundle.getSymbolicName() + " " + bundle.getVersion().toString();
}
}

View File

@ -0,0 +1,99 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.osgi;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitInfo;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.ejb.HibernatePersistence;
import org.hibernate.service.BootstrapServiceRegistryBuilder;
import org.osgi.framework.Bundle;
/**
* @author Brett Meyer
* @author Tim Ward
*/
public class OsgiPersistenceProvider extends HibernatePersistence {
private OsgiClassLoader osgiClassLoader;
private OsgiJtaPlatform osgiJtaPlatform;
private Bundle requestingBundle;
public OsgiPersistenceProvider (OsgiClassLoader osgiClassLoader,
OsgiJtaPlatform osgiJtaPlatform,
Bundle requestingBundle ) {
this.osgiClassLoader = osgiClassLoader;
this.osgiJtaPlatform = osgiJtaPlatform;
this.requestingBundle = requestingBundle;
}
@Override
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) {
if ( properties == null ) {
properties = new HashMap();
}
properties.put( AvailableSettings.JTA_PLATFORM, osgiJtaPlatform );
osgiClassLoader.addBundle( requestingBundle );
Ejb3Configuration cfg = new Ejb3Configuration();
Ejb3Configuration configured = cfg.configure( persistenceUnitName, properties );
return configured != null ? configured.buildEntityManagerFactory( getBuilder( properties ) ) : null;
}
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
if ( properties == null ) {
properties = new HashMap();
}
properties.put( AvailableSettings.JTA_PLATFORM, osgiJtaPlatform );
osgiClassLoader.addClassLoader( info.getClassLoader() );
Ejb3Configuration cfg = new Ejb3Configuration();
Ejb3Configuration configured = cfg.configure( info, properties );
return configured != null ? configured.buildEntityManagerFactory( getBuilder( properties ) ) : null;
}
private BootstrapServiceRegistryBuilder getBuilder(Map properties) {
BootstrapServiceRegistryBuilder builder = new BootstrapServiceRegistryBuilder();
final Collection<ClassLoader> classLoaders = (Collection<ClassLoader>) properties
.get( AvailableSettings.CLASSLOADERS );
if ( classLoaders != null ) {
for ( ClassLoader classLoader : classLoaders ) {
osgiClassLoader.addClassLoader( classLoader );
}
}
builder.with( osgiClassLoader );
return builder;
}
}

View File

@ -0,0 +1,55 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.osgi;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
/**
* See the description on {@link #OsgiSessionFactoryService}. This class
* is similar, providing an PersistenceProvider as an OSGi Service.
*
* @author Brett Meyer
* @author Tim Ward
*/
public class OsgiPersistenceProviderService implements ServiceFactory {
private OsgiClassLoader osgiClassLoader;
private OsgiJtaPlatform osgiJtaPlatform;
public OsgiPersistenceProviderService( OsgiClassLoader osgiClassLoader, OsgiJtaPlatform osgiJtaPlatform ) {
this.osgiClassLoader = osgiClassLoader;
this.osgiJtaPlatform = osgiJtaPlatform;
}
@Override
public Object getService(Bundle requestingBundle, ServiceRegistration registration) {
return new OsgiPersistenceProvider(osgiClassLoader, osgiJtaPlatform, requestingBundle);
}
@Override
public void ungetService(Bundle requestingBundle, ServiceRegistration registration, Object service) {
// ?
}
}

View File

@ -0,0 +1,83 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.osgi;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.BootstrapServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
/**
* Hibernate 4.2 and 4.3 still heavily rely on TCCL for ClassLoading. Although
* our ClassLoaderService removed some of the reliance, the service is
* unfortunately not available during Configuration. An OSGi
* bundle manually creating a SessionFactory would require numerous ClassLoader
* tricks (or may be impossible altogether).
*
* In order to fully control the TCCL issues and shield users from the
* knowledge, we're requiring that bundles use this OSGi ServiceFactory. It
* configures and provides a SessionFactory as an OSGi service.
*
* Note that an OSGi ServiceFactory differs from a Service. The ServiceFactory
* allows individual instances of Services to be created and provided to
* multiple client Bundles.
*
* @author Brett Meyer
* @author Tim Ward
*/
public class OsgiSessionFactoryService implements ServiceFactory {
private OsgiClassLoader osgiClassLoader;
private OsgiJtaPlatform osgiJtaPlatform;
public OsgiSessionFactoryService( OsgiClassLoader osgiClassLoader, OsgiJtaPlatform osgiJtaPlatform ) {
this.osgiClassLoader = osgiClassLoader;
this.osgiJtaPlatform = osgiJtaPlatform;
}
@Override
public Object getService(Bundle requestingBundle, ServiceRegistration registration) {
osgiClassLoader.addBundle( requestingBundle );
Configuration configuration = new Configuration();
configuration.getProperties().put( AvailableSettings.JTA_PLATFORM, osgiJtaPlatform );
configuration.configure();
BootstrapServiceRegistryBuilder builder = new BootstrapServiceRegistryBuilder();
builder.with( osgiClassLoader );
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder( builder.build() )
.applySettings(configuration.getProperties()).buildServiceRegistry();
return configuration.buildSessionFactory(serviceRegistry);
}
@Override
public void ungetService(Bundle requestingBundle, ServiceRegistration registration, Object service) {
( (SessionFactory) service).close();
}
}