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.
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.
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" />
I have a flow that receives large messages (that land in an RDBMS table) so I can't process too many of these at a given time. As such I'm throttling the processing using <int:poller max-messages-per-poll="" />, and also with some queues with a capacity set like <int:queue capacity="">. I understand that multiple threads/transactions will participate in this flow and for the use-case this is acceptible.
The query polling the DB takes some time to run and as such I don't want to run it more often than I need to. Additionally the messages this flow receives tend to come in within "bursts", meaning it might get 1000 messages then not get any in for an hour.
What I'd like to do is use a dynamic-poller that will poll infrequently (since as-noted the query is costly to run) unless I see that I got a burst of messages in which case I want to poll very frequently until all messages are processed. For example if I have <int:poller max-messages-per-poll="100" /> and I know the poller just read in 100 messages, then chances are good that there are more messages in the RDBMS that need to be processed and I should poll again immediately after our processing has completed.
I know Spring doesn't offer a way to modify a trigger to make it dynamic in nature and have already looked at the Spring Integration Ref “7.1.5 Change Polling Rate at Runtime"
and at the dynamic-poller sample project: Dynamic Poller
That's a start but I really need the poller to change it's frequency based on current load.
I might not be correct on this, but I thought perhaps Gary mentioned something like this would be interesting to implement in his talk on "Implementing High-Availability Architectures with Spring Integration".
In any event writing a class(es) to change the poller frequency doesn't seem to be a big deal. What is a bit more challenging is how to know when a poll has occurred that produced no results since nothing gets posted to the output channel.
Some options I've considered:
Attach a <int:wire-tap channel="" /> to the poller's channel which calls a <int:service-activator>. Service activator examines number of messages and adjusts poller's period on the DynamicPeriodicTrigger.
Problem is that this will never get called if no messages are received so once I adjust to poll more frequently that polling period will remain indefinately.
Same as #1 but add logic to DynamicPeriodicTrigger that will revert period back to the initialDelay after next trigger occurs or after certain period of time.
Use an <int:advice-chain> element within the <int:poller> element with a MethodInterceptor implementation.
Similar to what Artem suggests in this link.
While this allows me to get in front of the receive method, it does not grant me access to results of the receive method (which would give me the number of messages retrieved). Note this appears to be confirmed by what Gary mentions on this link.
The request handler advice chain is a special case; we had to take care to only advise the internal endpoint methods and not any downstream processing (on output channels).
Advising pollers is simpler because we're advising the whole flow. As described in section "7.1.4 Namespace Support" subsection "AOP Advice chains", you simply create an advice by implementing the MethodInterceptor interface.
See SourcePollingChannelAdapterFactoryBeanTests.testAdviceChain() for a very simple advice...
Code:
adviceChain.add(new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
adviceApplied.set(true);
return invocation.proceed();
}
});
This simply is used to assert that the advice was called properly; a real advice would add code before and/or after the invocation.proceed().
In effect, this advice advises all methods, but there is only one, (Callable.call()).
Create an AfterReturning advice with a pointcut that looks for the Message<T> receive() method.
Clone the JdbcPollingChannelAdapter and add my hooks in that new class.
Perhaps what Gary suggests on this link would be useful but the "gist" link is no longer valid.
UPDATED:
The option I ended up implementing was to use an AfterReturningAdvice that looked something like the following.
Original code:
<int-jdbc:inbound-channel-adapter id="jdbcInAdapter"
channel="inputChannel" data-source="myDataSource"
query="SELECT column1, column2 from tableA"
max-rows-per-poll="100">
<int:poller fixed-delay="10000"/>
</int-jdbc:inbound-channel-adapter>
New code:
<bean id="jdbcDynamicTrigger" class="DynamicPeriodicTrigger">
<constructor-arg name="period" value="20000" />
</bean>
<bean id="jdbcPollerMetaData" class="org.springframework.integration.scheduling.PollerMetadata">
<property name="maxMessagesPerPoll" value="1000"/>
<property name="trigger" ref="jdbcDynamicTrigger"/>
</bean>
<bean id="pollMoreFrequentlyForHighVolumePollingStrategy" class="springintegration.scheduling.PollMoreFrequentlyForHighVolumePollingStrategy">
<property name="newPeriod" value="1"/>
<property name="adjustmentThreshold" value="100"/>
<property name="pollerMetadata" ref="jdbcPollerMetaData"/>
</bean>
<aop:config>
<aop:aspect ref="pollMoreFrequentlyForHighVolumePollingStrategy" >
<aop:after-returning pointcut="bean(jdbcInAdapterBean) and execution(* *.receive(..))" method="afterPoll" returning="returnValue"/>
</aop:aspect>
</aop:config>
<bean id="jdbcInAdapterBean" class="org.springframework.integration.jdbc.JdbcPollingChannelAdapter">
<constructor-arg ref="myDataSource" />
<constructor-arg value="SELECT column1, column2 from tableA" />
<property name="maxRowsPerPoll" value="100" />
</bean>
<int:inbound-channel-adapter id="jdbcInAdapter" ref="jdbcInAdapterBean"
channel="inputChannel"
auto-startup="false">
<int:poller ref="jdbcPollerMetaData" />
</int:inbound-channel-adapter>
I've done a bit more research on this and feel that Spring Integration perhaps could offer some hooks into the pollers so that developers can better customize them.
For more info see https://jira.spring.io/browse/INT-3633
If that JIRA does not get implemented and someone is interested in the code I implemented add a comment to this and I'll make the code available on github or gist.
Thanks for opening the JIRA issue; we should discuss the feature over there because stack overflow is not well suited for extended conversations.
However, I'm not sure what you meant above by "...but the "gist" link is no longer valid...". It works fine for me... https://gist.github.com/garyrussell/5374267 but let's discuss in the JIRA.
I need to be able to update the refreshInterval for JMS client programmatically.
I tried to do it through JmsConfiguration bean, but that's useless, and I couldn't find any configuration on the ActiveMQConnectionFactory class that I could use to update that value.
You can set the recoveryInterval property on the ActiveMQComponent/JmsComponent or set the same property on the JmsConfiguration POJO.
However, since the Camel ActiveMQ/JMS consumer is based on a Spring JMS listeners, like DefaultMessageListenerContainer, you cannot simply change that parameter during runtime (if that's what you intend). You need to set the recoveryInterval before the route is created. You can of course recreate the route and possibly ActiveMQ component and have the recovery interval set programmatically.
If you really need this feature, you can subclass DefaultMessageListenerContainer to allow setRecoveryInterval to actually trigger during runtime (not sure how easy that is, there is some thread handling to watch out for). Your custom MLC can be supplied to camel via the messageListenerContainerFactoryRef option.
I have recently asked a question here where I asked how to reference a String object in the mule registry from a Mule Flow. That has been successfully answered my current problem is that I cannot populate this in the registry before the flow is initialized.
Thus in the following segment (password="#app.registry['password']]) null is always used and my connection fails:
<sfdc:config name="ConfigurableSalesforceConnection"username="${sfdc.username}"password="#app.registry['password']]"securityToken="${sfdc.securityToken}"doc:name="Salesforce" url="${sfdc.url}" />
Amongst other things I have added a listener to my flow, but this adds the value too late and the flow has already been initialized.
<notifications>
<notification event="CONTEXT"/>
<notification-listener ref="Initializer"/>
</notifications>
<spring:beans>
<spring:bean id="Initializer" name="Initializer"
class="initializer.InitAccountSync" />
</spring:beans>
My question consists of two parts, firstly is there a way to fore the reinitialization of a flow after start-up, and/or how can I store a value in the mule-context registry before said initialization?
Thanks in advance.
The registry is shared with Spring, you can register some java.lang.String beans using SpEL or a MethodInvokingFactoryBean.
However, for this case, if you are not retrieving it outside mule, what I would recommend is leverage jasypt.