Standalone Apache Camel application doesn' run - java

I'm on this problem: can't get my apache camel batch run. Here is the code:
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.main.Main;
public class Launch {
private Main main;
public static void main(String[] args) {
Launch l = new Launch();
System.out.println(System.getProperty("from") +" -> "+System.getProperty("to"));
try {
l.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
public void execute() throws Exception {
main = new Main();
main.enableHangupSupport();
main.addRouteBuilder(new FromFileToFile());
main.run();
}
private static class FromFileToFile extends RouteBuilder {
#Override
public void configure() throws Exception {
onException(Exception.class).handled(true).process(new Processor() {
public void process(Exchange arg0) throws Exception {
arg0.getException().printStackTrace();
}
});
from(System.getProperty("from") + "")
.filter(body().contains("DOTHIS"))
.process(new Processor() {
public void process(Exchange arg0) throws Exception {
System.out.println(arg0.getIn().getBody()
.toString());
}
}).to(System.getProperty("to"))
.to(System.getProperty("to") + ".BAK");
}
}
}
I don't want to use the Thread.sleep(...) workaround. I simply copied and modified the source stuff posted on this official docs page. When I run my dummy program using Eclipse, application simply hangs. I can't figure out what's wrong.

Your application doesn't probably hang, it just won't do anything. :)
You have defined filter that checks if Camel Message body contains word "DOTHIS". When you consume file with File consumer the body will be of type GenericFile. Then when your filter checks for that string it surely won't find it since the body is not string.
Solution: Convert file body to string first and then your filter will work and you get the result you were expecting. Conversion can be done like this
from(System.getProperty("from") + "")
.convertBodyTo(String.class, "UTF-8")
.filter(body().contains("DOTHIS"))
You might also want to increase logging level so you can get the grasp of what's going on in your route.

It was a problem about path. I passed arguments as options like this:
file://Users/francesco/..
As I'm using windows I must specify uri like this
file:///C:/Users/francesco/..
The batch doesn't hangs, it continues to poll directory for new files to consumes.

Related

Serenity screenplay pattern- upload file

We are trying cucumber serenity framework for end to end tests. I am fairly new the technology and I tired this simple code below.
actor.attemptsTo(Enter.theValue(path).into(Upload));
where path is the location of file i am trying to upload using browser's upload widget.Has anyone ever managed to perform actions like this using serenity screen play pattern.
Its really making us think of giving up serenity and just use cucumber-selenium framework as I can easily perform this using Upload.sendkeys(path);
Any help is much appreciated. Thanks in advance.
AS requested: Listing Steps:
public class ListingSteps
{
#Before
public void set_the_stage() {
OnStage.setTheStage(new OnlineCast());
}
#Given("^(.*) is able to click import products$") public void userIsAbleToClick(String actorName) throws Throwable
{
theActorCalled(actorName).wasAbleTo(Start.theApplication());
}
#When("^s?he imports a single item successfully$") public void heImportsASingleItemSuccessfully() throws Throwable
{
theActorInTheSpotlight().attemptsTo(Import.spreadsheet());
}
#Then("^(.*) are listed on ebay and amazon with all the right information$") public void itemsAreListedOnEbayAndAmazonWithAllTheRightInformation(String actorName, String SKU)
throws Throwable
{
//pending
}
Ignore then for now as its work in progress.
Import class:
public class Import implements Task
{
protected String path =
"C:\\somePathToFile\\populated_excel.xlsx";
public static Import spreadsheet()
{
return instrumented(Import.class);
}
#Override public <T extends Actor> void performAs(T actorName)
{
actorName.attemptsTo(Click.on(Products.ProductsScreen));
actorName.attemptsTo(Click.on(Products.Upload));
actorName.attemptsTo(Enter.theValue(path).into(Browse).thenHit(Keys.RETURN));//this is the line which is giving errors
actorName.attemptsTo(Click.on(Products.UploadButton));
}
}
Target Browse
public class Products
{
public static Target Browse = Target.the("browse file").locatedBy("//input[#type='file']");
}
Did you try removing these lines?
actorName.attemptsTo(Click.on(Products.ProductsScreen));
actorName.attemptsTo(Click.on(Products.Upload));
You don't need to open the upload file component, only write the file path directly to the input file element and perform the submit.
The way I managed to get this working was by using the FileToUpload class:
import net.thucydides.core.pages.components.FileToUpload;
FileToUpload fileToUpload = new FileToUpload(driver, fileName);
fileToUpload.fromLocalMachine().to(webElement);
I got this working with a simple:
import java.nio.file.*;
Path data = null;
try {
data = Paths.get(ClassLoader.getSystemResource(file).toURI());
} catch (URISyntaxException ignore) {}
ACTOR.attemptsTo(Upload.theFile(data).to(target));
file is an actual file that exists on your classpath, in src/test/resources if you have a Maven project.
target is something like:
Target.the("Image upload").located(By.xpath("//input[#type='file']"));

How to get the exception that was thrown when a Cucumber test failed in Java?

I can perform actions on test failure by using:
#After
public void afterTest(Scenario scenario) {
if (scenario.isFailed()) {
/*Do stuff*/
}
}
However some of the actions I need to perform depend on the Exception that was thrown and in what context it was thrown. Is there a way to get the Throwable that caused the test to fail? For example in JUnit I would do this by extending TestWatcher and add a rule to my tests:
#Override
protected void failed(Throwable e, Description description) {
/*Do stuff with e*/
}
However the cucumber-junit iplementation does not allow the use of rules, so this solution would not work with Cucumber.
I don't think I need to explain why accessing a thrown exception on test failure would be useful, however I will still provide an Example:
My test environment is not always stable, so my tests might fail unexpectedly at any moment (there's no specific place I can try to catch the exception since it could occur at any time). When this happens I need the test to reschedule for another attempt, and log the incident so that we can get some good statistical data on the environment instability (when, how frequent, how long etc.)
The problem with the work around suggested by Frank Escobar:
By using reflection to reach into a frameworks internals you're depending on implementation details. This is a bad practice, when ever the framework changes its implementation your code may break as you will observe in Cucumber v5.0.0.
Hooks in Cucumber are designed to manipulate the test execution context before and after a scenario. They're not made to report on the test execution itself. Reporting is cross cutting concern and best managed by using the plugin system.
For example:
package com.example;
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.plugin.event.Result;
import io.cucumber.plugin.event.Status;
import io.cucumber.plugin.event.TestCase;
import io.cucumber.plugin.event.TestCaseFinished;
public class MyTestListener implements ConcurrentEventListener {
#Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestCaseFinished.class, this::handleTestCaseFinished);
}
private void handleTestCaseFinished(TestCaseFinished event) {
TestCase testCase = event.getTestCase();
Result result = event.getResult();
Status status = result.getStatus();
Throwable error = result.getError();
String scenarioName = testCase.getName();
String id = "" + testCase.getUri() + testCase.getLine();
System.out.println("Testcase " + id + " - " + status.name());
}
}
When using JUnit 4 and TestNG you can activate this plugin using:
#CucumberOptions(plugin="com.example.MyTestListener")
With JUnit 5 you add it to junit-platform.properties:
cucumber.plugin=com.example.MyTestListener
Or if you are using the CLI
--plugin com.example.MyTestListener
I've implemented this method using reflections. You can't access directly to steps errors (stack trace). I've created this static method which allows you to access to "stepResults" attribute and then you can iterate and get the error and do whatever you want.
import cucumber.runtime.ScenarioImpl;
import gherkin.formatter.model.Result;
import org.apache.commons.lang3.reflect.FieldUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
#After
public void afterScenario(Scenario scenario) {
if (scenario.isFailed())
logError(scenario);
}
private static void logError(Scenario scenario) {
Field field = FieldUtils.getField(((ScenarioImpl) scenario).getClass(), "stepResults", true);
field.setAccessible(true);
try {
ArrayList<Result> results = (ArrayList<Result>) field.get(scenario);
for (Result result : results) {
if (result.getError() != null)
LOGGER.error("Error Scenario: {}", scenario.getId(), result.getError());
}
} catch (Exception e) {
LOGGER.error("Error while logging error", e);
}
}
You can to this by writing your own custom implementation of Formatter & Reporter interface. The empty implementation of Formatter is the NullFormatter.java which you can extend. You will need to provide implementations for the Reporter interface.
The methods which would be of interest will be the result() of the Reporter interface and possibly the done() method of Formatter. The result() has the Result object which has the exceptions.
You can look at RerunFormatter.java for clarity.
Github Formatter source
public void result(Result result) {
//Code to create logs or store to a database etc...
result.getError();
result.getErrorMessage();
}
You will need to add this class(com.myimpl.CustomFormRep) to the plugin option.
plugin={"pretty", "html:report", "json:reports.json","rerun:target/rerun.txt",com.myimpl.CustomFormRep}
More details on custom formatters.
You can use the rerun plugin to get a list of failed scenarios to run again. Not sure about scheduling a run of failed tests, code to create a batch job or schedule one on your CI tool.
This is the workaround for cucumber-java version 4.8.0 using reflection.
import cucumber.api.Result;
import io.cucumber.core.api.Scenario;
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.java.After;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
#After
public void afterScenario(Scenario scenario) throws IOException {
if(!scenario.getStatus().isOk(true)){
logError(scenario);
}
}
private static void logError(Scenario scenario) {
try {
Class clasz = ClassUtils.getClass("cucumber.runtime.java.JavaHookDefinition$ScenarioAdaptor");
Field fieldScenario = FieldUtils.getField(clasz, "scenario", true);
fieldScenario.setAccessible(true);
Object objectScenario = fieldScenario.get(scenario);
Field fieldStepResults = objectScenario.getClass().getDeclaredField("stepResults");
fieldStepResults.setAccessible(true);
ArrayList<Result> results = (ArrayList<Result>) fieldStepResults.get(objectScenario);
for (Result result : results) {
if (result.getError() != null) {
LOGGER.error(String.format("Error Scenario: %s", scenario.getId()), result.getError());
}
}
} catch (Exception e) {
LOGGER.error("Error while logging error", e);
}
}
For cucumber-js https://www.npmjs.com/package/cucumber/v/6.0.3
import { After } from 'cucumber'
After(async function(scenario: any) {
const exception = scenario.result.exception
if (exception) {
this.logger.log({ level: 'error', message: '-----------StackTrace-----------' })
this.logger.log({ level: 'error', message: exception.stack })
this.logger.log({ level: 'error', message: '-----------End-StackTrace-----------' })
}
})
After a lot of experimentation I now removed the Before/After-Annotations and rely on Cucumber-Events instead. They contain the TestCase (which is what the Scenario-class wraps) and a Result where you can call getError(); to get the Throwable.
Here is a simple example to get it working
import io.cucumber.plugin.EventListener;
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.plugin.event.Result;
import io.cucumber.plugin.event.Status;
import io.cucumber.plugin.event.TestCase;
import io.cucumber.plugin.event.TestCaseFinished;
import io.cucumber.plugin.event.TestCaseStarted;
import org.openqa.selenium.WebDriver;
public class TestCaseListener implements EventListener {
#Override
public void setEventPublisher(final EventPublisher publisher) {
publisher.registerHandlerFor(TestCaseStarted.class, this::onTestCaseStarted);
publisher.registerHandlerFor(TestCaseFinished.class, this::onTestCaseFinished);
}
public void onTestCaseStarted(TestCaseStarted event) {
TestCase testCase = event.getTestCase();
System.out.println("Starting " + testCase.getName());
// Other stuff you did in your #Before-Method.
// ...
}
private void onTestCaseFinished(final TestCaseFinished event) {
TestCase testCase = event.getTestCase();
System.out.println("Finished " + testCase.getName());
Result result = event.getResult();
if (result.getStatus() == Status.FAILED) {
final Throwable error = result.getError();
error.printStackTrace();
}
// Other stuff you did in your #After-Method.
// ...
}
}
All that's left to do is to register this class as a Cucumber-Plugin.
I did this by modifying my #CucumberOptions-annotation:
#CucumberOptions(plugin = {"com.example.TestCaseListener"})
I find this much cleaner than all of this reflection-madness, however it requires a lot more code-changes.
Edit
I don't know why, but this caused a lot of tests to randomly fail in a multithreaded environment.
I tried to figure it out, but now also use the ugly reflections mentioned in this thread:
public class SeleniumUtils {
private static final Logger log = LoggerFactory.getLogger(SeleniumUtils.class);
private static final Field field = FieldUtils.getField(Scenario.class, "delegate", true);
private static Method getError;
public static Throwable getError(Scenario scenario) {
try {
final TestCaseState testCase = (TestCaseState) field.get(scenario);
if (getError == null) {
getError = MethodUtils.getMatchingMethod(testCase.getClass(), "getError");
getError.setAccessible(true);
}
return (Throwable) getError.invoke(testCase);
} catch (Exception e) {
log.warn("error receiving exception", e);
}
return null;
}
}
If you just want to massage the result being sent to the report then you can extend the CucumberJSONFormatter and override the result method like this:
public class CustomReporter extends CucumberJSONFormatter {
CustomReporter(Appendable out) {
super(out);
}
/**
* Truncate the error in the output to the testresult.json file.
* #param result the error result
*/
#Override
void result(Result result) {
String errorMessage = null;
if (result.error) {
errorMessage = "Error: " + truncateError(result.error);
}
Result myResult = new Result(result.status, result.duration, errorMessage);
// Log the truncated error to the JSON report
super.result(myResult);
}
}
Then set the plugin option to:
plugin = ["com.myimpl.CustomReporter:build/reports/json/testresult.json"]

Liferay, how to get root logger?

I'm trying to develop portlet which sends email to administrators when a set of specified excpetions occured in a given time period. I'm trying to get root logger in liferay so i can add my appender to it and process all messages going trough the logging mechanism.
I looked into source code and seems that liferay uses java.util.logging.LogManager. I made a hook which is fired when server starts up. Here is my run() method:
public void run(String[] ids) throws ActionException {
System.out.println("initializing");
LogListener listener = new LogListener();
listener.setLevel(Level.ALL);
listener.setFilter(null);
Logger.getGlobal().addHandler(listener);
_log.debug("complete");
}
And LogListener class
package pl.com.mds.portlet.mailing.hook;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
public class LogListener extends Handler {
#Override
public void publish(LogRecord record) {
System.out.println("publishing******");
}
#Override
public void flush() {
// TODO Auto-generated method stub
System.out.println("have to flush! *****");
}
#Override
public void close() throws SecurityException {
// TODO Auto-generated method stub
System.out.println("close meee!");
}
}
But when some exception is thrown i cant see in console publishing****** only exception stacktrace. How can i get all logs in application and expceptions?
Thanks :)
Root logger can be obtained through standard procedure Logger.getLogger("logger name goes here"). Root logger has an empty string as its name. So you should edit your code as follows:
Logger.getLogger("").addHandler(listener);
Consider using LogFactoryUtil. Like following
private static final Log LOG = LogFactoryUtil.getLog(MyPortlet.class);

Apache Camel Resequence Program Not Working

I am trying to run a simple re-sequence program using Apache Camel. This program uses the Java DSL to re-sequence incoming Java messages. When I run this program the messages are written to the folder but do not appear to be in any particular order based on the header value or the alphabetical order of the single word in the message body. The files Camel creates are out of order still as if the resequence DSL function did nothing.
How can I get this program to actually order the messages like the Arrays.sort() method would do? Also, how can I get this program to resequence and then aggregate the messages in the correct sort order to a single file?
Here is the program... I call the main Camel route via the other class that has the main method.
import org.apache.camel.builder.RouteBuilder;
public class SortThoseMessages extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:pointA")
.resequence(header("grocery"))
.to("file:target/pointB");
}
}
The class below has main and produces the messages into the queue, pointA.
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.ProducerTemplate;
public class NewSequenceMain {
public static void main(String[] args) {
CamelContext c = new DefaultCamelContext();
try {
c.addRoutes(new SortThoseMessages());
ProducerTemplate template = c.createProducerTemplate();
c.start();
template.sendBodyAndHeader("direct:pointA", "apple", "grocery", 1);
template.sendBodyAndHeader("direct:pointA", "orange", "grocery", 3);
template.sendBodyAndHeader("direct:pointA", "bannanna", "grocery", 2);
Thread.sleep(5000);
c.stop();
} catch(Exception ex) {
System.err.println("Exception thrown -> " + ex);
System.err.println("Now printing stacktrace...");
ex.printStackTrace();
}
}
}
The messages maybe re-sequenced inside the Camel route but not when written to file. To see the re-sequence call the aggregator Java DSL to see the message bodies in the sequence order specified. The messages in the program posted are ordered according to the number of the header. The method call on the ProducerTemplate object reference sets the header as the integer in the last argument to the sendBodyAndHeader() method call.
To see the re-sequencing take effect in a single file as the destination of the Camel route please check out the example below:
import org.apache.camel.builder.RouteBuilder;
public class ReorganizingMessages extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:pointA")
.resequence(header("grocery"))
.to("log://org.apache.camel.howto?showAll=true&multiline=true")
.aggregate().constant(true).completionTimeout(100L).
aggregationStrategy(new StringAggregator())
.to("file:target/pointB");
}
}
The code above uses a custom aggregator Java bean that can be seen below.
import org.apache.camel.Exchange;
import org.apache.camel.processor.aggregate.AggregationStrategy;
public class StringAggregator implements AggregationStrategy {
#Override
public Exchange aggregate(Exchange old, Exchange new1) {
if (old == null) {
return new1;
}
String oldBody = old.getIn().getBody(String.class);
String newBody = new1.getIn().getBody(String.class);
old.getIn().setBody(oldBody + " " + newBody);
return old;
}
}

Apache camel send a simple message

I have a simply camel MINA server using the JAVA DSL, and I am running like the example documented here:
Running Camel standalone and have it keep running in JAVA
MINA 2 Component
I am trying to create a sample application hosted at "mina:tcp://localhost:9991" (aka MyApp_B) that sends a very simple message to a server hosted at "mina:tcp://localhost:9990" (aka MyApp_A).
I want is to send a simple message containing a String in the header (which is "Hellow World!") and with the address in the body.
public class MyApp_B extends Main{
public static final String MINA_HOST = "mina:tcp://localhost:9991";
public static void main(String... args) throws Exception {
MyApp_B main = new MyApp_B();
main.enableHangupSupport();
main.addRouteBuilder(
new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:start")
.setHeader("order", constant("Hello World!"))
.setBody(constant(MINA_HOST))
.to("mina:tcp://localhost:9990");
}
}
);
System.out.println("Starting Camel MyApp_B. Use ctrl + c to terminate the JVM.\n");
main.run();
}
}
public class MainApp_A {
public static void main(String... args) throws Exception {
Main main = new Main();
main.enableHangupSupport();
main.addRouteBuilder(new RouteBuilder(){
#Override
public void configure() throws Exception {
from("mina:tcp://localhost:9990").bean(MyRecipientListBean.class,
"updateServers").to("direct:debug");
from("direct:debug").process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Received order: " +
exchange.getIn().getBody());
}
});
}
});
main.run(args);
}
}
Bean used by MyApp_A:
public class MyRecipientListBean {
public final static String REMOVE_SERVER = "remove";
public final static String ADD_SERVER = "add";
private Set<String> servers = new HashSet<String>();
public void updateServers(#Body String serverURI,
#Header("order") String order){
System.out.println("===============================================\n");
System.out.println("Received " + order + "request from server " + serverURI + "\n");
System.out.println("===============================================\n");
if(order.equals(ADD_SERVER))
servers.add(serverURI);
else if(order.equals(REMOVE_SERVER))
servers.remove(serverURI);
}
}
I have done this code, however, the servers on the other side don't seem to receive anything. Therefore I have 2 questions:
Am I doing something wrong?
Is there a better way to send simple message using Camel?
MyApp_A does NOT send any messages. You need to send a message to the direct endpoint to start the route.
You can also change direct to a timer component to have it trigger every X second etc.
Added latest comment as requested:
yes and the direct route is also running. Its just that to send a
message to direct, you need to do that using Camel. direct is an
internal Camel component for sending messages between its endpoint
(routes). To send a message to it, you can use the producer template.
See chapter 7, section 7.7 in the Camel in Action book.

Categories