
org.hibernate.search.engine.service.impl.StandardServiceManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-search-engine Show documentation
Show all versions of hibernate-search-engine Show documentation
Core of the Object/Lucene mapper, query engine and index management
/*
* Hibernate Search, full-text search for your domain model
*
* 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.search.engine.service.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.search.cfg.spi.SearchConfiguration;
import org.hibernate.search.engine.service.classloading.spi.ClassLoaderService;
import org.hibernate.search.engine.service.spi.Service;
import org.hibernate.search.engine.service.spi.ServiceManager;
import org.hibernate.search.engine.service.spi.ServiceReference;
import org.hibernate.search.engine.service.spi.Startable;
import org.hibernate.search.engine.service.spi.Stoppable;
import org.hibernate.search.spi.BuildContext;
import org.hibernate.search.util.StringHelper;
import org.hibernate.search.util.impl.ClassLoaderHelper;
import org.hibernate.search.util.logging.impl.Log;
import org.hibernate.search.util.logging.impl.LoggerFactory;
/**
* Default implementation of the {@code ServiceManager} interface.
*
* @author Emmanuel Bernard
* @author Sanne Grinovero
* @author Hardy Ferentschik
*/
public class StandardServiceManager implements ServiceManager {
private static final Log log = LoggerFactory.make();
private final Properties properties;
private final BuildContext buildContext;
private final ConcurrentHashMap, ServiceWrapper>> cachedServices = new ConcurrentHashMap, ServiceWrapper>>();
private final Map, Object> providedServices;
private final Map, String> defaultServices;
private final ClassLoaderService classloaderService;
private volatile boolean allServicesReleased = false;
public StandardServiceManager(SearchConfiguration searchConfiguration, BuildContext buildContext) {
this( searchConfiguration, buildContext, Collections., String>emptyMap() );
}
public StandardServiceManager(SearchConfiguration searchConfiguration,
BuildContext buildContext,
Map, String> defaultServices) {
this.buildContext = buildContext;
this.properties = searchConfiguration.getProperties();
this.providedServices = createProvidedServices( searchConfiguration );
this.defaultServices = defaultServices;
this.classloaderService = searchConfiguration.getClassLoaderService();
}
@Override
@SuppressWarnings("unchecked")
public S requestService(Class serviceRole) {
if ( serviceRole == null ) {
throw new IllegalArgumentException( "'null' is not a valid service role" );
}
if ( allServicesReleased ) {
throw log.serviceRequestedAfterReleasedAllWasCalled();
}
// provided services have priority over managed services
if ( providedServices.containsKey( serviceRole ) ) {
return (S) providedServices.get( serviceRole );
}
ServiceWrapper wrapper = (ServiceWrapper) cachedServices.get( serviceRole );
if ( wrapper == null ) {
wrapper = createAndCacheWrapper( serviceRole );
}
wrapper.startVirtual();
return wrapper.getService();
}
@Override
public ServiceReference requestReference(Class serviceRole) {
return new ServiceReference<>( this, serviceRole );
}
@Override
public void releaseService(Class serviceRole) {
if ( serviceRole == null ) {
throw new IllegalArgumentException( "'null' is not a valid service role" );
}
if ( providedServices.containsKey( serviceRole ) ) {
return;
}
ServiceWrapper wrapper = cachedServices.get( serviceRole );
if ( wrapper != null ) {
wrapper.stopVirtual();
}
}
@Override
public void releaseAllServices() {
for ( ServiceWrapper wrapper : cachedServices.values() ) {
wrapper.ensureStopped();
}
allServicesReleased = true;
}
private Map, Object> createProvidedServices(SearchConfiguration searchConfiguration) {
Map, Object> tmpServices = new HashMap, Object>(
searchConfiguration.getProvidedServices()
);
if ( tmpServices.containsKey( ClassLoaderService.class ) ) {
throw log.classLoaderServiceContainedInProvidedServicesException();
}
else {
tmpServices.put( ClassLoaderService.class, searchConfiguration.getClassLoaderService() );
}
return Collections.unmodifiableMap( tmpServices );
}
/**
* The 'synchronized' is necessary to avoid loading the same service in parallel: enumerating service
* implementations is not threadsafe when delegating to Hibernate ORM's org.hibernate.boot.registry.classloading.spi.ClassLoaderService
*/
private synchronized ServiceWrapper createAndCacheWrapper(Class serviceRole) {
//Check again, for concurrent usage:
ServiceWrapper existingWrapper = (ServiceWrapper) cachedServices.get( serviceRole );
if ( existingWrapper != null ) {
return existingWrapper;
}
Set services = new HashSet<>();
for ( S service : requestService( ClassLoaderService.class ).loadJavaServices( serviceRole ) ) {
services.add( service );
}
if ( services.size() == 0 ) {
tryLoadingDefaultService( serviceRole, services );
}
else if ( services.size() > 1 ) {
throw log.getMultipleServiceImplementationsException(
serviceRole.toString(),
StringHelper.join( services, "," )
);
}
S service = services.iterator().next();
ServiceWrapper wrapper = new ServiceWrapper( service, serviceRole, buildContext );
@SuppressWarnings("unchecked")
ServiceWrapper previousWrapper = (ServiceWrapper) cachedServices.putIfAbsent( serviceRole, wrapper );
if ( previousWrapper != null ) {
wrapper = previousWrapper;
}
return wrapper;
}
private void tryLoadingDefaultService(Class serviceRole, Set services) {
// there is no loadable service. Check whether we have a default one we can instantiate
if ( defaultServices.containsKey( serviceRole ) ) {
S service = ClassLoaderHelper.instanceFromName(
serviceRole,
defaultServices.get( serviceRole ),
"default service",
this
);
services.add( service );
}
else {
throw log.getNoServiceImplementationFoundException( serviceRole.toString() );
}
}
private class ServiceWrapper {
private final S service;
private final BuildContext context;
private final Class serviceClass;
private int userCounter = 0;
private ServiceStatus status = ServiceStatus.STOPPED;
ServiceWrapper(S service, Class serviceClass, BuildContext context) {
this.service = service;
this.context = context;
this.serviceClass = serviceClass;
}
synchronized S getService() {
if ( status != ServiceStatus.RUNNING ) {
stateExpectedFailure();
}
return service;
}
synchronized void startVirtual() {
int previousValue = userCounter;
userCounter++;
if ( previousValue == 0 ) {
if ( status != ServiceStatus.STOPPED ) {
stateExpectedFailure();
}
startService( service );
}
if ( status != ServiceStatus.RUNNING ) {
//Could happen on circular dependencies
stateExpectedFailure();
}
}
synchronized void stopVirtual() {
userCounter--;
if ( userCounter == 0 ) {
if ( status != ServiceStatus.RUNNING ) {
stateExpectedFailure();
}
stopAndRemoveFromCache();
}
else if ( status != ServiceStatus.RUNNING ) {
//Could happen on circular dependencies
stateExpectedFailure();
}
}
synchronized void ensureStopped() {
if ( status != ServiceStatus.STOPPED ) {
log.serviceProviderNotReleased( serviceClass );
stopAndRemoveFromCache();
}
}
private void stopAndRemoveFromCache() {
status = ServiceStatus.STOPPING;
try {
if ( service instanceof Stoppable ) {
( (Stoppable) service ).stop();
}
}
catch (Exception e) {
log.stopServiceFailed( serviceClass, e );
}
finally {
status = ServiceStatus.STOPPED;
cachedServices.remove( serviceClass );
}
}
private void startService(final S service) {
status = ServiceStatus.STARTING;
if ( service instanceof Startable ) {
( (Startable) service ).start( properties, context );
}
status = ServiceStatus.RUNNING;
}
private void stateExpectedFailure() {
throw log.getUnexpectedServiceStatusException( status.name(), service.toString() );
}
}
private enum ServiceStatus {
RUNNING, STOPPED, STARTING, STOPPING
}
@Override
public ClassLoaderService getClassLoaderService() {
return classloaderService;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy