Wednesday, August 15, 2007

Using Embedded ServiceMix Client

One of the options for integrating ServiceMix with BPM solutions such as jBPM is to simply instantiate an 'on-the-fly' Servicemix client, then pass an XML message directly to a ServiceMix component that has been configured to receive it. An example of where this approach maybe warranted is if you had several ESB-type tasks that you wanted to perform in sequence, such as logging jBPM results to a database, then sending them via email to a predefined audience. Obviously, these are tasks that ServiceMix handles with aplomb, since it has prebuilt components for such things.

In the example presented in this article, I'm going to provide an example of calling ServiceMix File and XSTL components. The SM file component example is InOnly (i.e., fire-and-forget), whereas the XSLT example is asynchronous (block-and-wait for response).

We'll first look at the setup.xml file, which sets up the component definitions and ServiceMix JBI container.

setup.xml

<beans xmlns:sm="http://servicemix.apache.org/config/1.0"
xmlns:foo="http://servicemix.org/cheese"
xmlns:eip="http://servicemix.apache.org/eip/1.0">

<!-- the JBI container -->
<sm:container id="jbi" embedded="true">
<sm:activationSpecs>

<!-- returns dummy xml for testing -->
<sm:activationSpec componentName="test" service="foo:test" >
<sm:component>
<bean class="org.apache.servicemix.components.xslt.XsltComponent">
<property name="xsltResource" value="classpath:resources/test.xslt"/>
</bean>
</sm:component>
</sm:activationSpec>

<sm:activationSpec componentName="receiver"
service="foo:receiver">
<sm:component>
<bean
class="org.apache.servicemix.tck.ReceiverComponent" />

</sm:component>
</sm:activationSpec>

<sm:activationSpec componentName="fileSender"
service="foo:fileSender">
<sm:component>
<bean
class="org.apache.servicemix.components.file.FileWriter">

<property name="directory" value="/tmp/test" />

<property name="marshaler">
<bean
class="org.apache.servicemix.components.util.DefaultFileMarshaler">
<property name="fileName">
<bean
class="org.apache.servicemix.expression.JaxenStringXPathExpression">
<constructor-arg
value="'test.xml'" />
</bean>
</property>

</bean>
</property>
</bean>
</sm:component>
</sm:activationSpec>

</sm:activationSpecs>
</sm:container>


<bean id="client"
class="org.apache.servicemix.client.DefaultServiceMixClient">
<constructor-arg ref="jbi" />
</bean>
</beans>

In the above, you can see that I've setup two components -- the XSLT service named "test" and the file writer component named "fileSender". In the case of the XSLT component, the test.xslt file associated with it simply returns the following xml:

<hello>
<text>Hi!</text>
</hello>

In the case of the fileSender, it is configured to write a file to /tmp/test called "test.xml" with the contents of what was passed when it was invoked.

Next, we'll look at a jUnit test that illustrates how a ServiceMix instance can be instantiated and run through a Java client.

ServiceMixClientTest.java
package company.cop.check.test;
import javax.xml.namespace.QName;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.servicemix.client.ServiceMixClient;
import org.apache.servicemix.jbi.container.SpringJBIContainer;
import org.apache.servicemix.jbi.jaxp.SourceTransformer;
import org.apache.servicemix.jbi.resolver.EndpointResolver;
import org.apache.servicemix.tck.Receiver;
import org.apache.xbean.spring.context.ClassPathXmlApplicationContext;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.w3c.dom.Document;

/**
* @version $Revision: 1.2 $
*/
public class ServiceMixClientTest extends TestCase {

private static final transient Log LOG = LogFactory.getLog(ServiceMixClientTest.class);

protected AbstractXmlApplicationContext context;
protected ServiceMixClient client;
protected Receiver receiver;

protected SourceTransformer transformer = new SourceTransformer();

public void testCallingComponent() throws Exception {
QName service = new QName("http://servicemix.org/cheese", "fileSender");
EndpointResolver resolver = client.createResolverForService(service);
client.send(resolver, null, null, "world");

}

public void testXslt() throws Exception {
QName service = new QName("http://servicemix.org/cheese", "test");
EndpointResolver resolver = client.createResolverForService(service);
Document response = (Document) client.request(resolver, null, null, "world");
assertEquals(response.getChildNodes().item(0).getTextContent(), "Hi!");
System.out.println("Response: " + response.getChildNodes().item(0).getTextContent());
}

protected void setUp() throws Exception {
context = createBeanFactory();
client = getClient();
SpringJBIContainer jbi = (SpringJBIContainer) getBean("jbi");
}

protected ServiceMixClient getClient() throws Exception {
return (ServiceMixClient) getBean("client");
}

protected void tearDown() throws Exception {
super.tearDown();

if (context != null) {
context.close();
}
}

protected Object getBean(String name) {
Object answer = context.getBean(name);
assertNotNull("Could not find object in Spring for key: " + name, answer);
return answer;
}

protected AbstractXmlApplicationContext createBeanFactory() {
return new ClassPathXmlApplicationContext("resources/setup.xml");
}
}

The setup() method, called automatically by jUnit prior to each test, establishes the JBI ServiceMix container/server. Then, each of the test methods are called. The testCallingComponent() method sends a message to the "fileSender" ServiceMix component/service. It identifies the target service by QName, which is the namespace and name provided the service in the setup.xml file. The client.send() method sends a message using the provided parameters, notice the last of which is the XML packet. When run, this should result in a file called "test.xml" being written. The contents should be what was passed in the client.send operation, namely "world".

The testXslt() method is used to invoke the test XSLT service defined in setup.xml. Unlike the fileWriter, this is a synchronous call, as we anticipate the transformed XML being sent back to us. To do so, the client.request() method is used, and it will return back the transformed XML in the form of a DOM Document. That Document object is then traversed to printout the text contents of the "text" node. This should be "Hi!", as defined in the XSLT document.

Final Thoughts

As you can see, it's pretty straightforward to invoke ServiceMix within a standalone Java client, without requiring ServiceMix to be running as a stand-alone instance. Doing this, however, does impose some performance penalties, as instantiating the JBI container does consume some overhead.

We'll explore calling remote services from a running ServiceMix instance in another article. Another intriguing option would be to use a stateful EJB to maintain a ServiceMix context, but there could be some tricky concurrency issues with that approach.

Add to Technorati Favorites

Monday, August 13, 2007

SOA Design Principles

I've just completed an excellent book titled "SOA: Principles of Service Design" by Thomas Erl. Unfortunately, SOA has become such a hackneyed expression that its virtually lost all meaning. However, Erl does a nice job a describing what it really means to create a service-based architecture. These are some of the design principles he identified:

Service Loose Coupling

Coupling refers to a connection or relationship between two things. A measure of coupling is thus comparable to a level of dependency. In SOA, the objective is to reduce ("loosening") dependencies between the service contract, it's implementation, and its service consumers. This makes a service more reusable and flexible. A well-defined meta-data contract, such as provided through a web service WSDL, defines the interfaces with service.

Service Abstraction

Sometimes known as "data hiding", this principle emphasizes the need to hide as much as possible the underlying details of a service. This works in conjunction with Service Loose Coupling to help make services more reusable. This also permits changes to be made to the service without unduly impacting clients. In essence, the service should be a "black-box" that performs its operations without any understanding of the mechanics behind it.

Service Reusability

A service should not be specific to a single functional context. Instead, it should be "agnostic", and support numerous usage scenarios, without having functional dependencies. One challenge with service reusability is that it often promotes more granular-type services, because a service with narrower focus is often more easily reused. This can have performance implications and must be weighed carefully.

Service Autonomy

A service should exercise a high level of control over its underlying runtime environment. In other words, it should managed within its own environment, and be able to support a high degree of reliability and performance. The client should no visibility or concern over the environment in which the service is run.

Service Statelessness

Services should minimize resource consumption by deferring the management of state information, when necessary. A service that maintains state also likely reduces reusability, as it then is likely imposing a certain process or functional dependency on the service.

Service Discoverability


Services should be supplemented with communication meta-data by which they can be effectively discovered and interpreted. In other words, services should be defined within a common registry that enables users to readily understand its purpose and requirements. Obviously, this need becomes more pronounced as an organization grows and spawns multiple development groups or organizations.

Service Composability

Services should be effective composition participants, regardless of the size and complexity of the composition. For instance, this assumes that it will include comprehensive exception handling features and support multiple run-time scenarios. It also requires that the service be capable of handling multiple, concurrent transactions and be highly performing. Using a consistent invocation pattern is also instrumental in this.

Add to Technorati Favorites

Friday, August 3, 2007

What's wrong with BPEL?

Recently, I've spent a lot of time examining various BPEL-based products. They include Intalio, Cape Clear and Active Endpoints. After evaluating each of them, I've come to the conclusion that WS-BPEL is often not the right fit. If you're a pure-play Java shop, a solution such as JBoss jBPM maybe a much better fit (a future blog will go into some detail on how to use jBPM). The issues I see with BPEL are:

* BPEL, in an effort to support all vendors/platforms, only works with SOAP-based services. This means that every time an invocation is required, say to call an FTP or email adapter, the adapter/service must be exposed as a web service. That imposes a lot of extra overhead on the developer, and for internal services, seems onerous. Most of us don't live in a world where everything is automatically exposed as web services (I'm a big proponent of SOAP/Web services, but also realistic).

* Passing of data between activities/steps within a BPEL orchestration must be done using XML and WSDL constructs. Complex data transformations and assignments must frequently be performed using XSL, and developers who aren't well versed in the nuances of XSD schemas (in particular, namespaces) can become frustrated. Java and .NET developers must often wonder why they can't just pass a data class objects between the nodes.

* BPEL, by it's definition, is a closed standard. If a product is 100% BPEL compliant, extending it by supporting direct API java calls, for instance, automatically makes your orchestrations non-compliant, thus losing the benefits of the vendor neutrality and compatibility BPEL promises. Then what's the point of using BPEL?

* BPEL-based products are often app-server centric, meaning they have to be deployed an hosted. This often poses challenges from a testing standpoint, as developers must deploy their orchestration prior to being able to test it. Again, this impacts developer productivity in a negative way.

* BPEL introduces often very confusing nomenclature that must be learned. Concepts like "Partner Links", "Partner Link Types", "Partner Roles", "Port Types" and "Correlation Sets" are often not intuitive, and require a deep understanding of all things WSDL.

* Some products, such as Intalio, recognize the difficulties in modeling things natively in BPEL, so they use alternative, presumably more intuitive, solutions such as BPML. While that address some of the developer productivity concerns mentioned, they then rely on translation/code generation to output BPEL code. That "black-box" translation steps seems like a source of ongoing problems (the developer must, for example, try and determine why a diagram in BPML got translated into such-and-such BPEL code). Seems like a headache waiting to happen.



Add to Technorati Favorites