Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: swaldman/c3p0
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.10.2
Choose a base ref
...
head repository: swaldman/c3p0
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.11.0-pre1
Choose a head ref
  • 13 commits
  • 11 files changed
  • 1 contributor

Commits on Jan 29, 2025

  1. Copy the full SHA
    9c95f2d View commit details
  2. Copy the full SHA
    4f1d8f5 View commit details
  3. Trifurcate NewProxyConnection into AbstractNewProxyConnection, NewPro…

    …xyConnectionJdbc43Full, and NewProxyConnectionNoShardingKey to address Java7/8 fragility.
    swaldman committed Jan 29, 2025
    Copy the full SHA
    5b7f0fd View commit details
  4. Copy the full SHA
    16d439a View commit details

Commits on Feb 1, 2025

  1. Copy the full SHA
    4e42c82 View commit details
  2. Cap CHANGELOG for v0.10.2.

    swaldman committed Feb 1, 2025
    Copy the full SHA
    0137322 View commit details
  3. Copy the full SHA
    ddffff3 View commit details
  4. Double check that a Connection is still available after awaitAvailabl…

    …e terminates in BasicResourcePool.prelimCheckoutResource. Thanks C3Stones and xtb359
    swaldman committed Feb 1, 2025
    Copy the full SHA
    f7919b6 View commit details

Commits on Feb 10, 2025

  1. Use ReentrantLock rather than native synchronization for GooGooStatem…

    …entCache and CautiousStatementDestructionManager because native wait() pins loom threads.
    swaldman committed Feb 10, 2025
    Copy the full SHA
    2f14bf4 View commit details
  2. Copy the full SHA
    d0d7dc5 View commit details

Commits on Feb 12, 2025

  1. Redo locking/sigalling in BasicResourcePool using ReentrantLock and C…

    …ondition rather than native monitors and wait/notifyAll.
    swaldman committed Feb 12, 2025
    Copy the full SHA
    f317009 View commit details
  2. CHANGELOG updates.

    swaldman committed Feb 12, 2025
    Copy the full SHA
    60f185d View commit details
  3. Copy the full SHA
    bbebcc2 View commit details
18 changes: 18 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
c3p0-0.11.1-pre1
-- Rewrite BasicResourcePool and GooGooStatementCache in terms of
ReentrantLock and Condition.{await/signalAll} rather than native
monitors and wait()/notifyAll(), because wait() pins loom
virtual threads. Many thanks to alex-kormukhin for calling
attention to this issue and proposing the fix!
-- Double check after termination of BasicResourcePool.awaitAvailable(...)
that following notification/termination a resource (Connection) remains
available. Many thanks to C3Stones and xtb359 on github for calling
attention to this issue.
-- Prevent fragility under non-bootstrap / non-system CLASSLOADERs under
Java 8, where references to java.sql.ShardingKey not available under Java 8
provoke NoClassDefFoundError. Bifurcate Connection proxies, generate
version without sharding key methods under JVMs where ShardingKey is unavailable,
full JDBC 4.3 Connection API proxies where ShardingKey is present.
Thanks to Force Rs on github for finding the issue and suggesting the
solution!
c3p0-0.10.2
-- Prevent freeze due to re-waiting if a non-Exception Throwable occurs
while acquiring a PreparedStatement to cache. Thanks Totyo Totev!
c3p0-0.10.1
3 changes: 2 additions & 1 deletion build.mill
Original file line number Diff line number Diff line change
@@ -31,7 +31,8 @@ object `package` extends RootModule with JavaModule with PublishModule {

val organization = "com.mchange"
override def artifactName = T{"c3p0"}
override def publishVersion = T{"0.10.2"}

override def publishVersion = T{"0.11.0-pre1"}

// we are currently building in Java 11, but releasing Java 7 compatible class files
// for users of smaller JDBC subsets
2 changes: 1 addition & 1 deletion mill-build/build.mill
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import mill._, scalalib._

object `package` extends MillBuildRootModule {

val McjVersion = "0.3.1"
val McjVersion = "0.3.2"

object Dependency {
val MchangeCommonsJava = ivy"com.mchange:mchange-commons-java:${McjVersion}"
195 changes: 128 additions & 67 deletions proxy/src/com/mchange/v2/c3p0/codegen/JdbcProxyGenerator.java
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import java.io.*;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;
import com.mchange.v2.codegen.*;
import com.mchange.v2.codegen.intfc.*;
import com.mchange.v2.c3p0.C3P0ProxyConnection;
@@ -13,6 +14,22 @@ public abstract class JdbcProxyGenerator extends DelegatorGenerator
{
private final static boolean PREMATURE_DETACH_DEBUG = false;

// sharding key methods shouldn't be referred to under Java7/8, so we go through
// a lot of hassle to make separate Connection proxies that don't refer to them
// Thanks Force Rs on github for pointing this out and working through solutions!

private static Set findShardingKeyMethods()
{
Method[] allMethods = Connection.class.getMethods();
Set<Method> out = new HashSet<>();
for ( Method method : allMethods )
if (method.getName().startsWith("setShardingKey"))
out.add(method);
return out;
}

private static Set SHARDING_KEY_METHODS = findShardingKeyMethods();

JdbcProxyGenerator()
{
this.setGenerateInnerSetter( false );
@@ -73,10 +90,10 @@ protected void generateExtraDeclarations( Class intfcl, String genclass, Indente
{
super.generateExtraDeclarations( intfcl, genclass, iw );
iw.println();
iw.println("NewProxyConnection proxyCon;");
iw.println("AbstractNewProxyConnection proxyCon;");
iw.println();
iw.print( CodegenUtils.fqcnLastElement( genclass ) );
iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection, NewProxyConnection proxyCon )");
iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection, AbstractNewProxyConnection proxyCon )");
iw.println("{");
iw.upIndent();
iw.println("this( inner, parentPooledConnection );");
@@ -160,7 +177,7 @@ protected void generateExtraDeclarations( Class intfcl, String genclass, Indente
iw.println();
iw.println("Object creator;");
iw.println("Object creatorProxy;");
iw.println("NewProxyConnection proxyConn;");
iw.println("AbstractNewProxyConnection proxyConn;");
iw.println();
iw.print( CodegenUtils.fqcnLastElement( genclass ) );
iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection, Object c, Object cProxy )");
@@ -169,7 +186,7 @@ protected void generateExtraDeclarations( Class intfcl, String genclass, Indente
iw.println("this( inner, parentPooledConnection );");
iw.println("this.creator = c;");
iw.println("this.creatorProxy = cProxy;");
iw.println("if (creatorProxy instanceof NewProxyConnection) this.proxyConn = (NewProxyConnection) cProxy;");
iw.println("if (creatorProxy instanceof AbstractNewProxyConnection) this.proxyConn = (AbstractNewProxyConnection) cProxy;");
iw.downIndent();
iw.println("}");
}
@@ -369,7 +386,7 @@ protected void generateExtraDeclarations( Class intfcl, String genclass, Indente
// end concurrent-access-debug only!

iw.println("boolean is_cached;");
iw.println("NewProxyConnection creatorProxy;");
iw.println("AbstractNewProxyConnection creatorProxy;");
iw.println();
iw.println("// Although formally unnecessary, we sync access to myProxyResultSets on");
iw.println("// that set's own lock, in case clients (illegally but not uncommonly) close()");
@@ -390,7 +407,7 @@ protected void generateExtraDeclarations( Class intfcl, String genclass, Indente
iw.println();
iw.print( CodegenUtils.fqcnLastElement( genclass ) );
iw.println("( " + CodegenUtils.simpleClassName( intfcl ) +
" inner, NewPooledConnection parentPooledConnection, boolean cached, NewProxyConnection cProxy )");
" inner, NewPooledConnection parentPooledConnection, boolean cached, AbstractNewProxyConnection cProxy )");
iw.println("{");
iw.upIndent();
iw.println("this( inner, parentPooledConnection );");
@@ -455,18 +472,21 @@ protected void generateExtraImports( IndentedWriter iw ) throws IOException
// iw.println("}");
// }

static final class NewProxyConnectionGenerator extends JdbcProxyGenerator
static final class AbstractNewProxyConnectionGenerator extends JdbcProxyGenerator
{
String getInnerTypeName()
{ return "Connection"; }

{
this.setClassModifiers( Modifier.PUBLIC | Modifier.ABSTRACT );
this.setExtraInterfaces( new Class[] { C3P0ProxyConnection.class } );
this.setGenerateWrappingConstructor(false);
}

String getInnerTypeName()
{ return "Connection"; }

protected void generateDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
{
String mname = method.getName();

if ( jdbc4WrapperMethod( mname ) )
{
generateWrapperDelegateCode( intfcl, genclass, method, iw );
@@ -951,6 +971,26 @@ protected void generatePostDelegateCode( Class intfcl, String genclass, Method m
iw.println("finally");
iw.println("{ this.lock.unlock(); }");
}

// omit methods that refer to java.sql.ShardingKey from this abstract, parent Connection
// so that older JVMs without ShardingKey can safely use this class.
//
// Under newer JVMs, concrete NewProxyConnectionJdbc43 will include these methods.

private Set<Method> EXCLUDED_FROM_ABSTRACT_PARENT = SHARDING_KEY_METHODS;

private void generateFullDelegateMethod(Class intfcl, String genclass, Method method, IndentedWriter iw, boolean force) throws IOException
{
if ( force || !EXCLUDED_FROM_ABSTRACT_PARENT.contains( method ) )
super.generateFullDelegateMethod( intfcl, genclass, method, iw );
}

protected void generateFullDelegateMethod(Class intfcl, String genclass, Method method, IndentedWriter iw) throws IOException
{ this.generateFullDelegateMethod( intfcl, genclass, method, iw, false ); }

// for generating sublasses with extra API, particularly the sharding key stuff that should that has us bifurcating the proxies...
void subclassGenerateFullDelegateMethod( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
{ this.generateFullDelegateMethod( intfcl, genclass, method, iw, true ); }
}

//totally superfluous, but included to be "regular" and very specific, and as a hook for "general" overrides in future
@@ -1099,7 +1139,7 @@ protected void generateExtraDeclarations( Class intfcl, String genclass, Indente
iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection )");
iw.println("{");
iw.upIndent();
iw.println("this( inner );");
iw.println("this.inner = inner;");
iw.println("attach( parentPooledConnection );");
generateExtraConstructorCode( intfcl, genclass, iw );
iw.downIndent();
@@ -1193,7 +1233,40 @@ else if ("unwrap".equals( mname ))

private static boolean jdbc4WrapperMethod(String mname)
{ return "unwrap".equals(mname) || "isWrapperFor".equals(mname); }


private static String generateFinalNewProxyConnection(String fqcn, AbstractNewProxyConnectionGenerator ag, Set<Method> methodsToDelegate) throws IOException
{
int lastDot = fqcn.lastIndexOf(".");
String pkg = fqcn.substring(0,lastDot);
String sname = fqcn.substring(lastDot+1);

StringWriter sw = new StringWriter();
IndentedWriter iw = new IndentedWriter(sw);
iw.println("/* Autogenerated code!!! DO NOT HAND EDIT! */");
iw.println();
iw.println("package " + pkg + ";");
iw.println();
iw.println("import java.sql.*;");
iw.println("import com.mchange.v2.sql.SqlUtils;");
iw.println("import com.mchange.v2.c3p0.C3P0ProxyConnection;");
iw.println();
iw.println("public final class " + sname + " extends AbstractNewProxyConnection implements Connection, C3P0ProxyConnection");
iw.println("{");
iw.upIndent();
iw.println();
iw.println("public " + sname + "(Connection inner, NewPooledConnection parentPooledConnection) { super(inner, parentPooledConnection); }");
iw.println();
for (Method m : methodsToDelegate)
{
ag.subclassGenerateFullDelegateMethod( Connection.class, fqcn, m, iw );
iw.println();
}
iw.downIndent();
iw.println("}");
iw.println();
return sw.toString();
}

public static void main( String[] argv )
{
try
@@ -1216,58 +1289,77 @@ public static void main( String[] argv )
DelegatorGenerator mdgen = new NewProxyMetaDataGenerator();
DelegatorGenerator rsgen = new NewProxyResultSetGenerator();
DelegatorGenerator stgen = new NewProxyAnyStatementGenerator();
DelegatorGenerator cngen = new NewProxyConnectionGenerator();

/*
* Eliminating for c3p0-0.9.5
AbstractNewProxyConnectionGenerator acngen = new AbstractNewProxyConnectionGenerator();

// Usually stgen can be used for all three Statement types. However, we have temporarily
// implemented some very partial JDBC4 methods to maintain Hibernate support, prior to full JDBC4
// support in a future version. To do this, we'll need to force some methods into the PreparedStatement
// (and therefore CallableStatement) interfaces, methods that don't exist in the current JDBC3 build.
// We should be able to get rid of psgen (in favor of stgen above) when we are actually building against
// JDBC4 (so we don't need to artificially inject methods).
//DelegatorGenerator psgen = new NewProxyAnyStatementGenerator();
//psgen.setReflectiveDelegateMethods( JDBC4TemporaryPreparedStatementMethods.class.getMethods() );
*/

genclass( cngen, Connection.class, "com.mchange.v2.c3p0.impl.NewProxyConnection", srcroot );
genclass( acngen, Connection.class, "com.mchange.v2.c3p0.impl.AbstractNewProxyConnection", srcroot );
genclass( stgen, Statement.class, "com.mchange.v2.c3p0.impl.NewProxyStatement", srcroot );
//genclass( psgen, PreparedStatement.class, "com.mchange.v2.c3p0.impl.NewProxyPreparedStatement", srcroot );
//genclass( psgen, CallableStatement.class, "com.mchange.v2.c3p0.impl.NewProxyCallableStatement", srcroot );
genclass( stgen, PreparedStatement.class, "com.mchange.v2.c3p0.impl.NewProxyPreparedStatement", srcroot );
genclass( stgen, CallableStatement.class, "com.mchange.v2.c3p0.impl.NewProxyCallableStatement", srcroot );
genclass( rsgen, ResultSet.class, "com.mchange.v2.c3p0.impl.NewProxyResultSet", srcroot );
genclass( mdgen, DatabaseMetaData.class, "com.mchange.v2.c3p0.impl.NewProxyDatabaseMetaData", srcroot );

String fqcnNewProxyConnectionNoShardingKey = "com.mchange.v2.c3p0.impl.NewProxyConnectionNoShardingKey";
String fqcnNewProxyConnectionJdbc43Full = "com.mchange.v2.c3p0.impl.NewProxyConnectionJdbc43Full";
String pregenNewProxyConnectionNoShardingKey = generateFinalNewProxyConnection(fqcnNewProxyConnectionNoShardingKey, acngen, Collections.EMPTY_SET);
String pregenNewProxyConnectionJdbc43Full = generateFinalNewProxyConnection(fqcnNewProxyConnectionJdbc43Full, acngen, SHARDING_KEY_METHODS);

genclass( pregenNewProxyConnectionNoShardingKey, fqcnNewProxyConnectionNoShardingKey, srcroot );
genclass( pregenNewProxyConnectionJdbc43Full, fqcnNewProxyConnectionJdbc43Full, srcroot );
}
catch ( Exception e )
{ e.printStackTrace(); }
}

static void genclass( DelegatorGenerator dg, Class intfcl, String fqcn, File srcroot ) throws IOException
{
File genDir = new File( srcroot, dirForFqcn( fqcn ) );
if (! genDir.exists() )
File genDir = findEnsureDirectory( fqcn, srcroot );
String fileName = CodegenUtils.fqcnLastElement( fqcn ) + ".java";
Writer w = null;
try
{
System.err.println( JdbcProxyGenerator.class.getName() + " -- creating directory: " + genDir.getAbsolutePath() );
genDir.mkdirs();
w = new BufferedWriter( new FileWriter( new File( genDir, fileName ) ) );
dg.writeDelegator( intfcl, fqcn, w );
w.flush();
System.err.println("Generated " + fileName + " from DelegatorGenerator");
}
finally
{
try { if (w != null) w.close(); }
catch ( Exception e )
{ e.printStackTrace(); }
}
}

static void genclass( String pregenerated, String fqcn, File srcroot ) throws IOException
{
File genDir = findEnsureDirectory( fqcn, srcroot );
String fileName = CodegenUtils.fqcnLastElement( fqcn ) + ".java";
Writer w = null;
try
{
w = new BufferedWriter( new FileWriter( new File( genDir, fileName ) ) );
dg.writeDelegator( intfcl, fqcn, w );
w.write(pregenerated);
w.flush();
System.err.println("Generated " + fileName);
System.err.println("Generated " + fileName + " (directly)");
}
finally
{
try { if (w != null) w.close(); }
catch ( Exception e )
{ e.printStackTrace(); }
}
}
}

static File findEnsureDirectory( String fqcn, File srcroot ) throws IOException
{
File genDir = new File( srcroot, dirForFqcn( fqcn ) );
if (! genDir.exists() )
{
System.err.println( JdbcProxyGenerator.class.getName() + " -- creating directory: " + genDir.getAbsolutePath() );
genDir.mkdirs();
}
return genDir;
}

static String dirForFqcn( String fqcn )
@@ -1280,35 +1372,4 @@ static String dirForFqcn( String fqcn )
return sb.toString();
}

/*
* TEMPORARY CRITICAL JDBC4 METHOD SUPPORT (for Hibernate)
*
* As of c3p0-0.9.2, JDBC4 remains unsupported. Full support of Statement caching
* given new means of generating PreparedStatements will require a significant update
* of the statement cache, which just isn't there yet. However, Hibernate now uses some
* JDBC4 methods, and we'll add a temporary fix to support those methods until the
* JDBC4 spec is fully supported.
*/

/*
* Eliminating for c3p0-0.9.5
*
interface JDBC4TemporaryPreparedStatementMethods
{
public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException;
public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException;
public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException;
public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException;
public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException;
public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException;
public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException;
public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException;
public void setClob(int parameterIndex, Reader reader, long length) throws SQLException;
public void setClob(int parameterIndex, Reader reader) throws SQLException;
// test only
// public void finalize() throws Throwable;
}
*/
}
2 changes: 1 addition & 1 deletion src/com/mchange/v2/c3p0/impl/C3P0ImplUtils.java
Original file line number Diff line number Diff line change
@@ -316,7 +316,7 @@ public Object run()
* never intended to be called. we just want a compiler error if somehow we are building/code-generating
* against an old version of JDBC, as happened somehow with the c3p0-0.9.5-pre2 release
*/
public static void assertCompileTimePresenceOfJdbc4_Jdk17Api( NewProxyConnection npc ) throws SQLException
public static void assertCompileTimePresenceOfJdbc4_Jdk17Api( AbstractNewProxyConnection npc ) throws SQLException
{ npc.getNetworkTimeout(); }

private C3P0ImplUtils()
Loading