Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » resource » connectionmanager » [javadoc | source]
    1   /*
    2    * JBoss, Home of Professional Open Source.
    3    * Copyright 2006, Red Hat Middleware LLC, and individual contributors
    4    * as indicated by the @author tags. See the copyright.txt file in the
    5    * distribution for a full listing of individual contributors.
    6    *
    7    * This is free software; you can redistribute it and/or modify it
    8    * under the terms of the GNU Lesser General Public License as
    9    * published by the Free Software Foundation; either version 2.1 of
   10    * the License, or (at your option) any later version.
   11    *
   12    * This software is distributed in the hope that it will be useful,
   13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15    * Lesser General Public License for more details.
   16    *
   17    * You should have received a copy of the GNU Lesser General Public
   18    * License along with this software; if not, write to the Free
   19    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21    */
   22   package org.jboss.resource.connectionmanager;
   23   
   24   import java.io.ByteArrayOutputStream;
   25   import java.io.PrintStream;
   26   import java.lang.reflect.Method;
   27   import java.util.ArrayList;
   28   import java.util.Collection;
   29   import java.util.HashMap;
   30   import java.util.HashSet;
   31   import java.util.Iterator;
   32   import java.util.LinkedList;
   33   import java.util.Map;
   34   import java.util.Set;
   35   import java.util.WeakHashMap;
   36   
   37   import javax.management.ObjectName;
   38   import javax.resource.ResourceException;
   39   import javax.resource.spi.ConnectionRequestInfo;
   40   import javax.transaction.Synchronization;
   41   import javax.transaction.SystemException;
   42   import javax.transaction.Transaction;
   43   import javax.transaction.TransactionManager;
   44   
   45   import org.jboss.ejb.EnterpriseContext;
   46   import org.jboss.system.ServiceMBeanSupport;
   47   import org.jboss.tm.TxUtils;
   48   import org.jboss.tm.usertx.client.ServerVMClientUserTransaction;
   49   import org.jboss.util.Strings;
   50   
   51   /**
   52    * The CachedConnectionManager mbean manages associations between meta-aware objects
   53    * (those accessed through interceptor chains) and connection handles, and between
   54    *  user transactions and connection handles.  Normally there should only be one
   55    * such mbean.  It is called by CachedConnectionInterceptor, UserTransaction,
   56    * and all BaseConnectionManager2 instances.
   57    *
   58    * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
   59    * @author <a href="mailto:E.Guib@ceyoniq.com">Erwin Guib</a>
   60    * @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
   61    * @version $Revision: 74343 $
   62    */
   63   public class CachedConnectionManager
   64           extends ServiceMBeanSupport
   65           implements ServerVMClientUserTransaction.UserTransactionStartedListener,
   66           CachedConnectionManagerMBean
   67   {
   68      private boolean specCompliant;
   69   
   70      protected boolean trace;
   71      
   72      private boolean debug;
   73   
   74      protected boolean error;
   75   
   76      private ObjectName transactionManagerServiceName;
   77      private TransactionManager tm;
   78   
   79      /**
   80       * ThreadLocal that holds current calling meta-programming aware
   81       * object, used in case someone is idiotic enough to cache a
   82       * connection between invocations.and want the spec required
   83       * behavior of it getting hooked up to an appropriate
   84       * ManagedConnection on each method invocation.
   85       */
   86      private final ThreadLocal currentObjects = new ThreadLocal();
   87   
   88      /**
   89       * The variable <code>objectToConnectionManagerMap</code> holds the
   90       * map of meta-aware object to set of connections it holds, used by
   91       * the idiot spec compliant behavior.
   92       */
   93      private final Map objectToConnectionManagerMap = new HashMap();
   94   
   95      /**
   96       * Connection stacktraces
   97       */
   98      private Map connectionStackTraces = new WeakHashMap();
   99   
  100      /**
  101       * Default CachedConnectionManager managed constructor for mbeans.
  102       * Remember that this mbean should be a singleton.
  103       *
  104       * @jmx.managed-constructor
  105       */
  106      public CachedConnectionManager()
  107      {
  108         super();
  109         trace = log.isTraceEnabled();
  110      }
  111   
  112      public boolean isSpecCompliant()
  113      {
  114         return specCompliant;
  115      }
  116   
  117      public void setSpecCompliant(boolean specCompliant)
  118      {
  119         if (specCompliant)
  120            log.warn("THE SpecCompliant ATTRIBUTE IS MISNAMED SEE http://jira.jboss.com/jira/browse/JBAS-1662");
  121         this.specCompliant = specCompliant;
  122      }
  123   
  124      public boolean isDebug()
  125      {
  126         return debug;
  127      }
  128   
  129      public void setDebug(boolean value)
  130      {
  131         this.debug = value;
  132      }
  133   
  134      public boolean isError()
  135      {
  136         return error;
  137      }
  138   
  139      public void setError(boolean value)
  140      {
  141         this.error = value;
  142      }
  143   
  144      public ObjectName getTransactionManagerServiceName()
  145      {
  146         return transactionManagerServiceName;
  147      }
  148   
  149      public void setTransactionManagerServiceName(ObjectName transactionManagerServiceName)
  150      {
  151         this.transactionManagerServiceName = transactionManagerServiceName;
  152      }
  153   
  154      public CachedConnectionManager getInstance()
  155      {
  156         return this;
  157      }
  158   
  159      public int getInUseConnections()
  160      {
  161         synchronized (connectionStackTraces)
  162         {
  163            return connectionStackTraces.size();
  164         }
  165      }
  166   
  167      public Map listInUseConnections()
  168      {
  169         synchronized (connectionStackTraces)
  170         {
  171            HashMap result = new HashMap();
  172            for (Iterator i = connectionStackTraces.entrySet().iterator(); i.hasNext();)
  173            {
  174               Map.Entry entry = (Map.Entry) i.next();
  175               Throwable stackTrace = (Throwable) entry.getValue();
  176               ByteArrayOutputStream baos = new ByteArrayOutputStream();
  177               PrintStream ps = new PrintStream(baos);
  178               stackTrace.printStackTrace(ps);
  179               result.put(entry.getKey().toString(), baos.toString());
  180            }
  181            return result;
  182         }
  183      }
  184      
  185      protected void startService()
  186              throws Exception
  187      {
  188         tm = (TransactionManager) getServer().getAttribute(transactionManagerServiceName,
  189                 "TransactionManager");
  190         TransactionSynchronizer.setTransactionManager(tm);
  191         ServerVMClientUserTransaction.getSingleton().registerTxStartedListener(this);
  192         EnterpriseContext.setUserTransactionStartedListener(this);
  193      }
  194   
  195      protected void stopService() throws Exception
  196      {
  197         ServerVMClientUserTransaction.getSingleton().unregisterTxStartedListener(this);
  198         EnterpriseContext.setUserTransactionStartedListener(null);
  199      }
  200   
  201      //Object registration for meta-aware objects (i.e. this is called by interceptors)
  202   
  203      /**
  204       * Describe <code>pushMetaAwareObject</code> method here.
  205       * PUBLIC for TESTING PURPOSES ONLY!
  206       * 
  207       * @param rawKey an <code>Object</code> value
  208       * @param unsharableResources the unsharable resources
  209       * @exception ResourceException if an error occurs
  210       */
  211      public void pushMetaAwareObject(final Object rawKey, Set unsharableResources)
  212              throws ResourceException
  213      {
  214         LinkedList stack = (LinkedList) currentObjects.get();
  215         if (stack == null)
  216         {
  217            if (trace)
  218               log.trace("new stack for key: " + Strings.defaultToString(rawKey));
  219            stack = new LinkedList();
  220            currentObjects.set(stack);
  221         } // end of if ()
  222         else
  223         {
  224            if (trace)
  225               log.trace("old stack for key: " + Strings.defaultToString(rawKey));
  226            //At one time I attempted to recycle connections held over method calls.
  227            //This caused problems if the other method call started a new transaction.
  228            //To assure optimal use of connections, close them before calling out.
  229         } // end of else
  230         //check for reentrancy, reconnect if not reentrant.
  231         //wrap key to be based on == rather than equals
  232         KeyConnectionAssociation key = new KeyConnectionAssociation(rawKey);
  233         if (specCompliant && !stack.contains(key))
  234         {
  235            reconnect(key, unsharableResources);
  236         }
  237         stack.addLast(key);
  238      }
  239   
  240      /**
  241       * Describe <code>popMetaAwareObject</code> method here.
  242       * PUBLIC for TESTING PURPOSES ONLY!
  243       *
  244       * @exception ResourceException if an error occurs
  245       */
  246      public void popMetaAwareObject(Set unsharableResources)
  247              throws ResourceException
  248      {
  249         LinkedList stack = (LinkedList) currentObjects.get();
  250         KeyConnectionAssociation oldKey = (KeyConnectionAssociation) stack.removeLast();
  251         if (trace)
  252            log.trace("popped object: " + Strings.defaultToString(oldKey));
  253         if (specCompliant)
  254         {
  255            if (!stack.contains(oldKey))
  256            {
  257               disconnect(oldKey, unsharableResources);
  258            } // end of if ()
  259         }
  260         else if (debug)
  261         {
  262            if (closeAll(oldKey.getCMToConnectionsMap()) && error)
  263               throw new ResourceException("Some connections were not closed, see the log for the allocation stacktraces");
  264         }
  265   
  266         //At one time I attempted to recycle connections held over method calls.
  267         //This caused problems if the other method call started a new transaction.
  268         //To assure optimal use of connections, close them before calling out.
  269      }
  270   
  271      KeyConnectionAssociation peekMetaAwareObject()
  272      {
  273         LinkedList stack = (LinkedList) currentObjects.get();
  274         if (stack == null)
  275            return null;
  276         if (!stack.isEmpty())
  277            return (KeyConnectionAssociation) stack.getLast();
  278         else
  279            return null;
  280      }
  281   
  282      //ConnectionRegistration -- called by ConnectionCacheListeners (normally ConnectionManagers)
  283   
  284      void registerConnection(ConnectionCacheListener cm, ConnectionListener cl, Object connection, ConnectionRequestInfo cri)
  285      {
  286         if (debug)
  287         {
  288            synchronized (connectionStackTraces)
  289            {
  290               connectionStackTraces.put(connection, new Throwable("STACKTRACE"));
  291            }
  292         }
  293   
  294         KeyConnectionAssociation key = peekMetaAwareObject();
  295         if (trace)
  296            log.trace("registering connection from " + cm + ", connection : " + connection + ", key: " + key);
  297         if (key == null)
  298            return; //not participating properly in this management scheme.
  299   
  300         ConnectionRecord cr = new ConnectionRecord(cl, connection, cri);
  301         Map cmToConnectionsMap = key.getCMToConnectionsMap();
  302         Collection conns = (Collection) cmToConnectionsMap.get(cm);
  303         if (conns == null)
  304         {
  305            conns = new ArrayList();
  306            cmToConnectionsMap.put(cm, conns);
  307         }
  308         conns.add(cr);
  309      }
  310   
  311      void unregisterConnection(ConnectionCacheListener cm, Object c)
  312      {
  313         if (debug)
  314         {
  315            CloseConnectionSynchronization cas = getCloseConnectionSynchronization(false);
  316            if (cas != null)
  317               cas.remove(c);
  318            synchronized (connectionStackTraces)
  319            {
  320               connectionStackTraces.remove(c);
  321            }
  322         }
  323   
  324         KeyConnectionAssociation key = peekMetaAwareObject();
  325         if (trace)
  326            log.trace("unregistering connection from " + cm + ", object: " + c + ", key: " + key);
  327         if (key == null)
  328            return; //not participating properly in this management scheme.
  329   
  330         Map cmToConnectionsMap = key.getCMToConnectionsMap();
  331         Collection conns = (Collection) cmToConnectionsMap.get(cm);
  332         if (conns == null)
  333            return; // Can happen if connections are "passed" between contexts
  334         for (Iterator i = conns.iterator(); i.hasNext();)
  335         {
  336            if (((ConnectionRecord) i.next()).connection == c)
  337            {
  338               i.remove();
  339               return;
  340            }
  341         }
  342         throw new IllegalStateException("Trying to return an unknown connection2! " + c);
  343      }
  344   
  345      //called by UserTransaction after starting a transaction
  346      public void userTransactionStarted()
  347              throws SystemException
  348      {
  349         KeyConnectionAssociation key = peekMetaAwareObject();
  350         if (trace)
  351            log.trace("user tx started, key: " + key);
  352         if (key == null)
  353            return; //not participating properly in this management scheme.
  354   
  355         Map cmToConnectionsMap = key.getCMToConnectionsMap();
  356         Iterator cmToConnectionsMapIterator = cmToConnectionsMap.entrySet().iterator();
  357         while (cmToConnectionsMapIterator.hasNext())
  358         {
  359            Map.Entry cmToConnectionsMapEntry = (Map.Entry)cmToConnectionsMapIterator.next();
  360            ConnectionCacheListener cm = (ConnectionCacheListener) cmToConnectionsMapEntry.getKey();
  361            Collection conns = (Collection) cmToConnectionsMapEntry.getValue();
  362            cm.transactionStarted(conns);
  363         }
  364      }
  365   
  366      /**
  367       * The <code>reconnect</code> method gets the cmToConnectionsMap
  368       * from objectToConnectionManagerMap, copies it to the key, and
  369       * reconnects all the connections in it.
  370       *
  371       * @param key a <code>KeyConnectionAssociation</code> value
  372       * @param unsharableResources a <code>Set</code> value
  373       * @exception ResourceException if an error occurs
  374       */
  375      private void reconnect(KeyConnectionAssociation key, Set unsharableResources)
  376              throws ResourceException
  377      {
  378         Map cmToConnectionsMap = null;
  379         synchronized (objectToConnectionManagerMap)
  380         {
  381            cmToConnectionsMap = (Map) objectToConnectionManagerMap.get(key);
  382            if (cmToConnectionsMap == null)
  383               return;
  384         }
  385         key.setCMToConnectionsMap(cmToConnectionsMap);
  386         Iterator cmToConnectionsMapIterator = cmToConnectionsMap.entrySet().iterator();
  387         while (cmToConnectionsMapIterator.hasNext())
  388         {
  389            Map.Entry cmToConnectionsMapEntry = (Map.Entry)cmToConnectionsMapIterator.next();
  390            ConnectionCacheListener cm = (ConnectionCacheListener) cmToConnectionsMapEntry.getKey();
  391            Collection conns = (Collection) cmToConnectionsMapEntry.getValue();
  392            cm.reconnect(conns, unsharableResources);
  393         }
  394      }
  395   
  396      private void disconnect(KeyConnectionAssociation key, Set unsharableResources)
  397              throws ResourceException
  398      {
  399         Map cmToConnectionsMap = key.getCMToConnectionsMap();
  400         if (!cmToConnectionsMap.isEmpty())
  401         {
  402            synchronized (objectToConnectionManagerMap)
  403            {
  404               objectToConnectionManagerMap.put(key, cmToConnectionsMap);
  405            }
  406            Iterator cmToConnectionsMapIterator = cmToConnectionsMap.entrySet().iterator();
  407            while (cmToConnectionsMapIterator.hasNext())
  408            {
  409               Map.Entry cmToConnectionsMapEntry = (Map.Entry)cmToConnectionsMapIterator.next();
  410               ConnectionCacheListener cm = (ConnectionCacheListener) cmToConnectionsMapEntry.getKey();
  411               Collection conns = (Collection) cmToConnectionsMapEntry.getValue();
  412               cm.disconnect(conns, unsharableResources);
  413            }
  414         }
  415      }
  416   
  417      private boolean closeAll(Map cmToConnectionsMap)
  418      {
  419         if (debug == false)
  420            return false;
  421   
  422         boolean unclosed = false;
  423   
  424         Collection connections = cmToConnectionsMap.values();
  425         if (connections.size() != 0)
  426         {
  427            for (Iterator i = connections.iterator(); i.hasNext();)
  428            {
  429               Collection conns = (Collection) i.next();
  430               for (Iterator j = conns.iterator(); j.hasNext();)
  431               {
  432                  Object c = ((ConnectionRecord) j.next()).connection;
  433                  CloseConnectionSynchronization cas = getCloseConnectionSynchronization(true);
  434                  if (cas == null)
  435                  {
  436                     unclosed = true;
  437                     closeConnection(c);
  438                  }
  439                  else
  440                     cas.add(c);
  441               }
  442            }
  443         }
  444         
  445         return unclosed;
  446      }
  447   
  448      /**
  449       * Describe <code>unregisterConnectionCacheListener</code> method here.
  450       * This is a shutdown method called by a connection manager.  It will remove all reference
  451       * to that connection manager from the cache, so cached connections from that manager
  452       * will never be recoverable.
  453       * Possibly this method should not exist.
  454       * 
  455       * @param cm a <code>ConnectionCacheListener</code> value
  456       */
  457      void unregisterConnectionCacheListener(ConnectionCacheListener cm)
  458      {
  459         if (trace)
  460            log.trace("unregisterConnectionCacheListener: " + cm);
  461         synchronized (objectToConnectionManagerMap)
  462         {
  463            for (Iterator i = objectToConnectionManagerMap.values().iterator(); i.hasNext();)
  464            {
  465               Map cmToConnectionsMap = (Map) i.next();
  466               if (cmToConnectionsMap != null)
  467                  cmToConnectionsMap.remove(cm);
  468            }
  469         }
  470      }
  471   
  472      /**
  473       * The class <code>KeyConnectionAssociation</code> wraps objects so they may be used in hashmaps
  474       * based on their object identity rather than equals implementation. Used for keys.
  475       */
  476      private final static class KeyConnectionAssociation
  477      {
  478         //key
  479         private final Object o;
  480   
  481         //map of cm to list of connections for that cm.
  482         private Map cmToConnectionsMap;
  483   
  484         KeyConnectionAssociation(final Object o)
  485         {
  486            this.o = o;
  487         }
  488   
  489         public boolean equals(Object other)
  490         {
  491            return (other instanceof KeyConnectionAssociation) && o == ((KeyConnectionAssociation) other).o;
  492         }
  493   
  494         public String toString()
  495         {
  496            return Strings.defaultToString(o);
  497         }
  498         
  499         public int hashCode()
  500         {
  501            return System.identityHashCode(o);
  502         }
  503   
  504         public void setCMToConnectionsMap(Map cmToConnectionsMap)
  505         {
  506            this.cmToConnectionsMap = cmToConnectionsMap;
  507         }
  508   
  509         public Map getCMToConnectionsMap()
  510         {
  511            if (cmToConnectionsMap == null)
  512            {
  513               cmToConnectionsMap = new HashMap();
  514            } // end of if ()
  515            return cmToConnectionsMap;
  516         }
  517      }
  518   
  519      private void closeConnection(Object c)
  520      {
  521         try
  522         {
  523            Throwable e;
  524            synchronized (connectionStackTraces)
  525            {
  526               e = (Throwable) connectionStackTraces.remove(c);
  527            }
  528            Method m = c.getClass().getMethod("close", new Class[]{});
  529            try
  530            {
  531               if (e != null)
  532                  log.info("Closing a connection for you.  Please close them yourself: " + c, e);
  533               else
  534                  log.info("Closing a connection for you.  Please close them yourself: " + c);
  535               m.invoke(c, new Object[]{});
  536            }
  537            catch (Throwable t)
  538            {
  539               log.info("Throwable trying to close a connection for you, please close it yourself", t);
  540            }
  541         }
  542         catch (NoSuchMethodException nsme)
  543         {
  544            log.info("Could not find a close method on alleged connection objects.  Please close your own connections.");
  545         }
  546      }
  547   
  548      private CloseConnectionSynchronization getCloseConnectionSynchronization(boolean createIfNotFound)
  549      {
  550         try
  551         {
  552            Transaction tx = tm.getTransaction();
  553            if (tx != null)
  554            {
  555               TransactionSynchronizer.lock(tx);
  556               try
  557               {
  558                  CloseConnectionSynchronization cas = (CloseConnectionSynchronization) TransactionSynchronizer.getCCMSynchronization(tx);
  559                  if (cas == null && createIfNotFound && TxUtils.isActive(tx))
  560                  {
  561                     cas = new CloseConnectionSynchronization();
  562                     TransactionSynchronizer.registerCCMSynchronization(tx, cas);
  563                  }
  564                  return cas;
  565               }
  566               finally
  567               {
  568                  TransactionSynchronizer.unlock(tx);
  569               }
  570            }
  571         }
  572         catch (Throwable t)
  573         {
  574            log.debug("Unable to synchronize with transaction", t);
  575         }
  576         return null;
  577      }
  578   
  579      private class CloseConnectionSynchronization implements Synchronization
  580      {
  581         HashSet connections = new HashSet();
  582         boolean closing = false;
  583   
  584         public CloseConnectionSynchronization()
  585         {
  586         }
  587   
  588         public synchronized void add(Object c)
  589         {
  590            if (closing)
  591               return;
  592            connections.add(c);
  593         }
  594   
  595         public synchronized void remove(Object c)
  596         {
  597            if (closing)
  598               return;
  599            connections.remove(c);
  600         }
  601   
  602         public void beforeCompletion()
  603         {
  604         }
  605   
  606         public void afterCompletion(int status)
  607         {
  608            synchronized (this)
  609            {
  610               closing = true;
  611            }
  612            for (Iterator i = connections.iterator(); i.hasNext();)
  613               closeConnection(i.next());
  614            connections.clear(); // Help the GC
  615         }
  616      }
  617   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » resource » connectionmanager » [javadoc | source]