/**
*
* Copyright 2004 Protique 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.activemq.broker.impl;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.advisories.TempDestinationAdvisoryEvent;
import org.activemq.broker.BrokerClient;
import org.activemq.message.ActiveMQDestination;
import org.activemq.message.ActiveMQMessage;
import org.activemq.message.ActiveMQObjectMessage;
import org.activemq.message.ConnectionInfo;
import org.activemq.message.ConsumerInfo;
import org.activemq.message.Packet;
import org.activemq.message.ProducerInfo;
import org.activemq.util.IdGenerator;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
/**
* Manages advisory subscriptions and messages
*
* @version $Revision: 1.1.1.1 $
*/
class AdvisorySupport {
private static final Log log = LogFactory.getLog(AdvisorySupport.class);
private Set advisoryConsumers = new CopyOnWriteArraySet();
private Set consumers = new CopyOnWriteArraySet();
private Set producers = new CopyOnWriteArraySet();
private Set connections = new CopyOnWriteArraySet();
private Set tempDestConsumers = new CopyOnWriteArraySet();
private IdGenerator idGen = new IdGenerator();
private Map tempDestinations = new ConcurrentHashMap();//client ids = keys, Set of TempDestinationAdvisoryEvents =
// values
private DefaultBroker broker;
AdvisorySupport(DefaultBroker broker) {
this.broker = broker;
}
/**
* Add an advisory Consumer
*
* @param advisory
* @param sender
*/
void addAdvisory(BrokerClient sender, ConsumerInfo advisory) {
if (advisory != null && advisory.isAdvisory()) {
advisoryConsumers.add(advisory);
for (Iterator i = consumers.iterator();i.hasNext();) {
ConsumerInfo info = (ConsumerInfo) i.next();
dispatchToTarget(sender, generateAdvisory(advisory, info));
}
for (Iterator i = producers.iterator();i.hasNext();) {
ProducerInfo info = (ProducerInfo) i.next();
dispatchToTarget(sender, generateAdvisory(advisory, info));
}
for (Iterator i = connections.iterator();i.hasNext();) {
ConnectionInfo info = (ConnectionInfo) i.next();
dispatchToTarget(sender, generateAdvisory(advisory, info));
}
for (Iterator i = tempDestinations.values().iterator();i.hasNext();) {
Set set = (Set) i.next();
for (Iterator si = set.iterator();si.hasNext();) {
TempDestinationAdvisoryEvent event = (TempDestinationAdvisoryEvent) si.next();
dispatchToTarget(sender, generateAdvisory(advisory, event));
}
}
}
addConsumer(sender, advisory);
}
/**
* remove an advisory Consumer
*
* @param sender
* @param info
*/
void removeAdvisory(BrokerClient sender, ConsumerInfo info) {
advisoryConsumers.remove(info);
removeConsumer(sender, info);
}
/**
* Add a Consumer
*
* @param sender
* @param info
*/
private void addConsumer(BrokerClient sender, ConsumerInfo info) {
consumers.remove(info);
consumers.add(info);
dispatchToBroker(sender, generateAdvisoryMessage(info, info.getDestination().getTopicForConsumerAdvisory()));
}
/**
* Remove a Consumer
*
* @param sender
* @param info
*/
private void removeConsumer(BrokerClient sender, ConsumerInfo info) {
consumers.remove(info);
dispatchToBroker(sender, generateAdvisoryMessage(info, info.getDestination().getTopicForConsumerAdvisory()));
}
/**
* Add a Producer
*
* @param sender
* @param info
*/
void addProducer(BrokerClient sender, ProducerInfo info) {
producers.remove(info);
producers.add(info);
if (info.getDestination() != null) {
dispatchToBroker(sender, generateAdvisoryMessage(info, info.getDestination().getTopicForProducerAdvisory()));
}
}
/**
* Remove a Producer
*
* @param sender
* @param info
*/
void removeProducer(BrokerClient sender, ProducerInfo info) {
producers.remove(info);
if (info.getDestination() != null) {
dispatchToBroker(sender, generateAdvisoryMessage(info, info.getDestination().getTopicForProducerAdvisory()));
}
}
/**
* Add a Connection
*
* @param sender
* @param info
*/
void addConnection(BrokerClient sender, ConnectionInfo info) {
connections.remove(info);
connections.add(info);
ActiveMQDestination dest = ActiveMQDestination.createDestination(ActiveMQDestination.ACTIVEMQ_TOPIC,
ActiveMQDestination.CONNECTION_ADVISORY_PREFIX);
dispatchToBroker(sender, generateAdvisoryMessage(info, dest));
}
/**
* Remove a Connection
*
* @param sender
* @param info
*/
void removeConnection(BrokerClient sender, ConnectionInfo info) {
connections.remove(info);
removeAllTempDestinations(sender, info.getClientId());
ActiveMQDestination dest = ActiveMQDestination.createDestination(ActiveMQDestination.ACTIVEMQ_TOPIC,
ActiveMQDestination.CONNECTION_ADVISORY_PREFIX);
dispatchToBroker(sender, generateAdvisoryMessage(info, dest));
}
/**
* @param sender
* @param message
* @throws JMSException
*/
void processTempDestinationAdvisory(BrokerClient sender, ActiveMQMessage message) throws JMSException {
TempDestinationAdvisoryEvent event = (TempDestinationAdvisoryEvent) ((ObjectMessage) message).getObject();
processTempDestinationAdvisory(event);
}
/**
* @param advisory
* @param info
* @return an advisory message or null
*/
private ActiveMQMessage generateAdvisory(ConsumerInfo advisory, ConsumerInfo info) {
if (matchConsumer(advisory, info)) {
return generateAdvisoryMessage(advisory, info, info.getDestination().getTopicForConsumerAdvisory());
}
return null;
}
/**
* @param advisory
* @param info
* @return an advisory message or null
*/
private ActiveMQMessage generateAdvisory(ConsumerInfo advisory, ProducerInfo info) {
if (matchProducer(advisory, info)) {
return generateAdvisoryMessage(advisory, info, info.getDestination().getTopicForProducerAdvisory());
}
return null;
}
/**
* @param advisory
* @param info
* @return an advisory message or null
*/
private ActiveMQMessage generateAdvisory(ConsumerInfo advisory, ConnectionInfo info) {
if (matchConnection(advisory, info)) {
String destName = advisory.getDestination().getPhysicalName();
ActiveMQDestination dest = ActiveMQDestination.createDestination(advisory.getDestination()
.getDestinationType(), destName);
return generateAdvisoryMessage(advisory, info, dest);
}
return null;
}
/**
* Generate a TempDestinationAdvisoryEvent if the advisory is a match
*
* @param advisory
* @param event
* @return an advisory message or null
*/
private ActiveMQMessage generateAdvisory(ConsumerInfo advisory, TempDestinationAdvisoryEvent event) {
if (matchTempDestinationAdvisory(advisory, event.getDestination())) {
return generateAdvisoryMessage(advisory, event, event.getDestination().getTopicForTempAdvisory());
}
return null;
}
boolean matchConsumer(ConsumerInfo advisory, ConsumerInfo info) {
boolean result = false;
if (advisory != null && advisory.getDestination() != null && info != null && info.getDestination() != null) {
ActiveMQDestination advisoryDestination = advisory.getDestination();
ActiveMQDestination destination = info.getDestination();
if (advisoryDestination.isConsumerAdvisory()) {
ActiveMQDestination match = advisoryDestination.getDestinationBeingAdvised();
return match.matches(destination) || matchGeneralAdvisory(advisory, destination);
}
}
return result;
}
boolean matchProducer(ConsumerInfo advisory, ProducerInfo info) {
boolean result = false;
if (advisory != null && advisory.getDestination() != null && info != null && info.getDestination() != null) {
ActiveMQDestination advisoryDestination = advisory.getDestination();
ActiveMQDestination destination = info.getDestination();
if (advisoryDestination.isProducerAdvisory()) {
ActiveMQDestination match = advisoryDestination.getDestinationBeingAdvised();
return match.matches(destination) || matchGeneralAdvisory(advisory, destination);
}
}
return result;
}
boolean matchConnection(ConsumerInfo advisory, ConnectionInfo info) {
boolean result = false;
if (advisory != null && advisory.getDestination() != null && info != null) {
result = (advisory.getDestination().isConnectionAdvisory() && advisory.getDestination().matches(
ActiveMQDestination.createDestination(advisory.getDestination().getDestinationType(),
ActiveMQDestination.CONNECTION_ADVISORY_PREFIX)))
|| matchGeneralAdvisory(advisory, advisory.getDestination());
}
return result;
}
/**
* A consumer could listen for all advisories
*
* @param advisory
* @param destination
* @return true if a general 'catch-all' advisory subscriber
*/
private boolean matchGeneralAdvisory(ConsumerInfo advisory, ActiveMQDestination destination) {
boolean result = advisory.getDestination() != null && advisory.getDestination().isAdvisory();
if (result) {
ActiveMQDestination match = advisory.getDestination().getDestinationBeingAdvised();
result = match.matches(destination);
}
return result;
}
boolean matchTempDestinationAdvisory(ConsumerInfo advisory, ActiveMQDestination destination) {
boolean result = false;
if (advisory != null && advisory.getDestination() != null) {
ActiveMQDestination advisoryDestination = advisory.getDestination();
if (advisoryDestination.isTempDestinationAdvisory()) {
ActiveMQDestination match = advisoryDestination.getDestinationBeingAdvised();
return match.matches(destination) || matchGeneralAdvisory(advisory, destination);
}
}
return result;
}
private void processTempDestinationAdvisory(TempDestinationAdvisoryEvent event) {
String clientId = ActiveMQDestination.getClientId(event.getDestination());
Set set = (Set) tempDestinations.get(clientId);
if (event.isStarted()) {
if (set == null) {
set = new HashSet();
tempDestinations.put(clientId, set);
}
set.add(event);
}
else {
if (set != null) {
set.remove(event);
if (set.isEmpty()) {
tempDestinations.remove(clientId);
}
}
}
}
private void removeAllTempDestinations(BrokerClient sender, String clientId) {
Set set = (Set) tempDestinations.remove(clientId);
if (set != null) {
for (Iterator i = set.iterator();i.hasNext();) {
TempDestinationAdvisoryEvent event = (TempDestinationAdvisoryEvent) i.next();
event.setStarted(false);
processTempDestinationAdvisory(event);
for (Iterator it = advisoryConsumers.iterator();it.hasNext();) {
ConsumerInfo advisory = (ConsumerInfo) it.next();
dispatchToTarget(sender, generateAdvisory(advisory, event));
}
}
}
}
/**
* Generate an advisory message
*
* @param payload
* @param destination
* @return create ActiveMQMessage
*/
private ActiveMQMessage generateAdvisoryMessage(Packet payload, ActiveMQDestination destination) {
return generateAdvisoryMessage(null, payload, destination);
}
/**
* Generate an advisory message
*
* @param advisoryTarget
* @param payload
* @param destination
* @return create ActiveMQMessage
*/
private ActiveMQMessage generateAdvisoryMessage(final ConsumerInfo advisoryTarget, final Packet payload,
final ActiveMQDestination destination) {
ActiveMQObjectMessage advisoryMsg = null;
try {
advisoryMsg = new ActiveMQObjectMessage();
advisoryMsg.setJMSMessageID(idGen.generateId());
advisoryMsg.setJMSDestination(destination);
advisoryMsg.setExternalMessageId(true);
advisoryMsg.setDeliveryCount(DeliveryMode.NON_PERSISTENT);
advisoryMsg.setObject((Serializable) payload);
if (advisoryTarget != null) {
advisoryMsg.setConsumerNos(new int[]{advisoryTarget.getConsumerNo()});
}
}
catch (JMSException e) {
advisoryMsg = null;
log.warn("caught an exception generating an advisory", e);
}
return advisoryMsg;
}
private void dispatchToTarget(BrokerClient target, ActiveMQMessage message) {
if (target != null && message != null) {
target.dispatch(message);
}
}
private void dispatchToBroker(BrokerClient sender, ActiveMQMessage message) {
if (sender != null && message != null) {
try {
broker.sendMessage(sender, message);
}
catch (JMSException e) {
log.warn("caught an exception sending an advisory", e);
}
}
}
}