Wednesday, October 30, 2013

Exploring Spring Controller with Spring Form Tag

In the past article, I have shown you how to process an plain HTML form with Spring controller. But a more powerful way to process form is to use Spring’s @ModelAttribute and its spring:form tags. I will show you how to get that started here by modifying last article’s project setup. We will simply modify the Comment form and controller to use this feature.

In the same project, modify the src/webapp/comment.jsp view file into this:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags/form" %>
<spring:form modelAttribute="comment">
  <table>
    <tr>
        <td><spring:textarea path="text" rows="20" cols="80"/></td>
    </tr>
    <tr>
        <td colspan="2">
        <input type="submit" value="Post"/>
        </td>
    </tr>
  </table>
</spring:form>

This view now use the spring:form tag to render the comment form instead of plain HTML. I have only shown you one element here, but the spring:form tag library also comes with all the matching HTML form elements that helps you quickly render the form with data binded. This automatically triggers the CommentController when you submit. We will need to modify that to capture the form.

package springweb.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import springweb.data.Comment;
import springweb.data.CommentService;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Controller
public class CommentController {

    @Autowired
    private CommentService commentService;

    @RequestMapping(value="/comments")
    public ModelAndView comments() {
        List<Comment> comments = commentService.findComments();
        ModelAndView result = new ModelAndView("/comments");
        result.addObject("comments", comments);
        return result;
    }

    @ModelAttribute("comment")
    public Comment createFormModelAttribute() {
        return Comment.create("");
    }

    @RequestMapping(value="/comment")
    public String comment() {
        return "comment";
    }

    @RequestMapping(value="/comment", method = RequestMethod.POST)
    public ModelAndView postComment(HttpServletRequest req,
                                @ModelAttribute("comment") Comment comment) {
        String fromUrl = req.getRequestURI();
        String user = req.getRemoteUser();
        String userIp = req.getRemoteAddr();
        comment.setFromUserIp(userIp);
        comment.setFromUser(user);
        comment.setFromUrl(fromUrl);
        commentService.insert(comment);
        ModelAndView result = new ModelAndView("comment-posted");
        result.addObject("comment", comment);
        return result;
    }
}

The difference in this controller compare to the old one is we used @ModelAttribute with an form object (or Spring calls it a command object.) We can name it, which I called comment here. It’s just a java POJO class, nothing special. But it is used to capture all the form input and then pass to Controller, which is called data binding. Notice that it will instanciate by createFormModelAttribute() method as you request the form view first. If you pre-populate the pojo with text, then it will automatically shows in the form! When user submit the controller will process in postComment() method, and the form object is re-populated with new form input again for processing. This allows you to work with form in pure Object style, and in many ways, it’s shorter and cleaner compare to plain HTML form.

There are much to the Spring MVC form processing. One powerful feature is it able to help you orgainze the form object validation and collect the error messages. Spring also helps localize your error message text etc. You may read more on their reference doc.

Thursday, October 17, 2013

Exploring Spring Controller with JSTL view

Let’s improve our previous Spring JDBC application with some more exploration on Spring MVC’s Controller development. I will show another exercise of writing a new Controller that processes a HTML form and use JSTL tags in JSP view pages.

To enable JSTL in Spring MVC application, you would need to add the following to the WebAppConfig config class. Let’s move it outside of WebApp.java and into it’s own top level class file in src/main/java/springweb/WebAppConfig.java.

package springweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("springweb.controller")
public class WebAppConfig {
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver result = new InternalResourceViewResolver();
        result.setPrefix("/");
        result.setSuffix(".jsp");
        return result;
    }
}

Inside the InternalResourceViewResolver bean, you define where to find your JSP pages that may have JSTL tags in them. The prefix setter is a path in relative to your src/webapp location. This allow you to hide your JSP files completely if you want to. For example, by setting it to "/WEB-INF/jsp" then you may move and store all JSP files into src/webapp/WEB-INF/jsp which is private in the web application. The suffix is just the file extension. These two values allow you to return a view name inside the controller with just the basename of your JSP file, which can be short as "/myform" or "/index" etc.

If you are to use Tomcat as your web container, you would need to add JSTL jar dependency as well, since the Tomcat server doesn’t come with standard tag library! So add this into the pom.xml file now.

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

While you’re at the pom.xml file, you might want to add the Tomcat maven plugin so you can type less in command line when running your web application.

<project>
...
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
            </plugin>
        </plugins>
    </build>
...
</project>

With that, you should able to run mvn tomcat7:run in root of your project without plugin prefix.

So what does JSTL bring to your application? Well, quite a bit actually. It lets you use some standard JSP tags that’s frequently used when writing JSP views. I will demonstrate this with a set of Controller and views to capture user comments from the application. Note that I will try to show you how to work with Spring Controller in the most basic way only. The Spring actually comes with a custom form JSP tag that is much more powerful to use. I will reserve that as another article in another time. Today let us focus on learning more about basic Spring Controller and JSTL, and a bit more on Spring JDBC data service as well.

We want to capture user comment, so let’s add a database table to store that information. Add the following DDL into your src/main/resources/schema.sql file. Again, this is for H2 database per last article project setup.

CREATE TABLE COMMENT (
  ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  TEXT VARCHAR(10240) NOT NULL,
  FROM_USER VARCHAR(15) NULL,
  FROM_USER_IP VARCHAR(15) NULL,
  FROM_URL VARCHAR(1024) NULL,
  TAG VARCHAR(1024) NULL,
  TS DATETIME NOT NULL
);

This time, we will write a data model class to match this table. Let’s add src/main/java/springweb/data/Comment.java

package springweb.data;

import java.util.Date;

public class Comment {
    Long id;
    String text;
    String fromUrl;
    String fromUser;
    String fromUserIp;
    String tag;
    Date ts;

    public Long getId() {
        return id;
    }

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

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getFromUrl() {
        return fromUrl;
    }

    public void setFromUrl(String fromUrl) {
        this.fromUrl = fromUrl;
    }

    public String getFromUser() {
        return fromUser;
    }

    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }

    public String getFromUserIp() {
        return fromUserIp;
    }

    public void setFromUserIp(String fromUserIp) {
        this.fromUserIp = fromUserIp;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public Date getTs() {
        return ts;
    }

    public void setTs(Date ts) {
        this.ts = ts;
    }

    private String getTrimedComment(int maxLen) {
        if (text == null)
            return null;
        if (text.length() <= maxLen)
            return text;
        return text.substring(0, maxLen);
    }

    @Override
    public String toString() {
        return "Comment{" +
                "id=" + id +
                ", ts=" + ts +
                ", text='" + getTrimedComment(12) + '\'' +
                '}';
    }

    public static Comment create(String commentText) {
        Comment result = new Comment();
        result.setText(commentText);
        result.setTs(new Date());
        return result;
    }
}

Just as previous artcicle, we will write a data service to handle insert and retrieve of the data model. We add a new src/main/java/springweb/data/CommentService.java file

package springweb.data;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class CommentService {
    public static Log LOG = LogFactory.getLog(CommentService.class);

    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcInsert insertActor;
    private RowMapper<Comment> commentBeanRowMapper = new BeanPropertyRowMapper<Comment>(Comment.class);

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.insertActor = new SimpleJdbcInsert(dataSource)
            .withTableName("COMMENT")
            .usingGeneratedKeyColumns("ID");
    }

    public void insert(Comment comment) {
        LOG.info("Inserting Comment + " + comment);

        Map<String, Object> parameters = new HashMap<String, Object>(2);
        parameters.put("TEXT", comment.getText());
        parameters.put("FROM_USER", comment.getFromUser());
        parameters.put("FROM_USER_IP", comment.getFromUserIp());
        parameters.put("FROM_URL", comment.getFromUrl());
        parameters.put("TAG", comment.getTag());
        parameters.put("TS", comment.getTs());
        Number newId = insertActor.executeAndReturnKey(parameters);
        comment.setId(newId.longValue());

        LOG.info("New Comment inserted. Id=" + comment.getId());
    }

    public List<Comment> findComments() {
        String sql = "SELECT " +
                "ID as id, " +
                "TEXT as text, " +
                "TAG as tag, " +
                "TS as ts, " +
                "FROM_USER as fromUser, " +
                "FROM_USER_IP as fromUserIp, " +
                "FROM_URL as fromUrl " +
                "FROM COMMENT ORDER BY TS";
        List<Comment> result = jdbcTemplate.query(sql, commentBeanRowMapper);
        LOG.info("Found " + result.size() + " Comment records.");
        return result;
    }
}

Since we did not use any fancy ORM but just plain JDBC, we will have to write SQL in the data service. But thanks to Spring goodies, it makes life much easier with helpers such as SimpleJdbcInsert, which handles DB insert and retrieval of auto generated key etc. And also notice that in query, we use the Spring’s BeanPropertyRowMapper to automatically convert JDBC resultset into a java bean Comment object! Simple, direct and quick.

Now we add the Spring controller in src/main/java/springweb/controller/CommentController.java to handle web requests.

package springweb.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import springweb.data.Comment;
import springweb.data.CommentService;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Controller
public class CommentController {

    @Autowired
    private CommentService commentService;

    @RequestMapping(value="/comments")
    public ModelAndView comments() {
        List<Comment> comments = commentService.findComments();
        ModelAndView result = new ModelAndView("/comments");
        result.addObject("comments", comments);
        return result;
    }

    @RequestMapping(value="/comment")
    public String comment() {
        return "comment";
    }

    @RequestMapping(value="/comment", method = RequestMethod.POST)
    public ModelAndView postComment(HttpServletRequest req, @RequestParam String commentText) {
        String fromUrl = req.getRequestURI();
        String user = req.getRemoteUser();
        String userIp = req.getRemoteAddr();
        Comment comment = Comment.create(commentText);
        comment.setFromUserIp(userIp);
        comment.setFromUser(user);
        comment.setFromUrl(fromUrl);
        commentService.insert(comment);
        ModelAndView result = new ModelAndView("comment-posted");
        result.addObject("comment", comment);
        return result;
    }
}

In this controller we map /comment URL to handle display of the HTML form, which returns the comment.jsp view. The method default to handle a HTTP GET. Note that we remapped same /comment URL to handle HTTP POST on a separated postComment() method! See how clean the Spring Controller can be in this demo to handle HTTP request. Pay very close attention to the parameters declared in the postComment() method. Spring automatically handles the HTTP request object and map to your method just based upon the types you declared! On some cases you need to be explicit with the help of annotation such as @RequestParam, but Spring does the parsing of HTTP request and extraction for you! This saves you tons of repeated boiler plate code if we were to write a direct Servlet code.

Now let’s see the view and how to use JSTL. The /comments URL is mapped to src/main/webapp/comments.jsp view file, which will list all Comment model objects.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:choose>
<c:when test="${empty comments}">
    <p>There are no comments in system yet.</p>
</c:when>
<c:otherwise>
    <table border="1">
    <tr>
        <td>INDEX</td>
        <td>TIME</td>
        <td>FROM</td>
        <td>COMMENT</td>
    </tr>
    <c:forEach items="${comments}" var="comment" varStatus="status">
    <tr valign="top">
        <td>${status.index}</td>
        <td>${comment.ts}</td>
        <td>${comment.fromUserIp}</td>
        <%-- The c:out will escape html/xml characters. --%>
        <td><pre><c:out value="${comment.text}"/></pre></td>
    </tr>
    </c:forEach>
    </table>
</c:otherwise>
</c:choose>

Pretty standard stuff on JSTL. Next is the HTML form to post comment in src/main/webapp/comment.jsp file.

<form action="comment" method="POST">
<textarea name="commentText" rows="20" cols="80"></textarea>
<br/>
<input type="submit" value="Post"/>
</form>

When form is posted and processed successful, we simply return to a new page in src/main/webapp/comment-posted.jsp file as output.

<p>Your comment has been posted. Comment ID=${comment.id}</p>

If you've done these right, you should able to run mvn tomcat7:run and browse http://localhost:8080/spring-web-annotation/comment to see the form. Go to /comments URL to verify all the comments posted.

Note that despite I used Spring Controller as backend, all the views are in basic JSTL, and even the form are just basic HTML elements! I did this so you can see how flexible Spring Controller can be.

I know these are lot of code to post as a blog article today, but I wanted to be complete and try to show a working demo with tutorial notes. I choose to make it into a single post with file content instead of having you download a project somewhere else. It makes my notes and explanation easier to match onto the code.

And that will conclude our tutorial for today. Please leave a note if you find this helpful.

Thursday, October 10, 2013

Getting started with Spring JDBC in a web application

I have shown you how to setup a basic Spring 3 MVC web application in my previous article. Reusing that project setup as template, I will show you how to enhance it to work with JDBC. With this you can store and retrieve data from database. We will add a new controller and a data service through Spring so you can see how Spring injection and annotation configuration works together.

A direct JDBC based application is easy to setup in comparison to a full ORM such as Hibernate. You don’t need to worry AOP, TranactionManager, Entity mapping and full array of other configurations. On top of the JDK’s java.jdbc API, the Spring comes with spring-jdbc module that can boots your productively with their well known JdbcTemplate class. Let’s explore how this can be setup and run as web application.

Getting started and project setup

For demo purpose, I will use the in-memory version of H2Database as JDBC store. It’s simple to use and setup. And if you decided to use their FILE or TCP based database, you would simply have to re-set the datasource, and you may continue to explore more.

We will start by adding new dependencies to your existing spring-web-annotation/pom.xml file.

  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.3.163</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>3.2.4.RELEASE</version>
  </dependency>

With this, you will have access to Spring module classes for configuration. Find the previous src/main/java/springweb/WebApp.java file in your existing project and add what’s new compare to below:

package springweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.sql.DataSource;

public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{ RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ WebAppConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };
    }

    @Configuration
    @EnableWebMvc
    @ComponentScan("springweb.controller")
    public static class WebAppConfig {
    }

    @Configuration
    @ComponentScan("springweb.data")
    public static class RootConfig {
      @Bean
      public DataSource dataSource() {
        DataSource bean = new EmbeddedDatabaseBuilder()
          .setType(EmbeddedDatabaseType.H2)
          .addScript("classpath:schema.sql")
          .build();
        return bean;
      }
    }
}

What’s new in here is we introduced a new RootConfig class that will be loaded inside getRootConfigClasses() method. The RootConfig is just another Spring annotation based configuration that creates a new Spring context for bean definitions. We’ve created a bean there that will run the in-memory database. The bean returned by the builder also conveniently implemented the javax.sql.DataSource interface, so we can actually inject this into any data service and start using it immediately.

One more cool thing about the Spring embedded database builder is that it also runs any SQL script as part of the startup! For this demo, we will create a PING table in the src/main/resources/schema.sql file. This file is visible to Spring as in root of the CLASSPATH due to Maven standard source structure.

CREATE TABLE PING (
  ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  TAG VARCHAR(1024) NOT NULL,
  TS DATETIME NOT NULL
);

That’s the datasource setup. Now notice that I did not add this datasource Spring bean definition into existing WebAppConfig class. The reason is that we want a separate Spring context to configure all service level beans, while reserving the WebAppConfig for all Spring MVC related beans (such as Controller, URL mapping etc). This helps organize your bean definitions in hierarchical order of Spring contexts; with RootConfig as parent and WebAppConfig as child layers. This also means that all service beans in RootConfig are automatically visible to WebAppConfig; for the purpose of injection etc.

Also notice that with separated config classes, we are able to specify two distinct packages to scan for service components; we use springweb.controller for WebAppConfig and springweb.data for RootConfig. This is important and it can save you some troubles by letting Spring auto detecting your annotation based components.

Creating Data Service

Now it’s time we use the JDBC, so let’s write a data service class under src/main/java/springweb/data/PingService.java file.

package springweb.data;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Repository
public class PingService {
    public static Log LOG = LogFactory.getLog(PingService.class);

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void insert(String tag) {
        LOG.info("Inserting Ping tag: " + tag);
        jdbcTemplate.update("INSERT INTO PING(TAG, TS) VALUES(?, ?)", tag, new Date());
    }

    public List<Map<String, Object>> findAllPings() {
        return jdbcTemplate.queryForList("SELECT * FROM PING ORDER BY TS");
    }
}

The service is very straight forward. I expose two methods: one for insert and one for retrieve all Ping data. Noticed that I used @Repository to indicate to Spring that this class is a component service that perform data service. Also note how we inject the DataSource using a setter method, and then instantiate the JdbcTemplate as member field. From that we can take full advantage of the Spring JDBC API for query and update.

A note on logging. Spring core itself uses Apache common-logging, so I reused that API without even explicitly declare them in my pom.xml. If you want to see more details from log output, you should add log4j logger implementation to the project, and it should automatically work. I will leave that as your exercise.

Next we will need to write a Controller that can bring data to web UI page. We will create this under src/main/java/springweb/controller/PingController.java file.

package springweb.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import springweb.data.PingService;

import java.util.List;
import java.util.Map;

@Controller
public class PingController {

    @Autowired
    private PingService pingService;

    @RequestMapping(value="/ping/{tag}", produces="text/plain")
    @ResponseBody
    public String pingTag(@PathVariable("tag") String tag) {
        pingService.insert(tag);
        return "Ping tag '" + tag + "' has been inserted. ";
    }

    @RequestMapping(value="/pings", produces="text/plain")
    @ResponseBody
    public String pings() {
        List<Map<String, Object>> result = pingService.findAllPings();
  if (result.size() == 0)
   return "No record found.";

        StringBuilder sb = new StringBuilder();
        for (Map<String, Object> row : result) {
            sb.append("Ping" + row).append("\n");
        }
        return sb.toString();
    }
}

In this controller, you can easily see that the Ping data is fetched and updated through our data service by injection. I’ve declared and map URL /ping/{tag} to insert Ping data into the database. Spring has this very nice shorthand syntax annotation that can extract parameter from your URL path. In this case it let user to pass a simple tag keyword to be inserted as Ping record; so we can identify the source in database.

The other controller handler /pings URL is very straight forward; it simply returns all the records from PING table.

For demo purpose, I choose to not use JSP as view, but to return plain text directly from the Controller. Spring let you do this by adding @ResponseBody to the handler method. Notice also we can specify the content type as text/plain as output directly using the annotation.

Testing

To see your hard labor with above, you simply need to run the Maven tomcat plugin. The previous article has shown you an command to do that. Once you restarted it, you should able to open a browser and use these URLS for testing.

Happing programming

From this simple exercise, you can quickly see Spring MVC brings you many benefits; and a lot of fun in developing web application. Spring, by design principles, tends to be developers friendly, boots productivity and un-intrusively in your environment. It’s one of the reason I like to work with it a lot. I hope you enjoy this tutorial and go further explore on your own.

Happy programming!

Saturday, October 5, 2013

Getting started with annotation based Spring MVC web application

Here is a minimal way to get a Spring 3 MVC project started with Maven.

First create spring-web-annotation/pom.xml file and include the Spring dependency:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>spring-web-annotation</groupId>
    <artifactId>spring-web-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

Now create the Servlet 3 web initializer and the Spring annotation config for the MVC parts in spring-web-annotation/src/main/java/springweb/WebApp.java

package springweb;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ WebAppConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };
    }

    @Configuration
    @EnableWebMvc
    @ComponentScan("springweb.controller")
    public static class WebAppConfig {
    }
}

The WebApp class extends Spring's built in Servlet3 web initializer code. It allows Servlet3 container such as Tomcat7 to auto detect this web application without the need of web.xml configuration setup. Because of we do not use web.xml, we need this class to allow Spring to hook into the Servlet container to bootstrap their dispatcher servlet. Also instead of typical Spring beans xml file configuration, we now can use all annotation based using WebAppConfig.

Noticed that I have combined the WebAppConfig as inner class, but you can easily move it out as top level class in a full scale application. This is the Spring annotation version of container configuration. You can easily customize the application by adding new @Bean here.

Note: Do not forget to overwrite getServletMappings method with "/", or else your URL request will not direct to the Spring dispatcher for processing! A step that can easily forgotten and you might find your self chasing why Spring controllers are not working.

Above are really the minmal setup you need to start a war project. Next you want to add at least one controller to have some output to verify. Create this controller file spring-web-annotation/src/main/java/springweb/controller/IndexController.java

package springweb.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    @RequestMapping(value="/")
    public String index() {
        return "index";
    }
}

And now you would need the JSP view spring-web-annotation/src/main/webapp/index.jsp

Hello World.

Now cd into spring-web-annotation and execute mvn org.apache.tomcat.maven:tomcat7-maven-plugin:run. You should see your Spring application starting and be able to browse http://localhost:8080/spring-web-annotation URL.

There are lot of cool stuff you can do with Spring MVC. Checkout their awesome docs for more details.

Wednesday, September 11, 2013

The Apache Camel 2.12.0 has been released!

The Apache Camel 2.12.0 has been released! Yeh!

This is a special release to me mainly because I have help fixed few Jira Issues. :-P

Among these, I have helped ported the old quartz component into quartz2 using the new Quartz 2.0 API. As far as for Camel users concern, it should work just as old one, except it will now use the quartz2:// URL prefix instead. But the implementation uses Quartz2 library that's not backward compatible with old Quartz1.8, which is very old. In addition, the quartz2 component now has a new option deleteJob=false that will allow you to NOT delete the job created by Camel on shutdown, and it will also reuse existing job found with the same name in the Quartz scheduler if it exists during startup.

Obviously my contribution seems tiny compare to what the full release brings you, but it’s a start in helping out the project. I am glad the Camel folks accepted these patches and found it into their release.

Try out the latest Camel and see what you think.

Happing programming!

Monday, September 9, 2013

Exploring Apache Camel Core - Seda Component

The seda component in Apache Camel is very similar to the direct component that I’ve presented in previous blog, but in a asynchronous manner. To do this, it uses a java.util.concurrent.BlockingQueue as default implementation to queue up messages and disconnect from your main Route thread and then processing the messages in a separated thread. Because of this BlockingQueue, you need to be aware of the usage and config option.

One option needs to be aware of asynchronous processing is the it default to queue size is unbound, meaning it will grow as much as your memory allowed. To cap this, set size=1000. Let’s see an example.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;
import java.io.*;

public class SedaDemoCamel extends Main {
    static Logger LOG = LoggerFactory.getLogger(SedaDemoCamel.class);
    public static void main(String[] args) throws Exception {
        SedaDemoCamel main = new SedaDemoCamel();
        main.enableHangupSupport();
        main.addRouteBuilder(createRouteBuilder1());
        main.addRouteBuilder(createRouteBuilder2());
        main.addRouteBuilder(createRouteBuilder3());
        main.run(args);
    }
    // The file poller route
    static RouteBuilder createRouteBuilder1() {
        return new RouteBuilder() {
            public void configure() {
                from("file://target/input?preMove=staging&move=.processed")
                .process(new Processor() {
                    public void process(Exchange msg) {
                        CamelContext camelContext = msg.getContext();
                        ProducerTemplate producer = camelContext.createProducerTemplate();
                        String text = msg.getIn().getBody(String.class);
                        String fileName = (String)msg.getIn().getHeader("CamelFileName");
                        boolean specialFile = fileName.endsWith("_SPECIAL.dat");
                        if (specialFile)
                            producer.sendBody("seda:specialRoute", text);
                        else
                            producer.sendBody("seda:normalRoute", text);
                    }
                });
            }
        };
    }
    // The special file processing route
    static RouteBuilder createRouteBuilder2() {
        return new RouteBuilder() {
            public void configure() {
                from("seda:specialRoute")
                .process(new Processor() {
                    public void process(Exchange msg) {
                        LOG.info("Processing special file: " + msg);
                    }
                });
            }
        };
    }
    // The normal file processing route
    static RouteBuilder createRouteBuilder3() {
        return new RouteBuilder() {
            public void configure() {
                from("seda:normalRoute")
                .process(new Processor() {
                    public void process(Exchange msg) {
                        LOG.info("Processing normal file: " + msg);
                    }
                });
            }
        };
    }
}

You will notice that this demo code is very similar to the direct component demo, with few differences. First, we use seda endpoints. Second, in file poller, we read in the entire file content text. We do this because we are now passing to an asynchronous Route that will runs on separate threads. The poller is configured to move the processed file into different folder right after the first Route has ended. So we must ensure the processing Route is not depended on the path of the File, hence we will load entire text in instead.

Another interesting seda option is you may set the number of concurrent threads to receive the messages to process them! Let’s say if your normal files are heavy in traffic, then you can configure to use more threads on that part (default is just one thread.)

from("seda:normalRoute?concurrentConsumers=10")
.process(new Processor() {
    public void process(Exchange msg) {
        LOG.info("Processing normal file: " + msg);
    }
});

To verify that your are running concurrently, you can easily configure your logger to display thread name. For example with log4j, you can use this pattern:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %t [%c] - %m%n

There are more options availabe from Seda component that you may explore. Try it out with a Route and see it for yourself.

Sunday, September 8, 2013

Exploring Apache Camel Core - Direct Component

The Apache Camel allows you to create multiple Route's within a single CamelContext space. The direct component in Camel would allow you to bridge messages between these Route's. To demonstrate this, I will create few routes and pass messages between them.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;
import java.io.*;

public class DirectDemoCamel extends Main {
    static Logger LOG = LoggerFactory.getLogger(DirectDemoCamel.class);
    public static void main(String[] args) throws Exception {
        DirectDemoCamel main = new DirectDemoCamel();
        main.enableHangupSupport();
        main.addRouteBuilder(createRouteBuilder1());
        main.addRouteBuilder(createRouteBuilder2());
        main.addRouteBuilder(createRouteBuilder3());
        main.run(args);
    }
    // The file poller route
    static RouteBuilder createRouteBuilder1() {
        return new RouteBuilder() {
            public void configure() {
                from("file://target/input?preMove=staging&move=.processed")
                .process(new Processor() {
                    public void process(Exchange msg) {
                        CamelContext camelContext = msg.getContext();
                        ProducerTemplate producer = camelContext.createProducerTemplate();
                        File file = msg.getIn().getBody(File.class);
                        boolean specialFile = file.getName().endsWith("_SPECIAL.dat");
                        if (specialFile)
                            producer.send("direct:specialRoute", msg);
                        else
                            producer.send("direct:normalRoute", msg);
                    }
                });
            }
        };
    }
    // The special file processing route
    static RouteBuilder createRouteBuilder2() {
        return new RouteBuilder() {
            public void configure() {
                from("direct:specialRoute")
                .process(new Processor() {
                    public void process(Exchange msg) {
                        LOG.info("Processing special file: " + msg);
                    }
                });
            }
        };
    }
    // The normal file processing route
    static RouteBuilder createRouteBuilder3() {
        return new RouteBuilder() {
            public void configure() {
                from("direct:normalRoute")
                .process(new Processor() {
                    public void process(Exchange msg) {
                        LOG.info("Processing normal file: " + msg);
                    }
                });
            }
        };
    }
}

Here I have created 3 Route's and re-used the file component I have introduced in the past. The first Route polls a directory, and then based on the name of the file found, we send it to either to special or normal Route for processing. Because these Route's are separated, we need a bridge channel to pass the messages through, hence it's what the direct component does. The usage is simply use any unique name within the CamelContext, and it will serve as a direct memory queue to pass messages. You may read from or send to these queues. So as you can see, the direct component let you easily breakup a complex route workflow into smaller part.

In above demo, I have also introduced a bit of Camel core features: ProducerTemplate. Within a CamelContext you may create an instance of ProducerTemplate and it will allow you to send any messages to any endpoints dynamically at runtime. Usually you would probably want to store this producer object as member field instead of per each message processing. But for demo purpose, I will leave it as simple as that, and leave you as exercise to explore more on your own.

There are more options availabe from Direct component that you may explore. Try it out with a Route and see it for yourself.

Thursday, September 5, 2013

Exploring Apache Camel Core - File Component

A file poller is a very useful mechanism to solve common IT problems. Camel’s built-in file component is extremely flexible, and there are many options available for configuration. Let’s cover few common usages here.

Polling a directory for input files

Here is a typical Camel Route used to poll a directory for input files on every second.

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import java.io.*;

public class FileRouteBuilder extends RouteBuilder {
    static Logger LOG = LoggerFactory.getLogger(FileRouteBuilder.class);
    public void configure() {
        from("file://target/input?delay=1000")
        .process(new Processor() {
            public void process(Exchange msg) {
                File file = msg.getIn().getBody(File.class);
                LOG.info("Processing file: " + file);
            }
        });
    }
}

Run this with following

mvn compile exec:java -Dexec.mainClass=org.apache.camel.main.Main -Dexec.args='-r camelcoredemo.FileRouteBuilder'

The program will begin to poll your target/input folder under your current directory, and wait for incoming files. To test with input files, you would need to open another terminal, and then create some files as follow.

echo 'Hello 1' > target/input/test1.txt
echo 'Hello 2' > target/input/test2.txt

You should now see the first prompt window start to picking up the files and pass to the next Processor step. In the Processor, we obtain the File object from the message body. It then simply logs it’s file name. You may hit CTRL+C when you are done.

There many configurable options from file componet you may use in the URL, but most of the default settings are enough to get you going as simple case above. Some of these default behavior is such that if the input folder doesn’t exists, it will create it. And when the file is done processing by the Route, it will be moved into a .camel folder. If you don’t want the file at all after processing, then set delete=true in the URL.

Read in the file content and converting to different types

By default, the file component will create a org.apache.camel.component.file.GenericFile object for each file found and pass it down your Route as message body. You may retrieve all your file information through this object. Or alternatively, you may also use the Exchange API to auto convert the message body object to a type you expect to receive (eg: as with msg.getIn().getBody(File.class)). In above example, the File is a type you expect to get from the message body, and Camel hence will try to convert it for you. The Camel uses the context’s registry space to pre-registered many TypeConverter's that can handle most of the common data types (like Java primative etc) conversion. These TypeConverters are powerful way to make your Route and Processor more flexbile and portable.

Camel will not only convert just your File object from message body, but it can also read the file content. If your files are character text based, then you can simply do this.

        from("file://target/input?charset=UTF-8")
        .process(new Processor() {
            public void process(Exchange msg) {
                String text = msg.getIn().getBody(String.class);
                LOG.info("Processing text: " + text);
            }
        });

That’s it! Simply specify String type, and Camel will read your file in and pass the entire file text content as body message. You may even use the charset to change the encoding.

If you are dealing with binary file, then simply try byte[] bytes = msg.getIn().getBody(byte[].class); conversion instead. Pretty cool huh?

Polling and processing large files

When working with large files, there few options in file componet that you might want to use to ensure proper handling. For example, you might want to move the input file into a staging folder before the Route starts the processing; and when it’s done, move it to a .completed folder.

        from("file://target/input?preMove=staging&move=.completed")
        .process(new Processor() {
            public void process(Exchange msg) {
                File file = msg.getIn().getBody(File.class);
                LOG.info("Processing file: " + file);
            }
        });

To feed input files properly into the polling folder, it’s best if the sender generates the input files in a temporary folder first, and only when it’s ready then move it into the polling folder. This will minimize reading an incomplete file by the Route if the input file might take times to generate. Also another solution to this is to config file endpoint to only read the polling folder when there is a signal or ready marker file exists. For example:

        from("file://target/input?preMove=staging&move=.completed&doneFileName=ReadyFile.txt")
        .process(new Processor() {
            public void process(Exchange msg) {
                File file = msg.getIn().getBody(File.class);
                LOG.info("Processing file: " + file);
            }
        });

Above will only read the target/input folder when there is a ReadyFile.txt file exists. The marker file can be just an empty file, and it will be removed by Camel after polling. This solution would allow the sender to generate input files in however long time it might take.

Another concern with large file processing is to avoid loading entire file content into memory for processing. To be more practical, you want to split the file into records (eg: per line) and process it one by one (or called "streaming"). Here is how you would do that using Camel.

        from("file://target/input?preMove=staging&move=.completed")
        .split(body().tokenize("\n"))
        .streaming()
        .process(new Processor() {
            public void process(Exchange msg) {
                String line = msg.getIn().getBody(String.class);
                LOG.info("Processing line: " + line);
            }
        });

This Route will allow you to process large size file without cosuming too much memory and process it line by line very efficiently.

Writing messages back into file

The file component can also be used to write messages into files. Recall that we may use dataset component to generate sample messages. We will use that to feed the Route and send to the file component so you can see that each message generated will be saved into a file.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;
import org.apache.camel.component.dataset.*;

public class FileDemoCamel extends Main {
    static Logger LOG = LoggerFactory.getLogger(FileDemoCamel.class);
    public static void main(String[] args) throws Exception {
        FileDemoCamel main = new FileDemoCamel();
        main.enableHangupSupport();
        main.addRouteBuilder(createRouteBuilder());
        main.bind("sampleGenerator", createDataSet());
        main.run(args);
    }
    static RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
            public void configure() {
                from("dataset://sampleGenerator")
                .to("file://target/output");
            }
        };
    }
    static DataSet createDataSet() {
        return new SimpleDataSet();
    }
}

Compile and run it

mvn compile exec:java -Dexec.mainClass=camelcoredemo.FileDemoCamel

Upon complete you will see that 10 files would be generated in target/output folder with file name in ID-<hostname>-<unique-number>-<msg-seq-num> format.

There are more options availabe from File component that you may explore. Try it out with a Route and see it for yourself.

Wednesday, September 4, 2013

Exploring Apache Camel Core - DataSet Component

A good sample data generator can help you test program more throughly and help measure the processing throughput. The camel-core comes with a dataset component that can help you do this easily. All you need is to provide a bean that implements org.apache.camel.component.dataset.DataSet interface and bind it in CamelContext registry. Here is an example:

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;
import org.apache.camel.component.dataset.*;

public class DataSetDemoCamel extends Main {
    static Logger LOG = LoggerFactory.getLogger(DataSetDemoCamel.class);
    public static void main(String[] args) throws Exception {
        DataSetDemoCamel main = new DataSetDemoCamel();
        main.enableHangupSupport();
        main.addRouteBuilder(createRouteBuilder());
        main.bind("sampleGenerator", createDataSet());
        main.run(args);
    }
    static RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
            public void configure() {
                from("dataset://sampleGenerator")
                .to("log://demo");
            }
        };
    }
    static DataSet createDataSet() {
        return new SimpleDataSet();
    }
}

Compile and run it.

mvn compile exec:java -Dexec.mainClass=camelcoredemo.DataSetDemoCamel

In here we have used the built-in org.apache.camel.component.dataset.SimpleDataSet implementation, which by default will generate 10 messages with a text body set to <hello>world!</hello>. You may easily change the value, or even provide your own implementation starting with org.apache.camel.component.dataset.DataSetSupport base class to customize your data set.

Use DataSet Component to measure throughput

One useful feature of dataset component I found is using it to load test your Route. To do this, you have to adjust couple settings though. Let’s say if I want to load a large text file as sample input data and feed it to the Route, and then measure its throughout.

    static RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
            public void configure() {
                from("dataset://sampleGenerator?produceDelay=0")
                .to("log://demo?groupSize=100");
            }
        };
    }
    static DataSet createDataSet() {
        SimpleDataSet result = new SimpleDataSet();
        result.setSize(500);
        result.setDefaultBody(readFileToString("my-large-sample.txt");
        return result;
    }

Replace above in the Main class and you will notice that it will pump 500 messages into the Route, and it samples every 100 messages and display its throught rates. I have to add produceDelay=0 option so the generator so it will not pause between messages. Then I have added groupSize=100 option to log component for throughput measurement. I skipped readFileToString(String) demo code since I assume you can easily figured that out on your own. (Hint: checkout Apache commons-io library.)

There is another side of dataset component that you may use, and that is to receive and verify message content. You would simply use the same URL in a to(url) line. Internally Camel would assert your message body against your original.

There are more options availabe from DataSet component that you may explore. Try it out with a Route and see it for yourself.

Tuesday, September 3, 2013

Exploring Apache Camel Core - Log Component

There are many ways to log and inspect the messages as it pass through your Camel Route. The camel-core comes with a log component that let you inspect the message. So instead of write a separate Processor just to log a line as got processed, try using this:

from("timer://timer1?period=1s")
.to("log:demo")

By default, the log component will record your message body content through your logger name, demo in above case, at INFO level. Since you can give any name, you can control the logging LEVEL anyway you like through a Camel SLF4J logger implementation.

To log the message in DEBUG level, try this

from("timer://timer1?period=1s")
.to("log:demo?level=DEBUG")

Now if you use log4j as logger implementation, then ensure to add a logger config like this.

log4j.logger.demo = DEBUG
log4j.logger.org.apache.camel = INFO

The Camel message may have Properties and Headers as well, so to display these, you may add showAll=true.

When you process messages that have large body text, it might be more practical to just dislay certain number of characters. To do this, add maxChars=256 to URL.

How to measure Camel messages throughput rate

One of the hidden gem of the log componet is its ability to log messages throughput! You may specific group of messages to be logged, and once it reached that count, it will print the msgs/sec rate output. To enable this, just add groupSize option to URL.

To demo this, I will create a SampleGenerator bean processor that would flood the Route with sample messages. I will use the Camel context registry to bind the bean, and then reference it in the Route. Here is the demo code.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;

public class LogDemoCamel extends Main {
    static Logger LOG = LoggerFactory.getLogger(LogDemoCamel.class);
    public static void main(String[] args) throws Exception {
        LogDemoCamel main = new LogDemoCamel();
        main.enableHangupSupport();
        main.addRouteBuilder(createRouteBuilder());
        main.bind("sampleGenerator", new SampleGenerator());
        main.run(args);
    }
    static RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
            public void configure() {
                from("bean:sampleGenerator")
                .to("log://demo?groupSize=100");
            }
        };
    }
    static class SampleGenerator implements Processor{
        int count = 0;
        public void process(Exchange msg) throws Exception {
            if (count >= 500){
                LOG.info("Max count has reached. Do nothing.");
                Thread.sleep(Long.MAX_VALUE);
                return;
            }

            // Let's generate sample message.
            count++;
            LOG.trace("Generating sample msg #{}", count);
            msg.getOut().setBody("Sample msg");
        }
    }
}

Now you should able to compile and run this demo.

mvn compile exec:java -Dexec.mainClass=camelcoredemo.LogDemoCamel

When running this demo, you will notice the rate will be displayed on console and how fast you can pump message to Route and to process it. This is a very useful feature to help you measure and have a quick view on your Route's capability.

There are more options availabe from Log component that you may explore. Try it out with a Route and see it for yourself.

Monday, September 2, 2013

Exploring Apache Camel Core - Timer Component

Camel Timer is a simple and yet useful component. It brings the JDK’s timer functionality into your camel Route with very simple config.

   from("timer://mytimer?period=1000")
    .process(new Processor() {
        public void process(Exchange msg) {
            LOG.info("Processing {}", msg);
        }
    });

That will generate a timer event message every second. You may short hand 1000 with 1s instead. It supports m for minutes, or h for hours as well. Pretty handy.

Another useful timer feature is that it can limit (stop) the number of timer messages after a certain count. You simply need to add repeatCount option toward the url.

Couple of properties from the event message would be useful when handling the timer message. Here is an example how to read them.

   from("timer://mytimer?period=1s&repeatCount=5")
    .process(new Processor() {
        public void process(Exchange msg) {
            java.util.Date fireTime = msg.getProperty(Exchange.TIMER_FIRED_TIME, java.util.Date.class);
            int eventCount = msg.getProperty(Exchange.TIMER_COUNTER, Integer.class);
            LOG.info("We received {}th timer event that was fired on {}", eventCount, fireTime);
        }
    });

There are more options availabe from Timer component that you may explore. Try it out with a Route and see it for yourself.

Wednesday, August 28, 2013

Running Maven war packaging project without pom config

Use Jetty (latest 9.x requires JDK7)

bash> mvn org.eclipse.jetty:jetty-maven-plugin:run -Djetty.port=8081

Or use Jetty on JDK 6

bash> mvn org.mortbay.jetty:jetty-maven-plugin:8.1.12.v20130726:run -Djetty.port=8081

Or use Tomcat

bash> mvn org.apache.tomcat.maven:tomcat7-maven-plugin:run -Dmaven.tomcat.port=8081

Monday, August 26, 2013

How to create web-app with Quartz Scheduler and logging

I sometimes help out users in Quartz Scheduler forums. Once in a while some one would ask how can he/she setup the Quartz inside a web application. This is actualy a fairly simple thing to do. The library already comes with a ServletContextListener that you can use to start a Scheduler. I will show you a simple webapp example here.

First create a Maven pom.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
        "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>quartz-web-demo</groupId>
    <artifactId>quartz-web-demo</artifactId>
    <packaging>war</packaging>
    <version>1.0-SANPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.0</version>
        </dependency>
    </dependencies>

</project>

Then you need to create a src/main/webapp/META-INF/web.xml file.

<?xml version="1.0" encoding="UTF-8"?>
 <web-app version="2.5"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
        "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

     <context-param>
         <param-name>quartz:config-file</param-name>
         <param-value>quartz.properties</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:shutdown-on-unload</param-name>
         <param-value>true</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:wait-on-shutdown</param-name>
         <param-value>true</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:start-on-load</param-name>
         <param-value>true</param-value>
     </context-param>

     <listener>
         <listener-class>org.quartz.ee.servlet.QuartzInitializerListener</listener-class>
     </listener>

 </web-app>

And lastly, you need a src/main/resources/quartz.properties config file for Scheduler.

# Main Quartz configuration
org.quartz.scheduler.skipUpdateCheck = true
org.quartz.scheduler.instanceName = MyQuartzScheduler
org.quartz.scheduler.jobFactory.class = org.quartz.simpl.SimpleJobFactory
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5

You may configure many other things with Quartz, but above should get you started as in In-Memory scheduler.

Now you should able to compile and run it.

bash> mvn compile
bash> mvn org.apache.tomcat.maven:tomcat7-maven-plugin:2.1:run -Dmaven.tomcat.port=8081

How to configure logging for Quartz Scheduler

Another frequently asked question is how do they setup logging and see the DEBUG level messages. The Quartz Schedulers uses SLF4J, so you have many loggers options to choose. I will show you how to setup Log4j for example below.

First, add this to your pom.xml

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>

Then add src/main/resources/log4j.properties file to show messages onto STDOUT.

log4j.rootLogger=INFO, stdout
log4j.logger.org.quartz=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

Restart your web application on command line, and now you should see all the DEBUG level logging messages coming from Quartz library.

With everything running, your next question might be asking how do you access the scheduler from your web application? Well, when the scheduler is created by the servlet context listener, it is stored inside the web app’s ServletContext space with org.quartz.impl.StdSchedulerFactory.KEY key. So you may retrieve it and use it in your own Servlet like this:

public class YourServlet extends HttpServlet {
    public init(ServletConfig cfg) {
        String key = "org.quartz.impl.StdSchedulerFactory.KEY";
        ServletContext servletContext = cfg.getServletContext();
        StdSchedulerFactory factory = (StdSchedulerFactory) servletContext.getAttribute(key);
        Scheduler quartzScheduler = factory.getScheduler("MyQuartzScheduler");
        // TODO use quartzScheduler here.
    }
}

Now you are on your way to build your next scheduling application!

Have fun!

Saturday, August 24, 2013

MySchedule-3.2.1.0 is out!

The main work on this release is upgrade to Quartz 2.2.1

- Upgraded to Quartz 2.2.1
- Upgraded to SLF4J 1.7.5

- Quartz 2.2.1 API has some changes that break backward compatibility and this release addressed these
  issues:

  -- SchedulerPlugin interface now has different #initialize() signature
  -- SchedulerListener interface has added new method #schedulerStarting()
  -- Scheduler interface has couple new methods addJobs and schedulerJobs with Set instead of List.

Get it here: https://code.google.com/p/myschedule/downloads/list

Wednesday, August 21, 2013

Getting started with Apache Camel using Java

Apache Camel is a very useful library that helps you process events or messages from many different sources. You may move these messages through many different protocols such as between VM, HTTP, FTP, JMS, or even DIRECTORY/FILE, and yet still keep your processing code free of transport logic. This allows you to concentrate on digesting the content of the messages instead.

Here I will provide a tutorial on how you can get started with Apache Camel using Java.

Let’s start by creating a Maven project pom.xml file first.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>camel-spring-demo</groupId>
    <artifactId>camel-spring-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <camel.version>2.11.1</camel.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>${camel.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.5</version>
        </dependency>
    </dependencies>

</project>

We are only going to explore the camel-core, which actually contains quite of few useful components that you may use. Also for logging purpose, I have added a slf4j-simple as a logger implementation so we may see output on console.

Next you just need a class to construct an Route. A Route is like a instruction definition to Camel on how to move your messages from one point to another. We are going to create src/main/java/camelcoredemo/TimerRouteBuilder.java file that will generate a timer message on every second, and then pass to a processor that simply logs it.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;

public class TimerRouteBuilder extends RouteBuilder {
    static Logger LOG = LoggerFactory.getLogger(TimerRouteBuilder.class);
    public void configure() {
        from("timer://timer1?period=1000")
        .process(new Processor() {
            public void process(Exchange msg) {
                LOG.info("Processing {}", msg);
            }
        });
    }
}

That’s all you needed to get started. Now you may build and run this simple demo.

bash> mvn compile
bash> mvn exec:java -Dexec.mainClass=org.apache.camel.main.Main -Dexec.args='-r camelcoredemo.TimerRouteBuilder'

Notice that we didn’t even write a Java main class, but simply use the org.apache.camel.main.Main option to accepts a RouteBuilder class name as parameter. Then it will load and create the route automatically.

Controlling the CamelContext

When you start Camel, it creates a CamelContext object that holds many information on how to run it, including the definition of the Route we created. Now if you want to have more control over this CamelContext, then you would need to write your own Main class. I will show you a simple one here.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.impl.*;
import org.apache.camel.builder.*;

public class TimerMain {
    static Logger LOG = LoggerFactory.getLogger(TimerMain.class);
    public static void main(String[] args) throws Exception {
        new TimerMain().run();
    }
    void run() throws Exception {
        final CamelContext camelContext = new DefaultCamelContext();
        camelContext.addRoutes(createRouteBuilder());
        camelContext.setTracing(true);
        camelContext.start();

        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                try {
                    camelContext.stop();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });

        waitForStop();
    }
    RouteBuilder createRouteBuilder() {
        return new TimerRouteBuilder();
    }
    void waitForStop() {
        while (true) {
            try {
                Thread.sleep(Long.MAX_VALUE);
            } catch (InterruptedException e) {
                break;
            }
        }
    }
}

As you can see, we re-used the existing TimerRouteBuilder class inside createRouteBuilder() method. Our Main class now have full control when to create, start and stop the CamelContext. This context allow you to have control on how to configure Camel globally rather than on Route level. The javadoc link gives all the setter methods that you can explore on what it can do.

Noticed that we also need to provide few setup codes in our Main class. First we need to handle graceful shutdown, so we added a Java shutdown hook to invoke the context stop(). Secondly we need to add a thread block after context has started. The reason for this is that the CamelContext#start() method is non-blocking! If you don’t block your Main thread after start, then it will simply exit right after it, which will have not much use. You want to run Camel as a service (like a server) until you explicitly press CTRL+C to terminate the process.

Improving the Main class to start CamelContext

If you don’t want to deal with much of the Main class setup code such as above, then you may simply extends the org.apache.camel.main.Main class provided by camel-core intead. By piggy-back on this class, you will only not have your Context auto setup, but you will get all the additional command line features such as controlling how long to run the process for, enabling tracing, loading custom route class etc.

Refactoring previous example, here is how it look like.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;

public class TimerMain2 extends Main {
    static Logger LOG = LoggerFactory.getLogger(TimerMain2.class);
    public static void main(String[] args) throws Exception {
        TimerMain2 main = new TimerMain2();
        main.enableHangupSupport();
        main.addRouteBuilder(createRouteBuilder());
        main.run(args);
    }
    static RouteBuilder createRouteBuilder() {
        return new TimerRouteBuilder();
    }
}

Now our TimerMain2 is much shorter, and you may try it out and it should function the same as before.

bash> mvn compile
bash> mvn exec:java -Dexec.mainClass=camelcoredemo.TimerMain2 -Dexec.args='-t'

Notice that we have given -t option and it will dump Route tracing. Use -h and you will see all the available options.

Adding bean to the Camel Registry

In the TimerRouteBuilder example above, we have created a Processor on the fly. Now if you were to combine few different Processor together, it would be nicer to minimize the noise. Camel allow you to do this by registering processing beans in their registry space, and then you simply reference them in your route as bean component. Here is how I can convert above example into beans processing.

package camelcoredemo;

import org.slf4j.*;
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.Main;

public class TimerBeansMain extends Main {
    static Logger LOG = LoggerFactory.getLogger(TimerBeansMain.class);
    public static void main(String[] args) throws Exception {
        TimerBeansMain main = new TimerBeansMain();
        main.enableHangupSupport();
        main.bind("processByBean1", new Bean1());
        main.bind("processAgainByBean2", new Bean2());
        main.addRouteBuilder(createRouteBuilder());
        main.run(args);
    }
    static RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
                public void configure() {
                    from("timer://timer1?period=1000")
                    .to("bean:processByBean1")
                    .to("bean:processAgainByBean2");
                }
            };
    }

    // Processor beans
    static class Bean1 implements Processor {
        public void process(Exchange msg) {
            LOG.info("First process {}", msg);
        }
    }
    static class Bean2 implements Processor {
        public void process(Exchange msg) {
            LOG.info("Second process {}", msg);
        }
    }
}

Now you see my Route is very slim and without noise clutter; and I have refactored my processing code into individual classes. This promotes better code management and testing as you write more complex Route to address business logic. It let you build LEGO like block of re-usable POJO beans. Besides just processing beans, Camel use this registry space for many other services as well. For example you may customize many other component endpoints with additional features and or configurations. Or thing such as thread pool strategy implementation replacement etc.

The Route in example above is constructed using what’s called Java DSL. The route is very readable, and yet you’ll get full IDE support to browse all the methods available to use for your route.

I hope this article has helped you jump start your Camel ride. Besides the timer component mentioned, the camel-core also comes with the following components out of it’s core jar.

Have fun!

Monday, August 19, 2013

How to configure SLF4J with different logger implementations

There are many good benefits in using slf4j library as your Java application logging API layer. Here I will show few examples on how to use and configure it with different loggers.
You can think of slf4j as an Java interface, and then you would need an implementation (ONLY ONE) at runtime to provide the actual logging details, such as writing to STDOUT or to a file etc. Each logging implementation (or called binding) would obviously have their own way of configuring the log output, but your application will remain agnostic and always use the same org.slf4j.Logger API. Let’s see how this works in practice.

Using slf4j with Simple logger

Create a Maven based project and this in your pom.xml.
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
Now you may use Logger in your Java code like this.
package deng;
import org.slf4j.*;
public class Hello {
    static Logger LOGGER = LoggerFactory.getLogger(Hello.class);
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++)
            if (i % 2 == 0)
                LOGGER.info("Hello {}", i);
            else
                LOGGER.debug("I am on index {}", i);
    }
}
The above will get your program compiled, but when you run it, you will see these output.
bash> java deng.Hello
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
What it’s saying is that at runtime, you are missing the logging "implementation" (or the logger binding), so slf4j simply use a "NOP" implmentation, which does nothing. In order to see the output properly, you may try use an simple implementation that does not require any configuration at all! Just go back to your pom.xml and add the following:
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.5</version>
    </dependency>
Now you see logging output on STDOUT with INFO level. This simple logger will default show any INFO level message or higher. In order to see DEBUG messages, you would need to pass in this System Property -Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG at your Java startup.

Using slf4j with Log4j logger

Now we can experiment and swap different logger implementations, but your application code can remain the same. All we need is to replace slf4j-simple with another popular logger implementation, such as the Log4j.
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.5</version>
    </dependency>
Again, we must configure logging per implementation that we picked. In this case, we need an src/main/resources/log4j.properties file.
    log4j.rootLogger=DEBUG, STDOUT
    log4j.logger.deng=INFO
    log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
    log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
    log4j.appender.STDOUT.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
Re-run your program, and you should see similar output.

Using slf4j with JDK logger

The JDK actually comes with a logger package, and you can replace pom.xml with this logger implementation.
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk14</artifactId>
        <version>1.7.5</version>
    </dependency>
Now the configuration for JDK logging is a bit difficult to work with. Not only need a config file, such as src/main/resources/logging.properties, but you would also need to add a System properties -Djava.util.logging.config.file=logging.properties in order to have it pick it up. Here is an example to get you started:

.level=INFO
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINEST
deng.level=FINEST

Using slf4j with Logback logger

The logback logger implementation is a super dupa quality implementation. If you intend to write serious code that go into production, you may want to evaluate this option. Again modify your pom.xml to replace with this:
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.0.13</version>
    </dependency>
Here is a sample of configuration src/main/resources/logback.xml to get things started.
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="deng" level="DEBUG"/>

  <root level="INFO">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Writing your own library with slf4j logger

If you are providing an Java library for large end users consumption, it’s good idea to set your project to depend on slf4j-api only, and then let your user choose any logger implementation at their development or runtime environment. As end users, they may quickly select one of option above and take advatage of their own favorite logging implementation features.