<?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="events" xmlns="http://docbook.org/ns/docbook">
    <title>Interceptors and events</title>

    <para>
        It is useful for the application to react to certain events that occur
        inside Hibernate. This allows for the implementation of generic 
        functionality and the extension of Hibernate functionality.
    </para>

    <section xml:id="objectstate-interceptors" revision="3">
        <title>Interceptors</title>

        <para>
            The <literal>Interceptor</literal> interface provides callbacks from the session to the 
            application, allowing the application to inspect and/or manipulate properties of a
            persistent object before it is saved, updated, deleted or loaded. One 
            possible use for this is to track auditing information. For example, the following 
            <literal>Interceptor</literal> automatically sets the  <literal>createTimestamp</literal> 
            when an <literal>Auditable</literal> is created and updates the 
            <literal>lastUpdateTimestamp</literal> property when an <literal>Auditable</literal> is 
            updated.
        </para>

        <para>
            You can either implement <literal>Interceptor</literal> directly or extend
            <literal>EmptyInterceptor</literal>.
        </para>

        <programlisting role="JAVA"><![CDATA[package org.hibernate.test;

import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;

import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;

public class AuditInterceptor extends EmptyInterceptor {

    private int updates;
    private int creates;
    private int loads;

    public void onDelete(Object entity,
                         Serializable id,
                         Object[] state,
                         String[] propertyNames,
                         Type[] types) {
        // do nothing
    }

    public boolean onFlushDirty(Object entity,
                                Serializable id,
                                Object[] currentState,
                                Object[] previousState,
                                String[] propertyNames,
                                Type[] types) {

        if ( entity instanceof Auditable ) {
            updates++;
            for ( int i=0; i < propertyNames.length; i++ ) {
                if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
                    currentState[i] = new Date();
                    return true;
                }
            }
        }
        return false;
    }

    public boolean onLoad(Object entity,
                          Serializable id,
                          Object[] state,
                          String[] propertyNames,
                          Type[] types) {
        if ( entity instanceof Auditable ) {
            loads++;
        }
        return false;
    }

    public boolean onSave(Object entity,
                          Serializable id,
                          Object[] state,
                          String[] propertyNames,
                          Type[] types) {

        if ( entity instanceof Auditable ) {
            creates++;
            for ( int i=0; i<propertyNames.length; i++ ) {
                if ( "createTimestamp".equals( propertyNames[i] ) ) {
                    state[i] = new Date();
                    return true;
                }
            }
        }
        return false;
    }

    public void afterTransactionCompletion(Transaction tx) {
        if ( tx.wasCommitted() ) {
            System.out.println("Creations: " + creates + ", Updates: " + updates + "Loads: " + loads);
        }
        updates=0;
        creates=0;
        loads=0;
    }

}]]></programlisting>

        <para>
            There are two kinds of inteceptors: <literal>Session</literal>-scoped and
            <literal>SessionFactory</literal>-scoped.
        </para>

        <para>
            A <literal>Session</literal>-scoped interceptor is specified
            when a session is opened.
        </para>

        <programlisting role="JAVA"><![CDATA[Session session = sf.withOptions( new AuditInterceptor() ).openSession();]]></programlisting>

        <para>
            A <literal>SessionFactory</literal>-scoped interceptor is registered with the <literal>Configuration</literal>
            object prior to building the <literal>SessionFactory</literal>.  Unless
            a session is opened explicitly specifying the interceptor to use, the supplied interceptor
            will be applied to all sessions opened from that <literal>SessionFactory</literal>.  <literal>SessionFactory</literal>-scoped
            interceptors must be thread safe. Ensure that you do not store session-specific states, since multiple
            sessions will use this interceptor potentially concurrently.
        </para>
    
        <programlisting role="JAVA"><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>

    </section>

     <section xml:id="objectstate-events" revision="4">
        <title>Event system</title>

        <para>
            If you have to react to particular events in your persistence layer, you can
            also use the Hibernate <emphasis>event</emphasis> architecture. The event
            system can be used in addition, or as a replacement, for interceptors.
        </para>

        <para>
            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.
            However, you are free to implement a customization of one of the listener interfaces
            (i.e., the <literal>LoadEvent</literal> is processed by the registered implementation
            of the <literal>LoadEventListener</literal> interface), in which case their
            implementation would be responsible for processing any <literal>load()</literal> requests
            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 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). Here is an example of a custom load event listener:
        </para>

        <programlisting role="JAVA"><![CDATA[public class MyLoadListener implements LoadEventListener {
    // this is the single method defined by the LoadEventListener interface
    public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
            throws HibernateException {
        if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
            throw MySecurityException("Unauthorized access");
        }
    }
}]]></programlisting>

    </section>

    <section xml: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. 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 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>
            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>
        
       
    </section>

</chapter>

