Content-Based Router in SwitchYard using Camel

I have been working on a project which uses SwitchYard (SY) for some time now. Although I am not the main person responsible for coding in SY, I was able to do some modifications in that code base which sort of allowed me to check how SY is used in our project. Since doing those modifications, I have always felt that we are not really using SY’s full potential. For example, I know that SY supports CDI and Camel but I could not see references to these in our codes. I think this is mainly due to our unfamiliarity with SY.

In this post, I would like to try a simple use case that hopefully can show how powerful SY is if it is used with CDI and Camel. To do this, a simple service that can be accessed via HTTP will be created. The service will accept an XML payload that can have 1 of 2 possible formats.

One format will be something like

<request>
<person>
<name></name>
</person>
</request>

The other format will be something like

<request>
<robot>
<model></model>
</robot>
</request>

The service will have to print out “Hello, ${name}” or “Hello, ${model}” depending on the XML format received. This service will give us a chance to implement the Content-based Router Pattern in Camel.

Before we build the service, please note that this post assumes that you know the basics of SY and that you already have SwitchYard 1.0 and Eclipse IDE with SY plugin configured in your machine.

To start building the service, we first have to create a switchyard project in eclipse.

We then have to create a new Java interface with a hello() method. This interface will be the service that will take in incoming requests.

public interface HelloService {
	String hello();
}

After creating the service interface, we can then create a component that will handle service requests. In our case, the component will be a camel implementation and thus should extend from RouteBuilder.

public class CamelServiceRoute extends RouteBuilder {

	/**
	 * The Camel route is configured via this method.  The from endpoint is required to be a SwitchYard service.
	 */
	public void configure() {
		from("switchyard://HelloService")
			.streamCaching()
			.choice()
				.when(xpath("/request/person").booleanResult())
					.to("bean:person")
				.when(xpath("/request/robot").booleanResult())
					.to("bean:robot");			
	}
}

RouteBuilders allow the formulation of routing logic by exposing exchange and message details. In our configure() method above, the urn and message body were used to determine which bean to call.

For routes in SY, the from() method should always point to a SwitchYard endpoint. This means that the scheme should always be "switchyard:". This is shown in line 7 where the from() points to the interface (HelloService) that we previously created. streamCaching() in line 8 just specifies that the stream should be cached so that it can be used again.

The bulk of the routing logic is in the choice(), when() and to() specifications. Depending on the XML, the appropriate beans can be called for further processing. Camel provides the xpath() method to allow the XML payload to be inspected. If the the XML received is request -> person (lines 10-11), the “person” bean will be called . If the XML received is request -> robot (lines 12-13), the “robot” bean will be called.

We will use CDI to define the beans used in the 2 to() calls above.

@Named("person")
public class PersonBean {
	
	public void greet(@XPath("/request/person/name/text()") String name) {
		System.out.println("Hello, " + name);
	}

}

@Named("robot")
public class RobotBean {

	public void greet(@XPath("/request/robot/model/text()") String name) {
		System.out.println("Hello, " + name);
	}
}

Lines 1 and 10 use the java.inject.Named annotation to specify the names of these beans. Since both these classes only specify one public method each, Camel will call the single public method when these beans are called from the RouteBuilder class (For more information about bean binding, kindly check this). In lines 4 and 13, the parameters are annotated with org.apache.camel.language.XPath. @XPath instructs Camel to parse the XML payload and then assign the value to the parameter.

After creating the bean, the last step will be promoting and exposing HelloService by binding it to one of available SY bindings. For our service, we can use the HTTP binding. Bindings can be configured in the visual editor (of Eclipse IDE) or switchyard.xml.

<?xml version="1.0" encoding="UTF-8"?>
<switchyard xmlns="urn:switchyard-config:switchyard:1.0" xmlns:camel="urn:switchyard-component-camel:config:1.0" xmlns:http="urn:switchyard-component-http:config:1.0" xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912" name="sy-camel-hello" targetNamespace="urn:com.sy.camel:hello:1.0">
  <sca:composite name="sy-camel-hello" targetNamespace="urn:com.sy.camel:hello:1.0">
    <sca:component name="CamelServiceRoute">
      <camel:implementation.camel>
        <camel:java class="com.sy.camel.hello.CamelServiceRoute"/>
      </camel:implementation.camel>
      <sca:service name="HelloService">
        <sca:interface.java interface="com.sy.camel.hello.HelloService"/>
      </sca:service>
    </sca:component>
    <sca:service name="HelloService" promote="CamelServiceRoute/HelloService">
      <sca:interface.java interface="com.sy.camel.hello.HelloService"/>
      <http:binding.http name="helloHttp">
        <http:contextPath>http/hello</http:contextPath>
        <http:method>POST</http:method>
      </http:binding.http>
    </sca:service>
  </sca:composite>
</switchyard>

We can now test our application by issuing HTTP Post requests to http://localhost:8080/http/hello. The resulting console view should be as follows:

19:22:06,834 INFO  [stdout] (http-localhost/127.0.0.1:8080-1) Hello, Robocop

19:22:14,796 INFO  [stdout] (http-localhost/127.0.0.1:8080-1) Hello, Pam
Advertisements