001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq; 018 019import java.io.IOException; 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.concurrent.ConcurrentHashMap; 028import java.util.concurrent.ConcurrentMap; 029import java.util.concurrent.CopyOnWriteArrayList; 030import java.util.concurrent.CountDownLatch; 031import java.util.concurrent.LinkedBlockingQueue; 032import java.util.concurrent.RejectedExecutionHandler; 033import java.util.concurrent.ThreadFactory; 034import java.util.concurrent.ThreadPoolExecutor; 035import java.util.concurrent.TimeUnit; 036import java.util.concurrent.atomic.AtomicBoolean; 037import java.util.concurrent.atomic.AtomicInteger; 038import java.util.concurrent.atomic.AtomicLong; 039 040import javax.jms.Connection; 041import javax.jms.ConnectionConsumer; 042import javax.jms.ConnectionMetaData; 043import javax.jms.Destination; 044import javax.jms.ExceptionListener; 045import javax.jms.IllegalStateException; 046import javax.jms.InvalidDestinationException; 047import javax.jms.JMSException; 048import javax.jms.Queue; 049import javax.jms.QueueConnection; 050import javax.jms.QueueSession; 051import javax.jms.ServerSessionPool; 052import javax.jms.Session; 053import javax.jms.Topic; 054import javax.jms.TopicConnection; 055import javax.jms.TopicSession; 056import javax.jms.XAConnection; 057 058import org.apache.activemq.advisory.DestinationSource; 059import org.apache.activemq.blob.BlobTransferPolicy; 060import org.apache.activemq.broker.region.policy.RedeliveryPolicyMap; 061import org.apache.activemq.command.ActiveMQDestination; 062import org.apache.activemq.command.ActiveMQMessage; 063import org.apache.activemq.command.ActiveMQTempDestination; 064import org.apache.activemq.command.ActiveMQTempQueue; 065import org.apache.activemq.command.ActiveMQTempTopic; 066import org.apache.activemq.command.BrokerInfo; 067import org.apache.activemq.command.Command; 068import org.apache.activemq.command.CommandTypes; 069import org.apache.activemq.command.ConnectionControl; 070import org.apache.activemq.command.ConnectionError; 071import org.apache.activemq.command.ConnectionId; 072import org.apache.activemq.command.ConnectionInfo; 073import org.apache.activemq.command.ConsumerControl; 074import org.apache.activemq.command.ConsumerId; 075import org.apache.activemq.command.ConsumerInfo; 076import org.apache.activemq.command.ControlCommand; 077import org.apache.activemq.command.DestinationInfo; 078import org.apache.activemq.command.ExceptionResponse; 079import org.apache.activemq.command.Message; 080import org.apache.activemq.command.MessageDispatch; 081import org.apache.activemq.command.MessageId; 082import org.apache.activemq.command.ProducerAck; 083import org.apache.activemq.command.ProducerId; 084import org.apache.activemq.command.RemoveInfo; 085import org.apache.activemq.command.RemoveSubscriptionInfo; 086import org.apache.activemq.command.Response; 087import org.apache.activemq.command.SessionId; 088import org.apache.activemq.command.ShutdownInfo; 089import org.apache.activemq.command.WireFormatInfo; 090import org.apache.activemq.management.JMSConnectionStatsImpl; 091import org.apache.activemq.management.JMSStatsImpl; 092import org.apache.activemq.management.StatsCapable; 093import org.apache.activemq.management.StatsImpl; 094import org.apache.activemq.state.CommandVisitorAdapter; 095import org.apache.activemq.thread.Scheduler; 096import org.apache.activemq.thread.TaskRunnerFactory; 097import org.apache.activemq.transport.FutureResponse; 098import org.apache.activemq.transport.RequestTimedOutIOException; 099import org.apache.activemq.transport.ResponseCallback; 100import org.apache.activemq.transport.Transport; 101import org.apache.activemq.transport.TransportListener; 102import org.apache.activemq.transport.failover.FailoverTransport; 103import org.apache.activemq.util.IdGenerator; 104import org.apache.activemq.util.IntrospectionSupport; 105import org.apache.activemq.util.JMSExceptionSupport; 106import org.apache.activemq.util.LongSequenceGenerator; 107import org.apache.activemq.util.ServiceSupport; 108import org.apache.activemq.util.ThreadPoolUtils; 109import org.slf4j.Logger; 110import org.slf4j.LoggerFactory; 111 112public class ActiveMQConnection implements Connection, TopicConnection, QueueConnection, StatsCapable, Closeable, TransportListener, EnhancedConnection { 113 114 public static final String DEFAULT_USER = ActiveMQConnectionFactory.DEFAULT_USER; 115 public static final String DEFAULT_PASSWORD = ActiveMQConnectionFactory.DEFAULT_PASSWORD; 116 public static final String DEFAULT_BROKER_URL = ActiveMQConnectionFactory.DEFAULT_BROKER_URL; 117 public static int DEFAULT_THREAD_POOL_SIZE = 1000; 118 119 private static final Logger LOG = LoggerFactory.getLogger(ActiveMQConnection.class); 120 121 public final ConcurrentMap<ActiveMQTempDestination, ActiveMQTempDestination> activeTempDestinations = new ConcurrentHashMap<>(); 122 123 protected boolean dispatchAsync=true; 124 protected boolean alwaysSessionAsync = true; 125 126 private TaskRunnerFactory sessionTaskRunner; 127 private final ThreadPoolExecutor executor; 128 129 // Connection state variables 130 private final ConnectionInfo info; 131 private ExceptionListener exceptionListener; 132 private ClientInternalExceptionListener clientInternalExceptionListener; 133 private boolean clientIDSet; 134 private boolean isConnectionInfoSentToBroker; 135 private boolean userSpecifiedClientID; 136 137 // Configuration options variables 138 private ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy(); 139 private BlobTransferPolicy blobTransferPolicy; 140 private RedeliveryPolicyMap redeliveryPolicyMap; 141 private MessageTransformer transformer; 142 143 private boolean disableTimeStampsByDefault; 144 private boolean optimizedMessageDispatch = true; 145 private boolean copyMessageOnSend = true; 146 private boolean useCompression; 147 private boolean objectMessageSerializationDefered; 148 private boolean useAsyncSend; 149 private boolean optimizeAcknowledge; 150 private long optimizeAcknowledgeTimeOut = 0; 151 private long optimizedAckScheduledAckInterval = 0; 152 private boolean nestedMapAndListEnabled = true; 153 private boolean useRetroactiveConsumer; 154 private boolean exclusiveConsumer; 155 private boolean alwaysSyncSend; 156 private int closeTimeout = 15000; 157 private boolean watchTopicAdvisories = true; 158 private long warnAboutUnstartedConnectionTimeout = 500L; 159 private int sendTimeout =0; 160 private boolean sendAcksAsync=true; 161 private boolean checkForDuplicates = true; 162 private boolean queueOnlyConnection = false; 163 private boolean consumerExpiryCheckEnabled = true; 164 165 private final Transport transport; 166 private final IdGenerator clientIdGenerator; 167 private final JMSStatsImpl factoryStats; 168 private final JMSConnectionStatsImpl stats; 169 170 private final AtomicBoolean started = new AtomicBoolean(false); 171 private final AtomicBoolean closing = new AtomicBoolean(false); 172 private final AtomicBoolean closed = new AtomicBoolean(false); 173 private final AtomicBoolean transportFailed = new AtomicBoolean(false); 174 private final CopyOnWriteArrayList<ActiveMQSession> sessions = new CopyOnWriteArrayList<>(); 175 private final CopyOnWriteArrayList<ActiveMQConnectionConsumer> connectionConsumers = new CopyOnWriteArrayList<>(); 176 private final CopyOnWriteArrayList<TransportListener> transportListeners = new CopyOnWriteArrayList<>(); 177 178 // Maps ConsumerIds to ActiveMQConsumer objects 179 private final ConcurrentMap<ConsumerId, ActiveMQDispatcher> dispatchers = new ConcurrentHashMap<>(); 180 private final ConcurrentMap<ProducerId, ActiveMQMessageProducer> producers = new ConcurrentHashMap<>(); 181 private final LongSequenceGenerator sessionIdGenerator = new LongSequenceGenerator(); 182 private final SessionId connectionSessionId; 183 private final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); 184 private final LongSequenceGenerator tempDestinationIdGenerator = new LongSequenceGenerator(); 185 private final LongSequenceGenerator localTransactionIdGenerator = new LongSequenceGenerator(); 186 187 private AdvisoryConsumer advisoryConsumer; 188 private final CountDownLatch brokerInfoReceived = new CountDownLatch(1); 189 private BrokerInfo brokerInfo; 190 private IOException firstFailureError; 191 private int producerWindowSize = ActiveMQConnectionFactory.DEFAULT_PRODUCER_WINDOW_SIZE; 192 193 // Assume that protocol is the latest. Change to the actual protocol 194 // version when a WireFormatInfo is received. 195 private final AtomicInteger protocolVersion = new AtomicInteger(CommandTypes.PROTOCOL_VERSION); 196 private final AtomicLong maxFrameSize = new AtomicLong(Long.MAX_VALUE); 197 private final long timeCreated; 198 private final ConnectionAudit connectionAudit = new ConnectionAudit(); 199 private DestinationSource destinationSource; 200 private final Object ensureConnectionInfoSentMutex = new Object(); 201 private boolean useDedicatedTaskRunner; 202 protected AtomicInteger transportInterruptionProcessingComplete = new AtomicInteger(0); 203 private long consumerFailoverRedeliveryWaitPeriod; 204 private volatile Scheduler scheduler; 205 private final Object schedulerLock = new Object(); 206 private boolean messagePrioritySupported = false; 207 private boolean transactedIndividualAck = false; 208 private boolean nonBlockingRedelivery = false; 209 private boolean rmIdFromConnectionId = false; 210 211 private int maxThreadPoolSize = DEFAULT_THREAD_POOL_SIZE; 212 private RejectedExecutionHandler rejectedTaskHandler = null; 213 214 private List<String> trustedPackages = new ArrayList<>(); 215 private boolean trustAllPackages = false; 216 private int connectResponseTimeout; 217 218 /** 219 * Construct an <code>ActiveMQConnection</code> 220 * 221 * @param transport 222 * @param factoryStats 223 * @throws Exception 224 */ 225 protected ActiveMQConnection(final Transport transport, IdGenerator clientIdGenerator, IdGenerator connectionIdGenerator, JMSStatsImpl factoryStats) throws Exception { 226 227 this.transport = transport; 228 this.clientIdGenerator = clientIdGenerator; 229 this.factoryStats = factoryStats; 230 231 // Configure a single threaded executor who's core thread can timeout if 232 // idle 233 executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() { 234 @Override 235 public Thread newThread(Runnable r) { 236 Thread thread = new Thread(r, "ActiveMQ Connection Executor: " + transport); 237 //Don't make these daemon threads - see https://issues.apache.org/jira/browse/AMQ-796 238 //thread.setDaemon(true); 239 return thread; 240 } 241 }); 242 // asyncConnectionThread.allowCoreThreadTimeOut(true); 243 String uniqueId = connectionIdGenerator.generateId(); 244 this.info = new ConnectionInfo(new ConnectionId(uniqueId)); 245 this.info.setManageable(true); 246 this.info.setFaultTolerant(transport.isFaultTolerant()); 247 this.connectionSessionId = new SessionId(info.getConnectionId(), -1); 248 249 this.transport.setTransportListener(this); 250 251 this.stats = new JMSConnectionStatsImpl(sessions, this instanceof XAConnection); 252 this.factoryStats.addConnection(this); 253 this.timeCreated = System.currentTimeMillis(); 254 this.connectionAudit.setCheckForDuplicates(transport.isFaultTolerant()); 255 } 256 257 protected void setUserName(String userName) { 258 this.info.setUserName(userName); 259 } 260 261 protected void setPassword(String password) { 262 this.info.setPassword(password); 263 } 264 265 /** 266 * A static helper method to create a new connection 267 * 268 * @return an ActiveMQConnection 269 * @throws JMSException 270 */ 271 public static ActiveMQConnection makeConnection() throws JMSException { 272 ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); 273 return (ActiveMQConnection)factory.createConnection(); 274 } 275 276 /** 277 * A static helper method to create a new connection 278 * 279 * @param uri 280 * @return and ActiveMQConnection 281 * @throws JMSException 282 */ 283 public static ActiveMQConnection makeConnection(String uri) throws JMSException, URISyntaxException { 284 ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(uri); 285 return (ActiveMQConnection)factory.createConnection(); 286 } 287 288 /** 289 * A static helper method to create a new connection 290 * 291 * @param user 292 * @param password 293 * @param uri 294 * @return an ActiveMQConnection 295 * @throws JMSException 296 */ 297 public static ActiveMQConnection makeConnection(String user, String password, String uri) throws JMSException, URISyntaxException { 298 ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(user, password, new URI(uri)); 299 return (ActiveMQConnection)factory.createConnection(); 300 } 301 302 /** 303 * @return a number unique for this connection 304 */ 305 public JMSConnectionStatsImpl getConnectionStats() { 306 return stats; 307 } 308 309 /** 310 * Creates a <CODE>Session</CODE> object. 311 * 312 * @param transacted indicates whether the session is transacted 313 * @param acknowledgeMode indicates whether the consumer or the client will 314 * acknowledge any messages it receives; ignored if the 315 * session is transacted. Legal values are 316 * <code>Session.AUTO_ACKNOWLEDGE</code>, 317 * <code>Session.CLIENT_ACKNOWLEDGE</code>, and 318 * <code>Session.DUPS_OK_ACKNOWLEDGE</code>. 319 * @return a newly created session 320 * @throws JMSException if the <CODE>Connection</CODE> object fails to 321 * create a session due to some internal error or lack of 322 * support for the specific transaction and acknowledgement 323 * mode. 324 * @see Session#AUTO_ACKNOWLEDGE 325 * @see Session#CLIENT_ACKNOWLEDGE 326 * @see Session#DUPS_OK_ACKNOWLEDGE 327 * @since 1.1 328 */ 329 @Override 330 public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException { 331 checkClosedOrFailed(); 332 ensureConnectionInfoSent(); 333 if (!transacted) { 334 if (acknowledgeMode == Session.SESSION_TRANSACTED) { 335 throw new JMSException("acknowledgeMode SESSION_TRANSACTED cannot be used for an non-transacted Session"); 336 } else if (acknowledgeMode < Session.SESSION_TRANSACTED || acknowledgeMode > ActiveMQSession.MAX_ACK_CONSTANT) { 337 throw new JMSException("invalid acknowledgeMode: " + acknowledgeMode + ". Valid values are Session.AUTO_ACKNOWLEDGE (1), " + 338 "Session.CLIENT_ACKNOWLEDGE (2), Session.DUPS_OK_ACKNOWLEDGE (3), ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE (4) or for transacted sessions Session.SESSION_TRANSACTED (0)"); 339 } 340 } 341 return new ActiveMQSession(this, getNextSessionId(), transacted ? Session.SESSION_TRANSACTED : acknowledgeMode, isDispatchAsync(), isAlwaysSessionAsync()); 342 } 343 344 /** 345 * @return sessionId 346 */ 347 protected SessionId getNextSessionId() { 348 return new SessionId(info.getConnectionId(), sessionIdGenerator.getNextSequenceId()); 349 } 350 351 /** 352 * Gets the client identifier for this connection. 353 * <P> 354 * This value is specific to the JMS provider. It is either preconfigured by 355 * an administrator in a <CODE> ConnectionFactory</CODE> object or assigned 356 * dynamically by the application by calling the <code>setClientID</code> 357 * method. 358 * 359 * @return the unique client identifier 360 * @throws JMSException if the JMS provider fails to return the client ID 361 * for this connection due to some internal error. 362 */ 363 @Override 364 public String getClientID() throws JMSException { 365 checkClosedOrFailed(); 366 return this.info.getClientId(); 367 } 368 369 /** 370 * Sets the client identifier for this connection. 371 * <P> 372 * The preferred way to assign a JMS client's client identifier is for it to 373 * be configured in a client-specific <CODE>ConnectionFactory</CODE> 374 * object and transparently assigned to the <CODE>Connection</CODE> object 375 * it creates. 376 * <P> 377 * Alternatively, a client can set a connection's client identifier using a 378 * provider-specific value. The facility to set a connection's client 379 * identifier explicitly is not a mechanism for overriding the identifier 380 * that has been administratively configured. It is provided for the case 381 * where no administratively specified identifier exists. If one does exist, 382 * an attempt to change it by setting it must throw an 383 * <CODE>IllegalStateException</CODE>. If a client sets the client 384 * identifier explicitly, it must do so immediately after it creates the 385 * connection and before any other action on the connection is taken. After 386 * this point, setting the client identifier is a programming error that 387 * should throw an <CODE>IllegalStateException</CODE>. 388 * <P> 389 * The purpose of the client identifier is to associate a connection and its 390 * objects with a state maintained on behalf of the client by a provider. 391 * The only such state identified by the JMS API is that required to support 392 * durable subscriptions. 393 * <P> 394 * If another connection with the same <code>clientID</code> is already 395 * running when this method is called, the JMS provider should detect the 396 * duplicate ID and throw an <CODE>InvalidClientIDException</CODE>. 397 * 398 * @param newClientID the unique client identifier 399 * @throws JMSException if the JMS provider fails to set the client ID for 400 * this connection due to some internal error. 401 * @throws javax.jms.InvalidClientIDException if the JMS client specifies an 402 * invalid or duplicate client ID. 403 * @throws javax.jms.IllegalStateException if the JMS client attempts to set 404 * a connection's client ID at the wrong time or when it has 405 * been administratively configured. 406 */ 407 @Override 408 public void setClientID(String newClientID) throws JMSException { 409 checkClosedOrFailed(); 410 411 if (this.clientIDSet) { 412 throw new IllegalStateException("The clientID has already been set"); 413 } 414 415 if (this.isConnectionInfoSentToBroker) { 416 throw new IllegalStateException("Setting clientID on a used Connection is not allowed"); 417 } 418 419 this.info.setClientId(newClientID); 420 this.userSpecifiedClientID = true; 421 ensureConnectionInfoSent(); 422 } 423 424 /** 425 * Sets the default client id that the connection will use if explicitly not 426 * set with the setClientId() call. 427 */ 428 public void setDefaultClientID(String clientID) throws JMSException { 429 this.info.setClientId(clientID); 430 this.userSpecifiedClientID = true; 431 } 432 433 /** 434 * Gets the metadata for this connection. 435 * 436 * @return the connection metadata 437 * @throws JMSException if the JMS provider fails to get the connection 438 * metadata for this connection. 439 * @see javax.jms.ConnectionMetaData 440 */ 441 @Override 442 public ConnectionMetaData getMetaData() throws JMSException { 443 checkClosedOrFailed(); 444 return ActiveMQConnectionMetaData.INSTANCE; 445 } 446 447 /** 448 * Gets the <CODE>ExceptionListener</CODE> object for this connection. Not 449 * every <CODE>Connection</CODE> has an <CODE>ExceptionListener</CODE> 450 * associated with it. 451 * 452 * @return the <CODE>ExceptionListener</CODE> for this connection, or 453 * null, if no <CODE>ExceptionListener</CODE> is associated with 454 * this connection. 455 * @throws JMSException if the JMS provider fails to get the 456 * <CODE>ExceptionListener</CODE> for this connection. 457 * @see javax.jms.Connection#setExceptionListener(ExceptionListener) 458 */ 459 @Override 460 public ExceptionListener getExceptionListener() throws JMSException { 461 checkClosedOrFailed(); 462 return this.exceptionListener; 463 } 464 465 /** 466 * Sets an exception listener for this connection. 467 * <P> 468 * If a JMS provider detects a serious problem with a connection, it informs 469 * the connection's <CODE> ExceptionListener</CODE>, if one has been 470 * registered. It does this by calling the listener's <CODE>onException 471 * </CODE> 472 * method, passing it a <CODE>JMSException</CODE> object describing the 473 * problem. 474 * <P> 475 * An exception listener allows a client to be notified of a problem 476 * asynchronously. Some connections only consume messages, so they would 477 * have no other way to learn their connection has failed. 478 * <P> 479 * A connection serializes execution of its <CODE>ExceptionListener</CODE>. 480 * <P> 481 * A JMS provider should attempt to resolve connection problems itself 482 * before it notifies the client of them. 483 * 484 * @param listener the exception listener 485 * @throws JMSException if the JMS provider fails to set the exception 486 * listener for this connection. 487 */ 488 @Override 489 public void setExceptionListener(ExceptionListener listener) throws JMSException { 490 checkClosedOrFailed(); 491 this.exceptionListener = listener; 492 } 493 494 /** 495 * Gets the <code>ClientInternalExceptionListener</code> object for this connection. 496 * Not every <CODE>ActiveMQConnectionn</CODE> has a <CODE>ClientInternalExceptionListener</CODE> 497 * associated with it. 498 * 499 * @return the listener or <code>null</code> if no listener is registered with the connection. 500 */ 501 public ClientInternalExceptionListener getClientInternalExceptionListener() { 502 return clientInternalExceptionListener; 503 } 504 505 /** 506 * Sets a client internal exception listener for this connection. 507 * The connection will notify the listener, if one has been registered, of exceptions thrown by container components 508 * (e.g. an EJB container in case of Message Driven Beans) during asynchronous processing of a message. 509 * It does this by calling the listener's <code>onException()</code> method passing it a <code>Throwable</code> 510 * describing the problem. 511 * 512 * @param listener the exception listener 513 */ 514 public void setClientInternalExceptionListener(ClientInternalExceptionListener listener) { 515 this.clientInternalExceptionListener = listener; 516 } 517 518 /** 519 * Starts (or restarts) a connection's delivery of incoming messages. A call 520 * to <CODE>start</CODE> on a connection that has already been started is 521 * ignored. 522 * 523 * @throws JMSException if the JMS provider fails to start message delivery 524 * due to some internal error. 525 * @see javax.jms.Connection#stop() 526 */ 527 @Override 528 public void start() throws JMSException { 529 checkClosedOrFailed(); 530 ensureConnectionInfoSent(); 531 if (started.compareAndSet(false, true)) { 532 for (Iterator<ActiveMQSession> i = sessions.iterator(); i.hasNext();) { 533 ActiveMQSession session = i.next(); 534 session.start(); 535 } 536 } 537 } 538 539 /** 540 * Temporarily stops a connection's delivery of incoming messages. Delivery 541 * can be restarted using the connection's <CODE>start</CODE> method. When 542 * the connection is stopped, delivery to all the connection's message 543 * consumers is inhibited: synchronous receives block, and messages are not 544 * delivered to message listeners. 545 * <P> 546 * This call blocks until receives and/or message listeners in progress have 547 * completed. 548 * <P> 549 * Stopping a connection has no effect on its ability to send messages. A 550 * call to <CODE>stop</CODE> on a connection that has already been stopped 551 * is ignored. 552 * <P> 553 * A call to <CODE>stop</CODE> must not return until delivery of messages 554 * has paused. This means that a client can rely on the fact that none of 555 * its message listeners will be called and that all threads of control 556 * waiting for <CODE>receive</CODE> calls to return will not return with a 557 * message until the connection is restarted. The receive timers for a 558 * stopped connection continue to advance, so receives may time out while 559 * the connection is stopped. 560 * <P> 561 * If message listeners are running when <CODE>stop</CODE> is invoked, the 562 * <CODE>stop</CODE> call must wait until all of them have returned before 563 * it may return. While these message listeners are completing, they must 564 * have the full services of the connection available to them. 565 * 566 * @throws JMSException if the JMS provider fails to stop message delivery 567 * due to some internal error. 568 * @see javax.jms.Connection#start() 569 */ 570 @Override 571 public void stop() throws JMSException { 572 doStop(true); 573 } 574 575 /** 576 * @see #stop() 577 * @param checkClosed <tt>true</tt> to check for already closed and throw {@link java.lang.IllegalStateException} if already closed, 578 * <tt>false</tt> to skip this check 579 * @throws JMSException if the JMS provider fails to stop message delivery due to some internal error. 580 */ 581 void doStop(boolean checkClosed) throws JMSException { 582 if (checkClosed) { 583 checkClosedOrFailed(); 584 } 585 if (started.compareAndSet(true, false)) { 586 synchronized(sessions) { 587 for (Iterator<ActiveMQSession> i = sessions.iterator(); i.hasNext();) { 588 ActiveMQSession s = i.next(); 589 s.stop(); 590 } 591 } 592 } 593 } 594 595 /** 596 * Closes the connection. 597 * <P> 598 * Since a provider typically allocates significant resources outside the 599 * JVM on behalf of a connection, clients should close these resources when 600 * they are not needed. Relying on garbage collection to eventually reclaim 601 * these resources may not be timely enough. 602 * <P> 603 * There is no need to close the sessions, producers, and consumers of a 604 * closed connection. 605 * <P> 606 * Closing a connection causes all temporary destinations to be deleted. 607 * <P> 608 * When this method is invoked, it should not return until message 609 * processing has been shut down in an orderly fashion. This means that all 610 * message listeners that may have been running have returned, and that all 611 * pending receives have returned. A close terminates all pending message 612 * receives on the connection's sessions' consumers. The receives may return 613 * with a message or with null, depending on whether there was a message 614 * available at the time of the close. If one or more of the connection's 615 * sessions' message listeners is processing a message at the time when 616 * connection <CODE>close</CODE> is invoked, all the facilities of the 617 * connection and its sessions must remain available to those listeners 618 * until they return control to the JMS provider. 619 * <P> 620 * Closing a connection causes any of its sessions' transactions in progress 621 * to be rolled back. In the case where a session's work is coordinated by 622 * an external transaction manager, a session's <CODE>commit</CODE> and 623 * <CODE> rollback</CODE> methods are not used and the result of a closed 624 * session's work is determined later by the transaction manager. Closing a 625 * connection does NOT force an acknowledgment of client-acknowledged 626 * sessions. 627 * <P> 628 * Invoking the <CODE>acknowledge</CODE> method of a received message from 629 * a closed connection's session must throw an 630 * <CODE>IllegalStateException</CODE>. Closing a closed connection must 631 * NOT throw an exception. 632 * 633 * @throws JMSException if the JMS provider fails to close the connection 634 * due to some internal error. For example, a failure to 635 * release resources or to close a socket connection can 636 * cause this exception to be thrown. 637 */ 638 @Override 639 public void close() throws JMSException { 640 try { 641 // If we were running, lets stop first. 642 if (!closed.get() && !transportFailed.get()) { 643 // do not fail if already closed as according to JMS spec we must not 644 // throw exception if already closed 645 doStop(false); 646 } 647 648 synchronized (this) { 649 if (!closed.get()) { 650 closing.set(true); 651 652 if (destinationSource != null) { 653 destinationSource.stop(); 654 destinationSource = null; 655 } 656 if (advisoryConsumer != null) { 657 advisoryConsumer.dispose(); 658 advisoryConsumer = null; 659 } 660 661 Scheduler scheduler = this.scheduler; 662 if (scheduler != null) { 663 try { 664 scheduler.stop(); 665 } catch (Exception e) { 666 JMSException ex = JMSExceptionSupport.create(e); 667 throw ex; 668 } 669 } 670 671 long lastDeliveredSequenceId = -1; 672 for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) { 673 ActiveMQSession s = i.next(); 674 s.dispose(); 675 lastDeliveredSequenceId = Math.max(lastDeliveredSequenceId, s.getLastDeliveredSequenceId()); 676 } 677 for (Iterator<ActiveMQConnectionConsumer> i = this.connectionConsumers.iterator(); i.hasNext();) { 678 ActiveMQConnectionConsumer c = i.next(); 679 c.dispose(); 680 } 681 682 this.activeTempDestinations.clear(); 683 684 try { 685 if (isConnectionInfoSentToBroker) { 686 // If we announced ourselves to the broker.. Try to let the broker 687 // know that the connection is being shutdown. 688 RemoveInfo removeCommand = info.createRemoveCommand(); 689 removeCommand.setLastDeliveredSequenceId(lastDeliveredSequenceId); 690 try { 691 syncSendPacket(removeCommand, closeTimeout); 692 } catch (JMSException e) { 693 if (e.getCause() instanceof RequestTimedOutIOException) { 694 // expected 695 } else { 696 throw e; 697 } 698 } 699 doAsyncSendPacket(new ShutdownInfo()); 700 } 701 } finally { // release anyway even if previous communication fails 702 started.set(false); 703 704 // TODO if we move the TaskRunnerFactory to the connection 705 // factory 706 // then we may need to call 707 // factory.onConnectionClose(this); 708 if (sessionTaskRunner != null) { 709 sessionTaskRunner.shutdown(); 710 } 711 closed.set(true); 712 closing.set(false); 713 } 714 } 715 } 716 } finally { 717 try { 718 if (executor != null) { 719 ThreadPoolUtils.shutdown(executor); 720 } 721 } catch (Throwable e) { 722 LOG.warn("Error shutting down thread pool: " + executor + ". This exception will be ignored.", e); 723 } 724 725 ServiceSupport.dispose(this.transport); 726 727 factoryStats.removeConnection(this); 728 } 729 } 730 731 /** 732 * Tells the broker to terminate its VM. This can be used to cleanly 733 * terminate a broker running in a standalone java process. Server must have 734 * property enable.vm.shutdown=true defined to allow this to work. 735 */ 736 // TODO : org.apache.activemq.message.BrokerAdminCommand not yet 737 // implemented. 738 /* 739 * public void terminateBrokerVM() throws JMSException { BrokerAdminCommand 740 * command = new BrokerAdminCommand(); 741 * command.setCommand(BrokerAdminCommand.SHUTDOWN_SERVER_VM); 742 * asyncSendPacket(command); } 743 */ 744 745 /** 746 * Create a durable connection consumer for this connection (optional 747 * operation). This is an expert facility not used by regular JMS clients. 748 * 749 * @param topic topic to access 750 * @param subscriptionName durable subscription name 751 * @param messageSelector only messages with properties matching the message 752 * selector expression are delivered. A value of null or an 753 * empty string indicates that there is no message selector 754 * for the message consumer. 755 * @param sessionPool the server session pool to associate with this durable 756 * connection consumer 757 * @param maxMessages the maximum number of messages that can be assigned to 758 * a server session at one time 759 * @return the durable connection consumer 760 * @throws JMSException if the <CODE>Connection</CODE> object fails to 761 * create a connection consumer due to some internal error 762 * or invalid arguments for <CODE>sessionPool</CODE> and 763 * <CODE>messageSelector</CODE>. 764 * @throws javax.jms.InvalidDestinationException if an invalid destination 765 * is specified. 766 * @throws javax.jms.InvalidSelectorException if the message selector is 767 * invalid. 768 * @see javax.jms.ConnectionConsumer 769 * @since 1.1 770 */ 771 @Override 772 public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) 773 throws JMSException { 774 return this.createDurableConnectionConsumer(topic, subscriptionName, messageSelector, sessionPool, maxMessages, false); 775 } 776 777 /** 778 * Create a durable connection consumer for this connection (optional 779 * operation). This is an expert facility not used by regular JMS clients. 780 * 781 * @param topic topic to access 782 * @param subscriptionName durable subscription name 783 * @param messageSelector only messages with properties matching the message 784 * selector expression are delivered. A value of null or an 785 * empty string indicates that there is no message selector 786 * for the message consumer. 787 * @param sessionPool the server session pool to associate with this durable 788 * connection consumer 789 * @param maxMessages the maximum number of messages that can be assigned to 790 * a server session at one time 791 * @param noLocal set true if you want to filter out messages published 792 * locally 793 * @return the durable connection consumer 794 * @throws JMSException if the <CODE>Connection</CODE> object fails to 795 * create a connection consumer due to some internal error 796 * or invalid arguments for <CODE>sessionPool</CODE> and 797 * <CODE>messageSelector</CODE>. 798 * @throws javax.jms.InvalidDestinationException if an invalid destination 799 * is specified. 800 * @throws javax.jms.InvalidSelectorException if the message selector is 801 * invalid. 802 * @see javax.jms.ConnectionConsumer 803 * @since 1.1 804 */ 805 public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages, 806 boolean noLocal) throws JMSException { 807 checkClosedOrFailed(); 808 809 if (queueOnlyConnection) { 810 throw new IllegalStateException("QueueConnection cannot be used to create Pub/Sub based resources."); 811 } 812 813 ensureConnectionInfoSent(); 814 SessionId sessionId = new SessionId(info.getConnectionId(), -1); 815 ConsumerInfo info = new ConsumerInfo(new ConsumerId(sessionId, consumerIdGenerator.getNextSequenceId())); 816 info.setDestination(ActiveMQMessageTransformation.transformDestination(topic)); 817 info.setSubscriptionName(subscriptionName); 818 info.setSelector(messageSelector); 819 info.setPrefetchSize(maxMessages); 820 info.setDispatchAsync(isDispatchAsync()); 821 822 // Allows the options on the destination to configure the consumerInfo 823 if (info.getDestination().getOptions() != null) { 824 Map<String, String> options = new HashMap<>(info.getDestination().getOptions()); 825 IntrospectionSupport.setProperties(this.info, options, "consumer."); 826 } 827 828 return new ActiveMQConnectionConsumer(this, sessionPool, info); 829 } 830 831 // Properties 832 // ------------------------------------------------------------------------- 833 834 /** 835 * Returns true if this connection has been started 836 * 837 * @return true if this Connection is started 838 */ 839 public boolean isStarted() { 840 return started.get(); 841 } 842 843 /** 844 * Returns true if the connection is closed 845 */ 846 public boolean isClosed() { 847 return closed.get(); 848 } 849 850 /** 851 * Returns true if the connection is in the process of being closed 852 */ 853 public boolean isClosing() { 854 return closing.get(); 855 } 856 857 /** 858 * Returns true if the underlying transport has failed 859 */ 860 public boolean isTransportFailed() { 861 return transportFailed.get(); 862 } 863 864 /** 865 * @return Returns the prefetchPolicy. 866 */ 867 public ActiveMQPrefetchPolicy getPrefetchPolicy() { 868 return prefetchPolicy; 869 } 870 871 /** 872 * Sets the <a 873 * href="http://activemq.apache.org/what-is-the-prefetch-limit-for.html">prefetch 874 * policy</a> for consumers created by this connection. 875 */ 876 public void setPrefetchPolicy(ActiveMQPrefetchPolicy prefetchPolicy) { 877 this.prefetchPolicy = prefetchPolicy; 878 } 879 880 /** 881 */ 882 public Transport getTransportChannel() { 883 return transport; 884 } 885 886 /** 887 * @return Returns the clientID of the connection, forcing one to be 888 * generated if one has not yet been configured. 889 */ 890 public String getInitializedClientID() throws JMSException { 891 ensureConnectionInfoSent(); 892 return info.getClientId(); 893 } 894 895 /** 896 * @return Returns the timeStampsDisableByDefault. 897 */ 898 public boolean isDisableTimeStampsByDefault() { 899 return disableTimeStampsByDefault; 900 } 901 902 /** 903 * Sets whether or not timestamps on messages should be disabled or not. If 904 * you disable them it adds a small performance boost. 905 */ 906 public void setDisableTimeStampsByDefault(boolean timeStampsDisableByDefault) { 907 this.disableTimeStampsByDefault = timeStampsDisableByDefault; 908 } 909 910 /** 911 * @return Returns the dispatchOptimizedMessage. 912 */ 913 public boolean isOptimizedMessageDispatch() { 914 return optimizedMessageDispatch; 915 } 916 917 /** 918 * If this flag is set then an larger prefetch limit is used - only 919 * applicable for durable topic subscribers. 920 */ 921 public void setOptimizedMessageDispatch(boolean dispatchOptimizedMessage) { 922 this.optimizedMessageDispatch = dispatchOptimizedMessage; 923 } 924 925 /** 926 * @return Returns the closeTimeout. 927 */ 928 public int getCloseTimeout() { 929 return closeTimeout; 930 } 931 932 /** 933 * Sets the timeout before a close is considered complete. Normally a 934 * close() on a connection waits for confirmation from the broker; this 935 * allows that operation to timeout to save the client hanging if there is 936 * no broker 937 */ 938 public void setCloseTimeout(int closeTimeout) { 939 this.closeTimeout = closeTimeout; 940 } 941 942 /** 943 * @return ConnectionInfo 944 */ 945 public ConnectionInfo getConnectionInfo() { 946 return this.info; 947 } 948 949 public boolean isUseRetroactiveConsumer() { 950 return useRetroactiveConsumer; 951 } 952 953 /** 954 * Sets whether or not retroactive consumers are enabled. Retroactive 955 * consumers allow non-durable topic subscribers to receive old messages 956 * that were published before the non-durable subscriber started. 957 */ 958 public void setUseRetroactiveConsumer(boolean useRetroactiveConsumer) { 959 this.useRetroactiveConsumer = useRetroactiveConsumer; 960 } 961 962 public boolean isNestedMapAndListEnabled() { 963 return nestedMapAndListEnabled; 964 } 965 966 /** 967 * Enables/disables whether or not Message properties and MapMessage entries 968 * support <a 969 * href="http://activemq.apache.org/structured-message-properties-and-mapmessages.html">Nested 970 * Structures</a> of Map and List objects 971 */ 972 public void setNestedMapAndListEnabled(boolean structuredMapsEnabled) { 973 this.nestedMapAndListEnabled = structuredMapsEnabled; 974 } 975 976 public boolean isExclusiveConsumer() { 977 return exclusiveConsumer; 978 } 979 980 /** 981 * Enables or disables whether or not queue consumers should be exclusive or 982 * not for example to preserve ordering when not using <a 983 * href="http://activemq.apache.org/message-groups.html">Message Groups</a> 984 * 985 * @param exclusiveConsumer 986 */ 987 public void setExclusiveConsumer(boolean exclusiveConsumer) { 988 this.exclusiveConsumer = exclusiveConsumer; 989 } 990 991 /** 992 * Adds a transport listener so that a client can be notified of events in 993 * the underlying transport 994 */ 995 public void addTransportListener(TransportListener transportListener) { 996 transportListeners.add(transportListener); 997 } 998 999 public void removeTransportListener(TransportListener transportListener) { 1000 transportListeners.remove(transportListener); 1001 } 1002 1003 public boolean isUseDedicatedTaskRunner() { 1004 return useDedicatedTaskRunner; 1005 } 1006 1007 public void setUseDedicatedTaskRunner(boolean useDedicatedTaskRunner) { 1008 this.useDedicatedTaskRunner = useDedicatedTaskRunner; 1009 } 1010 1011 public TaskRunnerFactory getSessionTaskRunner() { 1012 synchronized (this) { 1013 if (sessionTaskRunner == null) { 1014 sessionTaskRunner = new TaskRunnerFactory("ActiveMQ Session Task", ThreadPriorities.INBOUND_CLIENT_SESSION, false, 1000, isUseDedicatedTaskRunner(), maxThreadPoolSize); 1015 sessionTaskRunner.setRejectedTaskHandler(rejectedTaskHandler); 1016 } 1017 } 1018 return sessionTaskRunner; 1019 } 1020 1021 public void setSessionTaskRunner(TaskRunnerFactory sessionTaskRunner) { 1022 this.sessionTaskRunner = sessionTaskRunner; 1023 } 1024 1025 public MessageTransformer getTransformer() { 1026 return transformer; 1027 } 1028 1029 /** 1030 * Sets the transformer used to transform messages before they are sent on 1031 * to the JMS bus or when they are received from the bus but before they are 1032 * delivered to the JMS client 1033 */ 1034 public void setTransformer(MessageTransformer transformer) { 1035 this.transformer = transformer; 1036 } 1037 1038 /** 1039 * @return the statsEnabled 1040 */ 1041 public boolean isStatsEnabled() { 1042 return this.stats.isEnabled(); 1043 } 1044 1045 /** 1046 * @param statsEnabled the statsEnabled to set 1047 */ 1048 public void setStatsEnabled(boolean statsEnabled) { 1049 this.stats.setEnabled(statsEnabled); 1050 } 1051 1052 /** 1053 * Returns the {@link DestinationSource} object which can be used to listen to destinations 1054 * being created or destroyed or to enquire about the current destinations available on the broker 1055 * 1056 * @return a lazily created destination source 1057 * @throws JMSException 1058 */ 1059 @Override 1060 public DestinationSource getDestinationSource() throws JMSException { 1061 if (destinationSource == null) { 1062 destinationSource = new DestinationSource(this); 1063 destinationSource.start(); 1064 } 1065 return destinationSource; 1066 } 1067 1068 // Implementation methods 1069 // ------------------------------------------------------------------------- 1070 1071 /** 1072 * Used internally for adding Sessions to the Connection 1073 * 1074 * @param session 1075 * @throws JMSException 1076 * @throws JMSException 1077 */ 1078 protected void addSession(ActiveMQSession session) throws JMSException { 1079 this.sessions.add(session); 1080 if (sessions.size() > 1 || session.isTransacted()) { 1081 optimizedMessageDispatch = false; 1082 } 1083 } 1084 1085 /** 1086 * Used interanlly for removing Sessions from a Connection 1087 * 1088 * @param session 1089 */ 1090 protected void removeSession(ActiveMQSession session) { 1091 this.sessions.remove(session); 1092 this.removeDispatcher(session); 1093 } 1094 1095 /** 1096 * Add a ConnectionConsumer 1097 * 1098 * @param connectionConsumer 1099 * @throws JMSException 1100 */ 1101 protected void addConnectionConsumer(ActiveMQConnectionConsumer connectionConsumer) throws JMSException { 1102 this.connectionConsumers.add(connectionConsumer); 1103 } 1104 1105 /** 1106 * Remove a ConnectionConsumer 1107 * 1108 * @param connectionConsumer 1109 */ 1110 protected void removeConnectionConsumer(ActiveMQConnectionConsumer connectionConsumer) { 1111 this.connectionConsumers.remove(connectionConsumer); 1112 this.removeDispatcher(connectionConsumer); 1113 } 1114 1115 /** 1116 * Creates a <CODE>TopicSession</CODE> object. 1117 * 1118 * @param transacted indicates whether the session is transacted 1119 * @param acknowledgeMode indicates whether the consumer or the client will 1120 * acknowledge any messages it receives; ignored if the 1121 * session is transacted. Legal values are 1122 * <code>Session.AUTO_ACKNOWLEDGE</code>, 1123 * <code>Session.CLIENT_ACKNOWLEDGE</code>, and 1124 * <code>Session.DUPS_OK_ACKNOWLEDGE</code>. 1125 * @return a newly created topic session 1126 * @throws JMSException if the <CODE>TopicConnection</CODE> object fails 1127 * to create a session due to some internal error or lack of 1128 * support for the specific transaction and acknowledgement 1129 * mode. 1130 * @see Session#AUTO_ACKNOWLEDGE 1131 * @see Session#CLIENT_ACKNOWLEDGE 1132 * @see Session#DUPS_OK_ACKNOWLEDGE 1133 */ 1134 @Override 1135 public TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException { 1136 return new ActiveMQTopicSession((ActiveMQSession)createSession(transacted, acknowledgeMode)); 1137 } 1138 1139 /** 1140 * Creates a connection consumer for this connection (optional operation). 1141 * This is an expert facility not used by regular JMS clients. 1142 * 1143 * @param topic the topic to access 1144 * @param messageSelector only messages with properties matching the message 1145 * selector expression are delivered. A value of null or an 1146 * empty string indicates that there is no message selector 1147 * for the message consumer. 1148 * @param sessionPool the server session pool to associate with this 1149 * connection consumer 1150 * @param maxMessages the maximum number of messages that can be assigned to 1151 * a server session at one time 1152 * @return the connection consumer 1153 * @throws JMSException if the <CODE>TopicConnection</CODE> object fails 1154 * to create a connection consumer due to some internal 1155 * error or invalid arguments for <CODE>sessionPool</CODE> 1156 * and <CODE>messageSelector</CODE>. 1157 * @throws javax.jms.InvalidDestinationException if an invalid topic is 1158 * specified. 1159 * @throws javax.jms.InvalidSelectorException if the message selector is 1160 * invalid. 1161 * @see javax.jms.ConnectionConsumer 1162 */ 1163 @Override 1164 public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException { 1165 return createConnectionConsumer(topic, messageSelector, sessionPool, maxMessages, false); 1166 } 1167 1168 /** 1169 * Creates a connection consumer for this connection (optional operation). 1170 * This is an expert facility not used by regular JMS clients. 1171 * 1172 * @param queue the queue to access 1173 * @param messageSelector only messages with properties matching the message 1174 * selector expression are delivered. A value of null or an 1175 * empty string indicates that there is no message selector 1176 * for the message consumer. 1177 * @param sessionPool the server session pool to associate with this 1178 * connection consumer 1179 * @param maxMessages the maximum number of messages that can be assigned to 1180 * a server session at one time 1181 * @return the connection consumer 1182 * @throws JMSException if the <CODE>QueueConnection</CODE> object fails 1183 * to create a connection consumer due to some internal 1184 * error or invalid arguments for <CODE>sessionPool</CODE> 1185 * and <CODE>messageSelector</CODE>. 1186 * @throws javax.jms.InvalidDestinationException if an invalid queue is 1187 * specified. 1188 * @throws javax.jms.InvalidSelectorException if the message selector is 1189 * invalid. 1190 * @see javax.jms.ConnectionConsumer 1191 */ 1192 @Override 1193 public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException { 1194 return createConnectionConsumer(queue, messageSelector, sessionPool, maxMessages, false); 1195 } 1196 1197 /** 1198 * Creates a connection consumer for this connection (optional operation). 1199 * This is an expert facility not used by regular JMS clients. 1200 * 1201 * @param destination the destination to access 1202 * @param messageSelector only messages with properties matching the message 1203 * selector expression are delivered. A value of null or an 1204 * empty string indicates that there is no message selector 1205 * for the message consumer. 1206 * @param sessionPool the server session pool to associate with this 1207 * connection consumer 1208 * @param maxMessages the maximum number of messages that can be assigned to 1209 * a server session at one time 1210 * @return the connection consumer 1211 * @throws JMSException if the <CODE>Connection</CODE> object fails to 1212 * create a connection consumer due to some internal error 1213 * or invalid arguments for <CODE>sessionPool</CODE> and 1214 * <CODE>messageSelector</CODE>. 1215 * @throws javax.jms.InvalidDestinationException if an invalid destination 1216 * is specified. 1217 * @throws javax.jms.InvalidSelectorException if the message selector is 1218 * invalid. 1219 * @see javax.jms.ConnectionConsumer 1220 * @since 1.1 1221 */ 1222 @Override 1223 public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException { 1224 return createConnectionConsumer(destination, messageSelector, sessionPool, maxMessages, false); 1225 } 1226 1227 public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages, boolean noLocal) 1228 throws JMSException { 1229 1230 checkClosedOrFailed(); 1231 ensureConnectionInfoSent(); 1232 1233 ConsumerId consumerId = createConsumerId(); 1234 ConsumerInfo consumerInfo = new ConsumerInfo(consumerId); 1235 consumerInfo.setDestination(ActiveMQMessageTransformation.transformDestination(destination)); 1236 consumerInfo.setSelector(messageSelector); 1237 consumerInfo.setPrefetchSize(maxMessages); 1238 consumerInfo.setNoLocal(noLocal); 1239 consumerInfo.setDispatchAsync(isDispatchAsync()); 1240 1241 // Allows the options on the destination to configure the consumerInfo 1242 if (consumerInfo.getDestination().getOptions() != null) { 1243 Map<String, String> options = new HashMap<>(consumerInfo.getDestination().getOptions()); 1244 IntrospectionSupport.setProperties(consumerInfo, options, "consumer."); 1245 } 1246 1247 return new ActiveMQConnectionConsumer(this, sessionPool, consumerInfo); 1248 } 1249 1250 /** 1251 * @return a newly created ConsumedId unique to this connection session instance. 1252 */ 1253 private ConsumerId createConsumerId() { 1254 return new ConsumerId(connectionSessionId, consumerIdGenerator.getNextSequenceId()); 1255 } 1256 1257 /** 1258 * Creates a <CODE>QueueSession</CODE> object. 1259 * 1260 * @param transacted indicates whether the session is transacted 1261 * @param acknowledgeMode indicates whether the consumer or the client will 1262 * acknowledge any messages it receives; ignored if the 1263 * session is transacted. Legal values are 1264 * <code>Session.AUTO_ACKNOWLEDGE</code>, 1265 * <code>Session.CLIENT_ACKNOWLEDGE</code>, and 1266 * <code>Session.DUPS_OK_ACKNOWLEDGE</code>. 1267 * @return a newly created queue session 1268 * @throws JMSException if the <CODE>QueueConnection</CODE> object fails 1269 * to create a session due to some internal error or lack of 1270 * support for the specific transaction and acknowledgement 1271 * mode. 1272 * @see Session#AUTO_ACKNOWLEDGE 1273 * @see Session#CLIENT_ACKNOWLEDGE 1274 * @see Session#DUPS_OK_ACKNOWLEDGE 1275 */ 1276 @Override 1277 public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException { 1278 return new ActiveMQQueueSession((ActiveMQSession)createSession(transacted, acknowledgeMode)); 1279 } 1280 1281 /** 1282 * Ensures that the clientID was manually specified and not auto-generated. 1283 * If the clientID was not specified this method will throw an exception. 1284 * This method is used to ensure that the clientID + durableSubscriber name 1285 * are used correctly. 1286 * 1287 * @throws JMSException 1288 */ 1289 public void checkClientIDWasManuallySpecified() throws JMSException { 1290 if (!userSpecifiedClientID) { 1291 throw new JMSException("You cannot create a durable subscriber without specifying a unique clientID on a Connection"); 1292 } 1293 } 1294 1295 /** 1296 * send a Packet through the Connection - for internal use only 1297 * 1298 * @param command 1299 * @throws JMSException 1300 */ 1301 public void asyncSendPacket(Command command) throws JMSException { 1302 if (isClosed()) { 1303 throw new ConnectionClosedException(); 1304 } else { 1305 doAsyncSendPacket(command); 1306 } 1307 } 1308 1309 private void doAsyncSendPacket(Command command) throws JMSException { 1310 try { 1311 this.transport.oneway(command); 1312 } catch (IOException e) { 1313 throw JMSExceptionSupport.create(e); 1314 } 1315 } 1316 1317 /** 1318 * Send a packet through a Connection - for internal use only 1319 * 1320 * @param command 1321 * 1322 * @throws JMSException 1323 */ 1324 public void syncSendPacket(final Command command, final AsyncCallback onComplete) throws JMSException { 1325 if(onComplete==null) { 1326 syncSendPacket(command); 1327 } else { 1328 if (isClosed()) { 1329 throw new ConnectionClosedException(); 1330 } 1331 try { 1332 this.transport.asyncRequest(command, new ResponseCallback() { 1333 @Override 1334 public void onCompletion(FutureResponse resp) { 1335 Response response; 1336 Throwable exception = null; 1337 try { 1338 response = resp.getResult(); 1339 if (response.isException()) { 1340 ExceptionResponse er = (ExceptionResponse)response; 1341 exception = er.getException(); 1342 } 1343 } catch (Exception e) { 1344 exception = e; 1345 } 1346 if (exception != null) { 1347 if ( exception instanceof JMSException) { 1348 onComplete.onException((JMSException) exception); 1349 } else { 1350 if (isClosed() || closing.get()) { 1351 LOG.debug("Received an exception but connection is closing"); 1352 } 1353 JMSException jmsEx = null; 1354 try { 1355 jmsEx = JMSExceptionSupport.create(exception); 1356 } catch(Throwable e) { 1357 LOG.error("Caught an exception trying to create a JMSException for " +exception,e); 1358 } 1359 // dispose of transport for security exceptions on connection initiation 1360 if (exception instanceof SecurityException && command instanceof ConnectionInfo){ 1361 try { 1362 forceCloseOnSecurityException(exception); 1363 } catch (Throwable t) { 1364 // We throw the original error from the ExceptionResponse instead. 1365 } 1366 } 1367 if (jmsEx != null) { 1368 onComplete.onException(jmsEx); 1369 } 1370 } 1371 } else { 1372 onComplete.onSuccess(); 1373 } 1374 } 1375 }); 1376 } catch (IOException e) { 1377 throw JMSExceptionSupport.create(e); 1378 } 1379 } 1380 } 1381 1382 private void forceCloseOnSecurityException(Throwable exception) { 1383 LOG.trace("force close on security exception:{}, transport={}", this, transport, exception); 1384 onException(new IOException("Force close due to SecurityException on connect", exception)); 1385 } 1386 1387 public Response syncSendPacket(Command command, int timeout) throws JMSException { 1388 if (isClosed()) { 1389 throw new ConnectionClosedException(); 1390 } else { 1391 try { 1392 Response response = (Response)(timeout > 0 1393 ? this.transport.request(command, timeout) 1394 : this.transport.request(command)); 1395 if (response.isException()) { 1396 ExceptionResponse er = (ExceptionResponse)response; 1397 if (er.getException() instanceof JMSException) { 1398 throw (JMSException)er.getException(); 1399 } else { 1400 if (isClosed() || closing.get()) { 1401 LOG.debug("Received an exception but connection is closing"); 1402 } 1403 JMSException jmsEx = null; 1404 try { 1405 jmsEx = JMSExceptionSupport.create(er.getException()); 1406 } catch(Throwable e) { 1407 LOG.error("Caught an exception trying to create a JMSException for " +er.getException(),e); 1408 } 1409 if (er.getException() instanceof SecurityException && command instanceof ConnectionInfo){ 1410 try { 1411 forceCloseOnSecurityException(er.getException()); 1412 } catch (Throwable t) { 1413 // We throw the original error from the ExceptionResponse instead. 1414 } 1415 } 1416 if (jmsEx != null) { 1417 throw jmsEx; 1418 } 1419 } 1420 } 1421 return response; 1422 } catch (IOException e) { 1423 throw JMSExceptionSupport.create(e); 1424 } 1425 } 1426 } 1427 1428 /** 1429 * Send a packet through a Connection - for internal use only 1430 * 1431 * @param command 1432 * 1433 * @return the broker Response for the given Command. 1434 * 1435 * @throws JMSException 1436 */ 1437 public Response syncSendPacket(Command command) throws JMSException { 1438 return syncSendPacket(command, 0); 1439 } 1440 1441 /** 1442 * @return statistics for this Connection 1443 */ 1444 @Override 1445 public StatsImpl getStats() { 1446 return stats; 1447 } 1448 1449 /** 1450 * simply throws an exception if the Connection is already closed or the 1451 * Transport has failed 1452 * 1453 * @throws JMSException 1454 */ 1455 protected synchronized void checkClosedOrFailed() throws JMSException { 1456 checkClosed(); 1457 if (transportFailed.get()) { 1458 throw new ConnectionFailedException(firstFailureError); 1459 } 1460 } 1461 1462 /** 1463 * simply throws an exception if the Connection is already closed 1464 * 1465 * @throws JMSException 1466 */ 1467 protected synchronized void checkClosed() throws JMSException { 1468 if (closed.get()) { 1469 throw new ConnectionClosedException(); 1470 } 1471 } 1472 1473 /** 1474 * Send the ConnectionInfo to the Broker 1475 * 1476 * @throws JMSException 1477 */ 1478 protected void ensureConnectionInfoSent() throws JMSException { 1479 synchronized(this.ensureConnectionInfoSentMutex) { 1480 // Can we skip sending the ConnectionInfo packet?? 1481 if (isConnectionInfoSentToBroker || closed.get()) { 1482 return; 1483 } 1484 //TODO shouldn't this check be on userSpecifiedClientID rather than the value of clientID? 1485 if (info.getClientId() == null || info.getClientId().trim().length() == 0) { 1486 info.setClientId(clientIdGenerator.generateId()); 1487 } 1488 syncSendPacket(info.copy(), getConnectResponseTimeout()); 1489 1490 this.isConnectionInfoSentToBroker = true; 1491 // Add a temp destination advisory consumer so that 1492 // We know what the valid temporary destinations are on the 1493 // broker without having to do an RPC to the broker. 1494 1495 ConsumerId consumerId = new ConsumerId(new SessionId(info.getConnectionId(), -1), consumerIdGenerator.getNextSequenceId()); 1496 if (watchTopicAdvisories) { 1497 advisoryConsumer = new AdvisoryConsumer(this, consumerId); 1498 } 1499 } 1500 } 1501 1502 public synchronized boolean isWatchTopicAdvisories() { 1503 return watchTopicAdvisories; 1504 } 1505 1506 public synchronized void setWatchTopicAdvisories(boolean watchTopicAdvisories) { 1507 this.watchTopicAdvisories = watchTopicAdvisories; 1508 } 1509 1510 /** 1511 * @return Returns the useAsyncSend. 1512 */ 1513 public boolean isUseAsyncSend() { 1514 return useAsyncSend; 1515 } 1516 1517 /** 1518 * Forces the use of <a 1519 * href="http://activemq.apache.org/async-sends.html">Async Sends</a> which 1520 * adds a massive performance boost; but means that the send() method will 1521 * return immediately whether the message has been sent or not which could 1522 * lead to message loss. 1523 */ 1524 public void setUseAsyncSend(boolean useAsyncSend) { 1525 this.useAsyncSend = useAsyncSend; 1526 } 1527 1528 /** 1529 * @return true if always sync send messages 1530 */ 1531 public boolean isAlwaysSyncSend() { 1532 return this.alwaysSyncSend; 1533 } 1534 1535 /** 1536 * Set true if always require messages to be sync sent 1537 * 1538 * @param alwaysSyncSend 1539 */ 1540 public void setAlwaysSyncSend(boolean alwaysSyncSend) { 1541 this.alwaysSyncSend = alwaysSyncSend; 1542 } 1543 1544 /** 1545 * @return the messagePrioritySupported 1546 */ 1547 public boolean isMessagePrioritySupported() { 1548 return this.messagePrioritySupported; 1549 } 1550 1551 /** 1552 * @param messagePrioritySupported the messagePrioritySupported to set 1553 */ 1554 public void setMessagePrioritySupported(boolean messagePrioritySupported) { 1555 this.messagePrioritySupported = messagePrioritySupported; 1556 } 1557 1558 /** 1559 * Cleans up this connection so that it's state is as if the connection was 1560 * just created. This allows the Resource Adapter to clean up a connection 1561 * so that it can be reused without having to close and recreate the 1562 * connection. 1563 */ 1564 public void cleanup() throws JMSException { 1565 doCleanup(false); 1566 } 1567 1568 public boolean isUserSpecifiedClientID() { 1569 return userSpecifiedClientID; 1570 } 1571 1572 public void doCleanup(boolean removeConnection) throws JMSException { 1573 if (advisoryConsumer != null && !isTransportFailed()) { 1574 advisoryConsumer.dispose(); 1575 advisoryConsumer = null; 1576 } 1577 1578 for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) { 1579 ActiveMQSession s = i.next(); 1580 s.dispose(); 1581 } 1582 for (Iterator<ActiveMQConnectionConsumer> i = this.connectionConsumers.iterator(); i.hasNext();) { 1583 ActiveMQConnectionConsumer c = i.next(); 1584 c.dispose(); 1585 } 1586 1587 if (removeConnection) { 1588 if (isConnectionInfoSentToBroker) { 1589 if (!transportFailed.get() && !closing.get()) { 1590 syncSendPacket(info.createRemoveCommand()); 1591 } 1592 isConnectionInfoSentToBroker = false; 1593 } 1594 if (userSpecifiedClientID) { 1595 info.setClientId(null); 1596 userSpecifiedClientID = false; 1597 } 1598 clientIDSet = false; 1599 } 1600 1601 started.set(false); 1602 } 1603 1604 /** 1605 * Changes the associated username/password that is associated with this 1606 * connection. If the connection has been used, you must called cleanup() 1607 * before calling this method. 1608 * 1609 * @throws IllegalStateException if the connection is in used. 1610 */ 1611 public void changeUserInfo(String userName, String password) throws JMSException { 1612 if (isConnectionInfoSentToBroker) { 1613 throw new IllegalStateException("changeUserInfo used Connection is not allowed"); 1614 } 1615 this.info.setUserName(userName); 1616 this.info.setPassword(password); 1617 } 1618 1619 /** 1620 * @return Returns the resourceManagerId. 1621 * @throws JMSException 1622 */ 1623 public String getResourceManagerId() throws JMSException { 1624 if (isRmIdFromConnectionId()) { 1625 return info.getConnectionId().getValue(); 1626 } 1627 waitForBrokerInfo(); 1628 if (brokerInfo == null) { 1629 throw new JMSException("Connection failed before Broker info was received."); 1630 } 1631 return brokerInfo.getBrokerId().getValue(); 1632 } 1633 1634 /** 1635 * Returns the broker name if one is available or null if one is not 1636 * available yet. 1637 */ 1638 public String getBrokerName() { 1639 try { 1640 brokerInfoReceived.await(5, TimeUnit.SECONDS); 1641 if (brokerInfo == null) { 1642 return null; 1643 } 1644 return brokerInfo.getBrokerName(); 1645 } catch (InterruptedException e) { 1646 Thread.currentThread().interrupt(); 1647 return null; 1648 } 1649 } 1650 1651 /** 1652 * Returns the broker information if it is available or null if it is not 1653 * available yet. 1654 */ 1655 public BrokerInfo getBrokerInfo() { 1656 return brokerInfo; 1657 } 1658 1659 /** 1660 * @return Returns the RedeliveryPolicy. 1661 * @throws JMSException 1662 */ 1663 public RedeliveryPolicy getRedeliveryPolicy() throws JMSException { 1664 return redeliveryPolicyMap.getDefaultEntry(); 1665 } 1666 1667 /** 1668 * Sets the redelivery policy to be used when messages are rolled back 1669 */ 1670 public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) { 1671 this.redeliveryPolicyMap.setDefaultEntry(redeliveryPolicy); 1672 } 1673 1674 public BlobTransferPolicy getBlobTransferPolicy() { 1675 if (blobTransferPolicy == null) { 1676 blobTransferPolicy = createBlobTransferPolicy(); 1677 } 1678 return blobTransferPolicy; 1679 } 1680 1681 /** 1682 * Sets the policy used to describe how out-of-band BLOBs (Binary Large 1683 * OBjects) are transferred from producers to brokers to consumers 1684 */ 1685 public void setBlobTransferPolicy(BlobTransferPolicy blobTransferPolicy) { 1686 this.blobTransferPolicy = blobTransferPolicy; 1687 } 1688 1689 /** 1690 * @return Returns the alwaysSessionAsync. 1691 */ 1692 public boolean isAlwaysSessionAsync() { 1693 return alwaysSessionAsync; 1694 } 1695 1696 /** 1697 * If this flag is not set then a separate thread is not used for dispatching messages for each Session in 1698 * the Connection. However, a separate thread is always used if there is more than one session, or the session 1699 * isn't in auto acknowledge or duplicates ok mode. By default this value is set to true and session dispatch 1700 * happens asynchronously. 1701 */ 1702 public void setAlwaysSessionAsync(boolean alwaysSessionAsync) { 1703 this.alwaysSessionAsync = alwaysSessionAsync; 1704 } 1705 1706 /** 1707 * @return Returns the optimizeAcknowledge. 1708 */ 1709 public boolean isOptimizeAcknowledge() { 1710 return optimizeAcknowledge; 1711 } 1712 1713 /** 1714 * Enables an optimised acknowledgement mode where messages are acknowledged 1715 * in batches rather than individually 1716 * 1717 * @param optimizeAcknowledge The optimizeAcknowledge to set. 1718 */ 1719 public void setOptimizeAcknowledge(boolean optimizeAcknowledge) { 1720 this.optimizeAcknowledge = optimizeAcknowledge; 1721 } 1722 1723 /** 1724 * The max time in milliseconds between optimized ack batches 1725 * @param optimizeAcknowledgeTimeOut 1726 */ 1727 public void setOptimizeAcknowledgeTimeOut(long optimizeAcknowledgeTimeOut) { 1728 this.optimizeAcknowledgeTimeOut = optimizeAcknowledgeTimeOut; 1729 } 1730 1731 public long getOptimizeAcknowledgeTimeOut() { 1732 return optimizeAcknowledgeTimeOut; 1733 } 1734 1735 public long getWarnAboutUnstartedConnectionTimeout() { 1736 return warnAboutUnstartedConnectionTimeout; 1737 } 1738 1739 /** 1740 * Enables the timeout from a connection creation to when a warning is 1741 * generated if the connection is not properly started via {@link #start()} 1742 * and a message is received by a consumer. It is a very common gotcha to 1743 * forget to <a 1744 * href="http://activemq.apache.org/i-am-not-receiving-any-messages-what-is-wrong.html">start 1745 * the connection</a> so this option makes the default case to create a 1746 * warning if the user forgets. To disable the warning just set the value to < 1747 * 0 (say -1). 1748 */ 1749 public void setWarnAboutUnstartedConnectionTimeout(long warnAboutUnstartedConnectionTimeout) { 1750 this.warnAboutUnstartedConnectionTimeout = warnAboutUnstartedConnectionTimeout; 1751 } 1752 1753 /** 1754 * @return the sendTimeout (in milliseconds) 1755 */ 1756 public int getSendTimeout() { 1757 return sendTimeout; 1758 } 1759 1760 /** 1761 * @param sendTimeout the sendTimeout to set (in milliseconds) 1762 */ 1763 public void setSendTimeout(int sendTimeout) { 1764 this.sendTimeout = sendTimeout; 1765 } 1766 1767 /** 1768 * @return the sendAcksAsync 1769 */ 1770 public boolean isSendAcksAsync() { 1771 return sendAcksAsync; 1772 } 1773 1774 /** 1775 * @param sendAcksAsync the sendAcksAsync to set 1776 */ 1777 public void setSendAcksAsync(boolean sendAcksAsync) { 1778 this.sendAcksAsync = sendAcksAsync; 1779 } 1780 1781 /** 1782 * Returns the time this connection was created 1783 */ 1784 public long getTimeCreated() { 1785 return timeCreated; 1786 } 1787 1788 private void waitForBrokerInfo() throws JMSException { 1789 try { 1790 brokerInfoReceived.await(); 1791 } catch (InterruptedException e) { 1792 Thread.currentThread().interrupt(); 1793 throw JMSExceptionSupport.create(e); 1794 } 1795 } 1796 1797 // Package protected so that it can be used in unit tests 1798 public Transport getTransport() { 1799 return transport; 1800 } 1801 1802 public void addProducer(ProducerId producerId, ActiveMQMessageProducer producer) { 1803 producers.put(producerId, producer); 1804 } 1805 1806 public void removeProducer(ProducerId producerId) { 1807 producers.remove(producerId); 1808 } 1809 1810 public void addDispatcher(ConsumerId consumerId, ActiveMQDispatcher dispatcher) { 1811 dispatchers.put(consumerId, dispatcher); 1812 } 1813 1814 public void removeDispatcher(ConsumerId consumerId) { 1815 dispatchers.remove(consumerId); 1816 } 1817 1818 public boolean hasDispatcher(ConsumerId consumerId) { 1819 return dispatchers.containsKey(consumerId); 1820 } 1821 1822 /** 1823 * @param o - the command to consume 1824 */ 1825 @Override 1826 public void onCommand(final Object o) { 1827 final Command command = (Command)o; 1828 if (!closed.get() && command != null) { 1829 try { 1830 command.visit(new CommandVisitorAdapter() { 1831 @Override 1832 public Response processMessageDispatch(MessageDispatch md) throws Exception { 1833 waitForTransportInterruptionProcessingToComplete(); 1834 ActiveMQDispatcher dispatcher = dispatchers.get(md.getConsumerId()); 1835 if (dispatcher != null) { 1836 // Copy in case a embedded broker is dispatching via 1837 // vm:// 1838 // md.getMessage() == null to signal end of queue 1839 // browse. 1840 Message msg = md.getMessage(); 1841 if (msg != null) { 1842 msg = msg.copy(); 1843 msg.setReadOnlyBody(true); 1844 msg.setReadOnlyProperties(true); 1845 msg.setRedeliveryCounter(md.getRedeliveryCounter()); 1846 msg.setConnection(ActiveMQConnection.this); 1847 msg.setMemoryUsage(null); 1848 md.setMessage(msg); 1849 } 1850 dispatcher.dispatch(md); 1851 } else { 1852 LOG.debug("{} no dispatcher for {} in {}", this, md, dispatchers); 1853 } 1854 return null; 1855 } 1856 1857 @Override 1858 public Response processProducerAck(ProducerAck pa) throws Exception { 1859 if (pa != null && pa.getProducerId() != null) { 1860 ActiveMQMessageProducer producer = producers.get(pa.getProducerId()); 1861 if (producer != null) { 1862 producer.onProducerAck(pa); 1863 } 1864 } 1865 return null; 1866 } 1867 1868 @Override 1869 public Response processBrokerInfo(BrokerInfo info) throws Exception { 1870 brokerInfo = info; 1871 brokerInfoReceived.countDown(); 1872 optimizeAcknowledge &= !brokerInfo.isFaultTolerantConfiguration(); 1873 getBlobTransferPolicy().setBrokerUploadUrl(info.getBrokerUploadUrl()); 1874 return null; 1875 } 1876 1877 @Override 1878 public Response processConnectionError(final ConnectionError error) throws Exception { 1879 executor.execute(new Runnable() { 1880 @Override 1881 public void run() { 1882 onAsyncException(error.getException()); 1883 } 1884 }); 1885 return null; 1886 } 1887 1888 @Override 1889 public Response processControlCommand(ControlCommand command) throws Exception { 1890 return null; 1891 } 1892 1893 @Override 1894 public Response processConnectionControl(ConnectionControl control) throws Exception { 1895 onConnectionControl((ConnectionControl)command); 1896 return null; 1897 } 1898 1899 @Override 1900 public Response processConsumerControl(ConsumerControl control) throws Exception { 1901 onConsumerControl((ConsumerControl)command); 1902 return null; 1903 } 1904 1905 @Override 1906 public Response processWireFormat(WireFormatInfo info) throws Exception { 1907 onWireFormatInfo((WireFormatInfo)command); 1908 return null; 1909 } 1910 }); 1911 } catch (Exception e) { 1912 onClientInternalException(e); 1913 } 1914 } 1915 1916 for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) { 1917 TransportListener listener = iter.next(); 1918 listener.onCommand(command); 1919 } 1920 } 1921 1922 protected void onWireFormatInfo(WireFormatInfo info) { 1923 protocolVersion.set(info.getVersion()); 1924 1925 long tmpMaxFrameSize = 0; 1926 try { 1927 tmpMaxFrameSize = info.getMaxFrameSize(); 1928 } catch (IOException e) { 1929 // unmarshal error on property map 1930 } 1931 1932 if(tmpMaxFrameSize > 0) { 1933 maxFrameSize.set(tmpMaxFrameSize); 1934 } 1935 } 1936 1937 /** 1938 * Handles async client internal exceptions. 1939 * A client internal exception is usually one that has been thrown 1940 * by a container runtime component during asynchronous processing of a 1941 * message that does not affect the connection itself. 1942 * This method notifies the <code>ClientInternalExceptionListener</code> by invoking 1943 * its <code>onException</code> method, if one has been registered with this connection. 1944 * 1945 * @param error the exception that the problem 1946 */ 1947 public void onClientInternalException(final Throwable error) { 1948 if ( !closed.get() && !closing.get() ) { 1949 if ( this.clientInternalExceptionListener != null ) { 1950 executor.execute(new Runnable() { 1951 @Override 1952 public void run() { 1953 ActiveMQConnection.this.clientInternalExceptionListener.onException(error); 1954 } 1955 }); 1956 } else { 1957 LOG.debug("Async client internal exception occurred with no exception listener registered: {}", 1958 error, error); 1959 } 1960 } 1961 } 1962 1963 /** 1964 * Used for handling async exceptions 1965 * 1966 * @param error 1967 */ 1968 public void onAsyncException(Throwable error) { 1969 if (!closed.get() && !closing.get()) { 1970 if (this.exceptionListener != null) { 1971 1972 if (!(error instanceof JMSException)) { 1973 error = JMSExceptionSupport.create(error); 1974 } 1975 final JMSException e = (JMSException)error; 1976 1977 executor.execute(new Runnable() { 1978 @Override 1979 public void run() { 1980 ActiveMQConnection.this.exceptionListener.onException(e); 1981 } 1982 }); 1983 1984 } else { 1985 LOG.debug("Async exception with no exception listener: {}", error, error); 1986 } 1987 } 1988 } 1989 1990 @Override 1991 public void onException(final IOException error) { 1992 onAsyncException(error); 1993 if (!closed.get() && !closing.get()) { 1994 executor.execute(new Runnable() { 1995 @Override 1996 public void run() { 1997 transportFailed(error); 1998 ServiceSupport.dispose(ActiveMQConnection.this.transport); 1999 brokerInfoReceived.countDown(); 2000 try { 2001 doCleanup(true); 2002 } catch (JMSException e) { 2003 LOG.warn("Exception during connection cleanup, " + e, e); 2004 } 2005 for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) { 2006 TransportListener listener = iter.next(); 2007 listener.onException(error); 2008 } 2009 } 2010 }); 2011 } 2012 } 2013 2014 @Override 2015 public void transportInterupted() { 2016 transportInterruptionProcessingComplete.set(1); 2017 for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) { 2018 ActiveMQSession s = i.next(); 2019 s.clearMessagesInProgress(transportInterruptionProcessingComplete); 2020 } 2021 2022 for (ActiveMQConnectionConsumer connectionConsumer : this.connectionConsumers) { 2023 connectionConsumer.clearMessagesInProgress(transportInterruptionProcessingComplete); 2024 } 2025 2026 if (transportInterruptionProcessingComplete.decrementAndGet() > 0) { 2027 if (LOG.isDebugEnabled()) { 2028 LOG.debug("transport interrupted - processing required, dispatchers: " + transportInterruptionProcessingComplete.get()); 2029 } 2030 signalInterruptionProcessingNeeded(); 2031 } 2032 2033 for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) { 2034 TransportListener listener = iter.next(); 2035 listener.transportInterupted(); 2036 } 2037 } 2038 2039 @Override 2040 public void transportResumed() { 2041 for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) { 2042 TransportListener listener = iter.next(); 2043 listener.transportResumed(); 2044 } 2045 } 2046 2047 /** 2048 * Create the DestinationInfo object for the temporary destination. 2049 * 2050 * @param topic - if its true topic, else queue. 2051 * @return DestinationInfo 2052 * @throws JMSException 2053 */ 2054 protected ActiveMQTempDestination createTempDestination(boolean topic) throws JMSException { 2055 2056 // Check if Destination info is of temporary type. 2057 ActiveMQTempDestination dest; 2058 if (topic) { 2059 dest = new ActiveMQTempTopic(info.getConnectionId(), tempDestinationIdGenerator.getNextSequenceId()); 2060 } else { 2061 dest = new ActiveMQTempQueue(info.getConnectionId(), tempDestinationIdGenerator.getNextSequenceId()); 2062 } 2063 2064 DestinationInfo info = new DestinationInfo(); 2065 info.setConnectionId(this.info.getConnectionId()); 2066 info.setOperationType(DestinationInfo.ADD_OPERATION_TYPE); 2067 info.setDestination(dest); 2068 syncSendPacket(info); 2069 2070 dest.setConnection(this); 2071 activeTempDestinations.put(dest, dest); 2072 return dest; 2073 } 2074 2075 /** 2076 * @param destination 2077 * @throws JMSException 2078 */ 2079 public void deleteTempDestination(ActiveMQTempDestination destination) throws JMSException { 2080 2081 checkClosedOrFailed(); 2082 2083 for (ActiveMQSession session : this.sessions) { 2084 if (session.isInUse(destination)) { 2085 throw new JMSException("A consumer is consuming from the temporary destination"); 2086 } 2087 } 2088 2089 activeTempDestinations.remove(destination); 2090 2091 DestinationInfo destInfo = new DestinationInfo(); 2092 destInfo.setConnectionId(this.info.getConnectionId()); 2093 destInfo.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); 2094 destInfo.setDestination(destination); 2095 destInfo.setTimeout(0); 2096 syncSendPacket(destInfo); 2097 } 2098 2099 public boolean isDeleted(ActiveMQDestination dest) { 2100 2101 // If we are not watching the advisories.. then 2102 // we will assume that the temp destination does exist. 2103 if (advisoryConsumer == null) { 2104 return false; 2105 } 2106 2107 return !activeTempDestinations.containsValue(dest); 2108 } 2109 2110 public boolean isCopyMessageOnSend() { 2111 return copyMessageOnSend; 2112 } 2113 2114 public LongSequenceGenerator getLocalTransactionIdGenerator() { 2115 return localTransactionIdGenerator; 2116 } 2117 2118 public boolean isUseCompression() { 2119 return useCompression; 2120 } 2121 2122 /** 2123 * Enables the use of compression of the message bodies 2124 */ 2125 public void setUseCompression(boolean useCompression) { 2126 this.useCompression = useCompression; 2127 } 2128 2129 public void destroyDestination(ActiveMQDestination destination) throws JMSException { 2130 2131 checkClosedOrFailed(); 2132 ensureConnectionInfoSent(); 2133 2134 DestinationInfo info = new DestinationInfo(); 2135 info.setConnectionId(this.info.getConnectionId()); 2136 info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); 2137 info.setDestination(destination); 2138 info.setTimeout(0); 2139 syncSendPacket(info); 2140 } 2141 2142 public boolean isDispatchAsync() { 2143 return dispatchAsync; 2144 } 2145 2146 /** 2147 * Enables or disables the default setting of whether or not consumers have 2148 * their messages <a 2149 * href="http://activemq.apache.org/consumer-dispatch-async.html">dispatched 2150 * synchronously or asynchronously by the broker</a>. For non-durable 2151 * topics for example we typically dispatch synchronously by default to 2152 * minimize context switches which boost performance. However sometimes its 2153 * better to go slower to ensure that a single blocked consumer socket does 2154 * not block delivery to other consumers. 2155 * 2156 * @param asyncDispatch If true then consumers created on this connection 2157 * will default to having their messages dispatched 2158 * asynchronously. The default value is true. 2159 */ 2160 public void setDispatchAsync(boolean asyncDispatch) { 2161 this.dispatchAsync = asyncDispatch; 2162 } 2163 2164 public boolean isObjectMessageSerializationDefered() { 2165 return objectMessageSerializationDefered; 2166 } 2167 2168 /** 2169 * When an object is set on an ObjectMessage, the JMS spec requires the 2170 * object to be serialized by that set method. Enabling this flag causes the 2171 * object to not get serialized. The object may subsequently get serialized 2172 * if the message needs to be sent over a socket or stored to disk. 2173 */ 2174 public void setObjectMessageSerializationDefered(boolean objectMessageSerializationDefered) { 2175 this.objectMessageSerializationDefered = objectMessageSerializationDefered; 2176 } 2177 2178 /** 2179 * Unsubscribes a durable subscription that has been created by a client. 2180 * <P> 2181 * This method deletes the state being maintained on behalf of the 2182 * subscriber by its provider. 2183 * <P> 2184 * It is erroneous for a client to delete a durable subscription while there 2185 * is an active <CODE>MessageConsumer </CODE> or 2186 * <CODE>TopicSubscriber</CODE> for the subscription, or while a consumed 2187 * message is part of a pending transaction or has not been acknowledged in 2188 * the session. 2189 * 2190 * @param name the name used to identify this subscription 2191 * @throws JMSException if the session fails to unsubscribe to the durable 2192 * subscription due to some internal error. 2193 * @throws InvalidDestinationException if an invalid subscription name is 2194 * specified. 2195 * @since 1.1 2196 */ 2197 public void unsubscribe(String name) throws InvalidDestinationException, JMSException { 2198 checkClosedOrFailed(); 2199 RemoveSubscriptionInfo rsi = new RemoveSubscriptionInfo(); 2200 rsi.setConnectionId(getConnectionInfo().getConnectionId()); 2201 rsi.setSubscriptionName(name); 2202 rsi.setClientId(getConnectionInfo().getClientId()); 2203 syncSendPacket(rsi); 2204 } 2205 2206 /** 2207 * Internal send method optimized: - It does not copy the message - It can 2208 * only handle ActiveMQ messages. - You can specify if the send is async or 2209 * sync - Does not allow you to send /w a transaction. 2210 */ 2211 void send(ActiveMQDestination destination, ActiveMQMessage msg, MessageId messageId, int deliveryMode, int priority, long timeToLive, boolean async) throws JMSException { 2212 checkClosedOrFailed(); 2213 2214 if (destination.isTemporary() && isDeleted(destination)) { 2215 throw new JMSException("Cannot publish to a deleted Destination: " + destination); 2216 } 2217 2218 msg.setJMSDestination(destination); 2219 msg.setJMSDeliveryMode(deliveryMode); 2220 long expiration = 0L; 2221 2222 if (!isDisableTimeStampsByDefault()) { 2223 long timeStamp = System.currentTimeMillis(); 2224 msg.setJMSTimestamp(timeStamp); 2225 if (timeToLive > 0) { 2226 expiration = timeToLive + timeStamp; 2227 } 2228 } 2229 2230 msg.setJMSExpiration(expiration); 2231 msg.setJMSPriority(priority); 2232 msg.setJMSRedelivered(false); 2233 msg.setMessageId(messageId); 2234 msg.onSend(); 2235 msg.setProducerId(msg.getMessageId().getProducerId()); 2236 2237 if (LOG.isDebugEnabled()) { 2238 LOG.debug("Sending message: " + msg); 2239 } 2240 2241 if (async) { 2242 asyncSendPacket(msg); 2243 } else { 2244 syncSendPacket(msg); 2245 } 2246 } 2247 2248 protected void onConnectionControl(ConnectionControl command) { 2249 if (command.isFaultTolerant()) { 2250 this.optimizeAcknowledge = false; 2251 for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) { 2252 ActiveMQSession s = i.next(); 2253 s.setOptimizeAcknowledge(false); 2254 } 2255 } 2256 } 2257 2258 protected void onConsumerControl(ConsumerControl command) { 2259 if (command.isClose()) { 2260 for (ActiveMQSession session : this.sessions) { 2261 session.close(command.getConsumerId()); 2262 } 2263 } else { 2264 for (ActiveMQSession session : this.sessions) { 2265 session.setPrefetchSize(command.getConsumerId(), command.getPrefetch()); 2266 } 2267 for (ActiveMQConnectionConsumer connectionConsumer: connectionConsumers) { 2268 ConsumerInfo consumerInfo = connectionConsumer.getConsumerInfo(); 2269 if (consumerInfo.getConsumerId().equals(command.getConsumerId())) { 2270 consumerInfo.setPrefetchSize(command.getPrefetch()); 2271 } 2272 } 2273 } 2274 } 2275 2276 protected void transportFailed(IOException error) { 2277 transportFailed.set(true); 2278 if (firstFailureError == null) { 2279 firstFailureError = error; 2280 } 2281 } 2282 2283 /** 2284 * Should a JMS message be copied to a new JMS Message object as part of the 2285 * send() method in JMS. This is enabled by default to be compliant with the 2286 * JMS specification. You can disable it if you do not mutate JMS messages 2287 * after they are sent for a performance boost 2288 */ 2289 public void setCopyMessageOnSend(boolean copyMessageOnSend) { 2290 this.copyMessageOnSend = copyMessageOnSend; 2291 } 2292 2293 @Override 2294 public String toString() { 2295 return "ActiveMQConnection {id=" + info.getConnectionId() + ",clientId=" + info.getClientId() + ",started=" + started.get() + "}"; 2296 } 2297 2298 protected BlobTransferPolicy createBlobTransferPolicy() { 2299 return new BlobTransferPolicy(); 2300 } 2301 2302 public int getProtocolVersion() { 2303 return protocolVersion.get(); 2304 } 2305 2306 public int getProducerWindowSize() { 2307 return producerWindowSize; 2308 } 2309 2310 public void setProducerWindowSize(int producerWindowSize) { 2311 this.producerWindowSize = producerWindowSize; 2312 } 2313 2314 public void setAuditDepth(int auditDepth) { 2315 connectionAudit.setAuditDepth(auditDepth); 2316 } 2317 2318 public void setAuditMaximumProducerNumber(int auditMaximumProducerNumber) { 2319 connectionAudit.setAuditMaximumProducerNumber(auditMaximumProducerNumber); 2320 } 2321 2322 protected void removeDispatcher(ActiveMQDispatcher dispatcher) { 2323 connectionAudit.removeDispatcher(dispatcher); 2324 } 2325 2326 protected boolean isDuplicate(ActiveMQDispatcher dispatcher, Message message) { 2327 return checkForDuplicates && connectionAudit.isDuplicate(dispatcher, message); 2328 } 2329 2330 protected void rollbackDuplicate(ActiveMQDispatcher dispatcher, Message message) { 2331 connectionAudit.rollbackDuplicate(dispatcher, message); 2332 } 2333 2334 public IOException getFirstFailureError() { 2335 return firstFailureError; 2336 } 2337 2338 protected void waitForTransportInterruptionProcessingToComplete() throws InterruptedException { 2339 if (!closed.get() && !transportFailed.get() && transportInterruptionProcessingComplete.get()>0) { 2340 LOG.warn("dispatch with outstanding dispatch interruption processing count " + transportInterruptionProcessingComplete.get()); 2341 signalInterruptionProcessingComplete(); 2342 } 2343 } 2344 2345 protected void transportInterruptionProcessingComplete() { 2346 if (transportInterruptionProcessingComplete.decrementAndGet() == 0) { 2347 signalInterruptionProcessingComplete(); 2348 } 2349 } 2350 2351 private void signalInterruptionProcessingComplete() { 2352 if (LOG.isDebugEnabled()) { 2353 LOG.debug("transportInterruptionProcessingComplete: " + transportInterruptionProcessingComplete.get() 2354 + " for:" + this.getConnectionInfo().getConnectionId()); 2355 } 2356 2357 FailoverTransport failoverTransport = transport.narrow(FailoverTransport.class); 2358 if (failoverTransport != null) { 2359 failoverTransport.connectionInterruptProcessingComplete(this.getConnectionInfo().getConnectionId()); 2360 if (LOG.isDebugEnabled()) { 2361 LOG.debug("notified failover transport (" + failoverTransport 2362 + ") of interruption completion for: " + this.getConnectionInfo().getConnectionId()); 2363 } 2364 } 2365 transportInterruptionProcessingComplete.set(0); 2366 } 2367 2368 private void signalInterruptionProcessingNeeded() { 2369 FailoverTransport failoverTransport = transport.narrow(FailoverTransport.class); 2370 if (failoverTransport != null) { 2371 failoverTransport.getStateTracker().transportInterrupted(this.getConnectionInfo().getConnectionId()); 2372 if (LOG.isDebugEnabled()) { 2373 LOG.debug("notified failover transport (" + failoverTransport 2374 + ") of pending interruption processing for: " + this.getConnectionInfo().getConnectionId()); 2375 } 2376 } 2377 } 2378 2379 /* 2380 * specify the amount of time in milliseconds that a consumer with a transaction pending recovery 2381 * will wait to receive re dispatched messages. 2382 * default value is 0 so there is no wait by default. 2383 */ 2384 public void setConsumerFailoverRedeliveryWaitPeriod(long consumerFailoverRedeliveryWaitPeriod) { 2385 this.consumerFailoverRedeliveryWaitPeriod = consumerFailoverRedeliveryWaitPeriod; 2386 } 2387 2388 public long getConsumerFailoverRedeliveryWaitPeriod() { 2389 return consumerFailoverRedeliveryWaitPeriod; 2390 } 2391 2392 protected Scheduler getScheduler() throws JMSException { 2393 Scheduler result = scheduler; 2394 if (result == null) { 2395 if (isClosing() || isClosed()) { 2396 // without lock contention report the closing state 2397 throw new ConnectionClosedException(); 2398 } 2399 synchronized (schedulerLock) { 2400 result = scheduler; 2401 if (result == null) { 2402 checkClosed(); 2403 try { 2404 result = new Scheduler("ActiveMQConnection["+info.getConnectionId().getValue()+"] Scheduler"); 2405 result.start(); 2406 scheduler = result; 2407 } catch(Exception e) { 2408 throw JMSExceptionSupport.create(e); 2409 } 2410 } 2411 } 2412 } 2413 return result; 2414 } 2415 2416 protected ThreadPoolExecutor getExecutor() { 2417 return this.executor; 2418 } 2419 2420 protected CopyOnWriteArrayList<ActiveMQSession> getSessions() { 2421 return sessions; 2422 } 2423 2424 /** 2425 * @return the checkForDuplicates 2426 */ 2427 public boolean isCheckForDuplicates() { 2428 return this.checkForDuplicates; 2429 } 2430 2431 /** 2432 * @param checkForDuplicates the checkForDuplicates to set 2433 */ 2434 public void setCheckForDuplicates(boolean checkForDuplicates) { 2435 this.checkForDuplicates = checkForDuplicates; 2436 } 2437 2438 public boolean isTransactedIndividualAck() { 2439 return transactedIndividualAck; 2440 } 2441 2442 public void setTransactedIndividualAck(boolean transactedIndividualAck) { 2443 this.transactedIndividualAck = transactedIndividualAck; 2444 } 2445 2446 public boolean isNonBlockingRedelivery() { 2447 return nonBlockingRedelivery; 2448 } 2449 2450 public void setNonBlockingRedelivery(boolean nonBlockingRedelivery) { 2451 this.nonBlockingRedelivery = nonBlockingRedelivery; 2452 } 2453 2454 public boolean isRmIdFromConnectionId() { 2455 return rmIdFromConnectionId; 2456 } 2457 2458 public void setRmIdFromConnectionId(boolean rmIdFromConnectionId) { 2459 this.rmIdFromConnectionId = rmIdFromConnectionId; 2460 } 2461 2462 /** 2463 * Removes any TempDestinations that this connection has cached, ignoring 2464 * any exceptions generated because the destination is in use as they should 2465 * not be removed. 2466 * Used from a pooled connection, b/c it will not be explicitly closed. 2467 */ 2468 public void cleanUpTempDestinations() { 2469 2470 if (this.activeTempDestinations == null || this.activeTempDestinations.isEmpty()) { 2471 return; 2472 } 2473 2474 Iterator<ConcurrentMap.Entry<ActiveMQTempDestination, ActiveMQTempDestination>> entries 2475 = this.activeTempDestinations.entrySet().iterator(); 2476 while(entries.hasNext()) { 2477 ConcurrentMap.Entry<ActiveMQTempDestination, ActiveMQTempDestination> entry = entries.next(); 2478 try { 2479 // Only delete this temp destination if it was created from this connection. The connection used 2480 // for the advisory consumer may also have a reference to this temp destination. 2481 ActiveMQTempDestination dest = entry.getValue(); 2482 String thisConnectionId = (info.getConnectionId() == null) ? "" : info.getConnectionId().toString(); 2483 if (dest.getConnectionId() != null && dest.getConnectionId().equals(thisConnectionId)) { 2484 this.deleteTempDestination(entry.getValue()); 2485 } 2486 } catch (Exception ex) { 2487 // the temp dest is in use so it can not be deleted. 2488 // it is ok to leave it to connection tear down phase 2489 } 2490 } 2491 } 2492 2493 /** 2494 * Sets the Connection wide RedeliveryPolicyMap for handling messages that are being rolled back. 2495 * @param redeliveryPolicyMap the redeliveryPolicyMap to set 2496 */ 2497 public void setRedeliveryPolicyMap(RedeliveryPolicyMap redeliveryPolicyMap) { 2498 this.redeliveryPolicyMap = redeliveryPolicyMap; 2499 } 2500 2501 /** 2502 * Gets the Connection's configured RedeliveryPolicyMap which will be used by all the 2503 * Consumers when dealing with transaction messages that have been rolled back. 2504 * 2505 * @return the redeliveryPolicyMap 2506 */ 2507 public RedeliveryPolicyMap getRedeliveryPolicyMap() { 2508 return redeliveryPolicyMap; 2509 } 2510 2511 public int getMaxThreadPoolSize() { 2512 return maxThreadPoolSize; 2513 } 2514 2515 public void setMaxThreadPoolSize(int maxThreadPoolSize) { 2516 this.maxThreadPoolSize = maxThreadPoolSize; 2517 } 2518 2519 /** 2520 * Enable enforcement of QueueConnection semantics. 2521 * 2522 * @return this object, useful for chaining 2523 */ 2524 ActiveMQConnection enforceQueueOnlyConnection() { 2525 this.queueOnlyConnection = true; 2526 return this; 2527 } 2528 2529 public RejectedExecutionHandler getRejectedTaskHandler() { 2530 return rejectedTaskHandler; 2531 } 2532 2533 public void setRejectedTaskHandler(RejectedExecutionHandler rejectedTaskHandler) { 2534 this.rejectedTaskHandler = rejectedTaskHandler; 2535 } 2536 2537 /** 2538 * Gets the configured time interval that is used to force all MessageConsumers that have optimizedAcknowledge enabled 2539 * to send an ack for any outstanding Message Acks. By default this value is set to zero meaning that the consumers 2540 * will not do any background Message acknowledgment. 2541 * 2542 * @return the scheduledOptimizedAckInterval 2543 */ 2544 public long getOptimizedAckScheduledAckInterval() { 2545 return optimizedAckScheduledAckInterval; 2546 } 2547 2548 /** 2549 * Sets the amount of time between scheduled sends of any outstanding Message Acks for consumers that 2550 * have been configured with optimizeAcknowledge enabled. 2551 * 2552 * @param optimizedAckScheduledAckInterval the scheduledOptimizedAckInterval to set 2553 */ 2554 public void setOptimizedAckScheduledAckInterval(long optimizedAckScheduledAckInterval) { 2555 this.optimizedAckScheduledAckInterval = optimizedAckScheduledAckInterval; 2556 } 2557 2558 /** 2559 * @return true if MessageConsumer instance will check for expired messages before dispatch. 2560 */ 2561 public boolean isConsumerExpiryCheckEnabled() { 2562 return consumerExpiryCheckEnabled; 2563 } 2564 2565 /** 2566 * Controls whether message expiration checking is done in each MessageConsumer 2567 * prior to dispatching a message. Disabling this check can lead to consumption 2568 * of expired messages. 2569 * 2570 * @param consumerExpiryCheckEnabled 2571 * controls whether expiration checking is done prior to dispatch. 2572 */ 2573 public void setConsumerExpiryCheckEnabled(boolean consumerExpiryCheckEnabled) { 2574 this.consumerExpiryCheckEnabled = consumerExpiryCheckEnabled; 2575 } 2576 2577 public List<String> getTrustedPackages() { 2578 return trustedPackages; 2579 } 2580 2581 public void setTrustedPackages(List<String> trustedPackages) { 2582 this.trustedPackages = trustedPackages; 2583 } 2584 2585 public boolean isTrustAllPackages() { 2586 return trustAllPackages; 2587 } 2588 2589 public void setTrustAllPackages(boolean trustAllPackages) { 2590 this.trustAllPackages = trustAllPackages; 2591 } 2592 2593 public int getConnectResponseTimeout() { 2594 return connectResponseTimeout; 2595 } 2596 2597 public void setConnectResponseTimeout(int connectResponseTimeout) { 2598 this.connectResponseTimeout = connectResponseTimeout; 2599 } 2600}