0

I'm struggling for 2 days long trying to solve this and I don't know what else I can do. My system was working pretty well until the moment I included @Audit in the entities. Of course, I included Hibernate Envers in the .pom file.

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-envers</artifactId>
</dependency>

I already tried these posts:

Hibernare envers Audit with Spring data JPA and Spring Boot

spring-data-envers Hibernate java.lang.NoSuchMethodError: org.hibernate.engine.spi.SessionImplementor.getTransactionCoordinator

Hibernate-envers throwing exception when Deleting entity with a collection using CrudRepository

But unfortunately with no success at all :(

I checked my maven repository and I noticed that it is using hibernate 5.0.12.Final versions of hibernate-core, hibernate-entitymanager and hibernate-envers no matter what version I set in the .pom file.

Here is my .pom file's dependencies:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-envers</artifactId>
  </dependency>
  <dependency>
    <groupId>org.hibernate.javax.persistence</groupId>
    <artifactId>hibernate-jpa-2.1-api</artifactId>
    <version>1.0.0.Final</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jersey</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
  </dependency>
  <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.1.3</version>
  </dependency>
  <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>2.6.1</version><!--$NO-MVN-MAN-VER$-->
  </dependency>

  <dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-core</artifactId>
    <version>1.5.13</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.restdocs</groupId>
    <artifactId>spring-restdocs-mockmvc</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
  </dependency>
</dependencies>

Here it is Listener class

public class BRAuditEnversListener implements RevisionListener {
  protected static final Logger log = Logger.getLogger("com.zerofila.web");

  @Autowired
  private UserService service;

  @Override
  public void newRevision(Object revisionEntity) {        
    BRAuditRevisionEntity customRevisionEntity = (BRAuditRevisionEntity) revisionEntity;
    customRevisionEntity.setUsername( service.getAuthenticatedUser().getEmail() );
  }
}

Revinfo class:

@Entity
@Table(name = "revinfo")
@RevisionEntity(BRAuditEnversListener.class)
public class BRAuditRevisionEntity implements Serializable {
  private static final long serialVersionUID = 1L;

  @RevisionNumber
  private int id;
  @RevisionTimestamp
  private long timestamp;
  private String username;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  @Transient
  public Date getRevisionDate() {
    return new Date(timestamp);
  }

  public long getTimestamp() {
    return timestamp;
  }

  public void setTimestamp(long timestamp) {
    this.timestamp = timestamp;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (!(o instanceof DefaultRevisionEntity)) {
        return false;
    }

    DefaultRevisionEntity that = (DefaultRevisionEntity) o;

    if (id != that.getId()) {
        return false;
    }
    if (timestamp != that.getTimestamp()) {
        return false;
    }

    return true;
  }

  @Override
  public int hashCode() {
    int result;
    result = id;
    result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
    return result;
  }

  @Override
  public String toString() {
    return "DefaultRevisionEntity(id = " + id + ", revisionDate = " + DateFormat.getDateTimeInstance().format(getRevisionDate()) + ")";
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }
}

Web service endpoint which calls the insert method:

    @Override
@RequestMapping(value = "/create", method = RequestMethod.POST)
public ResponseEntity<Local> create(@ApiParam(value = "Local json stream resource", required = true) @Valid @RequestBody Local local) {
    Local created = service.insert(local);

    if (null == created)
        return new ResponseEntity<>(HttpStatus.NOT_MODIFIED);

    return new ResponseEntity<Local>(created, HttpStatus.CREATED);
}

Here it is a method that throws up the exception:

@Transactional
@Override
public Local insert(Local local) {
    return repository.save( local );
}

And finally, the exception (pretty short, btw):

2017-08-26 20:18:52.006 ERROR 2784 --- [nio-8080-exec-4] org.hibernate.AssertionFailure           : HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): java.lang.NullPointerException
2017-08-26 20:18:52.009  WARN 2784 --- [nio-8080-exec-4] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

Do I need to configure something special? Maybe add some annotation? Any idea about what I can do?

  • It is not clear how saving an instance of `Local` triggers an audit event (which then leads to the said exception). Also, the POM file is way too complicated for a proper Spring Boot project (for example, why are start data JPA and JPA API included; they are already imported by Spring Boot starter). You should try implementing auditing with a stock Spring Boot project first (create one at http://start.spring.io), without introducing any dependencies that are irrelevant to auditing. When things work, gradually add other pieces. – manish Aug 27 '17 at 05:59
  • Thank you @manish, I'll check all these. What is curious is that (as I said) "My system was working pretty well until the moment I included @Audit in the entities." and the hibernate-envers as dependency, of course. Bottom line, do you recommend me to exclude these entries in my .pom file: `spring-boot-starter-data-jpa` and `hibernate-jpa-2.1-api` ? By the way, I added the method which calls the insert in the question. Thanks – Leandro Bortolotto Aug 27 '17 at 12:56
  • I would recommend creating a sample project with just `spring-boot-starter` and `hibernate-envers` dependencies and testing audit requirements with that. – manish Aug 27 '17 at 13:22

1 Answers1

0

After a long time (about 3 days) trying and testing and analyzing all the source, I found the error and consequently the solution.

The error was occurring because of this entry:

@Autowired
private UserService service;

For some reason, the service.getAuthenticatedUser() was returning null and then the exception was thrown up.

My solution was to get the authenticated user directly from SecurityContextHolder, like this:

public class BRAuditEnversListener implements RevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {        
        BRAuditRevisionEntity customRevisionEntity = (BRAuditRevisionEntity) revisionEntity;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        customRevisionEntity.setUsername( (String)authentication.getPrincipal() );
    }
}

Now, things are working :)