Monday, January 28, 2008

ServiceMix: Behavior Configuration

Okay, we have the skeleton of our SOA use case implementation in place but we have to configure it to make it actually work. First let's recognize that we need to do two types of configuration: behavior and deployment.

By behavior I mean configuring the JBI components in our SUs to perform their tasks. This is done in the xbean.xml file located in the /src/main/resources of each of our SUs.

By deployment I mean packaging the various SUs and their dependencies into a SA that can be deployed into our JBI container (ServiceMix). This configuration is done in the Maven pom.xml file located at the root of each SU and the SA.

We'll look at the behavior, xbean.xml, configuration first. We'll address each SU in the order they appear in our use case. So review the use case in the last post then we'll get started.

JBI
But before we do lets think about JBI. Java Business Integration is a product of JSR-208 and, as mentioned before, is the standard upon which ServiceMix is based. The standard describes a Normalized Message Router(NMR) over which components communicate via message exchanges. The NMR is, essentially, our bus. Four methods of calling a service, called Service Invocations, are required by the JBI standard. They are defined by Message Exchange Patterns(MEPs) . Service Invocations map to MEPs as follows:

Service Invocation
Message Exchange Pattern(MEP)
One-way
In-Only
Reliable One-way
Robust In-Only
Request-Response
In-Out
Request Optional-Response
In Optional-Out

Implementations are free to extend from this to implement additional MEPs but we're dealing with basics now so we'll leave it at that. Why is it important to point these out now? Well in the next step we'll define a "defaultMep".

MyJmsQueueTest

This is the entry and exit point for our process. It receives a message from the outside world and eventually sends the response back. ServiceMix comes with ActiveMq embedded so we'll use that as the host for the JMS queue. Our JMS request/response scenario will be implemented using temporary queues.

Now down to brass tacks. Our MyJmsQueueTest service is a Binding Component(BC) Service Unit(SU). The configuration XML file is located at:
myJmsSu/src/main/resources/xbean.xml
Here are the contents:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:jms="http://servicemix.apache.org/jms/1.0"
xmlns:test="http://test"
xmlns:amq="http://activemq.org/config/1.0">
<jms:endpoint service="test:MyJmsQueueTest"
endpoint="jmsQueue"
targetService="test:MyStaticRoutingSlipService"
targetEndpoint="myStaticRoutingSlipSu"
role="consumer"
destinationStyle="queue"
jmsProviderDestinationName="myJmsQueueTest"
defaultMep="http://www.w3.org/2004/08/wsdl/in-out"
defaultOperation="test:onMessage"
connectionFactory="#connectionFactory"></jms:endpoint>
<amq:connectionFactory id="connectionFactory" brokerURL="tcp://localhost:61616" />
</beans>
First note the name space definitions:
Now let's look at each setting:
  • endpoint
    • service="test:MyJmsQueueTest"
      This identifies our service on the NMR.
    • endpoint="jmsQueue"
      This identifies the endpoint within the service. Why an endpoint? Isn't service enough? Well if we decide latter that we want to also expose our "service" via SOAP we just add a new endpoint.
    • targetService="test:MyStaticRoutingSlipService"
      Our SU in this case is a Binding Component(BC). It is used to expose a service on our NMR to the outside world via a standard protocol. Here the "test:MyJmsQueueTest" service is being used to bind our "test:MyStaticRoutingSlipService" to a JMS queue.
    • targetEndpoint="myStaticRoutingSlipSu"
      Remember a service can have more than one endpoint. This is the specific endpoint within our "test:MyStaticRoutingSlipService" that we want to pass our message to.
    • role="consumer"
      Here we define our BC as a "consumer" of the inbound message. In other words it will listen to the defined JMS transport.
    • destinationStyle="queue"
      We define the JMS destination type as a queue (as opposed to a topic).
    • jmsProviderDestinationName="myJmsQueueTest"
      This is the name of the JMS queue our BC will listen to. Remember that we are using the version of ActiveMq that is embedded in ServiceMix as the JMS provider. In ActiveMq there is no need to configure the queue in advance of using it. If it does not exist ActiveMq will simply create it so no further configuration on the JMS provider side is reqired. This is not true of all JMS implementations (BEA for instance).
    • defaultMep="http://www.w3.org/2004/08/wsdl/in-out"
      Here we are declaring that we want to use the Request-Response Service Invocation (In-Out MEP) defined in the JBI standard. What will happen is ServiceMix will send our eventual response to the replyTo destination, defined in the header of the incoming JMS message, using the correlation id that is also defined in the header of the incoming JMS message.
    • defaultOperation="test:onMessage"
      This is the method to call on the target service (our POJOs).
    • connectionFactory="#connectionFactory"
      The connection factory, defined below, that contains JMS provider specific configuration details. Note the "#". This is XBean syntax to reference an inner bean definition.
  • connectionFactory
    • id="connectionFactory"
      The name referred to by the "connectionFactory" attribute of the endpoint element above. It identifies an inner bean definition with in this XBean
    • brokerURL="tcp://localhost:61616"
      The URL for the broker. Since we are using the embedded ActiveMq that's all we need.
MyStaticRoutingSlipService
So a client puts a message on a queue and the MyJmsQueueTest BC SU picks it up and routes it to our first SE SU the MyStaticRoutingSlipService. This service is going to want to route our message to a service, wait for a response and then send that response on to the next service on our "routing slip". Our routing slip is defined in:
myStaticRoutingSlip/src/main/resources/xbean.xml
Its contents:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:eip="http://servicemix.apache.org/eip/1.0"
xmlns:test="http://test">
<eip:static-routing-slip service="test:MyStaticRoutingSlipService" endpoint="myStaticRoutingSlipSu">
<eip:targets>
<eip:exchange-target service="test:MyS1PojoService" endpoint="myS1PojoSu"/>
<eip:exchange-target service="test:MyS2PojoService" endpoint="myS2PojoSu"/>
<eip:exchange-target service="test:MyS3PojoService" endpoint="myS3PojoSu"/>
</eip:targets>
</eip:static-routing-slip>
</beans>
First the name space definitions:
  • xmlns:jms="http://servicemix.apache.org/eip/1.0"
    This identifies the entries for the servicemix-eip component. This is the component ships with ServiceMix to support the standard set of Enterprise Integration Patterns. There appears to be a move toward deprecating this implementation in favor of the one provided by the Apache Camel effort. Camel, however, does not yet support the Routing Slip pattern so we'll use the servicemix.eip implementation for now.
  • xmlns:test="http://test"
    Entries relevant to our particular use case implementation.
Now the meat:
  • static-routing-slip
    The following entries simply identify our service and its only endpoint. This is the same as what was done for the MyJmsQueueTest BC SU.
    • service="test:MyStaticRoutingSlipService"
    • endpoint="myStaticRoutingSlipSu"
  • targets
    Okay, here's the interesting part. What follows is a list of service endpoints. This is our "static-routing-slip". Each service will be called in the order in which they appear in the list with the output of the previous service.
    • exchange-target service="test:MyS1PojoService" endpoint="myS1PojoSu"
    • exchange-target service="test:MyS2PojoService" endpoint="myS2PojoSu"
    • exchange-target service="test:MyS3PojoService" endpoint="myS3PojoSu"
MyS1PojoService, MyS2PojoService, MyS3PojoService
Now we need to define an SE SU for each of the services in our routing slip. In our contrived example they are simple POJOs that merely add something to the incoming message to prove that they did something. We have a separate definition for each. Since they are practically identical we'll look at the configurations then discuss the entries. (Yes, there are other ways but, again, basics first then we'll get fancy).

MyS1PojoService Configuration
XBean configuration location:
myS1Pojo/src/main/resources/xbean.xml
Contents:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:bean="http://servicemix.apache.org/bean/1.0"
xmlns:test="http://test">
<bean:endpoint service="test:MyS1PojoService" endpoint="myS1PojoSu" bean="#myBean"/>
<bean id="myBean" class="com.chariotsolutions.MyBean"/>
</beans>
MyS2PojoService Configuration
XBean configuration location:
myS2Pojo/src/main/resources/xbean.xml
Contents:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:bean="http://servicemix.apache.org/bean/1.0"
xmlns:test="http://test">
<bean:endpoint service="test:MyS2PojoService" endpoint="myS2PojoSu" bean="#myBean"/>
<bean id="myBean" class="com.chariotsolutions.MyBean"/>
</beans>
MyS3PojoService Configuration
XBean configuration location:
myS3Pojo/src/main/resources/xbean.xml
Contents:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:bean="http://servicemix.apache.org/bean/1.0"
xmlns:test="http://test">
<bean:endpoint service="test:MyS3PojoService" endpoint="myS3PojoSu" bean="#myBean"/>
<bean id="myBean" class="com.chariotsolutions.MyBean"/>
</beans>
Now lets look at the details. First the name spaces:
  • xmlns:bean="http://servicemix.apache.org/bean/1.0"
    These entries are for the servicemix-bean component. This component ships with ServiceMix to provide for the integration of POJOs into the JBI container. We can think of them as Message Driven Pojos (MDP) similar, in concept at least, to J2EE's Message Driven Beans(MDB).
    Note: The servicemix-jsr181 SE also provides POJO support by exposing the POJO as a web service (CXF).
  • xmlns:test="http://test"
    Entries relevant to our particular use case implementation.
Now the meat:
  • endpoint
    • service="test:MySnPojoService" endpoint="mySnPojoSu"
      These should be familiar by now. They identify our service and its only endpoint.
    • bean="#myBean"
      Here we use XBean syntax (remember # from the MyJmsQueueTest SU?) to reference an inner bean definition that describes our POJO.
  • bean
    This is the inner bean definition that describes our POJO.
    • id="myBean"
      A name to reference it by.
    • class="com.chariotsolutions.MyBean"
      The fully qualified name of our POJO.
Okay, we have finished configuring the behavior of our BC (MyJmsQueueTest) and SE (MyStaticRoutingSlipService, MyS1PojoService, MyS2PojoService and MyS3PojoService) SUs. They will have to be packaged in a Service Assembly(SA) before we can deploy. Before we get to that, however, there is one more thing to consider. Our POJO, com.chariotsolutions.MyBean, where did that come from? We'll look at that next.

3 comments:

Anonymous said...

It's one of the best example I never seen to start with ServiceMix.

Nicolas

skottk said...

Outstanding series of posts on ServiceMix.

Anonymous said...

I found this site using [url=http://google.com]google.com[/url] And i want to thank you for your work. You have done really very good site. Great work, great site! Thank you!

Sorry for offtopic