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