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.File; 020import java.io.InputStream; 021import java.io.Serializable; 022import java.net.URL; 023import java.util.Collections; 024import java.util.Iterator; 025import java.util.List; 026import java.util.concurrent.CopyOnWriteArrayList; 027import java.util.concurrent.ThreadPoolExecutor; 028import java.util.concurrent.atomic.AtomicBoolean; 029import java.util.concurrent.atomic.AtomicInteger; 030 031import javax.jms.BytesMessage; 032import javax.jms.Destination; 033import javax.jms.IllegalStateException; 034import javax.jms.InvalidDestinationException; 035import javax.jms.InvalidSelectorException; 036import javax.jms.JMSException; 037import javax.jms.MapMessage; 038import javax.jms.Message; 039import javax.jms.MessageConsumer; 040import javax.jms.MessageListener; 041import javax.jms.MessageProducer; 042import javax.jms.ObjectMessage; 043import javax.jms.Queue; 044import javax.jms.QueueBrowser; 045import javax.jms.QueueReceiver; 046import javax.jms.QueueSender; 047import javax.jms.QueueSession; 048import javax.jms.Session; 049import javax.jms.StreamMessage; 050import javax.jms.TemporaryQueue; 051import javax.jms.TemporaryTopic; 052import javax.jms.TextMessage; 053import javax.jms.Topic; 054import javax.jms.TopicPublisher; 055import javax.jms.TopicSession; 056import javax.jms.TopicSubscriber; 057import javax.jms.TransactionRolledBackException; 058 059import org.apache.activemq.blob.BlobDownloader; 060import org.apache.activemq.blob.BlobTransferPolicy; 061import org.apache.activemq.blob.BlobUploader; 062import org.apache.activemq.command.ActiveMQBlobMessage; 063import org.apache.activemq.command.ActiveMQBytesMessage; 064import org.apache.activemq.command.ActiveMQDestination; 065import org.apache.activemq.command.ActiveMQMapMessage; 066import org.apache.activemq.command.ActiveMQMessage; 067import org.apache.activemq.command.ActiveMQObjectMessage; 068import org.apache.activemq.command.ActiveMQQueue; 069import org.apache.activemq.command.ActiveMQStreamMessage; 070import org.apache.activemq.command.ActiveMQTempDestination; 071import org.apache.activemq.command.ActiveMQTempQueue; 072import org.apache.activemq.command.ActiveMQTempTopic; 073import org.apache.activemq.command.ActiveMQTextMessage; 074import org.apache.activemq.command.ActiveMQTopic; 075import org.apache.activemq.command.Command; 076import org.apache.activemq.command.ConsumerId; 077import org.apache.activemq.command.MessageAck; 078import org.apache.activemq.command.MessageDispatch; 079import org.apache.activemq.command.MessageId; 080import org.apache.activemq.command.ProducerId; 081import org.apache.activemq.command.RemoveInfo; 082import org.apache.activemq.command.Response; 083import org.apache.activemq.command.SessionId; 084import org.apache.activemq.command.SessionInfo; 085import org.apache.activemq.command.TransactionId; 086import org.apache.activemq.management.JMSSessionStatsImpl; 087import org.apache.activemq.management.StatsCapable; 088import org.apache.activemq.management.StatsImpl; 089import org.apache.activemq.thread.Scheduler; 090import org.apache.activemq.transaction.Synchronization; 091import org.apache.activemq.usage.MemoryUsage; 092import org.apache.activemq.util.Callback; 093import org.apache.activemq.util.LongSequenceGenerator; 094import org.slf4j.Logger; 095import org.slf4j.LoggerFactory; 096 097/** 098 * <P> 099 * A <CODE>Session</CODE> object is a single-threaded context for producing 100 * and consuming messages. Although it may allocate provider resources outside 101 * the Java virtual machine (JVM), it is considered a lightweight JMS object. 102 * <P> 103 * A session serves several purposes: 104 * <UL> 105 * <LI>It is a factory for its message producers and consumers. 106 * <LI>It supplies provider-optimized message factories. 107 * <LI>It is a factory for <CODE>TemporaryTopics</CODE> and 108 * <CODE>TemporaryQueues</CODE>. 109 * <LI>It provides a way to create <CODE>Queue</CODE> or <CODE>Topic</CODE> 110 * objects for those clients that need to dynamically manipulate 111 * provider-specific destination names. 112 * <LI>It supports a single series of transactions that combine work spanning 113 * its producers and consumers into atomic units. 114 * <LI>It defines a serial order for the messages it consumes and the messages 115 * it produces. 116 * <LI>It retains messages it consumes until they have been acknowledged. 117 * <LI>It serializes execution of message listeners registered with its message 118 * consumers. 119 * <LI>It is a factory for <CODE>QueueBrowsers</CODE>. 120 * </UL> 121 * <P> 122 * A session can create and service multiple message producers and consumers. 123 * <P> 124 * One typical use is to have a thread block on a synchronous 125 * <CODE>MessageConsumer</CODE> until a message arrives. The thread may then 126 * use one or more of the <CODE>Session</CODE>'s<CODE>MessageProducer</CODE>s. 127 * <P> 128 * If a client desires to have one thread produce messages while others consume 129 * them, the client should use a separate session for its producing thread. 130 * <P> 131 * Once a connection has been started, any session with one or more registered 132 * message listeners is dedicated to the thread of control that delivers 133 * messages to it. It is erroneous for client code to use this session or any of 134 * its constituent objects from another thread of control. The only exception to 135 * this rule is the use of the session or connection <CODE>close</CODE> 136 * method. 137 * <P> 138 * It should be easy for most clients to partition their work naturally into 139 * sessions. This model allows clients to start simply and incrementally add 140 * message processing complexity as their need for concurrency grows. 141 * <P> 142 * The <CODE>close</CODE> method is the only session method that can be called 143 * while some other session method is being executed in another thread. 144 * <P> 145 * A session may be specified as transacted. Each transacted session supports a 146 * single series of transactions. Each transaction groups a set of message sends 147 * and a set of message receives into an atomic unit of work. In effect, 148 * transactions organize a session's input message stream and output message 149 * stream into series of atomic units. When a transaction commits, its atomic 150 * unit of input is acknowledged and its associated atomic unit of output is 151 * sent. If a transaction rollback is done, the transaction's sent messages are 152 * destroyed and the session's input is automatically recovered. 153 * <P> 154 * The content of a transaction's input and output units is simply those 155 * messages that have been produced and consumed within the session's current 156 * transaction. 157 * <P> 158 * A transaction is completed using either its session's <CODE>commit</CODE> 159 * method or its session's <CODE>rollback </CODE> method. The completion of a 160 * session's current transaction automatically begins the next. The result is 161 * that a transacted session always has a current transaction within which its 162 * work is done. 163 * <P> 164 * The Java Transaction Service (JTS) or some other transaction monitor may be 165 * used to combine a session's transaction with transactions on other resources 166 * (databases, other JMS sessions, etc.). Since Java distributed transactions 167 * are controlled via the Java Transaction API (JTA), use of the session's 168 * <CODE>commit</CODE> and <CODE>rollback</CODE> methods in this context is 169 * prohibited. 170 * <P> 171 * The JMS API does not require support for JTA; however, it does define how a 172 * provider supplies this support. 173 * <P> 174 * Although it is also possible for a JMS client to handle distributed 175 * transactions directly, it is unlikely that many JMS clients will do this. 176 * Support for JTA in the JMS API is targeted at systems vendors who will be 177 * integrating the JMS API into their application server products. 178 * 179 * 180 * @see javax.jms.Session 181 * @see javax.jms.QueueSession 182 * @see javax.jms.TopicSession 183 * @see javax.jms.XASession 184 */ 185public class ActiveMQSession implements Session, QueueSession, TopicSession, StatsCapable, ActiveMQDispatcher { 186 187 /** 188 * Only acknowledge an individual message - using message.acknowledge() 189 * as opposed to CLIENT_ACKNOWLEDGE which 190 * acknowledges all messages consumed by a session at when acknowledge() 191 * is called 192 */ 193 public static final int INDIVIDUAL_ACKNOWLEDGE = 4; 194 public static final int MAX_ACK_CONSTANT = INDIVIDUAL_ACKNOWLEDGE; 195 196 public static interface DeliveryListener { 197 void beforeDelivery(ActiveMQSession session, Message msg); 198 199 void afterDelivery(ActiveMQSession session, Message msg); 200 } 201 202 private static final Logger LOG = LoggerFactory.getLogger(ActiveMQSession.class); 203 private final ThreadPoolExecutor connectionExecutor; 204 205 protected int acknowledgementMode; 206 protected final ActiveMQConnection connection; 207 protected final SessionInfo info; 208 protected final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); 209 protected final LongSequenceGenerator producerIdGenerator = new LongSequenceGenerator(); 210 protected final LongSequenceGenerator deliveryIdGenerator = new LongSequenceGenerator(); 211 protected final ActiveMQSessionExecutor executor; 212 protected final AtomicBoolean started = new AtomicBoolean(false); 213 214 protected final CopyOnWriteArrayList<ActiveMQMessageConsumer> consumers = new CopyOnWriteArrayList<ActiveMQMessageConsumer>(); 215 protected final CopyOnWriteArrayList<ActiveMQMessageProducer> producers = new CopyOnWriteArrayList<ActiveMQMessageProducer>(); 216 217 protected boolean closed; 218 private volatile boolean synchronizationRegistered; 219 protected boolean asyncDispatch; 220 protected boolean sessionAsyncDispatch; 221 protected final boolean debug; 222 protected final Object sendMutex = new Object(); 223 protected final Object redeliveryGuard = new Object(); 224 225 private final AtomicBoolean clearInProgress = new AtomicBoolean(); 226 227 private MessageListener messageListener; 228 private final JMSSessionStatsImpl stats; 229 private TransactionContext transactionContext; 230 private DeliveryListener deliveryListener; 231 private MessageTransformer transformer; 232 private BlobTransferPolicy blobTransferPolicy; 233 private long lastDeliveredSequenceId = -2; 234 235 /** 236 * Construct the Session 237 * 238 * @param connection 239 * @param sessionId 240 * @param acknowledgeMode n.b if transacted - the acknowledgeMode == 241 * Session.SESSION_TRANSACTED 242 * @param asyncDispatch 243 * @param sessionAsyncDispatch 244 * @throws JMSException on internal error 245 */ 246 protected ActiveMQSession(ActiveMQConnection connection, SessionId sessionId, int acknowledgeMode, boolean asyncDispatch, boolean sessionAsyncDispatch) throws JMSException { 247 this.debug = LOG.isDebugEnabled(); 248 this.connection = connection; 249 this.acknowledgementMode = acknowledgeMode; 250 this.asyncDispatch = asyncDispatch; 251 this.sessionAsyncDispatch = sessionAsyncDispatch; 252 this.info = new SessionInfo(connection.getConnectionInfo(), sessionId.getValue()); 253 setTransactionContext(new TransactionContext(connection)); 254 stats = new JMSSessionStatsImpl(producers, consumers); 255 this.connection.asyncSendPacket(info); 256 setTransformer(connection.getTransformer()); 257 setBlobTransferPolicy(connection.getBlobTransferPolicy()); 258 this.connectionExecutor=connection.getExecutor(); 259 this.executor = new ActiveMQSessionExecutor(this); 260 connection.addSession(this); 261 if (connection.isStarted()) { 262 start(); 263 } 264 265 } 266 267 protected ActiveMQSession(ActiveMQConnection connection, SessionId sessionId, int acknowledgeMode, boolean asyncDispatch) throws JMSException { 268 this(connection, sessionId, acknowledgeMode, asyncDispatch, true); 269 } 270 271 /** 272 * Sets the transaction context of the session. 273 * 274 * @param transactionContext - provides the means to control a JMS 275 * transaction. 276 */ 277 public void setTransactionContext(TransactionContext transactionContext) { 278 this.transactionContext = transactionContext; 279 } 280 281 /** 282 * Returns the transaction context of the session. 283 * 284 * @return transactionContext - session's transaction context. 285 */ 286 public TransactionContext getTransactionContext() { 287 return transactionContext; 288 } 289 290 /* 291 * (non-Javadoc) 292 * 293 * @see org.apache.activemq.management.StatsCapable#getStats() 294 */ 295 @Override 296 public StatsImpl getStats() { 297 return stats; 298 } 299 300 /** 301 * Returns the session's statistics. 302 * 303 * @return stats - session's statistics. 304 */ 305 public JMSSessionStatsImpl getSessionStats() { 306 return stats; 307 } 308 309 /** 310 * Creates a <CODE>BytesMessage</CODE> object. A <CODE>BytesMessage</CODE> 311 * object is used to send a message containing a stream of uninterpreted 312 * bytes. 313 * 314 * @return the an ActiveMQBytesMessage 315 * @throws JMSException if the JMS provider fails to create this message due 316 * to some internal error. 317 */ 318 @Override 319 public BytesMessage createBytesMessage() throws JMSException { 320 ActiveMQBytesMessage message = new ActiveMQBytesMessage(); 321 configureMessage(message); 322 return message; 323 } 324 325 /** 326 * Creates a <CODE>MapMessage</CODE> object. A <CODE>MapMessage</CODE> 327 * object is used to send a self-defining set of name-value pairs, where 328 * names are <CODE>String</CODE> objects and values are primitive values 329 * in the Java programming language. 330 * 331 * @return an ActiveMQMapMessage 332 * @throws JMSException if the JMS provider fails to create this message due 333 * to some internal error. 334 */ 335 @Override 336 public MapMessage createMapMessage() throws JMSException { 337 ActiveMQMapMessage message = new ActiveMQMapMessage(); 338 configureMessage(message); 339 return message; 340 } 341 342 /** 343 * Creates a <CODE>Message</CODE> object. The <CODE>Message</CODE> 344 * interface is the root interface of all JMS messages. A 345 * <CODE>Message</CODE> object holds all the standard message header 346 * information. It can be sent when a message containing only header 347 * information is sufficient. 348 * 349 * @return an ActiveMQMessage 350 * @throws JMSException if the JMS provider fails to create this message due 351 * to some internal error. 352 */ 353 @Override 354 public Message createMessage() throws JMSException { 355 ActiveMQMessage message = new ActiveMQMessage(); 356 configureMessage(message); 357 return message; 358 } 359 360 /** 361 * Creates an <CODE>ObjectMessage</CODE> object. An 362 * <CODE>ObjectMessage</CODE> object is used to send a message that 363 * contains a serializable Java object. 364 * 365 * @return an ActiveMQObjectMessage 366 * @throws JMSException if the JMS provider fails to create this message due 367 * to some internal error. 368 */ 369 @Override 370 public ObjectMessage createObjectMessage() throws JMSException { 371 ActiveMQObjectMessage message = new ActiveMQObjectMessage(); 372 configureMessage(message); 373 return message; 374 } 375 376 /** 377 * Creates an initialized <CODE>ObjectMessage</CODE> object. An 378 * <CODE>ObjectMessage</CODE> object is used to send a message that 379 * contains a serializable Java object. 380 * 381 * @param object the object to use to initialize this message 382 * @return an ActiveMQObjectMessage 383 * @throws JMSException if the JMS provider fails to create this message due 384 * to some internal error. 385 */ 386 @Override 387 public ObjectMessage createObjectMessage(Serializable object) throws JMSException { 388 ActiveMQObjectMessage message = new ActiveMQObjectMessage(); 389 configureMessage(message); 390 message.setObject(object); 391 return message; 392 } 393 394 /** 395 * Creates a <CODE>StreamMessage</CODE> object. A 396 * <CODE>StreamMessage</CODE> object is used to send a self-defining 397 * stream of primitive values in the Java programming language. 398 * 399 * @return an ActiveMQStreamMessage 400 * @throws JMSException if the JMS provider fails to create this message due 401 * to some internal error. 402 */ 403 @Override 404 public StreamMessage createStreamMessage() throws JMSException { 405 ActiveMQStreamMessage message = new ActiveMQStreamMessage(); 406 configureMessage(message); 407 return message; 408 } 409 410 /** 411 * Creates a <CODE>TextMessage</CODE> object. A <CODE>TextMessage</CODE> 412 * object is used to send a message containing a <CODE>String</CODE> 413 * object. 414 * 415 * @return an ActiveMQTextMessage 416 * @throws JMSException if the JMS provider fails to create this message due 417 * to some internal error. 418 */ 419 @Override 420 public TextMessage createTextMessage() throws JMSException { 421 ActiveMQTextMessage message = new ActiveMQTextMessage(); 422 configureMessage(message); 423 return message; 424 } 425 426 /** 427 * Creates an initialized <CODE>TextMessage</CODE> object. A 428 * <CODE>TextMessage</CODE> object is used to send a message containing a 429 * <CODE>String</CODE>. 430 * 431 * @param text the string used to initialize this message 432 * @return an ActiveMQTextMessage 433 * @throws JMSException if the JMS provider fails to create this message due 434 * to some internal error. 435 */ 436 @Override 437 public TextMessage createTextMessage(String text) throws JMSException { 438 ActiveMQTextMessage message = new ActiveMQTextMessage(); 439 message.setText(text); 440 configureMessage(message); 441 return message; 442 } 443 444 /** 445 * Creates an initialized <CODE>BlobMessage</CODE> object. A 446 * <CODE>BlobMessage</CODE> object is used to send a message containing a 447 * <CODE>URL</CODE> which points to some network addressible BLOB. 448 * 449 * @param url the network addressable URL used to pass directly to the 450 * consumer 451 * @return a BlobMessage 452 * @throws JMSException if the JMS provider fails to create this message due 453 * to some internal error. 454 */ 455 public BlobMessage createBlobMessage(URL url) throws JMSException { 456 return createBlobMessage(url, false); 457 } 458 459 /** 460 * Creates an initialized <CODE>BlobMessage</CODE> object. A 461 * <CODE>BlobMessage</CODE> object is used to send a message containing a 462 * <CODE>URL</CODE> which points to some network addressible BLOB. 463 * 464 * @param url the network addressable URL used to pass directly to the 465 * consumer 466 * @param deletedByBroker indicates whether or not the resource is deleted 467 * by the broker when the message is acknowledged 468 * @return a BlobMessage 469 * @throws JMSException if the JMS provider fails to create this message due 470 * to some internal error. 471 */ 472 public BlobMessage createBlobMessage(URL url, boolean deletedByBroker) throws JMSException { 473 ActiveMQBlobMessage message = new ActiveMQBlobMessage(); 474 configureMessage(message); 475 message.setURL(url); 476 message.setDeletedByBroker(deletedByBroker); 477 message.setBlobDownloader(new BlobDownloader(getBlobTransferPolicy())); 478 return message; 479 } 480 481 /** 482 * Creates an initialized <CODE>BlobMessage</CODE> object. A 483 * <CODE>BlobMessage</CODE> object is used to send a message containing 484 * the <CODE>File</CODE> content. Before the message is sent the file 485 * conent will be uploaded to the broker or some other remote repository 486 * depending on the {@link #getBlobTransferPolicy()}. 487 * 488 * @param file the file to be uploaded to some remote repo (or the broker) 489 * depending on the strategy 490 * @return a BlobMessage 491 * @throws JMSException if the JMS provider fails to create this message due 492 * to some internal error. 493 */ 494 public BlobMessage createBlobMessage(File file) throws JMSException { 495 ActiveMQBlobMessage message = new ActiveMQBlobMessage(); 496 configureMessage(message); 497 message.setBlobUploader(new BlobUploader(getBlobTransferPolicy(), file)); 498 message.setBlobDownloader(new BlobDownloader((getBlobTransferPolicy()))); 499 message.setDeletedByBroker(true); 500 message.setName(file.getName()); 501 return message; 502 } 503 504 /** 505 * Creates an initialized <CODE>BlobMessage</CODE> object. A 506 * <CODE>BlobMessage</CODE> object is used to send a message containing 507 * the <CODE>File</CODE> content. Before the message is sent the file 508 * conent will be uploaded to the broker or some other remote repository 509 * depending on the {@link #getBlobTransferPolicy()}. 510 * 511 * @param in the stream to be uploaded to some remote repo (or the broker) 512 * depending on the strategy 513 * @return a BlobMessage 514 * @throws JMSException if the JMS provider fails to create this message due 515 * to some internal error. 516 */ 517 public BlobMessage createBlobMessage(InputStream in) throws JMSException { 518 ActiveMQBlobMessage message = new ActiveMQBlobMessage(); 519 configureMessage(message); 520 message.setBlobUploader(new BlobUploader(getBlobTransferPolicy(), in)); 521 message.setBlobDownloader(new BlobDownloader(getBlobTransferPolicy())); 522 message.setDeletedByBroker(true); 523 return message; 524 } 525 526 /** 527 * Indicates whether the session is in transacted mode. 528 * 529 * @return true if the session is in transacted mode 530 * @throws JMSException if there is some internal error. 531 */ 532 @Override 533 public boolean getTransacted() throws JMSException { 534 checkClosed(); 535 return isTransacted(); 536 } 537 538 /** 539 * Returns the acknowledgement mode of the session. The acknowledgement mode 540 * is set at the time that the session is created. If the session is 541 * transacted, the acknowledgement mode is ignored. 542 * 543 * @return If the session is not transacted, returns the current 544 * acknowledgement mode for the session. If the session is 545 * transacted, returns SESSION_TRANSACTED. 546 * @throws JMSException 547 * @see javax.jms.Connection#createSession(boolean,int) 548 * @since 1.1 exception JMSException if there is some internal error. 549 */ 550 @Override 551 public int getAcknowledgeMode() throws JMSException { 552 checkClosed(); 553 return this.acknowledgementMode; 554 } 555 556 /** 557 * Commits all messages done in this transaction and releases any locks 558 * currently held. 559 * 560 * @throws JMSException if the JMS provider fails to commit the transaction 561 * due to some internal error. 562 * @throws TransactionRolledBackException if the transaction is rolled back 563 * due to some internal error during commit. 564 * @throws javax.jms.IllegalStateException if the method is not called by a 565 * transacted session. 566 */ 567 @Override 568 public void commit() throws JMSException { 569 checkClosed(); 570 if (!getTransacted()) { 571 throw new javax.jms.IllegalStateException("Not a transacted session"); 572 } 573 if (LOG.isDebugEnabled()) { 574 LOG.debug(getSessionId() + " Transaction Commit :" + transactionContext.getTransactionId()); 575 } 576 transactionContext.commit(); 577 } 578 579 /** 580 * Rolls back any messages done in this transaction and releases any locks 581 * currently held. 582 * 583 * @throws JMSException if the JMS provider fails to roll back the 584 * transaction due to some internal error. 585 * @throws javax.jms.IllegalStateException if the method is not called by a 586 * transacted session. 587 */ 588 @Override 589 public void rollback() throws JMSException { 590 checkClosed(); 591 if (!getTransacted()) { 592 throw new javax.jms.IllegalStateException("Not a transacted session"); 593 } 594 if (LOG.isDebugEnabled()) { 595 LOG.debug(getSessionId() + " Transaction Rollback, txid:" + transactionContext.getTransactionId()); 596 } 597 transactionContext.rollback(); 598 } 599 600 /** 601 * Closes the session. 602 * <P> 603 * Since a provider may allocate some resources on behalf of a session 604 * outside the JVM, clients should close the resources when they are not 605 * needed. Relying on garbage collection to eventually reclaim these 606 * resources may not be timely enough. 607 * <P> 608 * There is no need to close the producers and consumers of a closed 609 * session. 610 * <P> 611 * This call will block until a <CODE>receive</CODE> call or message 612 * listener in progress has completed. A blocked message consumer 613 * <CODE>receive</CODE> call returns <CODE>null</CODE> when this session 614 * is closed. 615 * <P> 616 * Closing a transacted session must roll back the transaction in progress. 617 * <P> 618 * This method is the only <CODE>Session</CODE> method that can be called 619 * concurrently. 620 * <P> 621 * Invoking any other <CODE>Session</CODE> method on a closed session must 622 * throw a <CODE> JMSException.IllegalStateException</CODE>. Closing a 623 * closed session must <I>not </I> throw an exception. 624 * 625 * @throws JMSException if the JMS provider fails to close the session due 626 * to some internal error. 627 */ 628 @Override 629 public void close() throws JMSException { 630 if (!closed) { 631 if (getTransactionContext().isInXATransaction()) { 632 if (!synchronizationRegistered) { 633 synchronizationRegistered = true; 634 getTransactionContext().addSynchronization(new Synchronization() { 635 636 @Override 637 public void afterCommit() throws Exception { 638 doClose(); 639 synchronizationRegistered = false; 640 } 641 642 @Override 643 public void afterRollback() throws Exception { 644 doClose(); 645 synchronizationRegistered = false; 646 } 647 }); 648 } 649 650 } else { 651 doClose(); 652 } 653 } 654 } 655 656 private void doClose() throws JMSException { 657 boolean interrupted = Thread.interrupted(); 658 dispose(); 659 RemoveInfo removeCommand = info.createRemoveCommand(); 660 removeCommand.setLastDeliveredSequenceId(lastDeliveredSequenceId); 661 connection.asyncSendPacket(removeCommand); 662 if (interrupted) { 663 Thread.currentThread().interrupt(); 664 } 665 } 666 667 final AtomicInteger clearRequestsCounter = new AtomicInteger(0); 668 void clearMessagesInProgress(AtomicInteger transportInterruptionProcessingComplete) { 669 clearRequestsCounter.incrementAndGet(); 670 executor.clearMessagesInProgress(); 671 // we are called from inside the transport reconnection logic which involves us 672 // clearing all the connections' consumers dispatch and delivered lists. So rather 673 // than trying to grab a mutex (which could be already owned by the message listener 674 // calling the send or an ack) we allow it to complete in a separate thread via the 675 // scheduler and notify us via connection.transportInterruptionProcessingComplete() 676 // 677 // We must be careful though not to allow multiple calls to this method from a 678 // connection that is having issue becoming fully established from causing a large 679 // build up of scheduled tasks to clear the same consumers over and over. 680 if (consumers.isEmpty()) { 681 return; 682 } 683 684 if (clearInProgress.compareAndSet(false, true)) { 685 for (final ActiveMQMessageConsumer consumer : consumers) { 686 consumer.inProgressClearRequired(); 687 transportInterruptionProcessingComplete.incrementAndGet(); 688 try { 689 connection.getScheduler().executeAfterDelay(new Runnable() { 690 @Override 691 public void run() { 692 consumer.clearMessagesInProgress(); 693 }}, 0l); 694 } catch (JMSException e) { 695 connection.onClientInternalException(e); 696 } 697 } 698 699 try { 700 connection.getScheduler().executeAfterDelay(new Runnable() { 701 @Override 702 public void run() { 703 clearInProgress.set(false); 704 }}, 0l); 705 } catch (JMSException e) { 706 connection.onClientInternalException(e); 707 } 708 } 709 } 710 711 void deliverAcks() { 712 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 713 ActiveMQMessageConsumer consumer = iter.next(); 714 consumer.deliverAcks(); 715 } 716 } 717 718 public synchronized void dispose() throws JMSException { 719 if (!closed) { 720 721 try { 722 executor.close(); 723 724 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 725 ActiveMQMessageConsumer consumer = iter.next(); 726 consumer.setFailureError(connection.getFirstFailureError()); 727 consumer.dispose(); 728 lastDeliveredSequenceId = Math.max(lastDeliveredSequenceId, consumer.getLastDeliveredSequenceId()); 729 } 730 consumers.clear(); 731 732 for (Iterator<ActiveMQMessageProducer> iter = producers.iterator(); iter.hasNext();) { 733 ActiveMQMessageProducer producer = iter.next(); 734 producer.dispose(); 735 } 736 producers.clear(); 737 738 try { 739 if (getTransactionContext().isInLocalTransaction()) { 740 rollback(); 741 } 742 } catch (JMSException e) { 743 } 744 745 } finally { 746 connection.removeSession(this); 747 this.transactionContext = null; 748 closed = true; 749 } 750 } 751 } 752 753 /** 754 * Checks that the session is not closed then configures the message 755 */ 756 protected void configureMessage(ActiveMQMessage message) throws IllegalStateException { 757 checkClosed(); 758 message.setConnection(connection); 759 } 760 761 /** 762 * Check if the session is closed. It is used for ensuring that the session 763 * is open before performing various operations. 764 * 765 * @throws IllegalStateException if the Session is closed 766 */ 767 protected void checkClosed() throws IllegalStateException { 768 if (closed) { 769 throw new IllegalStateException("The Session is closed"); 770 } 771 } 772 773 /** 774 * Checks if the session is closed. 775 * 776 * @return true if the session is closed, false otherwise. 777 */ 778 public boolean isClosed() { 779 return closed; 780 } 781 782 /** 783 * Stops message delivery in this session, and restarts message delivery 784 * with the oldest unacknowledged message. 785 * <P> 786 * All consumers deliver messages in a serial order. Acknowledging a 787 * received message automatically acknowledges all messages that have been 788 * delivered to the client. 789 * <P> 790 * Restarting a session causes it to take the following actions: 791 * <UL> 792 * <LI>Stop message delivery 793 * <LI>Mark all messages that might have been delivered but not 794 * acknowledged as "redelivered" 795 * <LI>Restart the delivery sequence including all unacknowledged messages 796 * that had been previously delivered. Redelivered messages do not have to 797 * be delivered in exactly their original delivery order. 798 * </UL> 799 * 800 * @throws JMSException if the JMS provider fails to stop and restart 801 * message delivery due to some internal error. 802 * @throws IllegalStateException if the method is called by a transacted 803 * session. 804 */ 805 @Override 806 public void recover() throws JMSException { 807 808 checkClosed(); 809 if (getTransacted()) { 810 throw new IllegalStateException("This session is transacted"); 811 } 812 813 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 814 ActiveMQMessageConsumer c = iter.next(); 815 c.rollback(); 816 } 817 818 } 819 820 /** 821 * Returns the session's distinguished message listener (optional). 822 * 823 * @return the message listener associated with this session 824 * @throws JMSException if the JMS provider fails to get the message 825 * listener due to an internal error. 826 * @see javax.jms.Session#setMessageListener(javax.jms.MessageListener) 827 * @see javax.jms.ServerSessionPool 828 * @see javax.jms.ServerSession 829 */ 830 @Override 831 public MessageListener getMessageListener() throws JMSException { 832 checkClosed(); 833 return this.messageListener; 834 } 835 836 /** 837 * Sets the session's distinguished message listener (optional). 838 * <P> 839 * When the distinguished message listener is set, no other form of message 840 * receipt in the session can be used; however, all forms of sending 841 * messages are still supported. 842 * <P> 843 * If this session has been closed, then an {@link IllegalStateException} is 844 * thrown, if trying to set a new listener. However setting the listener 845 * to <tt>null</tt> is allowed, to clear the listener, even if this session 846 * has been closed prior. 847 * <P> 848 * This is an expert facility not used by regular JMS clients. 849 * 850 * @param listener the message listener to associate with this session 851 * @throws JMSException if the JMS provider fails to set the message 852 * listener due to an internal error. 853 * @see javax.jms.Session#getMessageListener() 854 * @see javax.jms.ServerSessionPool 855 * @see javax.jms.ServerSession 856 */ 857 @Override 858 public void setMessageListener(MessageListener listener) throws JMSException { 859 // only check for closed if we set a new listener, as we allow to clear 860 // the listener, such as when an application is shutting down, and is 861 // no longer using a message listener on this session 862 if (listener != null) { 863 checkClosed(); 864 } 865 this.messageListener = listener; 866 867 if (listener != null) { 868 executor.setDispatchedBySessionPool(true); 869 } 870 } 871 872 /** 873 * Optional operation, intended to be used only by Application Servers, not 874 * by ordinary JMS clients. 875 * 876 * @see javax.jms.ServerSession 877 */ 878 @Override 879 public void run() { 880 MessageDispatch messageDispatch; 881 while ((messageDispatch = executor.dequeueNoWait()) != null) { 882 final MessageDispatch md = messageDispatch; 883 final ActiveMQMessage message = (ActiveMQMessage)md.getMessage(); 884 885 MessageAck earlyAck = null; 886 if (message.isExpired()) { 887 earlyAck = new MessageAck(md, MessageAck.EXPIRED_ACK_TYPE, 1); 888 } else if (connection.isDuplicate(ActiveMQSession.this, message)) { 889 LOG.debug("{} got duplicate: {}", this, message.getMessageId()); 890 earlyAck = new MessageAck(md, MessageAck.POSION_ACK_TYPE, 1); 891 earlyAck.setFirstMessageId(md.getMessage().getMessageId()); 892 earlyAck.setPoisonCause(new Throwable("Duplicate delivery to " + this)); 893 } 894 if (earlyAck != null) { 895 try { 896 asyncSendPacket(earlyAck); 897 } catch (Throwable t) { 898 LOG.error("error dispatching ack: {} ", earlyAck, t); 899 connection.onClientInternalException(t); 900 } finally { 901 continue; 902 } 903 } 904 905 if (isClientAcknowledge()||isIndividualAcknowledge()) { 906 message.setAcknowledgeCallback(new Callback() { 907 @Override 908 public void execute() throws Exception { 909 } 910 }); 911 } 912 913 if (deliveryListener != null) { 914 deliveryListener.beforeDelivery(this, message); 915 } 916 917 md.setDeliverySequenceId(getNextDeliveryId()); 918 lastDeliveredSequenceId = message.getMessageId().getBrokerSequenceId(); 919 920 final MessageAck ack = new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, 1); 921 922 final AtomicBoolean afterDeliveryError = new AtomicBoolean(false); 923 /* 924 * The redelivery guard is to allow the endpoint lifecycle to complete before the messsage is dispatched. 925 * We dont want the after deliver being called after the redeliver as it may cause some weird stuff. 926 * */ 927 synchronized (redeliveryGuard) { 928 try { 929 ack.setFirstMessageId(md.getMessage().getMessageId()); 930 doStartTransaction(); 931 ack.setTransactionId(getTransactionContext().getTransactionId()); 932 if (ack.getTransactionId() != null) { 933 getTransactionContext().addSynchronization(new Synchronization() { 934 935 final int clearRequestCount = (clearRequestsCounter.get() == Integer.MAX_VALUE ? clearRequestsCounter.incrementAndGet() : clearRequestsCounter.get()); 936 937 @Override 938 public void beforeEnd() throws Exception { 939 // validate our consumer so we don't push stale acks that get ignored 940 if (ack.getTransactionId().isXATransaction() && !connection.hasDispatcher(ack.getConsumerId())) { 941 LOG.debug("forcing rollback - {} consumer no longer active on {}", ack, connection); 942 throw new TransactionRolledBackException("consumer " + ack.getConsumerId() + " no longer active on " + connection); 943 } 944 LOG.trace("beforeEnd ack {}", ack); 945 sendAck(ack); 946 } 947 948 @Override 949 public void afterRollback() throws Exception { 950 LOG.trace("rollback {}", ack, new Throwable("here")); 951 // ensure we don't filter this as a duplicate 952 connection.rollbackDuplicate(ActiveMQSession.this, md.getMessage()); 953 954 // don't redeliver if we have been interrupted b/c the broker will redeliver on reconnect 955 if (clearRequestsCounter.get() > clearRequestCount) { 956 LOG.debug("No redelivery of {} on rollback of {} due to failover of {}", md, ack.getTransactionId(), connection.getTransport()); 957 return; 958 } 959 960 // validate our consumer so we don't push stale acks that get ignored or redeliver what will be redispatched 961 if (ack.getTransactionId().isXATransaction() && !connection.hasDispatcher(ack.getConsumerId())) { 962 LOG.debug("No local redelivery of {} on rollback of {} because consumer is no longer active on {}", md, ack.getTransactionId(), connection.getTransport()); 963 return; 964 } 965 966 RedeliveryPolicy redeliveryPolicy = connection.getRedeliveryPolicy(); 967 int redeliveryCounter = md.getMessage().getRedeliveryCounter(); 968 if (redeliveryPolicy.getMaximumRedeliveries() != RedeliveryPolicy.NO_MAXIMUM_REDELIVERIES 969 && redeliveryCounter >= redeliveryPolicy.getMaximumRedeliveries()) { 970 // We need to NACK the messages so that they get 971 // sent to the 972 // DLQ. 973 // Acknowledge the last message. 974 MessageAck ack = new MessageAck(md, MessageAck.POSION_ACK_TYPE, 1); 975 ack.setFirstMessageId(md.getMessage().getMessageId()); 976 ack.setPoisonCause(new Throwable("Exceeded ra redelivery policy limit:" + redeliveryPolicy)); 977 asyncSendPacket(ack); 978 979 } else { 980 981 MessageAck ack = new MessageAck(md, MessageAck.REDELIVERED_ACK_TYPE, 1); 982 ack.setFirstMessageId(md.getMessage().getMessageId()); 983 asyncSendPacket(ack); 984 985 // Figure out how long we should wait to resend 986 // this message. 987 long redeliveryDelay = redeliveryPolicy.getInitialRedeliveryDelay(); 988 for (int i = 0; i < redeliveryCounter; i++) { 989 redeliveryDelay = redeliveryPolicy.getNextRedeliveryDelay(redeliveryDelay); 990 } 991 992 /* 993 * If we are a non blocking delivery then we need to stop the executor to avoid more 994 * messages being delivered, once the message is redelivered we can restart it. 995 * */ 996 if (!connection.isNonBlockingRedelivery()) { 997 LOG.debug("Blocking session until re-delivery..."); 998 executor.stop(); 999 } 1000 1001 connection.getScheduler().executeAfterDelay(new Runnable() { 1002 1003 @Override 1004 public void run() { 1005 /* 1006 * wait for the first delivery to be complete, i.e. after delivery has been called. 1007 * */ 1008 synchronized (redeliveryGuard) { 1009 /* 1010 * If its non blocking then we can just dispatch in a new session. 1011 * */ 1012 if (connection.isNonBlockingRedelivery()) { 1013 ((ActiveMQDispatcher) md.getConsumer()).dispatch(md); 1014 } else { 1015 /* 1016 * If there has been an error thrown during afterDelivery then the 1017 * endpoint will be marked as dead so redelivery will fail (and eventually 1018 * the session marked as stale), in this case we can only call dispatch 1019 * which will create a new session with a new endpoint. 1020 * */ 1021 if (afterDeliveryError.get()) { 1022 ((ActiveMQDispatcher) md.getConsumer()).dispatch(md); 1023 } else { 1024 executor.executeFirst(md); 1025 executor.start(); 1026 } 1027 } 1028 } 1029 } 1030 }, redeliveryDelay); 1031 } 1032 md.getMessage().onMessageRolledBack(); 1033 } 1034 }); 1035 } 1036 1037 LOG.trace("{} onMessage({})", this, message.getMessageId()); 1038 messageListener.onMessage(message); 1039 1040 } catch (Throwable e) { 1041 LOG.error("error dispatching message: ", e); 1042 1043 // A problem while invoking the MessageListener does not 1044 // in general indicate a problem with the connection to the broker, i.e. 1045 // it will usually be sufficient to let the afterDelivery() method either 1046 // commit or roll back in order to deal with the exception. 1047 // However, we notify any registered client internal exception listener 1048 // of the problem. 1049 connection.onClientInternalException(e); 1050 } finally { 1051 if (ack.getTransactionId() == null) { 1052 try { 1053 asyncSendPacket(ack); 1054 } catch (Throwable e) { 1055 connection.onClientInternalException(e); 1056 } 1057 } 1058 } 1059 1060 if (deliveryListener != null) { 1061 try { 1062 deliveryListener.afterDelivery(this, message); 1063 } catch (Throwable t) { 1064 LOG.debug("Unable to call after delivery", t); 1065 afterDeliveryError.set(true); 1066 throw new RuntimeException(t); 1067 } 1068 } 1069 } 1070 /* 1071 * this can be outside the try/catch as if an exception is thrown then this session will be marked as stale anyway. 1072 * It also needs to be outside the redelivery guard. 1073 * */ 1074 try { 1075 executor.waitForQueueRestart(); 1076 } catch (InterruptedException ex) { 1077 connection.onClientInternalException(ex); 1078 } 1079 } 1080 } 1081 1082 /** 1083 * Creates a <CODE>MessageProducer</CODE> to send messages to the 1084 * specified destination. 1085 * <P> 1086 * A client uses a <CODE>MessageProducer</CODE> object to send messages to 1087 * a destination. Since <CODE>Queue </CODE> and <CODE>Topic</CODE> both 1088 * inherit from <CODE>Destination</CODE>, they can be used in the 1089 * destination parameter to create a <CODE>MessageProducer</CODE> object. 1090 * 1091 * @param destination the <CODE>Destination</CODE> to send to, or null if 1092 * this is a producer which does not have a specified 1093 * destination. 1094 * @return the MessageProducer 1095 * @throws JMSException if the session fails to create a MessageProducer due 1096 * to some internal error. 1097 * @throws InvalidDestinationException if an invalid destination is 1098 * specified. 1099 * @since 1.1 1100 */ 1101 @Override 1102 public MessageProducer createProducer(Destination destination) throws JMSException { 1103 checkClosed(); 1104 if (destination instanceof CustomDestination) { 1105 CustomDestination customDestination = (CustomDestination)destination; 1106 return customDestination.createProducer(this); 1107 } 1108 int timeSendOut = connection.getSendTimeout(); 1109 return new ActiveMQMessageProducer(this, getNextProducerId(), ActiveMQMessageTransformation.transformDestination(destination),timeSendOut); 1110 } 1111 1112 /** 1113 * Creates a <CODE>MessageConsumer</CODE> for the specified destination. 1114 * Since <CODE>Queue</CODE> and <CODE> Topic</CODE> both inherit from 1115 * <CODE>Destination</CODE>, they can be used in the destination 1116 * parameter to create a <CODE>MessageConsumer</CODE>. 1117 * 1118 * @param destination the <CODE>Destination</CODE> to access. 1119 * @return the MessageConsumer 1120 * @throws JMSException if the session fails to create a consumer due to 1121 * some internal error. 1122 * @throws InvalidDestinationException if an invalid destination is 1123 * specified. 1124 * @since 1.1 1125 */ 1126 @Override 1127 public MessageConsumer createConsumer(Destination destination) throws JMSException { 1128 return createConsumer(destination, (String) null); 1129 } 1130 1131 /** 1132 * Creates a <CODE>MessageConsumer</CODE> for the specified destination, 1133 * using a message selector. Since <CODE> Queue</CODE> and 1134 * <CODE>Topic</CODE> both inherit from <CODE>Destination</CODE>, they 1135 * can be used in the destination parameter to create a 1136 * <CODE>MessageConsumer</CODE>. 1137 * <P> 1138 * A client uses a <CODE>MessageConsumer</CODE> object to receive messages 1139 * that have been sent to a destination. 1140 * 1141 * @param destination the <CODE>Destination</CODE> to access 1142 * @param messageSelector only messages with properties matching the message 1143 * selector expression are delivered. A value of null or an 1144 * empty string indicates that there is no message selector 1145 * for the message consumer. 1146 * @return the MessageConsumer 1147 * @throws JMSException if the session fails to create a MessageConsumer due 1148 * to some internal error. 1149 * @throws InvalidDestinationException if an invalid destination is 1150 * specified. 1151 * @throws InvalidSelectorException if the message selector is invalid. 1152 * @since 1.1 1153 */ 1154 @Override 1155 public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { 1156 return createConsumer(destination, messageSelector, false); 1157 } 1158 1159 /** 1160 * Creates a <CODE>MessageConsumer</CODE> for the specified destination. 1161 * Since <CODE>Queue</CODE> and <CODE> Topic</CODE> both inherit from 1162 * <CODE>Destination</CODE>, they can be used in the destination 1163 * parameter to create a <CODE>MessageConsumer</CODE>. 1164 * 1165 * @param destination the <CODE>Destination</CODE> to access. 1166 * @param messageListener the listener to use for async consumption of messages 1167 * @return the MessageConsumer 1168 * @throws JMSException if the session fails to create a consumer due to 1169 * some internal error. 1170 * @throws InvalidDestinationException if an invalid destination is 1171 * specified. 1172 * @since 1.1 1173 */ 1174 public MessageConsumer createConsumer(Destination destination, MessageListener messageListener) throws JMSException { 1175 return createConsumer(destination, null, messageListener); 1176 } 1177 1178 /** 1179 * Creates a <CODE>MessageConsumer</CODE> for the specified destination, 1180 * using a message selector. Since <CODE> Queue</CODE> and 1181 * <CODE>Topic</CODE> both inherit from <CODE>Destination</CODE>, they 1182 * can be used in the destination parameter to create a 1183 * <CODE>MessageConsumer</CODE>. 1184 * <P> 1185 * A client uses a <CODE>MessageConsumer</CODE> object to receive messages 1186 * that have been sent to a destination. 1187 * 1188 * @param destination the <CODE>Destination</CODE> to access 1189 * @param messageSelector only messages with properties matching the message 1190 * selector expression are delivered. A value of null or an 1191 * empty string indicates that there is no message selector 1192 * for the message consumer. 1193 * @param messageListener the listener to use for async consumption of messages 1194 * @return the MessageConsumer 1195 * @throws JMSException if the session fails to create a MessageConsumer due 1196 * to some internal error. 1197 * @throws InvalidDestinationException if an invalid destination is 1198 * specified. 1199 * @throws InvalidSelectorException if the message selector is invalid. 1200 * @since 1.1 1201 */ 1202 public MessageConsumer createConsumer(Destination destination, String messageSelector, MessageListener messageListener) throws JMSException { 1203 return createConsumer(destination, messageSelector, false, messageListener); 1204 } 1205 1206 /** 1207 * Creates <CODE>MessageConsumer</CODE> for the specified destination, 1208 * using a message selector. This method can specify whether messages 1209 * published by its own connection should be delivered to it, if the 1210 * destination is a topic. 1211 * <P> 1212 * Since <CODE>Queue</CODE> and <CODE>Topic</CODE> both inherit from 1213 * <CODE>Destination</CODE>, they can be used in the destination 1214 * parameter to create a <CODE>MessageConsumer</CODE>. 1215 * <P> 1216 * A client uses a <CODE>MessageConsumer</CODE> object to receive messages 1217 * that have been published to a destination. 1218 * <P> 1219 * In some cases, a connection may both publish and subscribe to a topic. 1220 * The consumer <CODE>NoLocal</CODE> attribute allows a consumer to 1221 * inhibit the delivery of messages published by its own connection. The 1222 * default value for this attribute is False. The <CODE>noLocal</CODE> 1223 * value must be supported by destinations that are topics. 1224 * 1225 * @param destination the <CODE>Destination</CODE> to access 1226 * @param messageSelector only messages with properties matching the message 1227 * selector expression are delivered. A value of null or an 1228 * empty string indicates that there is no message selector 1229 * for the message consumer. 1230 * @param noLocal - if true, and the destination is a topic, inhibits the 1231 * delivery of messages published by its own connection. The 1232 * behavior for <CODE>NoLocal</CODE> is not specified if 1233 * the destination is a queue. 1234 * @return the MessageConsumer 1235 * @throws JMSException if the session fails to create a MessageConsumer due 1236 * to some internal error. 1237 * @throws InvalidDestinationException if an invalid destination is 1238 * specified. 1239 * @throws InvalidSelectorException if the message selector is invalid. 1240 * @since 1.1 1241 */ 1242 @Override 1243 public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) throws JMSException { 1244 return createConsumer(destination, messageSelector, noLocal, null); 1245 } 1246 1247 /** 1248 * Creates <CODE>MessageConsumer</CODE> for the specified destination, 1249 * using a message selector. This method can specify whether messages 1250 * published by its own connection should be delivered to it, if the 1251 * destination is a topic. 1252 * <P> 1253 * Since <CODE>Queue</CODE> and <CODE>Topic</CODE> both inherit from 1254 * <CODE>Destination</CODE>, they can be used in the destination 1255 * parameter to create a <CODE>MessageConsumer</CODE>. 1256 * <P> 1257 * A client uses a <CODE>MessageConsumer</CODE> object to receive messages 1258 * that have been published to a destination. 1259 * <P> 1260 * In some cases, a connection may both publish and subscribe to a topic. 1261 * The consumer <CODE>NoLocal</CODE> attribute allows a consumer to 1262 * inhibit the delivery of messages published by its own connection. The 1263 * default value for this attribute is False. The <CODE>noLocal</CODE> 1264 * value must be supported by destinations that are topics. 1265 * 1266 * @param destination the <CODE>Destination</CODE> to access 1267 * @param messageSelector only messages with properties matching the message 1268 * selector expression are delivered. A value of null or an 1269 * empty string indicates that there is no message selector 1270 * for the message consumer. 1271 * @param noLocal - if true, and the destination is a topic, inhibits the 1272 * delivery of messages published by its own connection. The 1273 * behavior for <CODE>NoLocal</CODE> is not specified if 1274 * the destination is a queue. 1275 * @param messageListener the listener to use for async consumption of messages 1276 * @return the MessageConsumer 1277 * @throws JMSException if the session fails to create a MessageConsumer due 1278 * to some internal error. 1279 * @throws InvalidDestinationException if an invalid destination is 1280 * specified. 1281 * @throws InvalidSelectorException if the message selector is invalid. 1282 * @since 1.1 1283 */ 1284 public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal, MessageListener messageListener) throws JMSException { 1285 checkClosed(); 1286 1287 if (destination instanceof CustomDestination) { 1288 CustomDestination customDestination = (CustomDestination)destination; 1289 return customDestination.createConsumer(this, messageSelector, noLocal); 1290 } 1291 1292 ActiveMQPrefetchPolicy prefetchPolicy = connection.getPrefetchPolicy(); 1293 int prefetch = 0; 1294 if (destination instanceof Topic) { 1295 prefetch = prefetchPolicy.getTopicPrefetch(); 1296 } else { 1297 prefetch = prefetchPolicy.getQueuePrefetch(); 1298 } 1299 ActiveMQDestination activemqDestination = ActiveMQMessageTransformation.transformDestination(destination); 1300 return new ActiveMQMessageConsumer(this, getNextConsumerId(), activemqDestination, null, messageSelector, 1301 prefetch, prefetchPolicy.getMaximumPendingMessageLimit(), noLocal, false, isAsyncDispatch(), messageListener); 1302 } 1303 1304 /** 1305 * Creates a queue identity given a <CODE>Queue</CODE> name. 1306 * <P> 1307 * This facility is provided for the rare cases where clients need to 1308 * dynamically manipulate queue identity. It allows the creation of a queue 1309 * identity with a provider-specific name. Clients that depend on this 1310 * ability are not portable. 1311 * <P> 1312 * Note that this method is not for creating the physical queue. The 1313 * physical creation of queues is an administrative task and is not to be 1314 * initiated by the JMS API. The one exception is the creation of temporary 1315 * queues, which is accomplished with the <CODE>createTemporaryQueue</CODE> 1316 * method. 1317 * 1318 * @param queueName the name of this <CODE>Queue</CODE> 1319 * @return a <CODE>Queue</CODE> with the given name 1320 * @throws JMSException if the session fails to create a queue due to some 1321 * internal error. 1322 * @since 1.1 1323 */ 1324 @Override 1325 public Queue createQueue(String queueName) throws JMSException { 1326 checkClosed(); 1327 if (queueName.startsWith(ActiveMQDestination.TEMP_DESTINATION_NAME_PREFIX)) { 1328 return new ActiveMQTempQueue(queueName); 1329 } 1330 return new ActiveMQQueue(queueName); 1331 } 1332 1333 /** 1334 * Creates a topic identity given a <CODE>Topic</CODE> name. 1335 * <P> 1336 * This facility is provided for the rare cases where clients need to 1337 * dynamically manipulate topic identity. This allows the creation of a 1338 * topic identity with a provider-specific name. Clients that depend on this 1339 * ability are not portable. 1340 * <P> 1341 * Note that this method is not for creating the physical topic. The 1342 * physical creation of topics is an administrative task and is not to be 1343 * initiated by the JMS API. The one exception is the creation of temporary 1344 * topics, which is accomplished with the <CODE>createTemporaryTopic</CODE> 1345 * method. 1346 * 1347 * @param topicName the name of this <CODE>Topic</CODE> 1348 * @return a <CODE>Topic</CODE> with the given name 1349 * @throws JMSException if the session fails to create a topic due to some 1350 * internal error. 1351 * @since 1.1 1352 */ 1353 @Override 1354 public Topic createTopic(String topicName) throws JMSException { 1355 checkClosed(); 1356 if (topicName.startsWith(ActiveMQDestination.TEMP_DESTINATION_NAME_PREFIX)) { 1357 return new ActiveMQTempTopic(topicName); 1358 } 1359 return new ActiveMQTopic(topicName); 1360 } 1361 1362 /** 1363 * Creates a <CODE>QueueBrowser</CODE> object to peek at the messages on 1364 * the specified queue. 1365 * 1366 * @param queue the <CODE>queue</CODE> to access 1367 * @exception InvalidDestinationException if an invalid destination is 1368 * specified 1369 * @since 1.1 1370 */ 1371 /** 1372 * Creates a durable subscriber to the specified topic. 1373 * <P> 1374 * If a client needs to receive all the messages published on a topic, 1375 * including the ones published while the subscriber is inactive, it uses a 1376 * durable <CODE>TopicSubscriber</CODE>. The JMS provider retains a 1377 * record of this durable subscription and insures that all messages from 1378 * the topic's publishers are retained until they are acknowledged by this 1379 * durable subscriber or they have expired. 1380 * <P> 1381 * Sessions with durable subscribers must always provide the same client 1382 * identifier. In addition, each client must specify a name that uniquely 1383 * identifies (within client identifier) each durable subscription it 1384 * creates. Only one session at a time can have a 1385 * <CODE>TopicSubscriber</CODE> for a particular durable subscription. 1386 * <P> 1387 * A client can change an existing durable subscription by creating a 1388 * durable <CODE>TopicSubscriber</CODE> with the same name and a new topic 1389 * and/or message selector. Changing a durable subscriber is equivalent to 1390 * unsubscribing (deleting) the old one and creating a new one. 1391 * <P> 1392 * In some cases, a connection may both publish and subscribe to a topic. 1393 * The subscriber <CODE>NoLocal</CODE> attribute allows a subscriber to 1394 * inhibit the delivery of messages published by its own connection. The 1395 * default value for this attribute is false. 1396 * 1397 * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to 1398 * @param name the name used to identify this subscription 1399 * @return the TopicSubscriber 1400 * @throws JMSException if the session fails to create a subscriber due to 1401 * some internal error. 1402 * @throws InvalidDestinationException if an invalid topic is specified. 1403 * @since 1.1 1404 */ 1405 @Override 1406 public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { 1407 checkClosed(); 1408 return createDurableSubscriber(topic, name, null, false); 1409 } 1410 1411 /** 1412 * Creates a durable subscriber to the specified topic, using a message 1413 * selector and specifying whether messages published by its own connection 1414 * should be delivered to it. 1415 * <P> 1416 * If a client needs to receive all the messages published on a topic, 1417 * including the ones published while the subscriber is inactive, it uses a 1418 * durable <CODE>TopicSubscriber</CODE>. The JMS provider retains a 1419 * record of this durable subscription and insures that all messages from 1420 * the topic's publishers are retained until they are acknowledged by this 1421 * durable subscriber or they have expired. 1422 * <P> 1423 * Sessions with durable subscribers must always provide the same client 1424 * identifier. In addition, each client must specify a name which uniquely 1425 * identifies (within client identifier) each durable subscription it 1426 * creates. Only one session at a time can have a 1427 * <CODE>TopicSubscriber</CODE> for a particular durable subscription. An 1428 * inactive durable subscriber is one that exists but does not currently 1429 * have a message consumer associated with it. 1430 * <P> 1431 * A client can change an existing durable subscription by creating a 1432 * durable <CODE>TopicSubscriber</CODE> with the same name and a new topic 1433 * and/or message selector. Changing a durable subscriber is equivalent to 1434 * unsubscribing (deleting) the old one and creating a new one. 1435 * 1436 * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to 1437 * @param name the name used to identify this subscription 1438 * @param messageSelector only messages with properties matching the message 1439 * selector expression are delivered. A value of null or an 1440 * empty string indicates that there is no message selector 1441 * for the message consumer. 1442 * @param noLocal if set, inhibits the delivery of messages published by its 1443 * own connection 1444 * @return the Queue Browser 1445 * @throws JMSException if the session fails to create a subscriber due to 1446 * some internal error. 1447 * @throws InvalidDestinationException if an invalid topic is specified. 1448 * @throws InvalidSelectorException if the message selector is invalid. 1449 * @since 1.1 1450 */ 1451 @Override 1452 public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException { 1453 checkClosed(); 1454 1455 if (topic == null) { 1456 throw new InvalidDestinationException("Topic cannot be null"); 1457 } 1458 1459 if (topic instanceof CustomDestination) { 1460 CustomDestination customDestination = (CustomDestination)topic; 1461 return customDestination.createDurableSubscriber(this, name, messageSelector, noLocal); 1462 } 1463 1464 connection.checkClientIDWasManuallySpecified(); 1465 ActiveMQPrefetchPolicy prefetchPolicy = this.connection.getPrefetchPolicy(); 1466 int prefetch = isAutoAcknowledge() && connection.isOptimizedMessageDispatch() ? prefetchPolicy.getOptimizeDurableTopicPrefetch() : prefetchPolicy.getDurableTopicPrefetch(); 1467 int maxPrendingLimit = prefetchPolicy.getMaximumPendingMessageLimit(); 1468 return new ActiveMQTopicSubscriber(this, getNextConsumerId(), ActiveMQMessageTransformation.transformDestination(topic), name, messageSelector, prefetch, maxPrendingLimit, 1469 noLocal, false, asyncDispatch); 1470 } 1471 1472 /** 1473 * Creates a <CODE>QueueBrowser</CODE> object to peek at the messages on 1474 * the specified queue. 1475 * 1476 * @param queue the <CODE>queue</CODE> to access 1477 * @return the Queue Browser 1478 * @throws JMSException if the session fails to create a browser due to some 1479 * internal error. 1480 * @throws InvalidDestinationException if an invalid destination is 1481 * specified 1482 * @since 1.1 1483 */ 1484 @Override 1485 public QueueBrowser createBrowser(Queue queue) throws JMSException { 1486 checkClosed(); 1487 return createBrowser(queue, null); 1488 } 1489 1490 /** 1491 * Creates a <CODE>QueueBrowser</CODE> object to peek at the messages on 1492 * the specified queue using a message selector. 1493 * 1494 * @param queue the <CODE>queue</CODE> to access 1495 * @param messageSelector only messages with properties matching the message 1496 * selector expression are delivered. A value of null or an 1497 * empty string indicates that there is no message selector 1498 * for the message consumer. 1499 * @return the Queue Browser 1500 * @throws JMSException if the session fails to create a browser due to some 1501 * internal error. 1502 * @throws InvalidDestinationException if an invalid destination is 1503 * specified 1504 * @throws InvalidSelectorException if the message selector is invalid. 1505 * @since 1.1 1506 */ 1507 @Override 1508 public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException { 1509 checkClosed(); 1510 return new ActiveMQQueueBrowser(this, getNextConsumerId(), ActiveMQMessageTransformation.transformDestination(queue), messageSelector, asyncDispatch); 1511 } 1512 1513 /** 1514 * Creates a <CODE>TemporaryQueue</CODE> object. Its lifetime will be that 1515 * of the <CODE>Connection</CODE> unless it is deleted earlier. 1516 * 1517 * @return a temporary queue identity 1518 * @throws JMSException if the session fails to create a temporary queue due 1519 * to some internal error. 1520 * @since 1.1 1521 */ 1522 @Override 1523 public TemporaryQueue createTemporaryQueue() throws JMSException { 1524 checkClosed(); 1525 return (TemporaryQueue)connection.createTempDestination(false); 1526 } 1527 1528 /** 1529 * Creates a <CODE>TemporaryTopic</CODE> object. Its lifetime will be that 1530 * of the <CODE>Connection</CODE> unless it is deleted earlier. 1531 * 1532 * @return a temporary topic identity 1533 * @throws JMSException if the session fails to create a temporary topic due 1534 * to some internal error. 1535 * @since 1.1 1536 */ 1537 @Override 1538 public TemporaryTopic createTemporaryTopic() throws JMSException { 1539 checkClosed(); 1540 return (TemporaryTopic)connection.createTempDestination(true); 1541 } 1542 1543 /** 1544 * Creates a <CODE>QueueReceiver</CODE> object to receive messages from 1545 * the specified queue. 1546 * 1547 * @param queue the <CODE>Queue</CODE> to access 1548 * @return 1549 * @throws JMSException if the session fails to create a receiver due to 1550 * some internal error. 1551 * @throws JMSException 1552 * @throws InvalidDestinationException if an invalid queue is specified. 1553 */ 1554 @Override 1555 public QueueReceiver createReceiver(Queue queue) throws JMSException { 1556 checkClosed(); 1557 return createReceiver(queue, null); 1558 } 1559 1560 /** 1561 * Creates a <CODE>QueueReceiver</CODE> object to receive messages from 1562 * the specified queue using a message selector. 1563 * 1564 * @param queue the <CODE>Queue</CODE> to access 1565 * @param messageSelector only messages with properties matching the message 1566 * selector expression are delivered. A value of null or an 1567 * empty string indicates that there is no message selector 1568 * for the message consumer. 1569 * @return QueueReceiver 1570 * @throws JMSException if the session fails to create a receiver due to 1571 * some internal error. 1572 * @throws InvalidDestinationException if an invalid queue is specified. 1573 * @throws InvalidSelectorException if the message selector is invalid. 1574 */ 1575 @Override 1576 public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { 1577 checkClosed(); 1578 1579 if (queue instanceof CustomDestination) { 1580 CustomDestination customDestination = (CustomDestination)queue; 1581 return customDestination.createReceiver(this, messageSelector); 1582 } 1583 1584 ActiveMQPrefetchPolicy prefetchPolicy = this.connection.getPrefetchPolicy(); 1585 return new ActiveMQQueueReceiver(this, getNextConsumerId(), ActiveMQMessageTransformation.transformDestination(queue), messageSelector, prefetchPolicy.getQueuePrefetch(), 1586 prefetchPolicy.getMaximumPendingMessageLimit(), asyncDispatch); 1587 } 1588 1589 /** 1590 * Creates a <CODE>QueueSender</CODE> object to send messages to the 1591 * specified queue. 1592 * 1593 * @param queue the <CODE>Queue</CODE> to access, or null if this is an 1594 * unidentified producer 1595 * @return QueueSender 1596 * @throws JMSException if the session fails to create a sender due to some 1597 * internal error. 1598 * @throws InvalidDestinationException if an invalid queue is specified. 1599 */ 1600 @Override 1601 public QueueSender createSender(Queue queue) throws JMSException { 1602 checkClosed(); 1603 if (queue instanceof CustomDestination) { 1604 CustomDestination customDestination = (CustomDestination)queue; 1605 return customDestination.createSender(this); 1606 } 1607 int timeSendOut = connection.getSendTimeout(); 1608 return new ActiveMQQueueSender(this, ActiveMQMessageTransformation.transformDestination(queue),timeSendOut); 1609 } 1610 1611 /** 1612 * Creates a nondurable subscriber to the specified topic. <p/> 1613 * <P> 1614 * A client uses a <CODE>TopicSubscriber</CODE> object to receive messages 1615 * that have been published to a topic. <p/> 1616 * <P> 1617 * Regular <CODE>TopicSubscriber</CODE> objects are not durable. They 1618 * receive only messages that are published while they are active. <p/> 1619 * <P> 1620 * In some cases, a connection may both publish and subscribe to a topic. 1621 * The subscriber <CODE>NoLocal</CODE> attribute allows a subscriber to 1622 * inhibit the delivery of messages published by its own connection. The 1623 * default value for this attribute is false. 1624 * 1625 * @param topic the <CODE>Topic</CODE> to subscribe to 1626 * @return TopicSubscriber 1627 * @throws JMSException if the session fails to create a subscriber due to 1628 * some internal error. 1629 * @throws InvalidDestinationException if an invalid topic is specified. 1630 */ 1631 @Override 1632 public TopicSubscriber createSubscriber(Topic topic) throws JMSException { 1633 checkClosed(); 1634 return createSubscriber(topic, null, false); 1635 } 1636 1637 /** 1638 * Creates a nondurable subscriber to the specified topic, using a message 1639 * selector or specifying whether messages published by its own connection 1640 * should be delivered to it. <p/> 1641 * <P> 1642 * A client uses a <CODE>TopicSubscriber</CODE> object to receive messages 1643 * that have been published to a topic. <p/> 1644 * <P> 1645 * Regular <CODE>TopicSubscriber</CODE> objects are not durable. They 1646 * receive only messages that are published while they are active. <p/> 1647 * <P> 1648 * Messages filtered out by a subscriber's message selector will never be 1649 * delivered to the subscriber. From the subscriber's perspective, they do 1650 * not exist. <p/> 1651 * <P> 1652 * In some cases, a connection may both publish and subscribe to a topic. 1653 * The subscriber <CODE>NoLocal</CODE> attribute allows a subscriber to 1654 * inhibit the delivery of messages published by its own connection. The 1655 * default value for this attribute is false. 1656 * 1657 * @param topic the <CODE>Topic</CODE> to subscribe to 1658 * @param messageSelector only messages with properties matching the message 1659 * selector expression are delivered. A value of null or an 1660 * empty string indicates that there is no message selector 1661 * for the message consumer. 1662 * @param noLocal if set, inhibits the delivery of messages published by its 1663 * own connection 1664 * @return TopicSubscriber 1665 * @throws JMSException if the session fails to create a subscriber due to 1666 * some internal error. 1667 * @throws InvalidDestinationException if an invalid topic is specified. 1668 * @throws InvalidSelectorException if the message selector is invalid. 1669 */ 1670 @Override 1671 public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException { 1672 checkClosed(); 1673 1674 if (topic instanceof CustomDestination) { 1675 CustomDestination customDestination = (CustomDestination)topic; 1676 return customDestination.createSubscriber(this, messageSelector, noLocal); 1677 } 1678 1679 ActiveMQPrefetchPolicy prefetchPolicy = this.connection.getPrefetchPolicy(); 1680 return new ActiveMQTopicSubscriber(this, getNextConsumerId(), ActiveMQMessageTransformation.transformDestination(topic), null, messageSelector, prefetchPolicy 1681 .getTopicPrefetch(), prefetchPolicy.getMaximumPendingMessageLimit(), noLocal, false, asyncDispatch); 1682 } 1683 1684 /** 1685 * Creates a publisher for the specified topic. <p/> 1686 * <P> 1687 * A client uses a <CODE>TopicPublisher</CODE> object to publish messages 1688 * on a topic. Each time a client creates a <CODE>TopicPublisher</CODE> on 1689 * a topic, it defines a new sequence of messages that have no ordering 1690 * relationship with the messages it has previously sent. 1691 * 1692 * @param topic the <CODE>Topic</CODE> to publish to, or null if this is 1693 * an unidentified producer 1694 * @return TopicPublisher 1695 * @throws JMSException if the session fails to create a publisher due to 1696 * some internal error. 1697 * @throws InvalidDestinationException if an invalid topic is specified. 1698 */ 1699 @Override 1700 public TopicPublisher createPublisher(Topic topic) throws JMSException { 1701 checkClosed(); 1702 1703 if (topic instanceof CustomDestination) { 1704 CustomDestination customDestination = (CustomDestination)topic; 1705 return customDestination.createPublisher(this); 1706 } 1707 int timeSendOut = connection.getSendTimeout(); 1708 return new ActiveMQTopicPublisher(this, ActiveMQMessageTransformation.transformDestination(topic),timeSendOut); 1709 } 1710 1711 /** 1712 * Unsubscribes a durable subscription that has been created by a client. 1713 * <P> 1714 * This method deletes the state being maintained on behalf of the 1715 * subscriber by its provider. 1716 * <P> 1717 * It is erroneous for a client to delete a durable subscription while there 1718 * is an active <CODE>MessageConsumer </CODE> or 1719 * <CODE>TopicSubscriber</CODE> for the subscription, or while a consumed 1720 * message is part of a pending transaction or has not been acknowledged in 1721 * the session. 1722 * 1723 * @param name the name used to identify this subscription 1724 * @throws JMSException if the session fails to unsubscribe to the durable 1725 * subscription due to some internal error. 1726 * @throws InvalidDestinationException if an invalid subscription name is 1727 * specified. 1728 * @since 1.1 1729 */ 1730 @Override 1731 public void unsubscribe(String name) throws JMSException { 1732 checkClosed(); 1733 connection.unsubscribe(name); 1734 } 1735 1736 @Override 1737 public void dispatch(MessageDispatch messageDispatch) { 1738 try { 1739 executor.execute(messageDispatch); 1740 } catch (InterruptedException e) { 1741 Thread.currentThread().interrupt(); 1742 connection.onClientInternalException(e); 1743 } 1744 } 1745 1746 /** 1747 * Acknowledges all consumed messages of the session of this consumed 1748 * message. 1749 * <P> 1750 * All consumed JMS messages support the <CODE>acknowledge</CODE> method 1751 * for use when a client has specified that its JMS session's consumed 1752 * messages are to be explicitly acknowledged. By invoking 1753 * <CODE>acknowledge</CODE> on a consumed message, a client acknowledges 1754 * all messages consumed by the session that the message was delivered to. 1755 * <P> 1756 * Calls to <CODE>acknowledge</CODE> are ignored for both transacted 1757 * sessions and sessions specified to use implicit acknowledgement modes. 1758 * <P> 1759 * A client may individually acknowledge each message as it is consumed, or 1760 * it may choose to acknowledge messages as an application-defined group 1761 * (which is done by calling acknowledge on the last received message of the 1762 * group, thereby acknowledging all messages consumed by the session.) 1763 * <P> 1764 * Messages that have been received but not acknowledged may be redelivered. 1765 * 1766 * @throws JMSException if the JMS provider fails to acknowledge the 1767 * messages due to some internal error. 1768 * @throws javax.jms.IllegalStateException if this method is called on a 1769 * closed session. 1770 * @see javax.jms.Session#CLIENT_ACKNOWLEDGE 1771 */ 1772 public void acknowledge() throws JMSException { 1773 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 1774 ActiveMQMessageConsumer c = iter.next(); 1775 c.acknowledge(); 1776 } 1777 } 1778 1779 /** 1780 * Add a message consumer. 1781 * 1782 * @param consumer - message consumer. 1783 * @throws JMSException 1784 */ 1785 protected void addConsumer(ActiveMQMessageConsumer consumer) throws JMSException { 1786 this.consumers.add(consumer); 1787 if (consumer.isDurableSubscriber()) { 1788 stats.onCreateDurableSubscriber(); 1789 } 1790 this.connection.addDispatcher(consumer.getConsumerId(), this); 1791 } 1792 1793 /** 1794 * Remove the message consumer. 1795 * 1796 * @param consumer - consumer to be removed. 1797 * @throws JMSException 1798 */ 1799 protected void removeConsumer(ActiveMQMessageConsumer consumer) { 1800 this.connection.removeDispatcher(consumer.getConsumerId()); 1801 if (consumer.isDurableSubscriber()) { 1802 stats.onRemoveDurableSubscriber(); 1803 } 1804 this.consumers.remove(consumer); 1805 this.connection.removeDispatcher(consumer); 1806 } 1807 1808 /** 1809 * Adds a message producer. 1810 * 1811 * @param producer - message producer to be added. 1812 * @throws JMSException 1813 */ 1814 protected void addProducer(ActiveMQMessageProducer producer) throws JMSException { 1815 this.producers.add(producer); 1816 this.connection.addProducer(producer.getProducerInfo().getProducerId(), producer); 1817 } 1818 1819 /** 1820 * Removes a message producer. 1821 * 1822 * @param producer - message producer to be removed. 1823 * @throws JMSException 1824 */ 1825 protected void removeProducer(ActiveMQMessageProducer producer) { 1826 this.connection.removeProducer(producer.getProducerInfo().getProducerId()); 1827 this.producers.remove(producer); 1828 } 1829 1830 /** 1831 * Start this Session. 1832 * 1833 * @throws JMSException 1834 */ 1835 protected void start() throws JMSException { 1836 started.set(true); 1837 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 1838 ActiveMQMessageConsumer c = iter.next(); 1839 c.start(); 1840 } 1841 executor.start(); 1842 } 1843 1844 /** 1845 * Stops this session. 1846 * 1847 * @throws JMSException 1848 */ 1849 protected void stop() throws JMSException { 1850 1851 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 1852 ActiveMQMessageConsumer c = iter.next(); 1853 c.stop(); 1854 } 1855 1856 started.set(false); 1857 executor.stop(); 1858 } 1859 1860 /** 1861 * Returns the session id. 1862 * 1863 * @return value - session id. 1864 */ 1865 protected SessionId getSessionId() { 1866 return info.getSessionId(); 1867 } 1868 1869 /** 1870 * @return 1871 */ 1872 protected ConsumerId getNextConsumerId() { 1873 return new ConsumerId(info.getSessionId(), consumerIdGenerator.getNextSequenceId()); 1874 } 1875 1876 /** 1877 * @return 1878 */ 1879 protected ProducerId getNextProducerId() { 1880 return new ProducerId(info.getSessionId(), producerIdGenerator.getNextSequenceId()); 1881 } 1882 1883 /** 1884 * Sends the message for dispatch by the broker. 1885 * 1886 * 1887 * @param producer - message producer. 1888 * @param destination - message destination. 1889 * @param message - message to be sent. 1890 * @param deliveryMode - JMS messsage delivery mode. 1891 * @param priority - message priority. 1892 * @param timeToLive - message expiration. 1893 * @param producerWindow 1894 * @param onComplete 1895 * @throws JMSException 1896 */ 1897 protected void send(ActiveMQMessageProducer producer, ActiveMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, 1898 MemoryUsage producerWindow, int sendTimeout, AsyncCallback onComplete) throws JMSException { 1899 1900 checkClosed(); 1901 if (destination.isTemporary() && connection.isDeleted(destination)) { 1902 throw new InvalidDestinationException("Cannot publish to a deleted Destination: " + destination); 1903 } 1904 synchronized (sendMutex) { 1905 // tell the Broker we are about to start a new transaction 1906 doStartTransaction(); 1907 TransactionId txid = transactionContext.getTransactionId(); 1908 long sequenceNumber = producer.getMessageSequence(); 1909 1910 //Set the "JMS" header fields on the original message, see 1.1 spec section 3.4.11 1911 message.setJMSDeliveryMode(deliveryMode); 1912 long expiration = 0L; 1913 if (!producer.getDisableMessageTimestamp()) { 1914 long timeStamp = System.currentTimeMillis(); 1915 message.setJMSTimestamp(timeStamp); 1916 if (timeToLive > 0) { 1917 expiration = timeToLive + timeStamp; 1918 } 1919 } 1920 message.setJMSExpiration(expiration); 1921 message.setJMSPriority(priority); 1922 message.setJMSRedelivered(false); 1923 1924 // transform to our own message format here 1925 ActiveMQMessage msg = ActiveMQMessageTransformation.transformMessage(message, connection); 1926 msg.setDestination(destination); 1927 msg.setMessageId(new MessageId(producer.getProducerInfo().getProducerId(), sequenceNumber)); 1928 1929 // Set the message id. 1930 if (msg != message) { 1931 message.setJMSMessageID(msg.getMessageId().toString()); 1932 // Make sure the JMS destination is set on the foreign messages too. 1933 message.setJMSDestination(destination); 1934 } 1935 //clear the brokerPath in case we are re-sending this message 1936 msg.setBrokerPath(null); 1937 1938 msg.setTransactionId(txid); 1939 if (connection.isCopyMessageOnSend()) { 1940 msg = (ActiveMQMessage)msg.copy(); 1941 } 1942 msg.setConnection(connection); 1943 msg.onSend(); 1944 msg.setProducerId(msg.getMessageId().getProducerId()); 1945 if (LOG.isTraceEnabled()) { 1946 LOG.trace(getSessionId() + " sending message: " + msg); 1947 } 1948 if (onComplete==null && sendTimeout <= 0 && !msg.isResponseRequired() && !connection.isAlwaysSyncSend() && (!msg.isPersistent() || connection.isUseAsyncSend() || txid != null)) { 1949 this.connection.asyncSendPacket(msg); 1950 if (producerWindow != null) { 1951 // Since we defer lots of the marshaling till we hit the 1952 // wire, this might not 1953 // provide and accurate size. We may change over to doing 1954 // more aggressive marshaling, 1955 // to get more accurate sizes.. this is more important once 1956 // users start using producer window 1957 // flow control. 1958 int size = msg.getSize(); 1959 producerWindow.increaseUsage(size); 1960 } 1961 } else { 1962 if (sendTimeout > 0 && onComplete==null) { 1963 this.connection.syncSendPacket(msg,sendTimeout); 1964 }else { 1965 this.connection.syncSendPacket(msg, onComplete); 1966 } 1967 } 1968 1969 } 1970 } 1971 1972 /** 1973 * Send TransactionInfo to indicate transaction has started 1974 * 1975 * @throws JMSException if some internal error occurs 1976 */ 1977 protected void doStartTransaction() throws JMSException { 1978 if (getTransacted() && !transactionContext.isInXATransaction()) { 1979 transactionContext.begin(); 1980 } 1981 } 1982 1983 /** 1984 * Checks whether the session has unconsumed messages. 1985 * 1986 * @return true - if there are unconsumed messages. 1987 */ 1988 public boolean hasUncomsumedMessages() { 1989 return executor.hasUncomsumedMessages(); 1990 } 1991 1992 /** 1993 * Checks whether the session uses transactions. 1994 * 1995 * @return true - if the session uses transactions. 1996 */ 1997 public boolean isTransacted() { 1998 return this.acknowledgementMode == Session.SESSION_TRANSACTED || (transactionContext.isInXATransaction()); 1999 } 2000 2001 /** 2002 * Checks whether the session used client acknowledgment. 2003 * 2004 * @return true - if the session uses client acknowledgment. 2005 */ 2006 protected boolean isClientAcknowledge() { 2007 return this.acknowledgementMode == Session.CLIENT_ACKNOWLEDGE; 2008 } 2009 2010 /** 2011 * Checks whether the session used auto acknowledgment. 2012 * 2013 * @return true - if the session uses client acknowledgment. 2014 */ 2015 public boolean isAutoAcknowledge() { 2016 return acknowledgementMode == Session.AUTO_ACKNOWLEDGE; 2017 } 2018 2019 /** 2020 * Checks whether the session used dup ok acknowledgment. 2021 * 2022 * @return true - if the session uses client acknowledgment. 2023 */ 2024 public boolean isDupsOkAcknowledge() { 2025 return acknowledgementMode == Session.DUPS_OK_ACKNOWLEDGE; 2026 } 2027 2028 public boolean isIndividualAcknowledge(){ 2029 return acknowledgementMode == ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE; 2030 } 2031 2032 /** 2033 * Returns the message delivery listener. 2034 * 2035 * @return deliveryListener - message delivery listener. 2036 */ 2037 public DeliveryListener getDeliveryListener() { 2038 return deliveryListener; 2039 } 2040 2041 /** 2042 * Sets the message delivery listener. 2043 * 2044 * @param deliveryListener - message delivery listener. 2045 */ 2046 public void setDeliveryListener(DeliveryListener deliveryListener) { 2047 this.deliveryListener = deliveryListener; 2048 } 2049 2050 /** 2051 * Returns the SessionInfo bean. 2052 * 2053 * @return info - SessionInfo bean. 2054 * @throws JMSException 2055 */ 2056 protected SessionInfo getSessionInfo() throws JMSException { 2057 SessionInfo info = new SessionInfo(connection.getConnectionInfo(), getSessionId().getValue()); 2058 return info; 2059 } 2060 2061 /** 2062 * Send the asynchronus command. 2063 * 2064 * @param command - command to be executed. 2065 * @throws JMSException 2066 */ 2067 public void asyncSendPacket(Command command) throws JMSException { 2068 connection.asyncSendPacket(command); 2069 } 2070 2071 /** 2072 * Send the synchronus command. 2073 * 2074 * @param command - command to be executed. 2075 * @return Response 2076 * @throws JMSException 2077 */ 2078 public Response syncSendPacket(Command command) throws JMSException { 2079 return connection.syncSendPacket(command); 2080 } 2081 2082 public long getNextDeliveryId() { 2083 return deliveryIdGenerator.getNextSequenceId(); 2084 } 2085 2086 public void redispatch(ActiveMQDispatcher dispatcher, MessageDispatchChannel unconsumedMessages) throws JMSException { 2087 2088 List<MessageDispatch> c = unconsumedMessages.removeAll(); 2089 for (MessageDispatch md : c) { 2090 this.connection.rollbackDuplicate(dispatcher, md.getMessage()); 2091 } 2092 Collections.reverse(c); 2093 2094 for (Iterator<MessageDispatch> iter = c.iterator(); iter.hasNext();) { 2095 MessageDispatch md = iter.next(); 2096 executor.executeFirst(md); 2097 } 2098 2099 } 2100 2101 public boolean isRunning() { 2102 return started.get(); 2103 } 2104 2105 public boolean isAsyncDispatch() { 2106 return asyncDispatch; 2107 } 2108 2109 public void setAsyncDispatch(boolean asyncDispatch) { 2110 this.asyncDispatch = asyncDispatch; 2111 } 2112 2113 /** 2114 * @return Returns the sessionAsyncDispatch. 2115 */ 2116 public boolean isSessionAsyncDispatch() { 2117 return sessionAsyncDispatch; 2118 } 2119 2120 /** 2121 * @param sessionAsyncDispatch The sessionAsyncDispatch to set. 2122 */ 2123 public void setSessionAsyncDispatch(boolean sessionAsyncDispatch) { 2124 this.sessionAsyncDispatch = sessionAsyncDispatch; 2125 } 2126 2127 public MessageTransformer getTransformer() { 2128 return transformer; 2129 } 2130 2131 public ActiveMQConnection getConnection() { 2132 return connection; 2133 } 2134 2135 /** 2136 * Sets the transformer used to transform messages before they are sent on 2137 * to the JMS bus or when they are received from the bus but before they are 2138 * delivered to the JMS client 2139 */ 2140 public void setTransformer(MessageTransformer transformer) { 2141 this.transformer = transformer; 2142 } 2143 2144 public BlobTransferPolicy getBlobTransferPolicy() { 2145 return blobTransferPolicy; 2146 } 2147 2148 /** 2149 * Sets the policy used to describe how out-of-band BLOBs (Binary Large 2150 * OBjects) are transferred from producers to brokers to consumers 2151 */ 2152 public void setBlobTransferPolicy(BlobTransferPolicy blobTransferPolicy) { 2153 this.blobTransferPolicy = blobTransferPolicy; 2154 } 2155 2156 public List<MessageDispatch> getUnconsumedMessages() { 2157 return executor.getUnconsumedMessages(); 2158 } 2159 2160 @Override 2161 public String toString() { 2162 return "ActiveMQSession {id=" + info.getSessionId() + ",started=" + started.get() + "} " + sendMutex; 2163 } 2164 2165 public void checkMessageListener() throws JMSException { 2166 if (messageListener != null) { 2167 throw new IllegalStateException("Cannot synchronously receive a message when a MessageListener is set"); 2168 } 2169 for (Iterator<ActiveMQMessageConsumer> i = consumers.iterator(); i.hasNext();) { 2170 ActiveMQMessageConsumer consumer = i.next(); 2171 if (consumer.hasMessageListener()) { 2172 throw new IllegalStateException("Cannot synchronously receive a message when a MessageListener is set"); 2173 } 2174 } 2175 } 2176 2177 protected void setOptimizeAcknowledge(boolean value) { 2178 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 2179 ActiveMQMessageConsumer c = iter.next(); 2180 c.setOptimizeAcknowledge(value); 2181 } 2182 } 2183 2184 protected void setPrefetchSize(ConsumerId id, int prefetch) { 2185 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 2186 ActiveMQMessageConsumer c = iter.next(); 2187 if (c.getConsumerId().equals(id)) { 2188 c.setPrefetchSize(prefetch); 2189 break; 2190 } 2191 } 2192 } 2193 2194 protected void close(ConsumerId id) { 2195 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 2196 ActiveMQMessageConsumer c = iter.next(); 2197 if (c.getConsumerId().equals(id)) { 2198 try { 2199 c.close(); 2200 } catch (JMSException e) { 2201 LOG.warn("Exception closing consumer", e); 2202 } 2203 LOG.warn("Closed consumer on Command, " + id); 2204 break; 2205 } 2206 } 2207 } 2208 2209 public boolean isInUse(ActiveMQTempDestination destination) { 2210 for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 2211 ActiveMQMessageConsumer c = iter.next(); 2212 if (c.isInUse(destination)) { 2213 return true; 2214 } 2215 } 2216 return false; 2217 } 2218 2219 /** 2220 * highest sequence id of the last message delivered by this session. 2221 * Passed to the broker in the close command, maintained by dispose() 2222 * @return lastDeliveredSequenceId 2223 */ 2224 public long getLastDeliveredSequenceId() { 2225 return lastDeliveredSequenceId; 2226 } 2227 2228 protected void sendAck(MessageAck ack) throws JMSException { 2229 sendAck(ack,false); 2230 } 2231 2232 protected void sendAck(MessageAck ack, boolean lazy) throws JMSException { 2233 if (lazy || connection.isSendAcksAsync() || getTransacted()) { 2234 asyncSendPacket(ack); 2235 } else { 2236 syncSendPacket(ack); 2237 } 2238 } 2239 2240 protected Scheduler getScheduler() throws JMSException { 2241 return this.connection.getScheduler(); 2242 } 2243 2244 protected ThreadPoolExecutor getConnectionExecutor() { 2245 return this.connectionExecutor; 2246 } 2247}