3

I've followed the tutorial for spring remoting, in particular HttpInvokerServiceExporter and have no problems setting up both the client and the server (factorybean).

Question is, I've noticed using Spring MVC, each interface is mapped to a particular URL

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

    <!-- *********************exposed web services*************************--> 
    <bean name="test_service" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="serviceInterface" value="foo.webservices.HelloServiceInterface" />
        <property name="service">
            <ref bean="helloService"></ref>
        </property>

Question is, if I have more than 1 method in my service interface, is it possible to map each of these interface methods to a URL themselves?

goh
  • 27,631
  • 28
  • 89
  • 151

3 Answers3

4

The idea behind HTTPInvokerServiceExporter is to provide an endpoint to receive remove method invocation.

On the server side it would be easy as configure a RemoteExporter, declaring the interface you want to expose and associating it to the real bean that will handle the invocations.

On the client it would be necessary configure the RemoteAcessor that basically needs the interface that is going be access on the server side.

The implementation over HTTP can be done using HttpInvokerServiceExporter on the server side like the following example:

Service Interface:


package foo.bar.services;

public interface MyService {

    public int sum(int num1, int num2);

}

For this service you would have an implementation (let's say foo.bar.services.DefaultMyService).

The exposition and configuration on spring context would look like:


<bean name="myService" class="foo.bar.DefaultMyService" />
<bean name="/myService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
  <property name="service" ref="myService"/>
  <property name="serviceInterface" value="foo.bar.MyService"/>
</bean>

With this configuration I just intended to have a instance of my implementation of MyService being exposed under foo.bar.MyService interface over the URL mapped by /myService (bean name) in the simpliest case using BeanNameUrlHandlerMapping that does nothing else than map a URL to a bean with the URL value (this case /myService).

On the client side (consumer), you will have a bean configured just declaring the interface you expect the remote side is exposing. For our service would be something like:

<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
  <property name="serviceUrl" value="http://foo.bar.org/springSample/myService"/>
  <property name="serviceInterface" value="foo.bar.MyService"/>
</bean>

At the moment spring instantiate the beans on client side, a proxy is instantiated and every method invocation would then be serialized and sent over HTTP to the endpoint (in this case http://foo.bar.org/springSample/myService. At the server side this request is deserialized and interpreted i.e. invoking the method on the real service being exposed (on our case DefaultMyService). The service will return something that will be serialized an gave as result to the HTTP request performed by the client. The client will receive it and deserialize it and return it to the original method invocator.

As you can see (and got from Spring documentation):

The server side will

Deserializes remote invocation objects and serializes remote invocation result objects. Uses Java serialization just like RMI, but provides the same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.

The client side will

Serializes remote invocation objects and deserializes remote invocation result objects. Uses Java serialization just like RMI, but provides the same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.

The serialization used with Spring Remoting on this case is Java serialization, meaning the HTTP request holds an entity body containing the serialized object (is good to keep in mind that in this case JVM version and class versions needs to be compatible).

The exposition is done as a whole, you could not separate it having one URL for each method of that interface. Therefore would be easier the use of Spring MVC and create a controller (@Controller) implement a method for each method you have on your interface annotating it as @RequestMapping (with the desired URL) and invoke the method on the service as follows the example:

Controller Sample


package foo.bar;

import foo.bar.service.MyService;

@Controller
public class MyService {
    @Autowired
    private MyService myService;

    @RequestMapping("/sum/{num1}/{num2}")
    public int sum(@PathVariable("num1") int num1, @PathVariable("num2") int num2) {
        return myService.sum(num1, num2);
    }

}

Using the context configuration

<context:component-scan base-package="foo.bar"/>

it would map the classes found on foo.bar and under packages automatically, meaning the Service implementation (DefaultMyService) could be mapped with @Service and @Autowired to the controller as done in the sample, without any bean configuration on the context xml.

But this will expose your service over REST interface, meaning it will handle HTTP plain requests that could be done by other consumers like a PHP consumer (this couldn't be done with Spring Remoting because it uses pure Java serialization).

If your client is Java you could definitively use Remoting and expose your service as a whole, if not the REST implementation using Spring MVC is a good solution.

Spring Documentation can be found here

Francisco Spaeth
  • 23,493
  • 7
  • 67
  • 106
1

I really recommend defining your controllers with the @Controller annotation. From the Spring documentation, start with adding a component scan to your Spring config.

<context:component-scan base-package="your.package.path"/>

Then, in a class, say, your.package.path.WhateverController, annotate the class:

@Controller
WhateverController

And annotate your methods with @RequestMapping:

@RequestMapping(value = "/helloWorld")
public ModelAndView helloWorld() {
...
}

@RequestMapping(value = "/project/{projectId}")
public ModelAndView helloWorld(@PathVariable String projectId) {
...
}

That's about it, multiple mappings per controller.

stevedbrown
  • 8,862
  • 8
  • 43
  • 58
1

I think you need to tell us what you are trying to do first. If you are exposing your services to a universal web client, REST or SOAP would be a better option; if you are exposing your services to another Spring bean in another application, then Spring Remoting might suffice.

The comment "exposed web services" in your spring configuration file seems unclear of your purpose.

When you expose a service to remote clients, you always need to provide a contract to let the remote clients know what's possible. REST exposes that contract using URL's; SOAP exposes that contract using WSDL's; and Spring Remoting just directly gives the client that contract in a java interface, so the client can inject that interface into its own beans and use it like a locally defined bean.

In your question you mentioned mapping a method to an URL, Spring Remoting can't do that because it exposes intefaces; what you want to do sound very REST to me. And I agree with @stevebrown, exposing your services using a mapped controller is a good approach.

Chao
  • 1,058
  • 7
  • 12