Interceptors and Exception Mapping for Appcelerator

I've been working on some code for both OS Integrators, LLC and for a client as well. I'm using the Appcelerator AJAX framework for both projects. In order to accomplish these goals, I've committed the following to head in the service:java component:

  • @ExceptionResponses/@ExceptionResponse
  • @BeforeMethods
  • @AfterMethods
  • @InterceptorMethod

For example let's say you have:

@Service(request=CREATE_TICKET_REQUEST, CREATE_TICKET_RESPONSE)
public void createTicket(Message request, Message response) {
...
}

You want this to require authentication. If the user has no session or isn't authorized to file tickets, you want to send a security error. So you create a method like this in a class called AuthenticationService:

public void checkAuthenticated(Message request, Message response) {
String sIsAuth = (String)request.getSession().getAttribute(AuthenticationService.AUTHENTICATED);
boolean isAuth = sIsAuth != null & Boolean.parseBoolean(sIsAuth);
if(!isAuth) {
response.getData().put(AuthenticationService.AUTHENTICATION_ERROR, Boolean.TRUE.toString());

throw new SecurityException("Security exception");
}

Next, you associate this method with your createTicket method by annotating it like so:

@BeforeMethods({
@InterceptorMethod(interceptor=AuthenticationService.class, method="checkAuthenticated")
})

However, this puts the exception and error in your create ticket response message. This makes generic error handling (i.e. redirect to the login screen for security exceptions) complicated. What you really want is a different message for security errors. Do this by annotating createTicket like so:

@ExceptionResponses({
@ExceptionResponse(exception=SecurityException.class, response="error.security.response")
})

This returns both the create ticket response message with error=true and the error.security.response message to the client. You can have as many @ExceptionResponse annotations as you like, but the order matters. For instance if at the top of the annotation you have RuntimeException and below that you have SecurityException the runtime exception mapping will not occur as security exception is a more specific instance of it.

The same is true for @BeforeMethods, the methods are executed in order, an exception thrown near the top of the stack is returned up the stack (and mapped where appropriate).

There is also @AfterMethods for completeness (it also enumerates @InterceptorMethod annotations), but I have never found them particularly useful to be honest.

Let me give due credit. I derived inspiration for this from:

  • EJB3 annotations
  • JBoss Client interceptor stack
  • Spring/AspectJ annotations

And received a lot of help from Martin Robinson @ Appcelerator.

To build it:

  1. Install Git
  2. From a command prompt type: git clone git://github.com/jhaynie/appcelerator_sdk.git
  3. make sure build/config.yml reads: :version: "2.3" at the top
  4. add a line just after :java: that reads :version: "1.0.21" (or incrementally more than you have in $HOME/.appcelerator/releases/service/java/)
  5. type "rake service:java" - you may need to install ruby, ruby's rake and its dependencies to make that work. Come to #appcelerator on irc.freenode.net and someone can probably help you through that
  6. type app install:service stage/service-java-1.0.21.zip
  7. now you can app create:project with this new feature

I'm interested in feedback from anyone who ends up using it. Thanks!