1 16 17 package org.springframework.jms.connection; 18 19 import java.lang.reflect.InvocationHandler ; 20 import java.lang.reflect.InvocationTargetException ; 21 import java.lang.reflect.Method ; 22 import java.lang.reflect.Proxy ; 23 import java.util.ArrayList ; 24 import java.util.List ; 25 26 import javax.jms.Connection ; 27 import javax.jms.ConnectionFactory ; 28 import javax.jms.ExceptionListener ; 29 import javax.jms.JMSException ; 30 import javax.jms.QueueConnection ; 31 import javax.jms.QueueConnectionFactory ; 32 import javax.jms.TopicConnection ; 33 import javax.jms.TopicConnectionFactory ; 34 35 import org.apache.commons.logging.Log; 36 import org.apache.commons.logging.LogFactory; 37 38 import org.springframework.beans.factory.DisposableBean; 39 import org.springframework.beans.factory.InitializingBean; 40 import org.springframework.util.Assert; 41 42 65 public class SingleConnectionFactory 66 implements ConnectionFactory , QueueConnectionFactory , TopicConnectionFactory , ExceptionListener , 67 InitializingBean, DisposableBean { 68 69 protected final Log logger = LogFactory.getLog(getClass()); 70 71 private ConnectionFactory targetConnectionFactory; 72 73 private String clientId; 74 75 private ExceptionListener exceptionListener; 76 77 private boolean reconnectOnException = false; 78 79 80 private Connection target; 81 82 83 private Connection connection; 84 85 86 private final Object connectionMonitor = new Object (); 87 88 89 93 public SingleConnectionFactory() { 94 } 95 96 101 public SingleConnectionFactory(Connection target) { 102 Assert.notNull(target, "Target Connection must not be null"); 103 this.target = target; 104 this.connection = getSharedConnectionProxy(target); 105 } 106 107 113 public SingleConnectionFactory(ConnectionFactory targetConnectionFactory) { 114 Assert.notNull(targetConnectionFactory, "Target ConnectionFactory must not be null"); 115 this.targetConnectionFactory = targetConnectionFactory; 116 } 117 118 119 123 public void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) { 124 this.targetConnectionFactory = targetConnectionFactory; 125 } 126 127 131 public ConnectionFactory getTargetConnectionFactory() { 132 return this.targetConnectionFactory; 133 } 134 135 144 public void setClientId(String clientId) { 145 this.clientId = clientId; 146 } 147 148 152 protected String getClientId() { 153 return this.clientId; 154 } 155 156 161 public void setExceptionListener(ExceptionListener exceptionListener) { 162 this.exceptionListener = exceptionListener; 163 } 164 165 169 protected ExceptionListener getExceptionListener() { 170 return this.exceptionListener; 171 } 172 173 184 public void setReconnectOnException(boolean reconnectOnException) { 185 this.reconnectOnException = reconnectOnException; 186 } 187 188 192 protected boolean isReconnectOnException() { 193 return this.reconnectOnException; 194 } 195 196 199 public void afterPropertiesSet() { 200 if (this.connection == null && getTargetConnectionFactory() == null) { 201 throw new IllegalArgumentException ("Connection or 'targetConnectionFactory' is required"); 202 } 203 } 204 205 206 public Connection createConnection() throws JMSException { 207 synchronized (this.connectionMonitor) { 208 if (this.connection == null) { 209 initConnection(); 210 } 211 return this.connection; 212 } 213 } 214 215 public Connection createConnection(String username, String password) throws JMSException { 216 throw new javax.jms.IllegalStateException ( 217 "SingleConnectionFactory does not support custom username and password"); 218 } 219 220 public QueueConnection createQueueConnection() throws JMSException { 221 Connection con = createConnection(); 222 if (!(con instanceof QueueConnection )) { 223 throw new javax.jms.IllegalStateException ( 224 "This SingleConnectionFactory does not hold a QueueConnection but rather: " + con); 225 } 226 return ((QueueConnection ) con); 227 } 228 229 public QueueConnection createQueueConnection(String username, String password) throws JMSException { 230 throw new javax.jms.IllegalStateException ( 231 "SingleConnectionFactory does not support custom username and password"); 232 } 233 234 public TopicConnection createTopicConnection() throws JMSException { 235 Connection con = createConnection(); 236 if (!(con instanceof TopicConnection )) { 237 throw new javax.jms.IllegalStateException ( 238 "This SingleConnectionFactory does not hold a TopicConnection but rather: " + con); 239 } 240 return ((TopicConnection ) con); 241 } 242 243 public TopicConnection createTopicConnection(String username, String password) throws JMSException { 244 throw new javax.jms.IllegalStateException ( 245 "SingleConnectionFactory does not support custom username and password"); 246 } 247 248 251 public void onException(JMSException ex) { 252 resetConnection(); 253 } 254 255 261 public void destroy() { 262 resetConnection(); 263 } 264 265 266 272 public void initConnection() throws JMSException { 273 if (getTargetConnectionFactory() == null) { 274 throw new IllegalStateException ( 275 "'targetConnectionFactory' is required for lazily initializing a Connection"); 276 } 277 synchronized (this.connectionMonitor) { 278 if (this.target != null) { 279 closeConnection(this.target); 280 } 281 this.target = doCreateConnection(); 282 prepareConnection(this.target); 283 if (logger.isInfoEnabled()) { 284 logger.info("Established shared JMS Connection: " + this.target); 285 } 286 this.connection = getSharedConnectionProxy(this.target); 287 } 288 } 289 290 293 public void resetConnection() { 294 synchronized (this.connectionMonitor) { 295 if (this.target != null) { 296 closeConnection(this.target); 297 } 298 this.target = null; 299 this.connection = null; 300 } 301 } 302 303 309 protected Connection doCreateConnection() throws JMSException { 310 return getTargetConnectionFactory().createConnection(); 311 } 312 313 322 protected void prepareConnection(Connection con) throws JMSException { 323 if (getExceptionListener() != null || isReconnectOnException()) { 324 ExceptionListener listenerToUse = getExceptionListener(); 325 if (isReconnectOnException()) { 326 listenerToUse = new InternalChainedExceptionListener(this, listenerToUse); 327 } 328 con.setExceptionListener(listenerToUse); 329 } 330 if (getClientId() != null) { 331 con.setClientID(getClientId()); 332 } 333 } 334 335 339 protected void closeConnection(Connection con) { 340 try { 341 try { 342 con.stop(); 343 } 344 finally { 345 con.close(); 346 } 347 } 348 catch (Throwable ex) { 349 logger.warn("Could not close shared JMS Connection", ex); 350 } 351 } 352 353 361 protected Connection getSharedConnectionProxy(Connection target) { 362 List classes = new ArrayList (3); 363 classes.add(Connection .class); 364 if (target instanceof QueueConnection ) { 365 classes.add(QueueConnection .class); 366 } 367 if (target instanceof TopicConnection ) { 368 classes.add(TopicConnection .class); 369 } 370 return (Connection ) Proxy.newProxyInstance( 371 getClass().getClassLoader(), 372 (Class []) classes.toArray(new Class [classes.size()]), 373 new SharedConnectionInvocationHandler(target)); 374 } 375 376 377 380 private static class SharedConnectionInvocationHandler implements InvocationHandler { 381 382 private final Connection target; 383 384 private SharedConnectionInvocationHandler(Connection target) { 385 this.target = target; 386 } 387 388 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 389 if (method.getName().equals("equals")) { 390 return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); 392 } 393 else if (method.getName().equals("hashCode")) { 394 return new Integer (hashCode()); 396 } 397 else if (method.getName().equals("setExceptionListener")) { 398 throw new javax.jms.IllegalStateException ( 400 "setExceptionListener call not supported on proxy for shared Connection. " + 401 "Set the 'exceptionListener' property on the SingleConnectionFactory instead."); 402 } 403 else if (method.getName().equals("setClientID")) { 404 throw new javax.jms.IllegalStateException ( 406 "setClientID call not supported on proxy for shared Connection. " + 407 "Set the 'clientId' property on the SingleConnectionFactory instead."); 408 } 409 else if (method.getName().equals("stop")) { 410 return null; 412 } 413 else if (method.getName().equals("close")) { 414 return null; 416 } 417 try { 418 Object retVal = method.invoke(this.target, args); 419 if (method.getName().equals("getExceptionListener") && retVal instanceof InternalChainedExceptionListener) { 420 InternalChainedExceptionListener listener = (InternalChainedExceptionListener) retVal; 422 return listener.getUserListener(); 423 } 424 else { 425 return retVal; 426 } 427 } 428 catch (InvocationTargetException ex) { 429 throw ex.getTargetException(); 430 } 431 } 432 } 433 434 435 439 private static class InternalChainedExceptionListener extends ChainedExceptionListener { 440 441 public InternalChainedExceptionListener(ExceptionListener internalListener, ExceptionListener userListener) { 442 addDelegate(internalListener); 443 if (userListener != null) { 444 addDelegate(userListener); 445 } 446 } 447 448 public ExceptionListener getUserListener() { 449 ExceptionListener [] delegates = getDelegates(); 450 return (delegates.length > 1 ? delegates[1] : null); 451 } 452 } 453 454 } 455 | Popular Tags |