Guice - AOP (Aspect Oriented Programming)

Posted on October 1, 2011


Aspect Oriend Programming (AOP) is a programming methodology or perspective, which was conceived to bring in more modular approach to the already existing 'Object Oriented Methodology'. Dependency Injection, which is another programming methodology to bring in more modular approach or to create loosely coupled modules, which compliments AOP. In this post, I'm going to write something about how the DI framework 'Guice' uses it's DI mechanism, to provide AOP concepts (method interceptors).

Lets think of a small application, where users need to purchase a licence or have some special privileges to use some functionality of the application. Below is the code for this app, which provides the main functionality of this app (Service.java interface and it's implementation - ServiceImpl.java):

Service.java

package com.chetty.licence;

/**
* Service Interface
*
* @author Babji, Chetty
*/
public interface Service {
public void doSomething1();
public void doSomething2();
}

ServiceImpl.java

package com.chetty.licence;

/**
* Service Implementation.
*
* @author Babji, Chetty
*/
public class ServiceImpl implements Service {
public void doSomething1() {
System.out.println("I'm doing something - 1");
}

@LicenceRequired
public void doSomething2() {
System.out.println("I'm doing something - 2");
}
}

As you see in the above Service interface implementation, there is a annotation called "LicenceRequired" for the second method. What we are trying to do is to provide limited access to the functionality of this app or that method, by keeping a check (by intercepting) on that method, whenever it is called. The code for the "LicenceRequired" annotation is as below:

LicenceRequired.java

package com.chetty.licence;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
*
* @author Babji, Chetty.
*/
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
public @interface LicenceRequired {}

Guice provides the "method interceptors" functionality by using "AOP Alliance" (aopalliance.jar) as it's external library. The annotation "LicenceRequired" is used as a marker (See ServiceImpl.java's doSomething2() method) to intercept the required method. Guice knows that this method has to be intercepted, by the following configuration (Guice Module):

LicenceModule.java

package com.chetty.licence.guice.module;

import com.chetty.licence.LicenceRequired;
import com.chetty.licence.LicenceChecker;
import com.chetty.licence.Service;
import com.chetty.licence.ServiceImpl;
import com.google.inject.AbstractModule;
import com.google.inject.matcher.Matchers;


/**
*
* @author b.chetty
*/
public class LicenceModule extends AbstractModule {
protected void configure() {
LicenceChecker licenceChecker = new LicenceChecker();
requestInjection(licenceChecker);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(LicenceRequired.class), licenceChecker);

bind(Service.class).to(ServiceImpl.class);
}
}

The service client or adapter, is as follows:

ServiceAdapter.java

package com.chetty.licence;

import com.google.inject.Inject;

/**
* Service Adapter.
*
* @author Babji, Chetty
*/
public class ServiceAdapter {
private final Service service;

@Inject
public ServiceAdapter(Service service) {
this.service = service;
}

public void doSomething() {
service.doSomething1();
service.doSomething2();
}
}

And the Licence checking functionality is done in the following class (Licence.java):

Licence.java

package com.chetty.licence;

/**
*
* @author Babji, Chetty
*/
public class Licence {
public boolean checkLicence(String licenceString) {
String licenceStringFromDB = getLicenceStringFromDB();
return (licenceString != null && licenceStringFromDB != null && licenceString.equals(licenceStringFromDB) ? true : false);
}

private String getLicenceStringFromDB() {
return "TEST";
}
}

And below is the class, that aids the method intercepting process. If you check the Guice Configuration module (LicenceModule.java), I have configured the "LicenceRequired" annotation, to work with "LicenceChecker" class, so that when the method with business logic, annotated with "LicenceRequired" is called, control is passed over to the "LicenceChecker" class.

LicenceChecker.java

package com.chetty.licence;

import com.google.inject.Inject;
import java.util.Scanner;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
*
* @author Babji, Chetty
*/
public class LicenceChecker implements MethodInterceptor {
@Inject
private Licence licence;

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//Get License String from the user. You can read the licence string from a licence-file, from connsole, etc options.
//In this case, I'm just reading it from the console.
System.out.println("You need a valid Licence to use this functionality...Please enter your Licence string: ");
Scanner input = new Scanner(System.in);
String licenceString = input.nextLine();

if(!licence.checkLicence(licenceString)) {
//Here, you can either throw an exception and show a "Invalid Licence" message or customize it according to your needs.
System.out.println("Invalid Licence!");
throw new IllegalStateException("Invalid Licence : " + invocation.getMethod().getName());
}

return invocation.proceed();
}
}

And, below is the main class (entry point) for this application called "licenceCheck":

LicenceApp.java

package com.chetty.licence;

import com.chetty.licence.guice.module.LicenceModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

/**
* @author Babji, Chetty
*/
public class LicenceApp {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new LicenceModule());
ServiceAdapter serviceInterface = injector.getInstance(ServiceAdapter.class);
serviceInterface.doSomething();
}
}


This app uses a hardcoded "TEST" string as licence string. This is checked with the user input licence string and if they don't match, the method with "LicenceRequired" annotation will not be executed. If you execute the application with a test string "TEST1234", "I'm doing something - 1" is printed, followed by "Invalid Licence!" and an IllegalStateExcception (implying that the second method - doSomething2() is not executed). If the user input licence string is "TEST", both the methods are executed. This idea can be customized and implemented in your own projects. There are also other uses of this AOP in handling transactions, logging, security, etc.

You can get the source code for this app, on Google's code repository: licenceCheck

Disclaimer: This sample project was created for fun and to learn new techniques. Use the code, at your own risk! :P

Blog Categories
Disclaimer
The views expressed on this blog are my personal views and do not reflect the views of my employer or campaigns I am supporting.

All sample code is provided for illustrative purposes only. These examples have not been thoroughly tested under all conditions. The writer therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.

All programs contained herein are provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.