//Sanity check
if (state.isXA() && !isXAAndConsideredNonTransacted(state))
{
if (trace) { log.trace("Session is XA"); }
ConnectionState connState = (ConnectionState)state.getParent();
ResourceManager rm = connState.getResourceManager();
// An XASession should never be closed if there is prepared ack work that has not yet been
// committed or rolled back. Imagine if messages had been consumed in the session, and
// prepared but not committed. Then the connection was explicitly closed causing the
// session to close. Closing the session causes any outstanding delivered but unacked
// messages to be cancelled to the server which means they would be available for other
// consumers to consume. If another consumer then consumes them, then recover() is called
// and the original transaction is committed, then this means the same message has been
// delivered twice which breaks the once and only once delivery guarantee.
if (rm.checkForAcksInSession(state.getSessionID()))
{
throw new IllegalStateException(
"Attempt to close an XASession when there are still uncommitted acknowledgements!");
}
}
int ackMode = state.getAcknowledgeMode();
//We need to either ack (for auto_ack) or cancel (for client_ack)
//any deliveries - this is because the message listener might have closed
//before on message had finished executing
if (ackMode == Session.AUTO_ACKNOWLEDGE || isXAAndConsideredNonTransacted(state))
{
//Acknowledge or cancel any outstanding auto ack
DeliveryInfo remainingAutoAck = state.getAutoAckInfo();
if (remainingAutoAck != null)
{
if (trace) { log.trace(this + " handleClosing(). Found remaining auto ack. Will ack " + remainingAutoAck); }
try
{
ackDelivery(del, remainingAutoAck);
if (trace) { log.trace(this + " acked it"); }
}
finally
{
state.setAutoAckInfo(null);
}
}
}
else if (ackMode == Session.DUPS_OK_ACKNOWLEDGE)
{
//Ack any remaining deliveries
if (!state.getClientAckList().isEmpty())
{
try
{
acknowledgeDeliveries(del, state.getClientAckList());
}
finally
{
state.getClientAckList().clear();
state.setAutoAckInfo(null);
}
}
}
else if (ackMode == Session.CLIENT_ACKNOWLEDGE)
{
// Cancel any oustanding deliveries
// We cancel any client ack or transactional, we do this explicitly so we can pass the
// updated delivery count information from client to server. We could just do this on the
// server but we would lose delivery count info.
// CLIENT_ACKNOWLEDGE cannot be used with MDBs (i.e. no connection consumer)
// so is always safe to cancel on this session
cancelDeliveries(del, state.getClientAckList());
state.getClientAckList().clear();
}
else if (state.isTransacted() && !state.isXA())
{
//We need to explicitly cancel any deliveries back to the server
//from the resource manager, otherwise delivery count won't be updated
ConnectionState connState = (ConnectionState)state.getParent();
ResourceManager rm = connState.getResourceManager();
List dels = rm.getDeliveriesForSession(state.getSessionID());
cancelDeliveries(del, dels);
}