Pico Web Remoting

Pico Web Remoting (PWR) allows web requests to be bounded directly to methods in a class. All public methods are eligible, and method parameters are pulled from query-string or form post-fields for invocation. PWR is not a page technology as such, as it uses AJAX invoation of methods on the server side. As such you most likely going to use this technology from JavaScript, with a library like jQuery that makes calling server-side functions that return JSON replies easy.

Every Object gets a URL !

We have tried to make PWR as transparent as possible. Thus objects that can be published via PWR, are POJOs. It is up to you whether you interface/impl separate them, but be reminded that by default all public methods will be published. As with all of the PicoContainer Web technologies, you will decide which scope a component belongs in - application, session or request - and compose them accordingly.

Publishing class and method names direct to the web, is alluring, but it is also dangerous. Do not think of this as publishing a domain model to the web, it is really a way for you to markup contacts in plain Java. It also suggests that you are going to want to have a different package, class and method naming design for the objects your publishing with PWR. Read about the example, then read Naming Considerations below

Example webapp : Simple email package.

We forked an example of jQuery and Java together made for an IBM AlphaWorks article: Working with jQuery, Part 1: Bringing desktop applications to the browser. The original had a custom servlet to decode requests, invoke methods and craft JSON replies. We've essentially deleted that servlet and leveraged PWR instead.

Binding objects to URLs - how it works

In the demo webapp there a 'send' method on a class called 'Sent'. The class name and method name are encoded in the URL. The method parameters are pulled from the form post fields, and the reply is a JSON representation of the methods return type. Here are some screenshots from a Firefox plugin called HttpFox and how the web request maps to the source method. The case in question is for the 'send' function of the Simple email app:

post

MessageData is just a POJO:

post

A GET invocation is quite similar. Instead of form post-fields the method parameters are mapped to query string parameters.

Exceptions

If PWR encounters an exception during the processing of a method invocation, it takes the message text and makes it available to the client in the following style:

{
  "ERROR": true,
  "message": "no such message ID"
}

Doing the scoped Dependency Injection setup.

Composition of the webapp is done in an implementation of WebappComposer, like so:

package org.picocontainer.web.sample.jqueryemailui;

import org.picocontainer.web.WebappComposer;
import static org.picocontainer.web.StringFromRequest.addStringRequestParameters;
import static org.picocontainer.web.IntFromRequest.addIntegerRequestParameters;
import org.picocontainer.MutablePicoContainer;
import static org.picocontainer.Characteristics.USE_NAMES;

public class JQueryWebappComposer implements WebappComposer {


    // These will be cached and shared for all sessions unless otherwise specified

    public void composeApplication(MutablePicoContainer appContainer) {
        appContainer.addComponent(MessageStore.class, InMemoryMessageStore.class);
    }


    // These components can depend on each other or those from the application scope
    // They will also be cached per session unless otherwise specified

    public void composeSession(MutablePicoContainer sessionContainer) {
        // stateless
    }


    // These components can depend on each other or those from the session scope
    // They will also be cached per session unless otherwise specified

    public void composeRequest(MutablePicoContainer requestContainer) {

        // User is specified in a Cookie (after signin obviously)
        requestContainer.addAdapter(new User.FromCookie());

        // Regular components. The public methods for which are published remotely
        requestContainer.as(USE_NAMES).addComponent(Auth.class);
        requestContainer.as(USE_NAMES).addComponent(Inbox.class);
        requestContainer.as(USE_NAMES).addComponent(Sent.class);
    }

}

In the above there is a single application-scoped component, InMemoryMessageStore, for all users/sessions and requests to share. All other components are added at request scope, meaning they are only instantiated at the time of request and only if they are needed. The major components are Auth, Inbox and Sent. They have their public methods made available. Inbox and Sent methods are effectively not available until login has been performed via the Auth class. Once you have logged in, a cookie is set, thus User.FromCookie (a Provider) allows access to that. The parameters for the methods in Auth, Inbox and Send are made available via the addStringRequestParameters(..) and addIntegerRequestParameters(..) methods. These set up multiple Injectors for to allow the binding by name of things from query-string parameters or form post-fields.

Convention over Configuration !

PicoContainer Web Remoting(by default) does not have XML files like Spring, JBoss or XWork (Struts2). It also tries to be as transparent as possible - there are are no base classes to extend, no interfaces to implement, and no mandatory annotations, no bean definitions, no URL mappings, no property assignments - you just call methods on components from a web page in JavaScript. This makes it easy to test components in plain unit tests too.

Here is an fairly standard web.xml for a PicoWebRemoting webapp:

	
	  Pico Web Remoting Demo

	  
	    webapp-composer-class
	    org.picocontainer.web.sample.jqueryemailui.JQueryDemoWebappComposer
	  

	  
	    pwrFilter
        org.picocontainer.web.remoting.PicoWebRemotingServlet$ServletFilter
	  

	  
	    pwrFilter
	    /pwr/*
	  

	  
        org.picocontainer.web.PicoServletContainerListener
	  

      
        pwrServlet
        org.picocontainer.web.remoting.JsonPicoWebRemotingServlet
        1
        
            scopes_to_publish
            session,request
        
        
            package_prefix_to_strip
            org.picocontainer.web.sample.jqueryemailui
        
      

      
        pwrServlet
        /pwr/*
      

	  
	    index.html
	  

	


Limiting Scopes to Publish

In the web.xml file, you can specify a 'scopes_to_publish' element that takes a comma separated list of scopes. One or more of 'application' , 'session' and 'request' (without the quotes). This listed will be analysed for publication as PWR invocable.

Limiting HTTP Methods for a Class' Methods

Only public method on components managed by PicoContainer are eligible. By default, all web methods (GET, POST, PUT, DELETE) are possible for a matching method. However there are annotations (in package org.picocontainer.web) with names @GET, @POST, @PUT, @DELETE, that restrict down possibilities. If you want a component's method to be only available for POST, put a @POST annotation above it. If you have a public method that you do not want to publish in a component that is otherwise published, use @NONE.

Shortening URLs to objects.

PWR by default will encode the whole class name including package. All dots in the package name are changed to slashes to make apparent directories, and the case of the Class name itself is preserved. With a web.xml element called package_prefix_to_strip it is possible to eliminate a prefix for all of the objects published. Thus, you could essentially turn http://mycompany.com/r/com/mycompany/store/Cart/buy into http://mycompany.com/r/store/Cart/buy if you wanted. The /r/ prefix is the mapping to the servlet that is configured in the web.xml - it could just as easily be /pwr/ or /rpc/ or /ajax/

Mapping to a Suffix

You can have a suffix such as .ajax or .json at the end the method name. For example http://mycompany.com/r/com/mycompany/store/Cart/buy.json might be what you want. There is an servlet init-param for the servlet in web.xml element called suffix_to_strip that would specify '.json' in this case.

Lower case Class names in path

In our example above Auth (the class) appears as 'Auth' in any URLs. If you specify an init parameter of 'lower_case_path' as true, then you can have it as 'auth' in URLs

Cross-site request forgery (CSRF) protection

Many AJAX capable technologies are vulnerable to CSRF attacks. PWR is no different. If you are running your webapp publically, and there are functions that could be valuable for a hacker to invoke simulating genuine user action, you will want to guard them. With PWR, you would set something in the page, that would be send back to the server with each AJAX request. The Guard class would be instantiated before the potentially vulnerable method would be invoked. The guard class would be instantiated and depend on the guard parameter, ensuring that it could be verified and that an exception could be thrown:

requestContainer.addComponent("guard", MyGuard.class);
requestContainer.as(GUARD, USE_NAMES).addComponent(VulnerableThing.class);

Explicit dependance on parameters from HTTP request

Binding to parameter from the HTTP Request is implicit as long as you use as(USE_NAMES) when adding components. If you the range of parameter names to be explicit, do the following in WebappComposition class

// these are pulled from query-strings (GET) or form-fields (POST)
addStringRequestParameters(requestContainer,
        "to", "subject", "message", "view",
        "userName", "password", "userId", "sec");

addIntegerRequestParameters(requestContainer, "msgId");

Naming considerations.

Consider the this URL: http://mycompany.com/r/com/mycompany/store/ShoppingCart/addToCart

It is what would result from a Java class called ShoppingCart with a method called addToCart (in package com.mycompany.store). Java is often long winded like this. You are going to want to change the rules when working with PWR. ShoppingCart can be shortened to Cart as it is clear being in a directory (package) 'store' what it is. Method 'addToCart' is also too verbose if we consider the URL and the implicit association with 'Cart'. Thus 'buy' might be clearer intent.

In short, when naming packages, classes and methods for PWR, you should think about contracts between the client and the server app, and not simply publishing preexisting names.

It is also the case that you can only have one method of a particular name that is public, otherwise PWR will not know which ne to invoke for incoming requests.

Further reading.

The Maven based example project can be checked out with Subversion from here.

Downloading.

See downloads on how to download the Pico Web Remoting jar either using Maven or by downloading the full distribution.