Browse DevX
Sign up for e-mail newsletters from DevX


Build a Java App Server Foundation for Thick-Client Deployment : Page 3

Web 2.0, rich Internet applications, and heavy JavaScript have become the latest rage. But why build a Web application that tries to look and act like a thick client when you can leverage a traditional J2EE/Web application server architecture to easily deploy an actual thick client?




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Spring Remoting
Now you should expose the stockService bean so that your forthcoming thick client(s) can invoke it. How? I've seen some thick-client-to-app-server architectures that effectively declare a Spring Controller and have the Controller return an XML file (think a Servlet whose response OutputStream is filled with said XML file). I've also seen some that exposed the stockService as a Web service. Neither is wrong, but there are better ways—which is where Spring Remoting comes in.

Spring Remoting allows you to declare your bean as a service and expose it using a variety of different protocols. Currently, Spring Remoting supports HTTP, Hessian, Burlap, RMI, or a JAX-RPC Web service. Not only that, Spring Remoting enables you to switch between protocols with no required code changes. So if you are building thick clients to deploy in your intranet, you can use RMI. Why RMI over HTTP? Well, how about two-way communication? Using RMI, you can have the server notify clients! By the same token, if you want to deploy your thick app outside of your network and past your firewalls, then you can expose that same service using HTTP—again, this requires no code change.

Now take a look at how you can achieve this:

  1. To expose the stockService over HTTP, you must declare a Spring Bean in the WebApplicationContext. To do so, add the following to springDispatcher-servlet.xml:

    <!-- httpInvoker exporter for the StockService --> <bean name="/stockServiceHttpInvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" lazy-init="false"> <property name="service"><ref bean="stockService"/></property> <property name="serviceInterface"> <value>stephenlum.services.stock.StockService</value> </property> </bean>

    This entry exposes the StockService over HTTP using Spring's HttpInvokerServiceExporter. When using Spring's HttpInvokerServiceExporter, you must also define the service and serviceInterface properties. Of additional note, notice that I have not declared a separate URLMappingHandler implementation. So the Web application will use Spring's default BeanNameUrlMappingHandler.

  2. Expose this service over RMI as well. Add the following entry to applicationContext.xml:

    <!-- rmi exporter for the StockService --> <bean name="/stockServiceRmi" class="org.springframework.remoting.rmi.RmiServiceExporter" lazy-init="false"> <property name="service"><ref bean="stockService"/></property> <property name="serviceName"><value>stockServiceRmi</value></property> <property name="serviceInterface"> <value>stephenlum.services.stock.StockService</value> </property> <property name="registryPort" value="1099"/> </bean>

    Note: Remember, running RMI on Tomcat requires that Tomcat be installed in a directory with no white space. (Click here to read the actual ASF Bugzilla bug. Apache has closed the ticket and is not going to fix it.).

    The second entry exposes the same StockService instance over RMI. It's that simple; the only differences being the Spring class you declare is RmiServiceExporter, and you must declare the serviceName property. Also, declare registryPort to 1099, RMI's default port.

  3. Write a quick test now. Create a new source folder under the stocktradeserver node called test. This new folder should reside at the same level as src.
  4. Create a new class called StockServiceImplTest, which extends TestCase. Create a test for the method getStocks(List<String> tickerList) called testGetStocks(), which throws Exception. In this method, you will test both the HTTP and RMI exposures.
  5. Now create an ApplicationContext for your test cases. Create file applicationContext-test.xml under the test directory and add the following:

    <beans> <bean id="stockServiceRmi" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <property name="serviceUrl"> <value>rmi://localhost:1099/stockServiceRmi</value> </property> <property name="serviceInterface"> <value>stephenlum.services.stock.StockService</value> </property> <property name="cacheStub" value="true"/> <property name="lookupStubOnStartup" value="true"/> <property name="refreshStubOnConnectFailure" value="true"/> </bean> <bean id="stockServiceHttpInvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> <property name="serviceUrl"> <value> http://localhost:8080/stocktradeserver/service/stockServiceHttpInvoker </value> </property> <property name="serviceInterface"> <value>stephenlum.services.stock.StockService</value> </property> </bean> </beans>

    Note that for bean stockServiceRmi, the property serviceUrl's context is the same as the serviceName value you declared in springDispatcher-servlet.xml:

    springDispatcher-servlet.xml serviceName="stockServiceRmi" applicationContext-test.xml serviceUrl="rmi://localhost:1099/stockServiceRmi"

  6. You're almost ready to test! The last step is to create a beanRefFactory.xml, because you will be using Spring's SingletonBeanFactoryLocator class. Create file beanRefFactory.xml under the test directory and add the following:

    <beans> <bean id="ctx" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg> <list> <value>applicationContext-test.xml</value> </list> </constructor-arg> </bean> </beans>

    Spring recommends SingletonBeanFactoryLocator to grab a reference to your ApplicationContext.

    Now, you must also let MyEclipse know about these new application contexts and the dependencies between them. To do this, right click on the stocktradeserver node in Package Explorer, and go back to MyEclipse-Spring. Click Add..., add the beanRefFactory.xml and applicationContext-test.xml files, and then click OK.

    Now click the Config Sets tab, and then New. For Name, enter stocktradeserver and check all four application contexts (see Figure 9). Click OK until the Properties window closes.

    Click to enlarge

    Figure 9. MyEclipse ConfigSets for StockTradeServer
  7. Now run your tests. In Eclipse, click the toolbar button Deploy MyEclipse J2EE project to Server (see Figure 10).

    Click to enlarge

    Figure 10. Deploy MyEclipse J2EE Server Button

    Make sure the project in the dropdown is stocktradeserver. Click Add, select Tomcat 5 as your server, and then click Finish. When you see a message saying Successfully deployed, click OK (see Figure 11).

    Click to enlarge

    Figure 11. StockTradeServer Successfully Deployed
  8. Now start Tomcat server (see Figure 12). Tomcat should start successfully.

    Click to enlarge

    Figure 12. Start Tomcat via MyEclipse Plugin
  9. Your first test will be through the browser. Open a Web browser and enter the URL: http://localhost:8080/stocktradeserver/service/stockServiceHttpInvoker.

    You should get this error. And that's actually a good sign! Why? Don't forget that you are making an HTTP request through the browser for a Java binary .class file. It absolutely should not be able to render. You can also see that HttpInvokerServiceExporter was invoked, meaning that the stockService is exposed over HTTP.

  10. Now run your JUnit test. In Package Explorer, navigate to your StockServiceImplTest class located under the test directory. Once you see the class, right click on it and choose Run As -> JUnit Test (see Figure 13).

    Click to enlarge

    Figure 13. Execute JUnit Test from Eclipse

Success! Your result should be a successfully executed JUnit test (see Figure 14).

Click to enlarge

Figure 14. StockServiceImplTest Successfully Executed

If you get an error like the following:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.net.MalformedURLException: no protocol: Files/Apache ... ... ... Caused by: java.net.MalformedURLException: no protocol: Files/Apache at java.net.URL.(URL.java:567) ... ... ...

Remember you can either remove/not invoke the RMI portions, or change Tomcat's location to one without whitespace.

Get Your Thick Client and App Server Talking

This tutorial has shown you how easy it is to expose your service using Spring and RMI. Thick-client-to-app-server communication is easier than it has ever been thanks to Spring Remoting. Spring's flexibility to change the protocol without changing your own code is just superb. As far as I'm concerned, it is better than any other solution out there—hand-rolled or otherwise. This protocol swap is particularly convenient if you want your server to push events—something that is not feasible using HTTP and Web clients.

Take the Next Step
Now that you have a functioning thick-client-to-app-server architecture, learn how to leverage the Eclipse Rich Client Platform to build an actual thick client in Stephen Lum's follow-up article: "Eclipse RCP Meets Spring: A Perfect Thick-Client Match".

Stephen Lum, a senior developer for an investment bank in London, has been programming in Java for more than seven years. He is Sun certified as a Java programmer, Oracle certified as a developer, and also is a CPA.
Thanks for your registration, follow us on our social networks to keep up-to-date