/**
*
* 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.codehaus.activemq.service.boundedvm;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import javax.jms.JMSException;
import org.codehaus.activemq.broker.BrokerClient;
import org.codehaus.activemq.filter.AndFilter;
import org.codehaus.activemq.filter.DestinationMap;
import org.codehaus.activemq.filter.Filter;
import org.codehaus.activemq.filter.FilterFactory;
import org.codehaus.activemq.filter.FilterFactoryImpl;
import org.codehaus.activemq.filter.NoLocalFilter;
import org.codehaus.activemq.message.ActiveMQDestination;
import org.codehaus.activemq.message.ActiveMQMessage;
import org.codehaus.activemq.message.ConsumerInfo;
import org.codehaus.activemq.message.MessageAck;
import org.codehaus.activemq.message.util.MemoryBoundedQueue;
import org.codehaus.activemq.message.util.MemoryBoundedQueueManager;
import org.codehaus.activemq.service.DeadLetterPolicy;
import org.codehaus.activemq.service.MessageContainer;
import org.codehaus.activemq.service.MessageContainerManager;
import org.codehaus.activemq.service.TransactionManager;
import org.codehaus.activemq.service.TransactionTask;
import org.codehaus.activemq.service.impl.AutoCommitTransaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
/**
* A MessageContainerManager for transient topics
*
* @version $Revision: 1.11 $
*/
/**
* A manager of MessageContainer instances
*/
public class TransientTopicBoundedMessageManager implements MessageContainerManager {
private static final Log log = LogFactory.getLog(TransientTopicBoundedMessageManager.class);
private MemoryBoundedQueueManager queueManager;
private ConcurrentHashMap containers;
private ConcurrentHashMap subscriptions;
private DestinationMap destinationMap;
private FilterFactory filterFactory;
private SynchronizedBoolean started;
private Map destinations;
private DeadLetterPolicy deadLetterPolicy;
private boolean decoupledDispatch = false;
/**
* Constructor for TransientTopicBoundedMessageManager
*
* @param mgr
*/
public TransientTopicBoundedMessageManager(MemoryBoundedQueueManager mgr) {
this.queueManager = mgr;
this.containers = new ConcurrentHashMap();
this.subscriptions = new ConcurrentHashMap();
this.destinationMap = new DestinationMap();
this.destinations = new ConcurrentHashMap();
this.filterFactory = new FilterFactoryImpl();
this.started = new SynchronizedBoolean(false);
}
/**
* start the manager
*
* @throws JMSException
*/
public void start() throws JMSException {
if (started.commit(false, true)) {
for (Iterator i = containers.values().iterator(); i.hasNext();) {
TransientTopicBoundedMessageContainer container = (TransientTopicBoundedMessageContainer) i.next();
container.start();
}
}
}
/**
* stop the manager
*
* @throws JMSException
*/
public void stop() throws JMSException {
if (started.commit(true, false)) {
for (Iterator i = containers.values().iterator(); i.hasNext();) {
TransientTopicBoundedMessageContainer container = (TransientTopicBoundedMessageContainer) i.next();
container.stop();
}
}
}
/**
* Add a consumer if appropiate
*
* @param client
* @param info
* @throws JMSException
*/
public synchronized void addMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
ActiveMQDestination destination = info.getDestination();
if (destination.isTopic()) {
TransientTopicBoundedMessageContainer container = (TransientTopicBoundedMessageContainer) containers
.get(client);
if (container == null) {
MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(client.toString());
container = new TransientTopicBoundedMessageContainer(this, client, queue);
containers.put(client, container);
if (started.get()) {
container.start();
}
}
if (log.isDebugEnabled()) {
log.debug("Adding consumer: " + info);
}
TransientTopicSubscription ts = container.addConsumer(createFilter(info), info);
if (ts != null) {
subscriptions.put(info.getConsumerId(), ts);
}
destinationMap.put(destination,container);
String name = destination.getPhysicalName();
//As the destinations are used for generating
//subscriptions for NetworkConnectors etc.,
//we should not generate duplicates by adding in
//durable topic subscribers
if (!info.isDurableTopic() && !destinations.containsKey(name)) {
destinations.put(name, destination);
}
}
}
/**
* @param client
* @param info
* @throws JMSException
*/
public synchronized void removeMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
ActiveMQDestination destination = info.getDestination();
if (destination.isTopic()) {
TransientTopicBoundedMessageContainer container = (TransientTopicBoundedMessageContainer) containers
.get(client);
if (container != null) {
container.removeConsumer(info);
if (container.isInactive()) {
containers.remove(client);
container.close();
destinationMap.remove(destination, container);
}
// lets check if we've no more consumers for this destination
//As the destinations are used for generating
//subscriptions for NetworkConnectors etc.,
//we should not count durable topic subscribers
if (!info.isDurableTopic() && !hasConsumerFor(destination)) {
destinations.remove(destination.getPhysicalName());
}
}
subscriptions.remove(info.getConsumerId());
}
}
/**
* Delete a durable subscriber
*
* @param clientId
* @param subscriberName
* @throws JMSException if the subscriber doesn't exist or is still active
*/
public void deleteSubscription(String clientId, String subscriberName) throws JMSException {
}
/**
* @param client
* @param message
* @throws JMSException
*/
public void sendMessage(final BrokerClient client, final ActiveMQMessage message) throws JMSException {
if (TransactionManager.getContexTransaction()==AutoCommitTransaction.AUTO_COMMIT_TRANSACTION){
doSendMessage(client, message);
}else {
// If there is no transaction.. then this executes directly.
TransactionManager.getContexTransaction().addPostCommitTask(new TransactionTask() {
public void execute() throws Throwable {
doSendMessage(client, message);
}
});
}
}
/**
* @param client
* @param message
* @throws JMSException
*/
private void doSendMessage(BrokerClient client, ActiveMQMessage message) throws JMSException {
Set set = destinationMap.get(message.getJMSActiveMQDestination());
if (!set.isEmpty()){
for (Iterator i = set.iterator(); i.hasNext();) {
TransientTopicBoundedMessageContainer container = (TransientTopicBoundedMessageContainer) i.next();
container.targetAndDispatch(client,message);
}
}
}
/**
* @param client
* @param ack
* @throws JMSException
*
*/
public void acknowledgeMessage(BrokerClient client, MessageAck ack) throws JMSException {
}
/**
* @throws JMSException
*
*/
public void poll() throws JMSException {
}
/**
* For Transient topics - a MessageContainer maps on to the messages
* to be dispatched through a BrokerClient, not a destination
* @param physicalName
* @return the MessageContainer used for dispatching - always returns null
* @throws JMSException
*/
public MessageContainer getContainer(String physicalName) throws JMSException {
return null;
}
/**
* @return a map of all the destinations
*/
public Map getDestinations() {
return Collections.unmodifiableMap(destinations);
}
/**
* Returns an unmodifiable map, indexed by String name, of all the {@link javax.jms.Destination}
* objects used by non-broker consumers directly connected to this container
*
* @return
*/
public Map getLocalDestinations() {
Map localDestinations = new HashMap();
for (Iterator iter = subscriptions.values().iterator(); iter.hasNext();) {
TransientTopicSubscription sub = (TransientTopicSubscription) iter.next();
if (sub.isLocalSubscription() && !sub.isDurableTopic()) {
final ActiveMQDestination dest = sub.getDestination();
localDestinations.put(dest.getPhysicalName(), dest);
}
}
return Collections.unmodifiableMap(localDestinations);
}
/**
* @return the DeadLetterPolicy for this Container Manager
*/
public DeadLetterPolicy getDeadLetterPolicy(){
return deadLetterPolicy;
}
/**
* Set the DeadLetterPolicy for this Container Manager
* @param policy
*/
public void setDeadLetterPolicy(DeadLetterPolicy policy){
this.deadLetterPolicy = policy;
}
/**
* @return Returns the decoupledDispatch.
*/
public boolean isDecoupledDispatch() {
return decoupledDispatch;
}
/**
* @param decoupledDispatch The decoupledDispatch to set.
*/
public void setDecoupledDispatch(boolean decoupledDispatch) {
this.decoupledDispatch = decoupledDispatch;
}
/**
* Create filter for a Consumer
*
* @param info
* @return the Fitler
* @throws javax.jms.JMSException
*/
protected Filter createFilter(ConsumerInfo info) throws JMSException {
Filter filter = filterFactory.createFilter(info.getDestination(), info.getSelector());
if (info.isNoLocal()) {
filter = new AndFilter(filter, new NoLocalFilter(info.getClientId()));
}
return filter;
}
protected boolean hasConsumerFor(ActiveMQDestination destination) {
for (Iterator i = containers.values().iterator(); i.hasNext();) {
TransientTopicBoundedMessageContainer container = (TransientTopicBoundedMessageContainer) i.next();
if (container.hasConsumerFor(destination)) {
return true;
}
}
return false;
}
/**
* @see org.codehaus.activemq.service.MessageContainerManager#createMessageContainer(org.codehaus.activemq.message.ActiveMQDestination)
*/
public void createMessageContainer(ActiveMQDestination dest) throws JMSException {
}
/**
* @see org.codehaus.activemq.service.MessageContainerManager#destroyMessageContainer(org.codehaus.activemq.message.ActiveMQDestination)
*/
public void destroyMessageContainer(ActiveMQDestination dest) throws JMSException {
containers.remove(dest);
destinationMap.removeAll(dest);
}
/**
* @see org.codehaus.activemq.service.MessageContainerManager#getMessageContainerAdmins()
*/
public Map getMessageContainerAdmins() throws JMSException {
return Collections.EMPTY_MAP;
}
}