/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.tm.usertx.server; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.UndeclaredThrowableException; import java.util.Map; import java.util.HashMap; import java.util.Collections; import javax.management.ObjectName; import javax.naming.InitialContext; import javax.naming.Context; import org.jboss.system.Registry; import org.jboss.system.ServiceMBeanSupport; import org.jboss.tm.usertx.client.ClientUserTransaction; import org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory; import org.jboss.tm.usertx.interfaces.UserTransactionSession; import org.jboss.invocation.Invocation; import org.jboss.invocation.MarshalledInvocation; /** * This is a JMX service handling the serverside of UserTransaction * usage for standalone clients. * * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a> * @author Scott.Stark@jboss.org * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a> * @version $Revision: 81030 $ */ public class ClientUserTransactionService extends ServiceMBeanSupport implements ClientUserTransactionServiceMBean { // Constants ----------------------------------------------------- /** The location where the javax.transaction.UserTransaction is bound on the server */ public static String JNDI_NAME = "UserTransaction"; // Attributes ---------------------------------------------------- /** The UserTransactionSession and UserTransactionSessionFactory method hashes */ private Map marshalledInvocationMapping = new HashMap(); /** The proxy factory service used for the UserTransactionSession */ private ObjectName txProxyName; /** The stateless proxy used for all UserTransactionSessions */ private Object txProxy; /** Set the name of the proxy factory service used for the * UserTransactionSession * @param proxyName */ public void setTxProxyName(ObjectName proxyName) { this.txProxyName = proxyName; } /** Expose UserTransactionSession and UserTransactionSessionFactory * interfaces via JMX to invokers. * * @jmx:managed-operation * * @param invocation A pointer to the invocation object * @return Return value of method invocation. * * @throws Exception Failed to invoke method. */ public Object invoke(Invocation invocation) throws Exception { // Set the method hash to Method mapping if (invocation instanceof MarshalledInvocation) { MarshalledInvocation mi = (MarshalledInvocation) invocation; mi.setMethodMap(marshalledInvocationMapping); } // Invoke the method via reflection Method method = invocation.getMethod(); Object[] args = invocation.getArguments(); Object value = null; try { if( UserTransactionSessionFactory.class.isAssignableFrom(method.getDeclaringClass()) ) { // Just return the UserTransactionSession proxy as its stateless value = txProxy; } else if( method.getName().equals("begin") ) { // Begin a new transaction Integer timeout = (Integer) args[0]; UserTransactionSession session = UserTransactionSessionImpl.getInstance(); value = session.begin(timeout.intValue()); } else if( method.getName().equals("destroy")) { /* We do nothing as the tx will timeout and the tx map is shared across all sessions as we have no association with the txs a given client has started. */ } else { UserTransactionSession session = UserTransactionSessionImpl.getInstance(); value = method.invoke(session, args); } } catch(InvocationTargetException e) { Throwable t = e.getTargetException(); if( t instanceof Exception ) throw (Exception) t; else throw new UndeclaredThrowableException(t, method.toString()); } return value; } // ServiceMBeanSupport overrides --------------------------------- protected void startService() throws Exception { Context ctx = new InitialContext(); // Bind the in VM UserTransaction interface ctx.bind(JNDI_NAME, ClientUserTransaction.getSingleton()); // Get the UserTransactionSession proxy txProxy = getServer().getAttribute(txProxyName, "Proxy"); // Build the UserTransactionSession interface method map HashMap tmpMap = new HashMap(13); Method[] methods = UserTransactionSession.class.getMethods(); for(int m = 0; m < methods.length; m ++) { Method method = methods[m]; Long hash = new Long(MarshalledInvocation.calculateHash(method)); tmpMap.put(hash, method); } // Add the UserTransactionSessionFactory interface method map methods = UserTransactionSessionFactory.class.getMethods(); for(int m = 0; m < methods.length; m ++) { Method method = methods[m]; Long hash = new Long(MarshalledInvocation.calculateHash(method)); tmpMap.put(hash, method); } marshalledInvocationMapping = Collections.unmodifiableMap(tmpMap); // Place our ObjectName hash into the Registry so invokers can resolve it. // We need this for the HA Proxy case where the only the target service // is added to the registry, which is how it should be. // In the non HA Proxy case, JRMPProxyFactory acts both as a proxy factory // and proxy which is conceptually wrong, hence this is an extra step in // that case. Registry.bind(new Integer(serviceName.hashCode()), serviceName); } protected void stopService() { try { Context ctx = new InitialContext(); ctx.unbind(JNDI_NAME); } catch (Exception e) { log.warn("Failed to unbind "+JNDI_NAME, e); } // Remove our ObjectName hash from Registry if the proxy factory had not Registry.unbind(new Integer(serviceName.hashCode())); } }