Monday, May 19, 2008

HTML Tag Template - Interface

     This is my first try out of Enum since Java5 came out. The trigger of this idea is that we outsourced the use of Struts along with all it's tags. So there is no easy way for us to write forms in jsp files. I won't say that my code is competitive with Struts' or any other open/share source's tags, but it will come in handy in many ways, at least it is for me.

     I started out with a simple interface.

HTMLTagTemplate.java

package net.yw.html;

import java.util.Map;

/**
 * @author KWang
 *
 */
public interface HTMLTagTemplate {
    
    public String doStart(Map<String, Object> propertyMap) throws HTMLTagException;
    
    public String doEnd();
}


HTMLTagException.java

package net.yw.html;

/**
 * @author KWang
 *
 */
public class HTMLTagException extends Exception {

    /**
     * 
     */
    private static final long serialVersionUID = -1L;

    /**
     * 
     */
    public HTMLTagException() {
    }

    /**
     * @param message
     */
    public HTMLTagException(String message) {
        super(message);
    }

    /**
     * @param cause
     */
    public HTMLTagException(Throwable cause) {
        super(cause);
    }

    /**
     * @param message
     * @param cause
     */
    public HTMLTagException(String message, Throwable cause) {
        super(message, cause);
    }

}


     In my next post, I'll talk about how to implement HTMLTagTemplate for general form tag use.

Example of Spring Based Ajax Servlet

     A lot people are into GWT when it comes to AJAX. No offense, but I found it disturbing to create a servlet for each AJAX action. So for my projects, I wrote one universal servlet for all my AJAX uses.

     I know there are all kinds of AJAX resources out there. But I started out with prototype and JSON's Json-lib. So, my servlet is also based on these two.

     Let's first take a look at the Servlet:

SpringBasedAjaxServlet.java

package com.mycompany.ajax.servlet;

import java.io.PrintWriter;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;

/**
 * @author KWang
 *
 */
public class SpringBasedAjaxServlet extends FrameworkServlet {
    
    private static final long serialVersionUID = 1L;
    protected static Logger log = Logger.getLogger(SpringBasedAjaxServlet.class);

    /* (non-Javadoc)
     * @see org.springframework.web.servlet.FrameworkServlet#doService(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String json = null;

        try {
            json = getJSONContent(request, response);
        } catch (Exception ex) {
            // Send back a 500 error code.
            log.debug(ex);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Can not create response");
            return;
        }

        response.setContentType("text/json; charset=UTF-8");
        response.setHeader("Cache-Control", "no-cache");
        // this will be the second parameter in onXxxxxx function, already
        // parsed as object
        // unfortunately this header won't work with i18n
        // if(StringUtils.isNotBlank(json))
        // response.setHeader("X-JSON", json);
        PrintWriter pw = response.getWriter();
        // has to set json here if it's not null
        // add security comment delimiters defined by prototype 1.5.1
        pw.write("/*-secure-\n" + json + "\n*/");
        pw.close();
    }

    /**
     * Each child class should override this method to generate the specific
     * JSON content necessary for each AJAX action.
     * 
     * @param request
     *            the {@javax.servlet.http.HttpServletRequest} object
     * @return a {@java.lang.String} representation of the JSON response/content
     */
    private String getJSONContent(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String action = request.getParameter("$action");
        String m = request.getParameter("$method");
        log.debug("calling method name: " + m);
        if (StringUtils.isNotBlank(action)) {
            Object actionClass = this.getWebApplicationContext().getBean(action);
            Method method = actionClass.getClass().getMethod(m, HttpServletRequest.class);
            if (method == null) 
                throw new NoSuchRequestHandlingMethodException(m, getClass());
            return (String) method.invoke(actionClass, new Object[] { request });
        } else {
            //for use with servlets that extend this class
            Method method = this.getClass().getMethod(m, HttpServletRequest.class);
            if (method == null)
                throw new NoSuchRequestHandlingMethodException(m, getClass());
            customSetting();
            return (String) method.invoke(this, new Object[] { request });
        }
    }


    protected void customSetting(){
        //if you extend this class, wire your beans here
    }
}


web.xml


    ...

    <servlet>
        <servlet-name>ajax</servlet-name>
        <servlet-class>com.mycompany.ajax.servlet.SpringBasedAjaxServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    ...

    <servlet-mapping>
        <servlet-name>ajax</servlet-name>
        <url-pattern>/AjaxServlet</url-pattern>
    </servlet-mapping>

    ...


     This servlet can be used in two ways. First, the servlet will get an action parameter from the request. If the parameter exists, then the servlet will retrieve the action class bean, invoke the specified method, and return the result as a String. Note that the method's name should also be passed in as a request parameter. And, the method should take in an HttpServletRequest (or any kind of request wrapper as you need) as argument. This should be used when there are a lot AJAX actions that cannot be hold in one class. One good reason for me to use the servlet this way is that I can use one action for both AJAX and non-AJAX calls. All I need to do is pass in a request parameter indicating whether it's an AJAX call or not and return different form of object, or return nothing at all, accordingly.

     The second way is extending this SpringBasedAjaxServlet and write all your AJAX calls inside the servlet. The customSetting method needs to be overwritten if you need to wire additional spring beans. It's a great way to use this servlet if you only have a few AJAX calls which are used widely across the application.

     That's a wrap for my 2 cents.