Showing posts with label Tutorial. Show all posts
Showing posts with label Tutorial. Show all posts

Sunday, March 8, 2009

Bookmark and Share

This is the third – and for now last – part of my series on Google's dependency injection (DI) framework Guice. The first part showed how to get started with Guice in 15 minutes, while the second installment explained Guice's concept of method interception.

Today I want to show how to integrate Guice with EJB. One might ask, whether that makes sense at all, as EJB 3 provides a DI container and an interceptor framework itself. But these services are only available for beans, which are managed by the container. You can't use DI and method interception for non-managed objects, and even within managed beans only certain objects (such as other session beans or JPA EntityManagers) can be injected.

But luckily Guice comes to the rescue and allows to use DI and method interception in and with arbitrary objects.

The example: Retrieving online weather information

As example for the integration of Guice and EJB in the following an application shall be built, that is able to retrieve the weather information (that is temperature and a textual description of the weather conditions) for a given location, using the freely available Yahoo! Weather RSS Feed.

We will encapsulate the access to the Yahoo! service in a dedicated client class. To allow for mocking the online requests in unit tests, we introduce an interface WeatherService that for now will be implementated by the actual Yahoo! client. The service interface just provides one method, getWeatherCondition():

1 2 3 4 5 6 
@ImplementedBy(WeatherServiceYahooImpl.class) public interface WeatherService {   WeatherCondition getWeatherCondition(String locationCode);  } 

WeatherCondition is a simple model class, that contains the weather information for one location:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 
package de.gmorling.guiceonejb.demo;  public class WeatherCondition {   final String text;   final int temperature;   final String location;   public WeatherCondition(String location, String text, int temperature) {  this.location = location;  this.text = text;  this.temperature = temperature;  }   //getters and setters, equals() and hashCode() ... } 

Now let's build the implementation of the WeatherService, based on the Yahoo! feed:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 
package de.gmorling.guiceonejb.demo;  import java.io.InputStream; import java.net.URL;  import org.jdom.Document; import org.jdom.Element; import org.jdom.Namespace; import org.jdom.input.SAXBuilder;  public class WeatherServiceYahooImpl implements WeatherService {   private final static String address = "http://weather.yahooapis.com/forecastrss?u=c&p=";   @Override  public WeatherCondition getWeatherCondition(String locationCode) {   InputStream inputStream = null;   try {  inputStream = new URL(address + locationCode).openStream();  Document doc = new SAXBuilder().build(inputStream);   Namespace namespace =   Namespace.getNamespace("http://xml.weather.yahoo.com/ns/rss/1.0");   Element channelElement = doc.getRootElement().getChild("channel");  Element locationElement = channelElement.getChild("location", namespace);  Element conditionElement = channelElement.getChild("item").getChild("condition", namespace);   return new WeatherCondition(  locationElement.getAttributeValue("city") + ", " + locationElement.getAttributeValue("country"),  conditionElement.getAttributeValue("text"),  Integer.parseInt(conditionElement.getAttributeValue("temp")));  }   catch (Exception e) {  throw new RuntimeException(e);  }  finally {  if (inputStream != null) {  try { inputStream.close(); } catch (Exception e) {}  }  }  }  } 

The implementation is pretty straight-forward. First we retrieve the weather information from the Yahoo! feed by opening an InputStream to the feed URL. Using the JDOM API we then create a Document object representing the returned RSS structure. From the child elements of that document we can retrieve all the information required to create a new WeatherCondition instance.

Writing a session bean

Having implemented the weather service, let's now create a stateless session bean, that uses this service. The bean in the following example just delegates all calls to the actual service, but of course one could imagine a more complex bean with its own logic etc. First we have to create the local interface for the session bean. Thanks to EJB 3 this is just a POJI (plain old java interface) annotated with @Local:

1 2 3 4 5 6 7 8 9 10 11 12 
package de.gmorling.guiceonejb.demo.ejb;  import javax.ejb.Local;  import de.gmorling.guiceonejb.demo.WeatherCondition;  @Local public interface WeatherInformationLocal {   WeatherCondition getWeatherCondition(String locationCode);  } 

Next we create the session bean by implementing the local interface:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 
package de.gmorling.guiceonejb.demo.ejb;  import javax.ejb.Stateless;  import com.google.inject.Inject;  import de.gmorling.guiceonejb.UsesGuice; import de.gmorling.guiceonejb.demo.WeatherCondition; import de.gmorling.guiceonejb.demo.WeatherService;  @Stateless @UsesGuice public class WeatherInformationBean implements WeatherInformationLocal {   @Inject  private WeatherService weatherService;   @Override  public WeatherCondition getWeatherCondition(String locationCode) {  return weatherService.getWeatherCondition(locationCode);  }  } 

To create a stateless session bean, we just annotate the class with the @Stateless annotation. Besides that, two things here are remarkable:

  • The injection of a WeatherService instance using Guice's @Inject annotation
  • The session bean being annotated with a new annotation, @UsesGuice

Bringing together Guice and EJB

How comes, that the weatherService field is obviously populated by Guice? This is realized by using an EJB interceptor (very similar to Guice's method interceptors, as described in the second installment of this series), that hooks into each call of the session bean's methods by the container and injects all fields annotated with @Inject. Let's have a look at that interceptor:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 
package de.gmorling.guiceonejb;  import javax.ejb.EJB; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext;  import com.google.inject.Injector;  public class GuiceInterceptor {   @EJB  private GuiceInjectorHolder injectorHolder;   @AroundInvoke  public Object injectByGuice(InvocationContext ctx) throws Exception {   if (ctx.getTarget().getClass().isAnnotationPresent(UsesGuice.class)) {   Injector injector = injectorHolder.getInjector();  injector.injectMembers(ctx.getTarget());  }   return ctx.proceed();  } } 

By annotating the method injectByGuice() with @AroundInvoke, we specify, that the interceptor shall be called for each method invocation on the target bean by the container. Within the method, we first check, whether the target bean is annotated with @UsesGuice. If that's the case, we obtain a Guice injector and use it to perform the dependency injection on the target bean. Finally we continue with the intercepted call by invoking the proceed() method of the passed invocation context.

Having implemented the interceptor, we must register it with the EJB container and specify, for which session beans it shall be invoked. As we want to enable Guice DI in basically any session bean, we do this by using the ejb-jar.xml deployment descriptor as follows (another option would be to annotate only selected beans with @Interceptors):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar   xmlns="http://java.sun.com/xml/ns/javaee"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"  version="3.0">   <interceptors>  <interceptor>  <interceptor-class>de.gmorling.guiceonejb.GuiceInterceptor</interceptor-class>  </interceptor>  </interceptors>  <assembly-descriptor>  <interceptor-binding>  <ejb-name>*</ejb-name>  <interceptor-class>de.gmorling.guiceonejb.GuiceInterceptor</interceptor-class>  </interceptor-binding>  </assembly-descriptor> </ejb-jar> 

One question is still unanswered: where did we retrieve the Guice injector from? We got it from another session bean, called GuiceInjectorHolder (which we get injected by the EJB container, as EJB – in contrast to Guice – allows DI also within interceptors).

The reason for this is, that we want to use the same Guice injector throughout the entire application. That's required in order to support Guice's singleton scope, that allows to specify objects to be singletons – but only within the context of one and the same injector instance.

This makes the Guice injector for our purposes actually a singleton object itself. Singletons used to be rather problematic in traditional JEE applications (mostly they are realized using static fields), but the upcoming EJB version 3.1 simplifies that matter by introducing the concept of singleton beans. That's very useful for our GuiceInjectorHolder, which looks as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 
//interface package de.gmorling.guiceonejb;  import com.google.inject.Injector;  public interface GuiceInjectorHolder {   Injector getInjector();   void setInjector(Injector injector);  }  //implementation package de.gmorling.guiceonejb;  import javax.ejb.Singleton; import javax.interceptor.ExcludeDefaultInterceptors;  import com.google.inject.Guice; import com.google.inject.Injector;  @Singleton @ExcludeDefaultInterceptors public class GuiceInjectorHolderBean implements GuiceInjectorHolder {   private Injector injector = Guice.createInjector();   @Override  public Injector getInjector() {  return injector;  }   @Override  public void setInjector(Injector injector) {  this.injector = injector;  }  } 

All we have to do is to annotate the bean implementation with @Singleton. This annotation is already supported by GlassFish V3 as well as by Apache OpenEJB, which we will use for unit testing purposes later on.

Annotating the bean with @ExcludeDefaultInterceptors ensures, that the GuiceInterceptor won't be applied to that bean, as there are no dependencies to be injected here. The setInjector() method allows to set the injector to be used, by default a standard injector without any configuration modules will be used.

Writing a unit test

That completes the Guice-EJB integration layer (comprising the @UsesGuice annotation, the GuiceInterceptor and the GuiceInjectorHolder bean). So it's time to give it a try in a unit test.

For unit testing our EJB application we can use the embeddable OpenEJB container. It allows to launch a fully functional EJB container within a unit test, without the need to install an application server, to perform a deployment before the test etc. as shown below:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 
package de.gmorling.guiceonejb;  import static org.junit.Assert.*;  import java.util.Properties;  import javax.naming.Context; import javax.naming.InitialContext;  import org.junit.After; import org.junit.Before; import org.junit.Test;  import com.google.inject.Guice;  import de.gmorling.guiceonejb.demo.WeatherCondition; import de.gmorling.guiceonejb.demo.ejb.WeatherInformationLocal;  public class GuiceInterceptorTest {   private InitialContext context;   @Before  public void setUp() throws Exception {  Properties properties = new Properties();   properties.setProperty(  Context.INITIAL_CONTEXT_FACTORY,  "org.apache.openejb.client.LocalInitialContextFactory");  properties.setProperty(  "openejb.deployments.classpath.include",  ".*guiceonejb/target/classes.*");  properties.setProperty(  "openejb.embedded.initialcontext.close",  "destroy");   context = new InitialContext(properties);  }   @Test  public void getWeatherInformationFromYahoo() throws Exception {   WeatherInformationLocal bean =   (WeatherInformationLocal) context.lookup("WeatherInformationBeanLocal");   assertNotNull(bean);   WeatherCondition weatherCondition = bean.getWeatherCondition("GMXX0025");  assertEquals("Dresden, GM", weatherCondition.getLocation());  }   @After  public void tearDown() throws Exception {  context.close();  } } 

All we have to do is to setup an InitialContext as shown within the setUp() method. This context can be used to retrieve an instance of the WeatherInformationBean. The call to getWeatherCondition() will be intercepted by the container, the WeatherService implementation based on the Yahoo! feed will be injected by Guice and the weather information for the specified location code will be retrieved.

Creating a mock implementation

Calling external services from within a unit test is mostly not a good idea (test success is subject to the service's availability, it will make the test running longer etc.). Therefore we will create a mock implementation of the WeatherService, that can be used in the unit test:

1 2 3 4 5 6 7 8 9 10 11 12 13 
package de.gmorling.guiceonejb;  import de.gmorling.guiceonejb.demo.WeatherCondition; import de.gmorling.guiceonejb.demo.WeatherService;  public class WeatherServiceMockImpl implements WeatherService {   public WeatherCondition getWeatherCondition(String locationCode) {   return new WeatherCondition("Dresden", "Rainy", 5);  }  } 

To bind the WeatherService interface to the mock implementation instead of the real one, we have to write a Guice configuration module as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 
package de.gmorling.guiceonejb;  import com.google.inject.AbstractModule;  import de.gmorling.guiceonejb.demo.WeatherService;  public class TestModule extends AbstractModule {   public void configure() {   bind(WeatherService.class).to(WeatherServiceMockImpl.class);  } } 

That configuration module can now be used to create a new Guice injector, that we'll set into the GuiceInjectorHolder:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
... @Test public void getWeatherInformationFromMock() throws Exception {   GuiceInjectorHolder holder =   (GuiceInjectorHolder) context.lookup("GuiceInjectorHolderBeanLocal");   assertNotNull(holder);  holder.setInjector(Guice.createInjector(new TestModule()));   WeatherInformationLocal bean =   (WeatherInformationLocal) context.lookup("WeatherInformationBeanLocal");   assertNotNull(bean);   WeatherCondition weatherCondition = bean.getWeatherCondition("GMXX0025");  assertEquals(new WeatherCondition("Dresden", "Rainy", 5), weatherCondition); } ... 

Guice will now inject the mock WeatherService implementation into the WeatherInformationBean, which makes the test independent from the real service at Yahoo!

Summary

That concludes the third and last part of my Guice tutorial. In this installment I showed, that is rather easy to use Guice within an EJB application (and that it is very simple to retrieve weather information from Yahoo! ;-)).

Guice is a great addition to EJB as it allows dependency injection and method interception also for objects not managed by the EJB container. The complete source code of this tutorial can be downloaded here. As usually I'd be happy on any feedback and comments.

Wednesday, January 7, 2009

Bookmark and Share

In my previous posting, I gave a short overview on the very basic features of Google's dependency injection container Guice. Today, I want to delve a bit more into Guice's means of aspect oriented programming (AOP) by musing on its abilities for method interception.

AOP is a common way for implementing cross-cutting concerns of an application, such as logging/tracing, transaction handling or permission checking.

As opposed to fully-fledged AOP solutions such as AspectJ, Guice's AOP facilities are limited to simple method interceptors. But to be fair, this should do the trick for most scenarios, while at the same time keeping the mechanism very easy to understand and use.

Create a new project

As in the first part of this tutorial, Maven shall be used as build tool. Unfortunately, one required 3rd party library – the AOP alliance API – is not declared as dependency in Guice's Maven POM. Therefore, we have to declare this dependency in our own pom.xml, which should look as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 
<project   xmlns="http://maven.apache.org/POM/4.0.0"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>org.gm</groupId>  <artifactId>guice-aop</artifactId>  <packaging>jar</packaging>  <version>1.0-SNAPSHOT</version>  <name>guice-aop</name>  <dependencies>  <dependency>  <groupId>junit</groupId>  <artifactId>junit</artifactId>  <version>4.5</version>  <scope>test</scope>  </dependency>  <dependency>  <groupId>com.google.code.guice</groupId>  <artifactId>guice</artifactId>  <version>1.0</version>  </dependency>  <dependency>  <groupId>aopalliance</groupId>  <artifactId>aopalliance</artifactId>  <version>1.0</version>  </dependency>  </dependencies>  <build>  <plugins>  <plugin>  <artifactId>maven-compiler-plugin</artifactId>  <version>RELEASE</version>  <configuration>  <source>1.5</source>  <target>1.5</target>  </configuration>  </plugin>  </plugins>  </build> </project> 

As exemplary business service for this tutorial let's now create a little video rental shop, where videos from the stock can be rented and new videos can be registered:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
package org.gm.guicetutorial;  public class VideoRental {   public boolean rentMovie(long movieId) {   System.out.println(  String.format("Movie %s rented.", movieId));   return true;  }   public boolean registerNewMovie(String name) {   System.out.println(  String.format("New movie \"%s\" registered.", name));   return true;  } } 

Create a simple interceptor

Now it's time to create our first method interceptor. Since Guice uses AOP alliance conform method interceptors, we have to implement the interface MethodInterceptor to do so. The following snippet shows the canonical example, a simple tracing interceptor:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 
package org.gm.guicetutorial.interceptors;  import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation;  public class TracingInterceptor implements MethodInterceptor {   @Override  public Object invoke(MethodInvocation invocation) throws Throwable {   long start = System.nanoTime();   try {  return invocation.proceed();  }   finally {  System.out.println(  String.format(  "Invocation of method %s() with parameters %s took %.1f ms.",  invocation.getMethod().getName(),  Arrays.toString(invocation.getArguments()),  (System.nanoTime() - start) / 1000000.0));  }  }  } 

The method invoke() will be called by the container for each intercepted method. The passed MethodInvocation object contains an instance of java.lang.reflect.Method to represent the called method, a reference to the object on which the method was called as well as the parameters of the invocation.

To execute the intercepted method call, the proceed() method of the passed invocation has to be invoked. Before and afterwards, one can do whatever one wants with the invocation, including a modification of the parameters or the return value or supressing the actual call at all. For the TracingInterceptor, we just log the called method together with the duration of the call.

Now we have to tell Guice, to which method invocations the interceptor shall be attached. As with the binding in the tutorial's first installment, we do this using a configuration module:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
package org.gm.guicetutorial;  import static com.google.inject.matcher.Matchers.*; import org.gm.guicetutorial.interceptors.TracingInterceptor; import com.google.inject.AbstractModule;  public class ExampleModule extends AbstractModule {   public void configure() {   bindInterceptor(  subclassesOf(VideoRental.class),  any(),  new TracingInterceptor());  } } 

By calling bindInterceptor() we specify, which interceptor (TracingInterceptor) shall be attached to which methods (any()) of which classes (subclassesOf(VideoRental.class)). The latter two are so-called matchers, which allow a type-safe and refactoring-safe way of linking interceptors and the calls to which these shall be applied. More information on the matchers, that Guice provides, can be found in the JavaDoc of com.google.inject.matcher.Matchers.

Finally we create a test. Note the use of Injector.injectMembers() which allows for injection of dependencies into objects, which are not instantiated under the reign of Guice, as it is the case with JUnit test classes.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 
package org.gm.guicetutorial;  import static org.junit.Assert.*;  import org.junit.Before; import org.junit.Test;  import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector;  public class VideoRentalTest {   @Inject  private VideoRental videoRental;   @Before  public void setup() {   Injector injector = Guice.createInjector(new ExampleModule());  injector.injectMembers(this);  }   @Test  public void testRentMovie() throws Exception {   assertTrue(videoRental.rentMovie(1));  }   @Test  public void testRegisterNewMovie() throws Exception {   assertTrue(videoRental.registerNewMovie("The Fugitive"));  }  } 

Executing the test should yield in an output similar to the following:

1 2 3 4 
Movie 1 rented. Invocation of method rentMovie([1]) took 10.5 ms. New movie "The Fugitive" registered. Invocation of method registerNewMovie([The Fugitive]) took 0.4 ms. 

A security checking interceptor

As a more advanced use of a method interceptor, let us now create an interceptor, that checks, if a given user is allowed to execute a certain action. The actions that a user is allowed to execute, shall be specified using a role based approach. Which role is required to perform a certain method call, shall be expressed using a custom annotation. The task of the interceptor will be to check, whether the current user has this role.

Note, that in a real-world scenario, one would probably have one level of indirection more by checking on a role's permissions instead of on the role itself, but let's keep things simple for now.

First of all, we need some sort of user manager, which holds the currently logged in user:

1 2 3 4 5 6 7 8 9 
package org.gm.guicetutorial;  public interface UserManager {   void setCurrentUser(User user);   User getCurrentUser();  } 

Our very simple implementation of the user manager looks as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 
package org.gm.guicetutorial;  public class UserManagerImpl implements UserManager {   private User currentUser;   @Override  public void setCurrentUser(User currentUser) {  this.currentUser = currentUser;  }   @Override  public User getCurrentUser() {  return currentUser;  }  } 

Within a multi-user server application, the user manager typically would use some sort of session mechanism to manage multiple users logged in at the same time. On the contrary, within a client application, the user manager might be a singleton object with application scope. For the sake of simplicity, we stick with the latter scenario, which we will realize leveraging Guice's SINGLETON scope as will be shown.

The User entity is a simple model class, which contains a user's name and his/her role(s) in form of a set of roles:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
package org.gm.guicetutorial;  import java.util.Set;  public class User {   private String name;   private Set<Role> roles;   public User(String name, Set<Role> roles) {   super();  this.name = name;  this.roles = roles;  }   public String getName() {  return name;  }   public Set<Role> getRoles() {  return roles;  }   @Override  public String toString() {  return name;  }  } 

To allow for specifying roles in a simple and still safe manner, we model the Role entity as enum:

1 2 3 4 5 6 7 
package org.gm.guicetutorial;  public enum Role {   CUSTOMER, EMPLOYEE;  } 

As mentioned above, it would be nice, to have a custom annotation, that can be used to specify, which role is required for the execution of a certain method.

The next snippet shows a simple blueprint, how such an annotation could look like. For now, the annotation has just one attribute – the standard attribute "value", which holds the name of the required role. By the use of meta annotations we specify further details of the annotation: it shall be usable at methods (@Target(ElementType.METHOD)) and accessible at runtime (@Retention(RetentionPolicy.RUNTIME)).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 
package org.gm.guicetutorial;  import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresRole {   Role value();  } 

Using our new annotation it's real fun to define, which roles are required to execute the methods of the VideoRental class:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
package org.gm.guicetutorial;  import com.google.inject.Inject;  public class VideoRental {   @Inject  UserManager userManager;   @RequiresRole(Role.CUSTOMER)  public boolean rentMovie(long movieId) {   System.out.println(String.format(  "Movie %s rented by user %s.",  movieId,  userManager.getCurrentUser()));   return true;  }   @RequiresRole(Role.EMPLOYEE)  public boolean registerNewMovie(String name) {   System.out.println(String.format(  "New movie \"%s\" registered by user %s.",  name,   userManager.getCurrentUser()));   return true;  } } 

Now let's have a look at the interceptor, that checks, wether the current user has the role required to invoke a certain method.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 
package org.gm.guicetutorial.interceptors;  import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.gm.guicetutorial.RequiresRole; import org.gm.guicetutorial.Role; import org.gm.guicetutorial.UserManager;  import com.google.inject.Inject;  public class RoleValidationInterceptor implements MethodInterceptor {   @Inject  private UserManager userManager;   @Override  public Object invoke(MethodInvocation invocation) throws Throwable {   Role requiredRole =   invocation.getMethod().getAnnotation(RequiresRole.class).value();   if( userManager.getCurrentUser() == null ||  !userManager.getCurrentUser().getRoles().contains(requiredRole)) {   throw new IllegalStateException("User requires role " + requiredRole);  }   return invocation.proceed();  } } 

We get the required role for a method invocation by evaluating the value attribute of the RequiresRole annotation. If this role is not contained within the current user's set of roles, an IllegalStateException is thrown. Otherwise, the intercepted method call is resumed.

As before, some plumbing has to be done now in the binding module:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 
package org.gm.guicetutorial;  import static com.google.inject.matcher.Matchers.*;  import java.util.ArrayList; import java.util.List;  import org.gm.guicetutorial.interceptors.RoleValidationInterceptor;  import com.google.inject.AbstractModule; import com.google.inject.Scopes;  public class ExampleModule extends AbstractModule {   private List<Object> injectees = new ArrayList<Object>();   public void configure() {   bind(UserManager.class).to(UserManagerImpl.class).in(Scopes.SINGLETON);   RoleValidationInterceptor roleValidationInterceptor = new RoleValidationInterceptor();   bindInterceptor(  any(),  annotatedWith(RequiresRole.class),  roleValidationInterceptor);   injectees.add(roleValidationInterceptor);  }   public List<Object> getInjectees() {  return injectees;  }  }  

First, we bind UserManagerImpl to the interface UserManager. By using the SINGLETON scope we ensure, that the same UserManager instance will be returned by the injector each time, so that the UserManger injected into VideoRental as well as into the interceptor is the same.

Secondly, the RoleValidationInterceptor has to be configured. As method matcher we specify annotatedWith(RequiresRole.class), thereby attaching the interceptor only to those methods, which are tagged with our new shiny annotation.

Unfortunately, Guice currently can not inject dependencies into interceptors within a module's configure() method. To circumvent this issue, we let the module return a collection of injectees, that we will process manually, once the injector is created. A more sophisticated way to tackle that problem is shown by Tim Peierl in his blog.

In the upcoming Guice release 2.0 the new method requestInjection() in AbstractModule can be used to specify any injectees, that shall be provided with dependencies upon creation of the injector.

Finally let's build a test for the new role validation mechanism:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 
package org.gm.guicetutorial;  import static org.junit.Assert.*;  import java.util.HashSet;  import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test;  import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector;  public class VideoRentalTest {   @Inject  private VideoRental videoRental;   @Inject  private UserManager userManager;   private static User customer;   private static User employee;   @BeforeClass  public static void setupUsers() {   customer = new User("Peter", EnumSet.of(Role.CUSTOMER));  employee = new User("Bob", EnumSet.of(Role.EMPLOYEE));  }   @Before  public void setup() {   ExampleModule module = new ExampleModule();   Injector injector = Guice.createInjector(module);  for (Object oneInjectee : module.getInjectees()) {  injector.injectMembers(oneInjectee);  }   injector.injectMembers(this);  }   @Test  public void testRentMovieSuccessfully() throws Exception {   userManager.setCurrentUser(customer);  assertTrue(videoRental.rentMovie(1));  }   @Test(expected = IllegalStateException.class)  public void testRentMovieFailing() throws Exception {   userManager.setCurrentUser(employee);  videoRental.rentMovie(1);  }   @Test  public void testRegisterNewMovieSuccessfully() throws Exception {   userManager.setCurrentUser(employee);  assertTrue(videoRental.registerNewMovie("The Fugitive"));  }   @Test(expected = IllegalStateException.class)  public void testRegisterNewMovieFailing() throws Exception {   userManager.setCurrentUser(customer);  assertTrue(videoRental.registerNewMovie("The Fugitive"));  } } 

In the class setup, two users – one customer, one employee – are instantiated. In the test setup, the Guice injector is created, followed by the injection of dependencies into the injectees as returned by the binding module. Four test methods are present, one testing successful execution and one testing failure of execution due to the lack of the required role for each of VideoRental's methods.

This concludes the 2nd part of my tutorial series on Google Guice. As shown, Guice provides a simple but still powerful means of aspect oriented programming. Also advanced scenarios such as security checking using a custom annotation are not very hard to realize. The complete source code can be downloaded here.

In the next installment, I will show how to use Guice within a JEE environment, namely its use together with EJB.

Thursday, January 1, 2009

Bookmark and Share

As I recently played around a bit with the creation of MD5 hashes in Java, I stumbled across an alternative MD5 library called "Fast MD5", which claims to be faster than the classes provided by the JDK. So I downloaded this lib, added it to my class path and started using it by commenting out the references to JDK's class MessageDigest and using Fast MD5's class MD5 instead.

Everything worked out fine, but I was not very satisfied from a software architectural point of view. Instead of toggling referenced classes by commenting and uncommenting code, the proper way to go should be dependency injection (DI), and I thought it would be a great chance to get a grip on Google's award-winning DI framework Guice. There we go ...

Extract the interface

One of DIs basic principles is the separation between the interface of a service and its implementation. So lets extract an interface out of last post's class MD5Helper and make this class become an implementation of that new interface:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
@ImplementedBy(MD5HelperStandardImpl.class) public interface MD5Helper {   String getMD5Hash(String input);  }  public class MD5HelperStandardImpl implements MD5Helper {   @Override  public String getMD5Hash(String input) {   try {  MessageDigest md = MessageDigest.getInstance("MD5");  byte[] messageDigest = md.digest(input.getBytes());   return new BigInteger(1, messageDigest).toString(16);  }  catch (NoSuchAlgorithmException e) {  throw new RuntimeException(e);  }  }  } 

Two things are remarkable:

  • The interface is annotated with Guice's @ImplementedBy annotation, specifying the interface's default implementation
  • The method getMD5Hash() became non-static, as static methods can't be declared within an interface.

May I introduce: The injector

How to use Guice now to get an instance of MD5Helper? Nothing could be easier. Just use Guice's injector as shown below:

1 2 3 4 5 6 7 8 9 10 11 12 13 
public class MD5HelperTest {   @Test  public void testStandardImpl() throws Exception {   Injector injector = Guice.createInjector();  MD5Helper helper = injector.getInstance(MD5Helper.class);   assertEquals(  "5d255e82ae2be1d1d54090bb20351106",  helper.getMD5Hash("Hello guicy world!"));  } } 

Custom bindings

Let's now create another implementation of the MD5Helper interface based upon the mentioned library "Fast MD5":

1 2 3 4 5 6 7 8 9 10 11 
public class MD5HelperFastImpl implements MD5Helper {   @Override  public String getMD5Hash(String input) {   MD5 md5 = new MD5();  md5.Update(input);   return new BigInteger(1, md5.Final()).toString(16);  } } 

How can we make sure to get this implementation, if we request an instance of MD5Helper? Obviously there can be only one default implementation, so there has to be another way to tell Guice which implementation we want.

And indeed there is. All we have to do, is to provide the injector with a so called module, that specifies the binding between the interface and the implementation, as shown below:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 
public class TestModule extends AbstractModule {   public void configure() {  bind(MD5Helper.class).to(MD5HelperFastImpl.class);  } }  public class MD5HelperTest {   ...   @Test  public void testFastImpl() throws Exception {   Injector injector = Guice.createInjector(new TestModule());  MD5Helper helper = injector.getInstance(MD5Helper.class);   assertEquals(  "5d255e82ae2be1d1d54090bb20351106",  helper.getMD5Hash("Hello guicy world!"));  } } 

Finally ... dependency injection!

Now imagine, there was another class which wants to make use of the MD5Helper. This class could make direct use of the injector itself, but actually this would degrade Guice to just some better sort of factory. Rather we can use Guice to inject dependency – actually that's what Guice is all about :-) How to do so? Have a look at the following example:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 
public class PasswordManager {   @Inject  private MD5Helper helper;   private Map passwordHashes;   public PasswordManager() {  passwordHashes = new HashMap();  passwordHashes.put("peter", "5ebe2294ecd0e0f08eab7690d2a6ee69");  }   public boolean passwordMatches(String userName, String enteredPassword) {  return  passwordHashes.containsKey(userName) &&  helper.getMD5Hash(enteredPassword).equals(passwordHashes.get(userName));  } } 

The example shows a simple password manager. It checks the password some user entered by comparing the MD5 hash of that entered password with the saved hash of the user's password.

Disclaimer: This code is just for demonstration purposes, a real implementation would include retrieving the existing hash from some sort of persistent storage and using some salt for hash generation. Generally it is recommendable to represent passwords with character arrays instead of strings, allowing for removing them from memory after processing, as described in JDK 6' Console class.

Back to the example. We make use of Guice's @Inject annotation to provide the PasswordManager with an MD5Helper. If we use Guice now to get an instance of PasswordManager, this dependency will be injected:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 
public class PasswordManagerTest {   private PasswordManager passwordManager;   @Before  public void setup() {   Injector injector = Guice.createInjector();  passwordManager = injector.getInstance(PasswordManager.class);  }   @Test  public void testPasswordMatches() throws Exception {   assertTrue(passwordManager.passwordMatches("peter", "secret"));  assertFalse(passwordManager.passwordMatches("peter", "wrong password"));  } } 

So actually you will very rarely make direct use of the Injector – only once at the top of your object graph. All dependencies annotated with @Inject in the root class (here PasswordManager) as well as in all classes referenced by it will be resolved by Guice automatically.

What's next?

Try it out yourself – download the source code of this tutorial and give Guice a try. The sources come in form of a Maven project. Just extract the archive and run mvn test. Maven will download Guice and run the tests of the tutorial.

That's the end of our 15 minute crash course in using Google's Guice, though that's surely not all to be said about this fantastic framework. Definitely Guice's user guide is worth reading – especially due to its refreshing brevity (approx. 20 pages).

Advanced features include custom binding annotations, scoped dependencies and a means of intercepting methods allowing for the implementation of cross-cutting concerns in an aspect oriented way of programming.