Default value for missing parameter in a Camel route - apache-camel

I have an application that uses Apache Camel with Active MQ. In my RouteBuilder class where I configure the routes, it is possible that a parameter may be missing from the in body. In that case, I would like to give it a default value.
Here is what I have now:
#Override
public void configure() throws Exception {
...
String invocation = "aMethod( ${body[context]}, ${body[aParam]}, ${body[param2]} )";
from(url).routeId(url).bean(bean, invocation);
}
In my case, param2 is a boolean which I would like to send forward to aMethod as false in case it doesn't exist. How can I do that?
I saw something here: http://camel.apache.org/simple.html#Simple-OGNLexpressionsupport where it says "You can also use the null safe operator (?.) to avoid NPE if for example the body does NOT have an address", but I can't figure how to write the invocation so I don't have an error in the specified case.

I believe you are looking to use a conditional similar to this:
from("")
.choice()
.when(header("myHeader").isNull())
.setHeader("myHeader").simple("false")
.end() //This might be endChoice can't remember syntax exactly
.bean(myBean, myMethod);
..revised for Object in the body
from("")
.processor( new Processor(Exchange exchange) {
MyObject obj = exchange.getIn().getBody(MyObject.class);
Boolean b = obj.getParam2();
if(b == null)
obj.setParam2(Boolean.FALSE);
}
.bean(myBean, myMethod);
NOTE: If your actual java class uses 'boolean' and not the 'Boolean' class wrapper, then all booleans when initialized are by default set to false. So if you have an object in your body and nobody has the the boolean it will default to false.

Related

pass object parameter to method in setHeader - camel

I am new to Camel and trying to find a way to pass object to method in SetHeader.
but i am getting an error,
org.apache.camel.language.bean.RuntimeBeanExpressionException: Failed to invoke method: getCustProcessDir('${header.CUST}') on null due to: org.apache.camel.component.bean.ParameterBindingException: Error during parameter binding on method: public java.lang.String CustDao.getCustProcessDir(Cust) at parameter #0 with type: class Cust with value type: class java.lang.String and value: Cust#199b87b5
at org.apache.camel.language.bean.BeanExpression.invokeOgnlMethod(BeanExpression.java:430) ~[camel-bean-3.3.0.jar:3.3.0]
at org.apache.camel.language.bean.BeanExpression.evaluate(BeanExpression.java:164) ~[camel-bean-3.3.0.jar:3.3.0]
codes:
fromF("file:C:/Users/a/Documents/Development/input/"
+ "?recursive=false&noop=true&delay=20000&readLockLoggingLevel=WARN&shuffle=true"
+ "&readLock=idempotent&idempotentRepository=#fileRepo&readLockRemoveOnCommit=true&readLockRemoveOnRollback=true&delete=true&moveFailed=%s"
, "C:/Users/a/Documents/Development/rejected/")
.routeId("fileMoveRoute")
.process(exchange -> {
exchange.getMessage().setHeader("Application_ID", appInfo.getInstanceId());
})
.threads(appInfo.getThreadCount())
.setHeader("CUST", method(CustDao.class, "getInboundCustWithfile('${header.CamelFilePath}')"))
.setHeader("PROCESS_DIR", method(CustDao.class, "getCustProcessDir('${header.CUST}')"))
...
public String getCustProcessDir(Cust cust) {
return appInfo.getDir() + cust.getCustprofid() + "/hold/";
}
public class Cust {
private int custid;
private String custprofid;
...
}
first setHeader("CUST"..) works and i believe that Header("CUST") has returned object values.
but I am not sure how it is stored in Camel. I've tried to find them from variables window during debug but was not able to find them. too many variables to look into... Where can i find this Header values during debug?
and how can i pass object values to the method?
.setHeader("PROCESS_DIR", method(CustDao.class, "getCustProcessDir('${header.CUST}')"))
or is there a better way to pass/handle object during routing?
Thanks,
I guess the single quotes around expressions like ${header.CUST} are the problem because the RuntimeBeanExpressionException complains that it receives the String Cust#199b87b5 instead of a Cust object.
Have a look at the Camel docs for Bean binding. There are no single quotes around method parameter expressions.
About the storage of header variables: they are stored on the Message object of Camel.
Exchange -> Message -> Headers
Exchange -> ExchangeProperties

How does this point cut work in Spring AOP?

I am learning spring AOP and got the following pointcut expression from one of the examples in AOP section of spring-framework-reference: Proceeding with Arguments
#Around("execution(List<Account> find*(..)) && " +
"com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +
"args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
String accountHolderNamePattern) throws Throwable {
String newPattern = preProcess(accountHolderNamePattern);
return pjp.proceed(new Object[] {newPattern});
}
As per my understanding this pointcut expression is incorrect as the and condition would never satisfy. Am I missing something very obvious?
Also I attempted the following (ref):
#Component
public class TestBean {
public void testMethod() {
}
}
Aspect class
#Before("com.test.bean.TestBean.testMethod()")
public void testAspect() {
System.out.println("Worked");
}
which failed with
Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut testMethod
at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:319)
There are syntax errors in both pointcuts. You cannot just write a method signature without execution() around it if you want to intercept a method. Also please do not forget to specify a return type or at least * for any return type.
The first pointcut would be syntactically correct like this:
#Around(
"execution(List<Account> find*(..)) && " +
"execution(* com.xyz.myapp.SystemArchitecture.inDataAccessLayer()) && " +
"args(accountHolderNamePattern)"
)
But It would still not match any method because of && (logical AND) requires both method executions to be true at the same time, which is impossible. Either find*(..) or inDataAccessLayer() is being executed, never both at the same time. You could work with || (logical OR) instead, but then the args() matcher would be ambiguous and thus the pointcut would be invalid again. I cannot tell you how to fix your pointcut unless you explain to me what you want to achieve. The way it is now it does not make any sense.
As for the second pointcut, you can fix it like this:
#Before("execution(* com.test.bean.TestBean.testMethod())")
The error message you quoted means that the AspectJ parser thinks you are referring to a named pointcut instead of your intended method execution.

How to debug serializable exception in Flink?

I've encountered several serializable exceptions, and I did some searching on Flink's internet and doc; there are some famous solutions like transient, extends Serializable etc. Each time the origin of exception is very clear, but in my case, i am unable to find where exactly it is not serialized.
Q: How should i debug this kind of Exception?
A.scala:
class executor ( val sink: SinkFunction[List[String]] {
def exe(): Unit = {
xxx.....addSink(sinks)
}
}
B.scala:
class Main extends App {
def createSink: SinkFunction[List[String]] = new StringSink()
object StringSink {
// static
val stringList: List[String] = List()
}
// create a testing sink
class StringSink extends SinkFunction[List[String]] {
override def invoke(strs: List[String]): Unit = {
// add strs into the variable "stringList" of the compagin object StringSink
}
}
new executor(createSink()).exe()
// then do somethings with the strings
}
The exception is:
The implementation of the SinkFunction is not serializable. The
object probably contains or references non serializable fields.
Two suspicious points that I found:
The instance of StringSink is passed into another file.
In the class of StringSink, it uses a static variable stringList
of its compagin object.
I faced similar problems. It used to take longtime to find out what member/object is not serializable. The exception logs are not really helpful.
What helped me is the following JVM option, which enables more details in exception trace.
Enable this option...
-Dsun.io.serialization.extendedDebugInfo=true
My first guess would be the you don't have a no argument constructor in StringSink
Rules for POJO types Clipped from here
Flink recognizes a data type as a POJO type (and allows “by-name” field referencing) if the following conditions are fulfilled:
The class is public and standalone (no non-static inner class)
The class has a public no-argument constructor
All non-static, non-transient fields in the class (and all superclasses) are either public (and non-final) or have a public getter- and a setter- method that follows the Java beans naming conventions for getters and setters.
Just add a no argument constructor and try again
class StringSink extends SinkFunction[List[String]] {
public StringSink() {
}
#override def invoke(strs: List[String]): Unit = {
// add strs into the variable "stringList" of the compagin object StringSink
}
}

Spring data mongo ConditionalGenericConverter empty TypeDescriptor

I'm trying to implement a somewhat general converter, which transforms the data based on a given annotation. Say I want to transform these annotated strings in any matter.
All is well, until the code hits my converter's "matches" method. The "sourceType" I'm getting is always stripped out of all of the useful information. Has anyone had any success with such a setup, or am I missing something?
public class TestStringWriteConverter implements ConditionalGenericConverter {
#Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.hasAnnotation(GivenAnnotation.class)) {
//->never gets executed, because sourceType is being stripped out of it's useful infos
}
I followed the problem to MappingMongoConverter from this package org.springframework.data.mongodb.core.convert
protected void writeInternal(Object obj, final DBObject dbo, MongoPersistentEntity<?> entity) {
//...
if (null != propertyObj) {
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, dbo, prop);
} else {
// I always end up here, which is correct but the whole prop object is being omitted in favor of the getFieldName() property
writeSimpleInternal(propertyObj, dbo, prop.getFieldName());
}
}
}
The spring versions I'm using:
<spring.version>3.2.5.RELEASE</spring.version>
<spring.data.version>1.3.2.RELEASE</spring.data.version>
Any help is much appreciated.
I think you misunderstand what sourceType.hasAnnotation(…) actually returns. As the name suggests, it inspects the type for annotations. So for a given type like this:
#MyAnnotation
class Foo { }
it would allow you to find #MyAnnotation. However you are writing about "annotated strings". I assume you mean something like:
class Bar {
#MyAnnotation
String property;
}
This is not a type annotation and the Converter API is not meant to cover such cases. If you think supporting such scenarios would be worthfile, please file a JIRA ticket.

Event subscription with prism giving methodaccess exception

I have an event that I am subscribing to in a View Model. The event subscription is done in the constructor of the view model which is created via unity.
What I found is if I subscribe as:
showViewAEvent.Subscribe(ShowViewAHasBeenRequested) or showViewAEvent.Subscribe(ShowViewAHasBeenRequested, False) I get the following error:
// {System.MethodAccessException: ModuleA.Views.ModuleAViewModel.ShowViewAHasBeenRequested(Boolean)
//at System.Delegate.BindToMethodInfo(Object target, RuntimeMethodHandle method, RuntimeTypeHandle methodType, DelegateBindingFlags flags)
//at System.Delegate.CreateDelegate(Type type, Object firstArgument, MethodInfo method, Boolean throwOnBindFailure)
//at System.Delegate.CreateDelegate(Type type, Object firstArgument, MethodInfo method)
//at Microsoft.Practices.Composite.Events.DelegateReference.TryGetDelegate()
//at Microsoft.Practices.Composite.Events.DelegateReference.get_Target()
//at Microsoft.Practices.Composite.Events.EventSubscription`1..ctor(IDelegateReference actionReference, IDelegateReference filterReference)
//at Microsoft.Practices.Composite.Presentation.Events.CompositePresentationEvent`1.Subscribe(Action`1 action, ThreadOption threadOption, Boolean keepSubscriberReferenceAlive, Predicate`1 filter)
//at Microsoft.Practices.Composite.Presentation.Events.CompositePresentationEvent`1.Subscribe(Action`1 action, ThreadOption threadOption, Boolean keepSubscriberReferenceAlive)
//at Microsoft.Practices.Composite.Presentation.Events.CompositePresentationEvent`1.Subscribe(Action`1 action, Boolean keepSubscriberReferenceAlive)
//at ModuleA.Views.ModuleAViewModel..ctor(IEventAggregator eventAggregator, IRegionManager regionManager)
//at BuildUp_ModuleA.Views.ModuleAViewModel(IBuilderContext )
//at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context)
//at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)
//at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)}
But, if I set the flag to true on the event subscription, I do not get the error.
As I am new to prism, I am still trying to work out if I am creating the subscription in the right place.
JD.
This is a known issue fully documented here :
http://compositewpf.codeplex.com/WorkItem/View.aspx?WorkItemId=4925
Bug in CompositePresentationEvent<>.Subscribe() prevents weak event references Title is required
Description Description is required
OVERVIEW:
The Subscribe() method of this class is documented as creating WeakReferences by default or when specified as keepSubscriberReferenceAlive=false in the overloads that include that parameter.
DETAILS:
This behavior is only correctly observed when a filter delegate is supplied. In all other cases (and all overloads of the Subscribe() method), a strong reference is created - regardless of the documented default and regardless of any supplied value for the keepSubscriberReferenceAlive parameter.
The source of this bug can be found in the following overload of this method:
CompositePresentationEvent.Subscribe(Action action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate filter)
In this method, the "filter" parameter is inspected. If the filter is not null, then processing continues correctly. However, if this parameter is null then a new pass-through delegate (always returns true) is created and used for the filter. The bug is that the DelegateReference object that is created from this pass-through delegate has the keepReferenceAlive parameter hard-coded to the value "true". This value should not be hard-coded, and instead the incoming parameter keepSubscriberReferenceAlive should be passed.
WORKAROUND:
There is a simple workaround for this issue. When registering a subscription, you should always use the verbose overload stated above, and always supply a filter delegate. Never pass "null" for the filter parameter. If the subscription should not be filtered, then a pass-through filter delegate should be used when a weak event reference is desired (the typical scenario):
EventAggregator.GetEvent().Subscribe(MyHandler, ThreadOption.PublisherThread, false, (dummy) => true);
There is NO workaround for the following abbreviated overloads, and these should not be used until the underlying bug has been patched:
CompositePresentationEvent.Subscribe(Action action)
CompositePresentationEvent.Subscribe(Action action, ThreadOption threadOption)
CompositePresentationEvent.Subscribe(Action action, bool keepSubscriberReferenceAlive)
CompositePresentationEvent.Subscribe(Action action, ThreadOption threadOption, bool keepSubscriberReferenceAlive)
Upon further research I found this thread:
http://compositewpf.codeplex.com/Thread/View.aspx?ThreadId=57362
I didn't realize that the Subscribe call was actually in the CallStack or I would have realized this earlier. Here's an excerpt:
Silverlight does not support weak
references to lambda expressions or
anonymous delegates. Therefore, the
filter parameter must be a separate
method if you are targeting
Silverlight.
Are you trying to use a lambda as your handler for that subscription? If so, it looks like all you need to do is use a real method.
EventService.GetEvent<GenericEvent<string>>().Subscribe(YourAction)
.....
public void YourAction(string topic)
{
if(topic == "something")
{
// more code
}
}
Is the ShowViewAHasBeenRequested method public? If not, it will not be reachable by the invoking code.
Has your ViewModel gone out of scope when the event is published? Passing True in the Subscribe method creates a strong reference, which keeps the Subscriber from getting GC'd after it has gone out of scope. Understand that doing this will create a memory leak, as every ViewModel you instanciate will continue to live on and respond to those published events.
I ran into this same problem when setting up some Subscriptions to events inside my Module's Initialize method - as far as I could tell nothing was holding a reference to my Module after it was done setting everything up.
I had exactly the same problem and solved it by making both the filter method and action method public as per user188067 and Konamiman's responses. i.e.
showViewAEvent.Subscribe(ShowViewAHasBeenRequested, ThreadOption.UIThread, false, ShouldHandleEvent);
public bool ShouldHandleError(object obj)
{
return true;
}
public void ShowViewAHasBeenRequested(object obj)
{
...
}

Resources