001: /*
002: * Distributed as part of c3p0 v.0.9.1.2
003: *
004: * Copyright (C) 2005 Machinery For Change, Inc.
005: *
006: * Author: Steve Waldman <swaldman@mchange.com>
007: *
008: * This library is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU Lesser General Public License version 2.1, as
010: * published by the Free Software Foundation.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public License
018: * along with this software; see the file LICENSE. If not, write to the
019: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: */
022:
023: package com.mchange.v2.c3p0;
024:
025: import java.util.*;
026: import com.mchange.v2.coalesce.*;
027: import com.mchange.v2.log.*;
028: import com.mchange.v2.c3p0.cfg.C3P0ConfigUtils;
029: import com.mchange.v2.c3p0.impl.*;
030:
031: import java.sql.SQLException;
032: import com.mchange.v2.c3p0.impl.IdentityTokenized;
033: import com.mchange.v2.c3p0.subst.C3P0Substitutions;
034: import com.mchange.v2.sql.SqlUtils;
035: import com.mchange.v2.util.DoubleWeakHashMap;
036:
037: import com.mchange.v2.c3p0.management.*;
038:
039: /*
040: * The primary purpose of C3P0Registry is to maintain a mapping of "identityTokens"
041: * to c3p0 DataSources so that if the same DataSource is looked up (and deserialized
042: * or dereferenced) via JNDI, c3p0 can ensure that the same instance is always returned.
043: * But there are subtle issues here. If C3P0Registry maintains hard references to
044: * DataSources, then they can never be garbage collected. But if c3p0 retains only
045: * weak references, then applications that look up DataSources, then dereference them,
046: * and then re-look them up again (not a great idea, but not uncommon) might see
047: * distinct DataSources over multiple lookups.
048: *
049: * C3P0 resolves this issue has followed: At first creation or lookup of a PooledDataSource,
050: * c3p0 creates a hard reference to that DataSource. So long as the DataSource has not
051: * been close()ed or DataSources.destroy()ed, subsequent lookups will consistently
052: * return the same DataSource. If the DataSource is never closed, then there is a potential
053: * memory leak (as well as the potential Thread leak and Connection leak). But if
054: * the DataSource is close()ed, only weak refernces to the DataSource will be retained.
055: * A lookup of a DataSource after it has been close()ed within the current VM may
056: * return the previously close()ed instance, or may return a fresh instance, depending
057: * on whether the weak reference has been cleared. In other words, the result of
058: * looking up a DataSource after having close()ed it in the current VM is undefined.
059: *
060: * Note that unpooled c3p0 DataSources are always held by weak references, since
061: * they are never explicitly close()ed. The result of looking up an unpooled DataSource,
062: * modifying it, dereferencing it, and then relooking up is therefore undefined as well.
063: *
064: * These issues are mostly academic. Under normal use scenarios, how c3p0 deals with
065: * maintaining its registry doesn't much matter. In the past, c3p0 maintained hard
066: * references to DataSources indefinitely. At least one user ran into side effects
067: * of the unwanted retention of old DataSources (in a process left to run for months
068: * at a time, and frequently reconstructing multiple DataSources), so now we take care
069: * to ensure that when users properly close() and dereference DataSources, they can
070: * indeed be garbage collected.
071: */
072: public final class C3P0Registry {
073: private final static String MC_PARAM = "com.mchange.v2.c3p0.management.ManagementCoordinator";
074:
075: //MT: thread-safe
076: final static MLogger logger = MLog.getLogger(C3P0Registry.class);
077:
078: //MT: protected by class' lock
079: static boolean banner_printed = false;
080:
081: //MT: protected by class' lock
082: static boolean registry_mbean_registered = false;
083:
084: //MT: thread-safe, immutable
085: private static CoalesceChecker CC = IdentityTokenizedCoalesceChecker.INSTANCE;
086:
087: //MT: protected by class' lock
088: //a weak, unsynchronized coalescer
089: private static Coalescer idtCoalescer = CoalescerFactory
090: .createCoalescer(CC, true, false);
091:
092: //MT: protected by class' lock
093: private static Map tokensToTokenized = new DoubleWeakHashMap();
094:
095: //MT: protected by class' lock
096: private static HashSet unclosedPooledDataSources = new HashSet();
097:
098: //MT: protected by its own lock
099: private static Map classNamesToConnectionTesters = Collections
100: .synchronizedMap(new HashMap());
101:
102: //MT: protected by its own lock
103: private static Map classNamesToConnectionCustomizers = Collections
104: .synchronizedMap(new HashMap());
105:
106: private static ManagementCoordinator mc;
107:
108: static {
109: classNamesToConnectionTesters.put(C3P0Defaults
110: .connectionTesterClassName(), C3P0Defaults
111: .connectionTester());
112:
113: String userManagementCoordinator = C3P0ConfigUtils
114: .getPropFileConfigProperty(MC_PARAM);
115: if (userManagementCoordinator != null) {
116: try {
117: mc = (ManagementCoordinator) Class.forName(
118: userManagementCoordinator).newInstance();
119: } catch (Exception e) {
120: if (logger.isLoggable(MLevel.WARNING))
121: logger
122: .log(
123: MLevel.WARNING,
124: "Could not instantiate user-specified ManagementCoordinator "
125: + userManagementCoordinator
126: + ". Using NullManagementCoordinator (c3p0 JMX management disabled!)",
127: e);
128: mc = new NullManagementCoordinator();
129: }
130: } else {
131: try {
132: Class.forName("java.lang.management.ManagementFactory");
133:
134: mc = (ManagementCoordinator) Class
135: .forName(
136: "com.mchange.v2.c3p0.management.ActiveManagementCoordinator")
137: .newInstance();
138: } catch (Exception e) {
139: if (logger.isLoggable(MLevel.INFO))
140: logger
141: .log(
142: MLevel.INFO,
143: "jdk1.5 management interfaces unavailable... JMX support disabled.",
144: e);
145: mc = new NullManagementCoordinator();
146: }
147: }
148: }
149:
150: public static ConnectionTester getConnectionTester(String className) {
151: try {
152: ConnectionTester out = (ConnectionTester) classNamesToConnectionTesters
153: .get(className);
154: if (out == null) {
155: out = (ConnectionTester) Class.forName(className)
156: .newInstance();
157: classNamesToConnectionTesters.put(className, out);
158: }
159: return out;
160: } catch (Exception e) {
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.", e);
165: return C3P0Defaults.connectionTester();
166: }
167: }
168:
169: public static ConnectionCustomizer getConnectionCustomizer(
170: String className) throws SQLException {
171: if (className == null)
172: return null;
173: else {
174: try {
175: ConnectionCustomizer out = (ConnectionCustomizer) classNamesToConnectionCustomizers
176: .get(className);
177: if (out == null) {
178: out = (ConnectionCustomizer) Class.forName(
179: className).newInstance();
180: classNamesToConnectionCustomizers.put(className,
181: out);
182: }
183: return out;
184: } catch (Exception e) {
185: if (logger.isLoggable(MLevel.WARNING))
186: logger.log(MLevel.WARNING,
187: "Could not create for find ConnectionCustomizer with class name '"
188: + className + "'.", e);
189: throw SqlUtils.toSQLException(e);
190: }
191: }
192: }
193:
194: // must be called from a static sync'ed method
195: private static void banner() {
196: if (!banner_printed) {
197: if (logger.isLoggable(MLevel.INFO))
198: logger.info("Initializing c3p0-"
199: + C3P0Substitutions.VERSION + " [built "
200: + C3P0Substitutions.TIMESTAMP + "; debug? "
201: + C3P0Substitutions.DEBUG + "; trace: "
202: + C3P0Substitutions.TRACE + ']');
203: banner_printed = true;
204: }
205: }
206:
207: // must be called from a static, sync'ed method
208: private static void attemptRegisterRegistryMBean() {
209: if (!registry_mbean_registered) {
210: mc.attemptManageC3P0Registry();
211: registry_mbean_registered = true;
212: }
213: }
214:
215: // must be called with class' lock
216: private static boolean isIncorporated(IdentityTokenized idt) {
217: return tokensToTokenized.keySet().contains(
218: idt.getIdentityToken());
219: }
220:
221: // must be called with class' lock
222: private static void incorporate(IdentityTokenized idt) {
223: tokensToTokenized.put(idt.getIdentityToken(), idt);
224: if (idt instanceof PooledDataSource) {
225: unclosedPooledDataSources.add(idt);
226: mc.attemptManagePooledDataSource((PooledDataSource) idt);
227: }
228: }
229:
230: public static synchronized IdentityTokenized reregister(
231: IdentityTokenized idt) {
232: if (idt instanceof PooledDataSource) {
233: banner();
234: attemptRegisterRegistryMBean();
235: }
236:
237: if (idt.getIdentityToken() == null)
238: throw new RuntimeException(
239: "[c3p0 issue] The identityToken of a registered object should be set prior to registration.");
240:
241: IdentityTokenized coalesceCheck = (IdentityTokenized) idtCoalescer
242: .coalesce(idt);
243:
244: if (!isIncorporated(coalesceCheck))
245: incorporate(coalesceCheck);
246:
247: return coalesceCheck;
248: }
249:
250: public static synchronized void markClosed(PooledDataSource pds) {
251: unclosedPooledDataSources.remove(pds);
252: mc.attemptUnmanagePooledDataSource(pds);
253: if (unclosedPooledDataSources.isEmpty()) {
254: mc.attemptUnmanageC3P0Registry();
255: registry_mbean_registered = false;
256: }
257: }
258:
259: public synchronized static Set getPooledDataSources() {
260: return (Set) unclosedPooledDataSources.clone();
261: }
262:
263: public synchronized static Set pooledDataSourcesByName(
264: String dataSourceName) {
265: Set out = new HashSet();
266: for (Iterator ii = unclosedPooledDataSources.iterator(); ii
267: .hasNext();) {
268: PooledDataSource pds = (PooledDataSource) ii.next();
269: if (pds.getDataSourceName().equals(dataSourceName))
270: out.add(pds);
271: }
272: return out;
273: }
274:
275: public synchronized static PooledDataSource pooledDataSourceByName(
276: String dataSourceName) {
277: for (Iterator ii = unclosedPooledDataSources.iterator(); ii
278: .hasNext();) {
279: PooledDataSource pds = (PooledDataSource) ii.next();
280: if (pds.getDataSourceName().equals(dataSourceName))
281: return pds;
282: }
283: return null;
284: }
285:
286: public synchronized static Set allIdentityTokens() {
287: Set out = Collections.unmodifiableSet(tokensToTokenized
288: .keySet());
289: //System.err.println( "allIdentityTokens(): " + out );
290: return out;
291: }
292:
293: public synchronized static Set allIdentityTokenized() {
294: HashSet out = new HashSet();
295: out.addAll(tokensToTokenized.values());
296: //System.err.println( "allIdentityTokenized(): " + out );
297: return Collections.unmodifiableSet(out);
298: }
299:
300: public synchronized static Set allPooledDataSources() {
301: Set out = Collections
302: .unmodifiableSet(unclosedPooledDataSources);
303: //System.err.println( "allPooledDataSources(): " + out );
304: return out;
305: }
306:
307: public synchronized static int getNumPooledDataSources() {
308: return unclosedPooledDataSources.size();
309: }
310:
311: public synchronized static int getNumPoolsAllDataSources()
312: throws SQLException {
313: int count = 0;
314: for (Iterator ii = unclosedPooledDataSources.iterator(); ii
315: .hasNext();) {
316: PooledDataSource pds = (PooledDataSource) ii.next();
317: count += pds.getNumUserPools();
318: }
319: return count;
320: }
321:
322: public synchronized int getNumThreadsAllThreadPools()
323: throws SQLException {
324: int count = 0;
325: for (Iterator ii = unclosedPooledDataSources.iterator(); ii
326: .hasNext();) {
327: PooledDataSource pds = (PooledDataSource) ii.next();
328: count += pds.getNumHelperThreads();
329: }
330: return count;
331: }
332: }
|