<?xml version='1.0' encoding="UTF-8"?>
<!--
  ~ Hibernate, Relational Persistence for Idiomatic Java
  ~
  ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
  ~ indicated by the @author tags or express copyright attribution
  ~ statements applied by the authors.  All third-party contributions are
  ~ distributed under license by Red Hat Middleware LLC.
  ~
  ~ This copyrighted material is made available to anyone wishing to use, modify,
  ~ copy, or redistribute it subject to the terms and conditions of the GNU
  ~ Lesser General Public License, as published by the Free Software Foundation.
  ~
  ~ This program is distributed in the hope that it will be useful,
  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
  ~ for more details.
  ~
  ~ You should have received a copy of the GNU Lesser General Public License
  ~ along with this distribution; if not, write to:
  ~ Free Software Foundation, Inc.
  ~ 51 Franklin Street, Fifth Floor
  ~ Boston, MA  02110-1301  USA
  -->

<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;

]>

<chapter id="events">
    <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 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 using one of the overloaded SessionFactory.openSession()
            methods accepting an <literal>Interceptor</literal>.
        </para>

        <programlisting role="JAVA"><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></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 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 Hibernate3 <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
            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>

        <para>
            The listeners should be considered singletons. This means 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:
        </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>

        <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 
            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.JACCPreDeleteEventListener"/>
<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
<listener type="pre-load" class="org.hibernate.secure.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.
        </para>

        <para>
            Next, while still in <literal>hibernate.cfg.xml</literal>, bind the permissions to roles:
        </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>

</chapter>

