HHH-10006 - Document configuration of JndiService;

HHH-10007 - Audit Services chapter in Integrations Guide
This commit is contained in:
Steve Ebersole 2015-07-31 09:21:59 -05:00
parent 2036280fa0
commit 8f4c2d5182
14 changed files with 438 additions and 25 deletions

View File

@ -6,7 +6,8 @@
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<chapter xmlns="http://docbook.org/ns/docbook"
<chapter xml:id="services"
xmlns="http://docbook.org/ns/docbook"
xmlns:xi="http://www.w3.org/2001/XInclude">
<title>Services and Registries</title>
@ -20,7 +21,7 @@
applications can leverage and customize Services and Registries.
</para>
<section>
<section xml:id="services-service">
<title>What is a Service?</title>
<para>
@ -58,7 +59,7 @@
practice. What's interesting is the ServiceRegistry and the pluggable swapping of the different implementors.
</para>
<section>
<section xml:id="services-contracts">
<title>Service contracts</title>
<para>
@ -190,7 +191,7 @@
</section>
<section>
<section xml:id="services-binding">
<title>ServiceBinding</title>
<para>
@ -203,8 +204,16 @@
<para>
There are 2 ways a Service becomes associated (bound) to a ServiceRegistry.
<itemizedlist>
<listitem>the Service can be directly instantiated and then handed to the ServiceRegistry</listitem>
<listitem>a ServiceInitiator can be given to the ServiceRegistry (which the ServiceRegistry will use if and when the Service is needed)</listitem>
<listitem>
<para>
the Service can be directly instantiated and then handed to the ServiceRegistry
</para>
</listitem>
<listitem>
<para>
a ServiceInitiator can be given to the ServiceRegistry (which the ServiceRegistry will use if and when the Service is needed)
</para>
</listitem>
</itemizedlist>
ServiceRegistry implementations register bindings through calls to the overloaded
<methodname>org.hibernate.service.internal.AbstractServiceRegistryImpl#createServiceBinding</methodname>
@ -217,7 +226,7 @@
</section>
<section>
<section xml:id="services-registry-types">
<title>Types of ServiceRegistries</title>
<para>
@ -225,7 +234,7 @@
type is a specialization for the purpose of type-safety, but they add no new functionality.
</para>
<section>
<section xml:id="services-registry-boot">
<title>BootstrapServiceRegistry</title>
<para>
@ -268,9 +277,22 @@
<para>
The specific capabilities exposed on this service include:
<itemizedlist>
<listitem>Locating Class references by name. This includes application classes as well as "integration" classes.</listitem>
<listitem>Locating resources (properties files, xml files, etc) as "classpath resources"</listitem>
<listitem>Interacting with <classname>java.util.ServiceLoader</classname>, Java's own service provider discovery mechanism</listitem>
<listitem>
<para>
Locating Class references by name. This includes application classes as well as "integration" classes.
</para>
</listitem>
<listitem>
<para>
Locating resources (properties files, xml files, etc) as "classpath resources"
</para>
</listitem>
<listitem>
<para>
Interacting with <classname>java.util.ServiceLoader</classname>, Java's own service
provider discovery mechanism
</para>
</listitem>
</itemizedlist>
</para>
</section>
@ -339,15 +361,27 @@
The short name mappings in this service can be managed, even by applications and integrators
which can be very powerful. For more information on this aspect, see:
<itemizedlist>
<listitem><methodname>BootstrapServiceRegistryBuilder#applyStrategySelector</methodname></listitem>
<listitem><methodname>BootstrapServiceRegistryBuilder#applyStrategySelectors</methodname></listitem>
<listitem>
<interfacename>org.hibernate.boot.registry.selector.StrategyRegistrationProvider</interfacename>
via ServiceLoader discovery
<para>
<methodname>BootstrapServiceRegistryBuilder#applyStrategySelector</methodname>
</para>
</listitem>
<listitem>
<para>
<methodname>BootstrapServiceRegistryBuilder#applyStrategySelectors</methodname>
</para>
</listitem>
<listitem>
<para>
<interfacename>org.hibernate.boot.registry.selector.StrategyRegistrationProvider</interfacename>
via ServiceLoader discovery
</para>
</listitem>
<listitem>
<para>
<methodname>StrategySelector#registerStrategyImplementor`</methodname> /
<methodname>StrategySelector#unRegisterStrategyImplementor</methodname>
</para>
</listitem>
</itemizedlist>
</para>
@ -355,7 +389,7 @@
</section>
<section>
<section xml:id="services-registry-standard">
<title>StandardServiceRegistry</title>
<para>
@ -383,12 +417,16 @@
exclusive) roles:
<itemizedlist>
<listitem>
<para>
<interfacename>org.hibernate.engine.jdbc.connections.spi.ConnectionProvider</interfacename> -
provides Connections in normal environments
</para>
</listitem>
<listitem>
<para>
<interfacename>org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider</interfacename> -
provides (tenant-specific) Connections in multi-tenant environments
</para>
</listitem>
</itemizedlist>
</para>
@ -453,7 +491,7 @@
</section>
<section>
<section xml:id="services-registry-sf">
<title>SessionFactoryServiceRegistry</title>
<para>
@ -488,8 +526,8 @@
<classname>org.hibernate.event.service.spi.DuplicationStrategy</classname> and its effect on
registration. The basic idea is to tell Hibernate:
<itemizedlist>
<listitem>what makes a listener a duplicate</listitem>
<listitem>how to handle duplicate registrations (error, first wins, last wins)</listitem>
<listitem><para>what makes a listener a duplicate</para></listitem>
<listitem><para>how to handle duplicate registrations (error, first wins, last wins)</para></listitem>
</itemizedlist>
</para>
</section>
@ -520,4 +558,126 @@
</section>
</section>
<section xml:id="services-custom">
<title>Custom Services</title>
<para>
So far we have focused on the Hibernate provided services. But applications and integrations
can provide their own services as well, either
<itemizedlist>
<listitem>
<para>providing a new implementation of a standard service (overriding)</para>
</listitem>
<listitem>
<para>providing a whole new service role (extending)</para>
</listitem>
</itemizedlist>
</para>
<section xml:id="services-overriding">
<title>Custom Service Implementations (overriding)</title>
<para>
We discussed swappability of service implementations above. Lets look at an example in practice.
For the sake of illustration, lets say that we have developed a new ConnectionProvider integrating
with the wonderful new latest-and-greatest connection pooling library. Let's look at the steps
necessary to make that happen.
</para>
<para>
The first step is to develop the actual integration by implementing the ConnectionProvider contract.
</para>
<example>
<title>Custom ConnectionProvider implementation</title>
<programlisting role="JAVA"><xi:include href="extras/override/LatestAndGreatestConnectionProviderImpl.java" parse="text" /></programlisting>
</example>
<para>
At this point we have a decision about how to integrate this new ConnectionProvider into Hibernate.
As you might guess, there are multiple ways.
</para>
<para>
As a first option, we might just require that the code bootstrapping the StandardServiceRegistry do
the integration.
</para>
<example>
<title>Overriding service implementation via StandardServiceRegistryBuilder</title>
<programlisting role="JAVA"><xi:include href="extras/override/ex1-direct.java" parse="text" /></programlisting>
</example>
<para>
A second option, if our LatestAndGreatestConnectionProviderImpl should always be used, would be to
provide a <interfacename>org.hibernate.service.spi.ServiceContributor</interfacename> implementation
as well to handle the integration on the users behalf.
</para>
<example>
<title>LatestAndGreatestConnectionProviderImplContributor</title>
<programlisting role="JAVA"><xi:include href="extras/override/ex2-contributor.java" parse="text" /></programlisting>
</example>
<para>
We still need to be able to tell Hibernate to perform this integration for us. To do that we leverage
Java's ServiceLoader. When building the StandardServiceRegistry, Hibernate will look for JDK
service providers of type <interfacename>org.hibernate.service.spi.ServiceContributor</interfacename>
and automatically integrate them. We discussed this behavior above. Here we'd define a classpath
resource named <filename>META-INF/services/org.hibernate.service.spi.ServiceContributor</filename>.
This file will have just a single line naming our impl.
</para>
<example>
<title>META-INF/services/org.hibernate.service.spi.ServiceContributor</title>
<programlisting><xi:include href="extras/override/ex2-meta-inf" parse="text" /></programlisting>
</example>
<para>
A third option, if we simply want to make our LatestAndGreatestConnectionProviderImpl available
as a configuration choice, we would again use a ServiceContributor but in a slightly
different way.
</para>
<example>
<title>LatestAndGreatestConnectionProviderImplContributor</title>
<programlisting role="JAVA"><xi:include href="extras/override/ex3-contributor.java" parse="text" /></programlisting>
</example>
<para>
That all allows the appication to pick our LatestAndGreatestConnectionProviderImpl by a short-name.
</para>
<example>
<title>Custom service short-name</title>
<programlisting role="JAVA"><xi:include href="extras/override/ex3-app.java" parse="text" /></programlisting>
</example>
</section>
<section xml:id="services-extending">
<title>Custom Service Roles (extending)</title>
<para>
We can also have the ServiceRegistry host custom services (completely new Service roles). As an example,
let's say our application publishes Hibernate events to a JMS Topic and that we want to leverage the
Hibernate ServiceRegistry to host a Service representing our publishing of events. So we will expand the
ServiceRegistry to host this completely new Service role for us and manage its lifecycle.
</para>
<example>
<title>The EventPublishingService service role</title>
<programlisting role="JAVA"><xi:include href="extras/extend/EventPublishingService.java" parse="text" /></programlisting>
</example>
<example>
<title>The EventPublishingService implementation</title>
<programlisting role="JAVA"><xi:include href="extras/extend/EventPublishingServiceImpl.java" parse="text" /></programlisting>
</example>
<example>
<title>An alternative EventPublishingService implementation</title>
<programlisting role="JAVA"><xi:include href="extras/extend/EventPublishingServiceImpl.java" parse="text" /></programlisting>
</example>
<para>
Because we have alternative implementations, it is a good idea to develop an initiator as well
that can choose between them at runtime.
</para>
<example>
<title>The EventPublishingServiceInitiator</title>
<programlisting role="JAVA"><xi:include href="extras/extend/EventPublishingServiceInitiator.java" parse="text" /></programlisting>
</example>
<para>
We could have the application register the EventPublishingServiceInitiator with the
StandardServiceRegistryBuilder, but it is much nicer to write a ServiceContributor to handle this
for the application.
</para>
</section>
</section>
</chapter>

View File

@ -0,0 +1,11 @@
public class DisabledEventPublishingServiceImpl implements EventPublishingService {
public static DisabledEventPublishingServiceImpl INSTANCE = new DisabledEventPublishingServiceImpl();
private DisabledEventPublishingServiceImpl() {
}
@Override
public void publish(Event theEvent) {
// nothing to do...
}
}

View File

@ -0,0 +1,3 @@
public interface EventPublishingService extends Service {
public void publish(Event theEvent);
}

View File

@ -0,0 +1,12 @@
public class EventPublishingServiceContributor
implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder builder) {
builder.addInitiator( EventPublishingServiceInitiator.INSTANCE );
// if we wanted to allow other strategies (e.g. a JMS
// Queue publisher) we might also register short names
// here with the StrategySelector. The initiator would
// then need to accept the strategy as a config setting
}
}

View File

@ -0,0 +1,50 @@
public class EventPublishingServiceImpl
implements EventPublishingService,
Configurable,
Startable,
Stoppable,
ServiceRegistryAwareService {
private ServiceRegistryImplementor serviceRegistry;
private String jmsConnectionFactoryName;
private String destinationName;
private Connection jmsConnection;
private Session jmsSession;
private MessageProducer publisher;
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
public void configure(Map configurationValues) {
this.jmsConnectionFactoryName = configurationValues.get( JMS_CONNECTION_FACTORY_NAME_SETTING );
this.destinationName = configurationValues.get( JMS_DESTINATION_NAME_SETTING );
}
@Override
public void start() {
final JndiService jndiService = serviceRegistry.getService( JndiService.class );
final ConnectionFactory jmsConnectionFactory = jndiService.locate( jmsConnectionFactoryName );
this.jmsConnection = jmsConnectionFactory.createConnection();
this.jmsSession = jmsConnection.createSession( true, Session.AUTO_ACKNOWLEDGE );
final Destination destination = jndiService.locate( destinationName );
this.publisher = jmsSession.createProducer( destination );
}
@Override
public void publish(Event theEvent) {
publisher.send( theEvent );
}
@Override
public void stop() {
publisher.close();
jmsSession.close();
jmsConnection.close();
}
}

View File

@ -0,0 +1,22 @@
public class EventPublishingServiceInitiator implements StandardServiceInitiator<EventPublishingService> {
public static EventPublishingServiceInitiator INSTANCE = new EventPublishingServiceInitiator();
public static final String ENABLE_PUBLISHING_SETTING = "com.acme.EventPublishingService.enabled";
@Override
public Class<R> getServiceInitiated() {
return EventPublishingService.class;
}
@Override
public R initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
final boolean enabled = extractBoolean( configurationValues, ENABLE_PUBLISHING_SETTING );
if ( enabled ) {
return new EventPublishingServiceImpl();
}
else {
return DisabledEventPublishingServiceImpl.INSTANCE;
}
}
...
}

View File

@ -0,0 +1,55 @@
import java.lang.Override;
public class LatestAndGreatestConnectionProviderImpl
implements ConnectionProvider, Startable, Stoppable, Configurable {
private LatestAndGreatestPoolBuilder lagPoolBuilder;
private LatestAndGreatestPool lagPool;
private boolean available = false;
@Override
public void configure(Map configurationValues) {
// extract our config from the settings map
lagPoolBuilder = buildBuilder( configurationValues );
}
@Override
public void start() {
// start the underlying pool
lagPool = lagPoolBuilder.buildPool();
available = true;
}
@Override
public void stop() {
available = true;
// stop the underlying pool
lagPool.shutdown();
}
@Override
public Connection getConnection() throws SQLException {
if ( !available ) {
throwException( "LatestAndGreatest ConnectionProvider not available for use" )
}
return lagPool.borrowConnection();
}
@Override
public void closeConnection(Connection conn) throws SQLException {
if ( !available ) {
warn( "LatestAndGreatest ConnectionProvider not available for use" )
}
if ( conn == null ) {
return;
}
lagPool.releaseConnection( conn );
}
...
}

View File

@ -0,0 +1,7 @@
StandardServiceRegistryBuilder builder = ...;
...
builder.addService(
ConnectionProvider.class,
new LatestAndGreatestConnectionProviderImpl()
);
...

View File

@ -0,0 +1,10 @@
public class LatestAndGreatestConnectionProviderImplContributor1
implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
serviceRegistryBuilder.addService(
ConnectionProvider.class,
new LatestAndGreatestConnectionProviderImpl()
);
}
}

View File

@ -0,0 +1 @@
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor1

View File

@ -0,0 +1,4 @@
StandardServiceRegistryBuilder builder = ...;
...
builder.applySetting( "hibernate.connection.provider_class", "lag" );
...

View File

@ -0,0 +1,14 @@
public class LatestAndGreatestConnectionProviderImplContributor1
implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
// here we will register a short-name for our service strategy
StrategySelector selector = serviceRegistryBuilder.getBootstrapServiceRegistry().
.getService( StrategySelector.class );
selector.registerStrategyImplementor(
ConnectionProvider.class,
"lag"
LatestAndGreatestConnectionProviderImpl.class
);
}
}

View File

@ -0,0 +1 @@
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor2

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<chapter xml:id="JNDI.xml"
xml:lang="en"
xmlns="http://docbook.org/ns/docbook">
<title>JNDI</title>
<para>
Hibernate does optionally interact with JNDI on the applications behalf. Generally
it does this when the application:
<itemizedlist>
<listitem>
<para>has asked the SessionFactory be bound to JNDI</para>
</listitem>
<listitem>
<para>has specified a DataSource to use by JNDI name</para>
</listitem>
<listitem>
<para>is using JTA transactions and the JtaPlatform needs to do JNDI lookups for TM, UT, etc</para>
</listitem>
</itemizedlist>
</para>
<para>
All of these JNDI calls route through a single service whose role is
<interfacename>org.hibernate.engine.jndi.spi.JndiService</interfacename>. The standard JndiService
accepts a number of configuration settings
<itemizedlist>
<listitem>
<para>
<literal>hibernate.jndi.class</literal> - names the
<interfacename>javax.naming.InitialContext</interfacename> implementation class to use. See
<constant>javax.naming.Context#INITIAL_CONTEXT_FACTORY</constant>
</para>
</listitem>
<listitem>
<para>
<literal>hibernate.jndi.url</literal> - names the JNDI InitialContext connection url. See
<constant>javax.naming.Context.PROVIDER_URL</constant>
</para>
</listitem>
<listitem>
<para>
Any other settings prefixed with <literal>hibernate.jndi.</literal> will be collected
and passed along to the JNDI provider.
</para>
</listitem>
</itemizedlist>
</para>
<note>
<para>
The standard JndiService assumes that all JNDI calls are relative to the same InitialContext. If your
application uses multiple naming servers for whatever reason, you will need a custom JndiService
implementation to handle those details.
</para>
</note>
</chapter>