This page contains list of steps and tips for implementation new routes.
Look at How to start new project? for starting new project ... Look at Development tips and Best practices. Adhere to conventions how to write the code. |
You can use RouteBeanNameGenerator for automatic bean names generation. |
Synchronous route is route where source system waits for response. This type of requests are not stored in OpenHub evidence, there are mentions in log files only. Possible error is immediately propagated to source system.
Steps for implementation are identical to those mentioned in previous chapter.
How to implement asynchronous routes is described in another page.
Checklist of features which can be used during route implementation:
Use SoupUI integration test for calling real web service to verify that everything works as expected. Unit test is good to have but it doesn't catch all possible problems. |
Example routes are in examples module. There are two basic examples - synchronous and asynchronous hello world service implemented as web services.
WSDL is available on /ws/hello.wsdl and endpoint on the URL /ws/hello/v1
See How to implement web services? for more details about implementing web services. |
Synchronous implementation is in class org.openhubframework.openhub.modules.in.hello.SyncHelloRoute
If comes web service request with element name syncHelloRequest and namespace http://openhubframework.org/ws/HelloService-v1) then this route is "activated" and request starts processing. Response is immediately send back to the source system.
@CamelConfiguration(value = SyncHelloRoute.ROUTE_BEAN) @Profile(ExampleProperties.EXAMPLE_PROFILE) public class SyncHelloRoute extends AbstractBasicRoute { static final String ROUTE_BEAN = "syncHelloRouteBean"; private static final String OPERATION_NAME = "syncHello"; private static final String ROUTE_ID_SYNC_HELLO = getRouteId(ServiceEnum.HELLO, OPERATION_NAME); static final String HELLO_SERVICE_NS = "http://openhubframework.org/ws/HelloService-v1"; @Override protected void doConfigure() throws Exception { from(getInWsUri(new QName(HELLO_SERVICE_NS, "syncHelloRequest"))) .routeId(ROUTE_ID_SYNC_HELLO) .policy(RouteConstants.WS_AUTH_POLICY) .to("throttling:sync:" + OPERATION_NAME) .unmarshal(jaxb(SyncHelloRequest.class)) .log(LoggingLevel.DEBUG, "Calling hello service with name: ${body.name}") .bean(this, "composeGreeting") .marshal(jaxb(SyncHelloResponse.class)); } @Handler public SyncHelloResponse composeGreeting(@Body SyncHelloRequest req) { Assert.notNull(req, "req must not be null"); String greeting = "Hello " + req.getName(); SyncHelloResponse res = new SyncHelloResponse(); res.setGreeting(greeting); return res; } } |
Asynchronous implementation is in class org.openhubframework.openhub.modules.in.hello.AsyncHelloRoute.
If comes web service request with element name asyncHelloRequest and namespace http://openhubframework.org/ws/HelloService-v1) then this route is "activated" and request starts processing. Firstly synchronous part is proceeded - input request is validated and saved into queue (database) and synchronous response is immediately send back to the source system with information that OpenHub accepted input request and ensures request processing. When request processing finishes then asynchronous confirmation with processing result is send to source system.
@CamelConfiguration(value = AsyncHelloRoute.ROUTE_BEAN) @Profile(ExampleProperties.EXAMPLE_PROFILE) public class AsyncHelloRoute extends AbstractBasicRoute { private static final Logger LOG = LoggerFactory.getLogger(AsyncHelloRoute.class); static final String ROUTE_BEAN = "asyncHelloRouteBean"; private static final String OPERATION_NAME = "asyncHello"; static final String ROUTE_ID_ASYNC_IN = getInRouteId(ServiceEnum.HELLO, OPERATION_NAME); static final String ROUTE_ID_ASYNC_OUT = getOutRouteId(ServiceEnum.HELLO, OPERATION_NAME); static final String URI_ASYNC_HELLO_OUT = "direct:" + ROUTE_ID_ASYNC_OUT; private static final String URI_PRINT_GREETING = "direct:printGreeting"; @Override protected void doConfigure() throws Exception { // asyncHello - input asynch message createRouteForAsyncHelloRouteIn(); // asyncHello - process delivery to external systems createRouteForAsyncHelloRouteOut(); } /** * Route for asynchronous <strong>asyncHello</strong> input operation. * <p/> * Prerequisite: none * <p/> * Output: {@link AsyncHelloResponse} */ private void createRouteForAsyncHelloRouteIn() { Namespaces ns = new Namespaces("h", SyncHelloRoute.HELLO_SERVICE_NS); // note: mandatory parameters are set already in XSD, this validation is extra XPathValidator validator = new XPathValidator("/h:asyncHelloRequest", ns, "h:name"); // note: only shows using but without any influence in this case Expression nameExpr = xpath("/h:asyncHelloRequest/h:name").namespaces(ns).stringResult(); AsynchRouteBuilder.newInstance(ServiceEnum.HELLO, OPERATION_NAME, getInWsUri(new QName(SyncHelloRoute.HELLO_SERVICE_NS, "asyncHelloRequest")), new AsynchResponseProcessor() { @Override protected Object setCallbackResponse(CallbackResponse callbackResponse) { AsyncHelloResponse res = new AsyncHelloResponse(); res.setConfirmAsyncHello(callbackResponse); return res; } }, jaxb(AsyncHelloResponse.class)) .withValidator(validator) .withObjectIdExpr(nameExpr) .build(this); } /** * Route for <strong>asyncHello</strong> operation - process delivery to external systems. * <p/> * Prerequisite: none */ private void createRouteForAsyncHelloRouteOut() { from(URI_ASYNC_HELLO_OUT) .routeId(ROUTE_ID_ASYNC_OUT) // xml -> AsyncHelloRequest .unmarshal(jaxb(AsyncHelloRequest.class)) .to("extcall:message:" + URI_PRINT_GREETING); from(URI_PRINT_GREETING) .bean(this, "printGreeting"); } @Handler public void printGreeting(@Body AsyncHelloRequest req) { Assert.notNull(req, "req must not be null"); String greeting = "Hello " + req.getName(); LOG.debug("Greeting: " + greeting); } } |
Each route implementation must have corresponding unit tests, at least one successful scenario.
See How to write unit test? for more details about writing unit tests. |
There are corresponding unit tests to each route - SyncHelloRouteTest resp. AsyncHelloRouteTest.
@ActiveRoutes(classes = SyncHelloRoute.class) public class SyncHelloRouteTest extends AbstractExampleModuleTest { @Produce(uri = TestWsUriBuilder.URI_WS_IN + "syncHelloRequest") private ProducerTemplate producer; @EndpointInject(uri = "mock:test") private MockEndpoint mock; /** * Checking successful calling. */ @Test public void testSyncHelloRoute() throws Exception { String xml = "<syncHelloRequest xmlns=\"http://openhubframework.org/ws/HelloService-v1\">" + " <name>Mr. Parker</name>" + "</syncHelloRequest>"; String output = producer.requestBody((Object)xml, String.class); SyncHelloResponse res = Tools.unmarshalFromXml(output, SyncHelloResponse.class); assertThat(res, notNullValue()); assertThat(res.getGreeting(), is("Hello Mr. Parker")); } } |
@ActiveRoutes(classes = AsyncHelloRoute.class) public class AsyncHelloRouteTest extends AbstractExampleModulesDbTest { private static final String REQ_XML = "<asyncHelloRequest xmlns=\"http://openhubframework.org/ws/HelloService-v1\">" + " <name>Mr. Parker</name>" + "</asyncHelloRequest>"; @Produce(uri = TestWsUriBuilder.URI_WS_IN + "asyncHelloRequest") private ProducerTemplate producer; @EndpointInject(uri = "mock:test") private MockEndpoint mock; @Test public void testRouteIn_responseOK() throws Exception { getCamelContext().getRouteDefinition(AsyncHelloRoute.ROUTE_ID_ASYNC_IN) .adviceWith(getCamelContext(), new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { TestUtils.replaceToAsynch(this); weaveAddLast().to("mock:test"); } }); mock.expectedMessageCount(1); // action String output = producer.requestBody((Object) REQ_XML, String.class); // verify mock.assertIsSatisfied(); // verify OK response AsyncHelloResponse res = Tools.unmarshalFromXml(output, AsyncHelloResponse.class); assertThat(res.getConfirmAsyncHello().getStatus(), is(ConfirmationTypes.OK)); // check message header Message inMsg = mock.getExchanges().get(0).getIn(); assertThat((String) inMsg.getHeader(AsynchConstants.OBJECT_ID_HEADER), is("Mr. Parker")); } @Test public void testRouteOut() throws Exception { getCamelContext().getRouteDefinition(AsyncHelloRoute.ROUTE_ID_ASYNC_OUT) .adviceWith(getCamelContext(), new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { weaveAddLast().to("mock:test"); } }); mock.expectedMessageCount(1); // action org.openhubframework.openhub.api.entity.Message msg = createAndSaveMessage(ExternalSystemTestEnum.CRM, ServiceTestEnum.ACCOUNT, "testOp", "payload"); producer.sendBodyAndHeader(AsyncHelloRoute.URI_ASYNC_HELLO_OUT, REQ_XML, AsynchConstants.MSG_HEADER, msg); // verify mock.assertIsSatisfied(); // nothing to verify } } |