Esempio n. 1
0
/*
 *  The primary purpose of C3P0Registry is to maintain a mapping of "identityTokens"
 *  to c3p0 DataSources so that if the same DataSource is looked up (and deserialized
 *  or dereferenced) via JNDI, c3p0 can ensure that the same instance is always returned.
 *  But there are subtle issues here. If C3P0Registry maintains hard references to
 *  DataSources, then they can never be garbage collected. But if c3p0 retains only
 *  weak references, then applications that look up DataSources, then dereference them,
 *  and then re-look them up again (not a great idea, but not uncommon) might see
 *  distinct DataSources over multiple lookups.
 *
 *  C3P0 resolves this issue has followed: At first creation or lookup of a PooledDataSource,
 *  c3p0 creates a hard reference to that DataSource. So long as the DataSource has not
 *  been close()ed or DataSources.destroy()ed, subsequent lookups will consistently
 *  return the same DataSource. If the DataSource is never closed, then there is a potential
 *  memory leak (as well as the potential Thread leak and Connection leak). But if
 *  the DataSource is close()ed, only weak refernces to the DataSource will be retained.
 *  A lookup of a DataSource after it has been close()ed within the current VM may
 *  return the previously close()ed instance, or may return a fresh instance, depending
 *  on whether the weak reference has been cleared. In other words, the result of
 *  looking up a DataSource after having close()ed it in the current VM is undefined.
 *
 *  Note that unpooled c3p0 DataSources are always held by weak references, since
 *  they are never explicitly close()ed. The result of looking up an unpooled DataSource,
 *  modifying it, dereferencing it, and then relooking up is therefore undefined as well.
 *
 *  These issues are mostly academic. Under normal use scenarios, how c3p0 deals with
 *  maintaining its registry doesn't much matter. In the past, c3p0 maintained hard
 *  references to DataSources indefinitely. At least one user ran into side effects
 *  of the unwanted retention of old DataSources (in a process left to run for months
 *  at a time, and frequently reconstructing multiple DataSources), so now we take care
 *  to ensure that when users properly close() and dereference DataSources, they can
 *  indeed be garbage collected.
 */
public final class C3P0Registry {
  private static final String MC_PARAM = "com.mchange.v2.c3p0.management.ManagementCoordinator";

  // MT: thread-safe
  static final MLogger logger = MLog.getLogger(C3P0Registry.class);

  // MT: protected by class' lock
  static boolean banner_printed = false;

  // MT: protected by class' lock
  static boolean registry_mbean_registered = false;

  // MT: thread-safe, immutable
  private static CoalesceChecker CC = IdentityTokenizedCoalesceChecker.INSTANCE;

  // MT: protected by class' lock
  // a weak, unsynchronized coalescer
  private static Coalescer idtCoalescer = CoalescerFactory.createCoalescer(CC, true, false);

  // MT: protected by class' lock
  private static Map tokensToTokenized = new DoubleWeakHashMap();

  // MT: protected by class' lock
  private static HashSet unclosedPooledDataSources = new HashSet();

  // MT: protected by its own lock
  private static Map classNamesToConnectionTesters = Collections.synchronizedMap(new HashMap());

  // MT: protected by its own lock
  private static Map classNamesToConnectionCustomizers = Collections.synchronizedMap(new HashMap());

  private static ManagementCoordinator mc;

  static {
    classNamesToConnectionTesters.put(
        C3P0Defaults.connectionTesterClassName(), C3P0Defaults.connectionTester());

    String userManagementCoordinator = C3P0ConfigUtils.getPropsFileConfigProperty(MC_PARAM);
    if (userManagementCoordinator != null) {
      try {
        mc = (ManagementCoordinator) Class.forName(userManagementCoordinator).newInstance();
      } catch (Exception e) {
        if (logger.isLoggable(MLevel.WARNING))
          logger.log(
              MLevel.WARNING,
              "Could not instantiate user-specified ManagementCoordinator "
                  + userManagementCoordinator
                  + ". Using NullManagementCoordinator (c3p0 JMX management disabled!)",
              e);
        mc = new NullManagementCoordinator();
      }
    } else {
      try {
        Class.forName("java.lang.management.ManagementFactory");

        mc =
            (ManagementCoordinator)
                Class.forName("com.mchange.v2.c3p0.management.ActiveManagementCoordinator")
                    .newInstance();
      } catch (Exception e) {
        if (logger.isLoggable(MLevel.INFO))
          logger.log(
              MLevel.INFO, "jdk1.5 management interfaces unavailable... JMX support disabled.", e);
        mc = new NullManagementCoordinator();
      }
    }
  }

  public static ConnectionTester getConnectionTester(String className) {
    try {
      ConnectionTester out = (ConnectionTester) classNamesToConnectionTesters.get(className);
      if (out == null) {
        out = (ConnectionTester) Class.forName(className).newInstance();
        classNamesToConnectionTesters.put(className, out);
      }
      return out;
    } catch (Exception e) {
      if (logger.isLoggable(MLevel.WARNING))
        logger.log(
            MLevel.WARNING,
            "Could not create for find ConnectionTester with class name '"
                + className
                + "'. Using default.",
            e);
      return C3P0Defaults.connectionTester();
    }
  }

  public static ConnectionCustomizer getConnectionCustomizer(String className) throws SQLException {
    if (className == null) return null;
    else {
      try {
        ConnectionCustomizer out =
            (ConnectionCustomizer) classNamesToConnectionCustomizers.get(className);
        if (out == null) {
          out = (ConnectionCustomizer) Class.forName(className).newInstance();
          classNamesToConnectionCustomizers.put(className, out);
        }
        return out;
      } catch (Exception e) {
        if (logger.isLoggable(MLevel.WARNING))
          logger.log(
              MLevel.WARNING,
              "Could not create for find ConnectionCustomizer with class name '" + className + "'.",
              e);
        throw SqlUtils.toSQLException(e);
      }
    }
  }

  // must be called from a static sync'ed method
  private static void banner() {
    if (!banner_printed) {
      if (logger.isLoggable(MLevel.INFO))
        logger.info(
            "Initializing c3p0-"
                + C3P0Substitutions.VERSION
                + " [built "
                + C3P0Substitutions.TIMESTAMP
                + "; debug? "
                + C3P0Substitutions.DEBUG
                + "; trace: "
                + C3P0Substitutions.TRACE
                + ']');
      banner_printed = true;
    }
  }

  // must be called from a static, sync'ed method
  private static void attemptRegisterRegistryMBean() {
    if (!registry_mbean_registered) {
      mc.attemptManageC3P0Registry();
      registry_mbean_registered = true;
    }
  }

  // must be called with class' lock
  private static boolean isIncorporated(IdentityTokenized idt) {
    return tokensToTokenized.keySet().contains(idt.getIdentityToken());
  }

  // must be called with class' lock
  private static void incorporate(IdentityTokenized idt) {
    tokensToTokenized.put(idt.getIdentityToken(), idt);
    if (idt instanceof PooledDataSource) {
      unclosedPooledDataSources.add(idt);
      mc.attemptManagePooledDataSource((PooledDataSource) idt);
    }
  }

  public static synchronized IdentityTokenized reregister(IdentityTokenized idt) {
    if (idt instanceof PooledDataSource) {
      banner();
      attemptRegisterRegistryMBean();
    }

    if (idt.getIdentityToken() == null)
      throw new RuntimeException(
          "[c3p0 issue] The identityToken of a registered object should be set prior to registration.");

    IdentityTokenized coalesceCheck = (IdentityTokenized) idtCoalescer.coalesce(idt);

    if (!isIncorporated(coalesceCheck)) incorporate(coalesceCheck);

    return coalesceCheck;
  }

  public static synchronized void markClosed(PooledDataSource pds) {
    unclosedPooledDataSources.remove(pds);
    mc.attemptUnmanagePooledDataSource(pds);
    if (unclosedPooledDataSources.isEmpty()) {
      mc.attemptUnmanageC3P0Registry();
      registry_mbean_registered = false;
    }
  }

  public static synchronized Set getPooledDataSources() {
    return (Set) unclosedPooledDataSources.clone();
  }

  /** @return the set of all PooledDataSources sharing the given dataSourceName */
  public static synchronized Set pooledDataSourcesByName(String dataSourceName) {
    Set out = new HashSet();
    for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); ) {
      PooledDataSource pds = (PooledDataSource) ii.next();
      if (pds.getDataSourceName().equals(dataSourceName)) out.add(pds);
    }
    return out;
  }

  /**
   * <b>Note:</b> If multiple PooledDataSources in your JVM share the same <tt>dataSourceName</tt>,
   * which of those multiple DataSources will be returned by this method is undefined!
   *
   * @return a PooledDataSource with the given <tt>dataSourceName</tt>, if at least one exists.
   *     <tt>null</tt> otherwise.
   */
  public static synchronized PooledDataSource pooledDataSourceByName(String dataSourceName) {
    for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); ) {
      PooledDataSource pds = (PooledDataSource) ii.next();
      if (pds.getDataSourceName().equals(dataSourceName)) return pds;
    }
    return null;
  }

  public static synchronized Set allIdentityTokens() {
    Set out = Collections.unmodifiableSet(tokensToTokenized.keySet());
    // System.err.println( "allIdentityTokens(): " + out );
    return out;
  }

  public static synchronized Set allIdentityTokenized() {
    HashSet out = new HashSet();
    out.addAll(tokensToTokenized.values());
    // System.err.println( "allIdentityTokenized(): " + out );
    return Collections.unmodifiableSet(out);
  }

  public static synchronized Set allPooledDataSources() {
    Set out = Collections.unmodifiableSet(unclosedPooledDataSources);
    // System.err.println( "allPooledDataSources(): " + out );
    return out;
  }

  public static synchronized int getNumPooledDataSources() {
    return unclosedPooledDataSources.size();
  }

  public static synchronized int getNumPoolsAllDataSources() throws SQLException {
    int count = 0;
    for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); ) {
      PooledDataSource pds = (PooledDataSource) ii.next();
      count += pds.getNumUserPools();
    }
    return count;
  }

  public synchronized int getNumThreadsAllThreadPools() throws SQLException {
    int count = 0;
    for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); ) {
      PooledDataSource pds = (PooledDataSource) ii.next();
      count += pds.getNumHelperThreads();
    }
    return count;
  }
}
Esempio n. 2
0
 public static synchronized Set allIdentityTokenized() {
   HashSet out = new HashSet();
   out.addAll(tokensToTokenized.values());
   // System.err.println( "allIdentityTokenized(): " + out );
   return Collections.unmodifiableSet(out);
 }
Esempio n. 3
0
 public static synchronized Set allPooledDataSources() {
   Set out = Collections.unmodifiableSet(unclosedPooledDataSources);
   // System.err.println( "allPooledDataSources(): " + out );
   return out;
 }
Esempio n. 4
0
 public static synchronized Set allIdentityTokens() {
   Set out = Collections.unmodifiableSet(tokensToTokenized.keySet());
   // System.err.println( "allIdentityTokens(): " + out );
   return out;
 }