So below I have Camel (via Spring DSL) successfully integrating my beans with ActiveMQ queues:
<!-- Note: this code is just a snippet; if you need to see more, please let me know! -->
<camelContext id="my-camel-context" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq-myinstance:queue:myqueue" />
<onException>
<exception>java.lang.Exception</exception>
<redeliveryPolicy maximumRedeliveries="2" />
<to uri="activemq-myinstance:queue_failures" />
</onException>
<to uri="bean:myBean?method=doCommand" />
</route>
</camelContext>
<bean id="jmsConnectionFactory-myqueue" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${activemq.instance.url}" />
</bean>
<bean id="pooledConnectionFactory-myqueue" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="maxConnections" value="64" />
<property name="maximumActive" value="${max.active.consumers}" />
<property name="connectionFactory" ref="jmsConnectionFactory-myqueue" />
</bean>
<bean id="jmsConfig-myqueue" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledConnectionFactory-myqueue"/>
<property name="concurrentConsumers" value="${max.active.consumers}"/>
</bean>
<bean id="activemq-myqueue" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig-myqueue"/>
</bean>
I'd like to explicitly enforce a socket timeout (on Socket.read()) - between Camel and ActiveMQ - of 25 seconds. Thus, when Camel attempts to route a message to/from ActiveMQ, if ActiveMQ takes more than 25 seconds to complete that response, I want the thread to exit gracefully. Obviously, if it's possible to also set up some kind of failover (so that requests that timeout can get replayed at a future time) that is greatly preferred over just losing the message!
How can I accomplish this? Thanks in advance!
Update: if Camel/JMS/ActiveMQ doesn't support this out of the box, I don't mind writing my own "ThreadManager" that interrupts/stops threads after 25-seconds, but I'm not sure what interface/classes to implement/extend, and to subsequently wire into my Spring beans.
By default Camel activemq request times out after 20 seconds.
For send timeouts there is a sendTimeout property on org.apache.activemq.ActiveMQConnectionFactory.
Also check the requesttimeout option for JMSConfiguraiton.
For failover you can set the failover transport in the broker url.
just set the timeout property on your brokerURL
failover:(tcp\://localhost\:61616)?timeout=25000
this will propagate an error back to your producer so you can handle it instead of having it just blocking the thread forever...
Related
I am currently using Apache ActiveMQ as a JMS solution to send various messages back and forth between clients. I would like to check the uptime for specific clients (i.e. Consumer or Producers), but I have not found a way to do this so far. I have checked the Advisory messages and StatisticsPlugin for any information on how to do this but have found nothing helpful.
I could try embedding a clock in my Consumer and Producer classes and creating a special advisory destination for sending messages to retrieve the time from those classes; however, this seems impractical.
Does anyone know of a better solution or a way to do this in ActiveMQ?
why advisory is not the solution ??
you can use it like this by routing messages from topic ActiveMQ.Advisory.Connection to a queue to process them when you want and calculate the uptime.
add this snippet to your activemq.xml
...
</broker>
<!-- configure the camel activemq component to use the current broker -->
<bean id="ActiveMQVMConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?create=false&waitForStart=10000"/>
<property name="userName" value="${activemq.username}"/>
<property name="password" value="${activemq.password}"/>
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent" >
<property name="connectionFactory" ref="ActiveMQVMConnectionFactory"/>
</bean>
<camelContext id="camel" trace="false" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:topic:ActiveMQ.Advisory.Connection" />
<to uri="activemq:queue:Advisory"/>
</route>
</camelContext>
I have a specific requirement where i need to send message to a server which won't always be available.
For this i used a network of brokers, specific to ActiveMQ.
The goal is to have a local application A (producer only) which will push message to another central application B (consumer only). However network won't always be available. So application's A broker have to store messages and wait for connection before it can send message to application B. SO basically A is a proxy which need to forward message to B when it is available
Broker's B configuration includes a durable topic which is listening on in order to consume message.
As said in ActiveMQ's documentation i have to use a static network bridge to do that, which is what i did.
Note : i can't have B subscribe to A, because there will be multiple instance of A and i can't configure all of them in B.
So here is my configuration (raw spring) for local application :
<!--As said in http://activemq.apache.org/spring-support.html use
a pooled conntection along with JMSTemplate -->
<amq:connectionFactory id="jmsFactory" brokerURL="${jms.broker.local.url}" />
<!--SpringJMSTemplate -->
<bean id="myJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
</bean>
<!-- local broker with embedded -->
<bean id="localbroker" class="org.apache.activemq.broker.BrokerService"
init-method="start" destroy-method="stop">
<property name="brokerName" value="localBroker" />
<property name="transportConnectorURIs">
<list>
<value>${jms.broker.local.url}</value>
</list>
</property>
<property name="networkConnectors">
<list>
<ref bean="networkConnector" />
</list>
</property>
</bean>
<amq:connectionFactory id="remoteJmsFactory"
brokerURL="${jms.broker.remote.url}" clientIDPrefix="BRIDGED-TEST" />
<bean id="networkConnector" class="org.apache.activemq.network.DiscoveryNetworkConnector">
<property name="uri" value="static:(${jms.broker.remote.url})"></property>
<property name="staticallyIncludedDestinations">
<list>
<bean class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg type="java.lang.String" value="${jms.topic.sample}"/>
</bean>
</list>
</property>
<property name="staticBridge" value="true"></property><!-- will deliver content even if no consumer, usefull for durable topic only -->
</bean>
The localbroker is an embedded broker connecting to a remote broker (the application you can download from apacheMQ page).
Here is the central configuration
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:${activemq.conf}/credentials.properties</value>
</property>
</bean>
<bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
lazy-init="false" scope="singleton"
init-method="start" destroy-method="stop">
</bean>
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" useVirtualDestSubs="true">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic=">" >
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="1000"/>
</pendingMessageLimitStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
<managementContext>
<managementContext createConnector="false"/>
</managementContext>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage percentOfJvmHeap="70" />
</memoryUsage>
<storeUsage>
<storeUsage limit="100 gb"/>
</storeUsage>
<tempUsage>
<tempUsage limit="50 gb"/>
</tempUsage>
</systemUsage>
</systemUsage>
<transportConnectors>
<transportConnector name="http" uri="http://0.0.0.0:61612?maximumConnections=1000&wireFormat.maxFrameSize=10485760"/>
</transportConnectors>
<shutdownHooks>
<bean xmlns="http://www.springframework.org/schema/beans" class="org.apache.activemq.hooks.SpringContextHook" />
</shutdownHooks>
</broker>
<import resource="jetty.xml"/>
So what is happening when i try to send/receive messages :
If the producer (A) is connected and consumer (B) is connected to their respective broker, and the broker are connected together it works fine.
If the consumer (B) is connected to his broker and there is message pending, while producer A's broker is disconnected, it works fine.
If the producer (A) is disconnected from network, A's broker won't deliver the message to B's broker when B is available again.
Before network connectors i tried jmsbridgeConnector using outboundTopicBridge in local broker configuration without any luck.
Here is the question : how do i get local's broker A send message to central's broker B on reconnect. And while it is not available, be sure that he won't lost any message.
Note :
The network which i work on is not alaways available (can be for days!), and i can rely only on http port, this is why it is the only one openned. This means that no multicast discovery is possible.
Message must be deliver only once.
The reason why i use local broker is to not manage what i have to send myself. They're only, at the moment, used to store and forward to the central.
EDIT : i have been able to make it working using a JMS Bridge, however i have a last problem, if the connection os lost at application booting or during application lifecycle, i need to restart my broker to be able to send messages.
I've been using this "store and forward" pattern with success using bridge.
I can not comment for network connector but for bridge, you have to :
use a recent version of jmeter due to Bug AMQ-5859
add a org.apache.activemq.network.jms.ReconnectionPolicy on the bridge
make sure you set the reconnectOnException on the remote broker connection factory
I have try all the way around and can't get it works using only configuration so i end up doing that myself :
<jms:listener-container container-type="default" factory-id="proxyFactory"
acknowledge="transacted" destination-type="topic" connection-factory="jmsFactory">
<bean id="remoteJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="remoteJmsFactory" />
<property name="pubSubDomain" value="true"/>
</bean>
<bean id="simpleMessageProxyListener"
class="com.xxx.jms.test.SimpleMessageProxyListener">
<property name="jmsTemplate" ref="remoteJmsTemplate" />
<property name="queueName" value="${jms.topic.sample}" />
</bean>
Basically i just have a class that subscribe to the local broker with a durable subscription, and send message to the remote, if it fails, session is rollbacked.
That simple proxy rely on Spring Listener's container, so it may be ably to work even if he listen on a remote broker, in my case, it's listening on a local embedded broker, so i won't have any problem.
If someone else have a only-configuration answer that works when stopping/starting the remote broker while local application live and don't need a restart to send the messages, feel free to post it, i will upvote and check.
Note : you must set jms.redeliveryPolicy.maximumDeliveries to -1 to have it working.
I'm using spring DMLC for my application with below settings, i'm facing strange behavior with DMLC that if I send 1000 messages on listener queue only ~1990 reaches to dmlc very quickly and ~10 get stuck on server, on further analysis i found that acknowledgements are not sent back for those 10 that's why i can see them on server, after few minutes acks is sent back but very slowly.
further on this i tried cacheConsumers=false in CachingConnectionFactory and everything becomes fine, however this makes frequent bind/unbind to mq server and creates huge consumer objects in jmv, does anyone have any solution how to solve this issue keeping cacheConsumers=true ?
<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="cachingjmsQueueConnectionFactory" />
<property name="destination" ref="queueDestination" />
<property name="messageListener" ref="queueDestination" />
<property name="concurrency" value="10-10" />
<property name="cacheLevel" value="1" />
<property name="transactionManager" ref="dbTransactionManager" />
<property name="sessionTransacted" value="true" />
</bean>
<bean id="cachingjmsQueueConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsQueueConnectionFactory" />
<property name="reconnectOnException" value="true" />
<property name="cacheConsumers" value="true" />
<property name="cacheProducers" value="true" />
<property name="sessionCacheSize" value="1" />
</bean>
You can set cacheConsumer to false on the cachingConnectionFactory and also change the cacheLevel to level 3 (CACHE_CONSUMER) on the DefaultMessageListenerClass. This way, the consumer will be cached at the DMLC level and the issue with stuck messages should be resolved without seeing frequent binds/unbinds.
The cacheConsumer should be set to false and you should have the DefaultMessageListenerClasse control the caching because is preferable to have the listener container handle appropriate caching within it's lifecycle. The following note in the Spring documentation (http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/listener/DefaultMessageListenerContainer.html) discusses this:
Note: Don't use Spring's CachingConnectionFactory in combination with
dynamic scaling. Ideally, don't use it with a message listener
container at all, since it is generally preferable to let the listener
container itself handle appropriate caching within its lifecycle.
Also, stopping and restarting a listener container will only work with
an independent, locally cached Connection - not with an externally
cached one.
I'm sending messages with InOut pattern to a JMS queue and set the replyTo to a predefined queue.
sometimes everything works fine and sometimes camel wont read replies from that queue and fail with TimedOutException.
sometimes I can see 2 or more consumers on the reply queue (using AMQ web console) but camel wont consume it.
When I see one consumer on the reply queue (using AMQ web console), everything works fine
and when I see many consumers on the reply queue (using AMQ web console), camel wont read replies from that queue and fail with TimedOutException.
Environment : Fuse ESB
Java Code
from("activemq:spirit.adapter.producer.shippingorder.commands")
.id("router-Shipping-Order-Commands")
.log(LoggingLevel.INFO, "Transport1", "router-Shipping-Order-Commands request: ${body}")
.to("activemq:transport.consumer.shippingorder.commands?replyTo=transport.producer.shippingorder.event&replyToType=Exclusive&requestTimeout=120000")
.log(LoggingLevel.INFO, "Transport1", "router response: ${body}");
activemq configuration
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://10.10.40.94:61616"/>
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="maxConnections" value="1" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledConnectionFactory" />
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>
This configuration is written in the camel-context.xml file and in the same file there is another activemq configuration used for another route
could you please advise?
Can you post your activemq configuration you are using? For example make sure you configure the init and destroy methods on the pool so it can properly start|stop as documented here: http://camel.apache.org/activemq
Hi All-
The problem was caused by some bugs in camel-jms 2.9.x which was shipped in Fuse ESB 7.0.1 and solved in Fuse ESB 7.1.0
If I enable transaction management on my DefaultMessageListenerContainer by specifying sessionTransacted=true or transactionManager=jmsTransactionManager, whenever an exception occurs in the MDP, the transaction is rolled back and the message is placed back on the queue. That then causes the message to be processed again, the transaction to roll back again over and over again so that it creates an endless loop.
I guess my question is ... what am I missing here? Why would you want the message to go back on the queue if it just means it will be processed over and over again?
<!-- jms connection factory -->
<bean name="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:ConnectionFactory" />
</bean>
<!-- jms transaction manager -->
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<!-- Destination for Inbound_Email_Q -->
<bean name="inboundEmailDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="queue/inbound_Email_Queue" />
</bean>
<!-- JmsTemplate for Inbound_Email_Q -->
<bean name="jmsInboundEmailTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="defaultDestination" ref="inboundEmailDestination" />
<property name="messageConverter" ref="xmlMessageConverter" />
</bean>
<!-- jms asynchronous listener -->
<bean id="emailMessageServiceMdp" class="org.site.wso.core.jms.EmailMessageServiceMdp" />
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<!-- <property name="transactionManager" ref="jmsTransactionManager" /> -->
<!-- <property name="sessionTransacted" value="true"/> -->
<property name="destination" ref="inboundEmailDestination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
<!-- jms message listener adapter -->
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="org.site.wso.core.jms.EmailMessageServiceMdp"/>
</constructor-arg>
<property name="messageConverter" ref="xmlMessageConverter"/>
</bean>
And here is my MDP:
public class EmailMessageServiceMdp implements MessageDelegate {
public void handleMessage(Object object) {
EmailMessageRequestVO requestVO = (EmailMessageRequestVO) object;
try {
//Service call that throw exception
} catch (Exception e) {
throw new ApplicationException(e);
}
}
}
The message redelivery is simply the default behaviour for your [configured] JMS implementation. It's up for endless debate what the relative usefulness of this is, but it would seem that rather than discard a message with some potentially unrecoverable data, some sort of retry is a sensible and conservative approach. For example, in your case, you appear to be converting a JMS message to an email message and dispatching to an SMTP server. If the SMTP gateway is down, you might want to hold onto the JMS messages and reprocess them when the gateway comes back up.
In general I would say your options for handling a message that failed processing are (depending on the JMS implementation):
Ditch the message.
Write the message to an error queue.
Redeliver the message after a delay of n seconds.
Redeliver the message n times and then write the message to an error queue.
Redeliver the message after a delay of n seconds x times and then write the message to an error queue.
If you prefer #1, then simply suppress the exception, commit the transaction and wave good bye to the message. For the rest, the JMS configuration (or the destination specific configuration) should handle those.
Additionally, if you want something more specific, you can interrogate the message's getJMSRedelivered() and/or the implementation specific message header property that indicates how many times the message has been redelivered (supported by most JMS implementations, but not standard) and dispose of the message accordingly.