Java tutorial
/** * Copyright (C) 2000-2010 Atomikos <info@atomikos.com> * * This code ("Atomikos TransactionsEssentials"), by itself, * is being distributed under the * Apache License, Version 2.0 ("License"), a copy of which may be found at * http://www.atomikos.com/licenses/apache-license-2.0.txt . * You may not use this file except in compliance with the License. * * While the License grants certain patent license rights, * those patent license rights only extend to the use of * Atomikos TransactionsEssentials by itself. * * This code (Atomikos TransactionsEssentials) contains certain interfaces * in package (namespace) com.atomikos.icatch * (including com.atomikos.icatch.Participant) which, if implemented, may * infringe one or more patents held by Atomikos. * It should be appreciated that you may NOT implement such interfaces; * licensing to implement these interfaces must be obtained separately from Atomikos. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ package com.atomikos.jms; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.jms.JMSException; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TopicSubscriber; import javax.jms.XASession; import com.atomikos.beans.PropertyUtils; import com.atomikos.datasource.pool.Reapable; import com.atomikos.datasource.xa.XATransactionalResource; import com.atomikos.datasource.xa.session.SessionHandleState; import com.atomikos.datasource.xa.session.SessionHandleStateChangeListener; import com.atomikos.icatch.CompositeTransaction; import com.atomikos.logging.Logger; import com.atomikos.logging.LoggerFactory; import com.atomikos.util.ClassLoadingHelper; import com.atomikos.util.DynamicProxy; class AtomikosJmsXaSessionProxy extends AbstractJmsSessionProxy implements SessionHandleStateChangeListener { private static final Logger LOGGER = LoggerFactory.createLogger(AtomikosJmsXaSessionProxy.class); private final static List PRODUCER_CONSUMER_METHODS = Arrays .asList(new String[] { "createConsumer", "createProducer", "createDurableSubscriber" }); private final static List SESSION_TRANSACTION_METHODS = Arrays.asList(new String[] { "commit", "rollback" }); private final static String CLOSE_METHOD = "close"; public static Object newInstance(XASession s, XATransactionalResource jmsTransactionalResource, SessionHandleStateChangeListener pooledConnection, SessionHandleStateChangeListener connectionProxy) throws JMSException { AtomikosJmsXaSessionProxy proxy = new AtomikosJmsXaSessionProxy(s, jmsTransactionalResource, pooledConnection, connectionProxy); Set<Class> interfaces = PropertyUtils.getAllImplementedInterfaces(s.getClass()); //see case 24532 interfaces.add(DynamicProxy.class); Class[] interfaceClasses = (Class[]) interfaces.toArray(new Class[0]); Set<Class> minimumSetOfInterfaces = new HashSet<Class>(); minimumSetOfInterfaces.add(Reapable.class); minimumSetOfInterfaces.add(DynamicProxy.class); minimumSetOfInterfaces.add(javax.jms.Session.class); Class[] minimumSetOfInterfaceClasses = (Class[]) minimumSetOfInterfaces.toArray(new Class[0]); List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(); classLoaders.add(Thread.currentThread().getContextClassLoader()); classLoaders.add(s.getClass().getClassLoader()); classLoaders.add(AtomikosJmsXaSessionProxy.class.getClassLoader()); return (Session) ClassLoadingHelper.newProxyInstance(classLoaders, minimumSetOfInterfaceClasses, interfaceClasses, proxy); } private XASession delegate; private boolean closed = false; private SessionHandleState state; private XATransactionalResource jmsTransactionalResource; private AtomikosJmsXaSessionProxy(XASession s, XATransactionalResource jmsTransactionalResource, SessionHandleStateChangeListener pooledConnection, SessionHandleStateChangeListener connectionProxy) { this.delegate = s; this.jmsTransactionalResource = jmsTransactionalResource; this.state = new SessionHandleState(jmsTransactionalResource, s.getXAResource()); state.registerSessionHandleStateChangeListener(pooledConnection); state.registerSessionHandleStateChangeListener(connectionProxy); state.registerSessionHandleStateChangeListener(this); //for JMS, session borrowed corresponds to creation of the session state.notifySessionBorrowed(); } public Object invoke(Object proxy, Method method, Object[] args) throws JMSException { String methodName = method.getName(); //see case 24532 if (methodName.equals("getInvocationHandler")) return this; synchronized (this) { if (closed) { if (!methodName.equals(CLOSE_METHOD)) { String msg = "Session was closed already - calling " + methodName + " is no longer allowed."; LOGGER.logWarning(this + ": " + msg); throw new javax.jms.IllegalStateException(msg); } return null; } if (SESSION_TRANSACTION_METHODS.contains(methodName)) { String msg = "Calling commit/rollback is not allowed on a managed session!"; // When using the Spring PlatformTransactionManager, there is always a call to commit on the Session in a synchronization's afterCompletion. // The PlatformTransactionManager uses that mechanism for non-JTA TX commit which happens because DefaultMessageListenerContainer.sessionTransacted // must be set to true. Spring catches TransactionInProgressException in case of JTA TX management. // This is fine except that we used to log this message at warning level when this happens which is annoying as it repeats once per TX -> lowered it to info. // // See: org.springframework.jms.connection.ConnectionFactoryUtils$JmsResourceSynchronization.afterCommit() // and org.springframework.jms.connection.JmsResourceHolder.commitAll() (as of Spring 2.0.8) if (LOGGER.isInfoEnabled()) LOGGER.logInfo(this + ": " + msg); throw new javax.jms.TransactionInProgressException(msg); } if (CLOSE_METHOD.equals(methodName)) { state.notifySessionClosed(); if (LOGGER.isInfoEnabled()) LOGGER.logInfo( this + ": closing session " + this + " - is terminated ? " + state.isTerminated()); if (state.isTerminated()) { //only destroy if there is no pending 2PC - otherwise this is done //in the registered synchronization destroy(true); } else { //close this handle but keep vendor session open for 2PC //see case 71079 destroy(false); } //see case 71079: return here to avoid delegating to vendor session return null; } if (PRODUCER_CONSUMER_METHODS.contains(methodName)) { if (LOGGER.isInfoEnabled()) LOGGER.logInfo(this + ": calling " + methodName + " on JMS driver session " + delegate); Object producerConsumerProxy = null; if ("createConsumer".equals(methodName)) { MessageConsumer vendorConsumer = null; try { vendorConsumer = (MessageConsumer) method.invoke(delegate, args); } catch (Exception e) { String msg = "Failed to create MessageConsumer: " + e.getMessage(); state.notifySessionErrorOccurred(); convertProxyError(e, msg); } producerConsumerProxy = new AtomikosJmsMessageConsumerProxy(vendorConsumer, state); } else if ("createProducer".equals(methodName)) { MessageProducer vendorProducer = null; try { vendorProducer = (MessageProducer) method.invoke(delegate, args); } catch (Exception e) { String msg = "Failed to create MessageProducer: " + e.getMessage(); state.notifySessionErrorOccurred(); convertProxyError(e, msg); } producerConsumerProxy = new AtomikosJmsMessageProducerProxy(vendorProducer, state); } else if ("createDurableSubscriber".equals(methodName)) { TopicSubscriber vendorSubscriber = null; try { vendorSubscriber = (TopicSubscriber) method.invoke(delegate, args); } catch (Exception e) { String msg = "Failed to create durable TopicSubscriber: " + e.getMessage(); state.notifySessionErrorOccurred(); convertProxyError(e, msg); } producerConsumerProxy = new AtomikosJmsTopicSubscriberProxy(vendorSubscriber, state); } if (LOGGER.isDebugEnabled()) LOGGER.logDebug(this + ": " + methodName + " returning " + producerConsumerProxy); return producerConsumerProxy; } try { if (LOGGER.isInfoEnabled()) LOGGER.logInfo(this + ": calling " + methodName + " on JMS driver session..."); Object ret = method.invoke(delegate, args); if (LOGGER.isDebugEnabled()) LOGGER.logDebug(this + ": " + methodName + " returning " + ret); return ret; } catch (Exception ex) { String msg = "Error delegating call to " + methodName + " on JMS driver"; state.notifySessionErrorOccurred(); convertProxyError(ex, msg); } } // synchronized (this) //dummy return to keep compiler happy return null; } protected void destroy(boolean closeXaSession) { if (closeXaSession) { //see case 71079: don't close vendor session if transaction is not done yet if (LOGGER.isDebugEnabled()) LOGGER.logDebug(this + ": closing underlying vendor session " + this); try { delegate.close(); } catch (JMSException e) { LOGGER.logWarning(this + ": could not close underlying vendor session", e); } } closed = true; } protected boolean isAvailable() { boolean ret = false; if (state != null) ret = state.isTerminated(); return ret; } protected boolean isErroneous() { boolean ret = false; if (state != null) ret = state.isErroneous(); return ret; } protected boolean isInTransaction(CompositeTransaction ct) { boolean ret = false; if (state != null) ret = state.isActiveInTransaction(ct); return ret; } protected boolean isInactiveTransaction(CompositeTransaction ct) { boolean ret = false; if (state != null) ret = state.isInactiveInTransaction(ct); return ret; } public void onTerminated() { destroy(true); } public String toString() { return "atomikos xa session proxy for resource " + jmsTransactionalResource.getName(); } }