Shahzad Bhatti Welcome to my ramblings and rants!

November 22, 2008

Implementing Actor-based message passing using Object-oriented methods

Filed under: Computing — admin @ 11:01 am

I wrote about use of actor based programming for parallel and concurrent programming previously [ 1 , 2 ], where an actor keeps its own thread of execution with a mailbox. Actors communicates to other actors by sending messages that are delivered to its mailbox. In this model, the message passing takes literal form rather than method invocation. I studied Actor based parallel computing in my post graduate research on parallel and distributed field. I learned message passing systems such as Actors, MPI, MPL were much more scalable than shared memory. This describes popularity of languages like Erlang and Scala that has adopted Actor model to tackle concurrency (object-oriented nature in Erlang). According to Ralph Johnson concurrent Erlang is object oriented. Alan Kay has also argued that message passing is more important than objects in object-oriented programming and regrets that he chose the term “object-oriented” instead of “message-passing”. Even Joe Armstrong, who generally dislikes object oriented programming admits similarity between object-oriented programming and Erlang’s message passing.

However, despite effectiveness of message passing in new era of concurrency, I find that pure message passing is a bit intimidating to an average programmer. In this blog, I will show how an object-oriented methods can be used to abstract actor-based message passing. The primary benefit of message passing alongside with immutability is its support of concurrency so the following example will demonstrate use of message passing in multiple threads. Let’s suppose, we are designing a simple chat service to send and receive messages and the message looks like following:

package actor;

public class ChatMessage {
    public final String fromUser;
    public final String toUser;
    public final String message;

    public ChatMessage(String fromUser, String toUser, String message) {
        this.fromUser = fromUser;
        this.toUser = toUser;
        this.message = message;
    }
}

Following is an interface for the chat service:

package actor;

import java.util.Collection;

public interface ChatService {
    /**
     * sends a message to another user * @param message - message object
     */
    @Asynchronous
    public void sendMessage(ChatMessage message);

    /**
     * retrieves messages that were sent to the user. * @param user - target user who received the messages * @return - collection of messages
     */
    public Collection<ChatMessage> getMessages(String user);

    /**
     * remove all messages
     */
    @Asynchronous
    public void clear();

    /**
     * return number of messages
     */
    public long countMessages();

    /**
     * @throws IllegalArgumentException
     */
    public void raiseError() throws IllegalArgumentException;
}

Note that above interface uses “@Asynchronous” annotation for “sendMessage” and “clear” methods. One of the benefit of message-passing is that you can optionally send asynchronous messages. So I defined an annotation to declare a particular method as asynchronous, e.g.

package actor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Asynchronous {
}
Following code implements the chat service in non-thread-safe fashion:
package actor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * Simple non-thread-safe implementation of ChatService
 */
public class ChatServiceImpl implements ChatService {
    private long count;
    private Map<String, Collection<ChatMessage>> messagesByUsers;

    public ChatServiceImpl() {
        messagesByUsers = new HashMap<String, Collection<ChatMessage>>();
    }

    /**
     * sends a message to another user * @param message - message object
     */
    public void sendMessage(ChatMessage message) {
        getMessages(message.toUser).add(message);
        count++;
    }

    /**
     * retrieves messages that were sent to the user. * @param user - target user who received the messages * @return - collection of messages
     */
    public Collection<ChatMessage> getMessages(String user) {
        Collection<ChatMessage> messages = messagesByUsers.get(user);
        if (messages == null) {
            messages = new ArrayList<ChatMessage>();
            messagesByUsers.put(user, messages);
        }
        return messages;
    }

    /**
     * remove all messages
     */
    public void clear() {
        messagesByUsers.clear();
    }

    /**
     * return number of messages
     */
    public long countMessages() {
        return count;
    }

    /**
     * @throws IllegalArgumentException
     */
    public void raiseError() throws IllegalArgumentException {
        throw new IllegalArgumentException();
    }
}

Note that above code cannot be used in multi-threading environment. If we were building thread-safe service using Java, we would have to add synchronization or locking such as:

package actor;

import java.util.Collection;

/**
 * Simple thread-safe implementation of ChatService
 */
public class SafeChatServiceImpl implements ChatService {
    private ChatService delegate;

    public SafeChatServiceImpl(ChatService delegate) {
        this.delegate = delegate;
    }

    /**
     * sends a message to another user * @param message - message object
     */
    public synchronized void sendMessage(ChatMessage message) {
        delegate.sendMessage(message);
    }

    /**
     * retrieves messages that were sent to the user. * @param user - target user who received the messages * @return - collection of messages
     */
    public synchronized Collection<ChatMessage> getMessages(String user) {
        return delegate.getMessages(user);
    }

    /**
     * remove all messages
     */
    public synchronized void clear() {
        delegate.clear();
    }

    /**
     * return number of messages
     */
    public synchronized long countMessages() {
        return delegate.countMessages();
    }

    /**
     * @throws IllegalArgumentException
     */
    public synchronized void raiseError() throws IllegalArgumentException {
        delegate.raiseError();
    }
}
Above code demonstrates use of composition to convert a non-thread-safe class into a thread-safe class.

Now the meat of this library is following factory class that converts any Java object into an actor:

package actor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * Factory class to convert any POJO class into Actor.
 */
public class ActorFactory {
    private static final int MAIL_BOX_SIZE = 100;

    private static class MethodRequest {
        private final Object object;
        private final Method method;
        private final Object[] args;
        private final BlockingQueue<MethodResponse> replyQueue;

        private MethodRequest(Object object, Method method, Object[] args, BlockingQueue<MethodResponse> replyQueue) {
            this.object = object;
            this.method = method;
            this.args = args;
            this.replyQueue = replyQueue;
        }
    }

    private static class MethodResponse {
        private Object response;
        private Exception exception;

        private MethodResponse(Object response) {
            this.response = response;
        }

        private MethodResponse(Exception exception) {
            this.exception = exception;
        }

        Object getResponse() throws Exception {
            if (exception != null) throw exception;
            return response;
        }
    }

    private static class ActorThread extends Thread {
        private volatile boolean shutdown;
        private BlockingQueue<MethodRequest> queue;

        private ActorThread(BlockingQueue<MethodRequest> queue) {
            this.queue = queue;
            setDaemon(true);
        }

        public void run() {
            while (!shutdown) {
                try {
                    MethodRequest request = queue.take();
                    try {
                        Object response = request.method.invoke(request.object, request.args);
                        if (request.replyQueue != null) {
                            request.replyQueue.put(new MethodResponse(response));
                        }
                    } catch (InvocationTargetException e) {
                        if (request.replyQueue != null) {
                            request.replyQueue.put(new MethodResponse((Exception) e.getCause()));
                        } else {
                            e.printStackTrace();
                        }
                    } catch (Exception e) {
                        if (request.replyQueue != null) {
                            request.replyQueue.put(new MethodResponse(e));
                        } else {
                            e.printStackTrace();
                        }
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupted();
                }
            }
        }
    }

    public static Object create(final Class ifaceClass, final Object object) {
        final BlockingQueue<MethodRequest> queue = new ArrayBlockingQueue<MethodRequest>(MAIL_BOX_SIZE);
        new ActorThread(queue).start();
        final InvocationHandler handler = new InvocationHandler() {
            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
                if (method.isAnnotationPresent(Asynchronous.class)) {
                    queue.put(new MethodRequest(object, method, args, null));
                    return null;
                } else {
                    final BlockingQueue<MethodResponse> replyQueue = new ArrayBlockingQueue<MethodResponse>(1);
                    queue.put(new MethodRequest(object, method, args, replyQueue));
                    return replyQueue.take().getResponse();
                }
            }
        };
        return ifaceClass.cast(Proxy.newProxyInstance(ifaceClass.getClassLoader(), new Class<?>[]{ifaceClass}, handler));
    }

    public static Object createWithoutActor(final Class ifaceClass, final Object object) {
        final InvocationHandler handler = new InvocationHandler() {
            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
                Object response = method.invoke(object, args);
                return response;
            }
        };
        return ifaceClass.cast(Proxy.newProxyInstance(ifaceClass.getClassLoader(), new Class<?>[]{ifaceClass}, handler));
    }
}
The ActoryFactory uses reflection and proxy features of Java. The client code creates an actor by invoking
public static Object create(final Class ifaceClass, final Object object) {
  ...
}

ChatService service = (ChatService) ActorFactory.create(ChatService.class, new ChatServiceImpl());
      
The create method instantiates a blocking queue that serves as mailbox for the actor and then starts a thread. The create method then instantiates InvocationHandler that intercepts all methods. Finally, the proxy object is returned to the client. When the client invokes any method it is intercepted by the proxy, which then sends the message to the mailbox of the actor. The proxy also checks if the method i defined as asynchronous then it does not wait for the reply. The thread simply waits until there is a message in the mailbox. The message for the actor is defined in MethodRequest that encapsulates reflection information to invoke a method. The actor thread invokes the method using reflection and puts back reply in the queue supplied by the proxy. The proxy waits for the reply and then sends back the reply back to the client. As an actor serves one request at a time, there is no need for locking or synchronization.

Following code demonstrate client test code:

package actor;

import junit.framework.*;

import java.util.Collection;

public class ActorTest extends TestCase {
    private static ChatService service;
    private static final int NUM_THREADS = 100;
    private static final int NUM_MESSAGES = 100;

    protected void setUp() throws Exception {
        super.setUp();
    }

    public void testSendGetMessages() throws Exception {
        for (int i = 0; i < NUM_MESSAGES; i++) {
            final String suffix = Thread.currentThread().getName() + "--" + i;
            ChatMessage janeToAdamMessage = new ChatMessage("jane" + suffix, "adam" + suffix, "hi there" + suffix);
            service.sendMessage(janeToAdamMessage);
            ChatMessage adamToJaneMessage = new ChatMessage("adam" + suffix, "jane" + suffix, "hi there too" + suffix);
            service.sendMessage(adamToJaneMessage);
            Collection<ChatMessage> janeMessages = service.getMessages("jane" + suffix);
            assertEquals(1, janeMessages.size());
            assertEquals("hi there too" + suffix, janeMessages.iterator().next().message);
            Collection<ChatMessage> adamMessages = service.getMessages("adam" + suffix);
            assertEquals(1, adamMessages.size());
            assertEquals("hi there" + suffix, adamMessages.iterator().next().message);
        }
    }

    public void testError() throws Exception {
        try {
            service.raiseError();
            fail("should have thrown IllegalArgumentException");
        } catch (IllegalArgumentException e) {
        }
    }

    public static Test suite() {
        TestSuite suite = new TestSuite(ActorTest.class);
        return suite;
    }

    private static void runTestsWith(ChatService svc, String logMessage) throws InterruptedException {
        service = svc;
        long started = System.currentTimeMillis();
        Thread[] threads = new Thread[NUM_THREADS];
        for (int i = 0; i < NUM_THREADS; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                    junit.textui.TestRunner.run(suite());
                }
            });
            threads[i].start();
        }
        for (int i = 0; i < NUM_THREADS; i++) {
            threads[i].join();
        }
        assertEquals(NUM_THREADS * NUM_MESSAGES * 2, service.countMessages());
        long elapsed = System.currentTimeMillis() - started;
        System.out.println("Completed " + logMessage + " in " + elapsed + " millis");
    }

    public static void main(String[] args) throws Exception {
        runTestsWith(new SafeChatServiceImpl(new ChatServiceImpl()), "thread-safe direct");
        runTestsWith((ChatService) ActorFactory.create(ChatService.class, new ChatServiceImpl()), "Actor model");
        runTestsWith((ChatService) ActorFactory.createWithoutActor(ChatService.class, new SafeChatServiceImpl(new ChatServiceImpl())), "thread-safe reflection");
    }
}
Above class demonstrates a variety of way you can instantiate service class, e.g. by simply invoking the constructor, using reflection and using actors. Note that you cannot simply run non-thread-safe implementation because it would produce incorrect results. For each type of service, the test creates 100 threads that try to send and receive messages. By the end of each test, 200,000 messages are sent and retrieved. Following are runtimes for the service invocation:
 Completed thread-safe direct in 286 millis
 Completed Actor model in 1412 millis
 Completed thread-safe reflection in 693 millis

As expected, using builtin synchronization yields the fastest response, reflection adds some overhead and finally actor based communication adds considerable more overhead. One of the feature of Erlang is its support of seamless networking support. Using an abstraction layer as above can easily be enhanced to start an actor on remote machine. Though, in that case the service will only exist on the remote server. As threads in Java are natives and map to light-weight process, they are much more heavy weight than process in Erlang, which only consumes 300 bytes per process. I have found that native threads are more suitable for CPU-bound processing and green or user-based threads suit well for IO-bound processing. In the end, biggest gain of above design from programmer’s perspective is that the client code looks exactly like any other object oriented and unlike Erlang you get real type safety in message passing. Another big advantage is use of polymorphism, above technique uses dependency inversion principle to interface and implementation separate, thus actual implementation can be changed as needed.

You can download all code from here

November 5, 2008

Ten things I hate about Enhydra Shark

Filed under: Computing — admin @ 12:17 pm

I am trying to use Enhydra Shark at work for implementing a workflow system, but I have been finding a lot of issues with it. Here are ten things that I found absolutely aweful about the Enhydra Shark:

  1. All methods throw Exception as part of the signature instead of specific exceptions.
  2. It uses constants instead of enums.
  3. Many classes use public fields and underscore as part of the name, e.g.
                      public final class AppParameter {
                                     public String the_actual_name = null;
     

    it reminds me of a quote ‘You can write FORTRAN in any language”.

  4. Every single Shark method requires transactions and many of the methods open and close several transactions per method, e.g. real code from another group using Shark:
                userTxn = getUserTransaction();
                userTxn.setTransactionTimeout(600);
                userTxn.begin();
                                                                                                  
                 // Connect to the shark process repository
                 SharkConnection connection = shark.getSharkConnection();
                 connection.attachToHandle(executor.getSharkSessionHandle());
     
                 WfProcess process = loadProcess(connection, userTxn);
                 userTxn.commit();
     
                 userTxn.begin();
                 WfActivity activity = loadActivity(shark, userTxn, connection);
                 userTxn.commit();
                                                                                                  
                 userTxn.setTransactionTimeout(600);
                 userTxn.begin();
                 WMSessionHandle toolAgentSession = toolAgent.connect(connectionInfo);
                 toolAgent.invokeApplication(session, toolAgentSession.getId(),
                             appInfo, toolInfo, null, processInstanceId, assignmentId,
                             parameters, appMode);
                 connection.disconnect();
                 userTxn.commit();
     

    Worst, some of the methods require both local and XA transactions.

  5. It uses its own connection factory so I can’t use apache commons or other connection pool. It defines connection properties in several files, which don’t work as expected, e.g. some parameters are used for upper bound and other for lower bound.
  6. It uses its own transaction factory so I can’t use declarative transactions using annotations or Spring. It is just not designed to work with any other framework.
  7. It requires each JVM to connect with a unique engine name and another team had to generate that name on startup.
  8. Its community does not answer questions (I never got response of my questions) and last activity on the mailing list was 10 months old.
  9. It does not use latest XPDL 2.0, which has been released a few years ago.
  10. Due to explicit connection management and transaction management several times per method, other groups using Shark found a lot of problems with multi-threading, scalability and reliability.

I like the frameworks or languages that follow “principle of least surprise”, on the other hand Shark follows “principle of voodoo magic” to get things working. In the end, I have not completely given up on Shark engine, but I have very low expectations.

October 22, 2008

Developing REST based services using JSR 311

Filed under: Java — admin @ 1:28 pm

Recently, I had to write a few services using REST style in Java. Though I have developed such services in past either using Java Servlet APIs, but I wanted to find something better. I had found writing REST style services in Ruby on Rails a breeze and I wanted something as testable and easy to configure. I found a new JSR 311 that does exactly that. Similar to EJB 3.0 and Servlet 3.0 trend, it allows you to write REST services using annotations. I found an open source project that implements JSR 311 and just released 1.0. I found most of the documentation was Maven and Glassfish driven and I wanted to simply try on Tomcat and Ant so here is how I did it:

Download and Install

First, download following jar libraries (you can see that I am using jars from maven repository because Jersey’s wiki was missing jars or pointing to old version):

Developing

My application used models to describe business object, DAOs for database access and I didn’t want to pollute my business objects with REST annotations so I created two new packages for resources and services. Here is a simple example that follows this separation of concerns:

Business Objects (Model)

The model simply consists of a contact class that stores contact information for a person.

 
 package rest;
 
 public class Contact {
     private final String name;
     private final String email;
     private final String address;
 
     public Contact(final String name, final String email, final String address) {
         this.name = name;
         this.email = email;
         this.address = address;
     }
 
 
     public String getName() {
         return name;
     }
 
     public String getEmail() {
         return email;
     }
 
     public String getAddress() {
         return address;
     }
 }
 

Data Access Layer

The data access layer simply uses a hashmap for storing and accessing these contacts, e.g.

 
 package rest;
 
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
 public class ContactDao {
     private static Map repository = new HashMap();
 
     public boolean save(Contact c) {
         boolean created = repository.get(c.getName()) != null;
         repository.put(c.getName(), c);
         return created;
     }
 
     public boolean delete(String name) {
         return repository.remove(name) != null;
     }
 
     public Contact get(String name) {
         return repository.get(name);
     }
 
     public Collection getAll() {
         return repository.values();
     }
 }
 
 

Resources

For resources, I defined ContactResource that adds XML annotations to convert Contact into XML and ContactsResponse for returning complete XML response, e.g.

 
 package rest;
 import javax.ws.rs.core.UriInfo;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.codehaus.jettison.json.JSONException;
 import org.codehaus.jettison.json.JSONObject;
                                                                                                                                                       
 
 @XmlAccessorType(XmlAccessType.PROPERTY)
 @XmlRootElement(name = "Contact")
 public class ContactResource {
     private UriInfo uriInfo;
     private Contact contact;
 
     public ContactResource(final UriInfo uriInfo, final String name, final String email, final String address) {
         this.uriInfo = uriInfo;
         this.contact = new Contact(name, email, address);
     }
 
 
     public ContactResource(final Contact contact) {
         this.contact = contact;
     }
 
 
     ContactResource() {
     }
 
 
     public JSONObject toJson() throws JSONException {
         return new JSONObject()
             .put("name", contact.getName())
             .put("email", contact.getEmail())
             .put("address",contact.getAddress());
     }
 
     @XmlElement(name = "Name")
     public String getName() {
         return contact.getName();
     }
 
     @XmlElement(name = "Email")
     public String getEmail() {
         return contact.getEmail();
     }
 
     @XmlElement(name = "Address")
     public String getAddress() {
         return contact.getAddress();
     }
 }
 
 
 

 
 package rest;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
 
 
 @XmlAccessorType(XmlAccessType.PROPERTY)
 @XmlRootElement(name = "ContactsResponse")
 public class ContactsResponse {
     private String uri;
     private String status;
     private Collection contacts;
 
     public ContactsResponse(String uri, String status, Collection contacts) {
         this.uri = uri;
         this.status = status;
         this.contacts = new ArrayList();
         for (Contact contact : contacts) {
             this.contacts.add(new ContactResource(contact));
         }
     }
 
     public ContactsResponse(String uri, String status, Contact contact) {
         this(uri, status, contact == null ? new ArrayList() : Collections.singleton(contact));
     }
 
     ContactsResponse() {
     }
 
     @XmlElement(name = "Uri")
     public String getUri() {
         return uri;
     }
     public void setUri(String uri) {
         this.uri = uri;
     }
     @XmlElement(name = "Status")
     public String getStatus() {
         return status;
     }
     public void setStatus(String status) {
         this.status = status;
     }
 
     @XmlElement(name = "Contacts")
     public Collection getContactResources() {
         return contacts;
     }
     public void setContactResources(Collection contacts) {
         this.contacts = contacts;
     }
 }
 
 

Service

Here is the meat of JSR 311 that defines annotations for the REST based web service, i.e.,

 
 package rest;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Collection;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.codehaus.jettison.json.JSONArray;
 import org.codehaus.jettison.json.JSONException;
 import org.springframework.context.annotation.Scope;
 
 import com.sun.jersey.api.spring.Autowire;
 
 @Path("/contacts/")
 @Produces( { "application/json", "application/xml" })
 @Consumes( { "application/json", "application/xml" })
 @Scope("singleton")
 @Autowire
 public class ContactService {
     private final ContactDao contactDao;
     @Context UriInfo uriInfo;
     @Context Request request;
 
     public ContactService(ContactDao dao) {
         this.contactDao = dao;
     }
 
     public ContactService() {
         this(new ContactDao()); // this will be injected in real-app
     }
 
     /**
      * create contact
      */
     @PUT
     @Consumes("*/*")
     @Produces("application/xml")
     @Path("{name}")
     public Response createcontact(
             @PathParam("name") String name,
             @FormParam("email") String email,
             @FormParam("address") String address) {
         Contact contact = new Contact(name, email, address);
         final boolean newRecord = contactDao.save(contact);
         if (newRecord) {
             try {
                 URI uri = uriInfo != null ? uriInfo.getAbsolutePath()
                         : new URI("/contacts/");
                 return Response.created(uri).build();
             } catch (URISyntaxException e) {
                 throw new RuntimeException("Failed to create uri", e);
             }
         } else {
             return Response.noContent().build();
         }
     }
 
     /**
      * deletes contact 
      * 
      */
     @DELETE
     @Consumes("*/*")
     @Path("{name}")
     public Response deletecontact(@PathParam("name") String name) {
         boolean deleted = contactDao.delete(name);
         if (deleted) {
             return Response.ok().build();
         } else {
             return Response.status(404).build();
         }
     }
 
     /**
      * @return contact in XML format
      */
     @GET
     @Consumes({"text/xml", "application/xml"})
     @Produces("application/xml")
     @Path("{name}")
     public ContactsResponse getcontactByXml(
             @PathParam("name") String name) {
         Contact contact = contactDao.get(name);
         String uri = uriInfo != null ? uriInfo.getAbsolutePath().toString() : "/contacts/";
         return new ContactsResponse(uri, "success", contact);
     }
 
     /**
      * @return contact in JSON format
      */
     @GET
     @Consumes("application/json")
     @Produces("application/json")
     @Path("{name}")
     public JSONArray getcontactByJson(@PathParam("name") String name) throws JSONException {
         Contact contact = contactDao.get(name);
         JSONArray arr = new JSONArray();
         arr.put(new ContactResource(contact).toJson());
         return arr;
     }
 
     /**
      * @return all contacts in XML format
      */
     @GET
     @Consumes({"text/xml", "application/xml"})
     @Produces("application/xml")
     public ContactsResponse getAllByXml() {
         Collection contacts = contactDao.getAll();
         String uri = uriInfo != null ? uriInfo.getAbsolutePath().toString() : "/contacts/";
         return new ContactsResponse(uri, "success", contacts);
     }
 
     /**
      * @return contacts in JSON format
      */
     @GET
     @Consumes("application/json")
     @Produces("application/json")
     public JSONArray getAllByJson() throws JSONException {
         Collection contacts = contactDao.getAll();
         JSONArray arr = new JSONArray();
         for (Contact contact : contacts) {
             arr.put(new ContactResource(contact).toJson());
         }
         return arr;
     }
 }
 
 

A few things to note:

  • @Path defines the URI used for accessing the service
  • I am using @PUT to store contacts (as the user is creating new URI as opposed to @POST where the application uses same URI). Also, I don’t have any method for update (which uses PUT) as I already am using PUT and create method simply updates the contact if it already exist.
  • @PathParam is retrieved from the URI itself, e.g. /contacts/myname
  • @FormParam is retrieved from POST form submission
  • I can use the same URI and return different type of data based on Content-Type, e.g. when user sets it to application/xml or text/xml I return XML content and when user sets it to application/json I return JSON format.
  • To return list of contacts I skip the name and simply use /contacts/

Servlet Configuration

I added servlet to handle REST requests to web.xml, e.g.

     <servlet>
         <servlet-name>RestServlet</servlet-name>
         <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
         <init-param>
             <param-name>com.sun.jersey.config.feature.Redirect</param-name>
             <param-value>true</param-value>
         </init-param>
         <init-param>
             <param-name>com.sun.jersey.config.feature.ImplicitViewables</param-name>
             <param-value>true</param-value>
         </init-param>
         <init-param>
             <param-name>com.sun.jersey.config.property.packages</param-name>
             <param-value>rest</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
     </servlet>
     <servlet-mapping>
         <servlet-name>RestServlet</servlet-name>
         <url-pattern>/svc/*</url-pattern>
     </servlet-mapping>
 

I used Spring to inject DAOs in real application but if you don’t need it then replace com.sun.jersey.spi.spring.container.servlet.SpringServlet with com.sun.jersey.spi.container.servlet.ServletContainer.

Also note that com.sun.jersey.config.property.packages defines package name of Java classes that defines service classes.

Deploying

I am assuming you already know how to package a war file and deploy it to application server such as Tomcat.

Testing it

Unit Testing it

A big advantage of JSR 311 is ease of test, e.g. here is a sample unit test:

 
 package rest;
 import javax.ws.rs.core.Response;
 
 import junit.framework.TestCase;
 import org.codehaus.jettison.json.JSONArray;
 import org.codehaus.jettison.json.JSONObject;
 
 import com.sun.jersey.api.NotFoundException;
 
 public class ContactServiceTest extends TestCase {
     private ContactService service;
 
     @Override
     protected void setUp() throws Exception {
         service = new ContactService();
     }
 
     public void testCreateContactWithGetContactByXml() {
         final String name = "shahbhat";
         final String email = "shahbhat@myhost";
         final String address = "shahbhat address ";
 
         Response response = service.createContact(name, email, address);
         assertEquals(201, response.getStatus());
         assertNull(response.getEntity());
 
         // recreate the same mapping and it should return no content
         response = service.createContact(name, email, address);
         assertEquals(204, response.getStatus());
         assertNull(response.getEntity());
 
         ContactsResponse contactResponse = service.getContactByXml(name);
         assertEquals(1, contactResponse.getContactResources().size());
         ContactResource contactResource = contactResponse.getContactResources().iterator().next();
         assertEquals(name, contactResource.getName());
         assertEquals(email, contactResource.getEmail());
         assertEquals(address, contactResource.getAddress());
     }
 
     public void testCreateContactWithGetContactByJson() throws Exception {
         final String name = "shahbhat";
         final String email = "shahbhat@myhost";
         final String address = "shahbhat address ";
 
         Response response = service.createContact(name, email, address);
         assertEquals(201, response.getStatus());
         assertNull(response.getEntity());
 
         JSONArray jsonArray = service.getContactByJson(name);
         assertEquals(1, jsonArray.length());
         JSONObject json = jsonArray.getJSONObject(0);
         assertEquals(name, json.getString("name"));
         assertEquals(email, json.getString("email"));
         assertEquals(address, json.getString("address"));
     }
 
     public void testDeleteContact() {
         final String name = "shahbhat";
         final String email = "shahbhat@myhost";
         final String address = "shahbhat address ";
 
         Response response = service.createContact(name, email, address);
         assertEquals(201, response.getStatus());
         assertNull(response.getEntity());
 
         ContactsResponse contactResponse = service.getContactByXml(name);
         assertEquals(1, contactResponse.getContactResources().size());
         ContactResource contactResource = contactResponse.getContactResources().iterator().next();
         assertEquals(name, contactResource.getName());
         assertEquals(email, contactResource.getEmail());
         assertEquals(address, contactResource.getAddress());
 
         response = service.deleteContact(name);
         assertEquals(200, response.getStatus());
 
         contactResponse = service.getContactByXml(name);
         assertEquals(0, contactResponse.getContactResources().size());
 
         //
         response = service.deleteContact(name);
         assertEquals(404, response.getStatus());
 
     }
 
     public void testGetAllContactByXml() {
         service.createContact("shahbhat", "shahbhat email", "shahbhat address");
         service.createContact("bill", "bill email", "bill address");
 
         ContactsResponse contactResponse = service.getAllByXml();
         assertEquals(2, contactResponse.getContactResources().size());
         for (ContactResource contactResource : contactResponse.getContactResources()) {
             if ("shahbhat".equals(contactResource.getName())) {
                 assertEquals("shahbhat email", contactResource.getEmail());
                 assertEquals("shahbhat address", contactResource.getAddress());
             } else if ("bill".equals(contactResource.getName())) {
                 assertEquals("bill email", contactResource.getEmail());
                 assertEquals("bill address", contactResource.getAddress());
             } else {
                 fail("unknown contact " + contactResource);
             }
         }
 
         service.deleteContact("shahbhat");
         service.deleteContact("bill");
 
         contactResponse = service.getAllByXml();
         assertEquals(0, contactResponse.getContactResources().size());
     }
 
 
     public void testGetAllContactByJson() throws Exception {
         service.createContact("shahbhat", "shahbhat email", "shahbhat address");
         service.createContact("bill", "bill email", "bill address");
 
         JSONArray jsonArray = service.getAllByJson();
         assertEquals(2, jsonArray.length());
 
         for (int i=0; i<2; i++) {
             JSONObject json = jsonArray.getJSONObject(i);
             if ("shahbhat".equals(json.getString("name"))) {
                 assertEquals("shahbhat email", json.getString("email"));
                 assertEquals("shahbhat address", json.getString("address"));
             } else if ("bill".equals(json.getString("name"))) {
                 assertEquals("bill email", json.getString("email"));
                 assertEquals("bill address", json.getString("address"));
             } else {
                 fail("unknown contact " + json);
             }
         }
         service.deleteContact("shahbhat");
         service.deleteContact("bill");
 
         jsonArray = service.getAllByJson();
         assertEquals(0, jsonArray.length());
     }
 }
 

Functional Testing it

Once deploy, you can use curl to functionally test it (though there are other automated tools available as well), e.g

Creating Contact

 curl -X PUT -d "email=myemail&address=myaddress"  http://shahbhat.desktop:8080/svc/contacts/bhatti
 

It will create a contact and to retrieve it, use

 curl http://shahbhat.desktop:8080/svc/contacts/bhatti
 

It should return

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ContactsResponse><Contacts><Address>myaddress</Address><Email>myemail</Email><Name>bhatti</Name></Contacts><Status>success</Status><Uri>http://shahbhat.desktop:8080/svc/contacts/bhatti</Uri></ContactsResponse>
 

To get JSON format use

 curl --header "Content-Type: application/json" http://shahbhat.desktop:8080/svc/contacts/bhatti
 

and it should return

 [{"name":"bhatti","email":"myemail","address":"myaddress"}]
 

You can create another contact e.g.

 curl -d "email=billemail&address=billaddress"  http://shahbhat.desktop:8080/svc/contacts/bill
 

Now to get all contacts use

 curl http://shahbhat.desktop:8080/svc/contacts/
 

and it will return

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ContactsResponse><Contacts><Address>myaddress</Address><Email>myemail</Email><Name>bhatti</Name></Contacts><Contacts><Address>billaddress</Address><Email>billemail</Email><Name>bill</Name></Contacts><Status>success</Status><Uri>http://shahbhat.desktop:8080/svc/contacts/</Uri></ContactsResponse>
 

And to get JSON format use

 curl --header "Content-Type: application/json" http://shahbhat.desktop:8080/svc/contacts/
 

which will return

 [{"name":"bhatti","email":"myemail","address":"myaddress"},{"name":"bill","email":"billemail","address":"billaddress"}]
 

Conclusion

I found this approach is easily testable with standard unit test and easy to develop. Though, there are few glitches, e.g. @QueryParam only works with GET and if you are POSTing to something like /uri?x=yy then you won't see query parameters. But, overall I am happy with the outcome and will continue to use this framework.

October 18, 2008

Concurrency Constructs

Filed under: Computing — admin @ 11:43 am

With the proliferation of multi-cores, concurrency has been a hot topic for past few years. I described a few ways to tackle concurrency in my blog earlier this year. As I mentioned in my blog, this problem is nothing for scientific and research community. Also, many languages have had devised numerous constructs to tackle these abstractions and other languages are adding support for concurency. I will go over some of these abstractions here:

Processes

All modern operating systems support preemptive multitasking and processes have been simple way to add concurrency. For example, this technique is used by Apache web server that launches multiple processes and serve thousands of clients at a time. There are also a number of system primitives for inter-process communication such as shared memory, sockets, pipes and other forms of middlewares and peer to peer technology. This technique is specially suitable for many scripting languages that don’t have concurrency support such as Perl, Ruby, CPython, etc. Despite their scripting nature, these languages are highly productive. For example, much of Amazon’s retail website is built using Perl/Mason and uses multiple processes served by FastCGI for handling concurrent requests. Though, Ruby and Python support green threads, but are not designed for concurrency. For example, Global Interpreter Lock (GIL) in Python only allows one thread to execute at a time. Similarly, Ruby has Global VM lock that also allows one thread to execute at a time. These limitations have also effected kind of applications you can built. For example, one of criticism of Ruby on Rails framework has been its lack of concurrency. Though, there have been a number of solutions that tackle this problem by multi-processing such as clusters of mongrel, FastCGI, etc.

This model is also used by Fork/Join, Map/Reduce, Master-Slave and Leader/Follower style of parallel programming, where the master process creates work and distributes work to workers, who work on it. There are a plenty of libraries and frameworks available for this such as Globus, Hadoop, MPI/MPL, PVM and Fork/Join in Java 7.

Kernel Threads

Many languages support kernel threads that are mapped to lightweight processes (LWP) and scheduled by the operating systems. The operating system context switches between threads using time-slicing that give perception of concurrency. Posix compliant operating systems offer a number of solutions for inter-thread communication and synchronization such as mutex, semaphores, condition variables. Many languages offer support for high level synchronization such as concurrent library in Java.

Green Threads/Fibers

As opposed to native threads, green threads run in user space and scheduled by using yield to allow other thread/fiber to execute. As user threads or fibers rely on collaboration, they are easily subject to starvation. Some languages encapsulate blocking I/O with nonblocking I/O and automatically yield fiber when they make blocking call. Effective use of fibers can help applications scale more than multi-processing or multi-threading, which are generally limited by the system resources. For example, in a performance comparison between Apache and Yaws, where latter was built in Erlang that uses user processes was able to scale upto 80,000 concurrent connections whereas Apache died at around 4000 connections. Ruby 1.9 has just added support for fibers, which can be used to build streams, e.g.

 fib = Fiber.new do  
    x, y = 0, 1 
    loop do  
     Fiber.yield y 
     x,y = y,x+y 
    end 
   end 
   20.times { puts fib.resume } 
 

Streams

Streams are sort of iterators over infinite lists and often used in functional languages, though many imperative programming languages now support them to create infinite lists. You can find a number of examples of Strams in languages like Groovy, Scala, Haskell, etc. Python offers similar capability via generators that contain yield keyword, which causes compiler to suspend generator until next() method of generator is called. For example,

 def generate_ints(N):
     for i in range(N):
         yield i
 
 >>> gen = generate_ints(2)
 >>> gen.next()
 >>> gen.next()
 

Coroutines

Coroutines are similar user threads and unlike subroutines, they have multiple entry and exit points. They are often built using contiuations or generators. In C, setjmp and longjmp can be used to implement coroutine. Ruby 1.9 added support for fibers that are more accurately semi-coroutines [pp 167].

Continuation

Continuation work like saving game at a checkpoint and resuming it. Traditionaly goto were used to implement continuations, though a number of languages have native support for continuations such as Scheme, Smalltalk, Ruby (callcc), Scala, C (setjmp/longjmp), etc. Though, they are not without problems but Seaside web framework based on Smalltalk has shown innovative way to write web applications using continuation passing style.

Actor model

I was introduced to actor model in mid mid 90s when working on graduate and post-graduate work. An actor has its own thread of execution with a mailbox. It communicates to other actors by sending messages that are delivered to its mailbox. Erlang, a language designed for concurrency, is based on actor model and more recently Scala has adopted actor model as well. When I worked for high performance computing (HPC) area in 90s, I used SGI systems that were built using NUMA based shared memory and messaging passing systems/libraries such as MPI, MPL, etc. I found message passing systems were much more scalable than shared memory, so I believe this style of programming will have best chance of succeeding. Though, I admit I found converting algorithms into message passing style is not easy.

Reactive or Event driven style

In reactive or event driven style, systems communicate by sending or listening on events. It’s sort of flip-style of threading, instead of creating new thread for each new concurrent task, it uses a fixed number of threads that cooperate. It is more scalable than multi-threading (kernel) as native threads are limited by available resources and context switching is expensive. For example, event-driven Mongrel server is much more scalable than Mogrel server. A number of J2EE application servers such as Glassfish, Jetty and Tomcat 6 have adopted Grizzly framework that uses asynchronous I/O to implement reactive style servers.

Software Transactional Memory

It’s difficult to write correct program using shared memory and as it requires proper locking of shared data and special attention to thread starvation and deadlock issues. A number of languages like Clojure, Haskell and Fortress have added support for software transactional memory that provide implicit locking through transactions. I have not used this style of programming but it seems to make writing concurrent applications easy. Though, I believe scalability with shared memory may only help with systems upto a few cores or processors.

TupleSpace based model

In tuple space, processes communicate to each other by storing messages in tuple spaces. The tuple space provides simple APIs like get, put, read and eval. Where get/read are blocking operations to implement dataflow based application and eval creates a new process to execute a task and store result in the tuple space. I built a similar system called JavaNOW, though there are a number of open source and commercial frameworks availeble such as JavaSpaces, GigaSpaces, Blitz.

Declarative concurrency

I heard recently Anders Hejlsberg and Guy Steele talked about concurrency and Anders suggested declarative concurrency where the programmer indicates parts of the applications that can be run concurrently. In a way, it is similar to compiler based parallelism used in High performance Fortran or Parallel Fortran that used data flow analysis to create parallel applications.

In nutshell, I find that more and more languages are adding constructs and frameworks to address concurrency and we will have to get used to these constructs to write programs that are future proof.

October 14, 2008

Observe, Orient, Decide and Act

Filed under: Methodologies — admin @ 8:49 pm

I have heard agile evangilists talk about influence of John Boyd on agile methodologies. I have also read about his story in books like Release It and more recently in Simple Architectures for Complex Enterprises, which I am currently reading. You can read more about Boyd’s background from wikipedia link above but he came up with iterative cycle of: observe, orient, decide and act (OODA). One of key finding he made was that shorter feedback or iteration loop of OODA with low quality was better than longer or tiring cycle of OODA with high quality. Despite the fact that everyone calls his/her organization agile, this feedback loop is real essense of agility.

October 12, 2008

Modules, Libraries, Components, Frameworks, Services and Platforms

Filed under: Computing — admin @ 9:21 pm

The software development is complex by nature and over the years, I have seen many abstractions for addressing this complexity such as modules, classes, libraries, components, packages, aspects, frameworks and platform, services, etc. Back when I started programming in 80s, I learned about Structured Programming and Top-down Design. Like modern good software practices it encouraged separation of concernsand was modular design. I learned from structured programming that modules should be loosely coupled and highly cohesive. I learned about various forms of coupling such as common, control, data, stamp, etc and how to avoid it. BASIC was my first programming language and I learned a number of languages afterwards, but C became my primary language. In that language, module meant c file that consisted of closely related functions.

I also understood concept of libraries (static), which were sort of modules or composition of modules that offered some useful functionality that I could link at compile/load time. In early 90s, I started learning object oriented programming and design and started using C++. It also highlighted modularity and introduced classes for combining data structure and behavior. It also emphasised encapsulation or information hiding and interfaces. Despite its impurity, I used C++ for many years. It was similar to C and it also used files for modules by defining closely related files in the same file.

In 1995, I also started using Java, which limited one class per file and offered packages for namespace. I heard many people equated modules to classes but I preferred packages for defining modules. Java also offered jar files for packaging libraries. In early days, Java also promoted JavaBeans as a way to add components and runtime wiring.

Around mid 90s to early 2000, I started learning about components, which were hot topics in those days. I learned Catalysis, Iconix and Component Software. A lot of companies were defining what components meant, such as SOM, Micrsoft’s COM, or their distributed counterparts such as DCOM, DSOM, CORBA, etc. Unlike static libraries, which were packaged as part of the application, those components offered binary and runtime pluggability. I used CORBA for many years, then moved to EJBs in early 2000s which offered more object oriened support for components. Historically, compile time components were difficult to use and led to DLL or Jar hell. Recent standars such as OSGI and Java JSR 291 offer nice dependency management and runtime isolation.

In mid 90s, I enrolled for Ph.D and was looking for interesting topics. One of my professor gave me some papers from Xeros Parc about aspect oriented programming. I ended up choosing different topic but liked the idea of aspects. AOP became popular in early 2000s and I have been using them whenever I get chance. Aspects addressed crosscutting concerns and offered a powerful way to modularize concerns like security, monitoring, transactions, caching, etc.

Web Services and Services oriented Architecture became popular in early 2000 and I started using Sun’s toolkit and Weblogic’s generators to convert Java/EJBs to Web Services. Similar to modules, libraries and components they try to sell on partitioning and reusability, though unlike former, the services didn’t have any compile or load time dependencies.

The component based software development then led to frameworks and I started hearing more about frameworks for UIs, eclipse IDE, etc. I found frameworks were different from libraries in several ways. For example, unlike libraries where client code simply invokes some functionality, the frameworks offered scaffolding to built applications and used inversion of control by offering hooks, callbacks or plugins to implment. I have been using a number of frameworks for many years such as Spring, Eclipse, Rails, etc.

For past a few years, more and more companies now offer platforms to build applications. The platforms are sort of like composition of frameworks with an API to build applications. Though, historically platforms were tied with specific operating systems or languages such as Java, .Net, iPhone, JavaME but Web has become the platform, which offers language and operating system independent services. There are unlimited choices for building new mashups and applications (see http://www.programmableweb.com/. Though, some of the web platforms such as Facebook, MySpace, Google App
Engine and Amazon Web Services offer more complete application hosting facilities and in the case of Facebook, MySpace or Twitter they also offer millions of potential users.

The cost of software development has become significantly cheaper due to open source and easily accessible libraries, frameworks and platforms. Though, what I described in this blog is no news to anyone, but I think it’s imporant to think about these differences and choices when building new applications.

October 3, 2008

Traits of good programmers

Filed under: Java — admin @ 11:00 pm

I love programming and have been doing it for more than 20 years. In that lifetime, I have also worked with a lot of good programmers and have discussed many times “what makes a good programmer” informally or during job interviews. Here are some of important traits that I have found in good programmers:

  • Simplicity and Clarity of thinking – I have found good programmers can think clearly and try to understand essense of underlying problem. Good programmers then develop simple solutions with minimal accidental complexity. I have found systems developed by good programs are easier to understand due to clarity of program design.
  • Passionate about programming – Programmming is a profession and I have seen a lot of programmers who considered programming a 9-5 job, who didn’t care about it outside the job. Many of those programmers did the same job for many years without learning anything new. I have found that best programmers care a great deal about programming and have a great passion about it. One way to distinguish programmers with passion is to ask them about side projects or open source projects that they started or worked on.
  • Continuous Learning – I have found good programmers spend a lot of time learning new skills or finding better ways to do things.
  • Generalist – Due to passion and ability to learn new things, good programmers have breadth of knowledge with varying skills and technolgies. They have modeling, UI, application, systems, database and other skills needed for softrware development.
  • Detail oriented – Programming requires a detail oriented person who can think about a problem and all issues that can effect the solution. I have found good programmers can think of all branch logic and all edge cases when solving a problem.
  • Visualize working of programs – Good programs have ability to visualize inner working of programs and can quickly pinpoint problem areas when things break.
  • Excellence vs Perfectionist – Real world projects are constrained by time and resources and good programmers opt for excellence by doing their best within those constraints rather than being perfectionist.
  • Inquisitive and inquiring mind – One of the hardest part of software development is determining key requirements and differentiating between wants and needs. Often stakeholders don’t know what they want. Good programmers work with users to detrmine what’s really needed for the system. Good programmers can probe users about real underlying problem that they are trying solve. In such systems, agile methodology also help where users can see something working and develop iteratively.
  • Communication – Good programmers communicate in multiple ways, they use design or code to communicate with other programmers. Senior programmers often need to sell or persuade other programmers about architecture or new practices.
  • Integrity and Courage – Though, this is a principle of XP but it’s also one of key trait of good programmers. I have found in most companies, management pulls deadlines and requirements out of the hat and good programmers have courage to speak up to the management when they see death march timelines.
  • Humility – This is also another attribute of XP but I have found in good programmers because programming is hard profession and everyone makes mistake. Though, I have also seen a lot of prima donnas who were smart (some of them) but were difficult to work with. On the other hand, I have found good programmers are humble and easy to work with.
  • Responsibility – Good programmers are profesional and they take full responsibility in developing fully tested and working solutions for the users.
  • Golden hammer – Due to being generalist, good programmers is less likely to use same solution or golden hammer to varying problems. Also, due to breadth of skills he/she is less likely to be swayed by technology or language de jure, instead good programmers try to evaluate real benefit of new technologies with some side projects.
  • Toolbox of solutions – Good programmers learn from varying solutions and often recognize patterns and similar problems that they have previously worked on. They use this toolbox of solutions and patterns when approaching new problem.
  • Productive – Often you hear about good programmers being 10 or 20 times more productive. Though, I admit it’s hard to measure productivity but I have found that good programmers are productive mostly due to above traits. For example, they develop simple code which is faster to develop and has less defect. They focus on essential probems that solve real problems. They are faster mostly because they practice alot with side projects and recognize patterns and previously solved solutions. When debugging and trouble shooting they can visualize software systems and can see problem areas. They don’t like to hackup things that will break in two weeks, instead they have courage to tell stakeholders risks of meeting unrealistic deadlines or releasing untested applications.

September 9, 2008

Setting up JRuby on Rails

Filed under: Ruby — admin @ 11:25 am

In my last blog, I wrote about recent JRuby on Rails project I did at amazon. In this blog, I will show some technical details on setting up JRuby on Rails with Tomcat. I used 2.1.1 of Rails and 1.1.4 of JRuby, here are steps to setup JRuby and Rails:

  • Download Jruby 1.1.4 from http://dist.codehaus.org/jruby/
  • Setup JRUBY_HOME and PATH
     export JRUBY_HOME=installed directory of jruby
     export PATH=$JRUBY_HOME/bin:$PATH
     
  • Install Rails
     gem install rails -y
     
  • Install ActiveRecord JDBC Adapter
     jruby -S gem install activerecord-jdbc-adapter
     
  • Install warbler plugin to create war file, older version of Rails (1.2 or older) used goldspike but the newer version requires warbler
     jruby -S gem install -y  warbler
     
  • Edit config/environment.rb and add
     if RUBY_PLATFORM =~ /java/
       require 'rubygems'
       RAILS_CONNECTION_ADAPTERS = %w(jdbc)
     end
     
  • Download mysql-jdbc driver as I used MySQL. (You may have to download different driver. )
  • Copy mysql-connector-java-5.1.6-bin.jar to lib directory of jruby
  • You can edit config/warble.rb and add other jar files or directories, e.g.
       config.java_libs += FileList["lib/*.jar"]
       config.pathmaps.java_classes << "%{build/classes/,}p"
       config.webxml.jruby.min.runtimes = 2
       config.webxml.jruby.max.runtimes = 24
     
  • Edit config/environment.rb and add define adapter, driver and url as follows:
     development:
       adapter: jdbc
       driver: com.mysql.jdbc.Driver
       url: jdbc:mysql://localhost/rspm_development
       username: rspmdb_user
       password: secret
                                                                                                                                                           
     test:
       adapter: jdbc
       driver: com.mysql.jdbc.Driver
       url: jdbc:mysql://localhost/rspm_test
       username: rspmdb_user
       password: secret
                                                                                                                                                           
     production:
       adapter: jdbc
       driver: com.mysql.jdbc.Driver
       url: jdbc:mysql://localhost/rspm_production
       username: rspmdb_user
       password: secret
     
  • Create a template web.xml.erb in config directory, e.g.
     <!DOCTYPE web-app PUBLIC
       "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     
       "http://java.sun.com/dtd/web-app_2_3.dtd">
     <web-app>
                                                                                                                                                           
         <% webxml.context_params.each do |k,v| %>
         <context-param>
             <param-name><%= k %></param-name>
             <param-value><%= v %></param-value>
     
         </context-param>
         <% end %>
                                                                                                                                                           
        <servlet>
           <servlet-name>Rails</servlet-name>
           <servlet-class>org.jruby.rack.RackServlet</servlet-class>
     
        </servlet>
                                                                                                                                                           
        <servlet-mapping>
            <servlet-name>Rails</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>
     
                                                                                                                                                           
                                                                                                                                                           
       <filter>
         <filter-name>RackFilter</filter-name>
         <filter-class>org.jruby.rack.RackFilter</filter-class>
       </filter>
     
       <filter-mapping>
         <filter-name>RackFilter</filter-name>
         <url-pattern>/*</url-pattern>
       </filter-mapping>
                                                                                                                                                           
        <listener>
     
           <listener-class><%= webxml.servlet_context_listener || 'org.jruby.rack.RackServletContextListener'%></listener-class>
        </listener>
                                                                                                                                                           
     </web-app>
                                                                                                                                                           
     
  • Now create war file
     jruby -S warble
     
  • Finally copy the war file to webapps directory of standard J2EE web container, in my case Tomcat 6.0
  • Now point the browser to your server, e.g. http://localhost:8080/your-controller and voilla.

On final note, I found warble quite slow in generating war file and tomcat takes a while to deploy the war file, so I use WEBrick for development and Tomcat for staging/production.

September 6, 2008

Rails save the day

Filed under: Ruby — admin @ 12:52 pm

In our daily scrum meeting at work last week, we identified one of application that had been crashing often. The application was pretty simple web application written in Java using Spring-MVC, Hibernate, Spring and Apache-commons libraries. However, the developer who released it embedded that application in another much bigger web application in order to save some deployment issues, which can be real pain at amazon. So, I volunteered to address the issue and rewrite the application in JRuby on Rails (2.1). It took me a day to rewrite all application and migrate all data from the old application. Along the way, I also added a couple of some additional features. The original application took a couple of weeks to develop and was consisted of over 6500 lines of code. The ruby version consisted a little less than 800 lines of code. I have been using Ruby on my own since 2004 and started learning Rails in 2005. Though, I started using Rails a bit more on some side projects after I attended Rails conference of 2006. I still use Java for most of the work, and was delighted to show speed of development with Rails at work. Since, the deployment issues are still pain in Rails so I chose JRuby that allows Rails apps to deploy on Tomcat, which we are using for most of the projects. Hopefuly, that will fix the stability issues and save me from pagers as I will be oncall next week.

August 27, 2008

Implementing HTTP Proxy Service with XSL Transformation

Filed under: Java — admin @ 10:19 pm

Recently I had to implement a portal that embedded contents from other web applications. I tried to build a framework for easily adding remote applications without any changes to the remote applications. The framework had following design:

  • For different functionality and pages we stored feature-link and url in configuration, that configuration basically allowed us to find where to
    get the HTML/XHTML render page or form.

  • However, instead of displaying raw HTML, I first converted it into valid XHTML using Tidy and then transformed such that all form submissions
    go through our proxy server. As part of the transformation, I added original form URLs, methods and cookies as hidden fields. This allowed
    me to keep the proxy server without any client state, which was nice for clustering.

  • When the form is submitted, the proxy server intercepts it and then makes the request to the remote application and sends back response. The proxy
    server reads the original target URLs, method types, cookies from the form so that remote application can manage state if it’s using Sessions.

Following are a few simplified classes that I developed for the proxy framework:

Content Transformation

First, I developed HTML to XHTML conversion and transformation to modify forms. Following is the XSLT that I used:

  1 <xsl:stylesheet
 
  2   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3   version="1.0"
  4   xmlns:xhtml="http://www.w3.org/1999/xhtml"
 
  5   xmlns="http://www.w3.org/1999/xhtml"
  6   exclude-result-prefixes="xhtml">
  7 
 
  8   <xsl:param name="callbackHandler"/>
  9   <xsl:param name="callbackUser"/>
 
 10   <xsl:param name="callbackState"/>
 11 
 12   <xsl:template match="@* | node()">
 
 13     <xsl:copy>
 14       <xsl:apply-templates select="@* | node()"/>
 15     </xsl:copy>
 
 16   </xsl:template>
 17 
 18   <xsl:template match="form">
 19     <xsl:copy>
 
 20       <xsl:apply-templates select="@*"/>
 21       <xsl:attribute name="action" xmlns:java="http://xml.apache.org/xslt/java">
 
 22             <xsl:param name="callbackOriginalActionUrl" select="@action"/>
 23             <xsl:text disable-output-escaping="yes">/web/myproxy?myarg=xxx&amp;otherarg=2</xsl:text>
 
 24             <xsl:value-of select="java:com.plexobject.transform.XslContentTransformer.setAction($callbackHandler, string(@action))" />
 25       </xsl:attribute>
 26       <xsl:attribute name="method" xmlns:java="http://xml.apache.org/xslt/java">
 
 27             <xsl:param name="callbackOriginalMethodType" select="@method"/>
 28             <xsl:text disable-output-escaping="yes">POST</xsl:text>
 
 29             <xsl:value-of select="java:com.plexobject.transform.XslContentTransformer.setMethod($callbackHandler, string(@method))" />
 30       </xsl:attribute>
 31       <xsl:attribute name="id">_Form</xsl:attribute>
 
 32       <xsl:attribute name="name">_Form</xsl:attribute>
 33         <input type="hidden" name="_user" value="{$callbackUser}"/>
 
 34         <input type="hidden" name="_originalActionUrl" value="{@action}"/>
 
 35         <input type="hidden" name="_orginalMethodType" value="{@method}"/>
 
 36         <input type="hidden" name="_userState" value="{$callbackState}"/>
 
 37       <xsl:apply-templates select="node()"/>
 38     </xsl:copy>
 39   </xsl:template>
 
 40 
 41 
 42   <xsl:template match="title"/>
 43 
 44 
 
 45 </xsl:stylesheet>
 46 
 47 
 48 
 

A few things note

  • xsl:param allows passing parameters from the runtime (Java)
  • xsl:template is matching for “form” tag and replaces action/method attributes and adds id/name attributes. It then adds a few input hidden fields
  • Finally, I am removing title tag

ContentTransformer interface

  1 package com.plexobject.transform;
 
  2 
  3 import java.util.Map;
  4 
  5 public interface ContentTransformer {
 
  6     /**
  7      * This method transforms given contents
 
  8      * 
  9      * @param contents
 10      *            - input contents
 
 11      * @param properties
 12      *            - input/output properties for transformation
 
 13      * @return transformed contents
 14      * @throws TransformationException
 
 15      *             - when error occurs while transforming content.
 
 16      */
 17     public String transform(String contents, Map<String, String> properties)
 18             throws TransformationException;
 
 19 }
 20 
 21 
 

ContentTransformer implementation

A few things to note in following implementation:

  • I use JTidy to convert HTML to XHTML
  • I pass some of the parameters to the XSL stylesheet and I also read a few properties back. Though, reading properties back is a bit kludgy but it works.
   1 package com.plexobject.transform;
 
   2 
   3 import java.io.ByteArrayInputStream;
   4 import java.io.ByteArrayOutputStream;
   5 import java.io.InputStream;
 
   6 import java.util.HashMap;
   7 import java.util.Map;
   8 
   9 import javax.xml.transform.Result;
 
  10 import javax.xml.transform.Source;
  11 import javax.xml.transform.Transformer;
  12 import javax.xml.transform.TransformerException;
 
  13 import javax.xml.transform.TransformerFactory;
  14 import javax.xml.transform.stream.StreamResult;
  15 import javax.xml.transform.stream.StreamSource;
 
  16 
  17 import org.w3c.tidy.Tidy;
  18 
  19 public class XslContentTransformer implements ContentTransformer {
 
  20     public static final String ACTION = "form_action";
  21     public static final String METHOD = "form_method";
 
  22 
  23     private static final Map<String, Map<String, String>> xslProperties = new HashMap<String, Map<String, String>>();
 
  24     private volatile Transformer transformer;
  25     private final String xslUri;
  26     private final boolean useTidy;
 
  27 
  28     public XslContentTransformer(final String xslUri, final boolean useTidy) {
 
  29         this.xslUri = xslUri;
  30         this.useTidy = useTidy;
  31     }
  32 
 
  33     public static final String setAction(final String callbackHandler,
  34             final String action) {
 
  35         getPropertiesForCallback(callbackHandler).put(ACTION, action);
  36         return "";
  37     }
  38 
 
  39     public static final String getAction(final String callbackHandler) {
  40         return getPropertiesForCallback(callbackHandler).get(ACTION);
 
  41     }
  42 
  43     public static final String setMethod(final String callbackHandler,
 
  44             final String method) {
  45         getPropertiesForCallback(callbackHandler).put(METHOD, method);
  46         return "";
  47     }
 
  48 
  49     public static final String getMethod(final String callbackHandler) {
  50         return getPropertiesForCallback(callbackHandler).get(METHOD);
 
  51     }
  52 
  53     /**
  54      * This method transforms given contents
 
  55      * 
  56      * @param contents
  57      *            - input contents
 
  58      * @param properties
  59      *            - input/output properties for transformation
 
  60      * @return transformed contents
  61      * @throws TransformationException
 
  62      *             - when error occurs while transforming content.
 
  63      */
  64     public String transform(String contents, Map<String, String> properties)
  65             throws TransformationException {
 
  66         initTransformer();
  67         final long started = System.currentTimeMillis();
  68 
  69         contents = contents.replaceAll("<!--.*?-->", "");
 
  70         InputStream in = new ByteArrayInputStream(contents.getBytes());
  71         if (useTidy) {
  72             in = tidy(in, (int) contents.length());
 
  73         }
  74 
  75         //
  76         final Source xmlSource = new StreamSource(in);
 
  77         final ByteArrayOutputStream out = new ByteArrayOutputStream(
  78                 (int) contents.length());
  79 
 
  80         final Result result = new StreamResult(out);
  81         String callbackHandler = properties.get("callbackHandler");
  82         if (callbackHandler == null) {
 
  83             callbackHandler = Thread.currentThread().getName();
  84         }
  85         final Map<String, String> props = new HashMap<String, String>();
 
  86         xslProperties.put(callbackHandler, props);
  87         transformer.setParameter("callbackHandler", callbackHandler);
  88         for (Map.Entry<String, String> e : properties.entrySet()) {
 
  89             transformer.setParameter(e.getKey(), e.getValue());
  90         }
  91         try {
  92             transformer.transform(xmlSource, result);
 
  93         } catch (TransformerException e) {
  94             throw new TransformationException("Failed to transform " + contents, e);
 
  95         }
  96         properties.put(ACTION, getAction(callbackHandler));
  97         properties.put(METHOD, getMethod(callbackHandler));
  98         xslProperties.remove(callbackHandler);
  99         return new String(out.toByteArray());
 
 100     }
 101 
 102     private static final Map<String, String> getPropertiesForCallback(
 
 103             String callbackHandler) {
 104         final Map<String, String> props = xslProperties.get(callbackHandler);
 105         if (props == null) {
 
 106             throw new NullPointerException(
 107                     "Failed to find properties for callback " + callbackHandler);
 108         }
 109         return props;
 
 110     }
 111 
 112     // no synchronization needed, multiple initialization is acceptable
 113     private final void initTransformer() {
 
 114         if (transformer == null) {
 115             try {
 116                 TransformerFactory transFact = TransformerFactory.newInstance();
 117                 InputStream in = getClass().getResourceAsStream(xslUri);
 
 118                 if (in == null) {
 119                     throw new TransformationException("failed to find xslt "
 120                             + xslUri);
 
 121                 }
 122                 Source xsltSource = new StreamSource(in);
 123                 transformer = transFact.newTransformer(xsltSource);
 124             } catch (TransformationException e) {
 
 125                 throw e;
 126             } catch (RuntimeException e) {
 127                 throw e;
 128             } catch (Exception e) {
 
 129                 throw new TransformationException(
 130                         "Failed to initialize XSL transformer", e);
 131             }
 132         }
 
 133     }
 134 
 135     private final InputStream tidy(InputStream in, int length) {
 136         ByteArrayOutputStream out = new ByteArrayOutputStream(length);
 
 137         Tidy converter = new Tidy();
 138         converter.setTidyMark(false);
 139         converter.setXmlOut(true);
 140         converter.setXmlPi(true);
 
 141         converter.setXmlPIs(true);
 142         converter.setNumEntities(true);
 143         converter.setDocType("omit");
 144         converter.setUpperCaseTags(false);
 
 145         converter.setUpperCaseAttrs(false);
 146         converter.setFixComments(true);
 147         converter.parse(in, out);
 148         return new ByteArrayInputStream(out.toByteArray());
 
 149     }
 150 }
 151 
 152 
 

Proxy

Following interfaces and classes show how GET/POST requests are proxied:

Proxy Interface

  1 package com.plexobject.web.proxy;
 
  2 
  3 import java.io.IOException;
  4 import java.util.Map;
  5 
 
  6 public interface HttpProxy {
  7     /**
  8      * This method issues a GET or POST request based on method and URI URI specified in the ProxyState
 
  9      * and adds given parameters to the request.
 
 10      * 
 11      * @param state
 12      *            - proxy state
 
 13      * @param params
 14      *            - name/value pairs of parameters that are sent to the get
 
 15      *            request
 16      */
 17     public ProxyResponse request(ProxyState state, Map<String, String[]> params)
 
 18             throws IOException;
 19 }
 20 
 21 
 

Proxy Implementation

Following class implements HttpProxy interface using HTTPClient library:

   1 package com.plexobject.web.proxy;
 
   2 
   3 import java.io.IOException;
   4 import java.util.ArrayList;
   5 import java.util.List;
 
   6 import java.util.Map;
   7 
   8 import org.apache.commons.httpclient.Cookie;
   9 import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
 
  10 import org.apache.commons.httpclient.HttpClient;
  11 import org.apache.commons.httpclient.HttpMethodBase;
  12 import org.apache.commons.httpclient.HttpState;
 
  13 import org.apache.commons.httpclient.NameValuePair;
  14 import org.apache.commons.httpclient.cookie.CookiePolicy;
  15 import org.apache.commons.httpclient.methods.GetMethod;
 
  16 import org.apache.commons.httpclient.methods.PostMethod;
  17 import org.apache.commons.httpclient.params.HttpMethodParams;
  18 
  19 import com.plexobject.io.IoUtil;
 
  20 
  21 public class HttpProxyImpl implements HttpProxy {
  22     private static final int CONNECTION_TIMEOUT_MILLIS = 30000;
 
  23 
  24     /**
  25      * This method issues a GET or POST request based on method and URI URI specified in the ProxyState
 
  26      * and adds given parameters to the request.
 
  27      * 
  28      * @param state
  29      *            - proxy state
 
  30      * @param params
  31      *            - name/value pairs of parameters that are sent to the get
 
  32      *            request
  33      */
  34     public ProxyResponse request(ProxyState state, Map<String, String[]> params)
 
  35             throws IOException {
  36         if (state.getMethod() == MethodType.GET) {
  37             return get(state, params);
 
  38         } else {
  39             return post(state, params);
  40         }
 
  41     }
  42 
  43 
  44     /**
  45      * This method issues a GET request on the URI specified in the ProxyState
 
  46      * and adds given parameters to the request.
 
  47      * 
  48      * @param state
  49      *            - proxy state
 
  50      * @param params
  51      *            - name/value pairs of parameters that are sent to the get
 
  52      *            request
  53      */
  54     private ProxyResponse get(ProxyState state, Map<String, String[]> params)
 
  55             throws IOException {
  56         GetMethod method = new GetMethod(state.getUri());
  57         method.setQueryString(toNameValues(params));
 
  58         return doRequest(state, params, method);
  59     }
  60 
  61     /**
 
  62      * This method issues a POST request on the URI specified in the ProxyState
 
  63      * and adds given parameters to the request.
 
  64      * 
  65      * @param state
  66      *            - proxy state
 
  67      * @param params
  68      *            - name/value pairs of parameters that are sent to the POST
 
  69      *            request
  70      */
  71     private ProxyResponse post(ProxyState state, Map<String, String[]> params)
 
  72             throws IOException {
  73         PostMethod method = new PostMethod(state.getUri());
  74         method.setRequestBody(toNameValues(params));
 
  75         return doRequest(state, params, method);
  76     }
  77 
  78     private ProxyResponse doRequest(ProxyState proxyState,
 
  79             Map<String, String[]> params, HttpMethodBase method)
  80             throws IOException {
  81         long started = System.currentTimeMillis();
 
  82         HttpClient client = new HttpClient();
  83         client.getHttpConnectionManager().getParams().setConnectionTimeout(
  84                 CONNECTION_TIMEOUT_MILLIS);
  85         client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
 
  86         method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
  87                 new DefaultHttpMethodRetryHandler(3, false));
  88 
  89         HttpState initialState = new HttpState();
 
  90         for (Cookie cookie : proxyState.getCookies()) {
  91             initialState.addCookie(cookie);
  92         }
  93         client.setState(initialState);
 
  94 
  95         try {
  96             int statusCode = client.executeMethod(method);
  97             String contents = IoUtil.read(method.getResponseBodyAsStream());
 
  98             //
  99             Cookie[] cookies = client.getState().getCookies();
 100             for (Cookie cookie : cookies) {
 101                 proxyState.addCookie(cookie);
 
 102             }
 103 
 104             return new ProxyResponse(statusCode, contents, proxyState);
 105         } catch (RuntimeException e) {
 
 106             throw e;
 107         } catch (IOException e) {
 108             throw e;
 109         } catch (Exception e) {
 
 110             throw new IOException("failed to process request", e);
 111         } finally {
 112             method.releaseConnection();
 
 113         }
 114     }
 115 
 116     private NameValuePair[] toNameValues(Map<String, String[]> params) {
 117         if (params == null || params.size() == 0) {
 
 118             return new NameValuePair[0];
 119         }
 120         List<NameValuePair> nvPairs = new ArrayList<NameValuePair>();
 
 121         for (Map.Entry<String, String[]> e : params.entrySet()) {
 122             String[] values = e.getValue();
 123             for (int j = 0; j < values.length; j++) {
 
 124                 nvPairs.add(new NameValuePair(e.getKey(), values[j]));
 125             }
 126         }
 127         return (NameValuePair[]) nvPairs.toArray(new NameValuePair[nvPairs
 
 128                 .size()]);
 129     }
 130 }
 131 
 132 
 

ProxyState

Following class maintains URL, cookies, headers, and other information related to web request:

   1 package com.plexobject.web.proxy;
 
   2 
   3 import java.io.Serializable;
   4 import java.io.UnsupportedEncodingException;
   5 import java.net.URLDecoder;
 
   6 import java.net.URLEncoder;
   7 import java.util.Collection;
   8 import java.util.Date;
 
   9 import java.util.HashMap;
  10 import java.util.Map;
  11 
  12 import org.apache.commons.httpclient.Cookie;
 
  13 
  14 /**
  15  * Class: ProxyState
 
  16  * 
  17  * Description: This class stores state needed to make a proxy request including
 
  18  * method type and cookies.
  19  * 
 
  20  */
  21 public class ProxyState implements Serializable {
  22     private static final long serialVersionUID = 1L;
 
  23     private static final String DATA_DELIMITER = "\n";
  24     private static final String COOKIE_DELIMITER = ";";
 
  25     private static final String NULL = "null";
  26 
  27     private String uri;
 
  28     private MethodType method;
  29     private Map<String, Cookie> cookies;
  30 
 
  31     /**
  32      * Constructors for ProxyState
  33      */
 
  34     public ProxyState(String uri, String method) {
  35         this(uri, MethodType.valueOf(method));
  36     }
  37 
 
  38     public ProxyState(String uri, MethodType method) {
  39         this.uri = uri;
  40         this.method = method;
  41         this.cookies = new HashMap<String, Cookie>();
 
  42     }
  43 
  44     /**
  45      * @return uri
 
  46      */
  47     public String getUri() {
  48         return this.uri;
 
  49     }
  50 
  51     /**
  52      * @return method
 
  53      */
  54     public MethodType getMethod() {
  55         return this.method;
 
  56     }
  57 
  58     /**
  59      * @return cookies
 
  60      */
  61     public Collection<Cookie> getCookies() {
  62         return this.cookies.values();
 
  63     }
  64 
  65 
  66     /**
  67      * @param cookies
 
  68      */
  69     public void addCookies(Collection<Cookie> cookies) {
  70         for (Cookie cookie : cookies) {
 
  71             addCookie(cookie);
  72         }
  73     }
  74 
  75     /**
 
  76      * @param cookie
  77      *            - to add
 
  78      */
  79     public void addCookie(Cookie cookie) {
  80         this.cookies.put(cookie.getName(), cookie);
 
  81     }
  82 
  83     public String getCookieString() {
  84         StringBuilder sb = new StringBuilder(512);
 
  85         for (Cookie cookie : cookies.values()) {
  86             if (cookie.getDomain() != null) {
  87                 sb.append(cookie.getDomain()).append(COOKIE_DELIMITER);
 
  88             } else {
  89                 sb.append(NULL).append(COOKIE_DELIMITER);
  90             }
  91             sb.append(cookie.getName()).append(COOKIE_DELIMITER).append(
 
  92                     cookie.getValue()).append(COOKIE_DELIMITER);
  93 
  94             if (cookie.getPath() != null) {
  95                 sb.append(cookie.getPath()).append(COOKIE_DELIMITER);
 
  96             } else {
  97                 sb.append(NULL).append(COOKIE_DELIMITER);
  98             }
  99             if (cookie.getExpiryDate() != null) {
 
 100                 sb.append(String.valueOf(cookie.getExpiryDate().getTime()))
 101                         .append(COOKIE_DELIMITER);
 102             } else {
 103                 sb.append(NULL).append(COOKIE_DELIMITER);
 104             }
 
 105             sb.append(String.valueOf(cookie.getSecure()))
 106                     .append(DATA_DELIMITER);
 107         }
 108         return sb.toString();
 109     }
 
 110 
 111 
 112     @Override
 113     public String toString() {
 114         StringBuilder sb = new StringBuilder(512);
 
 115         sb.append(uri.toString()).append(DATA_DELIMITER);
 116         sb.append(method.toString()).append(DATA_DELIMITER);
 117         sb.append(getCookieString());
 118         return sb.toString();
 119     }
 
 120 
 121     /**
 122      * This method converts proxy state into string based serialized state
 
 123      * 
 124      * @return string based serialized state
 
 125      */
 126     public String toExternalFormat() {
 127         try {
 128             return URLEncoder.encode(toString(), "UTF8");
 
 129         } catch (UnsupportedEncodingException e) {
 130             throw new IllegalStateException("failed to encode", e);
 131         }
 
 132     }
 133 
 134     /**
 135      * This method converts a string based serialized state into the proxy state
 
 136      * 
 137      * @param ser
 138      *            - string based serialized state
 
 139      * @return ProxyState
 140      * @throws IllegalArgumentException
 141      *             - if serialized state is null or corrupted.
 
 142      */
 143     public static ProxyState valueOf(String ser) {
 144         if (ser == null)
 
 145             throw new IllegalArgumentException("Null serialized object");
 146         String decoded;
 147         try {
 
 148             decoded = URLDecoder.decode(ser, "UTF8");
 149         } catch (UnsupportedEncodingException e) {
 150             throw new IllegalArgumentException("Unsupported encoding " + ser, e);
 
 151         }
 152         String[] lines = decoded.split(DATA_DELIMITER);
 153         if (lines.length < 2)
 154             throw new IllegalArgumentException(
 
 155                     "Insufficient number of tokens in serialized object ["
 156                             + decoded + "]");
 157         ProxyState state = new ProxyState(lines[0], lines[1]);
 158         for (int i = 2; i < lines.length; i++) {
 
 159             String[] cookieFields = lines[i].split(COOKIE_DELIMITER);
 160             if (cookieFields.length < 6)
 161                 throw new IllegalArgumentException(
 
 162                         "Insufficient number of tokens 6 in serialized cookies ["
 163                                 + lines[i] + "]/[" + decoded + "]");
 164             String domain = cookieFields[0];
 165             if (NULL.equals(domain)) {
 
 166                 domain = null;
 167             }
 168             String name = cookieFields[1];
 169             String value = cookieFields[2];
 170             String path = cookieFields[3];
 
 171             if (NULL.equals(path)) {
 172                 path = null;
 173             }
 174             Date expires = null;
 
 175             if (!NULL.equals(cookieFields[4])) {
 176                 expires = new Date(Long.parseLong(cookieFields[4]));
 177             }
 178             boolean secure = new Boolean(cookieFields[5]).booleanValue();
 
 179             Cookie cookie = new Cookie(domain, name, value, path, expires,
 180                     secure);
 181             state.addCookie(cookie);
 182         }
 183         return state;
 
 184     }
 185 
 186     @Override
 187     public boolean equals(Object o) {
 188         if (this == o)
 
 189             return true;
 190         if (!(o instanceof ProxyState))
 191             return false;
 
 192         final ProxyState other = (ProxyState) o;
 193         if (uri != null ? !uri.equals(other.uri) : other.uri != null)
 194             return false;
 
 195         if (method != null ? !method.equals(other.method)
 196                 : other.method != null)
 197             return false;
 
 198         return true;
 199     }
 200 
 201     @Override
 202     public int hashCode() {
 
 203         int result;
 204         result = (uri != null ? uri.hashCode() : 0);
 205         result = 29 * result + (method != null ? method.hashCode() : 0);
 
 206         return result;
 207     }
 208 }
 209 
 210 
 

ProxyResponse

Following class stores response from the HttpProxy interface:

  1 package com.plexobject.web.proxy;
 
  2 
  3 import java.io.Serializable;
  4 
  5 
  6 /**
 
  7  * Class: ProxyResponse
  8  * 
  9  * Description: This class stores proxy state and response.
 
 10  */
 11 public class ProxyResponse implements Serializable {
 12     private static final long serialVersionUID = 1L;
 
 13     private int responseCode;
 14     private String contents;
 15     private ProxyState state;
 
 16 
 17     /**
 18      * Constructor for ProxyResponse
 19      */
 
 20     public ProxyResponse(int responseCode, String contents, ProxyState state) {
 21         this.responseCode = responseCode;
 22         this.contents = contents;
 23         this.state = state;
 
 24     }
 25 
 26     /**
 27      * @return http response code
 
 28      */
 29     public int getResponseCode() {
 30         return this.responseCode;
 
 31     }
 32 
 33     /**
 34      * @return XHTML contents
 
 35      */
 36     public String getContents() {
 37         return this.contents;
 38     }
 
 39 
 40     /**
 41      * @return state associated with the proxy web request
 
 42      */
 43     public ProxyState getState() {
 44         return this.state;
 45     }
 
 46 
 47     @Override
 48     public String toString() {
 49         return this.responseCode + "\n" + this.state + "\n" + this.contents;
 
 50     }
 51 }
 52 
 53 
 

MethodType

Following class defines enum for http method types:

  1 package com.plexobject.web.proxy;
 
  2 
  3 /**
  4  * Class: MethodType
 
  5  * 
  6  * Description: Defines supported method types for proxy request.
 
  7  * 
  8  */
  9 public enum MethodType {
 
 10     GET, POST;
 11 }
 12 
 13 
 

Service Example

Following classes show how above HTTPProxy and ContentTransfomer interfaces can be used with Servlet/Portlet APIs:

ProxyService Interface

 1 package com.plexobject.web.service;
 
 2 import javax.servlet.http.*;
 3 import java.io.*;
 4 
 5 public interface ProxyService {
 
 6     public void render(HttpServletRequest request,  HttpServletResponse response) throws IOException ;
 7     public void submit(HttpServletRequest request,  HttpServletResponse response) throws IOException ;
 
 8 }
 9 
 0 
 

ProxyService Implementation

  1 package com.plexobject.web.service;
 
  2 import com.plexobject.web.proxy.*;
  3 import com.plexobject.transform.ContentTransformer;
  4 import javax.servlet.http.*;
 
  5 import java.io.*;
  6 import java.util.*;
  7 
  8 
 
  9 public class ProxyServiceImpl implements ProxyService {
 10     private HttpProxy httpProxy;
 11     private ContentTransformer contentTransformer;
 
 12     public ProxyServiceImpl(HttpProxy httpProxy, ContentTransformer contentTransformer) {
 13         this.httpProxy = httpProxy;
 14         this.contentTransformer = contentTransformer;
 15     }
 16 
 
 17     public void render(HttpServletRequest request,  HttpServletResponse response)  throws IOException {
 18         String url = "http://plexrails.plexobject.com/guest_book/sign";
 19         ProxyState state = new ProxyState(url, MethodType.GET);
 
 20         String inputXhtml = httpProxy.request(state, null).getContents();
 21         Map<String, String> properties = new HashMap<String, String>();
 22         properties.put("callbackState", state.toExternalFormat());
 
 23         String transformedXhtml = contentTransformer.transform(inputXhtml, properties);
 24         response.getWriter().println(transformedXhtml);
 25     }
 26 
 27     public void submit(HttpServletRequest request,  HttpServletResponse response)  throws IOException {
 
 28         String originalActionUrl = request.getParameter("originalActionUrl");
 29         String orginalMethodType = request.getParameter("orginalMethodType");
 30         ProxyState userState = ProxyState.valueOf(request.getParameter("userState"));
 31         Map<String, String[]> params = request.getParameterMap();
 
 32         ProxyState state = new ProxyState(originalActionUrl, orginalMethodType);
 33         state.addCookies(userState.getCookies());
 34         ProxyResponse proxyResponse = httpProxy.request(state, params);
 35         response.getWriter().println(proxyResponse.getContents());
 36     }
 
 37 }
 38 
 39 
 

Download Code

You can download above code from here.

Acknowledgement

I would like to thank the folks at XSLT forum of Programmer-to-Programmer (http://p2p.wrox.com/forum.asp?FORUM_ID=79) for answering my XSLT questions.

« Newer PostsOlder Posts »

Powered by WordPress