Does Resin 4 Support Remoting?

From Resin 4.0 Wiki

(Difference between revisions)
Jump to: navigation, search

Revision as of 00:00, 16 May 2012

A frequent question is: Does Resin 4 support EJB remoting?

The short answer is no. The longer answer is that Resin supports Hessian for remoting. Hessian (now Hessian 2) predates many other forms of remoting and is a wicked fast, binary protocol (faster than CORBA, RMI, SOAP, XML-RPC, etc). You could think of Hessian as a high performance binary JSON. Hessian has been ported to many languages. Hessian is a remoting framework and a flexible Java serialization framework.

You can expose any bean as a Hessian remote bean quite easily. Hessian has been around for 10 years, and is very solid. (Both Hessian and Resin are development and maintained by Caucho).

Resin 4 documentation does not have Hessian documentation yet, but Hessian usage has not changed in years. You can find a good tutorial on getting started with Hessian from the Resin 3 documentation. I've tried these tutorial steps in Resin 4 and the tutorial works as advertised.

Resin 4 is Java EE Web Profile certified as such it does not support CORBA, EJB remoting, etc. However Resin does support Java Dependency Injection (CDI), which allows you to easily find beans with certain annotations. What follows is a simple example that finds all @Stateless beans that have @Remote interfaces and automatically exposes those beans as remote hessian objects:


First let's show our remote interface

Contents

Remote interface

package example;

import javax.ejb.Remote;

@Remote
public interface HelloWorld {
	
	String hello();

}

Hello Service

package example;

import javax.ejb.Stateless;
import javax.inject.Named;

@Stateless @Named
public class HelloService implements HelloWorld{

	
	@Override
	public String hello() {
		return "Hello world!";
	}

}


HelloService client

package example;

import com.caucho.hessian.client.HessianProxyFactory;

public class HelloClient {

	public static void main(String[] args) throws Exception {
		String url = "http://localhost:8080/hessian/helloService";
		HessianProxyFactory factory = new HessianProxyFactory();
		HelloWorld hello = (HelloWorld) factory.create(HelloWorld.class, url);

		System.out.println("Hello: " + hello.hello());
	}

}

You could just register this bean via a web.xml file as follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:javaee="http://java.sun.com/xml/ns/javaee" 
                 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
    <init-param>
      <javaee:param-name>home-class</javaee:param-name>
      <javaee:param-value>example.HelloService</javaee:param-value>
    </init-param>
    <init-param>
      <javaee:param-name>home-api</javaee:param-name>
      <javaee:param-value>example.HelloWorld</javaee:param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/helloService</url-pattern>
  </servlet-mapping>
</web-app>

But what if you wanted to include 20 or 30 services in the war file. Using XML to configure all of those would be a drag. Death by XML!

Well, Servlet 3.0 allows you to programmatically add Servlets, and CDI allows you to discover Java EE managed beans. So instead of configuring XML, we can look up the beans and then configure hessian servlets for out beans as follows:

RemoterListener is a Application Lifecycle Listener class that uses CDI to lookup @Stateless @Remote beans

package example;

import java.util.Set;

import javax.ejb.Stateless;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;
import javax.ejb.Remote;
import javax.inject.Inject;

@WebListener
public class RemoterListener implements ServletContextListener {

	@Inject
	BeanManager beanManager;

	public void contextInitialized(ServletContextEvent event) {
		ServletRegistration.Dynamic registration;

		Set<Bean<?>> beans = beanManager.getBeans(Object.class);

		for (Bean<?> bean : beans) {
			System.out.println(bean.getBeanClass());
			System.out.println(bean.getName());

			if (!bean.getBeanClass().isAnnotationPresent(Stateless.class)) {
				continue;
			}
			if (bean.getName() == null || bean.getName().isEmpty()) {
				continue;
			}

			registration = event.getServletContext().addServlet(
					"hessian-" + bean.getName(),
					"com.caucho.hessian.server.HessianServlet");
			
			registration.setInitParameter("home-class", bean.getBeanClass()
					.getName());

			Class<?>[] interfaces = bean.getBeanClass().getInterfaces();
			for (Class<?> interface_ : interfaces) {
				if (interface_.isAnnotationPresent(Remote.class)) {
					registration.setInitParameter("home-api", bean.getBeanClass()
							.getName());
					break;
				}
			}

			registration.addMapping("/" + bean.getName());

		}

	}

	/**
	 * @see ServletContextListener#contextDestroyed(ServletContextEvent)
	 */
	public void contextDestroyed(ServletContextEvent event) {
	}

}

RemoterListener is a Application Lifecycle Listener class that uses CDI to lookup @Stateless @Remote beans. It takes the name of the bean, and and uses that for the URI path. For each bean that has a @Stateless annotation, it registers a HessianServlet programmatically.

In about 20 lines of code, you can expose all @Stateless/@Remote beans at remote services using the Hessian protocol using CDI and Servlet 3.0.

Personal tools
TOOLBOX
LANGUAGES