servicemix-jsr181

ServiceMix jsr181 component is a JBI Service Engine exposing (annotated) POJO as services on the JBI Bus.
It uses xfire internally to perform service invocations and xml marshaling.

Features:

  • no annotations
  • jsr181 annotations
  • commons-attributes annotations
  • aegis binding
  • jaxb2 binding
  • xmlbeans binding
  • wsdl auto generation
  • MTOM / attachments support

Installation

Installing the servicemix-jsr181 component can be done in several ways:

  • drop the installer zip in an hotdeploy directory monitored by ServiceMix
  • using ant tasks

Note that when using ant tasks, the component is not started, you will have to start it manually using ant tasks or a console.

Configuration

Several parameters can be configured using a JMX console.

Configuration
Name Type Description Default
printStackTraceInFaults boolean print the full stack trace in faults when an exception occurs false

Deployment

You can deploy Service Units containing a file named xbean.xml for activating consumer and provider endpoints.

This xml file should respect the given syntax, though this is a spring based xml configuration file from where all beans of class Jsr181Endpoint are retrieved.
See a full example here.  Note that you have to define the http namespace with

<beans xmlns:jsr181="http://servicemix.apache.org/jsr181/1.0">
    ...
</beans>

Any numbers of endpoints can be specified in the xbean.xml file.

As the main purpose is to expose a POJO, you will have to include the needed class files / jars in the service unit and reference them using the following tags in your xbean.xml configuration file:

<classpath>
  <location>.</location>
</classpath>

This will add the content of the location tags (relative to the unzipped service unit) to the classpath. The previous configuration will just add the class files contained in the service unit, as if it is a plain jar.

If you want to embed jars, just use something like

<classpath>
  <location>lib/foo.jar</location>
</classpath>

Endpoint

A few examples:

<jsr181:endpoint endpoint="endpoint1"
                 typeMapping="xmlbeans">
  <jsr181:pojo>
    <bean class="test.EchoService" />
  </jsr181:pojo>
</jsr181:endpoint>
<bean id="myPojo" class="test.EchoService" />
<jsr181:endpoint pojo="#myPojo" />
<jsr181:endpoint pojoClass="test.EchoService2"
                 annotations="none" />
Endpoint attributes
Name Type Description Required
annotations String The annotations used to configure the service. Can be "none", "java5", "jsr181", "commons". If not specified, the annotations type will be discovered by looking at the class. no
endpoint String JBI Endpoint name no (will be auto-generated if not specified)
interfaceName QName Interface QName implemented by the JBI endpoint no (will be auto-generated if not specified)
mtomEnabled boolean Enable MTOM / attachment support no (defaults to false)
pojo Object the instanciated POJO to service requests one of pojo or pojoClass
pojoClass String the class name of the POJO to service requests one of pojo or pojoClass
service QName JBI Service name no (will be auto-generated if not specified)
serviceInterface String the class name of the interface to expose as a service no
typeMapping String Can be "default", "xmlbeans", "jaxb2". Defaults to "default" (Aegis) if no annotations used, else defaults to "jaxb2" no
wsdlResource Spring resource if set, the wsdl will be retrieved from the given Spring resource no
style String The SOAP style to use (document, wrapped, rpc) no (defaults to "wrapped")
validationEnabled boolean Specifies if the payload should automatically be validated. This feature only works for JAXB 2.0 payloads. no

Accessing the JBI bus

The prefered way to access the JBI bus is by retrieving a ComponentContext implementation.
The spring BeanFactory has a parent factory which contains a bean named "context" that you can refer to.

<jsr181:endpoint ...>
  <jsr181:pojo>
    <bean class="xxx">
      <property name="context" ref="context" />
    </bean>
  </jsr181:pojo>
</jsr181:endpoint>

If you want to send a request to another service from your POJO, you can add the following method on your POJO:

private javax.jbi.component.ComponentContext context;

public void setContext(javax.jbi.component.ComponentContext context) {
  this.context = context;
}

You will be able to use the provided DeliveryChannel to send requests.

Note that only sendSync is allowed for active JBI exchanges (but you have to use send for DONE or ERROR status exchanges).

You can also use the client api:

public void myMethod() {
  ServiceMixClient client = new ServiceMixClientFacade(this.context);
  QName service = new QName("http://servicemix.org/cheese/", "receiver");
  EndpointResolver resolver = client.createResolverForService(service);
  client.send(resolver, null, null, "<hello>world</hello>");
}

Lightweight mode

The servicemix-jsr181 component can also be configured in a spring/xbean configuration file, for use in an embedded ServiceMix.
Here is an example of such a configuration:

<sm:activationSpec>
	<sm:component>
      <jsr181:component>
      	<jsr181:endpoints>
      		<jsr181:endpoint pojoClass="test.EchoService2"
                               serviceInterface="test.Echo"
   					 annotations="none" />
      	</jsr181:endpoints>
      </jsr181:component>
  </sm:component>
</sm:activationSpec>
Classpath issues when embedding servicemix-jsr181 component

When using the servicemix.xml configuration file to create jsr181 endpoints, you must include the servicemix-jsr181-xxx.jar in your classpath.
You will find this file inside the component installer (./components/servicemix-jsr181-xxx.zip).
Failing this, an IllegalArgumentException will be thrown with the following message:
Component name: xxxxxx is bound to an object which is not a JBI component, it is of type: javax.xml.namespace.QName
or aanother exception with

Proxies

You can create java proxies for JBI endpoints, provided that they expose a WSDL.

The basic configuration is the following:

<jsr181:proxy id="proxy"
              container="#jbi" 
              interfaceName="test:EchoPortType"
              type="test.Echo" />

You can use it from one of you client bean, or from inside another component, and call the JBI endpoint as a plain Java object.

From a jsr181 Service Unit, it could be used as following:

<jsr181:endpoint serviceInterface="test.Echo">
  <jsr181:pojo>
    <bean class="test.EchoProxy">
      <property name="echo">
        <jsr181:proxy service="test:EchoService" context="#context" type="test.Echo" />
      </property>
    </bean>
  </jsr181:pojo>
</jsr181:endpoint>
private Echo echo;

  public void setEcho(Echo echo) {
    this.echo = echo;
  }

  public void myMethod() {
    String result = echo.echo("world");
    ...
  }

EJBs

Using spring EJB proxies, you can easily expose an existing EJB on the JBI bus.

<jsr181:endpoint annotations="none" service="my:ejb" endpoint="ejb"
                 serviceInterface="foo.bar.Hello">
  <jsr181:pojo>
    <bean class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
      <property name="jndiName" value="my/jndi/path"/>
      <property name="businessInterface" value="foo.bar.Hello"/>
      <property name="jndiTemplate" ref="jndiTemplate"/>
    </bean>
  </jsr181:pojo>
</jsr181:endpoint>

MTOM support

MTOM is a way to handle large amounts of binary data in your services. Unlike attachments, the XML infoset stays the same. MTOM just "optimizes" any base64Binary data you have in your messages. When MTOM is turned on, this base64 data gets sent as a binary attachment saving time and space.

MTOM support can be turned on using:

<jsr181:endpoint mtomEnabled="true" ... />

MTOM is supported for the following classes:

  • DataSource
  • DataHandler
  • byte[]

If you have a bean with the following method:

public String echo(String msg, DataHandler binary) {
  ...
}

you will be able to call it using the following requests:

<echo xmlns:xop='http://www.w3.org/2004/08/xop/include'>
  <msg>hello world</msg>
  <binary>
    <xop:Include href='binary'/>
  </binary>
</echo>

provided that the JBI message contains an attachment named "binary".

Accessing the JBI exchange

Some times, while processing the incoming request, you need to access the underlying JBI exchange.
It can be done using the following code snippet:

public String echo(String msg, DataHandler binary) {
  ...
  MessageExchange exchange = org.apache.servicemix.jsr181.JBIContext.getMessageExchange();
  ...
}