Guicy Observers

Posted By Hoyt Summers Pittman

I have been experimenting with both Google’s cloud print services and Google Guice. A few months ago I created a basic Java based cloud printer proxy. I have recently been refactoring it to take advantage of Google Guice and dependency injection.

While the initial refactor was straight forward, I ran into a bump when I needed to perform post construction initialization. Whereas in Spring you will use a method such as afterPropertiesSet, in Guice you have to create a TypeListener, bind the listener in a module, and then register either an InjectionListener.

Before this refactor, I had been using the Java Observer and Observable classes and registered them manually. However, I didn’t want to litter constructors with calls to addObserver. Eventually I created an annotation to register an Observable class to my Observer object and have Guice to the heavy lifting.

Let’s take the following two java flavored pseudocode classes: a Service which will log us in and broadcast the log-in followed by a GUI element observing the log-in.

public class LoginService extends Observable {

public void handleLogin(GoogleUsernameAndPassword guap) {
        InputStream fis = null;
        HttpsURLConnection conn;
        try {
            ClientLogin cloudPrintLogin = new ClientLogin();
            cloudPrintLogin.setEmail(guap.getUserName());
            cloudPrintLogin.setPassword(guap.getPassword());

            clientLogin(cloudPrintLogin);
            this.properties.setProperty(PropertyLoader.Print_Auth, cloudPrintLogin.getAuth());

            setChanged();//Observable method
            notifyObservers();//Observable method
            
            
        } catch (Exception ex) {//Because Java ALWAYS has exceptions ;)
            Logger.getLogger(PrinterProxy.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

}

and our observer:


public class LoginCredentialsPanel extends javax.swing.JPanel implements Observer{

@Inject
public LoginCredentialsPanel(LoginService loginService) {
     loginService.addObserver(this);
}

@Override
    public void update(Observable o, Object arg) {
        if (o instanceof  LoginService) {
            authTokenField.setText(((LoginService)o).getAuth());
        }
    }

}

As we can see, I needed to manually add the observer to the observable. Not a huge deal, but a bit of boilerplate and leaking this in the constructor just didn’t sit well with me.

Now let’s create and apply my annotation, GuicedObserver. This annotation can be applied to any type definition and requires a value which is a class that extends Observable.

 @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME)
public @interface GuicedObserver {
    Class<? extends Observable> value();
}

Now we apply the annotation and remove the LoginService parameter, Inject annotation, and call to addObserver

@GuicedObserver(LoginService.class)
public class LoginCredentialsPanel extends javax.swing.JPanel implements Observer{

public LoginCredentialsPanel(LoginService loginService) {
}

//snip
}

This part of the design was easy. The next step, causing Guice to process the annotation was tricky. Guice’s bindListener method takes two parameters: a Matcher and a TypeLIstener. Guice provides a helper class, Matchers, which is supposed to make it easy to create simple Matcher instances. In reality, Guice’s Matchers utility class is not exactly good and the methods for generating Matcher objects for annotated classes or subclasses returns a type that you can’t just pass into bindListener. Fortunately a user had provided a solution on the Google Guice wiki.

/**
* @author ozguroktay.
*/
public class TypeMatchers {

   private static class SubClassesOf extends AbstractMatcher<TypeLiteral<?>> {
      private final Class<?> baseClass;

      private SubClassesOf(Class<?> baseClass) {
         this.baseClass = baseClass;
      }

      @Override
      public boolean matches(TypeLiteral<?> t) {
         return baseClass.isAssignableFrom( t.getRawType() );
      }
   }

   private static class AnnotatedWith extends AbstractMatcher<TypeLiteral<?>> {
      private final Class<? extends Annotation> baseClass;

      private AnnotatedWith(Class<? extends Annotation> baseClass) {
         this.baseClass = baseClass;
      }

      @Override
      public boolean matches(TypeLiteral<?> t) {
         try {
             return t.getRawType().isAnnotationPresent(baseClass);
         } catch (Exception e) {
             //LOG e
             return false;
         }
      }
   }
   
   /**
    * Matcher matches all classes that extends, implements or is the same as baseClass
    * @param baseClass
    * @return Matcher
    */
   public static Matcher<TypeLiteral<?>> subclassesOf(Class<?> baseClass) {
      return new SubClassesOf(baseClass);
   }
   
   public static Matcher<TypeLiteral<?>> annotatedWith(Class<? extends Annotation> aClass) {
        return new AnnotatedWith(aClass);
    }
   
}

This class creates Matcher instances which return true when they are run against an instance that either extends a defined class or is annotated with a defined class. Now I could finally add the following to my module in the configure method:

bindListener(TypeMatchers.subclassesOf(Observer.class).and(TypeMatchers.annotatedWith(GuicedObserver.class)), new GuicedObserverTypeListener());

This means that GuicedObserverTypeListener will be invoked every time a new type is encountered which both is annotated with GuicedObserver and extends Observer. Now we implement a type listener.

public class GuicedObserverTypeListener implements TypeListener {

    public GuicedObserverTypeListener() {
    }

    public <I> void hear(TypeLiteral<I> tl, final TypeEncounter<I> te) {
        Class<? extends Observable> clazz = ((Class<I>)tl.getType()).getAnnotation(GuicedObserver.class).value();
        final Provider<? extends Observable> p= te.getProvider(clazz); //1
        te.register(new InjectionListener<I>() {

            @Override
            public void afterInjection(I i) {
                if (i instanceof Observer) {
                    Class<? extends Observable> clazz = i.getClass().getAnnotation(GuicedObserver.class).value(); //2
                    p.get().addObserver((Observer) i); //3
                }
            }
        });
    }
}

This code is where all the magic happens. First (1) we must get a reference to the provider for the referenced GuicedObserver. This is because we are actually using an anonymous inner class whose methods will be called AFTER the injection of EVERY type. In other words, if we wait then the Guice API won’t allow us access to the TypeEncounter. In the InjecitonListener (2) we lookup the observable for the INSTANCE that we have finished injecting. Finally we grab an instance of our Observable from the Provider (3) that we configured in (1) and add the instance as an observer.

Now when I run my application, Guice will register annotated Observers for me. If you want to see the code in context, my current work on the project can be checked out from my SVN repo. Remember, this is a proof of concept and shouldn’t be treated as a final product.

Jul 12th, 2011

No Comments! Be The First!

Leave a Reply