How to save soap play to file in apache camel? - apache-camel

Tried to save soap request (jaxb payload) to file as xml payload in apache camel, via below code, but it failed.
Anyone know how to do it?
Any hints will be more than welcome!
Code:
public class CamelRouteBuilder extends RouteBuilder {
private static final String SOAP_ENDPOINT_URI = "cxf://http://localhost:{{soapEndpointPort}}/soap" +
"?serviceClass=org.yw.springbootcamelesb.soap.CreateFileService";
#Override
public void configure() throws Exception {
from(SOAP_ENDPOINT_URI).process(new CreateFileProcessor()).to("file:target/reports");;
}
}
exception I got is like below
pringbootcamelesb.soap.FileCreationStatus on:
Message[ID-YandeMBP-1536989276152-2-2]. Caused by: No type converter
available to convert from type:
org.yw.springbootcamelesb.soap.FileCreationStatus to the required
type: java.io.InputStream with value
org.yw.springbootcamelesb.soap.FileCreationStatus#75ec37c5.
Exchange[ID-YandeMBP-1536989276152-2-1]. Caused by:
[org.apache.camel.NoTypeConversionAvailableException - No type
converter available to convert from type:
org.yw.springbootcamelesb.soap.FileCreationStatus to the required
type: java.io.InputStream with value
org.yw.springbootcamelesb.soap.FileCreationStatus#75ec37c5]
at org.apache.camel.impl.MessageSupport.getMandatoryBody(MessageSupport.java:117)
~[camel-core-2.22.0.jar:2.22.0]
at org.apache.camel.component.file.FileOperations.storeFile(FileOperations.java:333)
~[camel-core-2.22.0.jar:2.22.0]
... 40 common frames omitted

it works with below code
JaxbDataFormat xmlDataFormat = new JaxbDataFormat();
JAXBContext con = JAXBContext.newInstance(FileCreationStatus.class);
xmlDataFormat.setContext(con);
from(SOAP_ENDPOINT_URI).process(new CreateFileProcessor())
.marshal(xmlDataFormat).to("file:target/output").unmarshal(xmlDataFormat);

Related

Apache Camel route to pojo

I have an Account Instance that is not in the format I need. I am trying Camel
but just not getting
the basic understanding. I have the un-formatted Instance successfully coming in and I display it in the log.
I have tried to extract the data from the body using a processor and directly to a POJO.
The POJO uses fixed length definitions. Which is needed do to passing data to a Mainframe DB. Please point me in a direction.
Got message:12 xZZ200ZZZZZZZ
public class AccountAddRoute extends RouteBuilder{
public void configure() throws Exception {
DataFormat bindy = new BindyFixedLengthDataFormat(AccountAddData.class);
from("direct:input")
.log(" In AccountAddRoute ")
.marshal(bindy)
.log("Got message:${body}");
.to(???????_)
My Apologies. Not a good explanation of what I am trying to do.
I have An Account POJO that is being built thru FORM. I need to save
it in 2 different formats. Incoming just the way it is and in a
fixed format to be sent to Mainframe. This is what I get from the form
Code1=12 x , Code2=200
I initiate rout by sending body of form in accountAddData Instance
producerTemplate.sendBody("direct:input", accountAddData);
public class AccountAddRoute extends RouteBuilder{
public void configure() throws Exception {
DataFormat bindy = new BindyFixedLengthDataFormat(AccountAddData.class);
from("direct:input")
.log(" In AccountAddRoute ")
.marshal(bindy)
.log("Got message:${body}");
.to(???????_)
Just for Testing purposes my Account Bindy Pojo is below
#DataField(pos =1, length=15, paddingChar='Z', trim=true, align="L")
private String Code1;
#DataField(pos =2, length=10, paddingChar='Z', align="L")
private String Code2;
When I log Body, I get >>
Got message:12 xZZ200ZZZZZZZ
However, my question is how do I create the new Instance in the
BINDY Format SO I can use it
==========
I did add processor and when I do display of body in processor >>
log.info(" In MyProcessor = " + exchange.getIn().getBody());
In MyProcessor = [B#3338b2b9
I was expecting to have the getter/setters available after getBody for the
accoountAddData instance. But obviously I don’t have a good understanding
of camel
exchange.getIn().getBody().????
Are you trying to unmarshal the incoming data? If thats the case, then you would need to use the following code.
from("direct:input")
.log(" In AccountAddRoute ")
.unmarshal(new BindyFixedLengthDataFormat(AccountAddData.class))
.log("Got message:${body}");
.to(???????_)

Unit testing with Apache Camel

I want to test below camel route. All the example which i find online has route starting with file, where as in my case i have a spring bean method which is getting called every few minutes and finally message is transformed and moved to jms as well as audit directory.
I am clue less on write test for this route.
All i have currently in my test case is
Mockito.when(tradeService.searchTransaction()).thenReturn(dataWithSingleTransaction);
from("quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*")
.bean(TradeService.class)
.marshal()
.jacksonxml(true)
.to("jms:queue:out-test")
.to("file:data/test/audit")
.end();
Testing with Apache Camel and Spring-Boot is really easy.
Just do the following (the example below is an abstract example just to give you a hint how you can do it):
Write a Testclass
Use the Spring-Boot Annotations to configure the test class.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
#RunWith(SpringRunner.class)
public class MyRouteTest {
#EndpointInject(uri = "{{sourceEndpoint}}")
private ProducerTemplate sourceEndpoint;
....
public void test() {
// send your body to the endpoint. See other provided methods too.
sourceEndpoint.sendBody([your input]);
}
}
In the src/test/application.properties:
Configure your Camel-Endpoints like the source and the target:
sourceEndpoint=direct:myTestSource
Hints:
It's good not to hardwire your start-Endpoint in the route directly when using spring-boot but to use the application.properties. That way it is easier to mock your endpoints for unit tests because you can change to the direct-Component without changing your source code.
This means instead of:
from("quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*")
you should write:
from("{{sourceEndpoint}}")
and configure the sourceEndpoint in your application.properties:
sourceEndpoint=quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*
That way you are also able to use your route for different situations.
Documentation
A good documentation about how to test with spring-boot can be found here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
For Apache Camel: http://camel.apache.org/testing.html
#the hand of NOD Thanks for your hints, i was going into completely wrong direction. After reading your answer i was able to write the basic test and from this i think i can take it forward.
Appreciate your time, however i see that based on my route it should drop an XML file to audit directory which is not happening.
Look like intermediate steps are also getting mocked, without I specifying anything.
InterceptSendToMockEndpointStrategy - Adviced endpoint [xslt://trans.xslt] with mock endpoint [mock:xslt:trans.xslt]
INFO o.a.c.i.InterceptSendToMockEndpointStrategy - Adviced endpoint [file://test/data/audit/?fileName=%24%7Bheader.outFileName%7D] with mock endpoint [mock:file:test/data/audit/]
INFO o.a.camel.spring.SpringCamelContext - StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
TradePublisherRoute.java
#Override
public void configure() throws Exception {
logger.info("TradePublisherRoute.configure() : trade-publisher started configuring camel route.");
from("{{trade-publisher.sourceEndpoint}}")
.doTry()
.bean(tradeService)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String dateStr = Constant.dateFormatForFileName.format(new Date());
logger.info("this is getting executed : " + dateStr);
exchange.setProperty(Constant.KEY_INCOMING_XML_FILE_NAME, "REQ-" + dateStr + Constant.AUDIT_FILE_EXTENSION);
exchange.setProperty(Constant.KEY_OUTGOING_XML_FILE_NAME, "RESP-" + dateStr + Constant.AUDIT_FILE_EXTENSION);
}
})
.marshal()
.jacksonxml(true)
.wireTap("{{trade-publisher.requestAuditDir}}" + "${header.inFileName}")
.to("{{trade-publisher.xsltFile}}")
.to("{{trade-publisher.outboundQueue}}")
.to("{{trade-publisher.responseAuditDir}}" + "${header.outFileName}")
.bean(txnService, "markSuccess")
.endDoTry()
.doCatch(Exception.class)
.bean(txnService, "markFailure")
.log(LoggingLevel.ERROR, "EXCEPTION: ${exception.stacktrace}")
.end();
TradePublisherRouteTest.java
#ActiveProfiles("test")
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest(classes = TradePublisherApplication.class)
#MockEndpoints
public class TradePublisherRouteTest {
#EndpointInject(uri = "{{trade-publisher.outboundQueue}}")
private MockEndpoint mockQueue;
#EndpointInject(uri = "{{trade-publisher.sourceEndpoint}}")
private ProducerTemplate producerTemplate;
#MockBean
TradeService tradeService;
private List<Transaction> transactions = new ArrayList<>();
#BeforeClass
public static void beforeClass() {
}
#Before
public void before() throws Exception {
Transaction txn = new Transaction("TEST001", "C001", "100", "JPM", new BigDecimal(100.50), new Date(), new Date(), 1000, "P");
transactions.add(txn);
}
#Test
public void testRouteConfiguration() throws Exception {
Mockito.when(tradeService.searchTransaction()).thenReturn(new Data(transactions));
producerTemplate.sendBody(transactions);
mockQueue.expectedMessageCount(1);
mockQueue.assertIsSatisfied(2000);
}
Please correct me if i am doing something wrong!

How to use Apache Camel exchange messages?

I am newcomer in Apache Camel. Please have a look to my code bellow:
I have a service which exposed as cxf webservice:
interface CxfService{
public OutputType hello(InputType input);
}
This is my route:
from("cxf:/test?serviceClass=" + CxfService.class.getName())
.to("log:cxfLog1")
.recipientList(simple("direct:${header.operationName}"));
from("direct:hello")
.process(new Processor(){
public void process(Exchange exchange) throws Exception {
InputType file = exchange.getIn().getBody(InputType.class);
exchange.getOut().setBody(new OutputType());
}
});
The code works as expected, it consume InputType and produce OutputType.
I want to borrow my body to do another stuffs, so i rewrite that like this:
from("cxf:/test?serviceClass=" + CxfService.class.getName())
.to("log:cxfLog1")
.recipientList(simple("direct:${header.operationName}"));
from("direct:hello")
.process(new Processor(){
public void process(Exchange exchange) throws Exception {
InputType file = exchange.getIn().getBody(InputType.class);
exchange.getOut().setHeader("header.temporary", new OutputType());
}
})
.to("some endpoint")
.setBody(simple("${header.temporary}"));
This webservice consume InputType and produce nothing. What wrong with that?
In your second piece of code, when setting the header.temporary, you should change two things:
setHeader("temporary", new OutputType()) - the 'header' prefix isn't
needed - you're addressing headers directly via the method call.
Use getIn() instead of getOut(). The input will get copied to the
output. You may want to do some research into the procedure for
Camel building the out message for details - I'm not 100% sure of
this one.
Change
exchange.getOut().setHeader("header.temporary", new OutputType());
To
exchange.getIn().setHeader("temporary"), new OutputType());
.setHeader() is when you use the simple language. In 99% of the cases getIn() is sufficient.

Camel-cmis - metadata setting error

A simple test of setting the document class of document stored in filenet worked in camel-cmis 2.16.2. Below is the route
from("file://C:/Target/DMS/").process(new Processor() {
#Override
public void process(Exchange e) throws Exception {
e.getIn().getHeaders().put(PropertyIds.CONTENT_STREAM_MIME_TYPE, "application/pdf; charset=UTF-8");
e.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/Test");
e.getIn().getHeaders().put("cmis:objectTypeId", "doc_Test");
e.getIn().getHeaders().put(PropertyIds.NAME, e.getIn().getHeader(Exchange.FILE_NAME));
}
}).to("cmis://http://test:9080/fncmis/resources/Service?repositoryId=TEST_REPO&username=TEST&password=RAW(TEST)");
When i check the document class of file stored in IBM Filenet - i could see the doc class as Test (symbolic name: doc_Test). But when i add atleast one of the parameter value of that class like below
e.getOut().getHeaders().put("prp_Field1","TestValue1");
Im getting NoSuchHeaderException for parameter "cmis:name" which i have already set as you can see the above route. Is this the correct way to set metadata parameters??
Below route for storing worked. Decide on the Customized classes,fields & data types, create metadata based on that & then same field names are to be set in camel headers before sending to cmis uri (Storing).
ObjectTypeId - Customized class name
CMIS_FOLDER_PATH - filenet folder path inside repository
NAME - File name to be stored
from("file://C:/Target/DMS/").process(new Processor() {
#Override
public void process(Exchange e) throws Exception {
e.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/TEST");
e.getIn().getHeaders().put("cmis:objectTypeId", "doc_Test");
e.getIn().getHeaders().put(PropertyIds.NAME, fileName + ".pdf");
e.getOut().getHeaders().put("prp_Field1","TestValue1");
e.getOut().getHeaders().put("prp_Field2","TestValue2");
e.getOut().getHeaders().put("prp_Field3","TestValue3");
}
}).to("cmis://http://test:9080/fncmis/resources/Service?repositoryId=TEST_REPO&username=TEST&password=RAW(TEST)");
Hope it helps someone..

apache camel #Produce method with Object argument instead of String

I am using Camel's POJO producing e.g.
{
public interface MyListener {
String sayHello(String name);
}
public class MyBean {
#Produce(uri = "activemq:foo")
protected MyListener producer;
public void doSomething() {
// lets send a message
String response = producer.sayHello("James");
}
}
}
The interfaces using method sayHello with string object which used as body in the camel. However, If i try to use any other Object here i get exception from camel saying no TypeConvertor found for BeanInvocation for Conversion java.io.InputStream.
I know is the object was allowed it would have been mentioned somewhere. But i want to reason why it has been done like that and if there's a way to work-around this.
I havent really used POJO messaging as yet. Maybe, an experienced user can help you better with this.
But from what I understand, it should be able to support any kind of object not just string.
The error that you're talking of seems to arise out of a mismatch down the route. I'm guessing there is some kind of issue with the consumption.
Can you please post the exact error stacktrace and the consumer method?
Thanks!
Struggling with the same problem right now. The only obvious workaround so far is to use #EndpointInject instead of #Produce - then you get ProducerTemplate and publish any object:
#EndpointInject(uri = "seda:report-send")
ProducerTemplate reportSender;
Now you can do
Object myObject = new Object();
reportSender.sendBody(myObject);
Or even
Object myObject = new Object();
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("Subject", "Mail subject");
headers.put("contentType", "text/plain");
reportSender.sendBodyAndHeaders(myObject, headers);

Resources