Announcement Announcement Module
Collapse
No announcement yet.
Roo + Envers Auditing Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Roo + Envers Auditing

    I'd like to use Envers with Roo (and hibernate) to audit changes. Is that possible or will it conflict?

    TIA
    rgds

  • #2
    I integrated Envers and hooked it to the org.hibernate.envers.event.AuditEventListener to insert update and delete, but Envers doesn't seem to get triggered by roo's persist and merge calls.

    I always get this exception:
    org.hibernate.envers.exception.RevisionDoesNotExis tException: There is no revision before or at Tue Nov 15 10:43:46 CET 2011.

    Any help is greatly appreciated, as without a working Envers I'd have to drop roo :-(

    Foo.java (my model)
    Code:
    package net.lilalinux.ls20.model;
    
    import javax.persistence.ManyToOne;
    import javax.persistence.OneToOne;
    
    import org.hibernate.envers.Audited;
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.roo.addon.entity.RooEntity;
    import org.springframework.roo.addon.javabean.RooJavaBean;
    import org.springframework.roo.addon.tostring.RooToString;
    import org.springframework.transaction.annotation.Transactional;
    
    @Transactional
    @Audited
    @Configurable
    @RooJavaBean
    @RooToString
    @RooEntity
    public class Foo {
        private String name;
    }
    TestEnvers.java
    Code:
    package net.lilalinux.ls20.test.model;
    import static org.junit.Assert.*;
    
    import java.util.Date;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    import org.hibernate.envers.AuditReader;
    import org.hibernate.envers.AuditReaderFactory;
    import org.hibernate.envers.Audited;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.mock.staticmock.MockStaticEntityMethods;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.transaction.annotation.Transactional;
    
    import net.lilalinux.ls20.model.Foo;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    /* copy src/main/resources/META-INF/ to src/test/resources/META-INF/ */
    @ContextConfiguration("classpath:**/applicationContext.xml")
    @MockStaticEntityMethods
    @Audited
    @Transactional
    @Configurable
    public class TestEnvers {
    	private EntityManager em;
    
    	@PersistenceContext
    	public void setEntityManager(EntityManager em) {
    		this.em = em;
    	}
    
    	@Test
    	public void testEnvers() {
    		Foo foo = new Foo();
    		foo.setName("Bar");
    		foo.persist();
    
    		try {
    			// do some changes
    			foo.setName("Foo");
    			foo.merge();
    
    			Date after = new Date();
    			AuditReader ar = AuditReaderFactory.get(em);
    			assertTrue(ar.isEntityClassAudited(Foo.class));
    			System.err.println("After:  "
    					+ ar.getRevisionNumberForDate(after));
    		} catch (org.hibernate.envers.exception.RevisionDoesNotExistException e) {
    			System.err.println(e.toString());
    		}
    	
    	
    	}
    }
    persistence.xml
    Code:
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
            <provider>org.hibernate.ejb.HibernatePersistence</provider>
            <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
                <!-- value="create" to build a new database on each run; value="update" to modify an existing database; value="create-drop" means the same as "create" but also drops tables when Hibernate closes; value="validate" makes no changes to the database -->
                <property name="hibernate.hbm2ddl.auto" value="create"/>
                <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
                <property name="hibernate.connection.charSet" value="UTF-8"/>
                <!-- Uncomment the following two properties for JBoss only -->
                <!-- property name="hibernate.validator.apply_to_ddl" value="false" /-->
                <!-- property name="hibernate.validator.autoregister_listeners" value="false" /-->
    <!-- 
    Added by me:
     -->
    			<property name="hibernate.ejb.event.post-insert"
    			          value="org.hibernate.envers.event.AuditEventListener" />
    			<property name="hibernate.ejb.event.post-update"
    			          value="org.hibernate.envers.event.AuditEventListener" />
    			<property name="hibernate.ejb.event.post-delete"
    			          value="org.hibernate.envers.event.AuditEventListener" />
            </properties>
        </persistence-unit>
    </persistence>
    applicationContext.xml
    Code:
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
        <!--
            This will automatically locate any and all property files you have
            within your classpath, provided they fall under the META-INF/spring
            directory. The located property files are parsed and their values can
            then be used within application context files in the form of
            ${propertyKey}.
        -->
        <context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>
        <!--
            Turn on AspectJ @Configurable support. As a result, any time you
            instantiate an object, Spring will attempt to perform dependency
            injection on that object. This occurs for instantiation via the "new"
            keyword, as well as via reflection. This is possible because AspectJ
            is used to "weave" Roo-based applications at compile time. In effect
            this feature allows dependency injection of any object at all in your
            system, which is a very useful feature (without @Configurable you'd
            only be able to dependency inject objects acquired from Spring or
            subsequently presented to a specific Spring dependency injection
            method). Roo applications use this useful feature in a number of
            areas, such as @PersistenceContext injection into entities.
        -->
        <context:spring-configured/>
        <!--
            This declaration will cause Spring to locate every @Component,
            @Repository and @Service in your application. In practical terms this
            allows you to write a POJO and then simply annotate the new POJO as an
            @Service and Spring will automatically detect, instantiate and
            dependency inject your service at startup time. Importantly, you can
            then also have your new service injected into any other class that
            requires it simply by declaring a field for your service inside the
            relying class and Spring will inject it. Note that two exclude filters
            are declared. The first ensures that Spring doesn't spend time
            introspecting Roo-specific ITD aspects. The second ensures Roo doesn't
            instantiate your @Controller classes, as these should be instantiated
            by a web tier application context. Refer to web.xml for more details
            about the web tier application context setup services.
            
            Furthermore, this turns on @Autowired, @PostConstruct etc support. These 
            annotations allow you to use common Spring and Java Enterprise Edition 
            annotations in your classes without needing to do any special configuration. 
            The most commonly used annotation is @Autowired, which instructs Spring to
            dependency inject an object into your class.
        -->
        <context:component-scan base-package="net.lilalinux.ls20">
            <context:exclude-filter expression=".*_Roo_.*" type="regex"/>
            <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
        </context:component-scan>
        <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
            <property name="driverClassName" value="${database.driverClassName}"/>
            <property name="url" value="${database.url}"/>
            <property name="username" value="${database.username}"/>
            <property name="password" value="${database.password}"/>
            <property name="testOnBorrow" value="true"/>
            <property name="testOnReturn" value="true"/>
            <property name="testWhileIdle" value="true"/>
            <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
            <property name="numTestsPerEvictionRun" value="3"/>
            <property name="minEvictableIdleTimeMillis" value="1800000"/>
        </bean>
        <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"/>
        </bean>
    
    <!-- 
    Added by me (only the next line):
     -->
        <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
            
        <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
        <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
            <property name="persistenceUnitName" value="persistenceUnit"/>
            <property name="dataSource" ref="dataSource"/>
        </bean>
    </beans>

    Comment


    • #3
      I actually have it working fine after adding the same configuration you display here. Now I just need to figure out how to query and display previous versions of an entity playing along with Roo (we have a need to do that in our application).

      Using Spring Roo 1.2.0 with @RooJpaActiveRecord entities and the GWT web scaffold.

      Comment


      • #4
        Any updates on this? I'm currently evaluating Roo for a new project and Envers integration would be great.

        Comment


        • #5
          I never had to implement fetching previous versions because the project changed directions, but as I stated earlier, the configuration above works at least for creating an audit trail. Versions are created fine for any create/update/delete.

          Comment

          Working...
          X