/**
*
* Copyright 2005 Hiram Chirino
*
* 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.advisories;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import org.activemq.message.ActiveMQDestination;
import org.activemq.message.ConsumerInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
/**
* A ProducerDemandAdvisor is used to know when a destination is in demand.
*
* Sometimes generating messages to send to a destination is very expensive
* and the application would like to avoid producing messages if there are no
* active consumers for the destination. There is a "demand" for messages
* when a consumer does come active.
*
* This object uses Advisory messages to know when consumer go active and
* inactive.
*/
public class ProducerDemandAdvisor {
private static final Log log = LogFactory.getLog(ProducerDemandAdvisor.class);
private final ActiveMQDestination destination;
private Connection connection;
private Session session;
private SynchronizedBoolean started = new SynchronizedBoolean(false);
private int consumerCount;
private ProducerDemandListener demandListener;
public ProducerDemandAdvisor( Connection connection, final Destination destination ) throws JMSException {
this.connection = connection;
this.destination = ActiveMQDestination.transformDestination(destination);
}
/**
* @param destination
*/
private void fireDemandEvent() {
demandListener.onEvent( new ProducerDemandEvent(destination, isInDemand()));
}
public boolean isInDemand() {
return consumerCount>0;
}
public ProducerDemandListener getDemandListener() {
return demandListener;
}
synchronized public void setDemandListener(ProducerDemandListener demandListener) {
this.demandListener = demandListener;
fireDemandEvent();
}
public void start() throws JMSException {
if (started.commit(false, true)) {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(destination.getTopicForConsumerAdvisory());
consumer.setMessageListener(new MessageListener(){
public void onMessage(Message msg) {
process(msg);
}
});
}
}
public void stop() throws JMSException {
if (started.commit(true, false)) {
if (session != null) {
session.close();
}
}
}
protected void process(Message msg) {
if (msg instanceof ObjectMessage) {
try {
ConsumerInfo info = (ConsumerInfo) ((ObjectMessage) msg).getObject();
ConsumerAdvisoryEvent event = new ConsumerAdvisoryEvent(info);
boolean inDemand = isInDemand();
if ( info.isStarted() ) {
consumerCount++;
} else {
consumerCount--;
}
// Notify listener if there was a change in demand.
if (inDemand ^ isInDemand() && demandListener != null) {
fireDemandEvent();
}
} catch (JMSException e) {
log.error("Failed to process message: " + msg);
}
}
}
}