I am working currently on an API that is exposed by Mule ESB 3.5.0 (non-EE). This API accepts a XML file with accounts to be imported via HTTP and puts this task definition into a RabbitMQ queue. Another Mule flow is responsible for taking items from the queue one-at-a-time (thanks to processingStrategy="synchronous") and feeding them to the platform core. The queue is required as the core is able to process one-file-at-a-time.
The setup above is up & running smoothly. What I would like to achieve now, is to enable our customers to troubleshoot the integration by exposing an HTTPS endpoint, where import statuses will be available (identified by some GUID and SHA1 of the request).
I created a simple POJO component that handles the logic of adding the status updates, the method signature being:
void addStatus(final String guid, final String status)
I managed to invoke the method above by defining the bean as
<bean id="importStatusComponent" class="com.example.ImportStatusComponent" />
and invoking the java-component in the Mule flow with:
<invoke object-ref="importStatusComponent" method="addStatus"
methodArguments="#[flowVars.guid], Import started"
methodArgumentTypes="java.lang.String, java.lang.String" />
As we would like to expose this to customers and allow them to implement some programmatic checking of the status, I decided to change the status type to an enum-based dictionary ImportStatusEnum.
Unfortunately, I am unable to fed enum into MEL that goes into <invoke methodArgument=""> tag attribute.
Examples of what I have tried:
1) Arguments as two separate MEL expressions.
<configuration>
<expression-language>
<import class="com.example.ImportStatusEnum" />
</expression-language>
</configuration>
<invoke object-ref="importStatusComponent" method="addStatus"
methodArguments="#[flowVars.guid], #[ImportStatusEnum.STARTED]"
methodArgumentTypes="java.lang.String, com.example.ImportStatusEnum" />
2) Arguments as a single MEL expression.
<configuration>
<expression-language>
<import class="com.example.ImportStatusEnum" />
</expression-language>
</configuration>
<invoke object-ref="importStatusComponent" method="addStatus"
methodArguments="#[flowVars.guid, ImportStatusEnum.STARTED]"
methodArgumentTypes="java.lang.String, com.example.ImportStatusEnum" />
3) Fully qualified class names instead of imports (not shown here).
How to pass an enum value as method argument to invoke component in Mule? Any help will be highly appreciated :)
This one will work
<invoke object-ref="importStatusComponent" method="addStatus" methodArguments="#[flowVars.guid], #[com.example.ImportStatusEnum.STARTED]" methodArgumentTypes="java.lang.String, com.example.ImportStatusEnum" />
Related
I have the following configuration:
<int:channel id="responseXmlChannel">
<int:interceptors>
<int:wire-tap channel="responseXmlLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="responseXmlLogger"
logger-name="test.SyncResponseLogger" level="DEBUG"
expression="'message received, headers:' + headers + ' payload:' + payload"/>
I am trying to add some session attributes to be logged. Current user for example.
I was taking a look at the header-enricher approach, but I could not find a good example of adding session attributes to the header using it.
Is there a way to do what I am trying to do? Is there other approach besides using the headers to add custom attributes to be logged?
My goal is to have in the logs something like
current user: $UserName. Payload: $Payload
OK. So, what is your question then? How to use header-enricher? I'm not sure what is your "session" object to be precise with the answer, but header-enricher can be configured to call any arbitrary method on beans. See its docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#header-enricher.
The other approach is too deal with ThreadLocal. That's exactly what the RequestContextHolder in Spring Web does for RequestAttributes with its HTTP session access. Although you need to keep in mind that moving to a different thread with queue or executor channels in between will lose the current thread context and you won't have access to that ThreadLocal. So, headers in the particular message is not so bad choice to do.
SOAP WS with trivial characteristics:
- WSDL address:
http://<ip>:<port>/service/name?wsdl
- target method name:
getData(...)
Part of Spring-integration config ('spring-context.xml') for calling WS:
...
<int:chain input-channel="data" output-channel="stdout">
<!-- transform message to request -->
<int:transformer
ref="soapRequestTransformer"
method="createReq">
</int:transformer>
<!-- define the WS method to be called (getData) -->
<int:header-enricher>
<int:header
name="SoapAction"
value="http://<ip>:<port>/service/name/getData"/>
</int:header-enricher>
<!-- call WS -->
<int-ws:outbound-gateway uri="http://<ip>:<port>/service/name"/>
</int:chain>
...
After transformer (successful) i have request object:
request.getHeaders(): {sequenceNumber=1, correlationId=2bfb560c-
96ba-9c35-96dc-ba16104604b9, id=32f43a24-dee2-461c-a9b9-92c8ee37aaec,
sequenceSize=12, timestamp=1532509142590}
request.getPayload(): org.types.GetDataRequest#77409e4b
How to properly setup the spring-integration to correctly call the WS?
Problem is that WS is not called.
How does this problem look? We can't say anything without at least some stack trace.
Is the name of the WS method used correctly?
The SoapAction value is vendor-specific. You have to consult the WS provided to determine what and how should be presented in that header.
How are the parameters passed?
Not sure about nay parameter. Yes, you can send some query params as well, but doesn't look like that is your questions. Please, be more specific.
By the way, if you send some POJO (e.g. your org.types.GetDataRequest), you need to use a marshaling gateway instead of simple one:
<int-ws:outbound-gateway marshaller="someMarshaller" uri="http://<ip>:<port>/service/name"/>
Not sure, of course, if you have a good XSD-genarated model, based on the provided info in that WSDL...
I defined a service xml like this:
<service verb="targetExample">
<in-parameters><parameter name="testInput"/></in-parameters>
<out-parameters><parameter name="testOutput"/></out-parameters>
<actions>
<set field="testOutput" value="Input was: ${testInput}"/>
<log level="info" message="targetExample testOutput: '${testOutput}'"/>
</actions>
</service>
I was curious if there is a chance to call it without adding a transition to wrap it. I tried to respect the service naming ${path}.${verb}#${noun} but in vain, i cannot call it without defining the following transition.
<transition name="localExample">
<service-call name="moqui.example.ExampleServices.targetExample" web-send-json-response="true"/>
<default-response type="none"/>
</transition>
Also, I was also wondering if someone managed to implement a service in java instead of groovy (I read in the documentation that it is possible).
What I ask for is a working example of test.java file in the following definition:
<service verb="targetJavaExample" type="java"
location="component://example/src/orq/moqui/example/test.java">
<in-parameters><parameter name="testInput"/></in-parameters>
<out-parameters><parameter name="testOutput"/></out-parameters>
</service>
Any service may also be called from within any <actions> tags defined in the screen xml. e.g. The beginning of sections, the beginning of forms (<row-actions>), embedded in various files, or put in a file of their own and run like a script etc.
And within a service, when defining the "actions" invoked by the service, you can always drop down to Groovy/Java any time by using <script>... </script>. - That is always the case within XML Actions. (Or <script><![CDATA[... ]]></script> if you have have characters that might be interpreted as XML markup.)
Hope that helps.
I have an HTTP Inbound Gateway in my Integration Application, which I will call during some save operation. It's like this. If I have one product, I will call the API once, and if I have more than once, then I will call multiple times. The problem is, for single invoke, SI works just fine. But for multiple calls, request and response get messed up. I thought Spring Integration Channels are just like MQ's, but it is not?
Let me explain this. Let's say I have 2 products. First, I invoke SI for Product A and then for B. Response of A got mapped to request B! It happens all the time. I don't want to use some dirty hacks like wait for the first response to come and invoke again. This means the system has to wait for a long time. I guess we can do it in Spring Integration using task executor, but with all the basic samples out there, I can't find the right one. So please help me find out how can I fix this issue!
My Configuration is :
<int:channel id="n2iMotorCNInvokeRequest" />
<int:channel id="n2iMotorCNInvokeResponse" />
<int:channel id="n2iInvoketransformerOut" />
<int:channel id="n2iInvokeobjTransformerOut" />
<int:channel id="n2iInvokegatewayOut" />
<int-http:inbound-gateway id="i2nInvokeFromPOS"
supported-methods="GET"
request-channel="i2nInvokeRequest"
reply-channel="i2nInvokeResponse"
path="/postProduct/{Id}"
mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"
reply-timeout="50000">
<int-http:header name="Id" expression="#pathVariables.Id"/>
</int-http:inbound-gateway>
<int:service-activator id="InvokeActivator"
input-channel="i2nInvokeRequest"
output-channel="i2nInvokeResponse"
ref="apiService"
method="getProductId"
requires-reply="true"
send-timeout="60000"/>
<int:transformer input-channel="i2nInvokeResponse"
ref="apiTransformer"
method="retrieveProductJson"
output-channel="n2iInvokeRequest"/>
<int-http:outbound-gateway request-channel="n2iInvokeRequest" reply-channel="n2iInvoketransformerOut"
url="http://10.xx.xx.xx/api/index.php" http-method="POST"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<int:service-activator
input-channel="n2iInvoketransformerOut"
output-channel="n2iInvokeobjTransformerOut"
ref="apiService"
method="productResponse"
requires-reply="true"
send-timeout="60000"/>
The i2nInvokeFromPOS gateway is what we call from Web Application which is where all the products will be created. This Integration API will fetch that data, and post it to the backend system so that it will get updated to the other POS locations too!
Steps :
I will send the productId to i2nInvokeFromPOS.
apiTransformer -> retrieveProductJson() method will fetch the product details from DB based on the ID
Send the Request JSON to Backend system using http:outbound-gateway
Get the response from Backend and update the product status as uploaded in DB. Happens in apiService -> productResponse()
Once the response for A is received, all I'm getting is HTTP 500 Error for the Request B! But the Backend API is just fine.
The framework is completely thread-safe - if you are seeing cross-talk between different requests/responses then one (or more) of your components that the framework is invoking is not thread-safe.
You can't keep state in fields in, for example, code invoked from a service activator.
I currently need to utilize three vhosts for this application. I am only receiving messages over one as a consumer, and the others for RPC calls. Currently I am using CachingConnectionFactory that I have subclassed one for each virtual host. Then I am making each of those subclasses beans. I can then of course grab the connection factory to create the RabbitTemplate for the correct vhost instance.
I saw in the documentation about the AbstractRoutingConnectionFactory but wanted to know if there are any reasons I should refactor my currently working code. I want the most maintainable and perfomant solution, not the "easiest" one.
Thanks!
I am not sure why you felt it was necessary to subclass the CachingConnectionFactory you can simply declare multiple factories...
<rabbit:connection-factory id="default" host="localhost" />
<rabbit:connection-factory id="foo" host="localhost" virtual-host="/foo" />
<rabbit:connection-factory id="bar" host="localhost" virtual-host="/bar" />
Whether or not you using a routing connection factory (e.g. SimpleRoutingConnectionFactory) depends on your application needs. If you don't use it you would need 3 RabbitTemplates and decide which one to use at runtime.
With a routing connection factory, the RabbitTemplate can make the decision based on the message content with a send-connection-factory-selector-expression.
There's not really a lot of difference except the second decouples your application from the decision. For example, you can set a message header customerId before sending (or during message conversion if you're using a convertAndSend method) and use a selector expression such as #vhostFor.select(messageProperties.headers.customerId).
If you later add a new vhost you wouldn't have to change your main application, just your vhostFor lookup bean to pick the right vhost.