org.hibernate.osgi.OsgiSessionFactoryService 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 org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader;
import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence;
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.CoreMessageLogger;
import org.jboss.logging.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.wiring.BundleWiring;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* Hibernate 4.2 and 4.3 still heavily rely on TCCL for ClassLoading. Although
* our ClassLoaderService removed some of the reliance, access to the proper ClassLoader
* via TCCL is still required in a few cases where we call out to external libs. 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 static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
OsgiSessionFactoryService.class.getName());
private OsgiJtaPlatform osgiJtaPlatform;
private OsgiServiceUtil osgiServiceUtil;
/**
* Constructs a OsgiSessionFactoryService
*
* @param osgiJtaPlatform The OSGi-specific JtaPlatform created in HibernateBundleActivator
* @param osgiServiceUtil Util object built in HibernateBundleActivator
*/
public OsgiSessionFactoryService(OsgiJtaPlatform osgiJtaPlatform, OsgiServiceUtil osgiServiceUtil) {
this.osgiJtaPlatform = osgiJtaPlatform;
this.osgiServiceUtil = osgiServiceUtil;
}
@Override
public Object getService(Bundle requestingBundle, ServiceRegistration registration) {
final OsgiClassLoader osgiClassLoader = new OsgiClassLoader();
// First, add the client bundle that's requesting the OSGi services.
osgiClassLoader.addBundle( requestingBundle );
// Then, automatically add hibernate-core. These are needed to load resources
// contained in the core jar.
osgiClassLoader.addBundle( FrameworkUtil.getBundle(SessionFactory.class) );
// Some "boot time" code does still rely on TCCL. "run time" code should all be using
// ClassLoaderService now.
final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader();
LinkedHashSet newTcclDelegates = new LinkedHashSet<>();
newTcclDelegates.add( osgiClassLoader );
newTcclDelegates.add( originalTccl );
final ClassLoader newTccl = new AggregatedClassLoader(
newTcclDelegates, TcclLookupPrecedence.NEVER
);
Thread.currentThread().setContextClassLoader( newTccl );
try {
return buildSessionFactory( requestingBundle, osgiClassLoader );
}
finally {
Thread.currentThread().setContextClassLoader( originalTccl );
}
}
private Object buildSessionFactory(
Bundle requestingBundle,
OsgiClassLoader osgiClassLoader) {
final BootstrapServiceRegistryBuilder bsrBuilder = new BootstrapServiceRegistryBuilder();
bsrBuilder.applyClassLoaderService( new OSGiClassLoaderServiceImpl( osgiClassLoader, osgiServiceUtil ) );
final Integrator[] integrators = osgiServiceUtil.getServiceImpls( Integrator.class );
for ( Integrator integrator : integrators ) {
bsrBuilder.applyIntegrator( integrator );
}
final StrategyRegistrationProvider[] strategyRegistrationProviders
= osgiServiceUtil.getServiceImpls( StrategyRegistrationProvider.class );
for ( StrategyRegistrationProvider strategyRegistrationProvider : strategyRegistrationProviders ) {
bsrBuilder.applyStrategySelectors( strategyRegistrationProvider );
}
final BootstrapServiceRegistry bsr = bsrBuilder.build();
final StandardServiceRegistryBuilder ssrBuilder = new StandardServiceRegistryBuilder( bsr );
// Allow bundles to put the config file somewhere other than the root level.
final BundleWiring bundleWiring = (BundleWiring) requestingBundle.adapt( BundleWiring.class );
final Collection cfgResources = bundleWiring.listResources(
"/",
"hibernate.cfg.xml",
BundleWiring.LISTRESOURCES_RECURSE
);
if (cfgResources.size() == 0) {
ssrBuilder.configure();
}
else {
if (cfgResources.size() > 1) {
LOG.warn( "Multiple hibernate.cfg.xml files found in the persistence bundle. Using the first one discovered." );
}
String cfgResource = "/" + cfgResources.iterator().next();
ssrBuilder.configure( cfgResource );
}
ssrBuilder.applySetting( AvailableSettings.JTA_PLATFORM, osgiJtaPlatform );
final StandardServiceRegistry ssr = ssrBuilder.build();
final MetadataBuilder metadataBuilder = new MetadataSources( ssr ).getMetadataBuilder();
final TypeContributor[] typeContributors = osgiServiceUtil.getServiceImpls( TypeContributor.class );
for ( TypeContributor typeContributor : typeContributors ) {
metadataBuilder.applyTypes( typeContributor );
}
return metadataBuilder.build().buildSessionFactory();
}
@Override
public void ungetService(Bundle requestingBundle, ServiceRegistration registration, Object service) {
((SessionFactory) service).close();
}
}