From 2377a67bbbc441c50e0d6c53a278b42ef92c8c2e Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Fri, 9 May 2008 04:03:04 +0000
Subject: Hide JDBCRepository class.

---
 .../amazon/carbonado/repo/jdbc/JDBCBlobLoader.java |   2 +-
 .../amazon/carbonado/repo/jdbc/JDBCClobLoader.java |   2 +-
 .../amazon/carbonado/repo/jdbc/JDBCRepository.java |  17 +-
 .../carbonado/repo/jdbc/JDBCRepositoryBuilder.java |  12 +-
 .../carbonado/repo/jdbc/JDBCStorableGenerator.java | 197 +++++++++++----------
 .../repo/jdbc/JDBCStorableIntrospector.java        |  36 +++-
 .../amazon/carbonado/repo/jdbc/JDBCSupport.java    |   6 +-
 .../carbonado/repo/jdbc/SupportResolver.java       |  43 +++++
 8 files changed, 197 insertions(+), 118 deletions(-)
 create mode 100644 src/main/java/com/amazon/carbonado/repo/jdbc/SupportResolver.java

(limited to 'src/main')

diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCBlobLoader.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCBlobLoader.java
index cd818c0..e8464e3 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCBlobLoader.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCBlobLoader.java
@@ -29,5 +29,5 @@ public interface JDBCBlobLoader {
     /**
      * @return Blob or null if missing
      */
-    java.sql.Blob load(JDBCRepository jdbcRepo) throws FetchException;
+    java.sql.Blob load(JDBCConnectionCapability cap) throws FetchException;
 }
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCClobLoader.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCClobLoader.java
index 9e3470a..0a980bb 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCClobLoader.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCClobLoader.java
@@ -29,5 +29,5 @@ public interface JDBCClobLoader {
     /**
      * @return Clob or null if missing
      */
-    java.sql.Clob load(JDBCRepository jdbcRepo) throws FetchException;
+    java.sql.Clob load(JDBCConnectionCapability cap) throws FetchException;
 }
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepository.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepository.java
index 250b54e..870ab34 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepository.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepository.java
@@ -51,6 +51,7 @@ import com.amazon.carbonado.capability.IndexInfo;
 import com.amazon.carbonado.capability.IndexInfoCapability;
 import com.amazon.carbonado.capability.ShutdownCapability;
 import com.amazon.carbonado.capability.StorableInfoCapability;
+import com.amazon.carbonado.info.StorableIntrospector;
 import com.amazon.carbonado.info.StorableProperty;
 import com.amazon.carbonado.sequence.SequenceCapability;
 import com.amazon.carbonado.sequence.SequenceValueProducer;
@@ -70,8 +71,7 @@ import com.amazon.carbonado.util.ThrowUnchecked;
  * @author bcastill
  * @see JDBCRepositoryBuilder
  */
-// Note: this class must be public because auto-generated code needs access to it
-public class JDBCRepository extends AbstractRepository<JDBCTransaction>
+class JDBCRepository extends AbstractRepository<JDBCTransaction>
     implements Repository,
                IndexInfoCapability,
                ShutdownCapability,
@@ -195,6 +195,8 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
     private final JDBCSupportStrategy mSupportStrategy;
     private JDBCExceptionTransformer mExceptionTransformer;
 
+    private final SupportResolver mResolver;
+
     private final JDBCTransactionManager mTxnMgr;
 
     // Mappings from IsolationLevel to best matching supported level.
@@ -224,7 +226,8 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
                    Integer fetchSize,
                    Map<String, Boolean> autoVersioningMap,
                    Map<String, Boolean> suppressReloadMap,
-                   String sequenceSelectStatement, boolean forceStoredSequence)
+                   String sequenceSelectStatement, boolean forceStoredSequence,
+                   SupportResolver resolver)
         throws RepositoryException
     {
         super(name);
@@ -243,6 +246,8 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
         mAutoVersioningMap = autoVersioningMap;
         mSuppressReloadMap = suppressReloadMap;
 
+        mResolver = resolver;
+
         mOpenConnections = new IdentityHashMap<Connection, Object>();
         mOpenConnectionsLock = new ReentrantLock(true);
 
@@ -327,7 +332,6 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
     /**
      * Returns true if a transaction is in progress and it is for update.
      */
-    // Is called by auto-generated code and must be public.
     public boolean isTransactionForUpdate() {
         return localTransactionScope().isForUpdate();
     }
@@ -344,7 +348,8 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
         throws RepositoryException, SupportException
     {
         try {
-            return JDBCStorableIntrospector.examine(type, mDataSource, mCatalog, mSchema);
+            return JDBCStorableIntrospector
+                .examine(type, mDataSource, mCatalog, mSchema, mResolver);
         } catch (SQLException e) {
             throw toRepositoryException(e);
         }
@@ -417,7 +422,6 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
      * Any connection returned by this method must be closed by calling
      * yieldConnection on this repository.
      */
-    // Note: This method must be public for auto-generated code to access it.
     public Connection getConnection() throws FetchException {
         try {
             if (mOpenConnections == null) {
@@ -493,7 +497,6 @@ public class JDBCRepository extends AbstractRepository<JDBCTransaction>
      * Gives up a connection returned from getConnection. Connection must be
      * yielded in same thread that retrieved it.
      */
-    // Note: This method must be public for auto-generated code to access it.
     public void yieldConnection(Connection con) throws FetchException {
         try {
             if (con.getAutoCommit()) {
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepositoryBuilder.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepositoryBuilder.java
index 9996aac..9117884 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepositoryBuilder.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCRepositoryBuilder.java
@@ -75,7 +75,9 @@ public class JDBCRepositoryBuilder extends AbstractRepositoryBuilder {
     private Map<String, Boolean> mSuppressReloadMap;
     private String mSequenceSelectStatement;
     private boolean mForceStoredSequence;
-    
+
+    private SupportResolver mResolver;
+
     public JDBCRepositoryBuilder() {
     }
 
@@ -88,7 +90,8 @@ public class JDBCRepositoryBuilder extends AbstractRepositoryBuilder {
              mFetchSize,
              getAutoVersioningMap(),
              getSuppressReloadMap(),
-             mSequenceSelectStatement, mForceStoredSequence);
+             mSequenceSelectStatement, mForceStoredSequence,
+             mResolver);
         rootRef.set(repo);
         return repo;
     }
@@ -408,4 +411,9 @@ public class JDBCRepositoryBuilder extends AbstractRepositoryBuilder {
             }
         }
     }
+
+    // Experimental feature.
+    void setSupportResolver(SupportResolver resolver) {
+        mResolver = resolver;
+    }
 }
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java
index 933817e..5207452 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java
@@ -181,7 +181,6 @@ class JDBCStorableGenerator<S extends Storable> {
         final Map<JDBCStorableProperty<S>, Class<?>> lobLoaderMap = generateLobLoaders();
 
         // Declare some types.
-        final TypeDesc jdbcRepoType = TypeDesc.forClass(JDBCRepository.class);
         final TypeDesc jdbcSupportType = TypeDesc.forClass(JDBCSupport.class);
         final TypeDesc resultSetType = TypeDesc.forClass(ResultSet.class);
         final TypeDesc connectionType = TypeDesc.forClass(Connection.class);
@@ -299,34 +298,34 @@ class JDBCStorableGenerator<S extends Storable> {
             mi.addException(TypeDesc.forClass(FetchException.class));
             CodeBuilder b = new CodeBuilder(mi);
 
-            LocalVariable repoVar = getJDBCRepository(b);
+            LocalVariable supportVar = getJDBCSupport(b);
             Label tryBeforeCon = b.createLabel().setLocation();
-            LocalVariable conVar = getConnection(b, repoVar);
+            LocalVariable conVar = getConnection(b, supportVar);
             Label tryAfterCon = b.createLabel().setLocation();
 
             b.loadThis();
-            b.loadLocal(repoVar);
+            b.loadLocal(supportVar);
             b.loadLocal(conVar);
             b.loadNull(); // No Lobs to update
             b.invokeVirtual(MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME,
                             TypeDesc.BOOLEAN,
-                            new TypeDesc[] {jdbcRepoType, connectionType, lobArrayType});
+                            new TypeDesc[] {jdbcSupportType, connectionType, lobArrayType});
             LocalVariable resultVar = b.createLocalVariable("result", TypeDesc.BOOLEAN);
             b.storeLocal(resultVar);
 
-            yieldConAndHandleException(b, repoVar, tryBeforeCon, conVar, tryAfterCon, false);
+            yieldConAndHandleException(b, supportVar, tryBeforeCon, conVar, tryAfterCon, false);
 
             b.loadLocal(resultVar);
             b.returnValue(TypeDesc.BOOLEAN);
         }
 
-        // Now define doTryLoad(JDBCRepositry, Connection, Lob[]). The Lob array argument
+        // Now define doTryLoad(JDBCSupport, Connection, Lob[]). The Lob array argument
         // is optional, and it indicates which (large) Lobs should be updated upon load.
         {
             MethodInfo mi = mClassFile.addMethod
                 (Modifiers.PROTECTED,
                  MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME, TypeDesc.BOOLEAN,
-                 new TypeDesc[] {jdbcRepoType, connectionType, lobArrayType});
+                 new TypeDesc[] {jdbcSupportType, connectionType, lobArrayType});
             mi.addException(TypeDesc.forClass(Exception.class));
             CodeBuilder b = new CodeBuilder(mi);
 
@@ -418,13 +417,16 @@ class JDBCStorableGenerator<S extends Storable> {
             Label tryEnd = b.createLabel().setLocation();
             b.returnVoid();
 
+            b.exceptionHandler(tryStart, tryEnd, RuntimeException.class.getName());
+            b.throwObject();
+
             b.exceptionHandler(tryStart, tryEnd, Exception.class.getName());
-            pushJDBCRepository(b);
-            // Swap exception object and JDBCRepository instance.
+            pushJDBCSupport(b);
+            // Swap exception object and JDBCSupport instance.
             b.swap();
             TypeDesc[] params = {TypeDesc.forClass(Throwable.class)};
-            b.invokeVirtual(jdbcRepoType, "toPersistException",
-                            TypeDesc.forClass(PersistException.class), params);
+            b.invokeInterface(jdbcSupportType, "toPersistException",
+                              TypeDesc.forClass(PersistException.class), params);
             b.throwObject();
         }
 
@@ -444,11 +446,11 @@ class JDBCStorableGenerator<S extends Storable> {
 
             b.exceptionHandler(tryStart, innerTryEnd, SQLException.class.getName());
             b.dup(); // dup the SQLException
-            pushJDBCRepository(b);
+            pushJDBCSupport(b);
             b.swap(); // swap the dup'ed SQLException to pass to method
-            b.invokeVirtual(jdbcRepoType, "isUniqueConstraintError",
-                            TypeDesc.BOOLEAN,
-                            new TypeDesc[] {TypeDesc.forClass(SQLException.class)});
+            b.invokeInterface(jdbcSupportType, "isUniqueConstraintError",
+                              TypeDesc.BOOLEAN,
+                              new TypeDesc[] {TypeDesc.forClass(SQLException.class)});
             Label notConstraint = b.createLabel();
             b.ifZeroComparisonBranch(notConstraint, "==");
             // Return false to indicate unique constraint violation.
@@ -461,13 +463,16 @@ class JDBCStorableGenerator<S extends Storable> {
 
             Label outerTryEnd = b.createLabel().setLocation();
 
+            b.exceptionHandler(tryStart, outerTryEnd, RuntimeException.class.getName());
+            b.throwObject();
+
             b.exceptionHandler(tryStart, outerTryEnd, Exception.class.getName());
-            pushJDBCRepository(b);
-            // Swap exception object and JDBCRepository instance.
+            pushJDBCSupport(b);
+            // Swap exception object and JDBCSupport instance.
             b.swap();
             TypeDesc[] params = {TypeDesc.forClass(Throwable.class)};
-            b.invokeVirtual(jdbcRepoType, "toPersistException",
-                            TypeDesc.forClass(PersistException.class), params);
+            b.invokeInterface(jdbcSupportType, "toPersistException",
+                              TypeDesc.forClass(PersistException.class), params);
             b.throwObject();
         }
 
@@ -479,8 +484,8 @@ class JDBCStorableGenerator<S extends Storable> {
             mi.addException(TypeDesc.forClass(PersistException.class));
             CodeBuilder b = new CodeBuilder(mi);
 
-            LocalVariable repoVar = getJDBCRepository(b);
-            LocalVariable conVar = getConnection(b, repoVar);
+            LocalVariable supportVar = getJDBCSupport(b);
+            LocalVariable conVar = getConnection(b, supportVar);
             Label tryAfterCon = b.createLabel().setLocation();
 
             // Push connection in preparation for preparing a statement.
@@ -752,7 +757,7 @@ class JDBCStorableGenerator<S extends Storable> {
                 // progress at this point.
 
                 b.loadThis();
-                b.loadLocal(repoVar);
+                b.loadLocal(supportVar);
                 b.loadLocal(conVar);
                 if (lobArrayVar == null) {
                     b.loadNull();
@@ -761,13 +766,13 @@ class JDBCStorableGenerator<S extends Storable> {
                 }
                 b.invokeVirtual(MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME,
                                 TypeDesc.BOOLEAN,
-                                new TypeDesc[] {jdbcRepoType, connectionType, lobArrayType});
+                                new TypeDesc[] {jdbcSupportType, connectionType, lobArrayType});
                 b.pop();
             }
 
             // Note: yieldConAndHandleException is not called, allowing any
             // SQLException to be thrown. The insert or tryInsert methods must handle it.
-            yieldCon(b, repoVar, conVar, tryAfterCon);
+            yieldCon(b, supportVar, conVar, tryAfterCon);
 
             b.loadConstant(true);
             b.returnValue(TypeDesc.BOOLEAN);
@@ -786,9 +791,9 @@ class JDBCStorableGenerator<S extends Storable> {
             // Only update properties with state DIRTY. Therefore, update
             // statement is always dynamic.
 
-            LocalVariable repoVar = getJDBCRepository(b);
+            LocalVariable supportVar = getJDBCSupport(b);
             Label tryBeforeCon = b.createLabel().setLocation();
-            LocalVariable conVar = getConnection(b, repoVar);
+            LocalVariable conVar = getConnection(b, supportVar);
             Label tryAfterCon = b.createLabel().setLocation();
 
             // Load connection in preparation for creating statement.
@@ -1105,7 +1110,7 @@ class JDBCStorableGenerator<S extends Storable> {
                 // progress at this point.
 
                 b.loadThis();
-                b.loadLocal(repoVar);
+                b.loadLocal(supportVar);
                 b.loadLocal(conVar);
                 if (lobArrayVar == null) {
                     b.loadNull();
@@ -1114,7 +1119,7 @@ class JDBCStorableGenerator<S extends Storable> {
                 }
                 b.invokeVirtual(MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME,
                                 TypeDesc.BOOLEAN,
-                                new TypeDesc[] {jdbcRepoType, connectionType, lobArrayType});
+                                new TypeDesc[] {jdbcSupportType, connectionType, lobArrayType});
                 // Even though a boolean is returned, the actual value for true and
                 // false is an int, 1 or 0.
                 b.storeLocal(updateCount);
@@ -1122,7 +1127,7 @@ class JDBCStorableGenerator<S extends Storable> {
 
             skipReload.setLocation();
 
-            yieldConAndHandleException(b, repoVar, tryBeforeCon, conVar, tryAfterCon, true);
+            yieldConAndHandleException(b, supportVar, tryBeforeCon, conVar, tryAfterCon, true);
 
             b.loadLocal(updateCount);
             b.returnValue(TypeDesc.BOOLEAN);
@@ -1140,9 +1145,9 @@ class JDBCStorableGenerator<S extends Storable> {
             deleteBuilder.append("DELETE FROM ");
             deleteBuilder.append(mInfo.getQualifiedTableName());
 
-            LocalVariable repoVar = getJDBCRepository(b);
+            LocalVariable supportVar = getJDBCSupport(b);
             Label tryBeforeCon = b.createLabel().setLocation();
-            LocalVariable conVar = getConnection(b, repoVar);
+            LocalVariable conVar = getConnection(b, supportVar);
             Label tryAfterCon = b.createLabel().setLocation();
 
             LocalVariable psVar = b.createLocalVariable("ps", preparedStatementType);
@@ -1160,7 +1165,7 @@ class JDBCStorableGenerator<S extends Storable> {
             b.storeLocal(resultVar);
 
             closeStatement(b, psVar, tryAfterPs);
-            yieldConAndHandleException(b, repoVar, tryBeforeCon, conVar, tryAfterCon, true);
+            yieldConAndHandleException(b, supportVar, tryBeforeCon, conVar, tryAfterCon, true);
 
             b.loadLocal(resultVar);
             b.returnValue(TypeDesc.BOOLEAN);
@@ -1212,24 +1217,15 @@ class JDBCStorableGenerator<S extends Storable> {
     }
 
     /**
-     * Generates code to get the JDBCRepository instance and store it in a
-     * local variable.
+     * Generates code to get the JDBCSupport instance and store it in a local
+     * variable.
      */
-    private LocalVariable getJDBCRepository(CodeBuilder b) {
-        pushJDBCRepository(b);
-        LocalVariable repoVar =
-            b.createLocalVariable("repo", TypeDesc.forClass(JDBCRepository.class));
-        b.storeLocal(repoVar);
-        return repoVar;
-    }
-
-    /**
-     * Generates code to push the JDBCRepository instance on the stack.
-     */
-    private void pushJDBCRepository(CodeBuilder b) {
+    private LocalVariable getJDBCSupport(CodeBuilder b) {
         pushJDBCSupport(b);
-        b.invokeInterface(TypeDesc.forClass(JDBCSupport.class), "getJDBCRepository",
-                          TypeDesc.forClass(JDBCRepository.class), null);
+        LocalVariable supportVar =
+            b.createLocalVariable("support", TypeDesc.forClass(JDBCSupport.class));
+        b.storeLocal(supportVar);
+        return supportVar;
     }
 
     /**
@@ -1242,14 +1238,15 @@ class JDBCStorableGenerator<S extends Storable> {
     }
 
     /**
-     * Generates code to get connection from JDBCRepository and store it in a local variable.
+     * Generates code to get connection from JDBCConnectionCapability and store
+     * it in a local variable.
      *
-     * @param repoVar reference to JDBCRepository
+     * @param capVar reference to JDBCConnectionCapability
      */
-    private LocalVariable getConnection(CodeBuilder b, LocalVariable repoVar) {
-        b.loadLocal(repoVar);
-        b.invokeVirtual(TypeDesc.forClass(JDBCRepository.class),
-                        "getConnection", TypeDesc.forClass(Connection.class), null);
+    private LocalVariable getConnection(CodeBuilder b, LocalVariable capVar) {
+        b.loadLocal(capVar);
+        b.invokeInterface(TypeDesc.forClass(JDBCConnectionCapability.class),
+                          "getConnection", TypeDesc.forClass(Connection.class), null);
         LocalVariable conVar = b.createLocalVariable("con", TypeDesc.forClass(Connection.class));
         b.storeLocal(conVar);
         return conVar;
@@ -1259,18 +1256,18 @@ class JDBCStorableGenerator<S extends Storable> {
      * Generates code which emulates this:
      *
      *     // May throw FetchException
-     *     JDBCRepository.yieldConnection(con);
+     *     JDBCConnectionCapability.yieldConnection(con);
      *
-     * @param repoVar required reference to JDBCRepository
+     * @param capVar required reference to JDBCConnectionCapability
      * @param conVar optional connection to yield
      */
-    private void yieldConnection(CodeBuilder b, LocalVariable repoVar, LocalVariable conVar) {
+    private void yieldConnection(CodeBuilder b, LocalVariable capVar, LocalVariable conVar) {
         if (conVar != null) {
-            b.loadLocal(repoVar);
+            b.loadLocal(capVar);
             b.loadLocal(conVar);
-            b.invokeVirtual(TypeDesc.forClass(JDBCRepository.class),
-                            "yieldConnection", null,
-                            new TypeDesc[] {TypeDesc.forClass(Connection.class)});
+            b.invokeInterface(TypeDesc.forClass(JDBCConnectionCapability.class),
+                              "yieldConnection", null,
+                              new TypeDesc[] {TypeDesc.forClass(Connection.class)});
         }
     }
 
@@ -1282,7 +1279,7 @@ class JDBCStorableGenerator<S extends Storable> {
      * @param sqlBuilder contains SQL statement right before the WHERE clause
      * @param conVar local variable referencing connection
      * @param psVar declared local variable which will receive PreparedStatement
-     * @param jdbcRepoVar when non-null, check transaction if SELECT should be FOR UPDATE
+     * @param capVar when non-null, check transaction if SELECT should be FOR UPDATE
      * @param instanceVar when null, assume properties are contained in
      * "this". Otherwise, invoke property access methods on storable referenced
      * in var.
@@ -1295,7 +1292,7 @@ class JDBCStorableGenerator<S extends Storable> {
          StringBuilder sqlBuilder,
          LocalVariable conVar,
          LocalVariable psVar,
-         LocalVariable jdbcRepoVar,
+         LocalVariable capVar,
          LocalVariable instanceVar)
         throws SupportException
     {
@@ -1332,10 +1329,10 @@ class JDBCStorableGenerator<S extends Storable> {
             b.loadConstant(sqlBuilder.toString());
 
             // Determine at runtime if SELECT should be " FOR UPDATE".
-            if (jdbcRepoVar != null) {
-                b.loadLocal(jdbcRepoVar);
-                b.invokeVirtual
-                    (jdbcRepoVar.getType(), "isTransactionForUpdate", TypeDesc.BOOLEAN, null);
+            if (capVar != null) {
+                b.loadLocal(capVar);
+                b.invokeInterface(TypeDesc.forClass(JDBCConnectionCapability.class),
+                                  "isTransactionForUpdate", TypeDesc.BOOLEAN, null);
                 Label notForUpdate = b.createLabel();
                 b.ifZeroComparisonBranch(notForUpdate, "==");
 
@@ -1406,10 +1403,10 @@ class JDBCStorableGenerator<S extends Storable> {
             }
 
             // Determine at runtime if SELECT should be " FOR UPDATE".
-            if (jdbcRepoVar != null) {
-                b.loadLocal(jdbcRepoVar);
-                b.invokeVirtual
-                    (jdbcRepoVar.getType(), "isTransactionForUpdate", TypeDesc.BOOLEAN, null);
+            if (capVar != null) {
+                b.loadLocal(capVar);
+                b.invokeInterface(TypeDesc.forClass(JDBCConnectionCapability.class),
+                                  "isTransactionForUpdate", TypeDesc.BOOLEAN, null);
                 Label notForUpdate = b.createLabel();
                 b.ifZeroComparisonBranch(notForUpdate, "==");
 
@@ -1669,27 +1666,27 @@ class JDBCStorableGenerator<S extends Storable> {
      *
      * ...
      * } finally {
-     *     JDBCRepository.yieldConnection(con);
+     *     JDBCConnectionCapability.yieldConnection(con);
      * }
      *
-     * @param repoVar required reference to JDBCRepository
+     * @param capVar required reference to JDBCConnectionCapability
      * @param conVar optional connection variable
      * @param tryAfterCon label right after connection acquisition
      */
     private void yieldCon
         (CodeBuilder b,
-         LocalVariable repoVar,
+         LocalVariable capVar,
          LocalVariable conVar,
          Label tryAfterCon)
     {
         Label endFinallyLabel = b.createLabel().setLocation();
         Label contLabel = b.createLabel();
 
-        yieldConnection(b, repoVar, conVar);
+        yieldConnection(b, capVar, conVar);
         b.branch(contLabel);
 
         b.exceptionHandler(tryAfterCon, endFinallyLabel, null);
-        yieldConnection(b, repoVar, conVar);
+        yieldConnection(b, capVar, conVar);
         b.throwObject();
 
         contLabel.setLocation();
@@ -1700,13 +1697,15 @@ class JDBCStorableGenerator<S extends Storable> {
      *
      * ...
      *     } finally {
-     *         JDBCRepository.yieldConnection(con);
+     *         JDBCConnectionCapability.yieldConnection(con);
      *     }
+     * } catch (RuntimeException e) {
+     *     throw e;
      * } catch (Exception e) {
-     *     throw JDBCRepository.toFetchException(e);
+     *     throw JDBCConnectionCapability.toFetchException(e);
      * }
      *
-     * @param repoVar required reference to JDBCRepository
+     * @param capVar required reference to JDBCConnectionCapability
      * @param txnVar optional transaction variable to commit/exit
      * @param tryBeforeCon label right before connection acquisition
      * @param conVar optional connection variable
@@ -1714,32 +1713,38 @@ class JDBCStorableGenerator<S extends Storable> {
      */
     private void yieldConAndHandleException
         (CodeBuilder b,
-         LocalVariable repoVar,
+         LocalVariable capVar,
          Label tryBeforeCon, LocalVariable conVar, Label tryAfterCon,
          boolean forPersist)
     {
         Label endFinallyLabel = b.createLabel().setLocation();
         Label contLabel = b.createLabel();
 
-        yieldConnection(b, repoVar, conVar);
+        yieldConnection(b, capVar, conVar);
         b.branch(contLabel);
 
         b.exceptionHandler(tryAfterCon, endFinallyLabel, null);
-        yieldConnection(b, repoVar, conVar);
+        yieldConnection(b, capVar, conVar);
+        b.throwObject();
+
+        b.exceptionHandler
+            (tryBeforeCon, b.createLabel().setLocation(), RuntimeException.class.getName());
         b.throwObject();
 
         b.exceptionHandler
             (tryBeforeCon, b.createLabel().setLocation(), Exception.class.getName());
-        b.loadLocal(repoVar);
-        // Swap exception object and JDBCRepository instance.
+        b.loadLocal(capVar);
+        // Swap exception object and JDBCConnectionCapability instance.
         b.swap();
         TypeDesc[] params = {TypeDesc.forClass(Throwable.class)};
         if (forPersist) {
-            b.invokeVirtual(TypeDesc.forClass(JDBCRepository.class), "toPersistException",
-                            TypeDesc.forClass(PersistException.class), params);
+            b.invokeInterface(TypeDesc.forClass(JDBCConnectionCapability.class),
+                              "toPersistException",
+                              TypeDesc.forClass(PersistException.class), params);
         } else {
-            b.invokeVirtual(TypeDesc.forClass(JDBCRepository.class), "toFetchException",
-                            TypeDesc.forClass(FetchException.class), params);
+            b.invokeInterface(TypeDesc.forClass(JDBCConnectionCapability.class),
+                              "toFetchException",
+                              TypeDesc.forClass(FetchException.class), params);
         }
         b.throwObject();
 
@@ -1971,7 +1976,7 @@ class JDBCStorableGenerator<S extends Storable> {
 
                     // The Lob in the array represents the new value. What is
                     // currently on the stack (as converted above) is the old
-                    // value currently in the database. Call the JDBCRepository
+                    // value currently in the database. Call the JDBCSupport
                     // updateXlob method, which stuffs the new blob contents
                     // into the old blob, thus updating it.
 
@@ -2131,7 +2136,7 @@ class JDBCStorableGenerator<S extends Storable> {
 
         boolean isClob = loaderType == JDBCClobLoader.class;
 
-        final TypeDesc jdbcRepoType = TypeDesc.forClass(JDBCRepository.class);
+        final TypeDesc capType = TypeDesc.forClass(JDBCConnectionCapability.class);
         final TypeDesc resultSetType = TypeDesc.forClass(ResultSet.class);
         final TypeDesc preparedStatementType = TypeDesc.forClass(PreparedStatement.class);
         final TypeDesc sqlLobType = TypeDesc.forClass
@@ -2155,14 +2160,14 @@ class JDBCStorableGenerator<S extends Storable> {
         }
 
         MethodInfo mi = cf.addMethod
-            (Modifiers.PUBLIC, "load", sqlLobType, new TypeDesc[] {jdbcRepoType});
+            (Modifiers.PUBLIC, "load", sqlLobType, new TypeDesc[] {capType});
         mi.addException(TypeDesc.forClass(FetchException.class));
         CodeBuilder b = new CodeBuilder(mi);
 
-        LocalVariable repoVar = b.getParameter(0);
+        LocalVariable capVar = b.getParameter(0);
 
         Label tryBeforeCon = b.createLabel().setLocation();
-        LocalVariable conVar = getConnection(b, repoVar);
+        LocalVariable conVar = getConnection(b, capVar);
         Label tryAfterCon = b.createLabel().setLocation();
 
         StringBuilder selectBuilder = new StringBuilder();
@@ -2179,7 +2184,7 @@ class JDBCStorableGenerator<S extends Storable> {
         b.storeLocal(instanceVar);
 
         Label tryAfterPs = buildWhereClauseAndPreparedStatement
-            (b, selectBuilder, conVar, psVar, repoVar, instanceVar);
+            (b, selectBuilder, conVar, psVar, capVar, instanceVar);
 
         b.loadLocal(psVar);
         b.invokeInterface(preparedStatementType, "executeQuery", resultSetType, null);
@@ -2209,7 +2214,7 @@ class JDBCStorableGenerator<S extends Storable> {
 
         closeResultSet(b, rsVar, tryAfterRs);
         closeStatement(b, psVar, tryAfterPs);
-        yieldConAndHandleException(b, repoVar, tryBeforeCon, conVar, tryAfterCon, false);
+        yieldConAndHandleException(b, capVar, tryBeforeCon, conVar, tryAfterCon, false);
 
         b.loadLocal(resultVar);
         b.returnValue(sqlLobType);
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java
index b5d7bd8..0b1492c 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java
@@ -96,6 +96,13 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
     public static <S extends Storable> JDBCStorableInfo<S> examine
         (Class<S> type, DataSource ds, String catalog, String schema)
         throws SQLException, SupportException
+    {
+        return examine(type, ds, catalog, schema, null);
+    }
+
+    public static <S extends Storable> JDBCStorableInfo<S> examine
+        (Class<S> type, DataSource ds, String catalog, String schema, SupportResolver resolver)
+        throws SQLException, SupportException
     {
         Object key = KeyFactory.createKey(new Object[] {type, ds, catalog, schema});
 
@@ -109,7 +116,15 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
             StorableInfo<S> mainInfo = examine(type);
             Connection con = ds.getConnection();
             try {
-                jInfo = examine(mainInfo, con, catalog, schema);
+                try {
+                    jInfo = examine(mainInfo, con, catalog, schema, resolver);
+                } catch (SupportException e) {
+                    if (resolver != null && resolver.resolve(mainInfo, con, catalog, schema)) {
+                        jInfo = examine(mainInfo, con, catalog, schema, resolver);
+                    } else {
+                        throw e;
+                    }
+                }
             } finally {
                 try {
                     con.close();
@@ -124,8 +139,8 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
             // added to cache. This makes it possible for joins to (directly or
             // indirectly) reference their own enclosing type.
             for (JDBCStorableProperty<S> jProperty : jInfo.getAllProperties().values()) {
-                ((JProperty<S>) jProperty).fillInternalJoinElements(ds, catalog, schema);
-                ((JProperty<S>) jProperty).fillExternalJoinElements(ds, catalog, schema);
+                ((JProperty<S>) jProperty).fillInternalJoinElements(ds, catalog, schema, resolver);
+                ((JProperty<S>) jProperty).fillExternalJoinElements(ds, catalog, schema, resolver);
             }
 
             return jInfo;
@@ -140,7 +155,8 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
      */
     private static <S extends Storable> JDBCStorableInfo<S> examine
         (StorableInfo<S> mainInfo, Connection con,
-         final String searchCatalog, final String searchSchema)
+         final String searchCatalog, final String searchSchema,
+         SupportResolver resolver)
         throws SQLException, SupportException
     {
         DatabaseMetaData meta = con.getMetaData();
@@ -850,6 +866,8 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
         addToSet(aliases, derived.toUpperCase());
         addToSet(aliases, derived.toLowerCase());
         addToSet(aliases, derived);
+        addToSet(aliases, base.toUpperCase());
+        addToSet(aliases, base.toLowerCase());
         addToSet(aliases, base);
 
         return aliases.toArray(new String[aliases.size()]);
@@ -1403,7 +1421,8 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
         }
 
         @SuppressWarnings("unchecked")
-        void fillInternalJoinElements(DataSource ds, String catalog, String schema)
+        void fillInternalJoinElements(DataSource ds, String catalog, String schema,
+                                      SupportResolver resolver)
             throws SQLException, SupportException
         {
             StorableProperty<S>[] mainInternal = mMainProperty.getInternalJoinElements();
@@ -1412,7 +1431,7 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
                 return;
             }
 
-            JDBCStorableInfo<S> info = examine(getEnclosingType(), ds, catalog, schema);
+            JDBCStorableInfo<S> info = examine(getEnclosingType(), ds, catalog, schema, resolver);
 
             JDBCStorableProperty<S>[] internal = new JDBCStorableProperty[mainInternal.length];
             for (int i=mainInternal.length; --i>=0; ) {
@@ -1421,7 +1440,8 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
             mInternal = internal;
         }
 
-        void fillExternalJoinElements(DataSource ds, String catalog, String schema)
+        void fillExternalJoinElements(DataSource ds, String catalog, String schema,
+                                      SupportResolver resolver)
             throws SQLException, SupportException
         {
             StorableProperty<?>[] mainExternal = mMainProperty.getExternalJoinElements();
@@ -1430,7 +1450,7 @@ public class JDBCStorableIntrospector extends StorableIntrospector {
                 return;
             }
 
-            JDBCStorableInfo<?> info = examine(getJoinedType(), ds, catalog, schema);
+            JDBCStorableInfo<?> info = examine(getJoinedType(), ds, catalog, schema, resolver);
 
             JDBCStorableProperty<?>[] external = new JDBCStorableProperty[mainExternal.length];
             for (int i=mainExternal.length; --i>=0; ) {
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCSupport.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCSupport.java
index 7c212cc..c3f85fa 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCSupport.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCSupport.java
@@ -19,6 +19,7 @@
 package com.amazon.carbonado.repo.jdbc;
 
 import java.sql.PreparedStatement;
+import java.sql.SQLException;
 
 import com.amazon.carbonado.Storable;
 import com.amazon.carbonado.FetchException;
@@ -31,9 +32,8 @@ import com.amazon.carbonado.gen.MasterSupport;
  *
  * @author Brian S O'Neill
  */
-public interface JDBCSupport<S extends Storable> extends MasterSupport<S> {
-    public JDBCRepository getJDBCRepository();
-
+public interface JDBCSupport<S extends Storable> extends MasterSupport<S>, JDBCConnectionCapability
+{
     // FIXME: Lob convert methods need to take Storable and property name. With
     // this, the optional Lob adapting trigger must be invoked.
 
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/SupportResolver.java b/src/main/java/com/amazon/carbonado/repo/jdbc/SupportResolver.java
new file mode 100644
index 0000000..3c99b54
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/SupportResolver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.jdbc;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.info.StorableInfo;
+
+/**
+ * Experimental interface for allowing tables to be created or altered when the
+ * Storable definition doesn't match the table or the table doesn't
+ * exist. Currently only used by unit tests.
+ *
+ * @author Brian S O'Neill
+ */
+interface SupportResolver {
+    /**
+     * @return true if support has been resolved
+     */
+    <S extends Storable> boolean resolve(StorableInfo<S> info,
+                                         Connection con, String catalog, String schema)
+        throws SQLException;
+}
-- 
cgit v1.2.3