HHH-6657 - Document org.hibernate.integrator.spi.IntegratorService

This commit is contained in:
Steve Ebersole 2012-01-10 13:03:42 -06:00
parent 066fea02c7
commit 0481dad403
4 changed files with 151 additions and 70 deletions

View File

@ -935,7 +935,25 @@
<section id="services-IntegratorService">
<title><interfacename>org.hibernate.integrator.spi.IntegratorService</interfacename></title>
<para>
Coming soon... See <ulink url="https://hibernate.onjira.com/browse/HHH-6657" />
Applications, add-ons and others all need to integrate with Hibernate which used to require
something, usually the application, to coordinate registering the pieces of each integration
needed onm behalf of each integrator. The intent of this service is to allow those integrators
to be discovered and to have them integrate themselves with Hibernate.
</para>
<para>
This service focuses on the discovery aspect. It leverages the Java
<classname>java.util.ServiceLoader</classname> capability provided by the
<interfacename>org.hibernate.service.classloading.spi.ClassLoaderService</interfacename>
in order to discover implementations of the
<interfacename>org.hibernate.integrator.spi.Integrator</interfacename> contract.
Integrators would simply define a file named
<filename>/META-INF/services/org.hibernate.integrator.spi.Integrator</filename> and make it
available on the classpath. <classname>java.util.ServiceLoader</classname> covers the
format of this file in detail, but essentially it list classes by FQN that implement the
<interfacename>org.hibernate.integrator.spi.Integrator</interfacename> one per line.
</para>
<para>
See <xref linkend="integrators"/>
</para>
</section>
</section>
@ -993,4 +1011,42 @@
</para>
</section>
<section id="integrators">
<title>Integrators</title>
<para>
The <interfacename>org.hibernate.integrator.spi.Integrator</interfacename> is intended to provide a simple
means for allowing developers to hook into the process of building a functioning SessionFactory. The
The <interfacename>org.hibernate.integrator.spi.Integrator</interfacename> interface defines 2 methods of
interest: <methodname>integrate</methodname> allows us to hook into the building process;
<methodname>disintegrate</methodname> allows us to hook into a SessionFactory shutting down.
</para>
<note>
<para>
There is a 3rd method defined on <interfacename>org.hibernate.integrator.spi.Integrator</interfacename>,
an overloaded form of <methodname>integrate</methodname> accepting a
<interfacename>org.hibernate.metamodel.source.MetadataImplementor</interfacename> instead of
<classname>org.hibernate.cfg.Configuration</classname>. This form is intended for use with the new
metamodel code scheduled for completion in 5.0
</para>
</note>
<para>
See <xref linkend="services-IntegratorService"/>
</para>
<section id="integrators-uses">
<title>Integrator use-cases</title>
<para>
The main use cases for an <interfacename>org.hibernate.integrator.spi.Integrator</interfacename> right
now are registering event listeners and providing services (see
<interfacename>org.hibernate.integrator.spi.ServiceContributingIntegrator</interfacename>). With 5.0
we plan on expanding that to allow altering the metamodel describing the mapping between object and
relational models.
</para>
<example>
<title>Registering event listeners</title>
<programlisting><xi:include href="extras/register-event-listeners-example.java" xmlns:xi="http://www.w3.org/2001/XInclude" parse="text"/></programlisting>
</example>
</section>
</section>
</chapter>

View File

@ -0,0 +1,23 @@
public class MyIntegrator implements org.hibernate.integrator.spi.Integrator {
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
// As you might expect, an EventListenerRegistry is the thing with which event listeners are registered It is a
// service so we look it up using the service registry
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
// If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an
// implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this
eventListenerRegistry.addDuplicationStrategy( myDuplicationStrategy );
// EventListenerRegistry defines 3 ways to register listeners:
// 1) This form overrides any existing registrations with
eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, myCompleteSetOfListeners );
// 2) This form adds the specified listener(s) to the beginning of the listener chain
eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst );
// 3) This form adds the specified listener(s) to the end of the listener chain
eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast );
}
}

View File

@ -171,15 +171,14 @@ public class AuditInterceptor extends EmptyInterceptor {
<para>
If you have to react to particular events in your persistence layer, you can
also use the Hibernate3 <emphasis>event</emphasis> architecture. The event
also use the Hibernate <emphasis>event</emphasis> architecture. The event
system can be used in addition, or as a replacement, for interceptors.
</para>
<para>
All the methods of the <literal>Session</literal> interface correlate
to an event. You have a <literal>LoadEvent</literal>, a <literal>FlushEvent</literal>, etc.
Consult the XML configuration-file DTD or the <literal>org.hibernate.event</literal>
package for the full list of defined event types. When a request is made of one of
Many methods of the <literal>Session</literal> interface correlate to an event type. The
full range of defined event types is declared as enum values on
<classname>org.hibernate.event.spi.EventType</classname>. When a request is made of one of
these methods, the Hibernate <literal>Session</literal> generates an appropriate
event and passes it to the configured event listeners for that type. Out-of-the-box,
these listeners implement the same processing in which those methods always resulted.
@ -190,19 +189,23 @@ public class AuditInterceptor extends EmptyInterceptor {
made of the <literal>Session</literal>.
</para>
<note>
<para>
See the <citetitle pubwork="book">Hibernate Developer Guide</citetitle> for information on registering
custom event listeners.
</para>
</note>
<para>
The listeners should be considered singletons. This means they are shared between
requests, and should not save any state as instance variables.
The listeners should be considered stateless; they are shared between requests, and should not save any
state as instance variables.
</para>
<para>
A custom listener implements the appropriate interface for the event it wants to
process and/or extend one of the convenience base classes (or even the default event
listeners used by Hibernate out-of-the-box as these are declared non-final for this
purpose). Custom listeners can either be registered programmatically through the
<literal>Configuration</literal> object, or specified in the Hibernate configuration
XML. Declarative configuration through the properties file is not supported. Here is an
example of a custom load event listener:
purpose). Here is an example of a custom load event listener:
</para>
<programlisting role="JAVA"><![CDATA[public class MyLoadListener implements LoadEventListener {
@ -215,80 +218,34 @@ public class AuditInterceptor extends EmptyInterceptor {
}
}]]></programlisting>
<para>
You also need a configuration entry telling Hibernate to use the listener in addition
to the default listener:
</para>
<programlisting role="XML"><![CDATA[<hibernate-configuration>
<session-factory>
...
<event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
Instead, you can register it programmatically:
</para>
<programlisting role="JAVA"><![CDATA[Configuration cfg = new Configuration();
LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
<para>
Listeners registered declaratively cannot share instances. If the same class name is
used in multiple <literal>&lt;listener/&gt;</literal> elements, each reference will
result in a separate instance of that class. If you need to share
listener instances between listener types you must use the programmatic registration
approach.
</para>
<para>
Why implement an interface and define the specific type during configuration? A
listener implementation could implement multiple event listener interfaces. Having the
type additionally defined during registration makes it easier to turn custom listeners on
or off during configuration.
</para>
</section>
<section id="objectstate-decl-security" revision="2">
<title>Hibernate declarative security</title>
<para>
Usually, declarative security in Hibernate applications is managed in a session facade
layer. Hibernate3 allows certain actions to be permissioned via JACC, and authorized
layer. Hibernate allows certain actions to be permissioned via JACC, and authorized
via JAAS. This is an optional functionality that is built on top of the event architecture.
</para>
<para>
First, you must configure the appropriate event listeners, to enable the use of JAAS
authorization.
</para>
<programlisting role="XML"><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.internal.JACCPreDeleteEventListener"/>
<listener type="pre-update" class="org.hibernate.secure.internal.JACCPreUpdateEventListener"/>
<listener type="pre-insert" class="org.hibernate.secure.internal.JACCPreInsertEventListener"/>
<listener type="pre-load" class="org.hibernate.secure.internal.JACCPreLoadEventListener"/>]]></programlisting>
<para>
Note that <literal>&lt;listener type="..." class="..."/&gt;</literal> is shorthand
for <literal>&lt;event type="..."&gt;&lt;listener class="..."/&gt;&lt;/event&gt;</literal>
when there is exactly one listener for a particular event type.
First, you must configure the appropriate event listeners, to enable the use of JACC
authorization. Again, see <citetitle pubwork="book">Hibernate Developer Guide</citetitle>
for the details. Below is an example of an appropriate
<interfacename>org.hibernate.integrator.spi.Integrator</interfacename> implementation for this purpose.
</para>
<programlisting role="JAVA"><xi:include href="../extras/jacc-event-reg-example.java" parse="text" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
Next, while still in <literal>hibernate.cfg.xml</literal>, bind the permissions to roles:
You must also decide how to configure your JACC provider. One option is to tell Hibernate what permissions
to bind to what roles and have it configure the JACC provider. This would be done in the
<literal>hibernate.cfg.xml</literal> file.
</para>
<programlisting role="XML"><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
<para>
The role names are the roles understood by your JACC provider.
</para>
</section>

View File

@ -0,0 +1,45 @@
import org.hibernate.event.service.spi.DuplicationStrategy;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.secure.internal.JACCPreDeleteEventListener;
import org.hibernate.secure.internal.JACCPreInsertEventListener;
import org.hibernate.secure.internal.JACCPreLoadEventListener;
import org.hibernate.secure.internal.JACCPreUpdateEventListener;
import org.hibernate.secure.internal.JACCSecurityListener;
public class JaccEventListenerIntegrator implements Integrator {
private static final DuplicationStrategy JACC_DUPLICATION_STRATEGY = new DuplicationStrategy() {
@Override
public boolean areMatch(Object listener, Object original) {
return listener.getClass().equals( original.getClass() ) &&
JACCSecurityListener.class.isInstance( original );
}
@Override
public Action getAction() {
return Action.KEEP_ORIGINAL;
}
};
@Override
@SuppressWarnings( {"unchecked"})
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
boolean isSecurityEnabled = configuration.getProperties().containsKey( AvailableSettings.JACC_ENABLED );
if ( !isSecurityEnabled ) {
return;
}
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
eventListenerRegistry.addDuplicationStrategy( JACC_DUPLICATION_STRATEGY );
final String jaccContextId = configuration.getProperty( Environment.JACC_CONTEXTID );
eventListenerRegistry.prependListeners( EventType.PRE_DELETE, new JACCPreDeleteEventListener(jaccContextId) );
eventListenerRegistry.prependListeners( EventType.PRE_INSERT, new JACCPreInsertEventListener(jaccContextId) );
eventListenerRegistry.prependListeners( EventType.PRE_UPDATE, new JACCPreUpdateEventListener(jaccContextId) );
eventListenerRegistry.prependListeners( EventType.PRE_LOAD, new JACCPreLoadEventListener(jaccContextId) );
}
}