I like Springframework very much and use it in my day to day business as it eases development in many places. A few days ago I started using Hibernate Envers in order to implement a simple auditing solution for a JPA based application. I was faced with the (typical) requirement that the username of the user currently logged in was written into the audit entity. With Envers you would do that using a org.hibernate.envers.RevisionListener. This is instantiated by the Envers runtime. That listener required access to the FacesContext (it’s a JSF based application) to get the user principal from the HTTP request. Not a big deal in general! Still I wanted to keep that testable and usable in various environments, so an abstraction was needed and finally something would have to inject that into my listener. As I said I’m a fan of Spring and this would be the perfect job and eventually one would propose “yeah, let’s do Spring AOP with AspectJ weaving”. As this application was already in production I didn’t want to add that at this time and was seeking for something more simple but still elegant.

Java as of 1.6 has the concept of SPI and ServiceLoader already built-in. I had never used it before. Here it seemed to perfectly match and it did.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.security.Principal;
public interface UserPrincipalProvider {
  Principal getUserPrincipal();
}

import java.security.Principal;
public class TestUserPrincipalProvider implements UserPrincipalProvider {

  public Principal getUserPrincipal() {
      return new Principal() {
          public String getName() {
              return "TEST";
          }
      };
  }
}

A file named META-INF/services/at.package.auditing.UserPrincipalProvider would be responsible for registering the provider implementation, it only contains one or multiple class names:

1
at.package.auditing.TestUserPrincipalProvider

For getting a runtime instance of such a provider I implemented a helper like

1
2
3
4
public static <T> T lookup(Class<T> clazz) {
  Iterator<T> iterator = ServiceLoader.load(clazz).iterator();
  return iterator.hasNext() ? iterator.next() : null;
}

It is called like ...lookup(UserPrincipalProvider.class).

Comments