Hello.

I have created a library to inject dependencies remotely, I call it Service
Architecture Model (SAM).

Logically it works as a injector that is split over several JVM machines.
The modules used to create the remote injector are standard guice modules.
To build the remote injector some additional information is needed, I call
this a "Service Architecture" because it defined what type of services are
available and which injection keys they provide.

A important feature on SAM is the execution model. When a call is realized
to a remote interface, the execution is delayed if the method returns void
or a nested interface. This means, that a loop that calls a remote object
many times can be translated to only one remote call. Also, it is possible
to pass references between injected services without any direct connection
between the services.

This execution model can change program semantic!!!. I know when this
happens, so a good architecture definition can ensure that semantic is
preserved.

The implementation is provided here:
https://github.com/paweld2/Service-Architecture-Model

I have created a test setup in this project:
https://github.com/paweld2/samExample

In the test project You have the definition of 3 modules:
StoreServiceModule
CourierServiceModule
ShoppingServiceModule

To create a standard injector on one JVM You may need to use this setup for
guice:
ShoppingServiceModule
--install--> CourierServiceModule
--install--> StoreServiceModule

With SAM You can create each module in a remote JVM and create a remote
injector. To do this :

   1. build the project: mvn compile
   2. start Courier service: ./runCourierServer.sh
   3. start Store service: ./runStoreServer.sh
   4. start Shopping service: ./runShoppingServer.sh

You will note how the services will connect to each other.


If someone is interested to talk about SAM, I will attend the JavaOne
conference. It will be a pleasure to show it on live.

Regards.
Pawel Cesar Sanjuan Szklarz.
http://paweld2.eu/ <http://paweld2.eu/#/>

<https://github.com/paweld2/samExample>

PD. Here is detailed explication about how the test project is setup.

Service modules are:
ShoppingServiceModule ->
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/impl/shopping/TestShoppingModule.java
StoreServiceModule ->
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/impl/store/TestStoreServiceModule.java
CourierServiceModule ->
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/impl/courier/TestCourierServiceModule.java

As You can see, this are standard guice modules.

Because SAM creates instances of each module in different machines, a
"Service Architecture" definition is necessary. You can see this
architecture as a definition of the information:

-- I have 3 type of service
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/architecture/SeeTestArchitecture.java

-- Each Service provide a predefined set of keys:
Shopping service ->
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/architecture/service/ShoppingService.java
 Store service ->
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/architecture/service/StoreService.java
Courier service ->
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/architecture/service/CourierService.java

- I have 3 implementations of the service:
https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/impl/TestArchitectureImplementation.java

Note how we point to the original guice modules:

registerImplementation(new
AbstractSamServiceImplementationDefinition<StoreService>(StoreService.class,
TestStoreServiceModule.class) {});

this says: "register a implementation of the service 'StoreService' that is
implemented in module 'TestStoreServiceModule'"

Also, because our ShoppingServiceModule injects keys from the submodules,
we have to declare this in the implementation registration:

registerImplementation(new
AbstractSamServiceImplementationDefinition<ShoppingService>(ShoppingService.class,
TestShoppingModule.class) {
            @Override
            protected void implementationDefinition() {
                withBindingsTo(StoreService.class);
                withBindingsTo(CourierService.class);

            }
        });
this says: "register a implementation of the service 'ShoppingService'
that is implemented in module 'TestShoppingModule' and that use
injection keys from service 'StoreService' and 'CourierService'"

Note that here the implementation refers to a Service definition, and
not to a concrete implementation. This means that we can change the
implementation at any time.



Now lets initialize the modules each in a different JVM and connect
then remotely. This is done as defined in method startService in this
abstract class:

https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/main/AbstractServiceRunner.java


the steps in startService method are as follows:

-- Create the execution environment and load architecture and
implementations definitions.

-- Choose which implementations should run in the server

-- Create a service instance of each implementation

-- Create a configuration that show how to connect the services

-- Expose the configuration as public services by a URL

-- make the tests with the remote injected service



The Store and Courier service don't have deeper dependencies, so the
setup is simple - just expose the service to a URL:

https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/main/CourierServer.java

https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/main/StoreServer.java


Now, the Shopping service needs to connect to a Store and Courier
service. Look at the definition:

https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/main/ShoppingServer.java


In the configuration definition we define the dependencies between the modules:

    @Override
    protected List<InjectionConfigurationElement>
createConfigurations(List<SamServiceInstance> samServiceInstances) {
        ImmutableList.Builder<InjectionConfigurationElement> builder =
ImmutableList.builder();

        InjectionConfigurationElement courier =
InjectionConfigurationBuilder.externalServiceBind(new
SamServiceKey(CourierService.class), courierURL);
        InjectionConfigurationElement store   =
InjectionConfigurationBuilder.externalServiceBind(new
SamServiceKey(StoreService.class), storeURL);

        InjectionConfigurationElement[] externalBindings = {store, courier};
        builder.add(
InjectionConfigurationBuilder.complexInstanceBind(
architectureManager, samServiceInstances.get(0),externalBindings));
        return builder.build();
    }
This says: "Shopping service must connect to a StoreService available
on address 'storeURL' and to a CourierService available on address
'courierURL'.


Now finally lets create the remote injector and execute some code.
This is done in the performTest method of ShoppingServer.

We take the Shopping service instance "localServices.get(0)", create a
transaction that will create the remote injector and pass a
ServiceAction that define how to interact with the remote injector.
The interaction just execute a method of the interface
"ShoppingStoreWithCourierInteraction" :

    @Override
    protected void performTests(List<LiftedServiceConfiguration>
localServices) {

        Key<ShoppingStoreWithCourierInteraction> serviceKey =
Key.get(ShoppingStoreWithCourierInteraction.class);
        Future<Integer> shoppingPrice =
transactionApi.executeServiceAction(localServices.get(0), new
ServiceAction<Integer,
ShoppingStoreWithCourierInteraction>(serviceKey) {
            @Override
            public Integer
executeInteraction(ShoppingStoreWithCourierInteraction service) {
                return service.makeShoping();
            }
        });
        try {
            Integer price = Await.result(shoppingPrice,
Duration.create(6, TimeUnit.SECONDS));
            logger.info("Shopping price is {}", price);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

Look at the implementation of "ShoppingStoreWithCourierInteraction"
provided here:

https://github.com/paweld2/samExample/blob/master/src/main/java/eu/paweld2/sam/example/impl/shopping/TestShoppingStoreWithCourierInteraction.java


References to types provided by the remote service are injected. Also,
note that on the line

boolean addressSetupOk = order.receiveExternalCourierService(address);

we pass a reference from a object coming from the Courier Service to the
Store Service. The big difference here, is that there is no connection
between Courier and Store service - They don't know about each others!.


The final result of the interaction is the price for the realized order.


Note that in this service interaction, the Customer using
ShoppingService can select its own courier service and ask the store
to provide pickup address. This means, that You don't have to provide
Your address to the store, only to You favorite Courier service.

-- 
You received this message because you are subscribed to the Google Groups 
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to