Skip navigation links

Package org.apache.juneau.rest.remoteable

Remoteable service API

See: Description

Package org.apache.juneau.rest.remoteable Description

Remoteable service API

Defines an API for remote proxy interfaces (e.g. Remoteable Services).

Table of Contents
  1. Remoteable Services

  2. Client Side

  3. Server Side

  4. @Remoteable Annotation

1 - Remoteable Services

The Remoteable Service API allows for client side code to use interface proxies for calling methods on POJOs on the server side.

Proxy interfaces are retrieved using the RestClient.getRemoteableProxy(Class) method. The remoteable servlet is a specialized subclass of RestServlet that provides a full-blown REST interface for calling remoteable services (e.g. POJOs) remotely.

The following simplified example shows how a method on a POJO on a server can be called through an interface on a client...

public interface IAddressBook { Person createPerson(CreatePerson cp) throws Exception; Person findPerson(int id); Address findAddress(int id); Person findPersonWithAddress(int id); }

The client side code for invoking this method is shown below...

// Create a RestClient using JSON for serialization, and point to the server-side remoteable servlet. RestClient client = new RestClientBuilder() .rootUrl("https://localhost:9080/juneau/sample/remoteable") .build(); // Create a proxy interface. IAddressBook ab = client.getRemoteableProxy(IAddressBook.class); // Invoke a method on the server side and get the returned result. Person p = ab.createPerson( new CreatePerson("Test Person", AddressBook.toCalendar("Aug 1, 1999"), new CreateAddress("Test street", "Test city", "Test state", 12345, true)) );

The requirements for a method to be callable through the remoteable service are:

2 - Client Side

Remoteable interface proxies are retrieved through the existing RestClient class.

It may seem that the client-side code would need to be complex. In reality, it builds upon existing serializing, parsing, and REST capabilities in Juneau resulting in very little additional code. The entire code for the RestClient.getRemoteableProxy(Class) method is shown below:

public <T> T getRemoteableProxy(final Class<T> interfaceClass) { return (T)Proxy.newProxyInstance( interfaceClass.getClassLoader(), new Class[] { interfaceClass }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) { try { String uri = remoteableServletUri + '/' + interfaceClass.getName() + '/' + ClassUtils.getMethodSignature(method); return doPost(uri, args).getResponse(method.getReturnType()); } catch (Exception e) { throw new RuntimeException(e); } } }); }

Since we build upon the existing RestClient API, we inherit all of it's features. For example, convenience methods for setting POJO filters and properties to customize the behavior of the serializers and parsers, and the ability to provide your own customized Apache HttpClient for handling various scenarios involving authentication and Internet proxies.

3 - Server Side

The server side is only slightly more complex, but boasts useful debugging and discovery capabilities.

The RemoteableServlet class is an implementation of RestServlet that provides a REST interface for invoking calls on POJOs. The RemoteableServlet class is abstract and must implement a single method for providing the set of POJOs to expose as remote interfaces.

The samples bundle includes a sample implementation of a remoteable service that can be used to interact with the address book POJO also included in the bundle. The method that must be implemented is RemoteableServlet.getServiceMap() that simply returns a mapping of Java interfaces (or classes) to POJO instances.

@RestResource( path="/remoteable" ) public class SampleRemoteableServlet extends RemoteableServlet { // The POJO being manipulated (i.e. the remoteable service) AddressBook addressBook = new AddressBook(); @Override /* RemoteableServlet */ protected Map<Class<?>,Object> getServiceMap() throws Exception { Map<Class<?>,Object> m = new LinkedHashMap<Class<?>,Object>(); // In this simplified example, we expose the same POJO service under two different interfaces. // One is IAddressBook which only exposes methods defined on that interface, and // the other is AddressBook itself which exposes all public methods defined on the class itself. m.put(IAddressBook.class, addressBook); m.put(AddressBook.class, addressBook); return m; } }

Since this class is a servlet, and can be deployed as such. In the sample code, it's listed as a child resource to org.apache.juneau.rest.samples.RootResources which makes it available under the URL /juneau/sample/remoteable.

If you point your browser to that URL, you get a list of available interfaces:

Clicking the hyperlinks on each shows you the list of methods that can be invoked on that service. Note that the IAddressBook link shows that you can only invoke methods defined on that interface, whereas the AddressBook link shows ALL public methods defined on that class. Since AddressBook extends from LinkedList, you may notice familiar collections framework methods listed.

Let's see how we can interact with this interface through nothing more than REST calls to get a better idea on how this works. We'll use the same method call as in the introduction. First, we need to create the serialized form of the arguments:

Object[] args = new Object[] { new CreatePerson("Test Person", AddressBook.toCalendar("Aug 1, 1999"), new CreateAddress("Test street", "Test city", "Test state", 12345, true)) }; String asJson = JsonSerializer.DEFAULT_LAX_READABLE.toString(args); System.err.println(asJson);

That produces the following JSON output:

[ { name: 'Test Person', birthDate: 'Aug 1, 1999', addresses: [ { street: 'Test street', city: 'Test city', state: 'Test state', zip: 12345, isCurrent: true } ] } ]

Note that in this example we're using JSON. However, various other content types can also be used such as XML, URL-Encoding, UON, or HTML. In practice however, JSON will preferred since it is often the most efficient.

Next, we can use a tool such as Poster to make the REST call. Methods are invoked by POSTing the serialized object array to the URI of the interface method. In this case, we want to POST our JSON to /juneau/sample/remoteable/org.apache.juneau.examples.addressbook.IAddressBook/createPerson(org.apache.juneau.examples.addressbook.CreatePerson). Make sure that we specify the Content-Type of the body as text/json. We also want the results to be returned as JSON, so we set the Accept header to text/json as well.

When we execute the POST, we should see the following successful response whose body contains the returned Person bean serialized to JSON:

From there, we could use the following code snippet to reconstruct the response object from JSON:

String response = "output from above"; Person p = JsonParser.DEFAULT.parse(response, Person.class);

If we alter our servlet to allow overloaded GET requests, we can invoke methods using nothing more than a browser...

@RestResource( path="/remoteable", properties={ // Allow us to use method=POST from a browser. @Property(name=REST_allowMethodParam, value="*") } ) public class SampleRemoteableServlet extends RemoteableServlet {

For example, here we call the findPerson(int) method to retrieve a person and get the returned POJO (in this case as HTML since that's what's in the Accept header when calling from a browser):

When specifying the POST body as a &content parameter, the method arguments should be in UON notation. See UonSerializer for more information about this encoding. Usually you can also pass in JSON if you specify &Content-Type=text/json in the URL parameters but passing in unencoded JSON in a URL may not work in all browsers. Therefore, UON is preferred.

4 - @Remoteable Annotation

What if you want fine-tuned control over which methods are exposed in an interface instead of just all public methods? For this, the @Remoteable annotation is provided. It can be applied to individual interface methods to only expose those methods through the remoteable servlet.

For example, to expose only the first 2 methods in our IAddressBook interface...

public interface IAddressBook { @Remoteable Person createPerson(CreatePerson cp) throws Exception; @Remoteable Person findPerson(int id); Address findAddress(int id); Person findPersonWithAddress(int id); }

On the server side, the option to restrict access to only annotated methods is defined through a property:

@RestResource( path="/remoteable", properties={ // Only expose methods annotated with @Remoteable. @Property(name=REMOTEABLE_includeOnlyRemotableMethods, value="true") } ) public class SampleRemoteableServlet extends RemoteableServlet {

The @Remoteable annotation can also be applied to the interface class to expose all public methods defined on that interface.

@Remoteable public interface IAddressBook { Person createPerson(CreatePerson cp) throws Exception; Person findPerson(int id); Address findAddress(int id); Person findPersonWithAddress(int id); }

Skip navigation links

Copyright © 2017 Apache. All rights reserved.