Local Service Proxies

Richard Hightower edited this page Apr 28, 2016 · 4 revisions

Tutorials

__

Docs

Getting Started

Basics

Concepts

REST

Callbacks and Reactor

Event Bus

Advanced

Integration

QBit case studies

QBit 2 Roadmap

Related Projects

Clone this wiki locally

Local service proxies are documented as follows.

To communicate with a service in the same JVM you need a service bundle or a service queue.

A service bundle is a collection of services that are addressable via a URL.

Let's say a service has the following methods in its interface:

    public interface SomeInterface {
        void method1();

        void method2(String hi, int amount);


        String method3(String hi, int amount);
    }

To invoke method1, you could create a simple proxy as follows:

        final SomeInterface myServiceProxy = serviceBundle.createLocalProxy(SomeInterface.class, "myService");
        myServiceProxy.method1();

        myServiceProxy.method2("hi", 5);

        myServiceProxy.flushSends();

The createLocalProxy is the key here. That creates a delegate that will enqueue messages to the service.

The bundle was created beforehand as follows:

        final Factory factory  = QBit.factory();
        final ServiceBundle bundle = factory.createBundle("/root");


        bundle.addService(myService);

        @RequestMapping ("myService")
        public class MyServiceClass implements SomeInterface {
            @Override
            public void method1() {
                   //do something
            }

            @Override
            public void method2(String hi, int amount) {
                //do something
            }

            @Override
            public String method3(String hi, int amount) {
                return "Hi " + hi + " " + amount;
            }
        }

There is also bean factories and such for Spring and Guice integration. A real service would be defined in its own class.

As you can see method 1 and method 2 are inherently asycn as they return void. No return type is expected so they are fire and forget calls.

You have options for handling a call to method3 which has a return type. You can use a Handler or you can use the response queue.

Here is an example of using the response queue.


        myServiceProxy.method3("hi", 5);
        bundle.flushSends(); //send all outstanding calls

        //Get the return from the responseReceiveQueue
        final Response<Object> objectResponse = responseReceiveQueue.pollWait();
        ok = "Hi hi 5".equals(objectResponse.body()) || die();

The responseObject can either be an exception/error or the actual response, i.e., the return value from the async call.

Alternatively you can use a handler as follows:

First we create the handler:

        ok = false;
        Handler<String> returnHandler = new Handler<String>() {
            @Override
            public void handle(String returnValue) {

                puts("We got", returnValue);

                ok = "Hihi 5".equals(returnValue);

            }
        };

We define a client interface for dealing with this service that is different than the actual service interface. Note the use of a service interface is completely optional. You can use Java classes without interfaces, but the client proxies must use an interface.

Client interface

    public static interface MyServiceInterfaceForClient {

        void method1();

        void method2(String hi, int amount);

        void method3(Handler<String> handler, String hi, int amount);
    }
        final MyServiceInterfaceForClient myServiceProxy = bundle.createLocalProxy(
                MyServiceInterfaceForClient.class,
                "myService");


        bundle.addService(myService);

Since you will no longer be polling the response queue, but using handlers, then you need to tell the service bundle to poll the response queue for you and call you handler for you.

        bundle.startReturnHandlerProcessor();

Then when you invoke the method, you pass the Handler.

        myServiceProxy.method3(returnHandler, "hi", 5);
        bundle.flushSends();

You can handle exceptions as well.

Given an interface like this for the client:


    public interface ClientInterfaceThrowsException {

        public void methodThrowsExceptionIf5(Handler<String> arg, String hi, int amount);
    }

We want to call as service like this:

        @RequestMapping ("myService")
        public class MyServiceClass  {


            public String methodThrowsExceptionIf5(String hi, int amount) {

                if (amount == 5) {
                    return die(String.class, "Hi " + hi + " " + amount);
                } else {
                    return "Hi " + hi + " " + amount;
                }
            }
        }

We create the service and register it with a service bundle.



        MyServiceClass myService = new MyServiceClass();


        final Factory factory  = QBit.factory();
        final ServiceBundle bundle = factory.createBundle("/root");


        bundle.addService(myService);
        bundle.startReturnHandlerProcessor();

Then we create a local service proxy.

        final ClientInterfaceThrowsException myServiceProxy = bundle.createLocalProxy(
                ClientInterfaceThrowsException.class,
                "myService");

Next we create two handlers. One handler is for Exceptions, and on handler is for return types. This way you can reuse your exception handler many times.

First Handler to handle return types (this gets called async)

        ok = false;
        Handler<String> returnHandler = new Handler<String>() {
            @Override
            public void handle(String returnValue) {

                puts("We got", returnValue);

                ok = "Hi hi 6".equals(returnValue);

            }
        };

Then a handler to handle exceptions.

        Handler<Throwable> exceptionHandler = new Handler<Throwable>() {
            @Override
            public void handle(Throwable exception) {

                puts("We got", exception.getMessage());

                ok = "Hi hi 5".equals(exception.getMessage());

            }
        };

Now we combine them:

        final Handler<String> handler = Handlers.handler(returnHandler, exceptionHandler);

Now we call the proxy with non exception case.

        myServiceProxy.methodThrowsExceptionIf5(handler, "hi", 6);
        bundle.flushSends();
        Sys.sleep(1000);

        ok = ok || die();

        ok = false;

Now we call it, forcing it to throw an exception.

        myServiceProxy.methodThrowsExceptionIf5(handler, "hi", 5);
        bundle.flushSends();
        Sys.sleep(1000);
        ok = ok || die();