/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.activemq.artemis.core.protocol.core.impl; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.ICoreMessage; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.SendAcknowledgementHandler; import org.apache.activemq.artemis.core.client.ActiveMQClientLogger; import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle; import org.apache.activemq.artemis.core.client.impl.AddressQueryImpl; import org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl; import org.apache.activemq.artemis.core.client.impl.ClientConsumerInternal; import org.apache.activemq.artemis.core.client.impl.ClientLargeMessageInternal; import org.apache.activemq.artemis.core.client.impl.ClientMessageInternal; import org.apache.activemq.artemis.core.client.impl.ClientProducerCreditsImpl; import org.apache.activemq.artemis.core.client.impl.ClientSessionImpl; import org.apache.activemq.artemis.core.message.impl.CoreMessage; import org.apache.activemq.artemis.core.protocol.core.Channel; import org.apache.activemq.artemis.core.protocol.core.ChannelHandler; import org.apache.activemq.artemis.core.protocol.core.CommandConfirmationHandler; import org.apache.activemq.artemis.core.protocol.core.CoreRemotingConnection; import org.apache.activemq.artemis.core.protocol.core.Packet; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ActiveMQExceptionMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateAddressMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateQueueMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateQueueMessage_V2; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateSessionMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateSharedQueueMessage_V2; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.DisconnectConsumerMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.DisconnectConsumerWithKillMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ReattachSessionMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ReattachSessionResponseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.RollbackMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionAcknowledgeMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionAddMetaDataMessageV2; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionBindingQueryMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionBindingQueryResponseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionBindingQueryResponseMessage_V2; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionBindingQueryResponseMessage_V3; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionBindingQueryResponseMessage_V4; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCloseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionConsumerCloseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionConsumerFlowCreditMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCreateConsumerMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionDeleteQueueMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionExpireMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionForceConsumerDelivery; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionIndividualAcknowledgeMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionProducerCreditsFailMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionProducerCreditsMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionQueueQueryMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionQueueQueryResponseMessage_V3; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveContinuationMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveLargeMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionRequestProducerCreditsMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendContinuationMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendLargeMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionUniqueAddMetaDataMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAAfterFailedMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXACommitMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAEndMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAForgetMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAGetInDoubtXidsResponseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAGetTimeoutResponseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAJoinMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAPrepareMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAResponseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAResumeMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXARollbackMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXASetTimeoutMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXASetTimeoutResponseMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAStartMessage; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.remoting.Connection; import org.apache.activemq.artemis.spi.core.remoting.ReadyListener; import org.apache.activemq.artemis.spi.core.remoting.SessionContext; import org.apache.activemq.artemis.utils.TokenBucketLimiterImpl; import org.apache.activemq.artemis.utils.VersionLoader; import org.jboss.logging.Logger; import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.DISCONNECT_CONSUMER; import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.EXCEPTION; import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_CONTINUATION; import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_LARGE_MSG; import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_MSG; public class ActiveMQSessionContext extends SessionContext { private static final Logger logger = Logger.getLogger(ActiveMQSessionContext.class); private final Channel sessionChannel; private final int serverVersion; private int confirmationWindow; private String name; private boolean killed; protected Channel getSessionChannel() { return sessionChannel; } protected String getName() { return name; } @Override public void resetName(String name) { this.name = name; } protected int getConfirmationWindow() { return confirmationWindow; } public ActiveMQSessionContext(String name, RemotingConnection remotingConnection, Channel sessionChannel, int serverVersion, int confirmationWindow) { super(remotingConnection); this.name = name; this.sessionChannel = sessionChannel; this.serverVersion = serverVersion; this.confirmationWindow = confirmationWindow; ChannelHandler handler = new ClientSessionPacketHandler(); sessionChannel.setHandler(handler); if (confirmationWindow >= 0) { sessionChannel.setCommandConfirmationHandler(confirmationHandler); } } @Override public int getReconnectID() { return sessionChannel.getReconnectID(); } public boolean isKilled() { return killed; } public void kill() { this.killed = true; } private final CommandConfirmationHandler confirmationHandler = new CommandConfirmationHandler() { @Override public void commandConfirmed(final Packet packet) { if (packet.getType() == PacketImpl.SESS_SEND) { SessionSendMessage ssm = (SessionSendMessage) packet; callSendAck(ssm.getHandler(), ssm.getMessage()); } else if (packet.getType() == PacketImpl.SESS_SEND_CONTINUATION) { SessionSendContinuationMessage scm = (SessionSendContinuationMessage) packet; if (!scm.isContinues()) { callSendAck(scm.getHandler(), scm.getMessage()); } } } private void callSendAck(SendAcknowledgementHandler handler, final Message message) { if (handler != null) { handler.sendAcknowledged(message); } else if (sendAckHandler != null) { sendAckHandler.sendAcknowledged(message); } } }; // Failover utility methods @Override public void returnBlocking(ActiveMQException cause) { sessionChannel.returnBlocking(cause); } @Override public void lockCommunications() { sessionChannel.lock(); } @Override public void releaseCommunications() { sessionChannel.setTransferring(false); sessionChannel.unlock(); } @Override public void cleanup() { sessionChannel.close(); // if the server is sending a disconnect // any pending blocked operation could hang without this sessionChannel.returnBlocking(); } @Override public void linkFlowControl(SimpleString address, ClientProducerCreditsImpl clientProducerCredits) { // nothing to be done here... Flow control here is done on the core side } @Override public void setSendAcknowledgementHandler(final SendAcknowledgementHandler handler) { sessionChannel.setCommandConfirmationHandler(confirmationHandler); this.sendAckHandler = handler; } @Override public void createSharedQueue(SimpleString address, SimpleString queueName, RoutingType routingType, SimpleString filterString, boolean durable) throws ActiveMQException { sessionChannel.sendBlocking(new CreateSharedQueueMessage_V2(address, queueName, routingType, filterString, durable, true), PacketImpl.NULL_RESPONSE); } @Override public void createSharedQueue(SimpleString address, SimpleString queueName, SimpleString filterString, boolean durable) throws ActiveMQException { createSharedQueue(address, queueName, null, filterString, durable); } @Override public void deleteQueue(final SimpleString queueName) throws ActiveMQException { sessionChannel.sendBlocking(new SessionDeleteQueueMessage(queueName), PacketImpl.NULL_RESPONSE); } @Override public ClientSession.QueueQuery queueQuery(final SimpleString queueName) throws ActiveMQException { SessionQueueQueryMessage request = new SessionQueueQueryMessage(queueName); SessionQueueQueryResponseMessage_V3 response = (SessionQueueQueryResponseMessage_V3) sessionChannel.sendBlocking(request, PacketImpl.SESS_QUEUEQUERY_RESP_V3); return response.toQueueQuery(); } @Override public boolean isWritable(ReadyListener callback) { return remotingConnection.isWritable(callback); } @Override public ClientConsumerInternal createConsumer(SimpleString queueName, SimpleString filterString, int windowSize, int maxRate, int ackBatchSize, boolean browseOnly, Executor executor, Executor flowControlExecutor) throws ActiveMQException { long consumerID = idGenerator.generateID(); ActiveMQConsumerContext consumerContext = new ActiveMQConsumerContext(consumerID); SessionCreateConsumerMessage request = new SessionCreateConsumerMessage(consumerID, queueName, filterString, browseOnly, true); SessionQueueQueryResponseMessage_V3 queueInfo = (SessionQueueQueryResponseMessage_V3) sessionChannel.sendBlocking(request, PacketImpl.SESS_QUEUEQUERY_RESP_V3); // The actual windows size that gets used is determined by the user since // could be overridden on the queue settings // The value we send is just a hint return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, calcWindowSize(windowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); } @Override public int getServerVersion() { return serverVersion; } @Override public ClientSession.AddressQuery addressQuery(final SimpleString address) throws ActiveMQException { if (sessionChannel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V4, getServerVersion())) { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP_V4); SessionBindingQueryResponseMessage_V4 response = (SessionBindingQueryResponseMessage_V4) packet; return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), response.isAutoCreateAddresses(), response.isDefaultPurgeOnNoConsumers(), response.getDefaultMaxConsumers()); } else if (sessionChannel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V3, getServerVersion())) { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP_V3); SessionBindingQueryResponseMessage_V3 response = (SessionBindingQueryResponseMessage_V3) packet; return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), response.isAutoCreateAddresses(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); } else if (sessionChannel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V2, getServerVersion())) { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP_V2); SessionBindingQueryResponseMessage_V2 response = (SessionBindingQueryResponseMessage_V2) packet; return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); } else { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP); SessionBindingQueryResponseMessage response = (SessionBindingQueryResponseMessage) packet; return new AddressQueryImpl(response.isExists(), response.getQueueNames(), false, false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); } } @Override public void closeConsumer(final ClientConsumer consumer) throws ActiveMQException { sessionChannel.sendBlocking(new SessionConsumerCloseMessage(getConsumerID(consumer)), PacketImpl.NULL_RESPONSE); } @Override public void sendConsumerCredits(final ClientConsumer consumer, final int credits) { sessionChannel.send(new SessionConsumerFlowCreditMessage(getConsumerID(consumer), credits)); } @Override public void forceDelivery(final ClientConsumer consumer, final long sequence) throws ActiveMQException { SessionForceConsumerDelivery request = new SessionForceConsumerDelivery(getConsumerID(consumer), sequence); sessionChannel.send(request); } @Override public void simpleCommit() throws ActiveMQException { sessionChannel.sendBlocking(new PacketImpl(PacketImpl.SESS_COMMIT), PacketImpl.NULL_RESPONSE); } @Override public void simpleRollback(boolean lastMessageAsDelivered) throws ActiveMQException { sessionChannel.sendBlocking(new RollbackMessage(lastMessageAsDelivered), PacketImpl.NULL_RESPONSE); } @Override public void sessionStart() throws ActiveMQException { sessionChannel.send(new PacketImpl(PacketImpl.SESS_START)); } @Override public void sessionStop() throws ActiveMQException { sessionChannel.sendBlocking(new PacketImpl(PacketImpl.SESS_STOP), PacketImpl.NULL_RESPONSE); } @Override public void addSessionMetadata(String key, String data) throws ActiveMQException { sessionChannel.sendBlocking(new SessionAddMetaDataMessageV2(key, data), PacketImpl.NULL_RESPONSE); } @Override public void addUniqueMetaData(String key, String data) throws ActiveMQException { sessionChannel.sendBlocking(new SessionUniqueAddMetaDataMessage(key, data), PacketImpl.NULL_RESPONSE); } @Override public void xaCommit(Xid xid, boolean onePhase) throws XAException, ActiveMQException { SessionXACommitMessage packet = new SessionXACommitMessage(xid, onePhase); SessionXAResponseMessage response = (SessionXAResponseMessage) sessionChannel.sendBlocking(packet, PacketImpl.SESS_XA_RESP); if (response.isError()) { throw new XAException(response.getResponseCode()); } if (logger.isTraceEnabled()) { logger.trace("finished commit on " + ClientSessionImpl.convert(xid) + " with response = " + response); } } @Override public void xaEnd(Xid xid, int flags) throws XAException, ActiveMQException { Packet packet; if (flags == XAResource.TMSUSPEND) { packet = new PacketImpl(PacketImpl.SESS_XA_SUSPEND); } else if (flags == XAResource.TMSUCCESS) { packet = new SessionXAEndMessage(xid, false); } else if (flags == XAResource.TMFAIL) { packet = new SessionXAEndMessage(xid, true); } else { throw new XAException(XAException.XAER_INVAL); } SessionXAResponseMessage response = (SessionXAResponseMessage) sessionChannel.sendBlocking(packet, PacketImpl.SESS_XA_RESP); if (response.isError()) { throw new XAException(response.getResponseCode()); } } @Override public void sendProducerCreditsMessage(final int credits, final SimpleString address) { sessionChannel.send(new SessionRequestProducerCreditsMessage(credits, address)); } /** * ActiveMQ Artemis does support large messages * * @return */ @Override public boolean supportsLargeMessage() { return true; } @Override public int getCreditsOnSendingFull(Message msgI) { return msgI.getEncodeSize(); } @Override public void sendFullMessage(ICoreMessage msgI, boolean sendBlocking, SendAcknowledgementHandler handler, SimpleString defaultAddress) throws ActiveMQException { SessionSendMessage packet = new SessionSendMessage(msgI, sendBlocking, handler); if (sendBlocking) { sessionChannel.sendBlocking(packet, PacketImpl.NULL_RESPONSE); } else { sessionChannel.sendBatched(packet); } } @Override public int sendInitialChunkOnLargeMessage(Message msgI) throws ActiveMQException { SessionSendLargeMessage initialChunk = new SessionSendLargeMessage(msgI); sessionChannel.send(initialChunk); return ((CoreMessage)msgI).getHeadersAndPropertiesEncodeSize(); } @Override public int sendLargeMessageChunk(Message msgI, long messageBodySize, boolean sendBlocking, boolean lastChunk, byte[] chunk, int reconnectID, SendAcknowledgementHandler messageHandler) throws ActiveMQException { return sendSessionSendContinuationMessage(this.sessionChannel, msgI, messageBodySize, sendBlocking, lastChunk, chunk, messageHandler); } @Override public int sendServerLargeMessageChunk(Message msgI, long messageBodySize, boolean sendBlocking, boolean lastChunk, byte[] chunk, SendAcknowledgementHandler messageHandler) throws ActiveMQException { return sendSessionSendContinuationMessage(this.sessionChannel, msgI, messageBodySize, sendBlocking, lastChunk, chunk, messageHandler); } @Override public void sendACK(boolean individual, boolean block, final ClientConsumer consumer, final Message message) throws ActiveMQException { PacketImpl messagePacket; if (individual) { messagePacket = new SessionIndividualAcknowledgeMessage(getConsumerID(consumer), message.getMessageID(), block); } else { messagePacket = new SessionAcknowledgeMessage(getConsumerID(consumer), message.getMessageID(), block); } if (block) { sessionChannel.sendBlocking(messagePacket, PacketImpl.NULL_RESPONSE); } else { sessionChannel.sendBatched(messagePacket); } } @Override public void expireMessage(final ClientConsumer consumer, Message message) throws ActiveMQException { SessionExpireMessage messagePacket = new SessionExpireMessage(getConsumerID(consumer), message.getMessageID()); sessionChannel.send(messagePacket); } @Override public void sessionClose() throws ActiveMQException { sessionChannel.sendBlocking(new SessionCloseMessage(), PacketImpl.NULL_RESPONSE); } @Override public void xaForget(Xid xid) throws XAException, ActiveMQException { SessionXAResponseMessage response = (SessionXAResponseMessage) sessionChannel.sendBlocking(new SessionXAForgetMessage(xid), PacketImpl.SESS_XA_RESP); if (response.isError()) { throw new XAException(response.getResponseCode()); } } @Override public int xaPrepare(Xid xid) throws XAException, ActiveMQException { SessionXAPrepareMessage packet = new SessionXAPrepareMessage(xid); SessionXAResponseMessage response = (SessionXAResponseMessage) sessionChannel.sendBlocking(packet, PacketImpl.SESS_XA_RESP); if (response.isError()) { throw new XAException(response.getResponseCode()); } else { return response.getResponseCode(); } } @Override public Xid[] xaScan() throws ActiveMQException { SessionXAGetInDoubtXidsResponseMessage response = (SessionXAGetInDoubtXidsResponseMessage) sessionChannel.sendBlocking(new PacketImpl(PacketImpl.SESS_XA_INDOUBT_XIDS), PacketImpl.SESS_XA_INDOUBT_XIDS_RESP); List<Xid> xids = response.getXids(); Xid[] xidArray = xids.toArray(new Xid[xids.size()]); return xidArray; } @Override public void xaRollback(Xid xid, boolean wasStarted) throws ActiveMQException, XAException { SessionXARollbackMessage packet = new SessionXARollbackMessage(xid); SessionXAResponseMessage response = (SessionXAResponseMessage) sessionChannel.sendBlocking(packet, PacketImpl.SESS_XA_RESP); if (response.isError()) { throw new XAException(response.getResponseCode()); } } @Override public void xaStart(Xid xid, int flags) throws XAException, ActiveMQException { Packet packet; if (flags == XAResource.TMJOIN) { packet = new SessionXAJoinMessage(xid); } else if (flags == XAResource.TMRESUME) { packet = new SessionXAResumeMessage(xid); } else if (flags == XAResource.TMNOFLAGS) { // Don't need to flush since the previous end will have done this packet = new SessionXAStartMessage(xid); } else { throw new XAException(XAException.XAER_INVAL); } SessionXAResponseMessage response = (SessionXAResponseMessage) sessionChannel.sendBlocking(packet, PacketImpl.SESS_XA_RESP); if (response.isError()) { ActiveMQClientLogger.LOGGER.errorCallingStart(response.getMessage(), response.getResponseCode()); throw new XAException(response.getResponseCode()); } } @Override public boolean configureTransactionTimeout(int seconds) throws ActiveMQException { SessionXASetTimeoutResponseMessage response = (SessionXASetTimeoutResponseMessage) sessionChannel.sendBlocking(new SessionXASetTimeoutMessage(seconds), PacketImpl.SESS_XA_SET_TIMEOUT_RESP); return response.isOK(); } @Override public int recoverSessionTimeout() throws ActiveMQException { SessionXAGetTimeoutResponseMessage response = (SessionXAGetTimeoutResponseMessage) sessionChannel.sendBlocking(new PacketImpl(PacketImpl.SESS_XA_GET_TIMEOUT), PacketImpl.SESS_XA_GET_TIMEOUT_RESP); return response.getTimeoutSeconds(); } @Override public void createAddress(SimpleString address, Set<RoutingType> routingTypes, final boolean autoCreated) throws ActiveMQException { CreateAddressMessage request = new CreateAddressMessage(address, routingTypes, autoCreated, true); sessionChannel.sendBlocking(request, PacketImpl.NULL_RESPONSE); } @Deprecated @Override public void createQueue(SimpleString address, SimpleString queueName, SimpleString filterString, boolean durable, boolean temp, boolean autoCreated) throws ActiveMQException { createQueue(address, ActiveMQDefaultConfiguration.getDefaultRoutingType(), queueName, filterString, durable, temp, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), autoCreated); } @Override public void createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filterString, boolean durable, boolean temp, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreated) throws ActiveMQException { CreateQueueMessage request = new CreateQueueMessage_V2(address, queueName, routingType, filterString, durable, temp, maxConsumers, purgeOnNoConsumers, autoCreated, true); sessionChannel.sendBlocking(request, PacketImpl.NULL_RESPONSE); } @Override public boolean reattachOnNewConnection(RemotingConnection newConnection) throws ActiveMQException { this.remotingConnection = newConnection; sessionChannel.transferConnection((CoreRemotingConnection) newConnection); Packet request = new ReattachSessionMessage(name, sessionChannel.getLastConfirmedCommandID()); Channel channel1 = getCoreConnection().getChannel(1, -1); ReattachSessionResponseMessage response = (ReattachSessionResponseMessage) channel1.sendBlocking(request, PacketImpl.REATTACH_SESSION_RESP); if (response.isReattached()) { ActiveMQClientLogger.LOGGER.replayingCommands(sessionChannel.getID(), response.getLastConfirmedCommandID()); // The session was found on the server - we reattached transparently ok sessionChannel.replayCommands(response.getLastConfirmedCommandID()); return true; } else { ActiveMQClientLogger.LOGGER.reconnectCreatingNewSession(sessionChannel.getID()); sessionChannel.clearCommands(); return false; } } @Override public void recreateSession(final String username, final String password, final int minLargeMessageSize, final boolean xa, final boolean autoCommitSends, final boolean autoCommitAcks, final boolean preAcknowledge) throws ActiveMQException { Packet createRequest = newCreateSession(username, password, minLargeMessageSize, xa, autoCommitSends, autoCommitAcks, preAcknowledge); boolean retry; do { try { getCreateChannel().sendBlocking(createRequest, PacketImpl.CREATESESSION_RESP); retry = false; } catch (ActiveMQException e) { // the session was created while its server was starting, retry it: if (e.getType() == ActiveMQExceptionType.SESSION_CREATION_REJECTED) { ActiveMQClientLogger.LOGGER.retryCreateSessionSeverStarting(name); retry = true; // sleep a little bit to avoid spinning too much try { Thread.sleep(10); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); throw e; } } else { throw e; } } } while (retry && !session.isClosing()); } protected CreateSessionMessage newCreateSession(String username, String password, int minLargeMessageSize, boolean xa, boolean autoCommitSends, boolean autoCommitAcks, boolean preAcknowledge) { return new CreateSessionMessage(name, sessionChannel.getID(), VersionLoader.getVersion().getIncrementingVersion(), username, password, minLargeMessageSize, xa, autoCommitSends, autoCommitAcks, preAcknowledge, confirmationWindow, null); } @Override public void recreateConsumerOnServer(ClientConsumerInternal consumerInternal) throws ActiveMQException { ClientSession.QueueQuery queueInfo = consumerInternal.getQueueInfo(); // We try and recreate any non durable queues, since they probably won't be there unless // they are defined in broker.xml // This allows e.g. JMS non durable subs and temporary queues to continue to be used after failover if (!queueInfo.isDurable()) { CreateQueueMessage_V2 createQueueRequest = new CreateQueueMessage_V2(queueInfo.getAddress(), queueInfo.getName(), queueInfo.getRoutingType(), queueInfo.getFilterString(), false, queueInfo.isTemporary(), queueInfo.getMaxConsumers(), queueInfo.isPurgeOnNoConsumers(), queueInfo.isAutoCreated(), false); sendPacketWithoutLock(sessionChannel, createQueueRequest); } SessionCreateConsumerMessage createConsumerRequest = new SessionCreateConsumerMessage(getConsumerID(consumerInternal), consumerInternal.getQueueName(), consumerInternal.getFilterString(), consumerInternal.isBrowseOnly(), false); sendPacketWithoutLock(sessionChannel, createConsumerRequest); int clientWindowSize = consumerInternal.getClientWindowSize(); if (clientWindowSize != 0) { SessionConsumerFlowCreditMessage packet = new SessionConsumerFlowCreditMessage(getConsumerID(consumerInternal), clientWindowSize); sendPacketWithoutLock(sessionChannel, packet); } else { // https://jira.jboss.org/browse/HORNETQ-522 SessionConsumerFlowCreditMessage packet = new SessionConsumerFlowCreditMessage(getConsumerID(consumerInternal), 1); sendPacketWithoutLock(sessionChannel, packet); } } @Override public void xaFailed(Xid xid) throws ActiveMQException { sendPacketWithoutLock(sessionChannel, new SessionXAAfterFailedMessage(xid)); } @Override public void restartSession() throws ActiveMQException { sendPacketWithoutLock(sessionChannel, new PacketImpl(PacketImpl.SESS_START)); } @Override public void resetMetadata(HashMap<String, String> metaDataToSend) { // Resetting the metadata after failover for (Map.Entry<String, String> entries : metaDataToSend.entrySet()) { sendPacketWithoutLock(sessionChannel, new SessionAddMetaDataMessageV2(entries.getKey(), entries.getValue(), false)); } } private Channel getCreateChannel() { return getCoreConnection().getChannel(1, -1); } private CoreRemotingConnection getCoreConnection() { return (CoreRemotingConnection) remotingConnection; } /** * This doesn't apply to other protocols probably, so it will be an ActiveMQ Artemis exclusive feature * * @throws ActiveMQException */ private void handleConsumerDisconnected(DisconnectConsumerMessage packet) throws ActiveMQException { DisconnectConsumerMessage message = packet; session.handleConsumerDisconnect(new ActiveMQConsumerContext(message.getConsumerId())); } private void handleReceivedMessagePacket(SessionReceiveMessage messagePacket) throws Exception { ClientMessageInternal msgi = (ClientMessageInternal) messagePacket.getMessage(); msgi.setDeliveryCount(messagePacket.getDeliveryCount()); msgi.setFlowControlSize(messagePacket.getPacketSize()); handleReceiveMessage(new ActiveMQConsumerContext(messagePacket.getConsumerID()), msgi); } private void handleReceiveLargeMessage(SessionReceiveLargeMessage serverPacket) throws Exception { ClientLargeMessageInternal clientLargeMessage = (ClientLargeMessageInternal) serverPacket.getLargeMessage(); clientLargeMessage.setFlowControlSize(serverPacket.getPacketSize()); clientLargeMessage.setDeliveryCount(serverPacket.getDeliveryCount()); handleReceiveLargeMessage(new ActiveMQConsumerContext(serverPacket.getConsumerID()), clientLargeMessage, serverPacket.getLargeMessageSize()); } private void handleReceiveContinuation(SessionReceiveContinuationMessage continuationPacket) throws Exception { handleReceiveContinuation(new ActiveMQConsumerContext(continuationPacket.getConsumerID()), continuationPacket.getBody(), continuationPacket.getPacketSize(), continuationPacket.isContinues()); } protected void handleReceiveProducerCredits(SessionProducerCreditsMessage message) { handleReceiveProducerCredits(message.getAddress(), message.getCredits()); } protected void handleReceiveProducerFailCredits(SessionProducerCreditsFailMessage message) { handleReceiveProducerFailCredits(message.getAddress(), message.getCredits()); } protected void handleReceiveSlowConsumerKillMessage(DisconnectConsumerWithKillMessage message) { if (message.getNodeID() != null) { kill(); } } private static int sendSessionSendContinuationMessage(Channel channel, Message msgI, long messageBodySize, boolean sendBlocking, boolean lastChunk, byte[] chunk, SendAcknowledgementHandler messageHandler) throws ActiveMQException { final boolean requiresResponse = lastChunk && sendBlocking; final SessionSendContinuationMessage chunkPacket = new SessionSendContinuationMessage(msgI, chunk, !lastChunk, requiresResponse, messageBodySize, messageHandler); final int expectedEncodeSize = chunkPacket.expectedEncodeSize(); //perform a weak form of flow control to avoid OOM on tight loops final CoreRemotingConnection connection = channel.getConnection(); final long blockingCallTimeoutMillis = Math.max(0, connection.getBlockingCallTimeout()); final long startFlowControl = System.nanoTime(); final boolean isWritable = connection.blockUntilWritable(expectedEncodeSize, blockingCallTimeoutMillis); if (!isWritable) { final long endFlowControl = System.nanoTime(); final long elapsedFlowControl = endFlowControl - startFlowControl; final long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedFlowControl); ActiveMQClientLogger.LOGGER.timeoutStreamingLargeMessage(); logger.debug("try to write " + expectedEncodeSize + " bytes after blocked " + elapsedMillis + " ms on a not writable connection: [" + connection.getID() + "]"); } if (requiresResponse) { // When sending it blocking, only the last chunk will be blocking. channel.sendBlocking(chunkPacket, PacketImpl.NULL_RESPONSE); } else { channel.send(chunkPacket); } return chunkPacket.getPacketSize(); } class ClientSessionPacketHandler implements ChannelHandler { @Override public void handlePacket(final Packet packet) { byte type = packet.getType(); try { switch (type) { case DISCONNECT_CONSUMER: { handleConsumerDisconnected((DisconnectConsumerMessage) packet); break; } case SESS_RECEIVE_CONTINUATION: { handleReceiveContinuation((SessionReceiveContinuationMessage) packet); break; } case SESS_RECEIVE_MSG: { handleReceivedMessagePacket((SessionReceiveMessage) packet); break; } case SESS_RECEIVE_LARGE_MSG: { handleReceiveLargeMessage((SessionReceiveLargeMessage) packet); break; } case PacketImpl.SESS_PRODUCER_CREDITS: { handleReceiveProducerCredits((SessionProducerCreditsMessage) packet); break; } case PacketImpl.SESS_PRODUCER_FAIL_CREDITS: { handleReceiveProducerFailCredits((SessionProducerCreditsFailMessage) packet); break; } case PacketImpl.DISCONNECT_CONSUMER_KILL: { handleReceiveSlowConsumerKillMessage((DisconnectConsumerWithKillMessage) packet); break; } case EXCEPTION: { // We can only log these exceptions // maybe we should cache it on SessionContext and throw an exception on any next calls ActiveMQExceptionMessage mem = (ActiveMQExceptionMessage) packet; ActiveMQClientLogger.LOGGER.receivedExceptionAsynchronously(mem.getException()); break; } default: { throw new IllegalStateException("Invalid packet: " + type); } } } catch (Exception e) { ActiveMQClientLogger.LOGGER.failedToHandlePacket(e); } sessionChannel.confirm(packet); } } private long getConsumerID(ClientConsumer consumer) { return ((ActiveMQConsumerContext) consumer.getConsumerContext()).getId(); } protected ClassLoader lookupTCCL() { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); } protected int calcWindowSize(final int windowSize) { int clientWindowSize; if (windowSize == -1) { // No flow control - buffer can increase without bound! Only use with // caution for very fast consumers clientWindowSize = -1; } else if (windowSize == 0) { // Slow consumer - no buffering clientWindowSize = 0; } else if (windowSize == 1) { // Slow consumer = buffer 1 clientWindowSize = 1; } else if (windowSize > 1) { // Client window size is half server window size clientWindowSize = windowSize >> 1; } else { throw ActiveMQClientMessageBundle.BUNDLE.invalidWindowSize(windowSize); } return clientWindowSize; } private void sendPacketWithoutLock(final Channel parameterChannel, final Packet packet) { packet.setChannelID(parameterChannel.getID()); Connection conn = parameterChannel.getConnection().getTransportConnection(); ActiveMQBuffer buffer = packet.encode(this.getCoreConnection()); conn.write(buffer, false, false); } }