HHH-8096 Re-work the OSGi class loading concepts

Conflicts:
	hibernate-entitymanager/src/main/java/org/hibernate/ejb/Ejb3Configuration.java
	hibernate-entitymanager/src/main/java/org/hibernate/ejb/HibernatePersistence.java
	hibernate-osgi/src/main/java/org/hibernate/osgi/HibernateBundleActivator.java
This commit is contained in:
Brett Meyer 2013-03-27 15:04:48 -04:00 committed by Brett Meyer
parent f2e4e32558
commit e9e29a40be
8 changed files with 346 additions and 215 deletions

View File

@ -37,7 +37,6 @@ import org.hibernate.boot.registry.selector.AvailabilityAnnouncer;
import org.hibernate.boot.registry.selector.internal.StrategySelectorBuilder;
import org.hibernate.integrator.internal.IntegratorServiceImpl;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.util.ClassLoaderHelper;
/**
* Builder for bootstrap {@link org.hibernate.service.ServiceRegistry} instances.
@ -199,9 +198,6 @@ public class BootstrapServiceRegistryBuilder {
if ( providedClassLoaders != null ) {
classLoaders.addAll( providedClassLoaders );
}
if ( ClassLoaderHelper.overridenClassLoader != null ) {
classLoaders.add( ClassLoaderHelper.overridenClassLoader );
}
classLoaderService = new ClassLoaderServiceImpl( classLoaders );
} else {

View File

@ -55,16 +55,6 @@ public class HibernatePersistenceProvider implements PersistenceProvider {
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;
}
/**
* {@inheritDoc}
* <p/>
@ -74,10 +64,6 @@ public class HibernatePersistenceProvider implements PersistenceProvider {
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) {
log.tracef( "Starting createEntityManagerFactory for persistenceUnitName %s", persistenceUnitName );
if ( environmentProperties != null ) {
properties.putAll( environmentProperties );
}
final EntityManagerFactoryBuilder builder = getEntityManagerFactoryBuilderOrNull( persistenceUnitName, properties );
if ( builder == null ) {
log.trace( "Could not obtain matching EntityManagerFactoryBuilder, returning null" );
@ -88,7 +74,7 @@ public class HibernatePersistenceProvider implements PersistenceProvider {
}
}
private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties) {
protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties) {
log.tracef( "Attempting to obtain correct EntityManagerFactoryBuilder for persistenceUnitName : %s", persistenceUnitName );
final Map integration = wrap( properties );
@ -153,9 +139,6 @@ public class HibernatePersistenceProvider implements PersistenceProvider {
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
log.tracef( "Starting createContainerEntityManagerFactory : %s", info.getPersistenceUnitName() );
if ( environmentProperties != null ) {
properties.putAll( environmentProperties );
}
return Bootstrap.getEntityManagerFactoryBuilder( info, properties ).build();
}

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,74 +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.internal.util.ClassLoaderHelper;
import org.hibernate.jpa.HibernatePersistenceProvider;
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 );
}
HibernatePersistenceProvider hpp = new HibernatePersistenceProvider();
Map map = new HashMap();
map.put( AvailableSettings.JTA_PLATFORM, new OsgiJtaPlatform( context ) );
hpp.setEnvironmentProperties( map );
Properties properties = new Properties();
properties.put( "javax.persistence.provider", HibernatePersistenceProvider.class.getName() );
context.registerService( PersistenceProvider.class.getName(), hpp, 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( HibernatePersistenceProvider.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", HibernatePersistenceProvider.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,82 @@
/*
* 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.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitInfo;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.osgi.framework.Bundle;
/**
* @author Brett Meyer
* @author Tim Ward
*/
public class OsgiPersistenceProvider extends HibernatePersistenceProvider {
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;
}
// TODO: Does "hibernate.classloaders" and osgiClassLoader need added to the
// EMFBuilder somehow?
@Override
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) {
if ( properties == null ) {
properties = new HashMap();
}
properties.put( AvailableSettings.JTA_PLATFORM, osgiJtaPlatform );
osgiClassLoader.addBundle( requestingBundle );
return super.createEntityManagerFactory( persistenceUnitName, properties );
}
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
if ( properties == null ) {
properties = new HashMap();
}
properties.put( AvailableSettings.JTA_PLATFORM, osgiJtaPlatform );
osgiClassLoader.addClassLoader( info.getClassLoader() );
return super.createContainerEntityManagerFactory( info, properties );
}
}

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.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
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 StandardServiceRegistryBuilder( builder.build() )
.applySettings(configuration.getProperties()).build();
return configuration.buildSessionFactory(serviceRegistry);
}
@Override
public void ungetService(Bundle requestingBundle, ServiceRegistration registration, Object service) {
( (SessionFactory) service).close();
}
}