KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mchange > v2 > c3p0 > C3P0Registry


1 /*
2  * Distributed as part of c3p0 v.0.9.1
3  *
4  * Copyright (C) 2005 Machinery For Change, Inc.
5  *
6  * Author: Steve Waldman <swaldman@mchange.com>
7  *
8  * This library is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License version 2.1, as
10  * published by the Free Software Foundation.
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
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this software; see the file LICENSE. If not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */

22
23
24 package com.mchange.v2.c3p0;
25
26 import java.util.*;
27 import com.mchange.v2.coalesce.*;
28 import com.mchange.v2.log.*;
29 import com.mchange.v2.c3p0.cfg.C3P0ConfigUtils;
30 import com.mchange.v2.c3p0.impl.*;
31
32 import java.sql.SQLException JavaDoc;
33 import com.mchange.v2.c3p0.impl.IdentityTokenized;
34 import com.mchange.v2.c3p0.subst.C3P0Substitutions;
35 import com.mchange.v2.sql.SqlUtils;
36 import com.mchange.v2.util.DoubleWeakHashMap;
37
38 import com.mchange.v2.c3p0.management.*;
39
40 /*
41  * The primary purpose of C3P0Registry is to maintain a mapping of "identityTokens"
42  * to c3p0 DataSources so that if the same DataSource is looked up (and deserialized
43  * or dereferenced) via JNDI, c3p0 can ensure that the same instance is always returned.
44  * But there are subtle issues here. If C3P0Registry maintains hard references to
45  * DataSources, then they can never be garbage collected. But if c3p0 retains only
46  * weak references, then applications that look up DataSources, then dereference them,
47  * and then re-look them up again (not a great idea, but not uncommon) might see
48  * distinct DataSources over multiple lookups.
49  *
50  * C3P0 resolves this issue has followed: At first creation or lookup of a PooledDataSource,
51  * c3p0 creates a hard reference to that DataSource. So long as the DataSource has not
52  * been close()ed or DataSources.destroy()ed, subsequent lookups will consistently
53  * return the same DataSource. If the DataSource is never closed, then there is a potential
54  * memory leak (as well as the potential Thread leak and Connection leak). But if
55  * the DataSource is close()ed, only weak refernces to the DataSource will be retained.
56  * A lookup of a DataSource after it has been close()ed within the current VM may
57  * return the previously close()ed instance, or may return a fresh instance, depending
58  * on whether the weak reference has been cleared. In other words, the result of
59  * looking up a DataSource after having close()ed it in the current VM is undefined.
60  *
61  * Note that unpooled c3p0 DataSources are always held by weak references, since
62  * they are never explicitly close()ed. The result of looking up an unpooled DataSource,
63  * modifying it, dereferencing it, and then relooking up is therefore undefined as well.
64  *
65  * These issues are mostly academic. Under normal use scenarios, how c3p0 deals with
66  * maintaining its registry doesn't much matter. In the past, c3p0 maintained hard
67  * references to DataSources indefinitely. At least one user ran into side effects
68  * of the unwanted retention of old DataSources (in a process left to run for months
69  * at a time, and frequently reconstructing multiple DataSources), so now we take care
70  * to ensure that when users properly close() and dereference DataSources, they can
71  * indeed be garbage collected.
72  */

73 public final class C3P0Registry
74 {
75     private final static String JavaDoc MC_PARAM = "com.mchange.v2.c3p0.management.ManagementCoordinator";
76     
77     //MT: thread-safe
78
final static MLogger logger = MLog.getLogger( C3P0Registry.class );
79
80     //MT: protected by class' lock
81
static boolean banner_printed = false;
82     
83     //MT: protected by class' lock
84
static boolean registry_mbean_registered = false;
85
86     //MT: thread-safe, immutable
87
private static CoalesceChecker CC = IdentityTokenizedCoalesceChecker.INSTANCE;
88
89     //MT: protected by class' lock
90
//a weak, unsynchronized coalescer
91
private static Coalescer idtCoalescer = CoalescerFactory.createCoalescer(CC, true , false);
92
93     //MT: protected by class' lock
94
private static Map tokensToTokenized = new DoubleWeakHashMap();
95
96     //MT: protected by class' lock
97
private static HashSet unclosedPooledDataSources = new HashSet();
98
99     //MT: protected by its own lock
100
private static Map classNamesToConnectionTesters = Collections.synchronizedMap( new HashMap() );
101
102     //MT: protected by its own lock
103
private static Map classNamesToConnectionCustomizers = Collections.synchronizedMap( new HashMap() );
104
105     private static ManagementCoordinator mc;
106
107     static
108     {
109         classNamesToConnectionTesters.put(C3P0Defaults.connectionTesterClassName(), C3P0Defaults.connectionTester());
110
111         String JavaDoc userManagementCoordinator = C3P0ConfigUtils.getPropFileConfigProperty(MC_PARAM);
112         if (userManagementCoordinator != null)
113         {
114             try
115             {
116                 mc = (ManagementCoordinator) Class.forName(userManagementCoordinator).newInstance();
117             }
118             catch (Exception JavaDoc e)
119             {
120                 if (logger.isLoggable(MLevel.WARNING))
121                     logger.log(MLevel.WARNING,
122                                "Could not instantiate user-specified ManagementCoordinator " + userManagementCoordinator +
123                                ". Using NullManagementCoordinator (c3p0 JMX management disabled!)",
124                                e );
125                 mc = new NullManagementCoordinator();
126             }
127         }
128         else
129         {
130             try
131             {
132                 Class.forName("java.lang.management.ManagementFactory");
133
134                 mc = (ManagementCoordinator) Class.forName( "com.mchange.v2.c3p0.management.ActiveManagementCoordinator" ).newInstance();
135             }
136             catch (Exception JavaDoc e)
137             {
138                 if ( logger.isLoggable( MLevel.INFO ) )
139                     logger.log( MLevel.INFO,
140                                     "jdk1.5 management interfaces unavailable... JMX support disabled.",
141                                     e);
142                 mc = new NullManagementCoordinator();
143             }
144         }
145     }
146
147     public static ConnectionTester getConnectionTester( String JavaDoc className )
148     {
149         try
150         {
151             ConnectionTester out = (ConnectionTester) classNamesToConnectionTesters.get( className );
152             if (out == null)
153             {
154                 out = (ConnectionTester) Class.forName( className ).newInstance();
155                 classNamesToConnectionTesters.put( className, out );
156             }
157             return out;
158         }
159         catch (Exception JavaDoc e)
160         {
161             if (logger.isLoggable( MLevel.WARNING ))
162                 logger.log( MLevel.WARNING,
163                                 "Could not create for find ConnectionTester with class name '" +
164                                 className + "'. Using default.",
165                                 e );
166             return C3P0Defaults.connectionTester();
167         }
168     }
169
170     public static ConnectionCustomizer getConnectionCustomizer( String JavaDoc className ) throws SQLException JavaDoc
171     {
172         if ( className == null )
173             return null;
174         else
175         {
176             try
177             {
178                 ConnectionCustomizer out = (ConnectionCustomizer) classNamesToConnectionCustomizers.get( className );
179                 if (out == null)
180                 {
181                     out = (ConnectionCustomizer) Class.forName( className ).newInstance();
182                     classNamesToConnectionCustomizers.put( className, out );
183                 }
184                 return out;
185             }
186             catch (Exception JavaDoc e)
187             {
188                 if (logger.isLoggable( MLevel.WARNING ))
189                     logger.log( MLevel.WARNING,
190                                     "Could not create for find ConnectionCustomizer with class name '" +
191                                     className + "'.",
192                                     e );
193                 throw SqlUtils.toSQLException( e );
194             }
195         }
196     }
197
198     // must be called from a static sync'ed method
199
private static void banner()
200     {
201         if (! banner_printed )
202         {
203             if (logger.isLoggable( MLevel.INFO ) )
204                 logger.info("Initializing c3p0-" + C3P0Substitutions.VERSION + " [built " + C3P0Substitutions.TIMESTAMP +
205                                 "; debug? " + C3P0Substitutions.DEBUG +
206                                 "; trace: " + C3P0Substitutions.TRACE
207                                 +']');
208             banner_printed = true;
209         }
210     }
211     
212     // must be called from a static, sync'ed method
213
private static void attemptRegisterRegistryMBean()
214     {
215         if (! registry_mbean_registered)
216         {
217             mc.attemptManageC3P0Registry();
218             registry_mbean_registered = true;
219         }
220     }
221
222     // must be called with class' lock
223
private static boolean isIncorporated( IdentityTokenized idt )
224     { return tokensToTokenized.keySet().contains( idt.getIdentityToken() ); }
225
226     // must be called with class' lock
227
private static void incorporate( IdentityTokenized idt )
228     {
229         tokensToTokenized.put( idt.getIdentityToken(), idt );
230         if (idt instanceof PooledDataSource)
231         {
232             unclosedPooledDataSources.add( idt );
233             mc.attemptManagePooledDataSource( (PooledDataSource) idt );
234         }
235     }
236
237     public static synchronized IdentityTokenized reregister(IdentityTokenized idt)
238     {
239         if (idt instanceof PooledDataSource)
240         {
241             banner();
242             attemptRegisterRegistryMBean();
243         }
244         
245         if (idt.getIdentityToken() == null)
246             throw new RuntimeException JavaDoc("[c3p0 issue] The identityToken of a registered object should be set prior to registration.");
247
248         IdentityTokenized coalesceCheck = (IdentityTokenized) idtCoalescer.coalesce(idt);
249
250         if (! isIncorporated( coalesceCheck ))
251             incorporate( coalesceCheck );
252
253         return coalesceCheck;
254     }
255     
256     public static synchronized void markClosed(PooledDataSource pds)
257     {
258         unclosedPooledDataSources.remove(pds);
259         mc.attemptUnmanagePooledDataSource( pds );
260         if (unclosedPooledDataSources.isEmpty())
261         {
262             mc.attemptUnmanageC3P0Registry();
263             registry_mbean_registered = false;
264         }
265     }
266
267     public synchronized static Set getPooledDataSources()
268     { return (Set) unclosedPooledDataSources.clone(); }
269
270     public synchronized static Set pooledDataSourcesByName( String JavaDoc dataSourceName )
271     {
272         Set out = new HashSet();
273         for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); )
274         {
275             PooledDataSource pds = (PooledDataSource) ii.next();
276             if ( pds.getDataSourceName().equals( dataSourceName ) )
277                 out.add( pds );
278         }
279         return out;
280     }
281
282     public synchronized static PooledDataSource pooledDataSourceByName( String JavaDoc dataSourceName )
283     {
284         for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); )
285         {
286             PooledDataSource pds = (PooledDataSource) ii.next();
287             if ( pds.getDataSourceName().equals( dataSourceName ) )
288                 return pds;
289         }
290         return null;
291     }
292
293     public synchronized static Set allIdentityTokens()
294     {
295         Set out = Collections.unmodifiableSet( tokensToTokenized.keySet() );
296         //System.err.println( "allIdentityTokens(): " + out );
297
return out;
298     }
299
300     public synchronized static Set allIdentityTokenized()
301     {
302         HashSet out = new HashSet();
303         out.addAll( tokensToTokenized.values() );
304         //System.err.println( "allIdentityTokenized(): " + out );
305
return Collections.unmodifiableSet( out );
306     }
307
308     public synchronized static Set allPooledDataSources()
309     {
310         Set out = Collections.unmodifiableSet( unclosedPooledDataSources );
311         //System.err.println( "allPooledDataSources(): " + out );
312
return out;
313     }
314
315     public synchronized static int getNumPooledDataSources()
316     { return unclosedPooledDataSources.size(); }
317
318     public synchronized static int getNumPoolsAllDataSources() throws SQLException JavaDoc
319     {
320     int count = 0;
321     for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext();)
322         {
323         PooledDataSource pds = (PooledDataSource) ii.next();
324         count += pds.getNumUserPools();
325         }
326     return count;
327     }
328
329     public synchronized int getNumThreadsAllThreadPools() throws SQLException JavaDoc
330     {
331     int count = 0;
332     for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext();)
333         {
334         PooledDataSource pds = (PooledDataSource) ii.next();
335         count += pds.getNumHelperThreads();
336         }
337     return count;
338     }
339 }
340
Popular Tags