/**
*
* 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.service.boundedvm;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.jms.JMSException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.broker.BrokerClient;
import org.activemq.filter.Filter;
import org.activemq.io.util.MemoryBoundedQueue;
import org.activemq.message.ActiveMQDestination;
import org.activemq.message.ActiveMQMessage;
import org.activemq.message.ConsumerInfo;
import org.activemq.message.MessageAck;
import org.activemq.service.MessageContainer;
import org.activemq.service.MessageContainerAdmin;
import org.activemq.service.MessageIdentity;
import org.activemq.service.Service;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
/**
* A MessageContainer for transient topics One of these exists for every active Connection consuming transient Topic
* messages
*
* @version $Revision: 1.1.1.1 $
*/
public class TransientTopicBoundedMessageContainer
implements
MessageContainer,
Service,
Runnable,
MessageContainerAdmin {
private SynchronizedBoolean started;
private TransientTopicBoundedMessageManager manager;
private BrokerClient client;
private MemoryBoundedQueue queue;
private Thread worker;
private CopyOnWriteArrayList subscriptions;
private Log log;
/**
* Construct this beast
*
* @param manager
* @param client
* @param queue
*/
public TransientTopicBoundedMessageContainer(TransientTopicBoundedMessageManager manager, BrokerClient client,
MemoryBoundedQueue queue) {
this.manager = manager;
this.client = client;
this.queue = queue;
this.started = new SynchronizedBoolean(false);
this.subscriptions = new CopyOnWriteArrayList();
this.log = LogFactory.getLog("TransientTopicBoundedMessageContainer:- " + client);
}
/**
* @return true if this Container has no active subscriptions
*/
public boolean isInactive() {
return subscriptions.isEmpty();
}
/**
* @return the BrokerClient this Container is dispatching to
*/
public BrokerClient getBrokerClient() {
return client;
}
/**
* Add a consumer to dispatch messages to
*
* @param filter
* @param info
*/
public TransientTopicSubscription addConsumer(Filter filter, ConsumerInfo info) {
TransientTopicSubscription ts = findMatch(info);
if (ts == null) {
ts = new TransientTopicSubscription(filter, info, client);
subscriptions.add(ts);
}
return ts;
}
/**
* Remove a consumer
*
* @param info
*/
public void removeConsumer(ConsumerInfo info) {
TransientTopicSubscription ts = findMatch(info);
if (ts != null) {
subscriptions.remove(ts);
}
}
/**
* start working
*/
public void start() {
if (started.commit(false, true)) {
if (manager.isDecoupledDispatch()) {
worker = new Thread(this, "TransientTopicDispatcher");
worker.setPriority(Thread.NORM_PRIORITY + 2);
worker.start();
}
}
}
/**
* See if this container should get this message and dispatch it
*
* @param sender the BrokerClient the message came from
* @param message
* @return true if it is a valid container
* @throws JMSException
*/
public boolean targetAndDispatch(BrokerClient sender, ActiveMQMessage message) throws JMSException {
boolean result = false;
if (!this.client.isClusteredConnection() || !sender.isClusteredConnection()) {
List tmpList = null;
for (Iterator i = subscriptions.iterator();i.hasNext();) {
TransientTopicSubscription ts = (TransientTopicSubscription) i.next();
if (!(ts.client.getChannel() != null && ts.client.getChannel().isMulticast() && ts.client.equals(sender)) && ts.isTarget(message)) {
if (tmpList == null) {
tmpList = new ArrayList();
}
tmpList.add(ts);
}
}
dispatchToQueue(message, tmpList);
result = tmpList != null;
}
return result;
}
/**
* stop working
*/
public void stop() {
started.set(false);
queue.clear();
}
/**
* close down this container
*/
public void close() {
if (started.get()) {
stop();
}
queue.close();
}
/**
* do some dispatching
*/
public void run() {
int count = 0;
ActiveMQMessage message = null;
while (started.get()) {
try {
message = (ActiveMQMessage) queue.dequeue(2000);
if (message != null) {
if (!message.isExpired()) {
client.dispatch(message);
if (++count == 250) {
count = 0;
Thread.yield();
}
}else {
if (log.isDebugEnabled()){
log.debug("Message: " + message + " has expired");
}
}
}
}
catch (Exception e) {
stop();
log.warn("stop dispatching", e);
}
}
}
private void dispatchToQueue(ActiveMQMessage message, List list) throws JMSException {
if (list != null && !list.isEmpty()) {
int[] ids = new int[list.size()];
for (int i = 0;i < list.size();i++) {
TransientTopicSubscription ts = (TransientTopicSubscription) list.get(i);
ids[i] = ts.getConsumerInfo().getConsumerNo();
}
message = message.shallowCopy();
message.setConsumerNos(ids);
if (manager.isDecoupledDispatch()) {
queue.enqueue(message);
}
else {
client.dispatch(message);
}
}
}
private TransientTopicSubscription findMatch(ConsumerInfo info) {
TransientTopicSubscription result = null;
for (Iterator i = subscriptions.iterator();i.hasNext();) {
TransientTopicSubscription ts = (TransientTopicSubscription) i.next();
if (ts.getConsumerInfo().equals(info)) {
result = ts;
break;
}
}
return result;
}
/**
* @param destination
* @return true if a
*/
public boolean hasConsumerFor(ActiveMQDestination destination) {
for (Iterator i = subscriptions.iterator();i.hasNext();) {
TransientTopicSubscription ts = (TransientTopicSubscription) i.next();
ConsumerInfo info = ts.getConsumerInfo();
if (info.getDestination().matches(destination)) {
return true;
}
}
return false;
}
/**
* @return the destination name
*/
public String getDestinationName() {
return "";
}
/**
* @param msg
* @return @throws JMSException
*/
public void addMessage(ActiveMQMessage msg) throws JMSException {
}
/**
* @param messageIdentity
* @param ack
* @throws JMSException
*/
public void delete(MessageIdentity messageIdentity, MessageAck ack) throws JMSException {
}
/**
* @param messageIdentity
* @return @throws JMSException
*/
public ActiveMQMessage getMessage(MessageIdentity messageIdentity) throws JMSException {
return null;
}
/**
* @param messageIdentity
* @throws JMSException
*/
public void registerMessageInterest(MessageIdentity messageIdentity) throws JMSException {
}
/**
* @param messageIdentity
* @param ack
* @throws JMSException
*/
public void unregisterMessageInterest(MessageIdentity messageIdentity) throws JMSException {
}
/**
* @param messageIdentity
* @return @throws JMSException
*/
public boolean containsMessage(MessageIdentity messageIdentity) throws JMSException {
return false;
}
/**
* @see org.activemq.service.MessageContainer#getMessageContainerAdmin()
*/
public MessageContainerAdmin getMessageContainerAdmin() {
return this;
}
/**
* @see org.activemq.service.MessageContainerAdmin#empty()
*/
public void empty() throws JMSException {
// TODO implement me
}
/**
* @see org.activemq.service.MessageContainer#isDeadLetterQueue()
*/
public boolean isDeadLetterQueue() {
return false;
}
}