Mule invoke message processor - spring

I came across "invoke" element (link) that can be used to invoke java methods from inside the flow. It seemed a perfect solutions for me, since I don't want to use Callable and entry points resolvers and I want to pass extra parameter to the method.
<invoke object-ref="yourBean"
method="yourMethod"
methodArguments="#[message.inboundProperties['inboundPropertyName']]" />
<set-property propertyName="outboundPropertyName"
value="#[payload]" />
I did something like the code above. My question is: how should I create "yourBean"?
I tried to create:
<spring:bean name="yourBean" class="class"/>
but got:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'yourBean' is defined
I also have other beans defined in my tag, but others seemed to work. Does anyone have similar issue?
EDIT:
When I deleted other beans definitions besides "yourBean" everything started to work, but when I add more beans definitions I got following error when I tried to use "yourBean":
Exception stack is:
1. null (java.lang.NullPointerException)
org.mule.processor.InvokerMessageProcessor:280 (null)
2. null (java.lang.NullPointerException). Message payload is of type: String (org.mule.api.MessagingException)
org.mule.execution.ExceptionToMessagingExceptionExecutionInterceptor:35 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/MessagingException.ht
ml)
--------------------------------------------------------------------------------
Root Exception stack trace:
java.lang.NullPointerException
at org.mule.processor.InvokerMessageProcessor.transformArgument(InvokerMessageProcessor.java:280)
at org.mule.processor.InvokerMessageProcessor.evaluateArguments(InvokerMessageProcessor.java:200)
at org.mule.processor.InvokerMessageProcessor.process(InvokerMessageProcessor.java:164)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
********************************************************************************
Which for me seems like the "yourBean" is not instantiated. But why?
My bean definitions:
<bean id="yourBean" class="com.example.BpmService"/>
<bean id="StatusUpdateContext" class="javax.xml.bind.JAXBContext" factory-method="newInstance">
<constructor-arg>
<array>
<value type="java.lang.Class">
com.example.OrderStatusUpdate
</value>
</array>
</constructor-arg>
</bean>

EDIT: Ok, id/name both some to work for Mule invoke. I have had no problems using invoke with multiple bean definitions. This sounds more like an issue with your beans, which you are not sharing, and/or Spring than Mule invoke.

Related

Rest DSL path typing to a POJO, can I register the POJO as a Bean and call the id to reference it?

This is a very low impact "issue" and I'm just looking out to learn something new about Camel and how it works.
I'm working to build a springboot service using camel context routes, and while it's working fine (so far), I'd like to set the rest path type to a class without having to explicitly call the package location, and instead, reference the class as a bean and use it's id to call it.
I have defined a camelContext, I have the rest, restConfigurations and the necessary http methods (get, post, put...), here's a mock of what I have right now.
I've registered a ServiceImpl class as a bean and have given it the id Service. In the Route, I can call the bean using a ref to it's id, and select the method that I want.
<beans xmlns ...>
...
<!-- start services -->
<bean class="com.daniel.rest-project.services.ServiceImpl" id="Service"/>
<!-- end services -->
...
<camelContext id="context" xmlns="http://camel.apache.org/schema/spring">
<rest bindingMode="au <!-- <to id="to-7b30ace2-b26d-4301-8bb0-ef007d566ab3" uri="direct:501"/> -->to" enableCORS="true" id="rest">
...
<post consumes="application/json" id="newTask" produces="application/json" uri="/task" type="com.daniel.rest-project.vo.TaskVO">
<description>New Task Object</description>
<param description="Task Object Body." name="body" required="true" type="body"/>
<to uri="direct:newTask"/>
</post>
...
</rest>
...
<route id="route-task-new">
<from id="from-task-new" uri="
<bean id="to-task-new" method="newTask" ref="Service"/>
</route>
...
</camelContext>
</beans>
Now, I've actually got implemented around 150 routes (project is pretty big) and I'm tired of having to declare the rest post type, right now it's using something not really similar to com.daniel.rest-project.vo..
There's a ton of classes that are named similarly and due to internal reasons, best practices are scarce, naming conventions are weird, there's more than one package I need to go to, etc.
It would feel a lot more organized to just define all the objects I need at the start of the XML file and reference them as I go, instead of having to state the whole way there every time, so, is there a way I can define those classes at the start, just like the services, and tell the post rest path to use the id of the bean, instead of having to give it the whole class location? There must be a way and I'm sure I've just been blind enough not to see it in the documentation for the past weeks.
Here's one of the methods I've tried thus far:
<!-- start classes -->
<bean class="com.daniel.rest-project.vo.TaskVO" id="TaskVO"/>
<!-- end classes -->
and then, tell the path to do something like this
<post consumes="application/json" id="newTask" produces="application/json" uri="/task" type="TaskVO">
<description>New Task Object</description>
<param description="Task Object Body." name="body" required="true" type="body"/>
<to uri="direct:newTask"/>
</post>
Which resulted in the following error:
Caused by: org.apache.camel.FailedToCreateRouteException: Failed to create route newTask: Route(newTask)[[From[rest:post:/task?routeId=newTask&... because of java.lang.ClassNotFoundException: TaskVO
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:209)
at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:1143)
..........
Caused by: org.apache.camel.RuntimeCamelException: java.lang.ClassNotFoundException: TaskvO
at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1830)
at org.apache.camel.impl.DefaultRouteContext.commit(DefaultRouteContext.java:206)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1307)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:204)
TaskVO implements Serializable, and only has one, empty, public constructor, followed by all of it's getters and setters.

Spring Core - Alias and Ids

I started learning Spring today and came across a strange behavior, so seeking any expert help on the behavior. I defined a bean with an id and verified the bean is available via ApplicationContext and Bean factory. Later created more beans with different IDs but created one alias with the exact same Id of the First Bean (xml snippet below)
<!-- Beans -->
<bean id="wolf" class="com.badwolf.spring.SpringWolf">
<constructor-arg type="java.lang.String" value="wolfy" />
<constructor-arg index="1" value="20" />
</bean>
<bean id="sweetWolf" class="com.badwolf.spring.SweetWolf" />
<!-- Aliases -->
<alias name="sweetWolf" alias="wolf" />
Now in the implementation class when getting the bean using "wolf" then getting the bean associated with the "Alias". Is this intended behavior of Spring? Do Aliases take precedence over the bean definition (which seems to be the core of Spring)?
What if my project is split into teams and on integration someone has used the same id and alias and all of a sudden a portion of functionality stops working.
Firstly, there are three Spring bean terminology here, name, alias, and id.
Spring bean can have multiple names, aliases, while it can only have one unique id.
Below is reference from Spring doc
Every bean has one or more identifiers. These identifiers must be unique within the container that hosts the bean. A bean usually has only one identifier, but if it requires more than one, the extra ones can be considered aliases.
So in your case, bean with id of "wolf" has Spring-generated name ("springwolf"), while bean with id "sweetWolf" has two names, Spring-generated name("sweestWolf") and name alias("wolf") (spring-bean-names).
Related discussions for alias vs name (1, 2)
Related discussion for alias vs id (1)

spEL in Bean Definition File : How to get a value in an inline list?

Given an inline list from a properties file:
nameList=john,smith,junior
From the bean definition file, I want to always extract the first item in my list. In this case, I would extract 'john'.
Using Spring Framework 3.0.4.RELEASE, my bean property looks as follows:
<property name="mySingleName" value="${nameList}" />
This obviously gives me the whole list.
I attempted to use the following to get the first item in the name list, but was met with an error:
<property name="mySingleName" value="#{${nameList}[0]}" />
The error was:
BeanExpressionException:Exception parsing failed: After parsing a valid expression, there is still more data in the expression:'comma(,)'
I have looked in the Book "Spring In Action", and have looked at the Spring spEL Documentation on Springs website. I have also googled for a quite a bit. Unfortunately, most of the code examples are done in code and not on the spring definition files. Any assistance in this would be greatly appreciated.
In your example you are reading a "nameList" property as a String ( not as a java.util.List ). You would need to convert it to a list first:
<bean id="listOfNames" class="org.springframework.util.StringUtils" factory-method="commaDelimitedListToSet">
<constructor-arg type="java.lang.String" value="${nameList}"/>
</bean>
and then you can apply SPeL to it:
<property name="mySingleName" value="#{listOfNames[0]}" />
similar problem / solution here: Spring: Reading collections form property files

c3p0 useScatteredAquireTask Spring Bean

Hello I wan't to know how can I set up a bean so that it sets the ScatteredAquireTask to "True".
I've been trying:
<bean id="c3p0Props" class="com.mchange.v2.resourcepool.BasicResourcePool.ScatteredAcquireTask" >
<property name="USE_SCATTTERED_ACQUIRE_TASK" value="true" />
</bean>
I also tried ...resourcepool.experimental.useScatteredAcquireTask... didn't worked. I'm not sure how can I set this on spring. I'm using 0.9.1.2, can't go to 0.9.2.prep1 at the moment. Thanks.
That's because USE_SCATTTERED_ACQUIRE_TASK isn't a property of the ScatteredAcquireTask class (i.e. there's no method called setUSE_SCATTTERED_ACQUIRE_TASK), it's an internal static field of the class that's not accessible to Spring.
You're not going to be able to set that values in a Spring bean defintion, you need to find out how to influence that value by some other means.

Can I use a property placeholder with Spring EL?

Before upgrading to Spring 3 I had this in my applicationContext.xml file:
<bean class="com.northgateis.pole.ws.PolePayloadValidatingInterceptor">
<property name="validateRequest" value="${validateRequest}" />
<property name="validateResponse" value="${validateResponse}" />
</bean>
where ${validateRequest) and ${validateRequest) refer to properties that may or may not be defined in my properties file.
In Spring 2, if these proeprties were not present in the properties file the setters on the bean were not called and so the defaults hard-coded in PolePayloadValidatingInterceptor were used.
After upgrading to Spring 3, it seems the behaviour is different: If the properties are not present in the properties file I get the following exception:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'annotationMapping' defined in class path resource [com/northgateis/pole/ws/applicationContext-ws.xml]: Could not resolve placeholder 'validateRequest'
at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:272)
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:75)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:640)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:615)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:405)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:272)
I tried dabbling with Spring EL but the following doesn't seem to work:
<bean class="com.northgateis.pole.ws.PolePayloadValidatingInterceptor">
<property name="validateRequest" value="${validateRequest?:true}" />
<property name="validateResponse" value="${validateResponse?:false}" />
</bean>
The value after the Elvis operator is always used, even when the properties are defined in the proeprties file. Interesting that the syntax is accepted.
Any suggestions?
It looks like Spring 3's handling of default values with the Elvis operator was rather broken. This has apparently been fixed (see SPR-7209) in the fresh-out-of-the-oven Spring 3.0.3, and the correct syntax should be the rather baroque:
#{${validateRequest}?:true}
There's no need for Spring EL for setting a default value for a missing property when resolving it with a placeholder configurer. Simply use ${validateRequest:true}. The "Elvis operator" is not concerned with resolving placeholders, it just relies on whatever input the placeholder configurer provides.
See SPR-4785.

Resources