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.jms.pool; 018 019import java.io.Serializable; 020import java.util.Iterator; 021import java.util.concurrent.CopyOnWriteArrayList; 022import java.util.concurrent.atomic.AtomicBoolean; 023 024import javax.jms.BytesMessage; 025import javax.jms.Destination; 026import javax.jms.JMSException; 027import javax.jms.MapMessage; 028import javax.jms.Message; 029import javax.jms.MessageConsumer; 030import javax.jms.MessageListener; 031import javax.jms.MessageProducer; 032import javax.jms.ObjectMessage; 033import javax.jms.Queue; 034import javax.jms.QueueBrowser; 035import javax.jms.QueueReceiver; 036import javax.jms.QueueSender; 037import javax.jms.QueueSession; 038import javax.jms.Session; 039import javax.jms.StreamMessage; 040import javax.jms.TemporaryQueue; 041import javax.jms.TemporaryTopic; 042import javax.jms.TextMessage; 043import javax.jms.Topic; 044import javax.jms.TopicPublisher; 045import javax.jms.TopicSession; 046import javax.jms.TopicSubscriber; 047import javax.jms.XASession; 048import javax.transaction.xa.XAResource; 049 050import org.apache.commons.pool.KeyedObjectPool; 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053 054public class PooledSession implements Session, TopicSession, QueueSession, XASession { 055 private static final transient Logger LOG = LoggerFactory.getLogger(PooledSession.class); 056 057 private final SessionKey key; 058 private final KeyedObjectPool<SessionKey, Session> sessionPool; 059 private final CopyOnWriteArrayList<MessageConsumer> consumers = new CopyOnWriteArrayList<MessageConsumer>(); 060 private final CopyOnWriteArrayList<QueueBrowser> browsers = new CopyOnWriteArrayList<QueueBrowser>(); 061 private final CopyOnWriteArrayList<PooledSessionEventListener> sessionEventListeners = new CopyOnWriteArrayList<PooledSessionEventListener>(); 062 private final AtomicBoolean closed = new AtomicBoolean(); 063 064 private MessageProducer producer; 065 private TopicPublisher publisher; 066 private QueueSender sender; 067 068 private Session session; 069 private boolean transactional = true; 070 private boolean ignoreClose; 071 private boolean isXa; 072 private boolean useAnonymousProducers = true; 073 074 public PooledSession(SessionKey key, Session session, KeyedObjectPool<SessionKey, Session> sessionPool, boolean transactional, boolean anonymous) { 075 this.key = key; 076 this.session = session; 077 this.sessionPool = sessionPool; 078 this.transactional = transactional; 079 this.useAnonymousProducers = anonymous; 080 } 081 082 public void addSessionEventListener(PooledSessionEventListener listener) { 083 // only add if really needed 084 if (!sessionEventListeners.contains(listener)) { 085 this.sessionEventListeners.add(listener); 086 } 087 } 088 089 protected boolean isIgnoreClose() { 090 return ignoreClose; 091 } 092 093 protected void setIgnoreClose(boolean ignoreClose) { 094 this.ignoreClose = ignoreClose; 095 } 096 097 @Override 098 public void close() throws JMSException { 099 if (ignoreClose) { 100 return; 101 } 102 103 if (closed.compareAndSet(false, true)) { 104 boolean invalidate = false; 105 try { 106 // lets reset the session 107 getInternalSession().setMessageListener(null); 108 109 // Close any consumers and browsers that may have been created. 110 for (Iterator<MessageConsumer> iter = consumers.iterator(); iter.hasNext();) { 111 MessageConsumer consumer = iter.next(); 112 consumer.close(); 113 } 114 115 for (Iterator<QueueBrowser> iter = browsers.iterator(); iter.hasNext();) { 116 QueueBrowser browser = iter.next(); 117 browser.close(); 118 } 119 120 if (transactional && !isXa) { 121 try { 122 getInternalSession().rollback(); 123 } catch (JMSException e) { 124 invalidate = true; 125 LOG.warn("Caught exception trying rollback() when putting session back into the pool, will invalidate. " + e, e); 126 } 127 } 128 } catch (JMSException ex) { 129 invalidate = true; 130 LOG.warn("Caught exception trying close() when putting session back into the pool, will invalidate. " + ex, ex); 131 } finally { 132 consumers.clear(); 133 browsers.clear(); 134 for (PooledSessionEventListener listener : this.sessionEventListeners) { 135 listener.onSessionClosed(this); 136 } 137 sessionEventListeners.clear(); 138 } 139 140 if (invalidate) { 141 // lets close the session and not put the session back into the pool 142 // instead invalidate it so the pool can create a new one on demand. 143 if (session != null) { 144 try { 145 session.close(); 146 } catch (JMSException e1) { 147 LOG.trace("Ignoring exception on close as discarding session: " + e1, e1); 148 } 149 } 150 try { 151 sessionPool.invalidateObject(key, session); 152 } catch (Exception e) { 153 LOG.trace("Ignoring exception on invalidateObject as discarding session: " + e, e); 154 } 155 } else { 156 try { 157 sessionPool.returnObject(key, session); 158 } catch (Exception e) { 159 javax.jms.IllegalStateException illegalStateException = new javax.jms.IllegalStateException(e.toString()); 160 illegalStateException.initCause(e); 161 throw illegalStateException; 162 } 163 } 164 165 session = null; 166 } 167 } 168 169 @Override 170 public void commit() throws JMSException { 171 getInternalSession().commit(); 172 } 173 174 @Override 175 public BytesMessage createBytesMessage() throws JMSException { 176 return getInternalSession().createBytesMessage(); 177 } 178 179 @Override 180 public MapMessage createMapMessage() throws JMSException { 181 return getInternalSession().createMapMessage(); 182 } 183 184 @Override 185 public Message createMessage() throws JMSException { 186 return getInternalSession().createMessage(); 187 } 188 189 @Override 190 public ObjectMessage createObjectMessage() throws JMSException { 191 return getInternalSession().createObjectMessage(); 192 } 193 194 @Override 195 public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException { 196 return getInternalSession().createObjectMessage(serializable); 197 } 198 199 @Override 200 public Queue createQueue(String s) throws JMSException { 201 return getInternalSession().createQueue(s); 202 } 203 204 @Override 205 public StreamMessage createStreamMessage() throws JMSException { 206 return getInternalSession().createStreamMessage(); 207 } 208 209 @Override 210 public TemporaryQueue createTemporaryQueue() throws JMSException { 211 TemporaryQueue result; 212 213 result = getInternalSession().createTemporaryQueue(); 214 215 // Notify all of the listeners of the created temporary Queue. 216 for (PooledSessionEventListener listener : this.sessionEventListeners) { 217 listener.onTemporaryQueueCreate(result); 218 } 219 220 return result; 221 } 222 223 @Override 224 public TemporaryTopic createTemporaryTopic() throws JMSException { 225 TemporaryTopic result; 226 227 result = getInternalSession().createTemporaryTopic(); 228 229 // Notify all of the listeners of the created temporary Topic. 230 for (PooledSessionEventListener listener : this.sessionEventListeners) { 231 listener.onTemporaryTopicCreate(result); 232 } 233 234 return result; 235 } 236 237 @Override 238 public void unsubscribe(String s) throws JMSException { 239 getInternalSession().unsubscribe(s); 240 } 241 242 @Override 243 public TextMessage createTextMessage() throws JMSException { 244 return getInternalSession().createTextMessage(); 245 } 246 247 @Override 248 public TextMessage createTextMessage(String s) throws JMSException { 249 return getInternalSession().createTextMessage(s); 250 } 251 252 @Override 253 public Topic createTopic(String s) throws JMSException { 254 return getInternalSession().createTopic(s); 255 } 256 257 @Override 258 public int getAcknowledgeMode() throws JMSException { 259 return getInternalSession().getAcknowledgeMode(); 260 } 261 262 @Override 263 public boolean getTransacted() throws JMSException { 264 return getInternalSession().getTransacted(); 265 } 266 267 @Override 268 public void recover() throws JMSException { 269 getInternalSession().recover(); 270 } 271 272 @Override 273 public void rollback() throws JMSException { 274 getInternalSession().rollback(); 275 } 276 277 @Override 278 public XAResource getXAResource() { 279 if (session instanceof XASession) { 280 return ((XASession) session).getXAResource(); 281 } 282 return null; 283 } 284 285 @Override 286 public Session getSession() { 287 return this; 288 } 289 290 @Override 291 public void run() { 292 if (session != null) { 293 session.run(); 294 } 295 } 296 297 // Consumer related methods 298 // ------------------------------------------------------------------------- 299 @Override 300 public QueueBrowser createBrowser(Queue queue) throws JMSException { 301 return addQueueBrowser(getInternalSession().createBrowser(queue)); 302 } 303 304 @Override 305 public QueueBrowser createBrowser(Queue queue, String selector) throws JMSException { 306 return addQueueBrowser(getInternalSession().createBrowser(queue, selector)); 307 } 308 309 @Override 310 public MessageConsumer createConsumer(Destination destination) throws JMSException { 311 return addConsumer(getInternalSession().createConsumer(destination)); 312 } 313 314 @Override 315 public MessageConsumer createConsumer(Destination destination, String selector) throws JMSException { 316 return addConsumer(getInternalSession().createConsumer(destination, selector)); 317 } 318 319 @Override 320 public MessageConsumer createConsumer(Destination destination, String selector, boolean noLocal) throws JMSException { 321 return addConsumer(getInternalSession().createConsumer(destination, selector, noLocal)); 322 } 323 324 @Override 325 public TopicSubscriber createDurableSubscriber(Topic topic, String selector) throws JMSException { 326 return addTopicSubscriber(getInternalSession().createDurableSubscriber(topic, selector)); 327 } 328 329 @Override 330 public TopicSubscriber createDurableSubscriber(Topic topic, String name, String selector, boolean noLocal) throws JMSException { 331 return addTopicSubscriber(getInternalSession().createDurableSubscriber(topic, name, selector, noLocal)); 332 } 333 334 @Override 335 public MessageListener getMessageListener() throws JMSException { 336 return getInternalSession().getMessageListener(); 337 } 338 339 @Override 340 public void setMessageListener(MessageListener messageListener) throws JMSException { 341 getInternalSession().setMessageListener(messageListener); 342 } 343 344 @Override 345 public TopicSubscriber createSubscriber(Topic topic) throws JMSException { 346 return addTopicSubscriber(((TopicSession) getInternalSession()).createSubscriber(topic)); 347 } 348 349 @Override 350 public TopicSubscriber createSubscriber(Topic topic, String selector, boolean local) throws JMSException { 351 return addTopicSubscriber(((TopicSession) getInternalSession()).createSubscriber(topic, selector, local)); 352 } 353 354 @Override 355 public QueueReceiver createReceiver(Queue queue) throws JMSException { 356 return addQueueReceiver(((QueueSession) getInternalSession()).createReceiver(queue)); 357 } 358 359 @Override 360 public QueueReceiver createReceiver(Queue queue, String selector) throws JMSException { 361 return addQueueReceiver(((QueueSession) getInternalSession()).createReceiver(queue, selector)); 362 } 363 364 // Producer related methods 365 // ------------------------------------------------------------------------- 366 @Override 367 public MessageProducer createProducer(Destination destination) throws JMSException { 368 return new PooledProducer(getMessageProducer(destination), destination); 369 } 370 371 @Override 372 public QueueSender createSender(Queue queue) throws JMSException { 373 return new PooledQueueSender(getQueueSender(queue), queue); 374 } 375 376 @Override 377 public TopicPublisher createPublisher(Topic topic) throws JMSException { 378 return new PooledTopicPublisher(getTopicPublisher(topic), topic); 379 } 380 381 public Session getInternalSession() throws IllegalStateException { 382 if (session == null) { 383 throw new IllegalStateException("The session has already been closed"); 384 } 385 return session; 386 } 387 388 public MessageProducer getMessageProducer() throws JMSException { 389 return getMessageProducer(null); 390 } 391 392 public MessageProducer getMessageProducer(Destination destination) throws JMSException { 393 MessageProducer result = null; 394 395 if (useAnonymousProducers) { 396 if (producer == null) { 397 // Don't allow for duplicate anonymous producers. 398 synchronized (this) { 399 if (producer == null) { 400 producer = getInternalSession().createProducer(null); 401 } 402 } 403 } 404 405 result = producer; 406 } else { 407 result = getInternalSession().createProducer(destination); 408 } 409 410 return result; 411 } 412 413 public QueueSender getQueueSender() throws JMSException { 414 return getQueueSender(null); 415 } 416 417 public QueueSender getQueueSender(Queue destination) throws JMSException { 418 QueueSender result = null; 419 420 if (useAnonymousProducers) { 421 if (sender == null) { 422 // Don't allow for duplicate anonymous producers. 423 synchronized (this) { 424 if (sender == null) { 425 sender = ((QueueSession) getInternalSession()).createSender(null); 426 } 427 } 428 } 429 430 result = sender; 431 } else { 432 result = ((QueueSession) getInternalSession()).createSender(destination); 433 } 434 435 return result; 436 } 437 438 public TopicPublisher getTopicPublisher() throws JMSException { 439 return getTopicPublisher(null); 440 } 441 442 public TopicPublisher getTopicPublisher(Topic destination) throws JMSException { 443 TopicPublisher result = null; 444 445 if (useAnonymousProducers) { 446 if (publisher == null) { 447 // Don't allow for duplicate anonymous producers. 448 synchronized (this) { 449 if (publisher == null) { 450 publisher = ((TopicSession) getInternalSession()).createPublisher(null); 451 } 452 } 453 } 454 455 result = publisher; 456 } else { 457 result = ((TopicSession) getInternalSession()).createPublisher(destination); 458 } 459 460 return result; 461 } 462 463 private QueueBrowser addQueueBrowser(QueueBrowser browser) { 464 browsers.add(browser); 465 return browser; 466 } 467 468 private MessageConsumer addConsumer(MessageConsumer consumer) { 469 consumers.add(consumer); 470 // must wrap in PooledMessageConsumer to ensure the onConsumerClose 471 // method is invoked when the returned consumer is closed, to avoid memory 472 // leak in this session class in case many consumers is created 473 return new PooledMessageConsumer(this, consumer); 474 } 475 476 private TopicSubscriber addTopicSubscriber(TopicSubscriber subscriber) { 477 consumers.add(subscriber); 478 return subscriber; 479 } 480 481 private QueueReceiver addQueueReceiver(QueueReceiver receiver) { 482 consumers.add(receiver); 483 return receiver; 484 } 485 486 public void setIsXa(boolean isXa) { 487 this.isXa = isXa; 488 } 489 490 @Override 491 public String toString() { 492 return "PooledSession { " + session + " }"; 493 } 494 495 /** 496 * Callback invoked when the consumer is closed. 497 * <p/> 498 * This is used to keep track of an explicit closed consumer created by this 499 * session, by which we know do not need to keep track of the consumer, as 500 * its already closed. 501 * 502 * @param consumer 503 * the consumer which is being closed 504 */ 505 protected void onConsumerClose(MessageConsumer consumer) { 506 consumers.remove(consumer); 507 } 508}