Freitag, 3. Februar 2012

Hessian Wrapper to Enable Context Propagation

One advantage od the hessian protocol (compared e.g. with pure RMI) is that it is able to propagate implicite context information together with a remote call. However there are a few things to prepare in order to use this feature:

First we need an extension of the HessianProxy passing context information to the request header.

public class HessianProxyWithContext extends HessianProxy {
 private static final long serialVersionUID = 1L;
 private Map context = new HashMap();
 public HessianProxyWithContext(URL url, HessianProxyFactory factory, Class type) {
  super(url, factory, type);
 }
 public HessianProxyWithContext(URL url, HessianProxyFactory factory) {
  super(url, factory);
 }
 void setContext(Map context) {
  this.context = context;
 }
 protected void addRequestHeaders(HessianConnection conn) {
  super.addRequestHeaders(conn);
  for (Entry e : context.entrySet()) {
   conn.addHeader(e.getKey(), e.getValue());
  }
 }
}

Then we need an extension of the factory using our new Proxy class:

public class HessianProxyWithContextFactory extends HessianProxyFactory {
 private Map context = new HashMap();
 public Map getContext() {
  return context;
 }
 public String putContext(String key, String value) {
  return context.put(key, value);
 }
 public Object create(Class api, URL url, ClassLoader loader) {
  if (api == null)
   throw new NullPointerException("api must not be null for HessianProxyFactory.create()");
  HessianProxyWithContext handler = new HessianProxyWithContext(url, this, api);
  handler.setContext(context);
  return Proxy.newProxyInstance(loader, new Class[] { api, HessianRemoteObject.class }, handler);
 }
}

Note that the context information can be set at the factory for the next call.
Finally we have to extract the context information from the request header at the server.
For this purpose we need to access the servlet request.
To achive this we use a little extension of the HessianServlet class:

public class HessianServletWithContext extends HessianServlet {
 private static final long serialVersionUID = 1L;
 private ServletRequest request = null;
 @Override
 public void service(ServletRequest req, ServletResponse response) throws IOException, ServletException {
  request = req;
  super.service(req, response);
 }
 public String getContext(String key) {
  String value = null;
  if(request instanceof HttpServletRequest) {
   value = ((HttpServletRequest) request).getHeader(key);
  }
  return value;
 }
}

Basically this was all we need. The following example shows how to use the little wrapper:
public interface TestInterface {
 public String echoContext(String call);
}
public class TestEndpoint extends HessianServletWithContext implements TestInterface {
 private static final long serialVersionUID = 1L;
 public String echoContext(String contextKey) {
  return contextKey +"="+ getContext(contextKey);
 }
}
public class ClientTest {
    private TestInterface test;
    @Before
    public void initProxy() throws MalformedURLException {
        String url = "http://localhost:8080/test/TestInterface";
        HessianProxyWithContextFactory factory = new HessianProxyWithContextFactory();
        factory.putContext("contextKey", "contextValue"); 
        test = (TestInterface) factory.create(TestInterface.class, url);
    }
    @Test
    public void echoContext() {
      System.out.println(test.echoContext("unknown"));
      System.out.println(test.echoContext("contextKey"));
    }
}

Keine Kommentare:

Kommentar veröffentlichen