Skip to content

Interceptors

DuyHai DOAN edited this page Jan 8, 2014 · 11 revisions

Introduction

To have tighter control on the persistence lifecycle of an entity, you can rely on Interceptors. Each interceptor is called upon a lifecycle event and modifies the entity state.

Below is the list of all possible lifecycle events:

  1. PRE_PERSIST
  2. POST_PERSIST
  3. PRE_UPDATE
  4. POST_UPDATE
  5. PRE_REMOVE
  6. POST_REMOVE
  7. POST_LOAD

Below are listed all operations that can be intercepted:

Operation Events
`persist()` PRE_PERSIST/POST_PERSIST
`update()` PRE_UPDATE/POST_UPDATE
`remove()` PRE_REMOVE/POST_REMOVE
`find()` POST_LOAD
`sliceQuery()` POST_LOAD
`typedQuery()` POST_LOAD
`rawTypedQuery()` POST_LOAD

Please note that getProxy() cannot be intercepted by the event POST_LOAD because the proxy object is empty so it is not sensible to have functional logic upon its creation (since all fields are NULL except the primary key)

Usage

To use interceptors, you must provide your own implementation of the interface Interceptor<T>:

public interface Interceptor<T> 
{

	public void onEvent(T entity);

	public List<Event> events();
}

public enum Event 
{
  PRE_PERSIST, POST_PERSIST, PRE_UPDATE, POST_UPDATE, PRE_REMOVE, POST_REMOVE, POST_LOAD;
}

The T onEvent(T entity) method should be implemented to perform functional logic. The whole raw entity (not proxy) is provided as method argument.

The List<Event> events() method defines a list of events this interceptor will be used for

Example:

public class UserInterceptor extends Interceptor<User> 
{

	public void onEvent(User entity) {
		if(entity.getBiography() == null) {
			entity.setBiography("TO DO");
		}
	}

	public List<Event> events() {
		return Arrays.asList(PRE_PERSIST,PRE_UPDATE);
	}
}

The abode UserInterceptor will update the biography to "TO DO" if it is not set before saving the entity.

Configuration

To register your interceptors with Achilles, you should pass them as parameter to the configuration parameter map:

	...
	List<Interceptor<?>> interceptors = Arrays.asList(userInterceptor); 
	configMap.put("achilles.event.interceptors",interceptors);
	...

Gotchas

There are some gotchas when using interceptors:

Null-check.

Indeed when you craft your own CQL query using the typedQuery() API, you may not select all fields to be retrieved. Consequently the entity will have some null fields when the POST_LOAD interceptors are applied. For saftety and to avoid the dreadful NullPointerException, it is strongly recommended to perform null checks in the code of your interceptors.

This recommendation also applies to find() operation since some fields may be null in Cassandra

Primary key

It is absolutely a bad idea to modify the primary key in your interceptors. In most cases there is no sensible reason to do so.

To avoid NullPointerException, Achilles will enforce null check (but not value check) on the primary key after each interceptor invocation.

Batch context

For a batch context, since flushing is done only when endBatch() is called, interceptors invocation is also delayed to flush time. You need to take it into account when designing your interceptor if you don't want any unexpected behavior.

Clarifying example:

	batchPM.startBatch();

	// PRE_PERSIST and POST_PERSIST interceptors detected, if any, but not invoked
	User managedUser =  batchPM.persist(new User(10L));

	managedUser.setAge(24L);

	// PRE_UPDATE and POST_UPDATE interceptors detected, but not invoked
	batchPM.update(managedUser);

	// Flush time, PRE_PERSIST, POST_PERSIST, PRE_UPDATE and POST_UPDATE interceptors are invoked
	batchPM.endBatch();

Home

Clone this wiki locally