If you are faced with having to write a rich client application in a multi-tier Java EE environment, you will typically connect to the application server over RMI. In theory, you are meant to use the servers Application Client Container and deploy your application as a client in that container. You probably won't do that though, because the client container is unfriendly for many reasons:
- As an example, the WebSphere 6.1 Client Container is a 200 megabyte install,
- Client Containers tend to be started as batch commands which set up the environment in which your application will run. If you however have an application that is meant to be started with a sexy launcher, as is the case with Eclipse RCP applications, you will struggle to get the environment created properly by the launcher, and its not supported by the vendor anyway,
- If you need to connect to the server securely (ie. so that serverside you have a valid security context allowing you to authorise users to call given services), then I personally have never been able to get the security callback mechanism to work. Theoretically you can tell the container to call your code at the point which it logs on to the server in order to get the credentials (eg. you can pop up a little login window),
For these reasons, I have never ever used a client container in a production environment. Instead I have repeatedly gone to the trouble of getting the client environment fit so that it can call the server over RMI with a security context. The problem is not only that this is painful, but that it is unsupported by the App Server vendor, and is almost guaranteed to break with the next release of the App Server, requiring further pain from a consultant in order to get be able to migrate to the next App Server version. Additionally, setting the environment for the Initial Context in a client, to connect to the server and find objects in the JNDI tree can be difficult and is often poorly documented (after all, you are meant to do it from inside the client container where you do not need to provide the environment yourself).
However, there is another way! In a previous article, I wrote about how I got an applet to send information back to the server by implementing a custom protocol containing serialised objects. In hindsight I discovered Hessian from Caucho which does a similar thing. Recently I have been looking into Spring which supports Hessian (Custom Binary over HTTP), Burlap (XML over HTTP) and its own kind of remoting, namely Java Serialisation over HTTP.
With little effort I was able to find out how to secure the HTTP connection with the server, meaning that I am now free to write an arbitrary rich client who can call services in a Java EE environment without the use of a Client Container, and without a non-supported RMI environment that is likely to not work after a server version migration. Woo Hoo! How? Easy, keep reading...
First, lets start with a little diagram:
This article won't go into detail about how to create an RCP application or plugin, and will simply call the remote service from a simple Java application. The steps involved in getting to where we are going are:
- Add a Datasource to JBoss
- Add Security to JBoss (using the datasource, although LDAP would be an option too)
- Create a Web Application containing the service implementation
- Create a client capable of calling the remote service
1) Add a datasource to JBoss
For this article, JBoss 4.2.2 was used. To add a datasource, create a configuration file:
where <instance> was "default" in the case of this example. This example uses MySQL 4.1. This configuration file should contain the following:
A restart of JBoss will make this datasource available in JNDI under the name
2. Add Security to JBoss
Add the following application policy to:
Before it can work, you will need to add two tables to the database. The following is some SQL to do that:
3) Create Web App
To create the web application you need the following files:
In the above
web.xml, I have configured a Spring ContextLoaderListener which uses the contextConfigLocation parameter to load the Spring configuration. A servlet has also been configured for the URL pattern "remoting" and this URL pattern has also been secured, requiring basic authentication in order to be requested.
remoting-servlet.xml is firstly named after the dispatcher servlet which was defined in
web.xml. Secondly, it defines the service exporter and our service that we wish to make remote, namely "TestService". What is missing from the above is the definition of the "testService" bean. That definition is located under the
That bean definition could equally have actually been included in
jboss-web.xml file contains the link between the security policy that was added to
login-config.xml, and the web application:
Next, on the web app classpath, we need to have two classes, namely the service interface and its implementation.
The web application now contains everything it needs to go. Restart JBoss and deploy the web application to it.
4) Create Client
All that is now left is to create a client that can call the remote service. Create a project with the following files:
To start with, the
applicationContext.xml is a Spring configuration and looks like this:
In the Spring configuration above, the remote service is defined in terms of its URL and its interface class. The "basicAuthenticationInvokerRequestExecutor" bean simply tells the "HttpInvokerProxyFactoryBean" to use a special InvokerRequestExecutor which is capable of logging into the service. The
BasicAuthenticationInvokerRequestExecutor basically comes from a Spring Forum, and is shown in its modified form next:
This class uses the Apache Commons HttpClient to use basic authentication when it logs into the web application when it calls the remote service.
Finally, the code which calls the remote service:
On thing missing in the above description, is that the client needs a reference to the service interface. In this example, built in Eclipse 3.4, the client project simply has a reference to the web application project containing the service interface. In reality you would want to have this interface in a client library which both the web application and the client can refer to.
Now everything is in place to call services on a remote server from a client, securely, and independently of application server Client Container. I chose to use Spring HTTP Remoting with standard Java serialisation (as opposed to Hessian or Burlap, which have their own problems due to restrictive serialisation), which locks the client and server to compatible Java versions. However, Java serialisation has not changed much in a while and I have successfully tested a client running in Sun Java 1.3, calling a Sun Java 1.6 server!
That is the end of this article, I hope it works for you, and gives you an insight into how to securely call services in a Java EE application server without the need to use the client container. Good Luck!