/**
* <a href="http://servicemix.org">ServiceMix: The open source ESB</a>
*
* Copyright 2005 RAJD Consultancy Ltd
*
* 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.servicemix.jbi.nmr.flow.jms;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet;
import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
import org.activemq.ActiveMQConnection;
import org.activemq.ActiveMQConnectionFactory;
import org.activemq.advisories.ConsumerAdvisor;
import org.activemq.advisories.ConsumerAdvisoryEvent;
import org.activemq.advisories.ConsumerAdvisoryEventListener;
import org.activemq.message.ConsumerInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.servicemix.jbi.framework.ComponentConnector;
import org.servicemix.jbi.framework.ComponentNameSpace;
import org.servicemix.jbi.framework.ComponentPacket;
import org.servicemix.jbi.framework.ComponentPacketEvent;
import org.servicemix.jbi.framework.ComponentPacketEventListener;
import org.servicemix.jbi.framework.LocalComponentConnector;
import org.servicemix.jbi.messaging.MessageExchangeImpl;
import org.servicemix.jbi.nmr.Broker;
import org.servicemix.jbi.nmr.flow.AbstractFlow;
import javax.jbi.JBIException;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.MessageExchange.Role;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Use for message routing among a network of containers. All routing/registration happens automatically.
*
* @version $Revision: 749 $
*/
public class JMSFlow extends AbstractFlow implements ConsumerAdvisoryEventListener, MessageListener, ComponentPacketEventListener {
private static final Log log = LogFactory.getLog(JMSFlow.class);
private static final String INBOUND_PREFIX = "org.servicemix.inbound.";
private String jmsURL = "peer://org.servicemix?persistent=false";
private String userName;
private String password;
private ActiveMQConnectionFactory connectionFactory;
private ActiveMQConnection connection;
private String broadcastDestinationName = "org.servicemix.JMSFlow";
private MessageProducer queueProducer;
private MessageProducer topicProducer;
private Topic broadcastTopic;
private Session broadcastSession;
private Session inboundSession;
private ConsumerAdvisor advisor;
private Map networkNodeKeyMap = new ConcurrentHashMap();
private Map networkComponentKeyMap = new ConcurrentHashMap();
private Map consumerMap = new ConcurrentHashMap();
private AtomicBoolean started = new AtomicBoolean(false);
/**
* The type of Flow
*
* @return the type
*/
public String getDescription() {
return "jms";
}
/**
* @return Returns the jmsURL.
*/
public String getJmsURL() {
return jmsURL;
}
/**
* @param jmsURL The jmsURL to set.
*/
public void setJmsURL(String jmsURL) {
this.jmsURL = jmsURL;
}
/**
* @return Returns the password.
*/
public String getPassword() {
return password;
}
/**
* @param password The password to set.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return Returns the userName.
*/
public String getUserName() {
return userName;
}
/**
* @param userName The userName to set.
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* @return Returns the connectionFactory.
*/
public ActiveMQConnectionFactory getConnectionFactory() {
return connectionFactory;
}
/**
* @param connectionFactory The connectionFactory to set.
*/
public void setConnectionFactory(ActiveMQConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
/**
* @return Returns the broadcastDestinationName.
*/
public String getBroadcastDestinationName() {
return broadcastDestinationName;
}
/**
* @param broadcastDestinationName The broadcastDestinationName to set.
*/
public void setBroadcastDestinationName(String broadcastDestinationName) {
this.broadcastDestinationName = broadcastDestinationName;
}
/**
* Initialize the Region
*
* @param broker
* @throws JBIException
*/
public void init(Broker broker, String subType) throws JBIException {
super.init(broker, subType);
broker.getRegistry().addComponentPacketListener(this);
try {
if (connectionFactory == null) {
if (jmsURL != null) {
connectionFactory = new ActiveMQConnectionFactory(jmsURL);
}
else {
connectionFactory = new ActiveMQConnectionFactory();
}
}
if (userName != null) {
connection = (ActiveMQConnection) connectionFactory.createConnection(userName, password);
} else {
connection = (ActiveMQConnection) connectionFactory.createConnection();
}
connection.setClientID(broker.getContainerName());
connection.start();
inboundSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = inboundSession.createQueue(INBOUND_PREFIX + broker.getContainerName());
MessageConsumer inboundQueue = inboundSession.createConsumer(queue);
inboundQueue.setMessageListener(this);
queueProducer = inboundSession.createProducer(null);
broadcastSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
broadcastTopic = broadcastSession.createTopic(broadcastDestinationName);
MessageConsumer broadcastConsumer = broadcastSession.createConsumer(broadcastTopic, null, true);
broadcastConsumer.setMessageListener(this);
topicProducer = broadcastSession.createProducer(broadcastTopic);
topicProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
advisor = new ConsumerAdvisor(connection, broadcastTopic);
advisor.addListener(this);
}
catch (JMSException e) {
log.error("Failed to initialize JMSFlow", e);
throw new JBIException(e);
}
}
/**
* start the flow
*
* @throws JBIException
*/
public void start() throws JBIException {
if (started.compareAndSet(false, true)) {
super.start();
try {
advisor.start();
}
catch (JMSException e) {
JBIException jbiEx = new JBIException("JMSException caught in start: " + e.getMessage());
throw jbiEx;
}
}
}
/**
* stop the flow
*
* @throws JBIException
*/
public void stop() throws JBIException {
if (started.compareAndSet(true, false)) {
super.stop();
try {
advisor.stop();
}
catch (JMSException e) {
JBIException jbiEx = new JBIException("JMSException caught in stop: " + e.getMessage());
throw jbiEx;
}
}
}
public void shutDown() throws JBIException {
super.shutDown();
stop();
if (this.connection != null) {
try {
this.connection.close();
}
catch (JMSException e) {
log.warn("Error closing JMS Connection", e);
}
}
}
/**
* useful for testing
*
* @return number of containers in the network
*/
public int numberInNetwork() {
return advisor.activeConsumers(broadcastTopic).size();
}
/**
* Process state changes in Components
*
* @param event
*/
public void onEvent(ComponentPacketEvent event) {
try {
String componentName = event.getPacket().getComponentNameSpace().getName();
if (event.getStatus() == ComponentPacketEvent.ACTIVATED){
Queue queue = inboundSession.createQueue(INBOUND_PREFIX +componentName);
MessageConsumer consumer = inboundSession.createConsumer(queue);
consumer.setMessageListener(this);
consumerMap.put(componentName,consumer);
} else if (event.getStatus() == ComponentPacketEvent.DEACTIVATED){
MessageConsumer consumer = (MessageConsumer) consumerMap.remove(componentName);
if (consumer != null){
consumer.close();
}
}
// broadcast change to the network
ObjectMessage msg = broadcastSession.createObjectMessage(event);
topicProducer.send(msg);
log.info("broadcast to internal JMS network: " + event);
}
catch (JMSException e) {
log.error("failed to broadcast to the internal JMS network: " + event, e);
}
}
/**
* ConsumerAdvisoerEventListener implementation
*
* @param event
*/
public void onEvent(ConsumerAdvisoryEvent event) {
if (started.get()) {
ConsumerInfo info = event.getInfo();
if (info.isStarted()) {
for (Iterator i = broker.getRegistry().getLocalComponentConnectors().iterator();i.hasNext();) {
LocalComponentConnector lcc = (LocalComponentConnector) i.next();
ComponentPacket packet = lcc.getPacket();
ComponentPacketEvent cpe = new ComponentPacketEvent(packet, ComponentPacketEvent.ACTIVATED);
onEvent(cpe);
}
}
else {
removeAllPackets(info.getClientId());
}
}
}
/**
* Distribute an ExchangePacket
*
* @param packet
* @throws MessagingException
*/
protected void doSend(MessageExchangeImpl me) throws MessagingException {
doRouting(me);
}
/**
* Distribute an ExchangePacket
*
* @param packet
* @throws MessagingException
*/
public void doRouting(MessageExchangeImpl me) throws MessagingException{
ComponentNameSpace id=me.getRole()==Role.PROVIDER?me.getDestinationId():me.getSourceId();
ComponentConnector cc=broker.getRegistry().getComponentConnector(id);
if(cc!=null){
// let ActiveMQ do the routing ...
try{
String componentName=cc.getComponentNameSpace().getName();
String destination = "";
if (me.getRole() == Role.PROVIDER){
destination = INBOUND_PREFIX + componentName;
}else {
destination = INBOUND_PREFIX + id.getContainerName();
}
Queue queue=inboundSession.createQueue(destination);
ObjectMessage msg=inboundSession.createObjectMessage(me);
queueProducer.send(queue,msg);
}catch(JMSException e){
log.error("Failed to send exchange: "+me+" internal JMS Network",e);
throw new MessagingException(e);
}
}else{
throw new MessagingException("No component with id ("+id+") - Couldn't route MessageExchange "+me);
}
}
/**
* MessageListener implementation
*
* @param message
*/
public void onMessage(Message message) {
try {
if (message != null && message instanceof ObjectMessage) {
ObjectMessage objMsg = (ObjectMessage) message;
Object obj = objMsg.getObject();
if (obj != null) {
if (obj instanceof ComponentPacketEvent) {
ComponentPacketEvent event = (ComponentPacketEvent) obj;
String containerName = event.getPacket().getComponentNameSpace().getContainerName();
processInBoundPacket(containerName, event);
}
else if (obj instanceof MessageExchangeImpl) {
MessageExchangeImpl me = (MessageExchangeImpl) obj;
super.doRouting(me);
}
}
}
}
catch (JMSException jmsEx) {
log.error("Caught an exception unpacking JMS Message: ", jmsEx);
}
catch (MessagingException e) {
log.error("Caught an exception routing ExchangePacket: ", e);
}
}
/**
* Process Inbound packets
*
* @param containerName
* @param event
*/
protected void processInBoundPacket(String containerName, ComponentPacketEvent event) {
ComponentPacket packet = event.getPacket();
if (!packet.getComponentNameSpace().getContainerName().equals(broker.getContainerName())) {
if (event.getStatus() == ComponentPacketEvent.ACTIVATED) {
addRemotePacket(containerName, packet);
}
else if (event.getStatus() == ComponentPacketEvent.DEACTIVATED) {
removeRemotePacket(containerName, packet);
}
else if (event.getStatus() == ComponentPacketEvent.STATE_CHANGE) {
updateRemotePacket(containerName, packet);
}
else {
log.warn("Unable to determine ComponentPacketEvent type: " + event.getStatus() + " for packet: "
+ packet);
}
}
}
private void addRemotePacket(String containerName, ComponentPacket packet) {
networkComponentKeyMap.put(packet.getComponentNameSpace(), containerName);
Set set = (Set) networkNodeKeyMap.get(containerName);
if (set == null) {
set = new CopyOnWriteArraySet();
networkNodeKeyMap.put(containerName, set);
}
ComponentConnector cc = new ComponentConnector(packet);
log.info("Adding Remote Component: " + cc);
broker.getRegistry().addRemoteComponentConnector(cc);
set.add(packet);
}
private void updateRemotePacket(String containerName, ComponentPacket packet) {
Set set = (Set) networkNodeKeyMap.get(containerName);
if (set != null) {
set.remove(packet);
set.add(packet);
}
ComponentConnector cc = new ComponentConnector(packet);
log.info("Updating remote Component: " + cc);
broker.getRegistry().updateRemoteComponentConnector(cc);
}
private void removeRemotePacket(String containerName, ComponentPacket packet) {
networkComponentKeyMap.remove(packet.getComponentNameSpace());
Set set = (Set) networkNodeKeyMap.get(containerName);
if (set != null) {
set.remove(packet);
ComponentConnector cc = new ComponentConnector(packet);
log.info("Removing remote Component: " + cc);
broker.getRegistry().removeRemoteComponentConnector(cc);
if (set.isEmpty()) {
networkNodeKeyMap.remove(containerName);
}
}
}
private void removeAllPackets(String containerName) {
Set set = (Set) networkNodeKeyMap.remove(containerName);
if (set != null) {
for (Iterator i = set.iterator();i.hasNext();) {
ComponentPacket packet = (ComponentPacket) i.next();
ComponentConnector cc = new ComponentConnector(packet);
log.info("Network node: " + containerName + " Stopped. Removing remote Component: " + cc);
broker.getRegistry().removeRemoteComponentConnector(cc);
networkComponentKeyMap.remove(packet.getComponentNameSpace());
}
}
}
}