why can't i write this camel route? - apache-camel

from("e1")
.split()
.method("bean", "m1")
.to("e2")
.end()
.split()
.method("bean", "m2")
.to("e3");
The compiler complains about the 2nd to. The reason is that for some reason, it thinks the second split returns ExpressionCaluse rather than ExpressionClause<SplitDefinition>, which causes the following method return type to be Object rather than SplitDefinition.

I tried it in Eclipse, and first I got the same result as you (with the eclipse code completion showing an error). Then I rewrote the route (e.g. splitting it up with assignments to
ProcessorDefinition pd = from("e1")....
pd.split()...
Then, back to the original code, so finally Eclipse got the idea correct and the error marker disappeared. I don't know if you was trying eclipse too?
#Override
public void configure() throws Exception {
from("e1")
.split()
.method("bean", "m1")
.to("e2")
.end()
.split().method("bean", "m2")
.to("e3");
}
I mean, it should work. The signature of split() in ProcessorDefinition is correct:
public ExpressionClause<SplitDefinition> split()
I guess this is a glitch somewhere in my dev. env. and probably yours too.. or something. Odd, anyway.

Related

Camel split with jsonPath having no results

In my camel route I want to send part of the output (json path is $._attachments) to another endpoint. This works. However, in a few corner cases it appears some of the json objects do not have this element. I guessed this would result in a CamelSplitSize being 0, and tried to test on that; however, the DefaultErrorHandler seems to kick in, rather than completing the route. What am I doing wrong here?
public class DiscoverAttachmentsFromCouchResponseRoute extends RouteBuilder {
#Override
public void configure() throws Exception
{
JsonPathExpression jsonPathExpression = new JsonPathExpression("$._attachments");
Predicate no_attachments = header("CamelSplitSize").isEqualTo("0");
errorHandler(
deadLetterChannel("broker1:queue:dlq.jsonobject.queue")
.maximumRedeliveries(3)
);
from("broker1:queue:input.jsonobject.queue")
.routeId("DiscoverAttachmentsFromCouchResponseRoute")
.threads(2)
.split(jsonPathExpression)
.marshal().json(JsonLibrary.Jackson,true)
.to("broker1:queue:with.attachments.queue")
.choice()
.when(no_attachments)
.log("No attachments found.")
.to("broker1:queue:no.attachments.queue")
.otherwise()
.log("A grand total of '${header.CamelSplitSize}' attachments were found.")
.endChoice();
}
}
Edit somethimes the answer is too obvious... I was looking at the wrong place. Why do I always seem to find an answer a few minutes after I asked it? Am direly in need of a rubber duck.
I changed the route a bit; Rather than handling the issue after the split, I should do it prior to the split:
.choice()
.when().jsonpath("$._attachments", true)
.to("vm://withattach")
.otherwise()
.to("vm://withoutattach")
.endChoice();
Then I only needed two new consumers from the vm://* and do the logic there. This seems to work. In jsonpath I can skip the exception which I cannot do in the JsonPathExpression nor in the split();

Camel doesn't retrieve SQS messages attributes

Here is the route:
from("aws-sqs://myQueue?accessKey=RAW(xxx)&secretKey=RAW(yyy)&deleteAfterRead=false")
.log("Attributes: ${header.CamelAwsSqsAttributes}")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Map<String, String> messageAttributes = (Map<String, String>) exchange.getIn().getHeader("CamelAwsSqsAttributes");
...
}
});
The .log() shows an empty map as well as if I print messageAttributes from the processor.
I also tried with the header "CamelAwsSqsMessageAttributes" instead of "CamelAwsSqsAttributes" but still nothing.
I see the attributes from the AWS console though.
By the way I get the message body, and I use Camel 2.15
I figured it out, here is an example to get queue attributes and message attributes:
main.bind("sqsAttributeNames", Collections.singletonList("All"));
main.bind("sqsMessageAttributeNames", Collections.singletonList("All"));
Or add those objects to the registry if you don't use org.apache.camel.main.Main
Then:
from("aws-sqs://myQueue?accessKey=RAW(xxx)&secretKey=RAW(yyy)&deleteAfterRead=false&attributeNames=#sqsAttributeNames&messageAttributeNames=#sqsMessageAttributeNames")
Of course you can replace Collections.singletonList("All") with the list of attributes you need if you don't want all of them.
I faced the same issue. When I am using camel-aws 2.16.x and I have my endpoint configured as follow
from("aws-sqs://myQueue?...&messageAttributeNames=#sqsMsgAttributeNames")
.to(...)
Then I have defined a Collection of String in my spring configuration file
#Bean
public Collection<String> sqsMsgAttributeNames() {
return Arrays.asList("Attr1", "Attr2");
}
Above settings work fine but ever since I upgraded to camel-aws 2.17.3. It no longer works. As mentioned in Camel SQS Component, collection of string no longer will be supported for messageAttributeNames and it should be a String with attributes separated by comma.
Note: The string containing attributes should not contain any white
spaces otherwise camel-aws component will only read the first
attribute. I went through the pain to debug on this. Besides, setting the
attribute value to be "All" does not work for me, none of the message
attributes will be read.
Below is the changes I made that allowed camel-aws's SqsConsumer to work again:
#Bean
public String sqsMsgAttributeNames() {
return String.format("%s,%s", "Attr1", "Attr2");
}
It is not an issue of Camel. It can be the default behavior of SQS or aws-java-sdk-core library.
As a quick solution this aws-sqs URL can be used
aws-sqs://myQueue?<other attributes here>&attributeNames=All
Keep in mind that localstack can work well without attributeNames parameter, unlike SQS.

Use a file as input to unit tests

I think I have seen an elegant way to use a file as input for a unit test in apache camel but my google skills are failing me.
What I want is instead of:
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
<snip>...long real life xml that quickly fills up test files.</snip>";
template.sendBody("direct:create", xml);
What I think I have seen is something like
template.sendBody("direct:create", someCamelMetod("/src/data/someXmlFile.xml"));
Anybody knows where/if this is documented?
Edit:
Cross posted to http://camel.465427.n5.nabble.com/Use-a-file-as-input-to-unit-tests-td5722069.html
What I ended up doing was just creating a
private void readFile(String fileName) throws ... function
Still interested if anyone knows a nicer way though.
If I understand your question correctly you want to send an xml file as input to the route you want to test. My solution would be to use the adviseWith strategy which is a part of the Camel test support. Read about it here: http://camel.apache.org/testing.html
So, say that the route under test is something like this:
from("jms:myQueue")
.routeId("route-1")
.beanRef(myTransformationBean)
.to("file:outputDirectory");
in your test you can send xml into this route by replacing this from a file polling consumer.
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceRouteFromWith("route-1", "file:myInputDirectory");
}
});
context.start();
Then you can put your input xml file in the myInputDirectory and it will be picket up and used as input to the route.
Not really, you have to do some minor work yourself. You know, reading a text file isn't that simple since you might want to know the encoding. In your first case (inline string), you're always using UTF-16. A file can be whatever, and you have to know it, since it won't tell you what encoding it is. Given you have UTF-8, you can do something like this:
public String streamToString(InputStream str){
Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A");
if (scanner.hasNext())
return scanner.next();
return "";
}
// from classpath using ObjectHelper from Camel.
template.sendBody("direct:create", streamToString(ObjectHelper.loadResourceAsStream("/src/data/someXmlFile.xml")));

Mocking a final method with PowerMock + EasyMock

I'm trying to mock a call to the final method ResourceBundle.getString(). With PowerMock 1.4.12 and EasyMock 3.1, the call is not being mocked; instead, the "real" method is called.
My test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ResourceBundle.class)
public class TestSuite {
#Before
public void setUp() throws Exception {
ResourceBundle resourceBundleMock = PowerMock.createNiceMock(ResourceBundle.class);
expect(resourceBundleMock.getString(BundleConstants.QUEUE)).andReturn("Queue");
PowerMock.replay(resourceBundleMock);
beanBeingTested.setMessages(resourceBundleMock);
}
...
}
Code in BeanBeingTested:
private ResourceBundle messages;
...
String label = messages.getString(BundleConstants.QUEUE);
Error message:
java.util.MissingResourceException: Can't find resource for bundle $java.util.ResourceBundle$$EnhancerByCGLIB$$e4a02557, key Queue
at java.util.ResourceBundle.getObject(ResourceBundle.java:384)
at java.util.ResourceBundle.getString(ResourceBundle.java:344)
at com.yoyodyne.BeanBeingTested.setUpMenus(BeanBeingTested.java:87)
When I step through the test case, the debugger shows the type of beanBeingTested.messages as "EasyMock for class java.util.ResourceBundle", so the mock is injected correctly. (Also, there's no error on the call to getString() within the expect() call during set up).
With a plain mock instead of a nice mock, I get the following error:
java.lang.AssertionError:
Unexpected method call handleGetObject("Queue"):
getString("Queue"): expected: 1, actual: 0
Any idea what I'm doing wrong?
Thanks.
You are creating an instance using EasyMock. Instead, when working with static methods, you must mock the class (using PowerMock).
It should work like that (tested with EasyMock 3.0 and PowerMock 1.5, though):
#RunWith(PowerMockRunner.class)
#PrepareForTest(ResourceBundle.class)
public class TestSuite {
#Before
public void setUp() throws Exception {
// mock the class for one method only
PowerMock.mockStaticNice(ResourceBundle.class, "getString");
// define mock-behaviour on the class, when calling the static method
expect(ResourceBundle.getString(BundleConstants.QUEUE)).andReturn("Queue");
// start the engine
PowerMock.replayAll();
}
}
(I'm aware this question is a few months old, but it might help others, though)
Try using:
#PrepareForTest({ResourceBundle.class, BeanBeingTested.class})
With only ResourceBundle in the PrepareForTest the mock will work when called directly from your unit test method, but when called from BeanBeingTested you get the real method being used.
Powermock documentation is lacking in this area.
Why bother mocking the call to the resource bundle? Generally, I try to avoid mocking the nuts and bolts of java, such as ArrayList, Date, etc. Resource bundles (and MessageFormat.format()) more or less fall into the same category for me. They generally operate on strings which are fundamentals, and if these things are broken or changing their behavior enough to break a test it's definitely something I want to know :)
Just let them grab the string (which presumably is about to be set in the UI, perhaps after . Don't bother to assert the value returned since you don't want edits to the bundle to break your test. If the string gets set on a mock UI component, This is a good place for anyObject(String.class) which correctly expresses the fact you (probably) don't actually care about the specific string displayed.
I also consider it a benefit when the test fails due to a missing message key. THAT I want to know.

During suite tests EasyMock says 0 matchers expected 1 recorded

So I've been using EasyMock's class extension for a while now. All of a sudden I'm getting this exception, but only when I run the entire test suite:
java.lang.IllegalStateException: 0 matchers expected, 1 recorded.
at org.easymock.internal.ExpectedInvocation.createMissingMatchers(ExpectedInvocation.java:42)
at org.easymock.internal.ExpectedInvocation.<init>(ExpectedInvocation.java:34)
at org.easymock.internal.ExpectedInvocation.<init>(ExpectedInvocation.java:26)
at org.easymock.internal.RecordState.invoke(RecordState.java:64)
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:24)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:56)
at org.easymock.classextension.internal.ClassProxyFactory$1.intercept(ClassProxyFactory.java:74)
at com.protrade.soccersim.data.emulator.matrix.PositionCategoryMatrix$$EnhancerByCGLIB$$c5298a7.getPossession(<generated>)
at com.protrade.soccersim.data.emulator.stats.team.PossessionCalculatorUnitTest.testDeterminePossessionHomeWin(PossessionCalculatorUnitTest.java:45)
The code involved is this little beauty (trimmed a bit):
#Before
public void setUp() throws Exception {
homeTeam = createMock( PositionCategoryMatrix.class );
awayTeam = createMock( PositionCategoryMatrix.class );
...
}
#Test
public void testDeterminePossessionHomeWin() {
expect(homeTeam.getPossession()).andReturn( 0.15151515 );
expect(awayTeam.getPossession()).andReturn( 0.01515152 );
replay( homeTeam, awayTeam );
...
}
The exception is being thrown on the first expect. And it really doesn't make sense. It says it's getting a matcher, but the method doesn't even take an argument. And odd enough it's only during test suites! I'm creating a new mock in the #Before, so it shouldn't be inheriting anything from somewhere else (not that some other method would have a matcher on it)
So, any ideas?
I was sick and tired of seeing this with each new legacy code base with EasyMock I had to work with. Write one new EasyMock test by the book and all of the sudden random tests start failing because of Matchers never captured. So I went looking how EasyMock stores those Matchers. It makes use of a final class LastControl, in that class are a few threadlocals where different things get stored. One of those was for the Matchers. Luck has it that there is a static method on there to pull all the Matchers from the threadlocal that where still on there. So this gave me this idea (with help of a collegue, thanks Sven, he wanted credit)
/**
* Base class to make sure all EasyMock matchers are cleaned up. This is not pretty but it will work
*
* #author N069261KDS
*
*/
public class BaseTest {
#Before
public void before(){
LastControl.pullMatchers();
}
#After
public void after(){
LastControl.pullMatchers();
}
}
Basicly let your test that fail with the Matchers error extend from this class and you'll be sure the Matchers are cleaned. Note this IS A WORKAROUND. The offending tests should have been written right in the first place. But if you have to wade through 5000+ tests , this is the lesser of two evils. I hope this will help people out !
While it's true that this can be a spurious message resulting from a "silly" EasyMock bug, it is also very likely to be due to invalid usage of the EasyMock API. In my case the message arose from this JUnit 3.8 test (and like you, this only happened when I ran my entire suite of tests, and only via Maven, not Eclipse):
public void testSomething() {
// Set up
MyArgumentType mockArg = (MyArgumentType) EasyMock.anyObject(); // bad API usage
// Invoke the method under test
final String result = objectUnderTest.doSomething(mockArg);
// Verify (assertions, etc.)
...
}
Instead of using anyObject(), I should have used createMock(MyArgumentType.class) or one of its variants. I don't know what I was thinking, I've written millions of these tests and used the API correctly.
The confusing bit is that the test that fails with the "wrong number of matchers" message isn't necessarily (or ever?) the one in which you used the API wrongly. It might be the first test executed after the buggy one that contains a replay() or verify() method, but I haven't verified this experimentally.
I had the same error message. I was (accidentally) using an isA() declaration in the call on the class under test
I.e.
classUnderTest.callStateChanged(calls, isA(LoggingOnlyListener.class));
when I meant:
classUnderTest.callStateChanged(calls, new LoggingOnlyListener());
And it was the test AFTER this one that failed every time.
I just ran into this problem, and I think I managed to figure it out. For me it was due the the previous test (that's in a different Class), where I was (incorrectly) using an EasyMock matcher in an Assert.assertEquals method.
It seems EasyMock couldn't complain about the extra matcher reported until the first expects methods was called.
I am running into a similar problem. From what I observed, even method returns are matched using Matchers. So if your first method fails for any reason, the matcher for the return match is still in the stack. That could be one reason why you are seeing 1 matchers recorded even when your method doesn't take any argument. Basically, the first method invocation never returned a value.
Which version of Easymock are you using?
I read a post about the release of v.2.5.2 and previuous versions might have a
dumb bug on the capture
try to use Easymock 2.5.2+

Resources