/* * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.flex.messaging.jms; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.jms.ConnectionFactory; import javax.jms.Destination; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.flex.messaging.SubscribeEvent; import org.springframework.flex.messaging.UnsubscribeEvent; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.listener.DefaultMessageListenerContainer; import org.springframework.jms.listener.adapter.MessageListenerAdapter; import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.util.Assert; import flex.messaging.FlexContext; import flex.messaging.messages.CommandMessage; import flex.messaging.messages.Message; import flex.messaging.services.MessageService; import flex.messaging.services.messaging.adapters.MessagingAdapter; /** * A {@link MessagingAdapter} implementation that enables sending and receiving messages via JMS. * * @author Mark Fisher * @author Jeremy Grelle */ public class JmsAdapter extends MessagingAdapter implements InitializingBean, BeanNameAware, ApplicationEventPublisherAware { private final Log logger = LogFactory.getLog(getClass()); private volatile ConnectionFactory connectionFactory; private volatile Object destination; private volatile boolean pubSubDomain; private volatile MessageConverter messageConverter; private volatile ApplicationEventPublisher applicationEventPublisher; private final JmsTemplate jmsTemplate = new JmsTemplate(); private final DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer(); private final Set<Object> subscriberIds = new HashSet<Object>(); private final Map<Object, String> clientSubscriptions = new HashMap<Object, String>(); /** * * {@inheritDoc} */ public void afterPropertiesSet() { Assert.notNull(this.connectionFactory, "connectionFactory is required"); Assert.notNull(this.destination, "destination or destination name is required"); this.jmsTemplate.setConnectionFactory(this.connectionFactory); MessageConverter converterToSet = this.messageConverter; if (converterToSet == null || !(converterToSet instanceof FlexMessageConverter)) { converterToSet = new FlexMessageConverter(converterToSet); } this.jmsTemplate.setMessageConverter(converterToSet); MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(); messageListenerAdapter.setMessageConverter(converterToSet); messageListenerAdapter.setDelegate(this); this.messageListenerContainer.setConnectionFactory(this.connectionFactory); this.messageListenerContainer.setMessageListener(messageListenerAdapter); this.messageListenerContainer.setAutoStartup(false); if (this.destination instanceof Destination) { this.jmsTemplate.setDefaultDestination((Destination) this.destination); this.messageListenerContainer.setDestination((Destination) this.destination); } else { this.jmsTemplate.setPubSubDomain(this.pubSubDomain); this.jmsTemplate.setDefaultDestinationName((String) this.destination); this.messageListenerContainer.setPubSubDomain(this.pubSubDomain); this.messageListenerContainer.setDestinationName((String) this.destination); } this.jmsTemplate.afterPropertiesSet(); this.messageListenerContainer.afterPropertiesSet(); } /** * Returns the {@link JmsTemplate} used by this adapter * * @return the jms template */ public JmsTemplate getJmsTemplate() { return this.jmsTemplate; } /** * * {@inheritDoc} */ @Override public boolean handlesSubscriptions() { return true; } /** * Invoked when a Message is received from a Flex client. */ @Override public Object invoke(Message flexMessage) { this.jmsTemplate.convertAndSend(flexMessage); return null; } /** * * {@inheritDoc} */ @Override public Object manage(CommandMessage commandMessage) { String clientId = (String) commandMessage.getClientId(); if (commandMessage.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) { this.subscriberIds.add(clientId); synchronized (this.messageListenerContainer) { if (!this.messageListenerContainer.isActive()) { this.messageListenerContainer.initialize(); } if (!this.messageListenerContainer.isRunning()) { this.messageListenerContainer.start(); } } if (this.logger.isInfoEnabled()) { this.logger.info("client [" + clientId + "] subscribed to destination [" + this.getDestination().getId() + "]"); } String flexClientId = FlexContext.getFlexClient().getId(); this.clientSubscriptions.put(clientId, flexClientId); this.applicationEventPublisher.publishEvent(new SubscribeEvent(flexClientId, clientId, this.getDestination().getId())); } else if (commandMessage.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) { this.subscriberIds.remove(clientId); synchronized (this.messageListenerContainer) { if (this.subscriberIds.isEmpty() && this.messageListenerContainer.isActive()){ this.messageListenerContainer.shutdown(); } } if (this.logger.isInfoEnabled()) { this.logger.info("client [" + clientId + "] unsubscribed from destination [" + this.getDestination().getId() + "]"); } String flexClientId = this.clientSubscriptions.remove(clientId); this.applicationEventPublisher.publishEvent(new UnsubscribeEvent(flexClientId, clientId, this.getDestination().getId())); } return null; } /** * * {@inheritDoc} */ public void setBeanName(String beanName) { this.setId(beanName); } /** * Sets the {@link ConnectionFactory} to use for sending and receiving JMS messages * * @param connectionFactory the connection factory */ public void setConnectionFactory(ConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } /** * Sets the {@link DestinationResolver} for resolving the JMS destination for this adapter * * @param destinationResolver the destination resolver */ public void setDestinationResolver(DestinationResolver destinationResolver) { Assert.notNull(destinationResolver, "destinationResolver must not be null"); this.jmsTemplate.setDestinationResolver(destinationResolver); this.messageListenerContainer.setDestinationResolver(destinationResolver); } /** * Sets the JMS {@link Destination} for messages sent and received by this adapter * * @param destination the destination */ public void setJmsDestination(Destination destination) { this.destination = destination; } /** * Sets the {@link MessageConverter} for messages sent and received by this adapter. * * @param messageConverter the message converter */ public void setMessageConverter(MessageConverter messageConverter) { this.messageConverter = messageConverter; } /** * Sets the JMS queue name for messages sent and received by this adapter. * * @param queueName the JMS queue name */ public void setQueueName(String queueName) { this.destination = queueName; } /** * Sets the JMS topic name for messages sent and received by this adapter. * * @param topicName the JMS topic name */ public void setTopicName(String topicName) { this.pubSubDomain = true; this.destination = topicName; } /** * Sets the {@link PlatformTransactionManager} to be used when sending and receiving messages * * @param transactionManager the transaction manager */ public void setTransactionManager(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "transactionManager must not be null"); this.messageListenerContainer.setTransactionManager(transactionManager); } /** * * {@inheritDoc} */ @Override public void start() { if (!this.messageListenerContainer.isActive()) { this.messageListenerContainer.initialize(); } super.start(); } /** * * {@inheritDoc} */ @Override public void stop() { this.messageListenerContainer.shutdown(); super.stop(); } DefaultMessageListenerContainer getMessageListenerContainer() { return this.messageListenerContainer; } /** * Invoked when a Message is received from a JMS client. */ void handleMessage(Message flexMessage) { flexMessage.setDestination(this.getDestination().getId()); MessageService messageService = (MessageService) getDestination().getService(); messageService.pushMessageToClients(flexMessage, true); messageService.sendPushMessageFromPeer(flexMessage, true); } public void setApplicationEventPublisher( ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } }