As you said before, these things are not Switchyard-specific problems but general SOA/integration problems. Unfortuanately it seems, that there is no general clearly defined language to adress the topics. But I think we are on the way to find our common language . So I start with a few (informal) definitions/conventions that I try to follow in my postings to facilitate that process.
- Component: Piece of software that provides Services. Often this is referred as service, but here its the implementation of a service
- Service: A set of functions that are provided by a component.
- Service-Interface: Actual description how you can access the service-functions in software (WSDL-Interface, Java-Interface,...)
- Refernce: A set of functions that are needed by the Component from somewhere else.
- Reference-Interface: Actual description how the reference-functions are used by the component (WSDL-Interface, Java-Interface,...)
The concept of separate Reference-Interfaces is great. I haven't seen this before, but I always thougt something like that is important.
So, when a component A calls another component B (A->B), the reference of component A (Reference-A) has to be linked to the service of component B(Service-B). This is easy when Reference-Interface-A equals Service-Interface-B, which is the only way I have seen until Switchyard (and I'm getting exited). In Switchyard it is possible that Reference-Interface-A differs Service-Interface-B:
{code:xml}
<sca:component name="MyServiceBuilder">
<camel:implementation.camel>
<camel:java class="org.example.syApp2.MyServiceBuilder"/>
</camel:implementation.camel>
<sca:service name="MyService">
<sca:interface.java interface="org.example.syApp2.MyService"/>
</sca:service>
<sca:reference name="OrderService">
<interface.esb inputType="java:org.example.syApp2.Bean1"/>
</sca:reference>
</sca:component>
<sca:component name="OrderServiceBean">
<bean:implementation.bean class="org.example.syApp2.OrderServiceBean"/>
<sca:service name="OrderService">
<sca:interface.java interface="org.example.syApp2.OrderService"/>
</sca:service>
</sca:component>
{code:xml}
Here the Reference-Interface for OrderService "esb" and the Service-Interface is "java". in order to link the components you need an adapter.
- adapter: Concept of transforming one Interface to another Interface. (Maybe one should say to link a Reference-Interface to a Service-Interface.) Also referred as contract-transformation above.
- Adapter: Concrete implementation for an adapter in Switchyard. Like org.switchyard.transform.Transformer and suggested above or other internal adapters.
- transformer: Concept of transforming messages (arguments of service-functions) from one type to another.
- Transformer: Switchyard implementation of a transformer
Switchyard of course has adapters, since it uses different interfaces for References an Services. And after all this formalism, I can ask my questions or rephrase the questions above:
- When is it theoretically possible and usefull to construct an adapter?
- How does Switchyard construct its adapters?
- When its theoretically possible to construct an adapter, but Switchyard can't right now, how could Switchyard support it in future?
I try to give some some answers (corrections welcome).
To Question 1 (Component-A calls Component-B):
Identity: The easiest case is when Refence-Interface-A equals Service-Interface-B. The Identity-Adapter. Trivial.
Embedding: When we have Java-Interfaces and Interface-B extends Interface-A like:
Reference-Interface-B:
void process(Order o);
Service-Interface-A:
void process(Order o);
void check(Order o);
We have a embedding of the Reference-Interface to Service-Interface. Methods and arguments are mapped by the Identity-Function.
1-1-Method: When both interfaces have only one method and the same arguments.
Reference-Interface-B:
void process(Order o);
Service-Interface-A:
void orderIt(Order o);
Methods are mapped in the only possible way, arguments are mapped by Identity. This reminds on the documenttion for Camel-Endpoints:
"operation-name : name of the service operation to be invoked. This is only used on references and is optional if the target service only has a single operation."
Camel Reference-Interfaces are created as Embedding to Service-Interfaces when you give the operation-name and can use a 1-1-Method adapter otherwise.
Transformer-based: When the method-names form an embedding and there is a Transformation that mapps the arguments
Reference-Interface-B:
void process(Order o);
Service-Interface-A:
void process(Order2 o);
void check(Order2 o);
Transformer
Order2 orderToOrder2(Order o)
When process(Order o) is used by component A, the Adapter calls process(orderToOrder2(o)) on component B. We have the Identity on Methods and need an injective transformation from Order to Order2. If there are return-values, one needs a projection on the returning class (this should be investigated in more detail).
General Embedding: From the examples above, I think one can that you need the following for an adapter:
- An (injective?) map from the Reference-methods to the Service-methods
- An (injective?) map from the arguments of the Reference-methods to the arguments of the Service-methods
- A (projection?) map for the return-values.
As usecase I have the replacement of one Service by a newer Version of the same Service in mind. The adapter has to make sure that the functionallity stays the same for the old interface. This might not be the only usecase and assertions for the mapping might change.
I tried to adress this general form in my example.
Reference-Interface Ordering:
void process(Order o);
void check(Order2 o);
Service-Interface Ordering2:
void orderIt(Order2 o);
void check(Order2 o);
void cancel(Order2 o);
where "orderIt" is just a new name for the "process". A possible Adapter-function is "Ordering adapt(final Ordering2 to){...}" in OrderAdapter.java
To Question 2
As far as I have seen Switchyard creates adapters by Embedding of method names together with Transformer-based adapters. The Switchyard-Dokumentation is a little unclear so maybe we can look at a Camel example from the dokumentation?
{code:xml}
<sca:component name="CamelComponent">
<implementation.camel xmlns="urn:switchyard-component-camel:config:1.0">
<route xmlns="http://camel.apache.org/schema/spring" id="Camel Test Route">
<log message="ItemId [${body}]"/>
<to uri="switchyard://WarehouseService?operationName=hasItem"/>
<log message="Title Name [${body}]"/>
</route>
</implementation.camel>
<sca:service name="OrderService">
<sca:interface.java interface="org.switchyard.component.camel.deploy.support.OrderService"/>
</sca:service>
<sca:reference name="WarehouseService">
<sca:interface.java interface="org.switchyard.component.camel.deploy.support.WarehouseService"/>
</sca:reference>
</sca:component>
{code:xml}
We have a Reference-Interface WarehouseService which is adressed from switchyard://WarehouseService?operationName=hasItem. In the documentation we have:
switchyard://[service-name]?operationName=[operation-name]
service-name : name of the SwitchYard service
operation-name : name of the service operation to be invoked. This is only used on references and is optional if the target service only has a single operation.
Am I right to assume that with my definitions/convention in case of a reference this should be:
service-name : name of the SwitchYard Reference
operation-name : name of the operation in the Reference-Interface to be invoked. This is only used on references and is optional if the target Reference only has a single operation.
So in the fist step the camel-endpoint is mapped to the Reference-Interface and then the Reference-Interface is mapped to the Service-Interface?
To Question 3
The General Embedding can't be done right now with switchyard. I thought maybe one could register Adapters like Transformers. Something like that:
{code:xml}
<adapters>
<adapter.java class="at.objectbay.tests.swe.OrderAdapter"
from="interface.java:at.objectbay.tests.swe.Ordering"
to="interface.java:at.objectbay.tests.swe.Ordering2"/>
</adapters>
{code:xml}
If Switchyard has to map a Reference-Interface to a Service-Interface it just uses the registered Adapter. If none is found, it tries to build an Adapter like it does now.
(And now its time to eat something )