Announcement Announcement Module
Collapse
No announcement yet.
MessageDestination backed by JmsAdapter Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • MessageDestination backed by JmsAdapter

    Hi,

    I'm trying to dynamically create JMS/Flex destinations at runtime. Can somebody provide an example (or show in the Spring source) how to back a JmsAdapter with a MessageDestination. So that when a Message is posted to a JMSDestination it is also picked up in the Flex MessageDestination (& then by the Flex client that has subscribed to this destination).

    Thanks in advance,

    Kevin.

  • #2
    Well, there are a couple ways to do it. You can take a more "manual" approach, where you just inject the MessageBroker and use its APIs directly. I don't really recommend this approach because it requires figuring out the proper lifecycle management for both the MessageDestination and the JmsAdapter, i.e. calling the right methods in the right order, etc. If you let Spring be in charge of creating those instances, then all of the lifecycle management is taken care of for you, because it's already baked into MessageDestinationFactory and JmsAdapter. I've been thinking about a clean way to do this and have come up with the following implementation that utilizes a combination of a request-scoped service in conjunction with @Bean factory methods (available in Spring 3.0...see here if you're unfamiliar with this feature: http://static.springsource.org/sprin...ns-annotations).

    First the code, then a little explanation...

    Here is a service that is responsible for the actual creation of the destinations and returning the new destination ID to the client so that it may subscribe to it:
    Code:
    package org.springframework.flex.samples.runtime.config;
    
    import javax.jms.ConnectionFactory;
    import javax.jms.Destination;
    
    import org.apache.activemq.command.ActiveMQQueue;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Scope;
    import org.springframework.context.annotation.ScopedProxyMode;
    import org.springframework.flex.messaging.MessageDestinationFactory;
    import org.springframework.flex.messaging.jms.JmsAdapter;
    import org.springframework.stereotype.Service;
    
    import flex.messaging.MessageBroker;
    
    @Service
    @Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
    public class DynamicJmsDestinationService implements BeanFactoryAware {
    	
    	private MessageBroker messageBroker;
    	
    	private ConnectionFactory connectionFactory;
    	
    	private BeanFactory beanFactory;
    	
    	private String destinationId;
    
    	public String createDynamicDestination() {
    		//Do something to calculate the destination id - could be derived from additional parameters passed into the method from the client if necessary
    		destinationId = ...;
    		beanFactory.getBean("dynamicJmsDestination");
    		return destinationId;
    	}
    	
    	@Bean
    	@Scope("prototype")
    	public MessageDestinationFactory dynamicJmsDestination() {
    		MessageDestinationFactory factory = new MessageDestinationFactory();
    		factory.setMessageBroker(this.messageBroker);
    		factory.setDestinationId(destinationId);
    		factory.setServiceAdapter("dynamicJmsAdapter");
    		return factory;
    	}
    
    	@Bean
    	@Scope("prototype")
    	public JmsAdapter dynamicJmsAdapter() {
    		JmsAdapter adapter = new JmsAdapter();
    		adapter.setId(destinationId + "Adapter");
    		adapter.setConnectionFactory(connectionFactory);
    		Destination destination = new ActiveMQQueue("queue."+destinationId);
    		adapter.setJmsDestination(destination);
    		return adapter;
    	}
    	
    	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    		this.beanFactory = beanFactory;		
    	}
    	
    	@Autowired
    	public void setConnectionFactory(ConnectionFactory connectionFactory) {
    		this.connectionFactory = connectionFactory;
    	}
    	
    	@Autowired
    	public void setMessageBroker(MessageBroker messageBroker) {
    		this.messageBroker = messageBroker;
    	}
    }
    The general idea is that the client makes an RPC call to createDynamicDestination to trigger the whole process. Since the bean is request scoped, a new instance of this service will get created and initialized by Spring for each such remoting request. I am assuming here that the unique ID for the destination needs to be calculated server-side. We store that calculated ID as an instance variable so that it will be available to the @Bean factory methods. The call to beanFactory.getBean is what triggers the actual destination creation process and allows getting the proper factory method and lifecycle callbacks from Spring. Once the necessary beans have been created successfully (if something fails, an error will propagate back to the client), we return the unique destination ID to the client so that they may subscribe to the new destination.

    In the dynamicJmsAdapter factory method, I'm just instantiating the ActiveMQQueue instance directly, but obviously you could vary this as appropriate to your JMS provider.

    Notice I'm not using the @RemotingDestination annotation...this is because I just discovered a bug with using it in conjunction with a bean that is a scoped proxy as this one is...we'll get that fixed for 1.0.2, but fortunately the XML version works just fine in the meantime, so you would need to have the following in your Spring XML config:

    Code:
    <flex:remoting-destination ref="dynamicJmsDestinationService"/>
    Hope that helps. This is fairly clean, but still not completely ideal to me (I prefer to avoid having to directly invoke the BeanFactory). I'm considering how we might better support the creation of dynamic messaging destinations in future releases by adding some additional attributes to the XML config tags and taking advantage of the SpEL support in Spring 3.

    Comment


    • #3
      Hi ,

      Thanks for this example, but I have a problem unsubscribing from these queues.

      When I call consumer.unsubscribe() & consumer.disconnect() on the Flex client then look at the subscriptions in jconsole I see that the number of subscribers doesn't change (i.e. it remains at 1), although the following message is logged:

      messaging.jms.JmsAdapter - client [DE928935-3A1E-5C4C-D688-E49CA0D55634] unsubscribed from destination [detail.panel_9312477D572F8FCFA743C89F3092BBF5]

      Is there anything else I need to do to unsubscribe? Also, I'd like to destroy these Queues that have been created when the client un-subscribes, do you know what actions I need to take?

      Thanks in advance,

      Kevin.

      Comment


      • #4
        Originally posted by killerlfc View Post
        When I call consumer.unsubscribe() & consumer.disconnect() on the Flex client then look at the subscriptions in jconsole I see that the number of subscribers doesn't change (i.e. it remains at 1), although the following message is logged:

        messaging.jms.JmsAdapter - client [DE928935-3A1E-5C4C-D688-E49CA0D55634] unsubscribed from destination [detail.panel_9312477D572F8FCFA743C89F3092BBF5]

        Is there anything else I need to do to unsubscribe?
        Are you using 1.0.2 yet? There were some fixes to the JmsAdapter along these lines.

        Originally posted by killerlfc View Post
        Also, I'd like to destroy these Queues that have been created when the client un-subscribes, do you know what actions I need to take?
        You could either make another remoting call to a service that knows how to destroy the Queue, or you could extend the JmsAdapter and overried the "manage" method to do it as part of the unsubscribe operation. If you were to go the extension route, just be sure to call super.manage(commandMessage) first so that the normal processing still takes place.

        Comment


        • #5
          Weird Timer already cancelled thrown by jmsTemplate.send every other run

          hello jeremy,

          I have tried your example. Dynamic jms destination creation is working. I have a bean that constantly sends message to these run time generated destination, which I see are being received by Flex. However looks like it keeps closing the topic and recreates it when jmsTemplate.send is called. So I see following exception being thrown every other time by jmsTemplate.send. It doesn't happen in every run, if I restart the tomcat it doesn't happen, then if I restart it again and it happens again.
          org.springframework.jms.UncategorizedJmsException: Uncategorized exception occured during JMS processing; nested exception is javax.jms.JMSException: Timer already cancelled

          Comment

          Working...
          X