Saving WS payload into database in Camel - jpa

I'm absolutely newbie in Apache Camel. I'd like to create a very simple app in which it would accept WS calls and save the payload into a database using JPA.
The payload's structure is quite simple. The root is a Marriage object. It contain some String and int and Date fields, a wife, a husband and a list of children (Person objects).
My goal is to save these data into two tables of a database: MARRIAGE, PERSON.
I've successfully created a jaxws:endpoint in which I listen and respond a dummy response.
I've created the tables and JPA entities.
I don't know how to "connect" the WS implementation with the spring configured JpaTemplate. Should I solve this problem with Camel routing using somehow a #Converter class or #Injet it into the WS implementing class by Spring. I'm confused.
Should I use cxf endpoint instead of jaxws endpoint?

You need to use camle-cxf endpoint if you want to use camel. What I would do is expose the endpoint as a camle-cxf endpoint. Something like this:
<camel-cxf:cxfEndpoint id="listenerEndpoint"
address="http://0.0.0.0:8022/Dummy/services/Dummy"
wsdlURL="wsdl/DummyService.wsdl"
xmlns:tns="http://dummy.com/ws/Dummy"
serviceName="tns:Dummy"
endpointName="tns:DummyService">
<camel-cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
<entry key="dataFormat" value="PAYLOAD"/>
</camel-cxf:properties>
</camel-cxf:cxfEndpoint>
Then I would have a simple Spring bean like this:
<bean id="processor" class="com.dummy.DummyProcessor">
<property name="..." value="..."/> //there goes your data source of jdbc template or whatever...
</bean>
If you want to use JPA just configure all the configuration and inject your entity manager into this bean.
The actual class would look something like this:
public class DummyProcessor {
#Trancational //If you need transaction to be at this level...
public void processRequest(Exchange exchange) {
YourPayloadObject object = exchange.getIn().getBody(YourPayloadObject.class);
//object - is your object from SOAP request, now you can get all the data and store it in the database.
}
}
The camel route would be like this:
<camel:camelContext trace="true" id="camelContext" >
<camel:route id="listenerEndpointRoute">
<camel:from uri="cxf:bean:listenerEndpoint?dataFormat=POJO&synchronous=true" />
<camel:log message="Got message. The expected operation is :: ${headers.operationName}"/>
<camel:choice>
<camel:when>
<camel:simple>${headers.operationName} == 'YourPayloadObject'</camel:simple>
<camel:bean ref="processor" method="processRequest"/>
</camel:when>
</camel:choice>
<camel:log message="Got message before sending to target: ${headers.operationName}"/>
<camel:to uri="cxf:bean:someTargetEndpointOrSomethingElse"/>
<camel:log message="Got message received from target ${headers.operationName}"/>
</camel:route>
</camel:camelContext>
Hope this helps.

Related

How to set couchbase operation timeout in spring data couchbase?

I have a simple spring project which try to retrieve a document from couchbase using spring-data-couchbase. I have configured the config by extending AbstractCouchbaseConfiguration. Everything works perfectly fine.
Since I use couchbase as a cache, now I need to set the operation timeout to a lower value. Anybody can shed some light on how to do it?
According to the docs, the correct answer is wrong. That's not the way it should be done...
When you extend from AbstractCouchbaseConfiguration
Default settings can be customized through the DefaultCouchbaseEnvironment.Builder or through the setting of system properties. Latter ones take always precedence and can be used to override builder settings at runtime too. http://docs.couchbase.com/sdk-api/couchbase-java-client-2.0.0/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.html
For instance, to customize the timeout connection:
#Override
protected CouchbaseEnvironment getEnvironment() {
DefaultCouchbaseEnvironment.builder().connectTimeout(15000);
return super.getEnvironment();
}
There are other options that can be assigned this way.
According the docs (https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html ),
Here is the application.properties :
spring.couchbase.env.timeouts.connect=5000ms # Bucket connections timeouts.
spring.couchbase.env.timeouts.key-value=2500ms # Blocking operations performed on a specific key timeout.
spring.couchbase.env.timeouts.query=7500ms # N1QL query operations timeout.
spring.couchbase.env.timeouts.socket-connect=1000ms # Socket connect connections timeout.
spring.couchbase.env.timeouts.view=7500ms # Regular and geospatial view operations timeout.
To define a timeout for the CouchbaseClient you have to provide it using the ConnectionFactory. Sadly, the current version of spring-data-couchbase doesn't provide a simple way to do that.
The class responsible to create connection factories is ConnectionFactoryBean, and it has a setter for the operations timeout, but I couldn't find anything for #Configuration classes.
Since you are extending AbstractCouchbaseConfiguration, you might want to override couchbaseClient():
public class MyCouchbaseConfiguration extends AbstractCouchbaseConfiguration {
...
private final CouchbaseConnectionFactoryBuilder builder = new CouchbaseConnectionFactoryBuilder();
private CouchbaseConnectionFactory connectionFactory;
...
#Override
#Bean(destroyMethod = "shutdown")
public CouchbaseClient couchbaseClient() throws Exception {
setLoggerProperty(couchbaseLogger());
if(connectionFactory == null){
builder.setOpTimeout(myTimeout);
// Set another parameters.
...
connectionFactory = builder.buildCouchbaseConnection(
bootstrapUris(bootstrapHosts()),
getBucketName(),
getBucketPassword()
);
}
return new CouchbaseClient(connectionFactory);
}
}
Also, you can call directly CouchbaseFactoryBean but it's not a good practice if you are not configuring your application using XML bean definitions.
Here is the XML configuration just in case:
<bean id="couchbase" class="org.springframework.data.couchbase.core.CouchbaseFactoryBean">
<property name="opTimeout" value="1000"/> <!-- 1 sec -->
<property name="bucket" value="myBucket"/>
<property name="password" value="myPassword"/>
<property name="host" value="myHost"/>
</bean>
<couchbase:template id="couchbaseTemplate"/>
For Spring Data Couchbase 2, adding the following property in application.properties did it
spring.couchbase.env.timeouts.connect=20000
I used queryTimeout to set the value of operation timeout as shown below:
CouchbaseEnvironment env = DefaultCouchbaseEnvironment.builder()
.connectTimeout(50000).socketConnectTimeout(50000)
.queryTimeout(1000)
.build();
this will ensure you get the response within 1s.
use this link to get different available options:
https://docs.couchbase.com/java-sdk/2.7/client-settings.html#timeout-options

Spring integration XMPP and Google Cloud Messaging

I'm using the spring integration xmpp module to write a custom implementation of a 3rd party Server connecting to GCM cloud services, as in GCM Cloud Connection Server (XMPP).
So far I've successfully connected to the GCM server, however when I send a message to the server I end up with something like:
<message id="m-1366082849205" to="REGISTRATION_ID">
<body>{"hello":"world"}</body>
</message>
, but I need to send something like this:
<message id="">
<gcm xmlns="google:mobile:data">
{
"to":"REGISTRATION_ID",
"message_id":"m-1366082849205"
"data":
{
"hello":"world",
}
}
</gcm>
</message>
I use the latest SI version, 4.0.4, this is my configuration in the xml:
<int-xmpp:outbound-channel-adapter
id="gcmOutboundAdapter" channel="gcmOutboundNotificationChannel"
xmpp-connection="gcmConnection" auto-startup="true"/>
I'm sending messages with the usual MessageBuilder like this:
Message<String> xmppOutboundMsg = MessageBuilder.withPayload(xmppPayload)
.setHeader(XmppHeaders.TO, REGISTRATION_ID)
.build();
where xmppPayload is a json string.
I need to configure/override the way the xmpp message is composed, what is the best practice to achieve the result? Should I override the class implementing int-xmpp:outbound-channel-adapter with a custom service activator, is there anyway to configure the way the xmpp message is composed?
Thanks for any help.
<gcm xmlns="google:mobile:data"> is a extended content element (see RFC 6120 8.4), which is modelled as PacketExtension in Smack. Do not subclass message, instead create a GCMPacketExtension class and add a instance of it to your message
message.addPacketExtension(gcmPackExtension)
The format of the message is hard-coded in the Smack Message.toXML() method (we use the smack library underneath).
See #Flow's answer.
Then, subclass ChatMessageSendingMessageHandler, overriding handleMessageInternal() - pretty much copy the code and set the extension after the message is created.
The easiest way to configure your custom handler is probably to put it in a chain...
<chain input-channel="gcmOutboundNotificationChannel">
<bean class="foo.MyChatMessageSendingMessageHandler">
<constructor-arg ref="gcmConnection" />
</bean>
</chain>
Or you can wire it up as a top level bean and inject it into a ConsumerEndpointFactoryBean.
Feel free to open a New Feature JIRA Issue and we'll consider adding an extension point to make this a bit easier.
Until we introduce the PackExtension injection, you can overcome it with custom <transformer ref="">, because the <int-xmpp:outbound-channel-adapter> can accept org.jivesoftware.smack.packet.Message as a Message payload:
<transformer ref="toGcmTransformer" output-channel="gcmOutboundNotificationChannel"/>
<int-xmpp:outbound-channel-adapter
id="gcmOutboundAdapter" channel="gcmOutboundNotificationChannel"
xmpp-connection="gcmConnection" auto-startup="true"/>
public class ToGcmTransformer extends AbstractTransformer {
protected Object doTransform(Message<String> message) throws Exception {
String to = message.getHeaders().get(XmppHeaders.TO, String.class);
xmppMessage = new org.jivesoftware.smack.packet.Message(to);
xmppMessage.setBody(message.getPayload());
xmppMessage.addPacketExtension(gcmPackExtension);
return xmppMessage;
}
}
Please, raise an issue about PackExtension support.
-->
<int:chain input-channel="gcmOutboundNotificationChannel">
<!--<int:transformer ref="toGcmTransformer" output-channel="gcmOutboundNotificationChannel"/>-->
<bean class="com.payumoney.cardhash.service.MyMessageSendingMessageHandler">
<constructor-arg ref="gcmConnection" />
</bean>
</int:chain>
<int:transformer id="testTransformer" ref="toGcmTransformer" input-channel="gcmInboundNotificationChannel"
method="doTransform" output-channel="gcmOutboundNotificationChannel"/>
<!--<int:transformer ref="toGcmTransformer" output-channel="gcmOutboundNotificationChannel"/>-->
<int-xmpp:outbound-channel-adapter
id="gcmOutboundAdapter" channel="gcmOutboundNotificationChannel"
xmpp-connection="gcmConnection" auto-startup="true"/>
<int:chain input-channel="gcmInboundNotificationChannel">
<bean class="com.payumoney.cardhash.service.PayumoneyNotificationListeningEndpoint">
<constructor-arg ref="gcmConnection" />
<property name="outputChannel" ref="gcmOutboundNotificationChannel" />
</bean>
</int:chain>
<int-xmpp:inbound-channel-adapter
id="gcmInboundAdapter" channel="gcmInboundNotificationChannel"
xmpp-connection="gcmConnection" extract-payload="true" auto-startup="true" />

XML marshalling in Spring Batch

I have a JdbcCursorItemReader which reads from a database and uses a row mapper to turn the resultset into an object. The requirement is now to marshall the object into XML, which must then be placed onto a message queue.
Three questions:
1. Should a marshaller in Spring Batch be implemented as an ItemProcessor or as an ItemWriter?
2. There is no need to write the XML to a file, but rather simply send it as a string object in a message. What is the best way in Spring Batch to do this?
3. If the marshaller and message dispatcher should both be implemented as separate writers, how is it possible to chain them.
Any example code or links would be very appreciated.
Thanks
I've thought Spring Batch could use a ChannelItemWriter for a while now. If you approached this from the perspective that you write the XML snips to a Spring Integration Channel (that can be then backed by a message queue), you could write the custom ItemWriter that way. That would allow you to approach the item conversion a couple different ways (either in the ItemProcessor or in via a transformer in Spring Integration). If you pursue this approach, I'd recommend contributing the writer back to Spring Batch.
Create a custom writer which will be used when data is written via ItemWriter.
A sample code HERE with some modifications as per your requirement:
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
public class CustomXMLWriter extends org.springframework.batch.item.xml.StaxEventItemWriter<User> {
private Writer myWriter;
/**
* Subclasses can override to customize the writer.
*/
#Override
protected XMLEventWriter createXmlEventWriter(XMLOutputFactory outputFactory, Writer writer)
throws XMLStreamException {
myWriter = new StringWriter();
return outputFactory.createXMLEventWriter(myWriter);
}
#Override
public void close() {
String outputXML = myWriter.toString() + "</" + this.getRootTagName() + ">";
System.out.println(outputXML);
// code here to add the resultant XML string in message queue
super.close();
}
}
In this example you have to mention output resource but the resultant XML file always contains just root tag name.
XML:
<bean id="customXMLItemWriter" class="com.mkyong.CustomXMLWriter">
<property name="resource" value="file:xml/outputs/users.xml" />
<property name="marshaller" ref="userUnmarshaller" />
<property name="rootTagName" value="users" />
</bean>

Putting output from a Spring Batch item writer onto a Spring Integration message channel

What is the best way to take output from a Spring Batch item writer - obviously run as part of a job - and to place it onto a Spring Integration message channel? I understand that one option is to code a Spring Batch listener (extending ItemWriteListener), which would be injected with a message channel, and then to attach the listener to the batch job. Is there another option?
One option to inject to the ItemWriter a Spring Integration <gateway>:
<int:gateway id="itemWriterGateway" service-interface="com.my.proj.MyGateway"/>
class MyItemWriter implements ItemWriter<Foo> {
#Autowired
private MyGateway gateway;
public void write(List<Foo> items) {
for(Foo foo : items) {
this.gateway.send(foo);
}
}
}
Another option to use ItemWriterAdapter:
<bean id="myItemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject" ref="itemWriterGateway"/>
<property name="targetMethod" value="send"/>
</bean>
I won't argue that StepListener may have value for you too. But here there is need to understand what you want to achieve using Spring Integration from Batch Job...
UPDATE
Actually, Listener won't help you, because the ItemWriter doesn't return anything and it encapsulates its logic around underlying resource.
As you say that you are using StaxEventItemWriter and its Resource is some output file, maybe there is a reason to decouple your logic and read that result File using <int-file:inbound-channel-adapter>...
UPDATE 2
) why won't the listener help
Because you need a result of ItemReader, otherwise you have to build your XML one more time in the SI flow from items (ItemWriteListener#afterWrite).
How could the the StaxEventItemWriter output, and the inbound adapter connect to, a resource which is held in memory?
How does your resource is in memory, if StaxEventItemWriter requires that it should exists?
From other side you can share that resource with ItemWriter and some SI endpoint. And right: you can do it from ChunkListener#afterChunk and get your resource from, e.g. jobParameters and send it to the SI Gateway.

How to invoke a method in restful web service using camel-cxf?

I want to invoke a rest service using apache camel. Currently i am using the cxfrs component to configure my endpoint. My route looks like below:
from("cxfrs://http://127.0.0.1:8080/RestServiceApp/?resourceClasses="com.sample.Server.HelloWorld").log("Route Started");
My problem is that i want to invoke a method present in the server class (HelloWorld in my case). Can you please tell me how do i call a particular method?
Camel doesn't call resource class methods. From the documentation on the Camel web-site http://camel.apache.org/cxfrs.html:
This class is used to configure the JAXRS properties ONLY. The methods
will NOT be executed during the routing of messages to the endpoint,
the route itself is responsible for ALL processing instead.
You need to write a custom processing logic, for example as following:
<from uri="cxfrs://http://127.0.0.1:8080/RestServiceApp/?resourceClasses="com.sample.Server.HelloWorld">
<choice>
<when>
<simple>${header.operationName} == 'operation1'</simple>
<to uri="direct:operation1" />
</when>
<when>
<simple>${header.operationName} == 'operation2'</simple>
<to uri="direct:operation2" />
</when>
....
</choice>