A peek into WebEngage’s security layer – super cool use of Java annotations

Note: If you are new to attribute programming, we recommend giving Attribute based programming or Java Annotations a read first.

To begin with, let us show you a small code snippet from one of our controllers. The saveSurveyResponse method underneath gets called every time some user takes an in-site survey on a customer’s website.

/**
 * This method is invoked everytime someone takes a survey on a customer's website.
 * It performs two main tasks -
 * 1. saves the user's response in our database
 * 2. refreshes the stats and data graphs for the corresponding survey
 */
public ... saveSurveyResponse(...) throws IOException {
  //mundane code here to compute a response object "surveyResponseDto"
 
  //first save the response in database
  this.publisherBc.saveSurveyResponse(licenseCode, surveyResponseDto);
 
  //then, refresh all the analytics associated with this survey
  this.publisherBc.refreshSurveyStatusOnResponse(licenseCode, surveyId);
}

As you can see above, the code performs two major tasks – #1. save the response in database and #2. refresh the analytics associated with the corresponding survey. We pre-compute a lot of data graphs and collate the information to create a bunch of Maps, so that the stats for our customers can be presented in real-time, in its true essence. That’s a lot of computing work. The method refreshSurveyStatusOnResponse usually takes about half a second (and much more at times) to complete. We can’t keep the end user on a third party website waiting because (s)he took a simple survey and we got in to some crazy computing business!

Solution? Simple – make that method call asynchronous.
Yes, one would either use a batch process or make that call asynchronous. However, it is a much bigger problem to address. There are several such methods in any application stack which should be executed asynchronously to keep the user experience intact. E.g. in any action that needs to send out an email, the sending email part can easily be made asynchronous because SMTP relays can be painful at times leading to huge lag; in the process, it keeps your end user waiting for a response.

Implementation? We created a cool Java annotation called Asynch.
We first created an annotation interface called Asynch and then implemented a method interceptor to look for this annotation on the callee. If the annotation were present, we’d execute it further in a new thread. Snippet from the implementation below.

/**
 * Defining the Asynch interface
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface Asynch {}
 
/**
 * Implementation of the Asynch interface. Every method in our controllers
 * goes through this interceptor. If the Asynch annotation is present,
 * this implementation invokes a new Thread to execute the method. Simple!
 */
public class AsynchInterceptor implements MethodInterceptor {
  public Object invoke(final MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    Annotation[] declaredAnnotations = method.getDeclaredAnnotations(); 
    if(declaredAnnotations != null && declaredAnnotations.length > 0) {
      for (Annotation annotation : declaredAnnotations) {
        if(annotation instanceof Asynch) {
          //start the requested task in a new thread and immediately
          //return back control to the caller
          new Thread(invocation.getMethod().getName()) {
            public void execute() {
              invocation.proceed();
            }
          }.start();
          return null;
        }
      }
    }
    return invocation.proceed();
  }
}

Done? Just that much?
Oh yes, pretty much. Here’s how the declaration of heavyweight method, refreshSurveyStatusOnResponse, looks like -

/**
 * So, earlier we had a simple method in our interface which we later
 * annotated with the Asynch @interface. Bang! The caller doesn't need
 * to worry about it now. This method (no matter who the caller is)
 * gets executed asynchronously. Ain't that awesome? 
 */
@Asynch
public void refreshSurveyStatusOnResponse(String licenseCode, Integer surveyId);

How did we use annotations to build our security layer?
Now that we gave you a fair idea of how we are using Annotations to our advantage, let’s dive a bit deeper. You are about to see how we built our entire Authorization and Authentication stacks using Java Annotation.

We are security freaks. Be it the web layer or data exchange layer, access to all our code is protected. The caller of a method is denied entry to the method if it does not have right privileges. Underneath is a small snippet from one of our web layer controllers. This method is invoked when someone tries to edit a survey from the WebEngage dashboard.

/**
 * This is a public method invoked via a URL on the site. Once a user on the site
 * tries to reach this method, the "rules" specified below (via annotations) are
 * evaluated. If it matches with the criterion specified for the UserAuth class,
 * the user is allowed an entry into the method; otherwise is shown the exit door!
 *
 * In the specific example below, only "signed-in" users who are "authorized 
 * publishers" (our terminology for WebEngage customers) AND have access to 
 * "survey configuration" related features, are allowed entry into this method.
 */
@UserAuth (
  userTypes = {
     UserType.SIGNED, 
     UserType.AUTHORIZED_PUBLISHER
  },
  publisherUserFeatures = {
     Feature.SURVEY_CONFIGURATION
  }
)
public ... edit(...) throws IOException{
 
}

Pretty nice. Right? So just by annotating the edit method with UserAuth, we made sure that survey edit URL’s returns a sweet nothing to those who are not supposed to use those URL’s. Where’s the beauty? This piece of annotation is reusable; we use it in a variety of ways on pretty much all the code that needs to be protected behind the concepts of user and their corresponding roles.

Of-course there’s a lot of application specific code behind understanding the annotation UserAuth. However, that’s one time and we have managed to reuse it in a much more sophisticated manner inside our Business Layer as well. Take a look at this usage in one of the methods below:

/**
 * This is our business component method for customers who intend to change
 * the styling of their surveys to match the CSS with their site's look and
 * feel [oh, we have a sexy CSS editor inside dashboard for them to do so ;)]
 *
 * This is a nested annotation. A list of @Authorize methods can be specified
 * as rules. Each of them specifying the method to be called (for authorization)
 * and the argument to be passed to it. For the caller to get access, these
 * values should meet the AuthRules criteria. If it does not, an 
 * AuthorizationException is thrown.
 */
@AuthRules(
  authRules = {
    @Authorize(
      method = "hasPublisherUserFeatureAccess", 
      sargs = {"$0", "SURVEY_STYLING"}
    ), 
    @Authorize(
      method = "hasPublisherFeatureAccess", 
      sargs = {"$0", "WE_SURVEY_CUSTOM_CSS"}
    )
  }
)
public void saveSurveyStyleCss(
  Integer publisherId, 
  AdhocAttributeName cssAdhocAttributeName, 
  String css
) throws AuthorizationException;

This explains how powerful annotations can get. At WebEngage, we use them to the fullest. Hope this article helps you build some cool stuff with attributes. Do let us know!

Note: Use the Asynch annotation idea with care. Spawning new threads without being in control can be fatal. If you plan to use it, make sure the threads are fetched from a pre-created thread pool.

Stay tuned. We love you!


10 responses to “A peek into WebEngage’s security layer – super cool use of Java annotations

  1. Pingback: Quora

  2. For asynchronous programming you cold have used Future and Executors with a thread pool. Did you evaluate the same ? Any special reason to create special annotation and thread spawn ( even from a pool )?

    • avlesh

      The primary reason for using annotations was re-use. Look at the first code snippet in this article. See the second method call. We have many such methods. Now, imagine a redundant block of code snippet (to use an Executor) all over your codebase. Instead, with annotations, we could simply put an @Asynch annotation on all such method definition and the code gets invoked in a new thread no matter who the caller is.

      Makes sense?

      • @Asynch
        public “”void”" refreshSurveyStatusOnResponse(String licenseCode, Integer surveyId);

        in current case method returns nothing AND you a new primitive thread spawned as follows,

        //return back control to the caller
        new Thread(invocation.getMethod().getName()) {
        public void execute() {
        invocation.proceed();
        }
        }.start();
        // plain thread management could have been averted by using “newCachedThreadPool”

        http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool%28%29
        ______________

        AND what in case when you require to return something from a method ( not just fire and forget case like above with return type void ) how it is being dealt then ?

        • avlesh

          Having a return type for the caller of an async method – does that sound logical?
          And the article does mention that we use a pool.

          • “Having a return type for the caller of an async method – does that sound logical?”

            Yes very much !

            Consider a real world and real time “multiple account summary” request from a net banking user who has multiple accounts in a single bank having legacy core banking system.

            We have a web application in picture. You have to develop a Service layer asynchronous method which in turn will be called from Action class to get List of account details.

            The async method we talk about is going to communicate to a TCP interface where you could query only one account at a time ( this is a constraint you can’t put multiple accounts in single request message as a payload, one account request->one summary response ).

            Now there are two approaches spawn individual threads inside async method and return the list of account summaries OR make TCP call sequentially ! In both case you are doing IO heavy proportion and zero data processing ! But ultimately list has to be prepared and returned in action layer !

            Pointers,
            http://docs.oracle.com/javaee/6/tutorial/doc/gkkqg.html
            “Retrieving the Final Result from an Asynchronous Method Invocation” :)

            Sorry for reiterating the thread pool part.

            Clever usage of annotations is good though ! :)

  3. Sanjeev Kumar Dangi

    Suppose I need to upload a user image on ec2 bucket. On server side, i spawn a thread and upload the image. But there are rare cases when image upload can fail. In that case, there is no way to tell the user that image upload has failed? Is this the right use case for using another thread?

  4. Badal Singh

    @Rakesh,
    You might have not faced any problem with Thread Executor but I have faced many problems while trying to build a thread manager.

    1.)Thread manager gives you the facility of changing maximum pool size but if you decrease the pool size in run-time it doesn’t give the expected results and when i dig into it turned out that there is no implementation for decreasing the pool size in class provided by java.
    2.)And even the FutureTask class is not upto the mark. You can’t even get the identity of the running task until it is finished(you can only get the returned object but after the execution).

    So I ended up writing my own threadpoolexecuter and concluded that java threadpoolexecuter are good for nothing.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">