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.