mq5.0-source.main.mq-client.src.main.java.com.sun.messaging.jmq.jmsclient.SessionImpl Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* %W% %G%
*/
package com.sun.messaging.jmq.jmsclient;
import java.io.*;
import javax.jms.*;
import java.util.*;
import java.io.IOException;
import java.io.PrintStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.messaging.jmq.io.*;
import com.sun.messaging.jmq.util.JMQXid;
import com.sun.messaging.jmq.util.timer.WakeupableTimer;
import com.sun.messaging.jmq.util.timer.TimerEventHandler;
import com.sun.messaging.AdministeredObject;
import com.sun.messaging.jms.ra.api.JMSRAManagedConnection;
import com.sun.messaging.jms.ra.api.JMSRAXASession;
import com.sun.messaging.jmq.jmsclient.resources.ClientResources;
import java.util.logging.Logger;
import java.util.logging.Level;
/** A JMS Session is a single threaded context for producing and consuming
* messages. Although it may allocate provider resources outside the Java
* virtual machine, it is considered a light-weight JMS object.
*
*
A session serves several purposes:
*
*
* - It is a factory for its message producers and consumers.
*
- It supplies provider-optimized message factories.
*
- It supports a single series of transactions that combine work
* spanning its producers and consumers into atomic units.
*
- A session defines a serial order for the messages it consumes and
* the messages it produces.
*
- A session retains messages it consumes until they have been
* acknowledged.
*
- A session serializes execution of message listeners registered with
* it's message consumers.
*
*
* A session can create and service multiple message producers and
* consumers.
*
*
One typical use is to have a thread block on a synchronous
* MessageConsumer until a message arrives. The thread may then use one or
* more of the Session's MessageProducers.
*
*
If a client desires to have one thread producing messages while others
* consume them, the client should use a separate Session for its producing
* thread.
*
*
Once a connection has been started, any session with a registered
* message listener(s) is dedicated to the thread of control that delivers
* messages to it. It is erroneous for client code to use this session
* or any of its constituent objects from another thread of control. The
* only exception to this is the use of the session or connection close
* method.
*
*
It should be easy for most clients to partition their work naturally
* into Sessions. This model allows clients to start simply and incrementally
* add message processing complexity as their need for concurrency grows.
*
*
The close method is the only session method that can be called
* while some other session method is being executed in another thread.
*
*
A session may be optionally specified as transacted. Each transacted
* session supports a single series of transactions. Each transaction groups
* a set of message sends and a set of message receives into an atomic unit
* of work. In effect, transactions organize a session's input message
* stream and output message stream into series of atomic units. When a
* transaction commits, its atomic unit of input is acknowledged and its
* associated atomic unit of output is sent. If a transaction rollback is
* done, its sent messages are destroyed and the session's input is
* automatically recovered.
*
*
The content of a transaction's input and output units is simply those
* messages that have been produced and consumed within the session's current
* transaction.
*
*
A transaction is completed using either its session's
* commit
or rollback
method. The completion of a
* session's current transaction automatically begins the next. The result
* is that a transacted session always has a current transaction within
* which its work is done.
*
*
JTS, or some other transaction monitor may be used to combine a
* session's transaction with transactions on other resources (databases,
* other JMS sessions, etc.). Since Java distributed transactions are
* controlled via JTA, use of the session's commit and rollback methods in
* this context is prohibited.
*
*
JMS does not require support for JTA; however, it does define how a
* provider supplies this support.
*
*
Although it is also possible for a JMS client to handle distributed
* transactions directly, it is unlikely that many JMS clients will do this.
* Support for JTA in JMS is targeted at systems vendors who will be
* integrating JMS into their application server products.
*
* @see javax.jms.QueueSession
* @see javax.jms.TopicSession
* @see javax.jms.XASession
*/
public class SessionImpl implements JMSRAXASession, Traceable, ContextableSession {
/** With this acknowledgement mode, the session automatically acknowledges
* a client's receipt of a message when it has either successfully
* returned from a call to receive or the message listener it has called
* to process the message successfully returns.
*/
//static final int AUTO_ACKNOWLEDGE = 1;
/** With this acknowledgement mode, the client acknowledges a message by
* calling a message's acknowledge method. Acknowledging a message
* acknowledges all messages that the Session has consumed.
*
*
When client acknowledgment mode is used, a client may build up a
* large number of unacknowledged messages while attempting to process
* them. A JMS provider should provide administrators with a way to
* limit client over-run so that clients are not driven to resource
* exhaustion and ensuing failure when some resource they are using
* is temporarily blocked.
*/
//static final int CLIENT_ACKNOWLEDGE = 2;
/** This acknowledgement mode instructs the session to lazily acknowledge
* the delivery of messages. This is likely to result in the delivery of
* some duplicate messages if JMS fails, it should only be used by
* consumers that are tolerant of duplicate messages. Its benefit is the
* reduction of session overhead achieved by minimizing the work the
* session does to prevent duplicates.
*/
//static final int DUPS_OK_ACKNOWLEDGE = 3;
private ServerSessionRunner serverSessionRunner = null;
private ConnectionConsumerImpl connectionConsumer = null;
// default values.
protected ConnectionImpl connection = null;
protected boolean isTransacted = false;
protected int acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
protected SessionReader sessionReader = null;
// protected SessionTransaction transaction = null;
protected SessionQueue sessionQueue = null;
protected ReadChannel readChannel = null;
//protected FlowControl flowControl = null;
protected Hashtable consumers = new Hashtable();
//producers are added here in case we need to recover
//the connection
protected Vector producers = new Vector();
protected WriteChannel writeChannel = null;
protected Long sessionId = null;
protected ProtocolHandler protocolHandler = null;
protected Transaction transaction = null;
protected boolean failoverOccurred = false;
protected int dupsOkLimit = 10; //dups ok non ack limit.
//flag to indicate if we need to check unacked message count
protected boolean isAckLimited = false;
//only used if isAckLimited set to true
protected int ackLimit = 100; //client non acknowledge limit
//number of messages not yet acked so far.
protected int ackCounter = 0;
//queue for unacked messages,
protected Vector unAckedMessageQueue = new Vector();
protected boolean isClosed = false;
protected boolean isStopped = false;
//is the XASession closed
protected boolean isXAClosed = false;
//is the XASession in rollback/commit
protected boolean isXAInRC = false;
//for flow control
protected boolean protectMode = false;
//XXX chiaming REVISIT: init when it is needed.
protected Hashtable browserConsumers = new Hashtable();
//for acknowledgement.
ReadWritePacket ackPkt = new ReadWritePacket();
ByteArrayOutputStream bos =
new ByteArrayOutputStream (ProtocolHandler.ACK_MESSAGE_BODY_SIZE);
DataOutputStream dos = new DataOutputStream(bos);
ReadWritePacket expirePkt = new ReadWritePacket();
ByteArrayOutputStream expireBos =
new ByteArrayOutputStream (ProtocolHandler.ACK_MESSAGE_BODY_SIZE);
DataOutputStream expireDos = new DataOutputStream(expireBos);
protected boolean setJMSXConsumerTXID = false;
protected boolean debug = Debug.debug;
//session sync object - used for session sync operations.
//Such as close, commit, recover, rollback, etc.
private Object syncObject = new Object();
private boolean inSyncState = false;
// reason why inSyncState is true
// we are only interested in knowing whether the other thread is executing close() or something else
private int inSyncStateOperation = INSYNCSTATE_NOTSET;
private static final int INSYNCSTATE_NOTSET = 0;
private static final int INSYNCSTATE_SESSION_CLOSING = 1;
private static final int INSYNCSTATE_CONSUMER_CLOSING = 2;
private static final int INSYNCSTATE_OTHER = 3;
//session sync object to replace synchronized (this).
//application can sync the session object and interfere the client runtime.
//bug 6302418 - imqReconnectEnabled does not work well on cluster.
private Object sessionSyncObj = new Object();
protected boolean xaTxnMode = false;
//sync object used for endpoint consumers in the RA
private Object raEndpointSyncObj = new Object();
//this field is never used.
//private boolean raEndpointSession = false;
//RA mc object to acquire txn cntxt
private JMSRAManagedConnection mc = null;
private boolean isDedicatedToServerSession = false;
//XXX PROTOCOL 3.5
//SessionID assigned to this session by the broker.
private long brokerSessionID = -1;
private int TEST_ackCount = 0;
private int TEST_rxCount = 0;
//if set, dups ok acks when reached unacked limit or session Q is empty.
protected boolean dupsOkAckOnEmptyQueue = false;
//if set, dups ok acks only when reached unack limit.
protected boolean dupsOkAckOnLimit = false;
//if set, dups ok acks when reached unack limit or max timeout.
protected boolean dupsOkAckOnTimeout = false;
//dups ok timeout value -- to set to session reader.
protected long dupsOkAckTimeout = 0;
//dups ok timestamp
protected long dupsOkTimestamp = 0;
//created to prevent deadlock - bugid 4987018
protected Object dupsOkSyncObj = new Object();
//allow session extension flag. if set to true, MQ extensions are allowed.
//such as NO_ACK mode.
protected boolean allowExtensions = false;
private volatile boolean inResetState = false;
//connection logging domain name.
public static final String SESSION_LOGGER_NAME =
ConnectionImpl.ROOT_LOGGER_NAME + ".session";
protected static final Logger sessionLogger =
Logger.getLogger(SESSION_LOGGER_NAME,
ClientResources.CLIENT_RESOURCE_BUNDLE_NAME);
//flag to indicate that the remote broker where the consumer(s) consumes messages from
//had failed. Messages are not able to commit/acknowledge until rollback/recover.
//see recreateConsumers() for detail information.
protected volatile boolean remore_broker_failed = false;
//HACC -- rollback only for the current transaction
//this is set when ServerSessionRunner.run() received
//exception from listener or ack failed (bug 6667940).
protected volatile boolean isRollbackOnly = false;
//assigned when isRollbackOnly happened.
protected Throwable rollbackCause = null;
public static boolean autoStartTxn = Boolean.getBoolean("imq.autoStartTxn");
public static boolean noBlockUntilTxnCompletes = Boolean.getBoolean("imq.noBlockUntilTxnCompletes");
public static boolean noBlockOnAutoAckNPTopics = Boolean.getBoolean("imq.noBlockOnAutoAckNPTopics");
private static long waitTimeoutForConsumerCloseDone = (long)
Integer.getInteger("imqWaitTimeoutForConsumerCloseDone", 90000).intValue();
private ThreadLocal isMessageListener = new ThreadLocal();
private Object asyncSendLock = new Object();
private ArrayList asyncSends =
new ArrayList();
private boolean noAsyncSendCBProcessor = true;
protected WakeupableTimer asyncSendCBProcessor = null;
protected static final Exception asyncSendWaitTimeoutEx =
getAsyncSendWaitTimeoutException();
protected static final Exception noAsyncSendCBProcessorEx =
getNoAsyncSendCBProcessorException();
protected static final Exception connectionBrokenEx =
getConnectionBrokenException();
private static Exception getAsyncSendWaitTimeoutException() {
String emsg = AdministeredObject.cr.getKString(
ClientResources.X_ASYNC_SEND_COMPLETION_WAIT_TIMEOUT);
return new JMSException(emsg,
ClientResources.X_ASYNC_SEND_COMPLETION_WAIT_TIMEOUT);
}
private static Exception getNoAsyncSendCBProcessorException() {
String emsg = AdministeredObject.cr.getKString(
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
return new javax.jms.IllegalStateException(emsg,
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
}
private static Exception getConnectionBrokenException() {
String emsg = AdministeredObject.cr.getKString(
ClientResources.E_CONNECTION_BROKEN);
return new JMSException(emsg, ClientResources.E_CONNECTION_BROKEN);
}
protected SessionImpl() {}
public SessionImpl(ConnectionImpl connection) throws JMSException {
this (connection, false, Session.AUTO_ACKNOWLEDGE, false, null);
}
/**
* Constructor for standard JMS sessions.
* @param connection
* @param isTransacted
* @param acknowledgeMode
* @throws JMSException
*/
public SessionImpl
(ConnectionImpl connection, boolean isTransacted, int acknowledgeMode)
throws JMSException {
this (connection, isTransacted, acknowledgeMode, false, null);
}
public SessionImpl
(ConnectionImpl connection, boolean isTransacted,
int acknowledgeMode, JMSRAManagedConnection mc)
throws JMSException {
this (connection, isTransacted, acknowledgeMode, false, mc);
}
/**
* Constructor for MQ extended sessions. Use this constructor for non
* transacted MQ extended session -- such as NO_ACKNOWLEDGE mode.
* @param connection
* @param acknowledgeMode
* @throws JMSException
*/
public SessionImpl
(ConnectionImpl connection, int acknowledgeMode) throws JMSException {
this (connection, false, acknowledgeMode, true, null);
}
public
SessionImpl (ConnectionImpl connection, boolean isTransacted,
int acknowledgeMode, boolean allowJMSExtension, JMSRAManagedConnection mc)
throws JMSException {
try {
//session's queue data structure. -- 6089070
sessionQueue = new SessionQueue();
sessionQueue.validateQueue();
//set allow extension flag.
this.allowExtensions = allowJMSExtension;
this.connection = connection;
/**
* check acknowledge mode
*/
if (isTransacted == false) {
if (acknowledgeMode == 0) {
acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
}
checkAckMode(acknowledgeMode);
}
this.writeChannel = connection.getWriteChannel();
this.readChannel = connection.getReadChannel();
this.protocolHandler = connection.getProtocolHandler();
this.isTransacted = isTransacted;
this.acknowledgeMode = acknowledgeMode;
sessionId = connection.getNextSessionId();
if (mc != null) {
this.mc = mc;
}
init();
logLifeCycle(ClientResources.I_SESSION_CREATED);
} catch (JMSException jmse) {
ExceptionHandler.throwJMSException(jmse);
}
}
/**
* Check ack mode.
*/
private void checkAckMode (int ackMode) throws JMSException {
if ( ackMode != Session.AUTO_ACKNOWLEDGE &&
ackMode != Session.CLIENT_ACKNOWLEDGE &&
ackMode != Session.DUPS_OK_ACKNOWLEDGE ) {
if ( this.allowExtensions == true ) {
//if this is MQ extension, NO_ACK mode is allowed.
if (ackMode == com.sun.messaging.jms.Session.NO_ACKNOWLEDGE) {
if (connection.getBrokerProtocolLevel() <= PacketType.VERSION350) {
String errorString = AdministeredObject.cr.getKString(
ClientResources.X_BROKER_NOT_SUPPORT_NO_ACK_MODE,
connection.getBrokerVersion());
JMSException jmse = new com.sun.messaging.jms.JMSException(
errorString,
ClientResources.X_BROKER_NOT_SUPPORT_NO_ACK_MODE);
ExceptionHandler.throwJMSException(jmse);
}
return;
}
}
String ackModeStr = String.valueOf(ackMode);
String errorString = AdministeredObject.cr.getKString(ClientResources.X_INVALID_ACKNOWLEDGE_MODE, ackModeStr);
JMSException jmse =
new JMSException (errorString,
ClientResources.X_INVALID_ACKNOWLEDGE_MODE);
ExceptionHandler.throwJMSException(jmse);
}
}
private void init() throws JMSException {
serverSessionRunner = new ServerSessionRunner(this, null);
//XXX PROTOCOL3.5
if (connection.getBrokerProtocolLevel() >= PacketType.VERSION350) {
protocolHandler.createSession(this);
}
//construct a transaction object to handle transactions
//handle differently if under an mc
if (isTransacted) {
if (mc == null) {
//setup Transaction delegate for local txns
transaction = new Transaction(this, true);
} else {
transaction = new Transaction(this, false);
if (mc.xaTransactionStarted()) {
transaction.setTransactionID(mc.getTransactionID());
xaTxnMode = true;
}// else {
// transaction.startNewLocalTransaction();
//}
}
}
/**
* move the following statement to the begining of the constructor.
* there seems to have a jdk bug that caused the usage of this
* object's internal data structure. (6174742, 6089070)
*/
//sessionQueue = new SessionQueue();
//if connection is in stop mode, lock the queue
if ( connection.getIsStopped() ) {
sessionQueue.setIsLocked( true );
}
//construct unAckedPacketQueue if the session is CLIENT_ACKNOWLEDGE
//mode or DUPS_OK_ACKNOWLEDGE mode or transacted session.
//XXX:GT TBF
/*
if ( acknowledgeMode == Session.CLIENT_ACKNOWLEDGE ||
acknowledgeMode == Session.DUPS_OK_ACKNOWLEDGE ||
(isTransacted ) ) {
unAckedMessageQueue = new Vector();
}
*/
//put to the readQTable
connection.addToReadQTable (sessionId, sessionQueue);
//put to session table
connection.addSession ( this );
//set dups ok limit
this.dupsOkLimit = connection.getDupsOkLimit();
this.isAckLimited = connection.getIsAckLimited();
//set client ack limit
this.ackLimit = connection.getAckLimit();
// set setJMSXConsumerTXID flag
setJMSXConsumerTXID = connection.connectionMetaData.setJMSXConsumerTXID &&
isTransacted;
//protect mode
protectMode = connection.getProtectMode();
//ConnectionConsumer workaround 4715054
isDedicatedToServerSession = connection.getIsDedicatedToConnectionConsumer();
//dups ok init.
dupsOkInit();
//session reader blocks on the session queue and dispatch pkt to the
//receivers.
sessionReader = new SessionReader (this);
if (isDedicatedToServerSession) {
sessionReader.close();
}
else {
//start the thread. It is blocked on session queue if queue is locked.
sessionReader.start();
}
}
/**
* dups ok ack init.
* default for appp client: dupsOkAckOnTimeout is set to true.
* dupsOkAckTimeout is set to 7000 milli secs.
* for MDB: dupsOkAckOnEmptyQueue is always set to true.
*/
protected void dupsOkInit() {
//set only if not transacted mode && not MDB
if ( (isTransacted == false) &&
(acknowledgeMode == Session.DUPS_OK_ACKNOWLEDGE) ) {
if ( isDedicatedToServerSession ) {
dupsOkAckOnEmptyQueue = true;
} else {
//get dups ok ack flag.
dupsOkAckOnEmptyQueue = connection.dupsOkAckOnEmptyQueue;
if ( dupsOkAckOnEmptyQueue == false ) {
//get timeout value -- default is set to 7000 milli secs
dupsOkAckTimeout = connection.dupsOkAckTimeout;
if ( dupsOkAckTimeout > 0 ) {
dupsOkAckOnTimeout = true;
} else {
dupsOkAckOnLimit = true;
dupsOkAckTimeout = 0;
}
}
}
}
if ( debug ) {
if ( dupsOkAckOnTimeout || dupsOkAckOnEmptyQueue || dupsOkAckOnLimit ) {
Debug.println("*** dupsOkAckOnEmptyQueue: " +
dupsOkAckOnEmptyQueue);
Debug.println("*** dupsOkAckOnTimeout: " + dupsOkAckOnTimeout);
Debug.println("*** dupsOkAckTimeout: " + dupsOkAckTimeout);
Debug.println("*** dupsOkAckOnLimit: " + dupsOkAckOnLimit);
} else {
Debug.println("*** Session ackMode: " + acknowledgeMode);
}
}
}
protected void switchOnXATransaction() throws JMSException {
//Switching from a local transaction to an XA transaction
//will force an implicit rollback -- i.e. have to commit before switching
// else work done so far is discarded
//unless an xaTxn has already been started on this session (and possibly suspended)
if (xaTxnMode)
return;
if (isTransacted) {
setInSyncState();
try {
receiveRollback();
transaction.rollbackToXA();
} finally {
//This will release sync state.
releaseInSyncState();
}
}
if (transaction == null) {
//setup Transaction delegate for txns (local = false)
transaction = new Transaction(this, false);
//Indicate that Session is transacted
isTransacted = true;
setJMSXConsumerTXID = connection.connectionMetaData.setJMSXConsumerTXID;
}
//Indicate that we are now in a dist txn
xaTxnMode = true;
}
//Called from XAResource -- prepare() and commit() only
protected void switchOffXATransaction() {
//No longer needs to be in a dist txn
xaTxnMode = false;
//Done with Transaction delegate!
isTransacted = false;
transaction = null;
//XXX:GT TBF if we need to be able to switch back to a transacted session
}
protected /*synchronized*/ void
addMessageConsumer( MessageConsumerImpl consumer ) throws JMSException {
//XXX PROTOCOL2.1
/*if (serverSessionRunner.getMessageListener() != null) {
String errorString = AdministeredObject.cr.getKString(AdministeredObject.cr.X_SVRSESSION_MESSAGECONSUMER);
throw new javax.jms.IllegalStateException(errorString, AdministeredObject.cr.X_SVRSESSION_MESSAGECONSUMER);
}*/
consumers.put (consumer.interestId, consumer);
}
//ConnectionConsumer workaround 4715054
protected void
checkBrowserCreation () throws JMSException {
if (isDedicatedToServerSession) {
checkConsumerCreation();
}
}
protected void
checkConsumerCreation () throws JMSException {
if (isDedicatedToServerSession || serverSessionRunner.getMessageListener() != null) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_SVRSESSION_MESSAGECONSUMER);
JMSException jmse =
new javax.jms.IllegalStateException(errorString,
ClientResources.X_SVRSESSION_MESSAGECONSUMER);
ExceptionHandler.throwJMSException(jmse);
}
}
protected /*synchronized*/ void
removeMessageConsumer ( MessageConsumerImpl consumer ) {
consumers.remove( consumer.interestId );
}
protected MessageConsumerImpl
getMessageConsumer ( Object key ) {
return (MessageConsumerImpl) consumers.get (key);
}
protected /*synchronized*/ void
addBrowserConsumer(BrowserConsumer consumer) {
consumer.getBrowser().addBrowserConsumer(consumer);
browserConsumers.put(consumer.interestId, consumer);
}
protected /*synchronized*/ void
removeBrowserConsumer(BrowserConsumer consumer) {
browserConsumers.remove(consumer.interestId);
consumer.getBrowser().removeBrowserConsumer(consumer);
}
protected BrowserConsumer
getBrowserConsumer ( Object key ) {
return (BrowserConsumer)browserConsumers.get (key);
}
/**
* Add the producer in the vector when it is created.
*/
protected void
addMessageProducer(MessageProducerImpl producer) {
producers.add(producer);
}
/**
* Remove the producer from the table when it is closed.
*/
protected void
removeMessageProducer(MessageProducerImpl producer) {
producers.remove(producer);
}
/**
* check destination existance in broker
*
* @param destination the destination to check
* @param selector the message selector
* @param browser if called for QueueBrowser create
*
* @exception InvalidDestinationException
* @exception InvalidSelectorException
* @exception JMSSecurityException
* @exception JMSException if fails
*/
protected void
verifyDestination(Destination destination, String selector, boolean browser)
throws JMSException {
protocolHandler.verifyDestination(destination, selector, browser);
}
/**
* get the content (list of SysMessageIDs) in the destination
*
* @param destination the destination
* @param selector the message selector
*
* @exception InvalidDestinationException
* @exception InvalidSelectorException
* @exception JMSSecurityException
* @exception JMSException
*/
/*protected SysMessageID[]
getMessageIdSet(Destination destination, String selector)
throws JMSException {
return protocolHandler.browse(destination, selector);
}*/
//XXX PROTOCOL2.1
protected SysMessageID[]
getMessageIdSet(Consumer consumer) throws JMSException {
return protocolHandler.browse (consumer);
}
/**
* request deliver all messages listed (SysMessageIDs) in the
* ByteArrayOutputStream to the browser consumer
*
* @param bos the ByteArrayOutputStream constains list of SysMessaegIDs
* @param consumer the BrowserConsumer who interest the messages
*
* @exception JMSException
*/
protected boolean requestMessages(ByteArrayOutputStream bos,
BrowserConsumer consumer) throws JMSException {
return protocolHandler.deliver(bos, consumer);
}
protected ProtocolHandler
getProtocolHandler() {
return connection.getProtocolHandler();
}
protected Long
getSessionId() {
return sessionId;
}
protected ConnectionImpl
getConnection() {
return connection;
}
protected void
addAsyncSendCallback(AsyncSendCallback cb) throws JMSException {
if (isClosed) {
String emsg = AdministeredObject.cr.getKString(
ClientResources.X_SESSION_CLOSED);
JMSException jmse = new javax.jms.IllegalStateException(emsg,
ClientResources.X_SESSION_CLOSED);
ExceptionHandler.throwJMSException(jmse);
}
createAsyncSendCBProcessor();
if (noAsyncSendCBProcessor) {
String emsg = AdministeredObject.cr.getKString(
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
JMSException jmse = new javax.jms.IllegalStateException(emsg,
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
ExceptionHandler.throwJMSException(jmse);
}
synchronized(asyncSendLock) {
asyncSends.add(cb);
}
}
private void createAsyncSendCBProcessor() {
synchronized(asyncSendLock) {
if (asyncSendCBProcessor == null) {
asyncSendCBProcessor = new WakeupableTimer(
"AsyncSendListenerProcessor["+toString()+"]",
new AsyncSendTimerEventHandler(),
0L, 0L,
AdministeredObject.cr.getKString(
ClientResources.I_ASYNC_SEND_LISTENER_PROCESSOR_THREAD_START)+
"["+this.toString()+"]",
AdministeredObject.cr.getKString(
ClientResources.I_ASYNC_SEND_LISTENER_PROCESSOR_THREAD_EXIT)+
"["+this.toString()+"]");
noAsyncSendCBProcessor = false;
}
}
}
private class AsyncSendTimerEventHandler implements TimerEventHandler {
public AsyncSendTimerEventHandler() {
}
public void handleOOMError(Throwable e) {
sessionLogger.log(Level.WARNING,
"OutOfMemoryError occurred in AsyncSendListenerProcessor thread["+
SessionImpl.this.toString()+"]", e);
}
public void handleLogInfo(String msg) {
sessionLogger.log(Level.FINE, msg+"["+SessionImpl.this.toString()+"]");
}
public void handleLogWarn(String msg, Throwable e) {
sessionLogger.log(Level.WARNING, msg+"["+SessionImpl.this.toString()+"]", e);
}
public void handleLogError(String msg, Throwable e) {
sessionLogger.log(Level.WARNING, msg+"["+SessionImpl.this.toString()+"]", e);
}
public void handleTimerExit(Throwable e) {
if (isClosed) {
return;
}
synchronized(asyncSendLock) {
noAsyncSendCBProcessor = true;
}
String emsg = AdministeredObject.cr.getKString(
ClientResources.E_ASYNC_SEND_CALLBACK_THREAD_EXIT);
sessionLogger.log(Level.SEVERE, emsg+"["+SessionImpl.this.toString()+"]", e);
}
public long runTask() {
sessionLogger.log(Level.FINEST, "asyncSendCBProcessor start runTask["+SessionImpl.this.toString()+"]");
long ret = 0L; //wait for next wakeup
AsyncSendCallback cb = null;
ArrayList calls = new ArrayList();
synchronized(asyncSendLock) {
if (asyncSends.size() == 0) {
sessionLogger.log(Level.FINEST, "asyncSendCBProcessor end runTask["+SessionImpl.this.toString()+"] ret=0L");
return 0L;
}
Iterator itr = asyncSends.iterator();
while (itr.hasNext()) {
cb = itr.next();
if (!cb.hasSendReturned()) {
break;
}
if (cb.isCompleted() || cb.isExceptioned()) {
calls.add(cb);
} else {
if (!cb.isOnAckWait()) {
break;
}
ret = connection.getAsyncSendCompletionWaitTimeout();
if (!cb.isTimedout()) {
cb.startTimeoutTimer();
break;
} else {
calls.add(cb);
}
}
}
}
Iterator itr = calls.iterator();
while (itr.hasNext()) {
cb = itr.next();
cb.callCompletionListener();
}
calls.clear();
sessionLogger.log(Level.FINEST, "asyncSendCBProcessor end runTask["+SessionImpl.this.toString()+"] ret="+ret);
return ret;
}
}
protected void
removeAsyncSendCallback(AsyncSendCallback cb) {
synchronized(asyncSendLock) {
asyncSends.remove(cb);
asyncSendLock.notifyAll();
}
}
protected void
writeJMSMessage (Message message, AsyncSendCallback asynccb)
throws JMSException {
//not calling checkSessionState just for performance reason ...
if (isClosed) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_SESSION_CLOSED);
JMSException jmse =
new javax.jms.IllegalStateException(errorString, ClientResources.X_SESSION_CLOSED);
ExceptionHandler.throwJMSException(jmse);
}
if (asynccb != null && xaTxnMode) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_ASYNC_SEND_XA_TXN);
JMSException jmse = new JMSException(errorString, ClientResources.X_ASYNC_SEND_XA_TXN);
ExceptionHandler.throwJMSException(jmse);
}
if (isTransacted) {
checkFailOver();
transaction.send(message, asynccb);
} else {
writeChannel.writeJMSMessage(message, asynccb);
}
}
protected void
waitAllAsyncSendCompletion(MessageProducerImpl producer)
throws JMSException {
checkPermissionForAsyncSend();
if (connection.isBroken()) {
synchronized(asyncSendLock) {
asyncSendLock.notifyAll();
}
}
AsyncSendCallback cb = null;
long endtime = System.currentTimeMillis() +
connection.getAsyncSendCompletionWaitTimeout();
long remaining = 0L;
if (producer == null) {
int asize = 0;
synchronized(asyncSendLock) {
asize = asyncSends.size();
}
boolean firstround = true;
while (asize > 0) {
remaining = connection.getAsyncSendCompletionWaitTimeout();
synchronized(asyncSendLock) {
boolean logged = false;
while (asyncSends.size() > 0 && remaining > 0L &&
(!firstround || !connection.isBroken() ||
!noAsyncSendCBProcessor)) {
try {
if (!logged) {
String msg = AdministeredObject.cr.getKString(
ClientResources.I_WAIT_ASYNC_SENDS_COMPLETE_SESSION,
String.valueOf(remaining), this);
sessionLogger.log(Level.INFO, msg);
logged = true;
}
asyncSendLock.wait(remaining);
} catch (InterruptedException e) {
}
remaining = endtime - System.currentTimeMillis();
}
if (connection.isBroken()) {
asyncSendLock.notifyAll();
}
if (asyncSends.size() > 0) {
Exception ex = (noAsyncSendCBProcessor ?
noAsyncSendCBProcessorEx:asyncSendWaitTimeoutEx);
if (connection.isBroken()) {
ex = connectionBrokenEx;
}
ex.fillInStackTrace();
Iterator itr = asyncSends.iterator();
while (itr.hasNext()) {
cb = itr.next();
cb.processException(ex);
}
if (noAsyncSendCBProcessor) {
String emsg = AdministeredObject.cr.getKString(
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
JMSException jmse = new JMSException(emsg,
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
ExceptionHandler.throwJMSException(jmse);
}
}
firstround = false;
asize = asyncSends.size();
} //synchronized
}
} else {
boolean loggered = false;
boolean found = true;
boolean firstround = true;
remaining = connection.getAsyncSendCompletionWaitTimeout();
while (found) {
found = false;
synchronized(asyncSendLock) {
Iterator itr = asyncSends.iterator();
while (itr.hasNext()) {
cb = itr.next();
if (cb.producer == producer) {
found = true;
if (remaining == 0L ||
(firstround &&
(connection.isBroken() ||
noAsyncSendCBProcessor))) {
Exception ex = (noAsyncSendCBProcessor ?
noAsyncSendCBProcessorEx:asyncSendWaitTimeoutEx);
if (connection.isBroken()) {
ex = connectionBrokenEx;
asyncSendLock.notifyAll();
}
ex.fillInStackTrace();
cb.processException(ex);
if (remaining == 0L) {
remaining = connection.getAsyncSendCompletionWaitTimeout();
loggered = false;
break;
}
} else {
if (noAsyncSendCBProcessor) {
String emsg = AdministeredObject.cr.getKString(
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
JMSException jmse = new JMSException(emsg,
ClientResources.X_NO_ASYNC_SEND_LISTENER_PROCESSOR_THREAD);
ExceptionHandler.throwJMSException(jmse);
}
try {
if (!loggered) {
String msg = AdministeredObject.cr.getKString(
ClientResources.I_WAIT_ASYNC_SENDS_COMPLETE_PRODUCER,
String.valueOf(remaining), producer);
sessionLogger.log(Level.FINE, msg);
loggered = true;
}
asyncSendLock.wait(remaining);
} catch (InterruptedException e) {
}
remaining = endtime - System.currentTimeMillis();
break;
}
}
}
firstround = false;
}
}
}
}
public void _stopFromRA() throws JMSException {
synchronized(raEndpointSyncObj) {
/**
* EndpointConsumer.stopMessageConsumer() calls this method to stop the
* SessionReader.
*
* Do not need to wait because the caller must wait for OnMessageRunners to ruturn.
* bug ID 6526078 - MQ thread deadlock during AS shutdown.
*/
stop(false);
}
}
protected void stop() throws JMSException {
stop (true);
}
/**
* Called by Connection.stop(), Session.close().
*/
protected void
stop(boolean doWait) throws JMSException {
if ( isStopped || isClosed ) {
return;
}
checkPermission();
synchronized ( sessionSyncObj ) {
sessionQueue.stop(doWait);
serverSessionRunner.serverSessionStop();
//lock receive queue for each consumer
//MessageConsumerImpl consumer = null;
//Enumeration enum = consumers.elements();
//while (enum.hasMoreElements()) {
// consumer = (MessageConsumerImpl) enum.nextElement();
// consumer.stop();
//}
MessageConsumerImpl[] consumerArray = (MessageConsumerImpl[])
consumers.values().toArray(new MessageConsumerImpl[0]);
for (int i = 0; i < consumerArray.length; i++) {
if ( doWait ) {
consumerArray[i].stop();
} else {
consumerArray[i].stopNoWait();
}
}
isStopped = true;
}
}
protected void setConnectionConsumer(ConnectionConsumerImpl cc) {
connectionConsumer = cc;
}
protected void resetServerSessionRunner() {
resetServerSessionRunner(true);
}
/**
* @param timeout in seconds
*/
protected void resetServerSessionRunner(boolean resetState) {
ServerSessionRunner ssr = serverSessionRunner;
if (ssr != null) ssr.reset(resetState);
}
/**
* Reset this session.
* 1. Stop session reader.
* 2. Clear session queue.
* 3. Clear Consumer queues.
* 4. Clear unAckedMessageQueue.
* 5. Restart session reader.
*
* bug ID 6302418 -- there is really no reason to synchronize this method.
*/
protected /**synchronized**/ void reset() throws JMSException {
//1. Stop session reader. Wait does not provide any benefit here.
try {
//fix for bug 6578150 - session.close() takes too long
//we are in recover state, the world is stopped already
//stop(false);
// 2. Clear session queue. -- PRIORITYQ
sessionQueue.clear();
// 3. Clear Consumer queue.
MessageConsumerImpl consumer = null;
Enumeration enum2 = consumers.elements();
while (enum2.hasMoreElements()) {
consumer = (MessageConsumerImpl) enum2.nextElement();
// PRIORITYQ
consumer.receiveQueue.clear();
}
// 4. Clear unacked message queue
if (unAckedMessageQueue != null) {
unAckedMessageQueue.removeAllElements();
}
// 5.Restart session reader
//we did not stop
//start();
// 6. clean up the consumers table. The objects will be
// re-constructed
// in ConnectionRecover. -- bug ID 6311895.
consumers.clear();
// 7. reset this flag to false since we will re-create all the
// sessions/consumers.
this.remore_broker_failed = false;
//8. HACC -- set reset flag for serversession state.
if (serverSessionRunner != null) {
serverSessionRunner.reset();
}
closeBrowserConsumers();
} finally {
sessionQueue.start();
this.isStopped = false;
}
}
protected void recreateSession() throws JMSException {
String emsg = AdministeredObject.cr.getKString(
AdministeredObject.cr.X_CONNECTION_FAILOVER);
JMSException ex = new JMSException(emsg, ClientResources.X_CONNECTION_FAILOVER);
AsyncSendCallback cb = null;
synchronized(asyncSendLock) {
Iterator itr = asyncSends.iterator();
while (itr.hasNext()) {
cb = itr.next();
cb.processException(ex);
}
}
if (connection.getBrokerProtocolLevel() >= PacketType.VERSION350) {
protocolHandler.createSession(this);
}
if (isTransacted) {
if ( connection.isConnectedToHABroker ) {
//resolve the state of transaction.
//verifyHATransaction();
} else {
// Start a new transaction.
transaction = new Transaction(this, true);
}
}
failoverOccurred = true;
}
protected /** synchronized **/ void
start() throws JMSException {
synchronized (sessionSyncObj) {
//unlock session queue
sessionQueue.start();
serverSessionRunner.serverSessionRun();
//unlock receive queue for each consumer
/*MessageConsumerImpl consumer = null;
Enumeration enum = consumers.elements();
while (enum.hasMoreElements()) {
consumer = (MessageConsumerImpl) enum.nextElement();
consumer.start();
}*/
MessageConsumerImpl[] consumerArray = (MessageConsumerImpl[])
consumers.values().toArray(new
MessageConsumerImpl[0]);
for (int i = 0; i < consumerArray.length; i++) {
consumerArray[i].start();
}
isStopped = false;
}
}
/**
* Throws IllegalStateException if called from message listener.
*/
protected void
checkPermission() throws JMSException {
if ( Thread.currentThread() == sessionReader.sessionThread
|| Thread.currentThread() == serverSessionRunner.getCurrentThread()) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_ILLEGAL_STATE);
javax.jms.IllegalStateException jmse =
new javax.jms.IllegalStateException(
errorString, ClientResources.X_ILLEGAL_STATE);
ExceptionHandler.throwJMSException(jmse);
}
}
protected void
checkPermissionForAsyncSend() throws JMSException {
if (asyncSendCBProcessor != null &&
asyncSendCBProcessor.isTimerThread(Thread.currentThread())) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_ILLEGAL_STATE);
javax.jms.IllegalStateException jmse =
new javax.jms.IllegalStateException(
errorString, ClientResources.X_ILLEGAL_STATE);
ExceptionHandler.throwJMSException(jmse);
}
}
/*
* This method is called from the RA and serializes acknowledgement to
* avoid any MT violations on the Session which by rule can only be operated
* on by a single thread 'at a time'
* Since it has to do things almost exactly like a transacted session but isn't
* really one, the code from acknowledge() is reproduced here
* XXX:candidate to be refactored
*/
public void acknowledgeUndeliverableFromRAEndpoint(
MessageImpl message,
XAResourceForRA xar, boolean sendToDMQ) throws JMSException
{
synchronized (raEndpointSyncObj) {
//do this for flow control
readChannel.flowControl.messageDelivered();
Consumer consumer = (Consumer) consumers.get(
new Long(message.getInterestID()));
//We know it *does not* have a ConnectionConsumer
/*
if (consumer == null) {
// It may be a ConnectionConsumer...
consumer = connection.interestTable.getConsumer(
new Long(message.getInterestID()));
}
*/
readChannel.flowControl.messageDelivered(consumer);
TEST_rxCount++;
try {
setInSyncState();
//We do not care about rollback since if transacted
//it is in an XA transaction and the rollback will be via XA
//We get the transactionID from the XAResource that was
//passed in
//System.out.println("\t\tSessionImpl:acknowledgeFromRA-msg="+message.toString());
if (xar != null && xar.started()) {
synchronized (xar) {
//System.out.println("\t\tSessionImpl:ackWxar:txID="+xar.getTransactionID()+":bkrSessionId="+brokerSessionID);
ackPkt.setTransactionID(xar.getTransactionID() );
//System.out.println("\t\tSessionImpl:ackWxar:txID="+xar.getTransactionID()+"...done:bkrSessionId="+brokerSessionID);
}
} else {
//System.out.println("\t\tSessionImpl:ackWithoutxar:bkrSessionId="+brokerSessionID);
ackPkt.setTransactionID(0L);
//System.out.println("\t\tSessionImpl:ackWithoutxar...done:bkrSessionId="+brokerSessionID);
}
writeMessageID (message);
doAcknowledgeUndeliverable(true, sendToDMQ);
//System.out.println("\t\tSessionImpl:doAcknowledge...done:bkrSessionId="+brokerSessionID);
} finally {
releaseInSyncState();
}
}
}
/*
* This method is called from the RA and serializes acknowledgement to
* avoid any MT violations on the Session which by rule can only be operated
* on by a single thread 'at a time'
* Since it has to do things almost exactly like a transacted session but isn't
* really one, the code from acknowledge() is reproduced here
* XXX:candidate to be refactored
*/
public void acknowledgeFromRAEndpoint(MessageImpl message, XAResourceForRA xar) throws JMSException {
synchronized (raEndpointSyncObj) {
if (sessionLogger.isLoggable(Level.FINER)) {
logMessageDelivered(message);
}
//do this for flow control
readChannel.flowControl.messageDelivered();
Consumer consumer = (Consumer) consumers.get(
new Long(message.getInterestID()));
//We know it *does not* have a ConnectionConsumer
/*
if (consumer == null) {
// It may be a ConnectionConsumer...
consumer = connection.interestTable.getConsumer(
new Long(message.getInterestID()));
}
*/
readChannel.flowControl.messageDelivered(consumer);
TEST_rxCount++;
boolean requiresAckFromBroker = true;
Hashtable originalProps = null;
try {
setInSyncState();
//We do not care about rollback since if transacted
//it is in an XA transaction and the rollback will be via XA
//We get the transactionID from the XAResource that was
//passed in
//System.out.println("\t\tSessionImpl:acknowledgeFromRA-msg="+message.toString());
if (xar != null && xar.started()) {
synchronized (xar) {
//System.out.println("\t\tSessionImpl:ackWxar:txID="+xar.getTransactionID()+":bkrSessionId="+brokerSessionID);
ackPkt.setTransactionID(xar.getTransactionID() );
//System.out.println("\t\tSessionImpl:ackWxar:txID="+xar.getTransactionID()+"...done:bkrSessionId="+brokerSessionID);
}
// performance optimisation
// Do not wait for reply from broker when
// sending message acknowledgments in a transaction.
// Reliability will be ensured by blocking on commit.
requiresAckFromBroker = !noBlockUntilTxnCompletes;
} else {
//System.out.println("\t\tSessionImpl:ackWithoutxar:bkrSessionId="+brokerSessionID);
ackPkt.setTransactionID(0L);
//System.out.println("\t\tSessionImpl:ackWithoutxar...done:bkrSessionId="+brokerSessionID);
if (!isTransacted && acknowledgeMode == Session.AUTO_ACKNOWLEDGE) {
if ((consumer != null && !consumer.getDurable()) ||
noBlockOnAutoAckNPTopics) {
/* performance optimisation
Normally block when acknowledging messages
to avoid duplicates in the event of provider failure.
However, duplicates are not a problem for NP topic sessions,
as messages will not be re-dispatched if client reconnects after failure. */
Destination d = message.getJMSDestination();
if (!message._getPersistent() && d instanceof Topic) {
requiresAckFromBroker = false;
}
}
}
}
writeMessageID (message);
if (message.getClientRetries() > 0) {
try {
originalProps = ackPkt.getProperties();
} catch (Exception e) {}
Hashtable props = new Hashtable();
props.put(ConnectionMetaDataImpl.JMSXDeliveryCount, Integer.valueOf(message.getClientRetries()));
ackPkt.setProperties(props);
}
doAcknowledge(requiresAckFromBroker);
//System.out.println("\t\tSessionImpl:doAcknowledge...done:bkrSessionId="+brokerSessionID);
} finally {
if (originalProps != null)
ackPkt.setProperties(originalProps);
releaseInSyncState();
}
}
}
public void acknowledgeFromRAEndpoint(MessageImpl message) throws JMSException {
synchronized (raEndpointSyncObj) {
acknowledge(message);
}
}
public void _appTransactedAck(MessageImpl message) throws JMSException {
Consumer consumer = (Consumer)consumers.get(
new Long(message.getInterestID()));
if (consumer == null) {
String errorString = AdministeredObject.cr.getKString(AdministeredObject.cr.X_CONSUMER_CLOSED);
throw new JMSException (errorString, AdministeredObject.cr.X_CONSUMER_CLOSED);
}
if (!(consumer instanceof MessageConsumerImpl)) { //should not happen
throw new JMSException(
"Operation not supported for consumer type: "+consumer.getClass().getName());
}
acknowledge(message);
((MessageConsumerImpl)consumer).lastDeliveredID = message.getMessageID();
}
protected void doPrefetch(Consumer consumer) {
readChannel.flowControl.messageDelivered();
readChannel.flowControl.messageDelivered(consumer);
TEST_rxCount++;
}
protected void acknowledge(MessageImpl message) throws JMSException {
acknowledge(message, true);
}
/* This method is called after MessageListener.onMessage() is returned
* or receive() call returned.
*
* This method dispatch the ack action based on the session ack mode.
*/
protected void acknowledge(MessageImpl message, boolean prefetch) throws JMSException{
if ( sessionLogger.isLoggable(Level.FINER) ) {
//String pktType = PacketType.getString( ((MessageImpl) message).getPacket().getPacketType());
//String param = pktType + "," +
// ", ConsumerID=" + message.getInterestID() +
// ", " + toString();
this.logMessageDelivered(message);
}
if (prefetch) {
//do this for flow control
readChannel.flowControl.messageDelivered();
}
Consumer consumer = (Consumer) consumers.get(
new Long(message.getInterestID()));
if (consumer == null) {
// It may be a ConnectionConsumer...
consumer = connection.interestTable.getConsumer(
new Long(message.getInterestID()));
}
if (prefetch) {
readChannel.flowControl.messageDelivered(consumer);
TEST_rxCount++;
}
try {
setInSyncState();
if (isTransacted) {
transactedAcknowledge(message);
}
else { //non-transacted
switch (acknowledgeMode) {
case Session.AUTO_ACKNOWLEDGE:
autoAcknowledge(message,
(consumer == null ? true:consumer.getDurable()));
break;
case Session.CLIENT_ACKNOWLEDGE:
prepareClientAcknowledge(message);
break;
case Session.DUPS_OK_ACKNOWLEDGE:
if ( dupsOkAckOnTimeout ) {
syncedDupsOkAcknowledge(message);
} else {
dupsOkAcknowledge(message);
}
break;
case com.sun.messaging.jms.Session.NO_ACKNOWLEDGE:
//NO_ACKNOWLEDGE mode -- do nothing
break;
default:
autoAcknowledge(message,
(consumer == null ? true:consumer.getDurable()));
break;
} //switch
}
} catch (JMSException jmse) {
//we only care about auto/dups-ok mode
if ( (isTransacted == false) &&
(acknowledgeMode == Session.AUTO_ACKNOWLEDGE ||
acknowledgeMode == Session.DUPS_OK_ACKNOWLEDGE)) {
//check if this is caused by the remote broker failure.
if (isRemoteException (jmse)) {
//recreate consumers in the session
this.recreateConsumers();
ExceptionHandler.throwRemoteAcknowledgeException
(jmse, ClientResources.X_AUTO_ACK_FAILED_REMOTE);
//construct the precise exception
//String errorString = AdministeredObject.cr.getKString(
// ClientResources.X_AUTO_ACK_FAILED_REMOTE);
//JMSException newjmse = new com.sun.messaging.jms.JMSException(errorString,
// ClientResources.X_AUTO_ACK_FAILED_REMOTE);
//set exception link
//newjmse.setLinkedException(jmse);
//throw the exception
//ExceptionHandler.throwJMSException(newjmse);
}
}
//re-throw the old one.
throw jmse;
} finally {
releaseInSyncState();
}
}
protected void acknowledgeExpired(MessageImpl message) throws JMSException {
acknowledgeExpired(message, true);
}
protected void acknowledgeExpired(MessageImpl message, boolean prefetch)
throws JMSException {
if ( sessionLogger.isLoggable(Level.FINER) ) {
this.logMessageExpired(message);
}
acknowledgeDeadMessage(message, prefetch, 1 /*expired*/);
}
protected void acknowledgeUndeliverable(MessageImpl message) throws JMSException {
if ( sessionLogger.isLoggable(Level.FINER) ) {
this.logMessageUndeliverable(message);
}
acknowledgeDeadMessage(message, true, 0 /*undeliverable*/);
}
protected void acknowledgeDeadMessage(MessageImpl message,
boolean prefetch,
int deadReason)
throws JMSException {
synchronized(raEndpointSyncObj) {
if (prefetch) {
readChannel.flowControl.messageDelivered();
}
Consumer consumer = (Consumer)consumers.get(new Long(message.getInterestID()));
if (consumer == null) {
// It may be a ConnectionConsumer...
consumer = connection.interestTable.getConsumer(new Long(message.getInterestID()));
}
if (prefetch) {
readChannel.flowControl.messageDelivered(consumer);
TEST_rxCount++;
}
try {
expireDos.writeLong(message.getInterestID());
message.getMessageID().writeID(expireDos);
expireDos.flush();
expireBos.flush();
expirePkt.setMessageBody(expireBos.toByteArray());
ackPkt.setSendAcknowledge(false);
protocolHandler.acknowledgeUndeliverable(expirePkt, true, deadReason);
} catch (Exception e) {
String destName = "";
try {
if (consumer != null) {
destName = consumer.getDestName();
} else {
destName= message.getJMSDestination().toString();
}
} catch (Exception e1) {};
Object args[] = { message.getMessageID(), destName,
message.getInterestID(), e.getMessage() };
String ecode = (deadReason == 1 ?
ClientResources.X_EXPIRE_MSG_TO_DMQ:
ClientResources.X_UNDELIVERABLE_MSG_TO_DMQ);
String estr = AdministeredObject.cr.getKString(ecode, args);
JMSException jmse = new JMSException(estr, ecode);
jmse.setLinkedException(e);
throw jmse;
} finally {
expireBos.reset();
}
}//synchronized
}
private void logMessageExpired(MessageImpl message) {
logMessage(message, ClientResources.I_EXPIRED_MSG_BEFORE_DELIVER_TO_CONSUMER);
}
private void logMessageUndeliverable(MessageImpl message) {
logMessage(message, ClientResources.I_UNDELIVERABLE_MSG);
}
private void logMessageDelivered(MessageImpl message) {
logMessage(message, ClientResources.I_CONSUMER_MESSAGE_DELIVERED);
}
private void logMessage(MessageImpl message, String key) {
try {
if (sessionLogger.isLoggable(Level.FINER)) {
String param = makeDebugStringForMessage(message);
sessionLogger.log(Level.FINER, key, param);
if (sessionLogger.isLoggable(Level.FINEST)) {
param = "MQTrace=MessageConsumer" +
", ConsumerID=" + ((MessageImpl) message).getInterestID() +
", Message=" + message.toString();
sessionLogger.log(Level.FINEST, key, param);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private String makeDebugStringForMessage(MessageImpl message) {
try {
com.sun.messaging.Destination mqDest = (com.sun.messaging.Destination)
message.getJMSDestination();
String domain = (mqDest.isQueue() ? "Queue:" : "Topic:");
String pktType = PacketType.getString(((MessageImpl)message)
.getPacket().getPacketType());
return ("MQTrace=MessageConsumer" +
", ThreadID=" + Thread.currentThread().getId() +
", ClientID=" + this.connection.getClientID() +
", ConnectionID=" + this.connection.getConnectionID() +
", SessionID=" + this.getBrokerSessionID() +
", ConsumerID=" + ((MessageImpl) message).getInterestID() +
", Destination=" +domain+mqDest.getName() +
", MessageID=" + message.getJMSMessageID() +
", MessageType=" + pktType);
} catch (Exception e) {
e.printStackTrace();
return "[MessageID="+message.getMessageID()+", ConsumerID="+message.getInterestID()+"]";
}
}
/**
* auto acknowledge.
*
* @param message
* the message to be acknowledged.
*/
protected void autoAcknowledge (MessageImpl message, boolean isdurable) throws JMSException {
//param true if require broker to ack back.
writeMessageID (message);
boolean requireAckFromBroker = true;
if (!isdurable || noBlockOnAutoAckNPTopics) {
// performance optimisation
// Normally block when acknowledging messages
// to avoid duplicates in the event of provider failure.
// However, duplicates are not a problem for NP topic sessions,
// as messages will not be re-dispatched if client reconnects after failure.
//
Destination d = message.getJMSDestination();
if (!message._getPersistent() && d instanceof Topic) {
requireAckFromBroker = false;
}
}
doAcknowledge(requireAckFromBroker);
}
/**
* For transacted session, each message is acknowledged by calling
* this method.
*
* @param message the message to be acknowledged.
*/
protected void
transactedAcknowledge (MessageImpl message) throws JMSException {
Hashtable originalProps = null;
try {
// put on ack list in case we have to rollback.
boolean isAddedToList = prepareTransactedAcknowledge(message);
// if it is added to the list, we know this message is not
// acked yet.
if (isAddedToList) {
ackPkt.setTransactionID(transaction.getTransactionID());
writeMessageID(message);
// performance optimisation
// Do not wait for reply from broker when
// sending message acknowledgments in a transaction.
// Reliability will be ensured by blocking on commit.
boolean requiresAckFromBroker = !noBlockUntilTxnCompletes;
doAcknowledge(requiresAckFromBroker);
}
} catch (JMSException jmse) {
//we only set the flag here. app needs to call Session.rollback() to
//recreate consumer and flag will be cleared.
if (jmse instanceof RemoteAcknowledgeException) {
this.remore_broker_failed = true;
}
//re-throw exception
throw jmse;
} finally {
if (originalProps != null)
ackPkt.setProperties(originalProps);
}
}
/**
* Write interestID and messageID to the byte array.
*
* @param message
* the message to be acked/redelivered.
*/
protected void
writeMessageID (MessageImpl message) throws JMSException {
try {
//XXX PROTOCOL2.1
dos.writeLong( message.getInterestID() );
message.getMessageID().writeID(dos);
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_CAUGHT_EXCEPTION);
}
}
protected void
writeMessageID (UnAckedMessage message) throws JMSException {
try {
//XXX PROTOCOL2.1
dos.writeLong( message.getConsumerID() );
message.getMessageID().writeID(dos);
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_CAUGHT_EXCEPTION);
}
}
/**
* Write interestID and messageID to the byte array.
*
* @param pkt the pkt to be acked/redelivered.
*/
protected void
writeMessageID (ReadOnlyPacket pkt) throws JMSException {
try {
//XXX PROTOCOL2.1
dos.writeLong( pkt.getConsumerID() );
pkt.getSysMessageID().writeID(dos);
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_CAUGHT_EXCEPTION);
}
}
/**
* This actually writes ack to the broker
*
* @param requireAckFromBroker true if requires broker to ack back.
*/
protected void
doAcknowledgeUndeliverable (boolean requireAckFromBroker, boolean sendToDMQ) throws JMSException {
try {
dos.flush();
bos.flush();
//set message body
ackPkt.setMessageBody( bos.toByteArray() );
//set bit if require broker to ack back.
ackPkt.setSendAcknowledge( requireAckFromBroker );
//write ack packet
protocolHandler.acknowledgeUndeliverable (ackPkt, sendToDMQ);
TEST_ackCount++;
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_MESSAGE_ACK);
} finally {
//reset buf count to 0
bos.reset();
//reset counter
ackCounter = 0;
//reset time stamp
dupsOkTimestamp = 0;
}
}
/**
* This actually writes ack to the broker
*
* @param requireAckFromBroker true if requires broker to ack back.
*/
protected void
doAcknowledge (boolean requireAckFromBroker) throws JMSException {
try {
dos.flush();
bos.flush();
//set message body
ackPkt.setMessageBody( bos.toByteArray() );
//set bit if require broker to ack back.
ackPkt.setSendAcknowledge( requireAckFromBroker );
//check failover flag again before acknowledge to broker.
//bug 6309751 - Unexpected Broker Internal Error during fail over.
this.checkFailOver();
protocolHandler.acknowledge(ackPkt);
TEST_ackCount++;
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_MESSAGE_ACK);
} finally {
//reset buf count to 0
bos.reset();
//reset counter
ackCounter = 0;
//reset time stamp
dupsOkTimestamp = 0;
}
}
/**
* DupsOkAcknowledge mode. Called by Session.acknowledge().
*
* @param message the message to be acked.
*/
protected void dupsOkAcknowledge (MessageImpl message) throws JMSException {
addMessageToAckList (message);
/**
* Acknowledge when reached limit or sessionQueue is empty.
* This is the minimum check required to ensure messages
* are acked. More sophicated ack may be used later.
*/
//if ( ackCounter == dupsOkLimit || sessionQueue.isEmpty() ) {
if ( dupsOkShouldAcknowledge() ) {
dupsOkCommitAcknowledge();
//dequeue ( unAckedMessageQueue );
//doAcknowledge(false);
}
}
protected void dupsOkCommitAcknowledge() throws JMSException {
if ( debug ) {
Debug.println("***** dups ok committing ack .... size: " + ackCounter);
}
dequeueUnAckedMessages();
doAcknowledge(false);
}
//called if session is dupsOkAckOnTimeout mode.
protected void syncedDupsOkAcknowledge (MessageImpl message) throws JMSException {
//sync on dupsOkSyncObj to prevent deadlock - bugid 4987018
synchronized (dupsOkSyncObj) {
//set timestamp if this is the first unacked msg.
if ( ackCounter == 0 ) {
dupsOkTimestamp = System.currentTimeMillis();
}
dupsOkAcknowledge (message);
}
}
//called by consumer reader.
protected void syncedDupsOkCommitAcknowledge() throws JMSException {
//sync on dupsOkSyncObj - bugid 4987018
synchronized (dupsOkSyncObj) {
if ( ackCounter > 0 ) {
dupsOkCommitAcknowledge();
}
}
}
protected boolean dupsOkShouldAcknowledge() {
if ( dupsOkAckOnTimeout ) {
//time elapsed since first unacked message was received.
boolean timeToAck =
(System.currentTimeMillis() - dupsOkTimestamp) >= dupsOkAckTimeout;
return ((ackCounter == dupsOkLimit) || timeToAck);
} else if ( dupsOkAckOnEmptyQueue ) {
return ( ackCounter == dupsOkLimit || sessionQueue.isEmpty() );
} else { //ack on limit
return (ackCounter == dupsOkLimit);
}
}
/**
* called by prepareClientAcknowledge() and prepareTransactedAcknowledge().
*
* Do NOT require to synchronize this method because ONLY one thread
* can call this method at a time.
*/
protected boolean
addMessageToAckList (MessageImpl message) throws JMSException {
boolean isAddedToList = false;
if ( message != null && message.getIsOnAckList() == false ) {
message.setIsOnAckList (true);
//unAckedMessageQueue.addElement(message);
//use light weight obj instead -- bug 4934856
UnAckedMessage unacked = new UnAckedMessage (message);
unAckedMessageQueue.addElement(unacked);
ackCounter ++;
isAddedToList = true;
}
return isAddedToList;
}
/**
* Remove message from ack list.
* Called when consumer is closed. This removes unacked messages
* on the list if any. -- 4934856
*/
protected void
removeMessageFromAckList(UnAckedMessage unacked) {
//message.setIsOnAckList (false);
unAckedMessageQueue.removeElement(unacked);
ackCounter --;
}
/**
* For client ack mode. Called by Session.acknowledge().
*
* @param message the message to be acked.
*/
protected /*synchronized*/ void
prepareClientAcknowledge (MessageImpl message) throws JMSException {
//if ( message.getIsOnAckList() == false ) {
addMessageToAckList (message);
if ( isAckLimited ) {
checkClientAckLimit();
}
//}
}
/**
* Method to check client unacknowledged method limit. Used by client
* acknowledge mode.
*/
protected void checkClientAckLimit() throws JMSException {
if ( ackCounter > ackLimit ) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_CLIENT_ACK_LIMIT);
Debug.println(errorString);
//throw new JMSException (errorString, ClientResources.X_CLIENT_ACK_LIMIT);
}
}
protected boolean
prepareTransactedAcknowledge (MessageImpl message) throws JMSException {
boolean isAddedToList = false;
isAddedToList = addMessageToAckList (message);
if ( isAckLimited ) {
checkTransactedAckLimit();
}
return isAddedToList;
}
/**
* Check transacted unack message limit. Used by transacted session.
*/
protected void checkTransactedAckLimit() throws JMSException {
//check unacked limit
if ( ackCounter > ackLimit ) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_COMMIT_LIMIT);
Debug.println(errorString);
//throw new JMSException (errorString, ClientResources.X_COMMIT_LIMIT);
}
}
/* (non-Javadoc)
* @see com.sun.messaging.jmq.jmsclient.ContextableSession#clientAcknowledge()
*/
public void clientAcknowledge() throws JMSException {
if (getAcknowledgeMode()!=Session.CLIENT_ACKNOWLEDGE) return;
if (failoverOccurred) {
// "Cannot acknowledge messages due to provider connection failover.
// "Subsequent acknowledge calls will also fail until the application calls session.recover()."
String errorString = AdministeredObject.cr.getKString(ClientResources.X_CLIENT_ACK_FAILOVER_OCCURRED);
JMSException jmse = new JMSException(errorString, ClientResources.X_CLIENT_ACK_FAILOVER_OCCURRED);
ExceptionHandler.throwJMSException(jmse);
}
checkSessionState();
if (unAckedMessageQueue.size() > 0) {
dequeueUnAckedMessages();
// write the list to the broker.
doClientAcknowledge();
}
}
/**
* Called by Message.acknowledge(). This method could be called from two
* different threads. One from SessionReader thread, and the other from
* the user thread. If called from SessionReader thread, the message is
* likely to be not on the ack list yet. We need to check if the current
* message is on the list. If not, it is added to the list.
*
* Only messages up to the current message should be acknowledged. There
* may be messages in the unAckedMessageQueue (messages has been delivered
* to the client) but the client decides not to acknowledge for whatever
* reasons. We should leave those messages in the queue according to the
* spec.
*/
protected /*synchronized*/ void
clientAcknowledge (MessageImpl message) throws JMSException {
if (failoverOccurred) {
// "Cannot acknowledge messages due to provider connection failover.
// "Subsequent acknowledge calls will also fail until the application calls session.recover()."
String errorString = AdministeredObject.cr.getKString(ClientResources.X_CLIENT_ACK_FAILOVER_OCCURRED);
JMSException jmse = new JMSException(errorString, ClientResources.X_CLIENT_ACK_FAILOVER_OCCURRED);
ExceptionHandler.throwJMSException(jmse);
}
/**
* When message consumer is closed, doAcknowledge flag is set
* to false. We(George, Amy and Chiaming) decided to throw
* exception in this case.
*/
//XXX:tharakan revisit since Session changes should now allow message to be acknowledged
//XXX:tharakan after the consumer is closed.
//if ( message.doAcknowledge == false ) {
// String errorString = AdministeredObject.cr.getKString(AdministeredObject.cr.X_CLIENT_ACKNOWLEDGE);
// throw new javax.jms.IllegalStateException (errorString, AdministeredObject.cr.X_CLIENT_ACKNOWLEDGE);
//}
this.checkClientAckMessage(message);
checkSessionState();
if ( isTransacted == false ) {
//check if on the list, if not, add this message to the list
prepareClientAcknowledge (message);
//The following code is to write unacked message IDs to byte array.
//The unAcked queue is searched sequentially until the message ID
//in the queue matches the current acking message ID.
//MessageImpl unAckedMessage = null;
//boolean found = false;
//while ( !found ) {
//make this simple and stupid. do not disturb the order of
//messages in the unAcked queue.
//unAckedMessage = (MessageImpl) unAckedMessageQueue.firstElement();
//writeMessageID ( unAckedMessage );
//remove since it has been moved to the out going array.
//unAckedMessageQueue.removeElementAt(0);
//if ( message.messageID.equals(unAckedMessage.messageID) ) {
//found = true;
//}
//}
/**
* JMS 1.0.2 (Update b) changed the meaning of Message.acknowledge()
* From ackowledge all messages consumed upto and including the
* current one in the session to acknowledge all messages
* consumed in the session
*/
if ( unAckedMessageQueue.size() > 0 ) {
dequeueUnAckedMessages();
//write the list to the broker.
//doAcknowledge(true);
doClientAcknowledge();
}
}
}
/**
* Called by Message.acknowledgeThisMessage().
* This method could be called from two different threads.
* One from SessionReader thread, and the other from the user thread.
* If called from SessionReader thread, the message is likely to be not
* on the ack list yet. We need to check if the current message is
* on the list. If not, it is added to the list.
*
* Only this message should be acknowledged. All other message in the
* unAckedMessageQueue should be left unacknowledged
*/
protected /*synchronized*/ void
clientAcknowledgeThisMessage (MessageImpl message) throws JMSException {
/**
* When message consumer is closed, doAcknowledge flag is set
* to false. We(George, Amy and Chiaming) decided to throw
* exception in this case.
*/
//XXX:tharakan revisit since Session changes should now allow message to be acknowledged
//XXX:tharakan after the consumer is closed.
//if ( message.doAcknowledge == false ) {
// String errorString = AdministeredObject.cr.getKString(AdministeredObject.cr.X_CLIENT_ACKNOWLEDGE);
// throw new javax.jms.IllegalStateException (errorString, AdministeredObject.cr.X_CLIENT_ACKNOWLEDGE);
//}
this.checkClientAckMessage(message);
checkSessionState();
if ( isTransacted == false ) {
//check if on the list, if not, add this message to the list
prepareClientAcknowledge (message);
//bug 4934856
UnAckedMessage unAckedMessage = null;
//kiss - linear search for the messageID
for (int i = 0; i < unAckedMessageQueue.size(); i++) {
unAckedMessage = (UnAckedMessage) unAckedMessageQueue.elementAt(i);
if (message.messageID.equals(unAckedMessage.getMessageID())) {
//write the message ID
writeMessageID(unAckedMessage);
//remove it from the unacked list
unAckedMessageQueue.removeElementAt(i);
//write the acknowledge list (one message) to the broker
//doAcknowledge(true);
doClientAcknowledge();
return;
}
}
}
}
/**
* Called by Message.acknowledgeUpThroughThisMessage().
* This method could be called from two different threads.
* One from SessionReader thread, and the other from the user thread.
* If called from SessionReader thread, the message is likely to be not
* on the ack list yet. We need to check if the current message is
* on the list. If not, it is added to the list.
*
* Only messages up to the current message should be acknowledged. There
* may be messages in the unAckedMessageQueue (messages has been delivered
* to the client) but the client decides not to acknowledge for whatever
* reasons. We should leave those messages in the queue.
*/
protected /*synchronized*/ void
clientAcknowledgeUpThroughThisMessage (MessageImpl message) throws JMSException {
// Messages cannot be acknowledged after connection failover.
// Reject client acknowledgements until the application calls
// recover()...
if (failoverOccurred) {
String errorString = AdministeredObject.cr.getKString(
ClientResources.X_CLIENT_ACK_FAILOVER_OCCURRED);
JMSException jmse = new JMSException(errorString,
ClientResources.X_CLIENT_ACK_FAILOVER_OCCURRED);
ExceptionHandler.throwJMSException(jmse);
}
/**
* When message consumer is closed, doAcknowledge flag is set
* to false. We(George, Amy and Chiaming) decided to throw
* exception in this case.
*/
//XXX:tharakan revisit since Session changes should now allow message to be acknowledged
//XXX:tharakan after the consumer is closed.
//if ( message.doAcknowledge == false ) {
// String errorString = AdministeredObject.cr.getKString(AdministeredObject.cr.X_CLIENT_ACKNOWLEDGE);
// throw new javax.jms.IllegalStateException (errorString, AdministeredObject.cr.X_CLIENT_ACKNOWLEDGE);
//}
this.checkClientAckMessage(message);
checkSessionState();
if ( isTransacted == false ) {
//check if on the list, if not, add this message to the list
prepareClientAcknowledge (message);
//check if the message is in the unacked queue.
if ( isMessageInUnAckedQueue(message) ) {
//The following code is to write unacked message IDs to byte array.
//The unAcked queue is searched sequentially until the message ID
//in the queue matches the current acking message ID.
UnAckedMessage unAckedMessage = null;
boolean found = false;
while (!found) {
//make this simple and stupid. do not disturb the order of
//messages in the unAcked queue. -- bug 4934856
unAckedMessage =
(UnAckedMessage) unAckedMessageQueue.firstElement();
writeMessageID(unAckedMessage);
//remove since it has been moved to the out going array.
unAckedMessageQueue.removeElementAt(0);
if (message.messageID.equals(unAckedMessage.getMessageID())) {
found = true;
}
}
//write the list to the broker.
//doAcknowledge(true);
doClientAcknowledge();
}
}
}
private void doClientAcknowledge() throws JMSException {
if (this.remore_broker_failed) {
ExceptionHandler.throwRemoteAcknowledgeException
(null, ClientResources.X_CLIENT_ACK_FAILED_REMOTE);
//String errorString = AdministeredObject.cr
// .getKString(ClientResources.X_CLIENT_ACK_FAILED_REMOTE);
//JMSException jmse = new com.sun.messaging.jms.JMSException(
// errorString, ClientResources.X_CLIENT_ACK_FAILED_REMOTE);
// throw the exception
//ExceptionHandler.throwJMSException(jmse);
}
try {
doAcknowledge(true);
} catch (JMSException jmse) {
if (isRemoteException(jmse)) {
//set this flag so that no further client ack is allowed.
this.remore_broker_failed = true;
//throw remote ack failed exception
ExceptionHandler.throwRemoteAcknowledgeException
(jmse, ClientResources.X_CLIENT_ACK_FAILED_REMOTE);
// rethrow
// construct the precise exception
//String errorString = AdministeredObject.cr
// .getKString(ClientResources.X_CLIENT_ACK_FAILED_REMOTE);
//JMSException newjmse = new com.sun.messaging.jms.JMSException(
// errorString, ClientResources.X_CLIENT_ACK_FAILED_REMOTE);
// set exception link
//newjmse.setLinkedException(jmse);
// throw the exception
//ExceptionHandler.throwJMSException(newjmse);
} else {
//no-op, re-throw the old exception
throw jmse;
}
}
}
protected boolean
isMessageInUnAckedQueue (MessageImpl message) throws JMSException {
boolean inQueue = false;
//4934856
UnAckedMessage unAckedMessage = null;
int size = unAckedMessageQueue.size();
for ( int i=0; i< size; i++ ) {
unAckedMessage = (UnAckedMessage) unAckedMessageQueue.elementAt(i);
if ( message.messageID.equals(unAckedMessage.getMessageID() ) ) {
inQueue = true;
//break the loop
i = size;
}
}
return inQueue;
}
/**
* Check if session is closed.
* @throws IllegalStateException if session is closed.
*/
protected void checkSessionState() throws JMSException {
if ( isClosed ) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_SESSION_CLOSED);
JMSException jmse =
new javax.jms.IllegalStateException(errorString,
ClientResources.X_SESSION_CLOSED);
ExceptionHandler.throwJMSException(jmse);
}
}
protected void checkFailOver() throws JMSException {
if ( isTransacted && failoverOccurred ) {
String errorString = AdministeredObject.cr.getKString(
ClientResources.X_TRANSACTION_INVALIDATED_FAILOVER);
JMSException jmse = new javax.jms.JMSException(errorString,
ClientResources.X_TRANSACTION_INVALIDATED_FAILOVER);
ExceptionHandler.throwJMSException(jmse);
}
}
/** Create a BytesMessage. A BytesMessage is used to send a message
* containing a stream of uninterpreted bytes.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public BytesMessage
createBytesMessage() throws JMSException {
checkSessionState();
//param true is to init DataOutputStream for writing.
return new BytesMessageImpl (true);
}
/** Create a MapMessage. A MapMessage is used to send a self-defining
* set of name-value pairs where names are Strings and values are Java
* primitive types.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public MapMessage
createMapMessage() throws JMSException {
checkSessionState();
return new MapMessageImpl();
}
/** Create a Message. The Message interface is the root interface of
* all JMS messages. It holds all the standard message header
* information. It can be sent when a message containing only header
* information is sufficient.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public Message
createMessage() throws JMSException {
checkSessionState();
Message message = new MessageImpl();
return message;
}
/** Create an ObjectMessage. An ObjectMessage is used to send a message
* that containing a serializable Java object.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public ObjectMessage
createObjectMessage() throws JMSException {
checkSessionState();
return new ObjectMessageImpl();
}
/** Create an initialized ObjectMessage. An ObjectMessage is used
* to send a message that containing a serializable Java object.
*
* @param object the object to use to initialize this message.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public ObjectMessage
createObjectMessage(Serializable object) throws JMSException {
checkSessionState();
ObjectMessage objectMessage = new ObjectMessageImpl();
objectMessage.setObject (object);
return objectMessage;
}
/** Create a StreamMessage. A StreamMessage is used to send a
* self-defining stream of Java primitives.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public StreamMessage
createStreamMessage() throws JMSException {
checkSessionState();
//param true is to init ObjectOutputStream for writing.
return new StreamMessageImpl (true);
}
/** Create a TextMessage. A TextMessage is used to send a message
* containing a String.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public TextMessage
createTextMessage() throws JMSException {
checkSessionState();
return new TextMessageImpl();
}
/** Create an initialized TextMessage. A TextMessage is used to send
* a message containing a String.
*
* @param text the string used to initialize this message.
*
* @exception JMSException if JMS fails to create this message
* due to some internal error.
*/
public TextMessage
createTextMessage(String text) throws JMSException {
checkSessionState();
TextMessageImpl message = new TextMessageImpl();
message.setText( text );
return message;
}
/** Is the session in transacted mode?
*
* @return true if in transacted mode
*
* @exception JMSException if JMS fails to return the transaction
* mode due to internal error in JMS Provider.
*/
public boolean
getTransacted() throws JMSException {
checkSessionState();
return isTransacted;
}
protected boolean
getTransactedNoCheck() {
return isTransacted;
}
/** Gets value for how messages are acknowledged.
*
* @return one of the following values: AUTO_ACKNOWLEDGE
,
* CLIENT_ACKNOWLEDGE
, DUPS_OK_ACKNOWLEDGE
*
* @exception JMSException if the JMS provider fails to return the
* acknowledge mode due to some internal error.
*
* @see Connection#createSession
* @since 1.1
*/
public int
getAcknowledgeMode() throws JMSException {
checkSessionState();
if (isTransacted) {
return 0;
} else {
return acknowledgeMode;
}
}
/** Commit all messages done in this transaction and releases any locks
* currently held.
*
* @exception JMSException if JMS implementation fails to commit the
* the transaction due to some internal error.
* @exception TransactionRolledBackException if the transaction
* gets rolled back due to some internal error
* during commit.
* @exception IllegalStateException if method is not called by a
* transacted session.
*/
public /*synchronized*/ void
commit() throws JMSException {
checkSessionState();
checkPermissionForAsyncSend();
//XXX:GT TBF
if (isTransacted == false) {
String errorString = AdministeredObject.cr.getKString(
ClientResources.X_NON_TRANSACTED);
JMSException jmse = new javax.jms.IllegalStateException(errorString,
ClientResources.X_NON_TRANSACTED);
ExceptionHandler.throwJMSException(jmse);
}
if (failoverOccurred) {
rollback();
String errorString = AdministeredObject.cr.getKString(
ClientResources.X_TRANSACTION_FAILOVER_OCCURRED);
JMSException jmse = new TransactionRolledBackException(errorString,
ClientResources.X_TRANSACTION_FAILOVER_OCCURRED);
ExceptionHandler.throwJMSException(jmse);
}
//HACC -- this is due to ack failed or runtime exception in MDB.onMessage.
//we prevent commit by throwing the exception here (bug 6667940).
if (isRollbackOnly) {
if (rollbackCause instanceof Exception) {
ExceptionHandler.handleException((Exception) rollbackCause,
ClientResources.X_CAUGHT_EXCEPTION);
} else {
Exception e = new Exception (rollbackCause);
ExceptionHandler.handleException(e, ClientResources.X_CAUGHT_EXCEPTION);
}
}
waitAllAsyncSendCompletion(null);
// set sync flag
setInSyncState();
try {
// acknowledge the current message if in the reader thread.
receiveCommit();
//commit all messages sent and receive
transaction.commit();
} catch (JMSException jmse) {
if (this.isRemoteException(jmse)) {
//we are rolling back the transaction here.
doRemoteFailedRollback(jmse);
} else {
throw jmse;
}
} finally {
releaseInSyncState();
}
}
protected void doRemoteFailedRollback(JMSException jmse) throws JMSException {
//recreate consumers -- transaction is rolled back
this.recreateConsumers();
//construct the precise exception
String errorString = AdministeredObject.cr.getKString(
ClientResources.X_COMMIT_FAILED_REMOTE);
JMSException newjmse =
new com.sun.messaging.jms.TransactionRolledBackException (errorString,
ClientResources.X_COMMIT_FAILED_REMOTE);
//set exception link
newjmse.setLinkedException(jmse);
//throw the exception
ExceptionHandler.throwJMSException(newjmse);
}
/** Rollback any messages done in this transaction and releases any locks
* currently held.
*
* @exception JMSException if JMS implementation fails to rollback the
* the transaction due to some internal error.
* @exception IllegalStateException if method is not called by a
* transacted session.
*
*/
public /*synchronized*/ void
rollback() throws JMSException {
checkSessionState();
checkPermissionForAsyncSend();
if ( isTransacted == false ) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_NON_TRANSACTED);
JMSException jmse =
new javax.jms.IllegalStateException (errorString,
ClientResources.X_NON_TRANSACTED );
ExceptionHandler.throwJMSException(jmse);
}
waitAllAsyncSendCompletion(null);
//This will make sure that session is not closed/closing.
setInSyncState();
try {
//check if we have a remote exception
if (remore_broker_failed) {
//delete and re-register consumers -- session is also rolled back.
recreateConsumers();
//reset the flag to false so that we can proceed
remore_broker_failed = false;
//we are done in rollback. messages not committed will be re-delivered.
return;
}
//request
//1. all messages in the unAckedMessageQueue to be redelivered.
//2. all messages in the session queue and receive queues to be
// redelivered.
if ( connection.isConnectedToHABroker ) {
rollbackHATransaction();
} else {
receiveRollback();
//roll back all messages in the send queue.
transaction.rollback();
}
} finally {
//HACC -- reset flag (set when received exception from mdb/ack)
this.isRollbackOnly = false;
this.rollbackCause = null;
//This will release sync state.
failoverOccurred = false;
releaseInSyncState();
}
}
private void rollbackHATransaction() throws JMSException {
try {
receiveRollback();
transaction.rollback();
} catch (JMSException jmse) {
String ecode = jmse.getErrorCode();
if (ClientResources.X_NET_WRITE_PACKET.equals(ecode)
|| ClientResources.X_NET_ACK.equals(ecode) ) {
this.rollbackFailed(jmse);
} else {
throw jmse;
}
}
}
private void rollbackFailed(JMSException jmse) throws JMSException {
if ( connection.imqReconnect == false ) {
throw jmse;
}
//connection.checkAndSetReconnecting();
yield();
connection.checkReconnecting(null);
if ( connection.isCloseCalled || connection.connectionIsBroken) {
throw jmse;
}
//receiveRollback();
//transaction.rollback();
//The transaction is rolled back by the broker. we only have to start
//a new transaction.
transaction.startNewLocalTransaction();
}
public static void yield() {
try {
Thread.yield();
Thread.sleep(3000);
} catch (Exception e) {
;
}
}
/**
* Close all consumers.
* Called from Session.close()
*
* @exception JMSException if close a consumer fails
*/
//must be called from synchronized method
private void closeConsumers() throws JMSException {
/*Enumeration e = consumers.elements();
MessageConsumerImpl consumer = null;
while ( e.hasMoreElements() ) {
consumer = (MessageConsumerImpl) e.nextElement();
consumer.close();
}*/
MessageConsumerImpl[] consumerArray = (MessageConsumerImpl[])
consumers.values().toArray ( new MessageConsumerImpl[0] );
for ( int i=0; i< consumerArray.length; i++ ) {
consumerArray[i].close(true);
}
consumers.clear();
}
private void closeProducers() throws JMSException {
MessageProducerImpl[] _producers = (MessageProducerImpl[])
producers.toArray(new MessageProducerImpl[0]);
for (int i = 0; i < _producers.length; i++) {
_producers[i].close();
}
producers.clear();
}
// must be called from synchronized method Session.close()
private void closeBrowserConsumers() throws JMSException {
//Enumeration e = browserConsumers.elements();
//BrowserConsumer consumer = null;
//while (e.hasMoreElements()) {
// consumer = (BrowserConsumer)e.nextElement();
// consumer.close();
//}
BrowserConsumer[] bcArray = (BrowserConsumer[])
browserConsumers.values().toArray( new BrowserConsumer[0] );
for ( int i=0; i< bcArray.length; i++ ) {
bcArray[i].close();
}
browserConsumers.clear();
}
/** Since a provider may allocate some resources on behalf of a Session
* outside the JVM, clients should close them when they are not needed.
* Relying on garbage collection to eventually reclaim these resources
* may not be timely enough.
*
* There is no need to close the producers and consumers
* of a closed session.
*
*
This call will block until a receive or message listener
* in progress has completed. A blocked message consumer
* receive call returns null when this session is closed.
*
*
Closing a transacted session must rollback the in-progress
* transaction.
*
*
This method is the only session method that can
* be concurrently called.
*
*
Invoking any other session method on a closed session must throw
* JMSException.IllegalStateException. Closing a closed session must
* NOT throw an exception.
*
* @exception JMSException if JMS implementation fails to close a
* Session due to some internal error.
*/
public /*synchronized*/ void
close() throws JMSException {
sessionLogger.log(Level.FINEST, "##### closing session. consumer table size: " + consumers.values().size());
//messages in the session queue.
int reduceFlowCount = 0;
//check if called from listener
checkPermission();
checkPermissionForAsyncSend();
try {
//This statement must be above synchronized block to avoid dead-lock
//if calling Session.rollback concurrently. bug ID 6390095 and
//6390006
prepareToClose(true);
synchronized ( sessionSyncObj ) {
try {
//closing a closed session, just return
if ( isClosed ) {
return;
}
//Wait for session to stop (this will block if another thread is in onMessage())
sessionQueue.stop(true);
//messages in the session queue
reduceFlowCount = sessionQueue.size();
//wait if commit/rollback/recover in process
//set inSyncState to true
//prepareToClose();
if ( isTransacted ) {
if (xaTxnMode) {
//**If we are in an xaTxn then we ack all received
//**messages.
//**The real commit happens only when the
//**XAResource.commit() is called by the TM
//ack all messages received in this session
//message has been acked already.
receiveCommit();
//All messages (recd & sent) [commit and rollback]
//will be handled by XAResource
} else {
if ( connection.isBroken() == false && connection.recoverInProcess == false ) {
transaction.releaseBrokerResource();
}
}
}
////
//close all consumers
closeConsumers();
closeProducers();
closeBrowserConsumers();
sessionReader.close();
serverSessionRunner.serverSessionClose();
} finally {
waitAllAsyncSendCompletion(null);
if (asyncSendCBProcessor != null) {
asyncSendCBProcessor.cancel();
}
}
connection.removeSession(this);
connection.removeFromReadQTable (sessionId);
//XXX PROTOCOL3.5
if (connection.getBrokerProtocolLevel() >= PacketType.VERSION350) {
if ( connection.isBroken() == false && connection.recoverInProcess == false) {
protocolHandler.deleteSession(this);
}
}
////
isClosed = true;
}
} finally {
sessionLogger.log(Level.FINEST, "***** consumer table size: " + consumers.values().size());
//unblock session queue
if ( isClosed == false ) {
this.sessionReader.close();
}
//unblock consumer queues
if ( consumers.values().size() > 0 ) {
this.cleanUpConsumers();
}
/**
* When closing session failed, we still want to mark this
* session as closed.
*/
isClosed = true;
serverSessionRunner.reset();
if (connectionConsumer != null) {
connectionConsumer.sessionClosed(this);
}
releaseInSyncState();
//bug 6271876 -- connection flow control
resetConnectionFlowControl (reduceFlowCount);
if ( sessionLogger.isLoggable(Level.FINE) ) {
logLifeCycle(ClientResources.I_SESSION_CLOSED);
}
}
if ( debug ) {
Debug.println ("session closed ...");
Debug.println (this);
}
}
protected void cleanUpConsumers() {
sessionLogger.log(Level.FINEST, "Cleaning up consumers in session. SessionID: " + this.sessionId);
MessageConsumerImpl[] consumerArray = (MessageConsumerImpl[])
consumers.values().toArray ( new MessageConsumerImpl[0] );
for ( int i=0; i< consumerArray.length; i++ ) {
consumerArray[i].receiveQueue.close();
consumerArray[i].isClosed = true;
}
consumers.clear();
}
//bug 6271876 -- connection flow control
protected void resetConnectionFlowControl (int reduceFlowCount) {
if ( connection.isCloseCalled ) {
return;
}
if ( connection.protectMode && reduceFlowCount > 0 ) {
readChannel.flowControl.resetFlowControl(connection, reduceFlowCount);
}
}
/**
* This method is provided for the MDB adaptor interface to close
* the session thread. The call is from session reader thread,
* and no other consumers/producers in the session.
*/
public void closeFromRA() {
synchronized (raEndpointSyncObj) {
sessionReader.close();
}
}
/*
* Set to true when this sesion is the one being used by an RA endpoint
*/
public void _setRAEndpointSession() {
//this field is never used.
//raEndpointSession = true;
}
/*
* start a local transaction
* called from RA when an mc is enlisted into a local txn rather than an xa txn
*/
public void _startLocalTransaction()
throws JMSException
{
if (isTransacted) {
//ensure that transaction is non-null
if (transaction == null) {
throw new com.sun.messaging.jms.JMSException("MQRA:S:Can't start local transaction-transacted w/o Transaction Object");
}
} else {
//ensure that transaction is null
if (transaction != null) {
throw new com.sun.messaging.jms.JMSException("MQRA:S:Can't start local transaction-already transacted");
}
transaction = new Transaction(this, true);
isTransacted = true;
}
}
/**
* Called by close/stop methods to check if need to wait for message
* listener to complete.
*/
protected boolean needToWait() {
if ( connection.isBroken() ) {
return false;
} else {
return true;
}
}
/** Stop message delivery in this session, and restart sending messages
* with the oldest unacknowledged message.
*
*
All consumers deliver messages in a serial order.
* Acknowledging a received message automatically acknowledges all
* messages that have been delivered to the client.
*
*
Restarting a session causes it to take the following actions:
*
*
* - Stop message delivery
*
- Mark all messages that might have been delivered but not
* acknowledged as `redelivered'
*
- Restart the delivery sequence including all unacknowledged
* messages that had been previously delivered.
*
*
Redelivered messages do not have to be delivered in
* exactly their original delivery order.
*
*
* @exception JMSException if JMS implementation fails to stop message
* delivery and restart message send due to
* due to some internal error.
* @exception IllegalStateException if method is called by a
* transacted session.
*/
public void
recover() throws JMSException {
//boolean decremented = false;
//Throw exception if closed
checkSessionState();
//transacted session is not allowed to call this method.
if ( isTransacted ) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_TRANSACTED);
JMSException jmse =
new javax.jms.IllegalStateException (errorString,
ClientResources.X_TRANSACTED);
ExceptionHandler.throwJMSException(jmse);
}
//NO_ACKNOWLEDGE mode does now allow to call this method.
if ( acknowledgeMode == com.sun.messaging.jms.Session.NO_ACKNOWLEDGE ) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_NO_ACKNOWLEDGE_RECOVER);
JMSException jmse = new javax.jms.IllegalStateException (errorString,
ClientResources.X_NO_ACKNOWLEDGE_RECOVER);
ExceptionHandler.throwJMSException(jmse);
}
//check if we have a remote exception
if (remore_broker_failed) {
//delete and re-register consumers
recreateConsumers();
//reset the flag to false so that we can proceed
remore_broker_failed = false;
//we are done in recover. messages not ack successfully will be re-delivered.
return;
}
//no-op for non client acked session
//bug ID 4678413 -- We now allow all ack mode to call Session.recover().
/*if ( acknowledgeMode != Session.CLIENT_ACKNOWLEDGE ) {
return;
}*/
setInSyncState();
try {
switch ( acknowledgeMode ) {
case Session.AUTO_ACKNOWLEDGE:
/**
* We want to make sure that for these conditions the
* message does not want to be acked.
*/
if ( Thread.currentThread() == sessionReader.sessionThread ) {
sessionReader.currentMessage.doAcknowledge = false;
}
else if ( Thread.currentThread() == serverSessionRunner.getCurrentThread()) {
serverSessionRunner.currentMessage.doAcknowledge = false;
}
//NOTE: fall through here.
case Session.CLIENT_ACKNOWLEDGE:
case Session.DUPS_OK_ACKNOWLEDGE:
//include the current message to recover if called from message listener
if ( Thread.currentThread() == sessionReader.sessionThread ) {
prepareClientAcknowledge (sessionReader.currentMessage);
}
else if ( Thread.currentThread() == serverSessionRunner.getCurrentThread()) {
prepareClientAcknowledge (serverSessionRunner.currentMessage);
}
break;
}
} catch (Exception e1) {}
try {
//stop message delivery from broker
stopSession();
/*
* Because the caller is the session controlling thread,
* we do not have to call stop/start methods. We know that
* no messages are delivering to the consumers at this moment.
*/
//GT Have to stop *unless* it is the session thread
//otherwise the redeliver lists will be wrong
if ((Thread.currentThread() != sessionReader.sessionThread) &&
(Thread.currentThread() != serverSessionRunner.getCurrentThread())) {
stop();
}
//request redeliver of unacked messages - set redelivered flag to true
//GT reorder the REDELIVER protocol msgs to ensure correct order for redelivery
//redeliverUnAckedMessages(true);
redeliverMessagesInQueues(false);
//request redeliver of messages in different queues but not delivered
//to the client yet - set redelivered flag to false
//GT reorder as explained above
//redeliverMessagesInQueues (false);
redeliverUnAckedMessages(true);
failoverOccurred = false;
//GT Have to start *unless* it is the session thread
if ((Thread.currentThread() != sessionReader.sessionThread) &&
(Thread.currentThread() != serverSessionRunner.getCurrentThread())) {
start();
}
} finally {
//start message delivery
releaseInSyncState();
resumeSession();
}
}
/**
* Tell the broker to stop sending messages for this session.
*
* The 3.0.x brokers don't know about sessions. So in that case we
* just stop the entire connection.
*/
protected void stopSession() throws JMSException {
if (connection.getBrokerProtocolLevel() < PacketType.VERSION350) {
protocolHandler.incStoppedCount();
protocolHandler.stop();
}
else {
protocolHandler.stopSession(brokerSessionID);
}
}
/**
* Tell the broker to resume message delivery for this session.
*
* The 3.0.x brokers don't know about sessions. So in that case we
* just start the connection.
*/
protected void resumeSession() throws JMSException {
if (connection.getBrokerProtocolLevel() < PacketType.VERSION350) {
protocolHandler.decStoppedCount();
//GT XXX timing hole - fix after FCS
// SB XXX There is no known test case for this timing
// hole. It is theoretically possible that if a thread
// calls connection.stop while another thread is doing
// session.recover or rollback, there may be some
// problems. Since raptor is capable of explicitly
// stopping a session (during rollback / recover) this is
// not an issue starting from 3.5 release...
if (!connection.getIsStopped())
protocolHandler.start();
}
else {
protocolHandler.resumeSession(brokerSessionID);
}
}
/**
* Called before commit/rollback/recover ...
*/
protected void
setInSyncState () throws JMSException {
synchronized ( syncObject ) {
checkSessionState();
if ( inSyncState ) {
// Some other thread is performing critical operation on this session
// which is illegal unless this thread is the MessageListener thread,
// and the other thread is calling Session.close() or Consumer.close
if (isIsMessageListenerThread() &&
(inSyncStateOperation == INSYNCSTATE_SESSION_CLOSING ||
inSyncStateOperation == INSYNCSTATE_CONSUMER_CLOSING)) {
// this is an onMessage() thread
// allow the close to continue in the other thread (it should be blocking until onMessage() returns)
// and carry on in this thread
return;
}
if (inSyncStateOperation == INSYNCSTATE_CONSUMER_CLOSING) {
long totalwaited = 0L;
long waittime = waitTimeoutForConsumerCloseDone;
while (inSyncState &&
inSyncStateOperation == INSYNCSTATE_CONSUMER_CLOSING &&
(waittime > 0L || waitTimeoutForConsumerCloseDone == 0L)) {
checkSessionState();
long starttime = System.currentTimeMillis();
try {
syncObject.wait(waittime);
} catch ( InterruptedException e ) {
Debug.printStackTrace(e);
}
totalwaited += (System.currentTimeMillis() - starttime);
waittime = waitTimeoutForConsumerCloseDone - totalwaited;
if (waittime < 0L) {
waittime = 0L;
}
}
if (!inSyncState) {
inSyncState = true;
inSyncStateOperation = INSYNCSTATE_OTHER;
return;
}
}
String errorString = AdministeredObject.cr.getKString(ClientResources.X_CONFLICT);
JMSException jmse =
new javax.jms.IllegalStateException(errorString, ClientResources.X_CONFLICT);
ExceptionHandler.throwJMSException(jmse);
}
inSyncState = true;
inSyncStateOperation = INSYNCSTATE_OTHER;
}
}
/**
* Called after commit/rollback/recover
*/
protected void
releaseInSyncState() {
synchronized ( syncObject ) {
inSyncState = false;
// unset the reason why we're in inSyncState
inSyncStateOperation=INSYNCSTATE_NOTSET;
syncObject.notifyAll();
}
}
/**
* Get inSync status.
*/
protected boolean getInSyncState() {
return inSyncState;
}
/**
* This method set the inSyncState flag to true.
* It waits until commit/rollback/recover returns.
*
*Commit/rollback/recover throws JMSException if they
* are called after close is called.
*/
protected void prepareToClose(boolean fromSessionClose) {
synchronized ( syncObject ) {
//if inSync state, wait until done
while ( inSyncState ) {
try {
syncObject.wait();
} catch ( InterruptedException e ) {
Debug.printStackTrace(e);
}
}
//set to true so that commit/rollback/recover
//will throw exception ...
inSyncState = true;
// record why we've set inSyncState
if (fromSessionClose) {
inSyncStateOperation = INSYNCSTATE_SESSION_CLOSING;
} else {
inSyncStateOperation = INSYNCSTATE_CONSUMER_CLOSING;
}
}
}
/**
* For transacted session, acknowledge all unacked messages. If
* Session.commit() is called from MessageListener, the current
* message is not acknowledged.
*
* NOTE: The implementation has been changed. The new implementation
* only need to acknowledge the current message if commit() is
* called from the reader thread.
*/
protected void receiveCommit() throws JMSException {
//include the current message to recover if called from message listener
if ( Thread.currentThread() == sessionReader.sessionThread ) {
transactedAcknowledge (sessionReader.currentMessage);
}
else if (Thread.currentThread() == serverSessionRunner.getCurrentThread()) {
transactedAcknowledge (serverSessionRunner.currentMessage);
}
//clean up unacked Q
//bug 6423696 - Session.rollback does not actually ...
//The fix is to clear the unAckedMessageQueue only after
//commit has successfully returned.
//The action to clear the Q is moved to Transaction.java
//unAckedMessageQueue.clear();
/*if ( Thread.currentThread() == sessionReader.sessionThread ) {
prepareTransactedAcknowledge (sessionReader.currentMessage);
}
else if (Thread.currentThread() == serverSessionRunner.getCurrentThread()) {
prepareTransactedAcknowledge (serverSessionRunner.currentMessage);
}
//only need to send ack if there are messages in the un acked queue.
if ( unAckedMessageQueue.size() > 0 ) {
dequeue ( unAckedMessageQueue );
//set transaction ID
ackPkt.setTransactionID( transaction.getTransactionID() );
//do acknowledge, require broker to ack back.
doAcknowledge(true);
}*/
}
/**
* This method is called after ProtocolHandler.commit() return
* a OK status.
*
*/
protected void clearUnackedMessageQ() {
this.unAckedMessageQueue.clear();
}
/**
* Called by rollback() in a transacted session. This method handles
* rollback for consuming messages
*
*
All messages in the unAckedMessageQueue, receive queues, and session
* queue will be redelivered.
*/
protected void receiveRollback() throws JMSException {
//XXX REVISIT chiaming: NO transaction ID involved???
//include the current message to recover if called from message listener
if ( Thread.currentThread() == sessionReader.sessionThread ) {
prepareTransactedAcknowledge (sessionReader.currentMessage);
}
else if (Thread.currentThread() == serverSessionRunner.getCurrentThread()) {
prepareTransactedAcknowledge (serverSessionRunner.currentMessage);
}
//stop message delivery from broker
stopSession();
//stop this session, all message delivery in the session is stopped.
//DO NOT need to stop this session because this is the controlling
//thread calling this method.
//GT Have to stop *unless* it is the session thread
//otherwise the redeliver lists will be wrong
if ((Thread.currentThread() != sessionReader.sessionThread) &&
(Thread.currentThread() != serverSessionRunner.getCurrentThread())) {
stop();
}
//put MID from unacked message queue to dos
//dequeueUnAckedMessages();
//put MID from all consumer queues and session queue to dos
//dequeueMessagesInQueues();
//for bug id 5018703 - we must send two separate pkt with
//the right redeliver flag. This is similar to recover().
this.redeliverMessagesInQueues(false);
this.redeliverUnAckedMessages(true);
//redeliver and set redeliver flag to true
//redeliver (true);
//start current session again
//GT Have to start *unless* it is the session thread
if ((Thread.currentThread() != sessionReader.sessionThread) &&
(Thread.currentThread() != serverSessionRunner.getCurrentThread())) {
start();
}
//start message delivery
resumeSession();
}
/**
* Caller must in setInSyncState block
*
* This method is used to rollback a transaction that failed to commit
* and broker indicates client runtime should rollback it
*/
protected void rollbackAfterReceiveCommit(JMQXid jmqXid) throws JMSException {
checkSessionState();
//stop message delivery from broker
stopSession();
boolean stoppedbyme = false;
try {
//Have to stop *unless* it is the session thread
//otherwise the redeliver lists will be wrong
if ((Thread.currentThread() != sessionReader.sessionThread) &&
(Thread.currentThread() != serverSessionRunner.getCurrentThread())) {
stop();
stoppedbyme = true;
}
this.redeliverMessagesInQueues(false);
if ((Thread.currentThread() != sessionReader.sessionThread) &&
(Thread.currentThread() != serverSessionRunner.getCurrentThread())) {
start();
stoppedbyme = false;
}
connection.getProtocolHandler().rollback(0L, jmqXid, true);
} finally {
try {
if (stoppedbyme) {
start();
}
} finally {
resumeSession();
}
}
}
/**
* Called by recover(). Redeliver messages in the unacked message queue.
*
* @param redeliverFlag the falg that broker will set to the messages when
* redeliver.
*/
protected void
redeliverUnAckedMessages (boolean redeliverFlag) throws JMSException {
dequeueUnAckedMessages();
//commit redeliver
redeliver( redeliverFlag );
}
/**
* Redeliver messages in the consumer's receive queues and session queue
*/
protected void
redeliverMessagesInQueues (boolean redeliverFlag) throws JMSException {
dequeueMessagesInQueues();
//commit redeliver
redeliver (redeliverFlag);
}
/**
* Dequeue unacked messages from unAckedMessageQueue and write
* the message IDs to the data output stream.
*
*
Reset the ack counter to 0 since the queue is empty.
*/
//protected void dequeueUnAckedMessages() throws JMSException {
//dequeue unacked message queue
// dequeue (unAckedMessageQueue);
//reset ack counter
// ackCounter = 0;
//}
/**
* Loop through each MessageConsumer and dequeue its receiveQueue
* in this session. Message Ids are written to the data output
* stream.
*
*
Dequeue the SessionQueue after the above action.
*/
protected void dequeueMessagesInQueues() throws JMSException {
MessageConsumerImpl consumer = null;
Enumeration enum2 = consumers.elements();
int reduceFlowCount = 0;
while (enum2.hasMoreElements()) {
consumer = (MessageConsumerImpl) enum2.nextElement();
// add messages in the consumer receive queue
reduceFlowCount = reduceFlowCount + consumer.receiveQueue.size();
//dequeue receive queue for each consumer in this session
dequeueReceiveQ (consumer.receiveQueue);
//XXX PROTOCOL 3.5
// Reset the flow control counters for all the consumers
// for this session.
readChannel.flowControl.resetFlowControl(consumer, 0);
}
//add messages in the session queue
reduceFlowCount = reduceFlowCount + sessionQueue.size();
//dequeue the currect session's session queue
dequeueSessionQ (sessionQueue);
//bug 6271876 -- connection flow control
resetConnectionFlowControl (reduceFlowCount);
//XXX REVISIT "dequeue" from serverSessionMessageQ ?
}
/**
* Loop through each element and write MID to dos. elements are deleted
* after used.
*
* @param queue the SessionQueue to be dequeued
*/
protected void dequeueReceiveQ (ReceiveQueue queue) throws JMSException {
MessageImpl message = null;
//GT
//System.out.println(">>>dequeing..ReceiveQ dump");
while (queue.isEmpty() == false) {
if ((message = (MessageImpl) queue.dequeue()) != null) {
writeMessageID ( message );
//GT
//message.dump(System.out);
}
}
//GT
//System.out.println(">>>dequeing..ReceiveQ dump done");
//System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
protected void dequeueSessionQ (SessionQueue queue) throws JMSException {
ReadOnlyPacket pkt = null;
//GT
//System.out.println(">>>dequeing..SessionQ dump");
while (queue.isEmpty() == false) {
if ((pkt = (ReadOnlyPacket) queue.dequeue()) != null) {
writeMessageID ( pkt );
//GT
//pkt.dump(System.out);
}
}
//GT
//System.out.println(">>>dequeing..SessionQ dump done");
//System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
/**
* Loop through each element and write MID to dos. elements are deleted
* after used.
*
* @param queue the Vector to be dequeued
*/
private void dequeueUnAckedMessages () throws JMSException {
//bug 4934856
UnAckedMessage unAckedMessage = null;
int size = unAckedMessageQueue.size();
//GT
//System.out.println(">>>dequeing..dequeue dump");
for ( int i=0; i< size; i++ ) {
unAckedMessage = (UnAckedMessage) unAckedMessageQueue.elementAt(i);
writeMessageID ( unAckedMessage );
//GT
//unAckedMessage.dump(System.out);
}
//GT
//System.out.println(">>>dequeing..dequeue dump done");
unAckedMessageQueue.removeAllElements();
this.ackCounter = 0;
}
/**
public void _redeliverMessageFromRA(MessageImpl message) throws JMSException {
synchronized (raEndpointSyncObj) {
if (message != null) {
writeMessageID(message);
ReadWritePacket pkt = new ReadWritePacket();
try {
dos.flush();
bos.flush();
//set message body
pkt.setMessageBody(bos.toByteArray());
//for transacted session, set transaction ID to pkt
////if ( isTransacted ) {
////pkt.setTransactionID( transaction.getTransactionID() );
////}
//write packet - ensure that this message is marked 'redelivered'
protocolHandler.redeliver (pkt, true, isTransacted);
//reset buf count to 0
bos.reset();
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_MESSAGE_REDELIVER);
}
}
}
}
**/
/**
* Redeliver all messages in the current DataOutputStream.
*
* @param redeliverFlag the flag for broker to set for redelivered
* messages.
*/
protected void redeliver (boolean redeliverFlag) throws JMSException {
//do not request redeliver if received no messages
if ( bos.size() == 0 ) {
return;
}
ReadWritePacket pkt = new ReadWritePacket();
try {
dos.flush();
bos.flush();
//set message body
pkt.setMessageBody( bos.toByteArray() );
//for transacted session, set transaction ID to pkt
if ( isTransacted ) {
pkt.setTransactionID( transaction.getTransactionID() );
}
//write packet
protocolHandler.redeliver (pkt, redeliverFlag, isTransacted);
//reset buf count to 0
bos.reset();
} catch (IOException e) {
ExceptionHandler.handleException(e, ClientResources.X_MESSAGE_REDELIVER);
}
}
/** Return the session's distinguished message listener (optional).
*
* @return the message listener associated with this session.
*
* @exception JMSException if JMS fails to get the message listener
* due to an internal error in JMS Provider.
*
* @see javax.jms.Session#setMessageListener
* @see javax.jms.ServerSessionPool
* @see javax.jms.ServerSession
*/
public MessageListener
getMessageListener() throws JMSException {
checkSessionState();
return serverSessionRunner.getMessageListener();
}
/** Set the session's distinguished message listener (optional).
* When it is set no other form of message receipt in the session can
* be used; however, all forms of sending messages are still supported.
* This is an expert facility not used by regular JMS clients.
*
* @param listener the message listener to associate with this session.
*
* @exception JMSException if JMS fails to set the message listener
* due to an internal error in JMS Provider.
*
* @see javax.jms.Session#getMessageListener
* @see javax.jms.ServerSessionPool
* @see javax.jms.ServerSession
*/
public /*synchronized*/ void
setMessageListener(MessageListener listener) throws JMSException {
checkSessionState();
if (listener != null && consumers.size() > 0) {
String errorString = AdministeredObject.cr.getKString(ClientResources.X_SVRSESSION_MESSAGECONSUMER);
JMSException jmse =
new javax.jms.IllegalStateException (errorString, ClientResources.X_SVRSESSION_MESSAGECONSUMER);
ExceptionHandler.throwJMSException(jmse);
}
serverSessionRunner.setMessageListener(listener);
}
/**
* Only intended to be used by Application Servers (optional operation).
*
* @see javax.jms.ServerSession
*/
public void run() {
try {
serverSessionRunner.run();
} finally {
//clean up?
;
}
}
protected void loadMessageToServerSession(MessageImpl message,
ServerSession ss,
boolean isDMQMessage) {
serverSessionRunner.loadMessage(message, ss, isDMQMessage);
}
protected SessionQueue getSessionQueue() {
return sessionQueue;
}
/**
* Non-public API. This method should be called right after ack is
* returned.
*
* @return ack flag for acknowledgement. If true, ack waits for broker's
* acknowledgement. Otherwise, acknowledge returns without waiting for
* broker's acknowledgement.
*/
public boolean _getAckSendAcknowledge() {
return ackPkt.getSendAcknowledge();
}
/**
* Get acknowledge mode
*/
public int _getAcknowledgeMode() {
return acknowledgeMode;
}
protected boolean _getXaTxnMode() {
return xaTxnMode;
}
protected void _setXaTxnMode(boolean mode) {
xaTxnMode = mode;
}
public long getBrokerSessionID() {
return brokerSessionID;
}
public void setBrokerSessionID(long brokerSessionID) {
this.brokerSessionID = brokerSessionID;
}
public Transaction _getTransaction()
{
return transaction;
}
public void setFailoverOccurred(boolean v) {
failoverOccurred = v;
}
public void initXATransactionForMC(long transactionID)
throws JMSException
{
if (transaction == null) {
transaction = new Transaction(this, false);
}
transaction.setTransactionID(transactionID);
xaTxnMode = true;
isTransacted = true;
}
public void finishXATransactionForMC()
{
//No longer needs to be in a dist txn
xaTxnMode = false;
//Done with Transaction delegate!
isTransacted = false;
transaction = null;
}
public void dump(PrintStream ps) {
ps.println ("------ SessionImpl dump ------");
ps.println ("broker session ID: " + brokerSessionID);
ps.println ("session ID: " + sessionId);
//dump session reader status
if ( sessionReader != null ) {
sessionReader.dump(ps);
}
//dump session queue status
if ( sessionQueue != null ) {
sessionQueue.dump (ps);
}
//dump unAckedMessageQueue
if ( unAckedMessageQueue != null ) {
ps.println ("Number of Unacked messages: " + unAckedMessageQueue.size());
}
ps.println ("# of message consumers: " + consumers.size());
//dump message consumers status
Enumeration enum2 = consumers.elements();
while ( enum2.hasMoreElements() ) {
MessageConsumerImpl consumer = (MessageConsumerImpl) enum2.nextElement();
consumer.dump (ps);
}
serverSessionRunner.dump(ps);
}
protected Hashtable getDebugState(boolean verbose) {
Hashtable ht = new Hashtable();
ht.put("sessionId", String.valueOf(sessionId));
ht.put("brokerSessionID", String.valueOf(brokerSessionID));
ht.put("isTransacted", String.valueOf(isTransacted));
ht.put("ackMode", String.valueOf(acknowledgeMode));
ht.put("dupsOkLimit", String.valueOf(dupsOkLimit));
ht.put("isAckLimited", String.valueOf(isAckLimited));
ht.put("ackLimit", String.valueOf(ackLimit));
ht.put("ackCounter", String.valueOf(ackCounter));
ht.put("xaTxnMode", String.valueOf(xaTxnMode));
ht.put("rxCount", String.valueOf(TEST_rxCount));
ht.put("ackCount", String.valueOf(TEST_ackCount));
ht.put("isStopped", String.valueOf(isStopped));
ht.put("# Consumers", String.valueOf(consumers.size()));
int n = 0;
Enumeration enum2 = consumers.elements();
while (enum2.hasMoreElements()) {
MessageConsumerImpl consumer = (MessageConsumerImpl)
enum2.nextElement();
ht.put("Consumer[" + n + "]", consumer.getDebugState(verbose));
n++;
}
ht.put("# Producers", String.valueOf(producers.size()));
MessageProducerImpl[] _producers = (MessageProducerImpl[])
producers.toArray(new MessageProducerImpl[0]);
for (int i = 0; i < _producers.length; i++) {
ht.put("Producer[" + i + "]",
_producers[i].getDebugState(verbose));
}
ht.put("unAckedMessageQueueSize", unAckedMessageQueue.size());
if (verbose) {
ht.put("unAckedMessageQueue", unAckedMessageQueue);
}
SessionQueue ssq = sessionQueue;
if (ssq != null) {
ht.put("sessionQueue", ssq.getDebugState(verbose));
}
ConnectionConsumerImpl cc = connectionConsumer;
if (cc != null) {
ht.put("connectionConsumer", cc.getDebugState(verbose));
}
ServerSessionRunner ssr = serverSessionRunner;
if (ssr != null) {
ht.put("serverSessionRunner", ssr.getDebugState(verbose));
}
return ht;
}
/**
* backward compatibility -- 4934856
* @param message the message used to call client ack method.
* @throws JMSException
*/
private void
checkClientAckMessage (MessageImpl message) throws JMSException {
if (connection.getBrokerProtocolLevel() <
com.sun.messaging.jmq.io.PacketType.VERSION350) {
Long id = new Long (message.getInterestID());
if ( consumers.containsKey ( id ) == false ) {
// "Cannot acknowledge message for closed consumer"
String errorString = AdministeredObject.cr.getKString(ClientResources.X_CLIENT_ACKNOWLEDGE);
JMSException jmse =
new javax.jms.IllegalStateException (errorString, ClientResources.X_CLIENT_ACKNOWLEDGE);
ExceptionHandler.throwJMSException(jmse);
}
}
}
/**
* When message consumer is closed, we need to remove unacked
* messages for transacted/clientAck session. -- 4934856
*/
protected void removeUnAckedMessages(Long interestId) throws JMSException {
int size = unAckedMessageQueue.size();
if ( size > 0 ) { //there are messages in the unacked q.
Vector removeq = new Vector();
//interest id for this consumer
//XXX PROTOCOL2.1
long consumerID = interestId.longValue();
//find unacked messages for this consumer and put
//them in the removeq
//this is not synchronized because no other thread is
//modifying the unackq.
for ( int i=0; i