Mercurial > hg > monetdb-java
changeset 18:8e57d20b5e80
Corrected implementation of java.sql.Wrapper methods isWrapperFor() and unwrap().
They now properly return expected results instead of always return false or throw an SQLException.
Added test program Test_Wrapper.java
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 13 Oct 2016 16:31:08 +0200 (2016-10-13) |
parents | 1bf638b6a7c2 |
children | 7acc09f59b21 |
files | ChangeLog src/main/java/nl/cwi/monetdb/jdbc/MonetWrapper.java tests/Test_Wrapper.java tests/build.xml |
diffstat | 4 files changed, 159 insertions(+), 41 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ # ChangeLog file for java # This file is updated with Maddlog +* Thu Oct 13 2016 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Corrected implementation of java.sql.Wrapper methods isWrapperFor() + and unwrap(). They now properly return expected results instead of + always return false or throw an SQLException. + * Thu Oct 6 2016 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> - Corrected return values of DatabaseMetaData methods nullsAreSortedHigh(), nullsAreSortedLow(), getMaxCursorNameLength(), getMaxProcedureNameLength(),
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetWrapper.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetWrapper.java @@ -8,66 +8,71 @@ package nl.cwi.monetdb.jdbc; -import java.sql.*; +import java.sql.SQLException; /** - * A Wrapper class that implements nothing. - * - * This Class is used to simply provide JDBC4 Wrapper functions, for as - * long as we don't really understand that they are for and what they - * are supposed to do. Hence the implementations are very stupid and - * non-useful, ignoring any argument and claiming stuff doesn't work, or - * won't work out. + * A Wrapper class which provide the ability to retrieve the delegate instance + * when the instance in question is in fact a proxy class. * - * @author Fabian Groffen - * @version 1.0 + * The wrapper pattern is employed by many JDBC driver implementations to provide + * extensions beyond the traditional JDBC API that are specific to a data source. + * Developers may wish to gain access to these resources that are wrapped (the delegates) + * as proxy class instances representing the the actual resources. + * This class contains a standard mechanism to access these wrapped resources + * represented by their proxy, to permit direct access to the resource delegates. + * + * @author Fabian Groffen, Martin van Dinther + * @version 1.1 */ -public class MonetWrapper implements Wrapper { +public class MonetWrapper implements java.sql.Wrapper { /** * Returns true if this either implements the interface argument or * is directly or indirectly a wrapper for an object that does. * Returns false otherwise. If this implements the interface then * return true, else if this is a wrapper then return the result of - * recursively calling isWrapperFor on the wrapped object. If this - * does not implement the interface and is not a wrapper, return + * recursively calling <code>isWrapperFor</code> on the wrapped object. + * If this does not implement the interface and is not a wrapper, return * false. This method should be implemented as a low-cost operation - * compared to unwrap so that callers can use this method to avoid - * expensive unwrap calls that may fail. If this method returns true - * then calling unwrap with the same argument should succeed. + * compared to <code>unwrap</code> so that callers can use this method to avoid + * expensive <code>unwrap</code> calls that may fail. + * If this method returns true then calling <code>unwrap</code> with the same argument should succeed. * * @param iface a Class defining an interface. - * @return true if this implements the interface or directly or - * indirectly wraps an object that does. - * @throws SQLException if an error occurs while determining - * whether this is a wrapper for an object with the given - * interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws SQLException if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 */ @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { - return false; + return iface != null && iface.isAssignableFrom(getClass()); } /** * Returns an object that implements the given interface to allow - * access to non-standard methods, or standard methods not exposed - * by the proxy. If the receiver implements the interface then the - * result is the receiver or a proxy for the receiver. If the - * receiver is a wrapper and the wrapped object implements the - * interface then the result is the wrapped object or a proxy for - * the wrapped object. Otherwise return the the result of calling - * unwrap recursively on the wrapped object or a proxy for that - * result. If the receiver is not a wrapper and does not implement - * the interface, then an SQLException is thrown. + * access to non-standard methods, or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface + * or a proxy for that object. + * If the receiver implements the interface then the result is the receiver + * or a proxy for the receiver. + * If the receiver is a wrapper and the wrapped object implements the interface + * then the result is the wrapped object or a proxy for the wrapped object. + * Otherwise return the result of calling <code>unwrap</code> recursively on + * the wrapped object or a proxy for that result. + * If the receiver is not a wrapper and does not implement the interface, + * then an <code>SQLException</code> is thrown. * - * @param iface A Class defining an interface that the result must - * implement. - * @return an object that implements the interface. May be a proxy - * for the actual implementing object. - * @throws SQLException If no object found that implements the - * interface + * @param iface A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws SQLException If no object found that implements the interface + * @since 1.6 */ @Override + @SuppressWarnings("unchecked") public <T> T unwrap(Class<T> iface) throws SQLException { - throw new SQLException("No object found (not implemented)", "0A000"); + if (isWrapperFor(iface)) { + return (T) this; + } + throw new SQLException("Cannot unwrap to interface: " + (iface != null ? iface.getName() : ""), "0A000"); } }
new file mode 100644 --- /dev/null +++ b/tests/Test_Wrapper.java @@ -0,0 +1,102 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 1997 - July 2008 CWI, August 2008 - 2016 MonetDB B.V. + */ + +import java.sql.*; + +public class Test_Wrapper { + public static void main(String[] args) throws Exception { + // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); + final Connection con = DriverManager.getConnection(args[0]); + System.out.println("Connected. Auto commit is: " + con.getAutoCommit()); + + try { + final String jdbc_pkg = "java.sql."; + final String monetdb_jdbc_pkg = "nl.cwi.monetdb.jdbc."; + + checkIsWrapperFor("Connection", con, jdbc_pkg, "Connection"); + checkIsWrapperFor("Connection", con, monetdb_jdbc_pkg, "MonetConnection"); + checkIsWrapperFor("Connection", con, jdbc_pkg, "Statement"); + checkIsWrapperFor("Connection", con, monetdb_jdbc_pkg, "MonetStatement"); + + DatabaseMetaData dbmd = con.getMetaData(); + checkIsWrapperFor("DatabaseMetaData", dbmd, jdbc_pkg, "DatabaseMetaData"); + checkIsWrapperFor("DatabaseMetaData", dbmd, monetdb_jdbc_pkg, "MonetDatabaseMetaData"); + checkIsWrapperFor("DatabaseMetaData", dbmd, jdbc_pkg, "Statement"); + checkIsWrapperFor("DatabaseMetaData", dbmd, monetdb_jdbc_pkg, "MonetStatement"); + + ResultSet rs = dbmd.getSchemas(); + checkIsWrapperFor("ResultSet", rs, jdbc_pkg, "ResultSet"); + checkIsWrapperFor("ResultSet", rs, monetdb_jdbc_pkg, "MonetResultSet"); + checkIsWrapperFor("ResultSet", rs, jdbc_pkg, "Statement"); + checkIsWrapperFor("ResultSet", rs, monetdb_jdbc_pkg, "MonetStatement"); + + ResultSetMetaData rsmd = rs.getMetaData(); + checkIsWrapperFor("ResultSetMetaData", rsmd, jdbc_pkg, "ResultSetMetaData"); + checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSet"); + checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSet$rsmdw"); // it is a private class of MonetResultSet + checkIsWrapperFor("ResultSetMetaData", rsmd, jdbc_pkg, "Statement"); + checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetStatement"); + + rs.close(); + + Statement stmt = con.createStatement(); + checkIsWrapperFor("Statement", stmt, jdbc_pkg, "Statement"); + checkIsWrapperFor("Statement", stmt, monetdb_jdbc_pkg, "MonetStatement"); + checkIsWrapperFor("Statement", stmt, jdbc_pkg, "Connection"); + checkIsWrapperFor("Statement", stmt, monetdb_jdbc_pkg, "MonetConnection"); + + stmt.close(); + + PreparedStatement pstmt = con.prepareStatement("SELECT name FROM sys.tables WHERE system AND name like ?"); + checkIsWrapperFor("PreparedStatement", pstmt, jdbc_pkg, "PreparedStatement"); + checkIsWrapperFor("PreparedStatement", pstmt, monetdb_jdbc_pkg, "MonetPreparedStatement"); + checkIsWrapperFor("PreparedStatement", pstmt, jdbc_pkg, "Statement"); + checkIsWrapperFor("PreparedStatement", pstmt, monetdb_jdbc_pkg, "MonetStatement"); + checkIsWrapperFor("PreparedStatement", pstmt, jdbc_pkg, "Connection"); + checkIsWrapperFor("PreparedStatement", pstmt, monetdb_jdbc_pkg, "MonetConnection"); + + ParameterMetaData pmd = pstmt.getParameterMetaData(); + checkIsWrapperFor("ParameterMetaData", pmd, jdbc_pkg, "ParameterMetaData"); + checkIsWrapperFor("ParameterMetaData", pmd, monetdb_jdbc_pkg, "MonetPreparedStatement"); + checkIsWrapperFor("ParameterMetaData", pmd, monetdb_jdbc_pkg, "MonetPreparedStatement$pmdw"); // it is a private class of MonetPreparedStatement + checkIsWrapperFor("ParameterMetaData", pmd, jdbc_pkg, "Connection"); + checkIsWrapperFor("ParameterMetaData", pmd, monetdb_jdbc_pkg, "MonetConnection"); + + ResultSetMetaData psrsmd = pstmt.getMetaData(); + checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, jdbc_pkg, "ResultSetMetaData"); + checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, monetdb_jdbc_pkg, "MonetPreparedStatement"); + checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, monetdb_jdbc_pkg, "MonetPreparedStatement$rsmdw"); // it is a private class of MonetPreparedStatement + checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, jdbc_pkg, "Connection"); + checkIsWrapperFor("PrepStmt ResultSetMetaData", psrsmd, monetdb_jdbc_pkg, "MonetConnection"); + + pstmt.close(); + + } catch (SQLException e) { + while ((e = e.getNextException()) != null) + System.out.println("FAILED :( " + e.getMessage()); + } + con.close(); + } + + private static void checkIsWrapperFor(String objnm, Wrapper obj, String pkgnm, String classnm) { + try { + Class<?> clazz = Class.forName(pkgnm + classnm); + boolean isWrapper = obj.isWrapperFor(clazz); + System.out.print(objnm + ". isWrapperFor(" + classnm + ") returns: " + isWrapper); + if (isWrapper) { + Object wobj = obj.unwrap(clazz); + System.out.print("\tCalled unwrap(). Returned object is " + (wobj != null ? "not null, so oke" : "null !!")); + } + System.out.println(); + } catch (ClassNotFoundException cnfe) { + System.out.println(cnfe.toString()); + } catch (SQLException se) { + System.out.println(se.getMessage()); + } + } +}
--- a/tests/build.xml +++ b/tests/build.xml @@ -125,6 +125,7 @@ Copyright 1997 - July 2008 CWI, August 2 <antcall target="Test_Smoreresults" /> <antcall target="Test_Int128" /> <antcall target="Test_FetchSize" /> + <antcall target="Test_Wrapper" /> <antcall target="BugConcurrent_clients_SF_1504657" /> <antcall target="BugConcurrent_sequences" /> <antcall target="BugDatabaseMetaData_Bug_3356" /> @@ -142,7 +143,6 @@ Copyright 1997 - July 2008 CWI, August 2 <arg value="${jdbc_url}" /> </java> </target> - <!-- convenience targets for the outside caller to specify which test(s) should be run --> @@ -319,13 +319,19 @@ Copyright 1997 - July 2008 CWI, August 2 <param name="test.class" value="Test_Int128" /> </antcall> </target> - + <target name="Test_FetchSize"> <antcall target="test_class"> <param name="test.class" value="Test_FetchSize" /> </antcall> </target> - + + <target name="Test_Wrapper"> + <antcall target="test_class"> + <param name="test.class" value="Test_Wrapper" /> + </antcall> + </target> + <target name="BugConcurrent_clients_SF_1504657"> <antcall target="test_class"> <param name="test.class" value="BugConcurrent_clients_SF_1504657" />