KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > resource > connectionmanager > CachedConnectionManager


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * 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 JavaDoc;
25 import java.io.PrintStream JavaDoc;
26 import java.lang.reflect.Method JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.LinkedList JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.WeakHashMap JavaDoc;
36
37 import javax.management.ObjectName JavaDoc;
38 import javax.resource.ResourceException JavaDoc;
39 import javax.resource.spi.ConnectionRequestInfo JavaDoc;
40 import javax.transaction.Synchronization JavaDoc;
41 import javax.transaction.SystemException JavaDoc;
42 import javax.transaction.Transaction JavaDoc;
43 import javax.transaction.TransactionManager JavaDoc;
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: 38338 $
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 JavaDoc transactionManagerServiceName;
77    private TransactionManager JavaDoc 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 JavaDoc currentObjects = new ThreadLocal JavaDoc();
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 JavaDoc objectToConnectionManagerMap = new HashMap JavaDoc();
94
95    /**
96     * Connection stacktraces
97     */

98    private Map JavaDoc connectionStackTraces = new WeakHashMap JavaDoc();
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 JavaDoc getTransactionManagerServiceName()
145    {
146       return transactionManagerServiceName;
147    }
148
149    public void setTransactionManagerServiceName(ObjectName JavaDoc 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 JavaDoc listInUseConnections()
168    {
169       synchronized (connectionStackTraces)
170       {
171          HashMap JavaDoc result = new HashMap JavaDoc();
172          for (Iterator JavaDoc i = connectionStackTraces.entrySet().iterator(); i.hasNext();)
173          {
174             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
175             Throwable JavaDoc stackTrace = (Throwable JavaDoc) entry.getValue();
176             ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
177             PrintStream JavaDoc ps = new PrintStream JavaDoc(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 JavaDoc
187    {
188       tm = (TransactionManager JavaDoc) 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 JavaDoc
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 JavaDoc rawKey, Set JavaDoc unsharableResources)
212            throws ResourceException JavaDoc
213    {
214       LinkedList JavaDoc stack = (LinkedList JavaDoc) currentObjects.get();
215       if (stack == null)
216       {
217          if (trace)
218             log.trace("new stack for key: " + Strings.defaultToString(rawKey));
219          stack = new LinkedList JavaDoc();
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 JavaDoc unsharableResources)
247            throws ResourceException JavaDoc
248    {
249       LinkedList JavaDoc stack = (LinkedList JavaDoc) 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 JavaDoc("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 JavaDoc stack = (LinkedList JavaDoc) 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 JavaDoc connection, ConnectionRequestInfo JavaDoc cri)
285    {
286       if (debug)
287       {
288          synchronized (connectionStackTraces)
289          {
290             connectionStackTraces.put(connection, new Throwable JavaDoc("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 JavaDoc cmToConnectionsMap = key.getCMToConnectionsMap();
302       Collection JavaDoc conns = (Collection JavaDoc) cmToConnectionsMap.get(cm);
303       if (conns == null)
304       {
305          conns = new ArrayList JavaDoc();
306          cmToConnectionsMap.put(cm, conns);
307       }
308       conns.add(cr);
309    }
310
311    void unregisterConnection(ConnectionCacheListener cm, Object JavaDoc 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 JavaDoc cmToConnectionsMap = key.getCMToConnectionsMap();
331       Collection JavaDoc conns = (Collection JavaDoc) cmToConnectionsMap.get(cm);
332       if (conns == null)
333          return; // Can happen if connections are "passed" between contexts
334
for (Iterator JavaDoc 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 JavaDoc("Trying to return an unknown connection2! " + c);
343    }
344
345    //called by UserTransaction after starting a transaction
346
public void userTransactionStarted()
347            throws SystemException JavaDoc
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 JavaDoc cmToConnectionsMap = key.getCMToConnectionsMap();
356       for (Iterator JavaDoc i = cmToConnectionsMap.keySet().iterator(); i.hasNext();)
357       {
358          ConnectionCacheListener cm = (ConnectionCacheListener) i.next();
359          Collection JavaDoc conns = (Collection JavaDoc) cmToConnectionsMap.get(cm);
360          cm.transactionStarted(conns);
361       }
362    }
363
364    /**
365     * The <code>reconnect</code> method gets the cmToConnectionsMap
366     * from objectToConnectionManagerMap, copies it to the key, and
367     * reconnects all the connections in it.
368     *
369     * @param key a <code>KeyConnectionAssociation</code> value
370     * @param unsharableResources a <code>Set</code> value
371     * @exception ResourceException if an error occurs
372     */

373    private void reconnect(KeyConnectionAssociation key, Set JavaDoc unsharableResources)
374            throws ResourceException JavaDoc
375    {
376       Map JavaDoc cmToConnectionsMap = null;
377       synchronized (objectToConnectionManagerMap)
378       {
379          cmToConnectionsMap = (Map JavaDoc) objectToConnectionManagerMap.get(key);
380          if (cmToConnectionsMap == null)
381             return;
382       }
383       key.setCMToConnectionsMap(cmToConnectionsMap);
384       for (Iterator JavaDoc i = cmToConnectionsMap.keySet().iterator(); i.hasNext();)
385       {
386          ConnectionCacheListener cm = (ConnectionCacheListener) i.next();
387          Collection JavaDoc conns = (Collection JavaDoc) cmToConnectionsMap.get(cm);
388          cm.reconnect(conns, unsharableResources);
389       }
390    }
391
392    private void disconnect(KeyConnectionAssociation key, Set JavaDoc unsharableResources)
393            throws ResourceException JavaDoc
394    {
395       Map JavaDoc cmToConnectionsMap = key.getCMToConnectionsMap();
396       if (!cmToConnectionsMap.isEmpty())
397       {
398          synchronized (objectToConnectionManagerMap)
399          {
400             objectToConnectionManagerMap.put(key, cmToConnectionsMap);
401          }
402          for (Iterator JavaDoc i = cmToConnectionsMap.keySet().iterator(); i.hasNext();)
403          {
404             ConnectionCacheListener cm = (ConnectionCacheListener) i.next();
405             Collection JavaDoc conns = (Collection JavaDoc) cmToConnectionsMap.get(cm);
406             cm.disconnect(conns, unsharableResources);
407          }
408       }
409    }
410
411    private boolean closeAll(Map JavaDoc cmToConnectionsMap)
412    {
413       if (debug == false)
414          return false;
415
416       boolean unclosed = false;
417
418       Collection JavaDoc connections = cmToConnectionsMap.values();
419       if (connections.size() != 0)
420       {
421          for (Iterator JavaDoc i = connections.iterator(); i.hasNext();)
422          {
423             Collection JavaDoc conns = (Collection JavaDoc) i.next();
424             for (Iterator JavaDoc j = conns.iterator(); j.hasNext();)
425             {
426                Object JavaDoc c = ((ConnectionRecord) j.next()).connection;
427                CloseConnectionSynchronization cas = getCloseConnectionSynchronization(true);
428                if (cas == null)
429                {
430                   unclosed = true;
431                   closeConnection(c);
432                }
433                else
434                   cas.add(c);
435             }
436          }
437       }
438       
439       return unclosed;
440    }
441
442    /**
443     * Describe <code>unregisterConnectionCacheListener</code> method here.
444     * This is a shutdown method called by a connection manager. It will remove all reference
445     * to that connection manager from the cache, so cached connections from that manager
446     * will never be recoverable.
447     * Possibly this method should not exist.
448     *
449     * @param cm a <code>ConnectionCacheListener</code> value
450     */

451    void unregisterConnectionCacheListener(ConnectionCacheListener cm)
452    {
453       if (trace)
454          log.trace("unregisterConnectionCacheListener: " + cm);
455       synchronized (objectToConnectionManagerMap)
456       {
457          for (Iterator JavaDoc i = objectToConnectionManagerMap.values().iterator(); i.hasNext();)
458          {
459             Map JavaDoc cmToConnectionsMap = (Map JavaDoc) i.next();
460             if (cmToConnectionsMap != null)
461                cmToConnectionsMap.remove(cm);
462          }
463       }
464    }
465
466    /**
467     * The class <code>KeyConnectionAssociation</code> wraps objects so they may be used in hashmaps
468     * based on their object identity rather than equals implementation. Used for keys.
469     */

470    private final static class KeyConnectionAssociation
471    {
472       //key
473
private final Object JavaDoc o;
474
475       //map of cm to list of connections for that cm.
476
private Map JavaDoc cmToConnectionsMap;
477
478       KeyConnectionAssociation(final Object JavaDoc o)
479       {
480          this.o = o;
481       }
482
483       public boolean equals(Object JavaDoc other)
484       {
485          return (other instanceof KeyConnectionAssociation) && o == ((KeyConnectionAssociation) other).o;
486       }
487
488       public String JavaDoc toString()
489       {
490          return Strings.defaultToString(o);
491       }
492       
493       public int hashCode()
494       {
495          return System.identityHashCode(o);
496       }
497
498       public void setCMToConnectionsMap(Map JavaDoc cmToConnectionsMap)
499       {
500          this.cmToConnectionsMap = cmToConnectionsMap;
501       }
502
503       public Map JavaDoc getCMToConnectionsMap()
504       {
505          if (cmToConnectionsMap == null)
506          {
507             cmToConnectionsMap = new HashMap JavaDoc();
508          } // end of if ()
509
return cmToConnectionsMap;
510       }
511    }
512
513    private void closeConnection(Object JavaDoc c)
514    {
515       try
516       {
517          Throwable JavaDoc e;
518          synchronized (connectionStackTraces)
519          {
520             e = (Throwable JavaDoc) connectionStackTraces.remove(c);
521          }
522          Method JavaDoc m = c.getClass().getMethod("close", new Class JavaDoc[]{});
523          try
524          {
525             if (e != null)
526                log.info("Closing a connection for you. Please close them yourself: " + c, e);
527             else
528                log.info("Closing a connection for you. Please close them yourself: " + c);
529             m.invoke(c, new Object JavaDoc[]{});
530          }
531          catch (Throwable JavaDoc t)
532          {
533             log.info("Throwable trying to close a connection for you, please close it yourself", t);
534          }
535       }
536       catch (NoSuchMethodException JavaDoc nsme)
537       {
538          log.info("Could not find a close method on alleged connection objects. Please close your own connections.");
539       }
540    }
541
542    private CloseConnectionSynchronization getCloseConnectionSynchronization(boolean createIfNotFound)
543    {
544       try
545       {
546          Transaction JavaDoc tx = tm.getTransaction();
547          if (TxUtils.isActive(tx))
548          {
549             TransactionSynchronizer.lock(tx);
550             try
551             {
552                CloseConnectionSynchronization cas = (CloseConnectionSynchronization) TransactionSynchronizer.getCCMSynchronization(tx);
553                if (cas == null && createIfNotFound)
554                {
555                   cas = new CloseConnectionSynchronization();
556                   TransactionSynchronizer.registerCCMSynchronization(tx, cas);
557                }
558                return cas;
559             }
560             finally
561             {
562                TransactionSynchronizer.unlock(tx);
563             }
564          }
565       }
566       catch (Throwable JavaDoc t)
567       {
568          log.debug("Unable to synchronize with transaction", t);
569       }
570       return null;
571    }
572
573    private class CloseConnectionSynchronization implements Synchronization JavaDoc
574    {
575       HashSet JavaDoc connections = new HashSet JavaDoc();
576       boolean closing = false;
577
578       public CloseConnectionSynchronization()
579       {
580       }
581
582       public synchronized void add(Object JavaDoc c)
583       {
584          if (closing)
585             return;
586          connections.add(c);
587       }
588
589       public synchronized void remove(Object JavaDoc c)
590       {
591          if (closing)
592             return;
593          connections.remove(c);
594       }
595
596       public void beforeCompletion()
597       {
598       }
599
600       public void afterCompletion(int status)
601       {
602          synchronized (this)
603          {
604             closing = true;
605          }
606          for (Iterator JavaDoc i = connections.iterator(); i.hasNext();)
607             closeConnection(i.next());
608          connections.clear(); // Help the GC
609
}
610    }
611 }
612
Popular Tags