I have a Route like the following.
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
policy.setRouteStartDate(new Date());
policy.setRouteStartRepeatInterval(1000);
from("file:data/in")
.routePolicy(policy)
.to("direct:validateInput")
.to("direct:changeInput")
.to("file:data/out");
So the Route takes an file from the inputfolder every second. After some validation and changing it writes it out to an out folder.
Now I would like to be able to close the actual route at every point. So If some error happens at the direct route validateInput the following two parts should not be excecuted.
I could do this with some doTry() and doCatch() but this will look ugly and hard to read.
Question: is it somehow possible to stop on loop of the main route without stopping the route complete? Like this the actual file won't be printed to the outfolder but the file comming in 5 seconds can be processed in normal way.
Creating a new Process and stopping the main Route in a seperate Thread doesn't work.
The file is still written into the date/out folder
The route is stopped complet and won't take any files anymore.
just have your validateInput step throw an Exception and use an onException() clause to handle the exception, that will short-circuit the flow for just that message and allow for processing future files dropped into the 'data/in' directory normally
I believe you best option is using doCatch and doFinally. I think anything different would be much more difficult to read and very ugly.
http://camel.apache.org/validation.html
This is the most logical approach to solving this problem.
It works good, but 1 Problem is still left. My actual Example looks like the following:
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
policy.setRouteStartDate(new Date());
policy.setRouteStartRepeatInterval(1000);
//start main route
from("file:test-data/in?delete=true")
.routePolicy(policy)
.onException(Exception.class)
.log(LoggingLevel.ERROR, "${exception}")
.handled(true).useOriginalMessage()
.to("file://test-data/error?autoCreate=true")
.end()
.log(LoggingLevel.DEBUG, "Processing file ${file:name}")
.to("direct:validateInput")
.to("direct:validateContent")
.to("direct:validateOutput")
.to("file:test-data/out");
from("direct:validateInput")
.doTry()
.to("validator:message.xsd")
.doCatch(ValidationException.class)
.process(getErrorProcessor("Could not validate import against the xsd. Message: ${exception}; File: ${body}"));
//...
}
}
private Processor getErrorProcessor(final String message) {
return new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
throw new Exception(message);
}
};
}
If I put an incorrect file into the in folder I get the following error message:
ERROR route1:145 - java.lang.Exception: Could not validate import against the xsd. Message: ${exception}; File: ${body}
As you can see, camel replaced the ${...} attribute one time. But after this I won't replace the new ${...} elements. Any Idear how I can tell camel replace any new {...} parts or how I can replace them before by my self?
found an answer which is good. For all folks who find this question here an "complete" solution.
public void start() {
try {
CamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
errorHandler(loggingErrorHandler().level(LoggingLevel.ERROR));
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
policy.setRouteStartDate(new Date());
policy.setRouteStartRepeatInterval(1000);
//Exception Handling in case the xsd validation fails
onException(SomeSpecialException.class)
.log(LoggingLevel.ERROR, "${exception}")
.handled(true).useOriginalMessage()
.to("file:test-data/error?autoCreate=true")
.end();
//start main route
from("file:test-data/in?delete=true")
.routePolicy(policy)
.log(LoggingLevel.DEBUG, "Processing file ${file:name}")
.to("direct:validateInput")
.to("direct:validateContent")
.to("direct:validateOutput")
.to("file:test-data/out");
//start of separate Routes
from("direct:validateInput")
.doTry()
.to("validator:message.xsd")
.doCatch(ValidationException.class)
.process(getErrorProcess());
//...
}
});
camelContext.start();
} catch (Exception e) {
LOGGER.error("Error while processing camel route: " + e.getMessage());
}
}
/**
* throws an SomeSpecialException in case of calling
*
* #return a Processor which is supposed to be called in case an {#link org.apache.camel.ValidationException} happens
*/
private Processor getErrorProcess() {
return new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
StringBuilder message = new StringBuilder();
String fileContent = exchange.getIn().getBody(String.class);
String originalErrorMessage;
try {
SchemaValidationException schemaValidationException = (SchemaValidationException) exchange.getProperties().get("CamelExceptionCaught");
originalErrorMessage = schemaValidationException.getMessage();
} catch (Exception e) {
originalErrorMessage = "Could not retrieve original error message.";
}
message.append("Could not validate import against the xsd. ")
.append("Original message: ").append(originalErrorMessage).append("; ")
.append("File:").append(fileContent);
throw new SomeSpecialException(message.toString());
}
};
}
Thanks for everyone that helped me.
Related
Issue:
I have multiple route sending messages to an ActiveMQ queue which later gets processed by a processor to save status information
The same message sending from ProducerTemplate to ActiveMQ queue somehow breakes the same code by not triggering logs on console, and saving status information to a randomly generated file name.
Desired Behavior:
Both sending method gets the messages processed the same way
Code Explanation:
On below codes the Save processor is the one producing the weird behavior where logs dont show up on console and writes to some random file, file name is basically the clientID from ActiveMQ
StartRoute calling the activemq:save works correctly
Code:
public class Save implements Processor {
public void process(Exchange exchange) throws Exception {
try {
Map<String, Object> map = new HashMap<String, Object>() {};
map.put(".....", ".....");
map.put(".....", ".....");
map.put(".....", ".....");
map.put(".....", ".....");
ProducerTemplate template = exchange.getContext().createProducerTemplate();
String response = template.requestBodyAndHeaders("activemq:save", "Batch Job Started", map, String.class);
FluentProducerTemplate FluentTemplate = exchange.getContext().createFluentProducerTemplate();
String result = FluentTemplate
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.withHeader(".....", ".....")
.to("activemq:save")
.request(String.class);
} catch (Exception e) {
.....
}
}
}
public class StartRoute extends RouteBuilder {
restConfiguration()
.component("servlet")
.enableCORS(true);
rest("/start").id("start")
.post("/{filename}")
.route()
.....
.process(new Save())
.wireTap(startBackgroundProcessRoute)
.to("activemq:save")
.endRest();
}
public class SaveRoute extends RouteBuilder {
from("activemq:save")
.log("started saving")
.process(new FormatText())
.to("file:///status");
}
This question came from my original problem described here:
Camel Multicast Route call order
Solution can be found also there:
Camel Multicast Route call order
However to satisfy this question:
It seems Camel have few bugs regarding producer templates and maybe ActiveMQ, this is my initial conclusion.
The Only way i was able to use ProducerTemplate without issue is to use the send function with Exchanges, send() sends the messages correctly to the ActiveMQ same way as the to() however for whatever reason the content was still not written to the file.
After I dropped the ActiveMQ between the routes, everything started to work consistently. So possible miss configuration on ActiveMQ component or possible another camel framework bug.
If anyone knows the exact answer i would be happy to hear / see the reason for this behavior.
Code example:
public class SaveProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
ProducerTemplate template = exchange.getContext().createProducerTemplate();
template.send(Utilities.trackBatchJobStatus, exchange);
/** NOTE
* DO NOT USE any other functions which are not working with EXCHANGES.
* Functions that uses body, header values and such are bugged
*/
}
}
Their are numerous resources on internet but I couldn't find a simple answer to my problem.
I want my test case to fail and report a one liner meaningful message instead of full stack trace.
I tried using try, catch, if, else but I want my test to fail not to pass and throw message.
Scenario - Load url, if url doesn't load throw error and abort test and move to next iteration of loading next url
Any solution ?
You can try... try catch
try {
doStuff();//this code is failing
}
catch (YourExpectedException e){ //catch the correct exception here
//this passes your custom message AND the caught exception
//should also stop the test
throw new AssertionError("This is my custom message", e);
}
There are two ways how to do it. I would like to suggest you the most simple and beautiful ways.
1) Checking exception and if exception on of you specify you will execute some code or do something. Here is the example:
#AfterMethod
public void afterMethod(ITestResult result) throws IOException {
Throwable exception = result.getThrowable();
if (exception instanceof org.openqa.selenium.TimeoutException
||
exception instanceof org.openqa.selenium. NoSuchElementException) {
Assert.fail("Some message or code");
}
}
2)
You can implement WebDriverEventListener in your project.
What it means?
It means that WebDriver allow to execute some logic before and after some events. You can add try-catch in the implementation of methods.
Example:
#Override
public void beforeFindBy(By by, WebElement webElement, WebDriver webDriver) {
// your code
}
#Override
public void afterFindBy(By by, WebElement webElement, WebDriver webDriver) {
// your code
}
#Override
public void beforeClickOn(WebElement webElement, WebDriver webDriver) {
// your code
}
#Override
public void afterClickOn(WebElement webElement, WebDriver webDriver) {
// your code
}
Here is more detail example: link
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.
To retrieve some open data from a remote web server to process, I'm trying out Apache Camel.
The problem is that it seems that the data is never received. I have tried the jetty, ahc and cxf components but can't get it to work. For example like this:
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class CamelHttpDemo {
public static void main(final String... args) {
final CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
this.from("direct:start")
.to("ahc:http://camel.apache.org/")
.process(exchange -> {
System.out.println(exchange);
});
}
});
context.start();
Thread.sleep(10000);
context.stop();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
No output is written so the line System.out.println(exchange); is never executed and I assume the data is not retrieved.
I'm using the most recent version of Apache Camel, 2.17.1.
You need some message producer in your route to emit Exchange that would trigger the http component. Your route starts with direct:start which cannot emit new Exchanges, it just sits and waits for someone to initiate the process.
The easiest way to make your route work is to replace direct:start with some producer. For instance, replacing it with this timer .from("timer://foo?fixedRate=true&period=10000") will trigger your http-request once every 10 seconds.
If you want to initiate the request manually, you need to create a ProducerTemplate and use it to send a message to direct:start. That would be:
ProducerTemplate template = context.createProducerTemplate();
template.sendMessage("direct:start", "Message body");
I am getting a strange situation at the code below which simply routes request to Google and returns response.
It works well but when I activate the line commented out as "//Activating this line causes empty response on browser" to print out returned response from http endpoint (Google), response is disappear, nothing is displayed on browser. I thought it might be related with input stream of http response which can be consumed only once and I activated Stream Caching on context but nothing changed.
Apache Camel version is 2.11.0
Any suggestions are greatly appreciated, thanks in advance.
public class GoogleCaller {
public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("jetty:http://0.0.0.0:8081/myapp/")
.to("jetty://http://www.google.com?bridgeEndpoint=true&throwExceptionOnFailure=false")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Response received from Google, is streamCaching = " + exchange.getContext().isStreamCaching());
System.out.println("----------------------------------------------IN MESSAGE--------------------------------------------------------------");
System.out.println(exchange.getIn().getBody(String.class));
System.out.println("----------------------------------------------OUT MESSAGE--------------------------------------------------------------");
//System.out.println(exchange.getOut().getBody(String.class)); //Activating this line causes empty response on browser
}
});
}
});
context.setTracing(true);
context.setStreamCaching(true);
context.start();
}
}
As you use a custom processor to process the message, you should keep it in mind the in message of the exchange has response message from the google, if you are using exchange.getOut(), camel will create a new empty out message for you and treat it as response message.
Because you don't set the out message body in the processor, it makes sense that you get the empty response in the browser.