Jul 24, 2016

The Evil of "Evil Annotations"

This is an evil article (Sorry I use the word "evil" which is copied from the article). And below is why
1) Annotation, like Java source code, is a way to express developer's intention to runtime environment. And it is a very expressive way of doing that. The author's point of "reducing the reusability of class" makes non-sense because:
  • the code with JEE annotation or other annotation is designed to run into a certain container/application server that follow the standard and understand the semantic of the anntoation as per designed
  • you can't blame Java source code reduce the reusabilty because it cannot run by a ruby interpreter, right?
2) In the "Evil Annotations Are Sometimes in the Wrong Place" section, the author point out that@ApplicationScoped does not belong to the class "DocumentFormatter" as shown below:
@ApplicationScoped  
public class DocumentFormatter {   
  ...  
}
And my view is: the annotation clearly expressed one important factor of the class: it is an application level singleton object, and container/application does not need to instantiate the instance of that class multiple times. Without using that annotation, you probably will go back to the Singleton pattern, which adds couple lines of code without any additional benefit.
The other example about JPA class is also funny, the point of "which couples the persistence directly to domain objects", should be rephrased into "which reduce the couples between persistence layer and domain object", because any persistence layer that implement the JPA requirement can handle your domain object. I wonder if the author is still thinking of writing his own JDBC code to handle persistence of the domain object. Here I want to mention that "persistence" IS one attribute of a domain object, and it is the system specification of your application, it is unfair to split it out from the domain object just because it doesn't represent the functional specification. If you are doing that, it's kind of like to say a programmer shall not eat because eating is not the domain feature of a programmer, which is just programming.
3) The author gives out the alternative to annotation: "of course good old Java Code", but I couldn't see any real world project/framework/application support that conclusion. And I want to point out the author's affinity to "the Java Language and its Compiler" is not generic in any way. The reason people relies on Java language and it's compiler is because it ONE of the standard of transfer developer's intention (in Java source code) into instructions to be run in a reliable standard environment (JVM). And there are many other "languages and compilers" could be used to do the same thing. On the flip side of the concern, the author looks like ignored the runtime environment which is also a standard part of the system, JVM is no doubt one of them, and JVM itself is not the only example of it. JEE containers and many other stacks are also good example of standard runtime environment, and they do recognized annotations (standard or even non-standard).
In conclusion, the problems of the article are:
  1. The author's overlook of system specification while highlighting functional specification. (Found in the JPA and @ApplicationScoped example)
  2. The author's overlook of Runtime environment while highlighting the language/compiler. (Found in across the whole article)

Mar 6, 2016

Introduce to ActFramework - an new MVC/RESTFul service framework in Java

I am happy to share my recent Open Source project ActFramework here.
So some events about ActFramework:
  1. About one year ago I start working on ActFramework.
  2. Three months ago I start using ActFramework to rewrite a commercial project which was written in Jersey originally, and the result is great. We have reduced the lines of code from 8900 to 4500 while keeping the same feature set and functionality.
  3. Last Wednesday I presented ActFramework in the first Sydney JVM meetup at FinTech hub in 2016.
In this blog I will brief major features of ActFramework

Controller

@GetAction("/")  
public void home() {
}    
@PostAction("/customer")   
public String createCustomer(Customer customer) {
      customerDao.save(customer);
      return customer.getId();  
}    
@PutAction("/customer/{id}/email")  
public void updateCustomerEmail(String id, String email) {
      Customer customer = customerDao.findById(id);
      notFoundIfNull(customer);
      customer.setEmail(email);
      customerDao.save(customer);
}
See the documentation for more about controller and action methods

Routing

There are two ways to create routing table:
  1. Through resources/routes file
    GET /customer/{id} any.pkg.CustomerController.get
    POST /customer any.pkg.CustomerController.create  
    ...  
  2. Through action method annoation as shown above
See the documentation for more about routing

Model and DAO

ActFramework support Morphia for MongoDB and Ebean for SQL. You can declare your Model class as a POJO with proper annotation. MorphiaDao and EbeanDao are provided to support find/save/delete entities:
@Entity  
public class Customer {
      @Id
      private ObjectId id;
      private String name;

      public void setId(ObjectId id) {
          this.id = id;
      }

      public ObjectId getId() {
          return this.id;
      }  

      public void setName(String name) {
          this.name = name;
      }  

      public String getName() {
          return this.name;
      }
}
public class CustomerController {
      @Inject MorphiaDao<Customer> dao;

      @GetAction("/customer")
      public Iterable<Customer> list() {
          return dao.findAll();
      } 

      @GetAction("/customer/{id}")
      public Customer get(String id) {
          return dao.findById(new ObjectId(id));
      }  

      @PostAction("/customer")
      public String create(Customer customer) {
          dao.save(customer);
          return customer.getId().toString();
      }
}
See the documentation on more about Model and DAO

Jobs and scheduler

public class MyClass {
      @Every("2s")
      public void jobA() {
          ...
      }  

      @InvokedAfter("MyClass.jobA")
      public void runAfterJobA() {  
        ...
      }   

      @OnAppStart
      public void jobB() {  
        ...
      }

      ...
}
See the documentation for more about jobs and scheduling

Events

public class CustomerController {
      @Inject
      EbeanDao dao;

      @Inject
      EventBus eventBus;

      public void create(Customer customer) {
          dao.save(customer);
          eventBus.trigger("new-customer", customer);
      }

}
@Mailer  
public class PostOffice extends Mailer.Util {

      @On("new-customer")
      @Async
      public void sendWelcomeLetter(Customer customer) {
          to(customer.getEmail());
          from("noreply@mycom.com");
          send(customer);
      }
}
See the documentation for more about events

Interceptors

  public class App {
      @Before(except = "login,logout")
      public void authenticate(ActionContext context) {
          if (!context.session().contains("username")) {
              if (context.isAjax()) {
                  throw new UnAuthorized();
              } else {
                  redirect("/login");
              }
          }
      }

      @Action(value = "/login" methods = {H.Method.GET, H.Method.POST})
      public void login() {
          ...
      }
  }
@With(App.class)  
public class CustomerController() {
      ...
}
See the documentation for more about interceptors

More features

  1. Sending Email
  2. Creating CLI commands

Comparing to PlayFramework v1.x

Project layout

Controller and action methods

  • Play controller must extends play.mvc.Controller. Action method must be void and static
  • Act controller does not need to extend any class. However extending act.controller.Controller.Util makes it easy to use renderXxx methods. Act action method does not need to be static. Act action method can return object

Routing

Templates

  • Play use groovy as default template engine
  • Act use Rythm as default template engine
  • Both framework support plugin different template engine

Models and DAO

  • Play's DAO methods are built into play.db.Model. Sub class get the data access capablitity via byte code enhancement
  • Act framework does not require Model class to extend any class. ActFramework inject default DAO implemenation to host class (e.g. a Controller, or Mailer) to provide DAO capablity

SQL Database access

  • Play use JPA/Hibernate to achieve SQL database access
  • Act use Ebean to achieve SQL database access

MongoDB access

  • Play use PlayMoprhia plugin (created by author of ActFramework) to access mongodb
  • Act use Morphia plugin to access mongodb

Jobs

  • Play job must be put into a class that extends play.jobs.Job. Each class implement one job logic
  • Act job is just a public void method without parameters. The class hosting job method does not require to extend any class

Events

Asynchronous http request handling

i18n

  • Play provides an easy to use i18n support
  • Act support i18n through Rythm's i18n() extension. Act's i18n support needs to be refined and improved

Cache

Sending email

Testing

  • Play provides a very descent testing facilities
  • Act relies on standard junit to implement application testing. Act also provides a specific act-test to support application's Model test. At the moment Morphia support is provided.

Security

  • Play provides limited security support. However there are some third party plugins like Deadbolt and play-aaa (created by author of ActFramework)
  • Act provides authentication/role based authorization/accounting via act-aaa

Modules and depedencies

  • Play applied it's specific way to manage modules and dependencies
  • Act relies on maven to manage dependencies and modules

Database evolutions

  • Play provides database evolution support
  • Act does not provide specific support on database evolution.

logging

  • Play provides a Logger class with static logging method. See document
  • Act does not have any specific class for logging. Developer could use any Logging solution (e.g. Log4J) in their application. However developer are also free to use App.logger to create log items.

Deployment

  • Play can be deployed as a fullstack application or as a war file in JEE application servers
  • Act support only fullstack application deployment at the moment (0.1.1-SNAPSHOT)

Resources