11.6. About Using External Services

Any of the pluggable contracts we have seen so far allows for the injection of a service. The most notable example being the DirectoryProvider. The full list is:
  • DirectoryProvider
  • ReaderProvider
  • OptimizerStrategy
  • BackendQueueProcessor
  • Worker
  • ErrorHandler
  • MassIndexerProgressMonitor
Some of these components need to access a service which is either available in the environment or whose lifecycle is bound to the SearchFactory. Sometimes, you even want the same service to be shared amongst several instances of these contract.

11.6.1. Exposing a Service

To expose a service, you need to implement org.hibernate.search.spi.ServiceProvider<T>. T is the type of the service you want to use. Services are retrieved by components via their ServiceProvider class implementation.

11.6.1.1. Managed Services

If your service ought to be started when Hibernate Search starts and stopped when Hibernate Search stops, you can use a managed service. Make sure to properly implement the start and stop methods of ServiceProvider. When the service is requested, the getService method is called.

Example 11.7. Example of ServiceProvider implementation

public class CacheServiceProvider implements ServiceProvider<Cache> {
    private CacheManager manager;

    public void start(Properties properties) {
        //read configuration
        manager = new CacheManager(properties);
    }

    public Cache getService() {
        return manager.getCache(DEFAULT);
    }

    void stop() {
        manager.close();
    }
}

Note

The ServiceProvider implementation must have a no-arg constructor.
To be transparently discoverable, such service should have an accompanying META-INF/services/org.hibernate.search.spi.ServiceProvider whose content list the (various) service provider implementation(s).

Example 11.8. Content of META-INF/services/org.hibernate.search.spi.ServiceProvider

com.acme.infra.hibernate.CacheServiceProvider

11.6.1.2. Provided Services

Alternatively, the service can be provided by the environment bootstrapping Hibernate Search. In this case, the CacheContainer instance is not managed by Hibernate Search and the start/stop methods of its corresponding service provider will not be used.

Note

Provided services have priority over managed services. If a provider service is registered with the same ServiceProvider class as a managed service, the provided service will be used.
The provided services are passed to Hibernate Search via the SearchConfiguration interface (getProvidedServices).

Important

Provided services are used by frameworks controlling the lifecycle of Hibernate Search and not by traditional users.
If a service instance must be retrieved from the environment, use registry services like JNDI and look the service up in the provider.

11.6.2. Using a Service

Many of the pluggable contracts of Hibernate Search can use services. Services are accessible via the BuildContext interface.

Example 11.9. Example of a Directory Provider Using a Cache Service

public CustomDirectoryProvider implements DirectoryProvider<RAMDirectory> {
    private BuildContext context;

    public void initialize(
        String directoryProviderName, 
        Properties properties, 
        BuildContext context) {
        //initialize
        this.context = context;
    }

    public void start() {
        Cache cache = context.requestService( CacheServiceProvider.class );
        //use cache
    }

    public RAMDirectory getDirectory() {
        // use cache
    }

    public stop() {
        //stop services
        context.releaseService( CacheServiceProvider.class );
    } 
}
When you request a service, an instance of the service is served to you. Make sure to then release the service. This is fundamental. Note that the service can be released in the DirectoryProvider.stop method if the DirectoryProvider uses the service during its lifetime or could be released right away of the service is simply used at initialization time.