Encapsulate backend specifics with custom Camel component that wraps existing component - apache-camel

We have different backends (mostly SOAP so I will focus on this) each with its own specifics (security, default timeout, error handling etc).
On the other hand we have a lot of integration services that orchestrate calls to these backends. For example "service A" first calls "backend 1" and then "backend 2" with SOAP requests. Finally it uses the responses of both backends to create a response.
There are a lot of services like "service A", each of them has an individual orchestration. Each of them is an individual project with its own repository and is deployed as a small Spring-Boot unit.
Let's say that 4 out of 10 services call "backend 1". They do not necessarily call the same service of "backend 1", but they all need to implement the specifics of this same backend. For example send special headers, a specific security token, use a retry strategy for this backend etc.
To avoid that every integration service repeats the specifics for "backend 1" or "backend 2", I would like to somehow encapsulate these specifics.
Because we use Apache Camel, I assume that I could do this with a custom Camel component. But because components typically integrate a new type of backend, I don't know if I should do this or if it is a bad idea. My backend types (for example SOAP) already exist as components, I would just wrap them with specifics.
For example to "encapsulate" a SOAP backend I could create a custom component that delegates to the CXF component to create concrete service endpoints with the common specifics of this backend (headers, security, etc).
In the Camel route the endpoints would perhaps look somehow like this
MyBackend://serviceUri?option1=value1
and under the hood it would create
cxf://serviceUri?backendSpecific1=valueA&backendSpecific2=valueB&option1=value1
Or are there other, better suited extension points to use for this use case?

There is a way to achieve this to some level. Its called endpoint reuse. Let me take you on how I typically tackle a problem like this.
I normally declare all my SOAP and JMS enpoints in my camel-context.xml. I then create xml files for each route and reference the endpoints defined in my camel-context.xml from there.
Here is a short example of my camel-context.xml note the routeContextRef element:
<bp:blueprint
xmlns="http://camel.apache.org/schema/blueprint"
xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd
http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0 http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd
http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.2.0 http://aries.apache.org/schemas/blueprint-ext/blueprint-ext-1.2.xsd">
<camelContext id="foo-bar" trace="{{camel.context.trace}}" autoStartup="true" useMDCLogging="true" useBreadcrumb="true" depends-on="jms" threadNamePattern="#camelId#-thread-#counter#" managementNamePattern="#camelId#-#version#" allowUseOriginalMessage="false" streamCache="false">
<routeContextRef ref="foo-bar-routes"/>
<endpoint id="jms-gateway-v1" uri="jms:queue:Foo.Bar.System.Evt.1.0.T" />
<endpoint id="rs-gateway-v1" uri="cxfrs://bean://rs-gateway-impl-v1" />
</camelContext>
I then create a foo-bar-routes.xml and I reference the endpoint from there like so:
<bp:blueprint xmlns="http://camel.apache.org/schema/blueprint"
xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ers="http://www.fooxml.com/events/resourceitem/service/v1"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<routeContext id="foo-bar-routes">
<!--
This route receives an event from the JMS queue
-->
<route id="jms-gateway-route-v1">
<from ref="jms-gateway-v1" />
<to ref="rs-gateway-v1" />
</route>
</bp:blueprint>
So in short I wrap my SOAP and JMS components in endpoint definitions and then reuse these in routes that are defined in the own xml document. I could add a second route in another XML file and reuse the endpoint in that route just add, rinse and repeat.
I know the JavaDSL has similar functionality so it is achievable as well using the JavaDSL as well.

Related

ActiveMQ embedded bridge to Camel JMS bridge

I have an old application which handle JMS messages with ActiveMQ 5.8.0 and some JNDI remote topic connected to this ActiveMQ.
I have a connector like that :
<bean class="org.apache.activemq.network.jms.JmsConnector">
<property name="outboundTopicConnectionFactory" ref="jmsConnectionFactoryTo" />
<property name="outboundClientId" value="${remote.clientId}" />
<property name="jndiOutboundTemplate" ref="jndiTemplateTo" />
<property name="preferJndiDestinationLookup" value="true" />
<property name="inboundTopicBridges">
<list>
<bean class="org.apache.activemq.network.jms.InboundTopicBridge">
<property name="inboundTopicName" value="${remote.topic.to}"/>
<property name="localTopicName" value="${local.topic.to}"/>
<property name="consumerName" value="${remote.consumer.name}"/>
<property name="selector" value="${remote.selector}"/>
</bean>
</list>
</property>
</bean>
It works great, but now, for some technical reasons (strict JMS 1.1), I need to use "ConnectionFactory" instead of "TopicConnectionFactory".
With the actual configuration, I'm stuck because ActiveMQ seems to use "TopicConnectionFactory" instead of "ConnectionFactory", and my new class "MyConnectionFactoryImpl" implements "ConnectionFactory" now :
nested exception is org.springframework.beans.ConversionNotSupportedException:
Failed to convert property value of type 'com.webmethods.jms.impl.MyConnectionFactoryImpl'
to required type 'javax.jms.TopicConnectionFactory'
for property 'outboundTopicConnectionFactory';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type [com.webmethods.jms.impl.MyConnectionFactoryImpl]
to required type [javax.jms.TopicConnectionFactory] for property 'outboundTopicConnectionFactory':
no matching editors or conversion strategy found
In "org.apache.activemq.network.jms.JmsConnector" class, it use everywhere "TopicConnectionFactory", which is not recommended anymore in JMS 1.1.
EDIT :
According to #Justin Bertram, I need to use Camel instead of ActiveMQ embedded bridge. But I can't find any example of XML configuration which I can use to replace my actual two beans JMSConnector. Which is the simple way to do this keeping my XML config files ?
As the documentation for the JMS to JMS Bridge (i.e. org.apache.activemq.network.jms.JmsConnector) states:
ActiveMQ provides bridging functionality to other JMS providers that implement the JMS 1.0.2 and above specification.
In other words, the whole goal of the JMS to JMS Bridge is to use the JMS 1.0.2 interface(s). Changing it so that it only used JMS 1.1 would defeat the purpose.
The documentation also states that you should use Camel instead of the JMS to JMS Bridge:
Warning, try Camel first!
Note that we recommend you look at using Apache Camel for bridging ActiveMQ to or from any message broker (or indeed any other technology, protocol or middleware) as its much easier to:
keep things flexible; its very easy to map different queue/topic to one or more queues or topics on the other provider
perform content based routing, filtering and other Enterprise Integration Patterns
allows you to work with any technology, protocol or middleware, not just JMS providers
Therefore I recommend you use Camel instead of org.apache.activemq.network.jms.JmsConnector.
I would think that having your code return a TopicConnectionFactory would be the simplest solution. Even the JMS 2.0 specification provides the TopicConnectionFactory. No matter what version of ActiveMQ you are using, you certainly have the option of using the TopicConnectionFactory in your code and providing that to your bridge.
Note that the Camel route:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="mqseries:Foo.Bar"/>
<to uri="activemq:Cheese"/>
</route>
</camelContext>
has no error handling. For example, if the 'to' endpoint is down, this route will read from the 'from' endpoint and just throw the messages on the floor. Furthermore, if the 'to' component is not configured to use a caching/pooling connection factory, then a new JMS connection will be created for each message sent. This has poor performance and can result in many sockets in the TIME_WAIT state. Bottom line - beware trivial Camel routes.

How I can create a camel globalObject?

I need a global Object for all routes, processes and Components. In this global Object I would save configuration parameters. But I don't know how and where I can set a global Object, and how I can read it in my own process and my own components.
I create the camel Context in Spring and have a RouteBuilder to build my routes.
Thank you
If you want to setup your route, then you may use PropertyPlaceholderConfigurer, see here:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<camelContext xmlns="http://activemq.apache.org/camel/schema/spring">
<route>
<from uri="activemq:${someQueueName}"/>
<to uri="mock:results"/>
</route>
</camelContext>
Alternatively, you may use ApplicationContextRegistry that allows you to look up beans in the Spring ApplicationContext. This implementation is automatically used when you’re using Camel in a Spring environment, see here. E.g., access the registry as follows:
String myValue = exchange.getContext().getRegistry().lookupByNameAndType("myKey", String.class);

how to configure Apache Camel Quartz endpoint to use JDBCJobStore

I have configured Quartz endpoint for the scheduling requirement. However currently in my route configuration, trigger information is hard coded in the XML configuration file. As per the requirement, trigger information needs to come from DB.
<camel:route>
<camel:from uri="quartz://commandActions/MSFI?cron=15+17+13+?+*+MON-SUN+*" />
<camel:bean ref="userGateway" method="generateCommand" />
<camel:to uri="wmq:SU.SCHEDULER" />
</camel:route>
Quartz documentation says Jobs and triggers can be stored in database and are accessed using JDBCJobStore. Is it possible to configure Camel Quartz endpoint to use JDBCJobStore? I tried to find out an example but couldn't find. If someone has implemented this before, kindly share an example.
Thanks,
Vaibhav
Yeah see the quartz documentation how to configure it to use a jdbc job store. You can do this using a quartz.properties file, which you can tell Camel to use.
See the Camel side part here: http://camel.apache.org/quartz at the section Configuring quartz.properties file

Can you tell what kind of database is the WSDL using?

Can you tell what kind of database is the WSDL using? The reason for asking is the 3rd party only give me a url and and didnt tell me anything about the database. I requested them to give me the document to explain a bit but they never did.
I am new in this area and after some research, but i cannot sure what database (oracle?sql?xml?)
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="AccountService" targetNamespace="http:// local host/web/" xmlns:ns1="http://client.local host/web//" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://local host/web/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xsd:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://local host/web/" xmlns:tns="local host/web/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="ArrayOfAccount">
<xsd:sequence>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="Account" nillable="true" type="tns:Account"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Account">
<xsd:sequence>
<xsd:element minOccurs="0" name="currentbalance" nillable="true" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
The second question is, can i use .asp call out the element from the database directly?
The last question is, WSDL is just a set of policy (rules) telling the server to generate the result?
No, it is not possible to determine the internal implementation of a web service solely from it's WSDL.
For your questions:
You can't tell what sort of database, or even anything about the back-end of a Web service from the WSDL. It is specifically a descriptive document that shows what types of methods are available. It could be a Java based Web service, .Net, or whatever, and point to what ever database the Web service developer decided to use. The point of having a service model is that a client doesn't really need to know anything about the inner workings of a service it uses, just what functions you can call, and what sort of data it accepts, which is what the WSDL provides.
If you are hosting the Web service, then you would have the database and directions to set up the connection string. In that case, your ASP application "could" connect to the database, but it shouldn't, since it would defeat the purpose of having the service in the first place. It's better to update the service to include whatever functions you need. From your request though, it sounds like you have no control over the Web service, so you won't have any connection directly to the database. If the database were on the same network as your service, and there were no firewall policies blocking database connections, you could theoretically connect to the database the service uses. But, again, that would defeat the purpose of having the service.
WSDL is just an XML file that describes what the service can do. It doesn't actually do any work. The work is handled when you make an Http request with something like WebClient or HttpClient to a URL passing in the needed information. So, the WSDL may show in XML:
Method URL:
http://someURL/UpdateAccounts
Accepts Parameters:
Account
Then in your service you would code to post a request to
http://someURL/UpdateAccounts
passing in Account converted to XML.
A WSDL is a document describing a webservice, listing all the web methods, and their paramaters. It helps you build a client that can consume the webservice. It describes how you can communicate with the webservice. The webservice itself could be implemented anyway the creator wants, with or without a DB. They aren't related at all.
WSDL describes only the interface, like the function definition. Not the internals about the service. So no, you can not tell.

Multiple Jira instances on a single Tomcat 6 server?

I have a feeling this is a stupid question but I can't find the answer anywhere...
I need to deploy 2 Jira instances on asingle Tomcat server, I can't figure out how to pass in the jira.home property
The documentation says I need to:-
Add a web context property called 'jira.home' — this property is set in different files depending on your application server. For example, for Tomcat (and therefore for JIRA Standalone), you will need to configure the server.xml file. For other application servers you may need to configure the web.xml file, or set 'Context parameter' options on the deployment UI of the application server, etc. Note that If you have specified a JIRA home in jira-application.properties (ie. the recommended method), it will override your web context property.
I was hoping something like this would work.
<Context jira.home="d:/jira/data" path="" docBase="D:\Jira\atlassian-jira-enterprise-4.1\dist-tomcat\tomcat-6\atlassian-jira-4.1.war" debug="0">
<Resource name="jdbc/JiraDS" auth="Container" type="javax.sql.DataSource"
username="sa"
password="*****"
driverClassName="net.sourceforge.jtds.jdbc.Driver"
url="jdbc:jtds:sqlserver://*****:1433/jira41_519;user=****;password=****"
/>
<Resource name="UserTransaction" auth="Container" type="javax.transaction.UserTransaction"
factory="org.objectweb.jotm.UserTransactionFactory" jotm.timeout="60"/>
<Manager pathname=""/>
</Context>
Any ideas??
ah ha!
This is what I was looking for!
<Parameter name="jira.home" value="d:/jira/dataResearch" override="false"/>

Resources