File transfer over HTTP using camel - java

I'm trying to set up a Camel route for transferring files over HTTP. I'm also trying to understand the concept as I'm new to this.
When I code something like below, does that mean I'm routing a simple message over HTTP? Could I call Jetty the consumer in this case? I'm able to run the below code and call the browser and see the message successfully.
from("jetty://http://localhost:32112/greeting")
.setBody(simple("Hello, world!"));
However, I want to send a simple message(eventually an XML) over HTTP following which I would want to save it on disk and analyse it further. Should the code like below work?
CamelContext context = new DefaultCamelContext();
ProducerTemplate template = context.createProducerTemplate();
template.sendBody("direct:start", "This is a test message");
from("direct:start")
.to("jetty://localhost:32112/greeting");
from("jetty://http://localhost:32112/greeting")
.to("direct:end");
Should I be not using direct:start here for parsing XMLs?
Thanks a lot for the help.

first you have to create your routes and start your context. Then you can send messages via your template.
The route could look like this
from("jetty:http://0.0.0.0:32112/greeting")
.routeId("xml-converter-route").autoStartup(false)
.bean(xmlConverterBean, "convertXmlMethodToBeCalledInBean()")
;

If you just want to transfer data and nothing else use restlet or netty-http4. More lightweight than jetty.
from("restlet:/http://localhost:32112/greeting").convertBodyTo(String.class).log(LoggingLevel.INFO, "filetransfer", "log of body: ${body} and headers ${headers}").to("file://C:/test?fileName=body.txt");

Here's a camel test which may help you understand how these components work.
public class CamelRESTExampleTest extends CamelTestSupport {
Logger LOG = LoggerFactory.getLogger(CamelRESTExampleTest.class);
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
// Create a service listening on port 8080
from("restlet:http://localhost:8080/xmlFileService?restletMethod=post")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String rawXML = exchange.getIn().getBody(String.class);
LOG.info("rawXML=" + rawXML);
}
});
// Read files from the local directory and send to the service.
// Create a test.xml file in this directory and it will be read in
from("file:src/test/resources/data?noop=true")
.to("restlet:http://localhost:8080/xmlFileService?restletMethod=post");
}
};
}
#Test
public void test() throws InterruptedException {
// Give the route time to complete
TimeUnit.SECONDS.sleep(5);
}
}

Related

How to create a test for DeadLetter Kafka

In my little microservice, I created a Producer Kafka to send the messages with errors (messages having errors in the JSON format) inside the DeadLetter in this way :
#Component
public class KafkaProducer {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendDeadLetter(String message) {
kafkaTemplate.send("DeadLetter", message);
}
}
I would like to create a JUnitTest for the completeness of the project, but I have no idea how to create the eventuality of a possible JSON error in order to create the test. I thank everyone for any possible help and advice
To create a JUnitTest consistent with your code. I should recreate the case where you pass it a warped or invalid JSON. In your case, I would opt to configure a MockConsumer from which to read any message that the logic of your code will be invited to the dead letter.
To have a usable test structure, I recommend something like this:
#KafkaListener(topics = "yourTopic")
public void listen(String message) {
messages.add(message);
}
For testing a basic structure could be
#Test
public void testDeadLetter(){
//Set up a mockConsumer
MockConsumer<String,String> yourMockConsumer = new MockConsumer<String,String> (OffsetResetStrategy.EARLIEST);
yourMockConsumer.subscribe(Collections.singletonList("yourTopic"));
//Sending message on embedded Kafka broker
String error = "ERRORE";
kafkaTemplate.send("yourTopic", error);
//Reading the message may take a second
Thread.sleep(1000);
//Create an Assert that checks you that the message is equal to the error specified above
}
I hope it will be useful to you!
You can create Kafka topic using testcontainers and write your tests on top of that.
Sharing an example on how to use testcontainers https://github.com/0001vrn/testcontainers-example

MockEndpoint fails to run route

Using mock to launch a salesforce streaming route as shown here fails for the following route:
from("salesforce:AccountUpdateTopic?notifyForFields=ALL&notifyForOperations=ALL")
.tracing().convertBodyTo(String.class).to("file:D:/tmp/")
.to("mock:output")
.log("SObject ID: ${body}");
in
package org.apache.camel.component.salesforce;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.component.salesforce.internal.OperationName;
import org.junit.Test;
public class StreamingApiIntegrationTest extends AbstractSalesforceTestBase {
#Test
public void testSubscribeAndReceive() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:AccountUpdateTopic");
mock.start();
Thread.sleep(10000);
mock.stop();
}
#Override
protected RouteBuilder doCreateRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
// test topic subscription
from("salesforce:AccountUpdateTopic?notifyForFields=ALL&notifyForOperations=ALL").tracing().convertBodyTo(String.class).to("file:D:/tmp/").to("mock:output").log("SObject ID: ${body}");
}
};
}
}
Running this test does not start the route (updates are not fetched from Salesforce and stored in /tmp/).
Can mock run a route and wait for updates from Salesforce? Is there a shorter example that allows for testing salesforce routes without making use of spring?
You misunderstood the Camel Mock component. Mocks are not starting anything. They are just endpoints who record and assert the messages they receive.
To trigger a Camel route you have to send a message to it. You can do this easily using a ProducerTemplate.
It is this line from the example you mention that does exactly that.
CreateSObjectResult result = template().requestBody(
"direct:upsertSObject", merchandise, CreateSObjectResult.class);
template is the ProducerTemplate and requestBody the method to send a message to the endpoint direct:upsertSObject and wait for a response. See the Javadocs of ProducerTemplate for the various existing signatures.

Apache Camel BindException: "Can't Assign Requested Address"

I am in the process of learning how to use Camel. I am having an issue with the following snippet of code:
#SpringBootApplication
public class FeefooExampleApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(FeefooExampleApplication.class, args);
CamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new CamelConfig());
camelContext.start();
Blah blah = new Blah();
blah.getFeefoData();
}
}
My CamelConfig Class is the following:
package com.example.camel;
import com.example.feefo.FeedbackProcessor;
import org.apache.camel.builder.RouteBuilder;
public class CamelConfig extends RouteBuilder {
private FeedbackProcessor feedbackProcessor = new FeedbackProcessor();
#Override
public void configure() throws Exception {
from("jetty:http://cdn2.feefo.com/api/xmlfeedback?merchantidentifier=example-retail-merchant")
.convertBodyTo(String.class)
.bean(feedbackProcessor, "processFeedback") ;
}
}
The error that is reported is the following: 'Exception in thread "main" java.net.BindException: Can't assign requested address'
Would anybody be able to help ?
Thank You
When used as a consumer, the jetty component create an HTTP server, listening to a HTTP request, and creating an exchange with this request.
In other words, when you do from("jetty:http://cdn2.feefo.com/.."), you are asking jetty to create an HTTP server with the network interface associated to "cdn2.feefo.com": This fails (well, I have assumed your machine is not this host)
If you want to request this HTTP address, you have to use jetty (or the http4 component) as a producer. For example:
from("direct:check_xmlfeedback")
.to("jetty:http://cdn2.feefo.com/api/xmlfeedback?merchantidentifier=example-retail-merchant")
...
and call your route with :
context.getProducerTemplate().requestBody("direct:check_xmlfeedback", null);
If you want to periodically poll this HTTP address, you can use the timer component:
from("timer:check?period=5m")
.to("jetty:http://cdn2.feefo.com/api/xmlfeedback?merchantidentifier=example-retail-merchant")
...

Camel: how to check response http response

I am pretty new with Camel. I have been trying to submit a data (Json from a file) to a webservice. This is my code:
public static void main(String args[]) throws Exception {
// create CamelContext
CamelContext context = new DefaultCamelContext();
// add our route to the CamelContext
context.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from("file:data/inbox?noop=true")
.marshal()
.string()
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.to("http://www.a-service.com");
}
});
// start the route and let it do its work
context.start();
Thread.sleep(10000);
// stop the CamelContext
context.stop();
}
Then the webservice will response with Json which can be
{result:OK}
or
{result:FAIL}
Now, if a response has responseCode as 200, Camel will consider as success.
My question is, how can I have a validating process for responsed JSon so that if it is FAIL, Camel should not consider as success?
Solution Credit #Namphibian:
By adding processor and the end. This code has been tested:
from("file:data/inbox?noop=true")
.marshal()
.string("UTF-8")
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.to("http://monrif-test.userspike.com/monrif/rss/monrif_-all-global")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
String msg = in.getBody(String.class);
System.out.println("Response: " + msg);
if(msg.contains("OK")){
// go to party
}else{
throw new Exception("test exception");
}
}
});
There are two broad strategies you can use to achieve this.
Processor Based:
Add a processor to the end of the route. In this processor do the check if the webservice then responds with a true or false value.
A processor would look something like this:
package com.example;
import java.util.Map;
import org.apache.camel.Body;
import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.apache.camel.Headers;
import org.apache.camel.Message;
public class GeneralProcessor {
#Handler
public void PrepapreErrorImportReport
(
#Headers Map hdr
, Exchange exch
)
{
//todo: Get the message as a string;
Message in = exch.getIn();
String msg = (String)in.getBody();
// Now check if body contains failed or ok.
if(msg.contains("OK")){
//todo: go party the message was OK
}
else{
//todo: Oh Oh! Houston we have a problem
}
}
}
You can then modify your route to use this processor.
The Simple Expression Language
This is one way the other way is to use the simple expression language. See the example below on how to use this.
from("file:data/inbox?noop=true")
.marshal()
.string()
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.to("http://www.a-service.com")
.choice()
.when(simple("${body} contains 'OK'")).to("activemq:okqueue")
.otherwise().to("activemq:queue:other");
Notice the simple("${body} contains 'OK'") piece of code. That is the power of simple.
Both approaches have uses.
In the Process method , you can use below method and it will work
LOGGER.info("Response code " + message.getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class));

Camel file unit test

I am new to Apache Camel, I have written a simple route to scan a directory (/test), file will be processed when it was copied into the directory. Anyone has an idea on how to write a camel unit test to test the following route? Is there a way to mock the process of copying the file into the /test directory so that the route will be triggered.
public void configure() {
from( "file:/test?preMove=IN_PROGRESS" +
"&move=completed/${date:now:yyyyMMdd}/${file:name}" +
"&moveFailed=FAILED/${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.${file:ext}" )
.process(new Processor() {
public void process(Exchange exchange) throws IOException {
File file = (File) exchange.getIn().getBody();
// read file content ......
}
});
}
You have done the routing by one of many correct ways. But there exist some more important pieces to make your code run - you should create a context, create a router with this your configure(), add it to a context, and run this context.
Sorry, I prefer beans to processors, so you have also to register a bean. And make you processing a normal named method in a named class.
I think, the most compact info is here. JUnit test is a standalone app and you need to run Camel as a standalone app for JUnit testing.
I think the basic idea is that you mock the end endpoint so you can check what is coming out your route. There are a few different ways, but you could test your route as follows:
public class MyRouteTest extends CamelSpringTestSupport {
private static final String INPUT_FILE = "myInputFile.xml";
private static final String URI_START = "direct:start";
private static final String URI_END = "mock:end";
#Override
public boolean isUseAdviceWith() {
return true;
}
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(CamelTestConfig.class); // this is my Spring test config, where you wire beans
}
#Override
protected RouteBuilder createRouteBuilder() {
MyRoute route = new MyRoute();
route.setFrom(URI_START); // I have added getter and setters to MyRoute so I can mock 'start' and 'end'
route.setTo(URI_END);
return route;
}
#Test
public void testMyRoute() throws Exception {
MockEndpoint result = getMockEndpoint(URI_END);
context.start();
// I am just checking I receive 5 messages, but you should actually check the content with expectedBodiesReceived() depending on what your processor does to the those files.
result.expectedMessageCount(5);
// I am just sending the same file 5 times
for (int i = 0; i < 5; i++) {
template.sendBody(URI_START, getInputFile(INPUT_FILE));
}
result.assertIsSatisfied();
context.stop();
}
private File getInputFile(String name) throws URISyntaxException, IOException {
return FileUtils.getFile("src", "test", "resources", name);
}
I am sure you already solved your issue is 2013, but this is how I would solve it in 2017. Regards

Categories

Resources