001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.activemq;
019
020import java.util.concurrent.atomic.AtomicInteger;
021
022import javax.jms.ConnectionConsumer;
023import javax.jms.IllegalStateException;
024import javax.jms.JMSException;
025import javax.jms.ServerSession;
026import javax.jms.ServerSessionPool;
027import javax.jms.Session;
028
029import org.apache.activemq.command.ConsumerInfo;
030import org.apache.activemq.command.MessageDispatch;
031
032/**
033 * For application servers, <CODE>Connection</CODE> objects provide a special
034 * facility for creating a <CODE>ConnectionConsumer</CODE> (optional). The
035 * messages it is to consume are specified by a <CODE>Destination</CODE> and a
036 * message selector. In addition, a <CODE>ConnectionConsumer</CODE> must be
037 * given a <CODE>ServerSessionPool</CODE> to use for processing its messages.
038 * <p/>
039 * <P>
040 * Normally, when traffic is light, a <CODE>ConnectionConsumer</CODE> gets a
041 * <CODE>ServerSession</CODE> from its pool, loads it with a single message,
042 * and starts it. As traffic picks up, messages can back up. If this happens, a
043 * <CODE>ConnectionConsumer</CODE> can load each <CODE>ServerSession</CODE>
044 * with more than one message. This reduces the thread context switches and
045 * minimizes resource use at the expense of some serialization of message
046 * processing.
047 * 
048 * @see javax.jms.Connection#createConnectionConsumer
049 * @see javax.jms.Connection#createDurableConnectionConsumer
050 * @see javax.jms.QueueConnection#createConnectionConsumer
051 * @see javax.jms.TopicConnection#createConnectionConsumer
052 * @see javax.jms.TopicConnection#createDurableConnectionConsumer
053 */
054
055public class ActiveMQConnectionConsumer implements ConnectionConsumer, ActiveMQDispatcher {
056
057    private ActiveMQConnection connection;
058    private ServerSessionPool sessionPool;
059    private ConsumerInfo consumerInfo;
060    private boolean closed;
061
062    /**
063     * Create a ConnectionConsumer
064     * 
065     * @param theConnection
066     * @param theSessionPool
067     * @param theConsumerInfo
068     * @throws JMSException
069     */
070    protected ActiveMQConnectionConsumer(ActiveMQConnection theConnection, ServerSessionPool theSessionPool, ConsumerInfo theConsumerInfo) throws JMSException {
071        this.connection = theConnection;
072        this.sessionPool = theSessionPool;
073        this.consumerInfo = theConsumerInfo;
074
075        this.connection.addConnectionConsumer(this);
076        this.connection.addDispatcher(consumerInfo.getConsumerId(), this);
077        this.connection.syncSendPacket(this.consumerInfo);
078    }
079
080    /**
081     * Gets the server session pool associated with this connection consumer.
082     * 
083     * @return the server session pool used by this connection consumer
084     * @throws JMSException if the JMS provider fails to get the server session
085     *                 pool associated with this consumer due to some internal
086     *                 error.
087     */
088
089    public ServerSessionPool getServerSessionPool() throws JMSException {
090        if (closed) {
091            throw new IllegalStateException("The Connection Consumer is closed");
092        }
093        return this.sessionPool;
094    }
095
096    /**
097     * Closes the connection consumer. <p/>
098     * <P>
099     * Since a provider may allocate some resources on behalf of a connection
100     * consumer outside the Java virtual machine, clients should close these
101     * resources when they are not needed. Relying on garbage collection to
102     * eventually reclaim these resources may not be timely enough.
103     * 
104     * @throws JMSException
105     */
106
107    public void close() throws JMSException {
108        if (!closed) {
109            dispose();
110            this.connection.asyncSendPacket(this.consumerInfo.createRemoveCommand());
111        }
112
113    }
114
115    public void dispose() {
116        if (!closed) {
117            this.connection.removeDispatcher(consumerInfo.getConsumerId());
118            this.connection.removeConnectionConsumer(this);
119            closed = true;
120        }
121    }
122
123    public void dispatch(MessageDispatch messageDispatch) {
124        try {
125            messageDispatch.setConsumer(this);
126
127            ServerSession serverSession = sessionPool.getServerSession();
128            Session s = serverSession.getSession();
129            ActiveMQSession session = null;
130
131            if (s instanceof ActiveMQSession) {
132                session = (ActiveMQSession)s;
133            } else if (s instanceof ActiveMQTopicSession) {
134                ActiveMQTopicSession topicSession = (ActiveMQTopicSession)s;
135                session = (ActiveMQSession)topicSession.getNext();
136            } else if (s instanceof ActiveMQQueueSession) {
137                ActiveMQQueueSession queueSession = (ActiveMQQueueSession)s;
138                session = (ActiveMQSession)queueSession.getNext();
139            } else {
140                connection.onClientInternalException(new JMSException("Session pool provided an invalid session type: " + s.getClass()));
141                return;
142            }
143
144            session.dispatch(messageDispatch);
145            serverSession.start();
146        } catch (JMSException e) {
147            connection.onAsyncException(e);
148        }
149    }
150
151    public String toString() {
152        return "ActiveMQConnectionConsumer { value=" + consumerInfo.getConsumerId() + " }";
153    }
154
155    public void clearMessagesInProgress(AtomicInteger transportInterruptionProcessingComplete) {
156        // future: may want to deal with rollback of in progress messages to track re deliveries
157        // before indicating that all is complete.        
158    }
159
160    public ConsumerInfo getConsumerInfo() {
161        return consumerInfo;
162    }
163}