You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by ht...@apache.org on 2015/06/22 15:39:31 UTC

svn commit: r1686870 - in /openjpa/branches/2.2.x: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/ openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/ openjpa-lib/src/main/java/or...

Author: hthomann
Date: Mon Jun 22 13:39:30 2015
New Revision: 1686870

URL: http://svn.apache.org/r1686870
Log:
OPENJPA-2558: Implement a way to select the db representation of Boolean values.

Added:
    openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java
    openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentationFactory.java
    openjpa/branches/2.2.x/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java
Modified:
    openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
    openjpa/branches/2.2.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties
    openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java
    openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java
    openjpa/branches/2.2.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java

Added: openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java?rev=1686870&view=auto
==============================================================================
--- openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java (added)
+++ openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java Mon Jun 22 13:39:30 2015
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.openjpa.jdbc.sql;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * <p>Defines how a {@code Boolean} or {@code boolean} value
+ * gets stored in the database by default.</p>
+ *
+ * <p>The {@link DBDictionary} defines a default representation for {@code Boolean}
+ * and {@code boolean} fields in JPA entities. The {@link org.apache.openjpa.jdbc.sql.OracleDictionary}
+ * for example uses a {@code NUMBER(1)} with the values {@code (int) 1} and {@code (int) 0} by default.
+ * However, sometimes you like to use a different default representation for Boolean values in your database.
+ * If your application likes to store boolean values in a {@code CHAR(1)} field with {@code "T"} and
+ * {@code "F"} values then you might configure the {@link org.apache.openjpa.jdbc.sql.DBDictionary}
+ * to use  the {@code "STRING_TF"} BooleanRepresentation:
+ * <pre>
+ * &lt;property name="openjpa.jdbc.DBDictionary"
+ *     value="(BitTypeName=CHAR(1),BooleanTypeName=CHAR(1),BooleanRepresentation=STRING_10)"/&gt
+ * </pre>
+ *
+ * Please note that you still need to adopt the mapping separately by setting the
+ * {@code BitTypeName} and/or {@code BooleanTypeName} (depending on your database) to
+ * the desired type in the database.
+ * </p>
+ *
+ * <p>The following {@code BooleanRepresentation} configuration options are possible:
+ * <ul>
+ *     <li>One of the values of
+ *     {@link org.apache.openjpa.jdbc.sql.BooleanRepresentationFactory#BUILTIN_BOOLEAN_REPRESENTATIONS}
+ *     , e.g.:
+ *         <pre>
+ * &lt;property name="openjpa.jdbc.DBDictionary" value="(BooleanRepresentation=STRING_YN)"/&gt
+ *         </pre>
+ *     </li>
+ *     <li>
+ *         Two slash ({@code '/'}) separated true/false value strings:
+ *         <pre>
+ * &lt;property name="openjpa.jdbc.DBDictionary" value="(BooleanRepresentation=oui/non)"/&gt
+ *         </pre>
+ *     </li>
+ *     <li>
+ *         A fully qualified class name of your own {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation}
+ *         implementation, e.g.:
+ *         <pre>
+ * &lt;property name="openjpa.jdbc.DBDictionary"
+ *     value="(BooleanRepresentation=com.mycompany.MyOwnBoolRepresentation)"/&gt
+ *         </pre>
+ *     </li>
+ * </ul>
+ *
+ * </p>
+ *
+ * <p>If a single column uses a different representation then they
+ * still can tweak this for those columns with the
+ * {@code org.apache.openjpa.persistence.ExternalValues} annotation.</p>
+ * @param <REPRESENTATION_TYPE> the java type which is used to store the Boolean in the database,
+ *                             e.g. {@code String} or {@code Integer}
+ */
+public interface BooleanRepresentation<REPRESENTATION_TYPE> {
+
+    /**
+     * Set the boolean value into the statement
+     */
+    public void setBoolean(PreparedStatement stmnt, int columnIndex, boolean val) throws SQLException;
+
+    /**
+     * Read the boolean from the given ResultSet
+     */
+    public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException;
+
+
+    /**
+     * @return return the representation for {@code true} and {@code false}
+     */
+    public REPRESENTATION_TYPE getRepresentation(boolean bool);
+
+}

Added: openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentationFactory.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentationFactory.java?rev=1686870&view=auto
==============================================================================
--- openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentationFactory.java (added)
+++ openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentationFactory.java Mon Jun 22 13:39:30 2015
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.openjpa.jdbc.sql;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.UserException;
+
+/**
+ * Factory which is being used to create the active {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation}.
+*/
+public class BooleanRepresentationFactory {
+
+    public static BooleanRepresentation INT_10 = new Int10BooleanRepresentation();
+    public static BooleanRepresentation BOOLEAN = new BooleanBooleanRepresentation();
+
+    /**
+     * {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation}s built in by default.
+     * Key is their config name, value is the BooleanRepresentation.
+     */
+    static final Map<String, BooleanRepresentation<?>> BUILTIN_BOOLEAN_REPRESENTATIONS;
+
+    static {
+        BUILTIN_BOOLEAN_REPRESENTATIONS = new HashMap<String, BooleanRepresentation<?>>();
+
+        /**
+         * Booleans are natively supported by this very database.
+         * The database column is e.g. a NUMBER(1)
+         * OpenJPA will use preparedStatement.setBoolean(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("BOOLEAN", BOOLEAN);
+
+        /**
+         * Booleans are stored as numeric int 1 and int 0 values.
+         * The database column is e.g. a NUMBER(1)
+         * OpenJPA will use preparedStatement.setInt(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("INT_10", INT_10);
+
+        /**
+         * Booleans are stored as String "1" for {@code true}
+         * and String "0" for {@code false}.
+         * The database column is e.g. a CHAR(1) or VARCHAR(1)
+         * OpenJPA will use preparedStatement.setString(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_10", new StringBooleanRepresentation("1", "0"));
+
+        /**
+         * Booleans are stored as String "Y" for {@code true}
+         * and String "N" for {@code false}.
+         * The database column is e.g. a CHAR(1) or VARCHAR(1)
+         * OpenJPA will use preparedStatement.setString(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_YN", new StringBooleanRepresentation("Y", "N"));
+
+        /**
+         * Booleans are stored as String "y" for {@code true}
+         * and String "n" for {@code false}.
+         * The database column is e.g. a CHAR(1) or VARCHAR(1)
+         * OpenJPA will use preparedStatement.setString(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_YN_LOWERCASE", new StringBooleanRepresentation("y", "n"));
+
+        /**
+         * Booleans are stored as String "T" for {@code true}
+         * and String "F" for {@code false}.
+         * The database column is e.g. a CHAR(1) or VARCHAR(1)
+         * OpenJPA will use preparedStatement.setString(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_TF", new StringBooleanRepresentation("T", "F"));
+
+        /**
+         * Booleans are stored as String "t" for {@code true}
+         * and String "f" for {@code false}.
+         * The database column is e.g. a CHAR(1) or VARCHAR(1)
+         * OpenJPA will use preparedStatement.setString(..) for it
+         */
+        BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_TF_LOWERCASE", new StringBooleanRepresentation("t", "f"));
+
+    }
+
+
+    public static BooleanRepresentation valueOf(String booleanRepresentationKey, ClassLoader cl) {
+        // 1st step, try to lookup the BooleanRepresentation from the default ones
+        BooleanRepresentation booleanRepresentation = BUILTIN_BOOLEAN_REPRESENTATIONS.get(booleanRepresentationKey);
+
+        if (booleanRepresentation == null && booleanRepresentationKey.contains("/")) {
+            // if the key contains a '/' then the first value is the key for 'true', the 2nd value is for 'false'
+            String[] vals = booleanRepresentationKey.split("/");
+            if (vals.length == 2) {
+                booleanRepresentation = new StringBooleanRepresentation(vals[0], vals[1]);
+            }
+        }
+        else {
+            // or do a class lookup for a custom BooleanRepresentation
+            try {
+                Class<? extends BooleanRepresentation> booleanRepresentationClass
+                        = (Class<? extends BooleanRepresentation>) cl.loadClass(booleanRepresentationKey);
+                booleanRepresentation = booleanRepresentationClass.newInstance();
+            }
+            catch (Exception e) {
+                // nothing to do
+            }
+        }
+
+        if (booleanRepresentation == null) {
+            Localizer _loc = Localizer.forPackage(BooleanRepresentation.class);
+            throw new UserException(_loc.get("unknown-booleanRepresentation",
+                    new Object[]{booleanRepresentationKey,
+                            Arrays.toString(BUILTIN_BOOLEAN_REPRESENTATIONS.keySet().toArray(new String[]{}))}
+            ));
+
+        }
+
+        return booleanRepresentation;
+    }
+
+
+
+    /**
+     * BooleanRepresentation which takes 2 strings for true and false representations
+     * as constructor parameter;
+     */
+    public static class StringBooleanRepresentation implements BooleanRepresentation<String> {
+        private final String trueRepresentation;
+        private final String falseRepresentation;
+
+        public StringBooleanRepresentation(String trueRepresentation, String falseRepresentation) {
+            this.trueRepresentation = trueRepresentation;
+            this.falseRepresentation = falseRepresentation;
+        }
+
+        @Override
+        public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException {
+            stmnt.setString(idx, val ? trueRepresentation : falseRepresentation);
+        }
+
+        @Override
+        public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException {
+            return trueRepresentation.equals(rs.getString(columnIndex));
+        }
+
+        @Override
+        public String getRepresentation(boolean bool) {
+            return bool ? trueRepresentation : falseRepresentation;
+        }
+
+        @Override
+        public String toString() {
+            return "StringBooleanRepresentation with the following values for true and false: "
+                    + trueRepresentation + " / " + falseRepresentation;
+        }
+    }
+
+    public static class BooleanBooleanRepresentation implements BooleanRepresentation<Boolean> {
+        @Override
+        public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException {
+            stmnt.setBoolean(idx, val);
+        }
+
+        @Override
+        public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException {
+            return rs.getBoolean(columnIndex);
+        }
+
+        @Override
+        public Boolean getRepresentation(boolean bool) {
+            return bool;
+        }
+
+        @Override
+        public String toString() {
+            return "BooleanBooleanRepresentation";
+        }
+    }
+
+    public static class Int10BooleanRepresentation implements BooleanRepresentation<Integer> {
+        @Override
+        public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{
+            stmnt.setInt(idx, val ? 1 : 0);
+        }
+
+        @Override
+        public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException {
+            return rs.getInt(columnIndex) > 0;
+        }
+
+        @Override
+        public Integer getRepresentation(boolean bool) {
+            return bool ? 1 : 0;
+        }
+
+        @Override
+        public String toString() {
+            return "Int10BooleanRepresentation";
+        }
+    }
+
+}

Modified: openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=1686870&r1=1686869&r2=1686870&view=diff
==============================================================================
--- openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original)
+++ openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Mon Jun 22 13:39:30 2015
@@ -324,6 +324,12 @@ public class DBDictionary
     public enum DateMillisecondBehaviors { DROP, ROUND, RETAIN };    
     private DateMillisecondBehaviors dateMillisecondBehavior;
     
+    /**
+     * Defines how {@code Boolean} and {@code boolean} values get represented
+     * in OpenJPA. Default to {@code INT_10} for backward compatibility.
+     */
+    protected BooleanRepresentation booleanRepresentation = BooleanRepresentationFactory.INT_10;
+    
     public int characterColumnSize = 255;
     public String arrayTypeName = "ARRAY";
     public String bigintTypeName = "BIGINT";
@@ -696,7 +702,7 @@ public class DBDictionary
      */
     public boolean getBoolean(ResultSet rs, int column)
         throws SQLException {
-        return rs.getBoolean(column);
+        return booleanRepresentation.getBoolean(rs, column);
     }
 
     /**
@@ -1050,7 +1056,7 @@ public class DBDictionary
     public void setBoolean(PreparedStatement stmnt, int idx, boolean val,
         Column col)
         throws SQLException {
-        stmnt.setInt(idx, (val) ? 1 : 0);
+        booleanRepresentation.setBoolean(stmnt, idx, val);
     }
 
     /**
@@ -2769,12 +2775,11 @@ public class DBDictionary
     /**
      * Append <code>elem</code> to <code>selectSQL</code>.
      * @param selectSQL The SQLBuffer to append to.
-     * @param alias A {@link SQLBuffer} or a {@link String} to append.
+     * @param elem A {@link SQLBuffer} or a {@link String} to append.
      *
      * @since 1.1.0
      */
-    protected void appendSelect(SQLBuffer selectSQL, Object elem, Select sel,
-        int idx) {
+    protected void appendSelect(SQLBuffer selectSQL, Object elem, Select sel, int idx) {
         if (elem instanceof SQLBuffer)
             selectSQL.append((SQLBuffer) elem);
         else
@@ -3147,7 +3152,7 @@ public class DBDictionary
      * getValidColumnName method of the DB dictionary should be invoked to make
      * it valid.
      * 
-     * @see getValidColumnName
+     * @see #getValidColumnName(org.apache.openjpa.jdbc.identifier.DBIdentifier, org.apache.openjpa.jdbc.schema.Table)
      */
     public final Set<String> getInvalidColumnWordSet() {
         return invalidColumnWordSet;
@@ -5389,10 +5394,9 @@ public class DBDictionary
      * Validate that the given name is not longer than given maximum length. Uses the unqualified name
      * from the supplied {@link DBIdentifier} by default..
      * 
-     * @param identifer The database identifier to check.
+     * @param identifier The database identifier to check.
      * @param length    Max length for this type of identifier
      * @param msgKey    message identifier for the exception.
-     * @param qualified If true the qualified name of the DBIdentifier will be used. 
      * 
      * @throws {@link UserException} with the given message key if the given name is indeed longer.
      * @return the same name.
@@ -5405,7 +5409,7 @@ public class DBDictionary
      * Validate that the given name is not longer than given maximum length. Conditionally uses the unqualified name
      * from the supplied {@link DBIdentifier}.
      * 
-     * @param identifer The database identifier to check.
+     * @param identifier The database identifier to check.
      * @param length    Max length for this type of identifier
      * @param msgKey    message identifier for the exception.
      * @param qualified If true the qualified name of the DBIdentifier will be used. 
@@ -5458,7 +5462,7 @@ public class DBDictionary
     }
 
     /**
-     * @param metadata the DatabaseMetaData to use to determine whether delimiters can be supported
+     * @param metaData the DatabaseMetaData to use to determine whether delimiters can be supported
      */
     private void setSupportsDelimitedIdentifiers(DatabaseMetaData metaData) {
         try {
@@ -5665,4 +5669,24 @@ public class DBDictionary
             dateMillisecondBehavior = null;
         }
     }
+    
+    public BooleanRepresentation getBooleanRepresentation() {
+        return booleanRepresentation;
+    }
+    
+    public void setBooleanRepresentation(String booleanRepresentationKey) {
+        BooleanRepresentation evaluatedBooleanRepresentation = null;
+        if (booleanRepresentationKey != null && booleanRepresentationKey.length() > 0) {
+            ClassLoader cl = conf.getAppClassLoader();
+            evaluatedBooleanRepresentation = BooleanRepresentationFactory.valueOf(booleanRepresentationKey, cl);
+        }
+        
+        booleanRepresentation = evaluatedBooleanRepresentation != null
+                        ? evaluatedBooleanRepresentation
+                        : BooleanRepresentationFactory.INT_10;
+        
+        if (log.isInfoEnabled()) {
+            log.info(_loc.get("using-booleanRepresentation", booleanRepresentation));
+        }
+    }
 }

Modified: openjpa/branches/2.2.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties?rev=1686870&r1=1686869&r2=1686870&view=diff
==============================================================================
--- openjpa/branches/2.2.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties (original)
+++ openjpa/branches/2.2.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties Mon Jun 22 13:39:30 2015
@@ -230,4 +230,8 @@ jdbc4-setbinarystream-unsupported: The J
 sequence-cache-warning: Setting the useNativeSequenceCache property on the DBDictionary no longer has an \
     effect.  Code has been added to allow, by default, the functionality provided in previous releases \
     via the useNativeSequenceCache property.
+unknown-booleanRepresentation: Unknown BooleanRepresentation {0}. Value must be one of {1}.  You can also \
+     hand over a 'truerepresentation/falserepresentation' String or a fully qualified class name of your \
+     own BooleanRepresentation implementation.
+using-booleanRepresentation: BooleanRepresentation {0} got picked up.
     

Added: openjpa/branches/2.2.x/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java?rev=1686870&view=auto
==============================================================================
--- openjpa/branches/2.2.x/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java (added)
+++ openjpa/branches/2.2.x/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java Mon Jun 22 13:39:30 2015
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.openjpa.jdbc.sql;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
+
+/**
+ * Test for the {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation} factory and default impls
+ */
+public class TestBooleanRepresentation  extends TestCase {
+
+
+    public void testBooleanRepresentation() throws Exception {
+
+        checkBooleanRepresentation("BOOLEAN", Boolean.class, Boolean.TRUE, Boolean.FALSE);
+        checkBooleanRepresentation("INT_10", Integer.class, 1, 0);
+        checkBooleanRepresentation("STRING_10", String.class, "1", "0");
+
+        checkBooleanRepresentation("STRING_YN", String.class, "Y", "N");
+        checkBooleanRepresentation("STRING_YN_LOWERCASE", String.class, "y", "n");
+
+        checkBooleanRepresentation("STRING_TF", String.class, "T", "F");
+        checkBooleanRepresentation("STRING_TF_LOWERCASE", String.class, "t", "f");
+
+        // and now up to more sophisticated ones:
+        checkBooleanRepresentation("oui/non", String.class, "oui", "non");
+
+        checkBooleanRepresentation(
+                "org.apache.openjpa.jdbc.sql.TestBooleanRepresentation$DummyTestBooleanRepresentation",
+                String.class, "somehowtrue", "somehowfalse");
+    }
+
+    private <T> void checkBooleanRepresentation(String representationKey, final Class<T> expectedType,
+                                                final T yesRepresentation, final T noRepresentation)
+        throws Exception {
+        ClassLoader cl = TestBooleanRepresentation.class.getClassLoader();
+        BooleanRepresentation booleanRepresentation = BooleanRepresentationFactory.valueOf(representationKey, cl);
+        Assert.assertNotNull(booleanRepresentation);
+
+        DummyPreparedStatement<T> dummyPreparedStatement = new DummyPreparedStatement<T>(expectedType);
+
+        booleanRepresentation.setBoolean(dummyPreparedStatement, 1, true);
+        Assert.assertEquals(yesRepresentation, dummyPreparedStatement.getBooleanRepresentationValue());
+
+        booleanRepresentation.setBoolean(dummyPreparedStatement, 1, false);
+        Assert.assertEquals(noRepresentation, dummyPreparedStatement.getBooleanRepresentationValue());
+
+
+        // and also test getBoolean!
+        ResultSet yesRs = (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class},
+                new InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                if (String.class.equals(expectedType) && !"getString".equals(method.getName()) ||
+                    Boolean.class.equals(expectedType) && !"getBoolean".equals(method.getName()) ||
+                    Integer.class.equals(expectedType) && !"getInt".equals(method.getName())) {
+                    Assert.fail("wrong ResultSet method " + method.getName()
+                                + "for expectedType " + expectedType.getName());
+                }
+                return yesRepresentation;
+            }
+        });
+        Assert.assertTrue(booleanRepresentation.getBoolean(yesRs, 1));
+
+        ResultSet noRs = (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class},
+                new InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                if (String.class.equals(expectedType) && !"getString".equals(method.getName()) ||
+                    Boolean.class.equals(expectedType) && !"getBoolean".equals(method.getName()) ||
+                    Integer.class.equals(expectedType) && !"getInt".equals(method.getName())) {
+                    Assert.fail("wrong ResultSet method " + method.getName()
+                                + "for expectedType " + expectedType.getName());
+                }
+                return noRepresentation;
+            }
+        });
+        Assert.assertFalse(booleanRepresentation.getBoolean(noRs, 1));
+    }
+
+
+    /**
+     * A small trick to 'intercept' the PreparedStatement call inside the BooleanRepresentation
+     */
+    public static class DummyPreparedStatement<T> extends DelegatingPreparedStatement {
+        private final Class<T> expectedType;
+        private Object booleanRepresentationValue;
+
+
+        public DummyPreparedStatement(Class<T> expectedType) {
+            super(null, null);
+            this.expectedType = expectedType;
+        }
+
+        public T getBooleanRepresentationValue() {
+            return (T) booleanRepresentationValue;
+        }
+
+        public void setBooleanRepresentationValue(T booleanRepresentationValue) {
+            this.booleanRepresentationValue = booleanRepresentationValue;
+        }
+
+        @Override
+        public void setBoolean(int idx, boolean b) throws SQLException {
+            Assert.assertEquals(Boolean.class, expectedType);
+            booleanRepresentationValue = b;
+        }
+
+        @Override
+        public void setString(int idx, String s) throws SQLException {
+            Assert.assertEquals(String.class, expectedType);
+            booleanRepresentationValue = s;
+        }
+
+        @Override
+        public void setInt(int idx, int i) throws SQLException {
+            Assert.assertEquals(Integer.class, expectedType);
+            booleanRepresentationValue = i;
+        }
+    }
+
+    public static class DummyTestBooleanRepresentation implements BooleanRepresentation<String> {
+        @Override
+        public void setBoolean(PreparedStatement stmnt, int columnIndex, boolean val) throws SQLException {
+            stmnt.setString(columnIndex, getRepresentation(val));
+        }
+
+        @Override
+        public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException {
+            return "somehowtrue".equals(rs.getString(columnIndex));
+        }
+
+        @Override
+        public String getRepresentation(boolean bool) {
+            return bool ? "somehowtrue" : "somehowfalse";
+        }
+    }
+}

Modified: openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java?rev=1686870&r1=1686869&r2=1686870&view=diff
==============================================================================
--- openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java (original)
+++ openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/Configuration.java Mon Jun 22 13:39:30 2015
@@ -252,11 +252,21 @@ public interface Configuration
     public Object clone();
     
     /**
-     * Modifies a <em>dynamic</em> property of this receiver even when 
-     * {@link #setReadOnly(boolean) frozen}. 
-     *
-     * @since 1.0.0
+     * Sets the ClassLoader that may be used to load application classes, resources, or open URLS.
      */
+    public void setAppClassLoader(ClassLoader cl);
+    
+    /**
+     * Gets the ClassLoader that may be used to load application classes, resources, or open URLS.
+     */
+    public ClassLoader getAppClassLoader();
+    
+//    /**
+//     * Modifies a <em>dynamic</em> property of this receiver even when 
+//     * {@link #setReadOnly(boolean) frozen}. 
+//     *
+//     * @since 1.0.0
+//     */
 //    public void modifyDynamic(String property, Object newValue);
 //    
 //    /**

Modified: openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java?rev=1686870&r1=1686869&r2=1686870&view=diff
==============================================================================
--- openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java (original)
+++ openjpa/branches/2.2.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java Mon Jun 22 13:39:30 2015
@@ -67,6 +67,7 @@ import org.apache.openjpa.lib.util.Multi
 import org.apache.openjpa.lib.util.ParseException;
 import org.apache.openjpa.lib.util.Services;
 import org.apache.openjpa.lib.util.StringDistance;
+
 import serp.util.Strings;
 
 /**
@@ -129,6 +130,8 @@ public class ConfigurationImpl
     //Ant task needs to defer the resource loading 
     //until the classpath setting is loaded properly
     private boolean _deferResourceLoading = false; 
+    
+    private ClassLoader _appCL = null;
 
     /**
      * Default constructor. Attempts to load default properties through
@@ -1109,4 +1112,18 @@ public class ConfigurationImpl
         addValue(val);
         return val;
     }
+    
+    /**
+     * Sets the ClassLoader that may be used to load application classes, resources, or open URLS.
+     */
+    public void setAppClassLoader(ClassLoader cl) {
+        _appCL = cl;
+    }
+    
+    /**
+     * Gets the ClassLoader that may be used to load application classes, resources, or open URLS.
+     */
+    public ClassLoader getAppClassLoader() {
+        return _appCL;
+    }
 }

Modified: openjpa/branches/2.2.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java?rev=1686870&r1=1686869&r2=1686870&view=diff
==============================================================================
--- openjpa/branches/2.2.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java (original)
+++ openjpa/branches/2.2.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java Mon Jun 22 13:39:30 2015
@@ -90,6 +90,7 @@ public class PersistenceProviderImpl
 
             BrokerFactory factory = getBrokerFactory(cp, poolValue, BundleUtils.getBundleClassLoader());
             OpenJPAConfiguration conf = factory.getConfiguration();
+            conf.setAppClassLoader(BundleUtils.getBundleClassLoader());
             _log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);            
             pd.checkPuNameCollisions(_log,name);
             
@@ -188,6 +189,7 @@ public class PersistenceProviderImpl
             }
 
             OpenJPAConfiguration conf = factory.getConfiguration();
+            conf.setAppClassLoader(pui.getClassLoader());
             setPersistenceEnvironmentInfo(conf, pui);
             _log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
             // now we can log any transformer exceptions from above