Remoteable service API

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

##### Table of Contents

## 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 {@link org.apache.juneau.rest.client.RestClient#getRemoteableProxy(Class)} method. The remoteable servlet is a specialized subclass of {@link org.apache.juneau.rest.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 = RestClient.create() .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:

- The method must be public.
- The parameter and return types must be serializable and parsable.

## 2 - Client Side

Remoteable interface proxies are retrieved through the existing {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.remoteable.RemoteableServlet} class is an implementation of {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.remoteable.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 {@link org.apache.juneau.uon.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 {@link org.apache.juneau.remoteable.Remoteable @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); }