Simple Spring HTTP Remoting Example

I am using this blog entry to demonstrate by simple example the use of the Spring Framework's HTTP Remoting. There are numerous online resources on this subject, so my intention here is to provide an extremely simple but complete demonstration of using Spring's HTTP Remoting with non-browser clients.

The Spring approach to HTTP Remoting allows clients to communicate with the Spring-hosted server code via HTTP without the client code requiring any knowledge of HTTP being used. Instead, the client Java code only "sees" normal business-related Java objects (usually interfaces) rather than HTTP-specific objects.

Spring HTTP Remoting generally requires Spring and Java on both the server side and client side. However, if those two requirements can be met, Spring HTTP Remoting is easily applied.

The following steps allow HTTP communication between Spring-hosted clients and servers. After first briefly outlining the steps, I'll then delve into them in more detail (including code samples).

  1. Create or use an existing Spring bean that typically implements a Java interface.

    This is nothing special to HTTP remoting and is the same step you'd need to take to do most things in Spring (a notable exception is

    Spring JDBC

    that does not require any Spring beans to be used).

  2. Create the Spring XML configuration file for associating the bean created in step #1 with a Spring application context.

    As with Step #1, this XML file is nothing particular to Spring HTTP Remoting, but is instead common to nearly all Spring Framework wiring and configuration.

  3. Create or add to web.xml file.

    This third step is the first step that is more particular to Spring HTTP Remoting, but is still generally applicable with

    Spring MVC framework

    . This step includes adding the servlet class and URL mappings as one usually uses with

    Java EE

    servlets

    and

    JavaServer Pages

    . The most important part of this step is to specify the Spring

    DispatcherServlet

    . An optional "link" is also provided in this

    web.xml

    file to a context config location where one or more Spring XML application context files are located and used.

  4. Create the Spring-specific servlet context file.

    This XML file looks a lot like a "normal" Spring application context XML configuration file, but its name is prescribed by the convention of servlet name followed by a hypen and the word servlet. In other words, if the servlet was called "somewebthing" in the

    web.xml

    file, this Spring servlet configuration file would be called

    somewebthing-servlet.xml

    . This file contains the configuration for the

    HttpInvokerServiceExporter

    (the piece of this that is particular to the HTTP Remoting covered in this blog entry) and URL mapping information.

  5. Test!

    Although the simple client will be writting without HTTP in mind and will appear to only be using Java objects, it will actually be invoking the service via HTTP. This will be "proven" by running the client without the service deployed and watching for the resulting HTTP error code.

I'll now move onto demonstrating the above steps in greater detail and attempt to illustrate them concretely with code samples.

Step #1: The Bean and Its Interface

This step is no different than defining Java classes and interfaces they implement for use with Spring. The following code listings show the interface (StateCapitalServiceIF) and the implementing class (StateCapitalService) used for this example.

--- StateCapitalServiceIF.java ---

package examples.springhttp;

import java.io.Serializable;

/**
 * The State Capital Service interface that the client will use to access 
 * server-side functionality via HTTP.
 */
public interface StateCapitalServiceIF extends Serializable
{
   /**
    * Provide capital of state whose name is provided.
    * 
    * @param stateName Name of state whose capital is desired.
    * @return Capital of the specified state; null if not found.
    */
   public String getCapital(final String stateName);
}

--- StateCapitalService.java ---

package examples.springhttp;

import java.util.Map;

/**
 * Implementation of functionality to be run after being called by client via
 * HTTP.
 */
public class StateCapitalService implements StateCapitalServiceIF
{
   Map<String, String> statesAndCapitals = null;

   public StateCapitalService()
   {
   }

   /**
    * Set my states to state capitals mapping.
    * 
    * @param statesAndCapitals States to state capitals mapping.
    */
   public void setStatesAndCapitals(final Map<String,String> statesAndCapitals)
   {
      this.statesAndCapitals = statesAndCapitals;
   }

   /**
    * Provide capital of state whose name is provided.
    * 
    * @param stateName Name of state whose capital is desired.
    * @return Capital of the specified state; null if not found.
    */
   public String getCapital(final String stateName)
   {
      return this.statesAndCapitals.get(stateName);
   }
}

Step #2: Spring Application Context Configuration File

I like to keep Spring's HTTP-specific configuration separate from the bean's XML configuration. Therefore, the bean's configuration is exactly like one would see normally with Spring. To configure the StateCapitalService class above, the following configuration is used:

--- spring-http-config.xml ---

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

   <util:map id="statesCapitalsMap">
      <entry key="Alabama" value="Montgomery" />
      <entry key="Alaska" value="Juneau" />
      <entry key="Arizona" value="Phoenix" />
      <entry key="Arkansas" value="Little Rock" />
      <entry key="California" value="Sacramento" />
      <entry key="Colorado" value="Denver" />
      <entry key="Connecticut" value="Hartford" />
   </util:map>

   <bean id="stateCapitalService"
         class="examples.springhttp.StateCapitalService"
         p:statesAndCapitals-ref="statesCapitalsMap" />

</beans>

So far, nothing specific to HTTP Remoting has been done. In fact, the bean, its interface, and its XML application context configuration could all be run by a normal Java SE class like the one shown below:

--- MainServiceAppContext.java ---

package examples.springhttp;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Demonstrates how Spring bean can be used without any HTTP involvement.
 */
public class MainServiceAppContext
{
   public static void printStateInfo(
      final StateCapitalServiceIF stateCapitalMapper,
      final String state)
   {
      System.out.println(
           "The capital of " + state + " is "
         + stateCapitalMapper.getCapital(state));
   }

   /**
    * @param args the command line arguments
    */
   public static void main(String[] args)
   {
      final ApplicationContext context =
         new ClassPathXmlApplicationContext(
            "examples/springhttp/spring-http-config.xml" );
      StateCapitalServiceIF stateCapitalMapper =
         (StateCapitalServiceIF) context.getBean("stateCapitalService");
      printStateInfo(stateCapitalMapper, "Alabama");
      printStateInfo(stateCapitalMapper, "Colorado");
   }
}

Step #3: The web.xml File

This web.xml file is familiar to anyone who has developed a Java EE web application. The web.xml used in this example is shown next.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

   <display-name>
      Simple Spring HTTP Remoting Example
   </display-name>

   <description>
      This is meant as an extremely simple example of using Spring's HTTP
      Remoting capability.
   </description>

   <servlet>
      <servlet-name>statesCapitals</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>statesCapitals</servlet-name>
      <url-pattern>/statesCapitals</url-pattern>
   </servlet-mapping>

   <listener>
      <listener-class>
         org.springframework.web.context.ContextLoaderListener
      </listener-class>
   </listener>

   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/examples/springhttp/spring-http-config.xml</param-value>
   </context-param>

</web-app>

Step #4: The Servlet Context Configuration File

Because the servlet in this example is named "statesCapitals," a Spring servlet configuration file named statesCapitals-servlet.xml needs to be provided. It is shown next:

--- statesCapitals-servlet.xml ---

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

   <bean id="httpStateCapitalService"
         class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"
         p:service-ref="stateCapitalService">
      <property name="serviceInterface">
         <value>examples.springhttp.StateCapitalServiceIF</value>
      </property>
   </bean>

   <bean id="urlMapping"
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
      <property name="mappings">
         <props>
            <prop key="/statesCapitals">httpStateCapitalService</prop>
         </props>
      </property>
   </bean>

</beans>

Step #5: Testing It

We need to configure the client to communicate via HTTP with our server-side application. The configuration for this is contained in spring-http-client-config.xml for this example and is shown next:

--- spring-http-client-config.xml ---

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

   <bean id="stateCapitalProxyService"
         class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
      <property name="serviceUrl">
         <value>http://localhost:8080/SpringHTTPExample/statesCapitals</value>
      </property>
      <property name="serviceInterface">
         <value>examples.springhttp.StateCapitalServiceIF</value>
      </property>
   </bean>

</beans>

The client code that uses the above XML to bootstrap a Spring container and call the server-side code via HTTP is in the class HttpClient and that code is shown next:

--- HttpClient.java ---

package examples.springhttp.client;

import examples.springhttp.StateCapitalServiceIF;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * This class demonstrates a client of a Spring HTTP-exposed service and shows
 * how the client interacts with the server as if using normal Java objects
 * rather than using anything HTTP specific.
 */
public class HttpClient
{
   public static void printStateInfo(
      final StateCapitalServiceIF stateCapitalMapper,
      final String state)
   {
      System.out.println(
           "The capital of " + state + " is "
         + stateCapitalMapper.getCapital(state));
   }

   public static void main(final String[] arguments)
   {
      final ApplicationContext context =
         new ClassPathXmlApplicationContext(
            "examples/springhttp/client/spring-http-client-config.xml");
      final StateCapitalServiceIF stateCapitalService =
         (StateCapitalServiceIF) context.getBean("stateCapitalProxyService");
      printStateInfo(stateCapitalService, "Colorado");
      printStateInfo(stateCapitalService, "Alabama");
   }
}
Related:
1 2 Page 1
Page 1 of 2