Wednesday, July 2, 2014

Create an extensible set of commands using: Spring,Annotations and simple interface

Lately I've found myself using the same technique more than once in order to create some easily extensible mechanism, using: Spring, Annotations and simple predefined interface

I think the best way to picture the usage would be an example:
Let's say you need some kind of email editor in your application which will allow your user to use predefined set of keywords that will be evaluated during the email sending.

For example: 
If the user wrote following text in email body :  Hi today is ${system.Date} and I'm ...
Then  the recipient will get following content  :  Hi today is 2/7/2014 and I'm ...

In case your task is a bit more complicated than that and you need more comprehensive set of keywords, that will be extended during the time or even you would like to allow 3rd party developers to extend it, following technique might be handy.

I'll skip the part when you find  ${prefix.keyword} pattern in the code and start from the point you have extracted the keyword already.
 

   
 //.......
    for(/*... do the iteration*/){
        // get your keyword from text
        String keywordValue =  getKeywordValue(foundKeyword);
        // replace your ${foundKeyword} with keywordValue
    } 

//......
    // call the Helper class to get the correct executor
    private String getKeywordValue(String key) {
        KeywordExecutor keywordExecutor = KeywordExecutorsHelper.getKeywordExecutor(key);
        if(keywordExecutor != null){
            return  keywordExecutor.execute(context);
        }
        // if we couldn't find suitable executor just return the keyword as is 
        return key;
    }

Now let's have a look on how KeywordExecutor implemented, for sake of our example let's assume we have 2 keywords to implement: system.Date and system.Time

 // the interface
 public interface KeywordExecutor {

    // you can pass any parameters if you need them for your business case
    String execute();
 }

 // annotation that will hold the required keyword 
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Keyword {

    String value();
 }


 @Component // Spring Component annotation 
 @Keyword("system.Time") // here goes our annotation with the the keyword 
 public class TimeKeyword implements KeywordExecutor {


    // the implementation itself 
    @Override
    public String execute() {
        Date now = new Date();
        // using Spring SimpleDateFormat here 
        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
        return format.format(now);
    }
 }


// same as above with slight format change
 @Component
 @Keyword("system.Date")
 public class DateKeyword implements KeywordExecutor {


    @Override
    public String execute() {
        Date now = new Date();
        SimpleDateFormat format = new SimpleDateFormat("dd-MMM-yyyy");
        return format.format(now);
    }
 }

You can see that execute method can be as complex as we need and the rest of the code won't be affected.

The last thing that we need is our KeywordExecutorsHelper implementation:

 
 // in this sample I just reading all the classes every time getKeywordExecutor method called, you can optimize this by
 // saving the Map a static variable and lazy initializing it during the first getKeywordExecutor call 
 public class KeywordExecutorsHelper {
    public static KeywordExecutor getKeywordExecutor(String keyword) {

        // getting all the beans annotated with Keyword annotation 
        Map keywordExecutors = SpringUtils.getBeanWithAnnotation(Keyword.class);
        // the bean name used as a key in retrieved Map
        for (String beanName : keywordExecutors.keySet()) {
            // since all the beans implementing the same interface, I can safely cast each bean to KeywordExecutor 
            KeywordExecutor keywordExecutor = (KeywordExecutor) keywordExecutors.get(beanName);
            // getting the specific annotation instance from current bean using simple java reflection
            Keyword keywordAnnotation = keywordExecutor.getClass().getAnnotation(Keyword.class);

            // reading the value attribute and comparing with received keyword
            if (keyword.equalsIgnoreCase(keywordAnnotation.value())) {
                return keywordExecutor;
            }

        }
        // no implementation found for requested keyword
        return null;
    }

}

So, lets summarize what have we done and why you might need it:
  • What
    • Defined an interface that will help us to encapsulate bean implementation (execute in the case above)
    • Define an annotation that will help us to find all the annotated classes using Spring and will be used as a placeholder for required attributes  (Keyword in the case described above)
    • Define a Helper class that will retrieve all the possible implementations from Spring context and will  return a specific one by demand
    • Finally use the  Helper class to get the specific implementation 
  • Why
    • In case you need an extensible set of commands that can be summoned using keyword only this implementation can  be very handy
    • In case you want to outsource the implementation and will receive some kind of 3rd jar, you'll have to define only the interface and the annotation
Hope it was helpful.