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