001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.dbutils; 018 019 import java.beans.IndexedPropertyDescriptor; 020 import java.beans.PropertyDescriptor; 021 import java.lang.reflect.InvocationHandler; 022 import java.lang.reflect.Method; 023 import java.lang.reflect.Proxy; 024 import java.sql.ParameterMetaData; 025 import java.sql.PreparedStatement; 026 import java.sql.SQLException; 027 import java.sql.Types; 028 import java.util.Arrays; 029 030 import junit.framework.TestCase; 031 032 public class QueryRunnerTest extends TestCase { 033 QueryRunner runner; 034 PreparedStatement stmt; 035 036 static final Method getParameterCount, getParameterType, getParameterMetaData; 037 static { 038 try { 039 getParameterCount = ParameterMetaData.class.getMethod("getParameterCount", new Class[0]); 040 getParameterType = ParameterMetaData.class.getMethod("getParameterType", new Class[]{int.class}); 041 getParameterMetaData = PreparedStatement.class.getMethod("getParameterMetaData", new Class[0]); 042 } catch (Exception e) { 043 throw new RuntimeException(e); 044 } 045 } 046 047 public void setUp() { 048 runner = new QueryRunner(); 049 stmt = fakePreparedStatement(); 050 } 051 052 public void testFillStatementWithNull() throws Exception { 053 stmt = fakeFillablePreparedStatement(false, new int[] {Types.VARCHAR, Types.BIGINT}); 054 runner.fillStatement(stmt, new Object[] { null, null }); 055 } 056 057 public void testFillStatementWithNullOracle() throws Exception { 058 stmt = fakeFillablePreparedStatement(true, new int[] {Types.VARCHAR, Types.BIGINT}); 059 runner.fillStatement(stmt, new Object[] { null, null }); 060 } 061 062 private PreparedStatement fakeFillablePreparedStatement(final boolean simulateOracle, final int[] types) throws NoSuchMethodException { 063 // prepare a mock ParameterMetaData and a mock PreparedStatement to return the PMD 064 final ParameterMetaData pmd = mockParameterMetaData(simulateOracle,types); 065 InvocationHandler stmtHandler = new InvocationHandler() { 066 public Object invoke(Object proxy, Method method, Object[] args) 067 throws Throwable { 068 if (getParameterMetaData.equals(method)) { 069 return pmd; 070 } 071 return null; 072 } 073 }; 074 return ProxyFactory.instance().createPreparedStatement(stmtHandler); 075 } 076 077 private ParameterMetaData mockParameterMetaData(final boolean simulateOracle, final int[] types) { 078 InvocationHandler pmdHandler = new InvocationHandler() { 079 public Object invoke(Object proxy, Method method, Object[] args) 080 throws Throwable { 081 if (getParameterCount.equals(method)) { 082 return new Integer(types.length); 083 } 084 if (getParameterType.equals(method)) { 085 if (simulateOracle) throw new SQLException("Oracle fails when you call getParameterType"); 086 int arg = ((Integer)args[0]).intValue(); 087 return new Integer(types[arg-1]); 088 } 089 return null; 090 } 091 }; 092 093 return (ParameterMetaData) Proxy.newProxyInstance( 094 pmdHandler.getClass().getClassLoader(), 095 new Class[] {ParameterMetaData.class}, 096 pmdHandler); 097 } 098 099 public void testFillStatementWithBean() throws SQLException { 100 TestBean tb = new TestBean(); 101 tb.setOne("uno"); 102 tb.setTwo("dos"); 103 tb.setThree("tres"); 104 NoOpFillStatement fakeQueryRunner = new NoOpFillStatement(); 105 fakeQueryRunner.fillStatementWithBean(stmt, tb, new String[] {"three", "two", "one"}); 106 String[] expected = new String[] {"tres", "dos", "uno"}; 107 assertArrayEquals("Statement filled with incorrect parameters", expected, fakeQueryRunner.params); 108 } 109 110 private PreparedStatement fakePreparedStatement() { 111 InvocationHandler noOpHandler = new InvocationHandler() { 112 public Object invoke(Object proxy, Method method, Object[] args) 113 throws Throwable { 114 return null; 115 } 116 }; 117 PreparedStatement stmt = ProxyFactory.instance().createPreparedStatement(noOpHandler); 118 return stmt; 119 } 120 121 public void testFillStatementWithBeanErrorNoReadMethod() throws Exception { 122 TestBean tb = new TestBean(); 123 PropertyDescriptor noReadMethod = new PropertyDescriptor("one", TestBean.class, null, "setOne"); 124 125 PropertyDescriptor properties[] = new PropertyDescriptor[] { noReadMethod }; 126 try { 127 runner.fillStatementWithBean(stmt, tb, properties); 128 fail("Expected RuntimeException: tried to use a property with no read method"); 129 } catch (RuntimeException expected) {} 130 } 131 132 public void testFillStatementWithBeanErrorBadReadMethod() throws Exception { 133 PropertyDescriptor badReadMethod = new IndexedPropertyDescriptor("indexed", getClass(), null, null, "getIndexed", null) { 134 public synchronized Method getReadMethod() { 135 return super.getIndexedReadMethod(); 136 } 137 }; 138 PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; 139 try { 140 runner.fillStatementWithBean(stmt, this, properties); 141 fail("Expected RuntimeException: tried to use a property with no no-arg read method"); 142 } catch (RuntimeException expected) {} 143 } 144 145 public void testFillStatementWithBeanErrorReadMethodThrows() throws Exception { 146 PropertyDescriptor badReadMethod = new PropertyDescriptor("throwsException", getClass(), "getThrowsException", null); 147 PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; 148 try { 149 runner.fillStatementWithBean(stmt, this, properties); 150 fail("Expected RuntimeException: tried to call a method that throws"); 151 } catch (RuntimeException expected) {} 152 } 153 154 public void testFillStatementWithBeanErrorReadMethodPrivate() throws Exception { 155 getPrivate(); 156 PropertyDescriptor badReadMethod = new BadPrivatePropertyDescriptor(); 157 PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; 158 try { 159 runner.fillStatementWithBean(stmt, this, properties); 160 fail("Expected RuntimeException: tried to call a private method"); 161 } catch (RuntimeException expected) {} 162 } 163 164 class BadPrivatePropertyDescriptor extends PropertyDescriptor { 165 Method getPrivate; 166 BadPrivatePropertyDescriptor() throws Exception { 167 super("throwsException", QueryRunnerTest.class, "getThrowsException", null); 168 getPrivate = QueryRunnerTest.class.getDeclaredMethod("getPrivate", new Class[0]); 169 } 170 171 public synchronized Method getReadMethod() { 172 if (getPrivate == null) return super.getReadMethod(); 173 return getPrivate; 174 } 175 } 176 177 public void testRethrowNullMessage() { 178 // DBUTILS-40 179 SQLException sqe = new SQLException((String)null); 180 QueryRunner qr = new QueryRunner(); 181 try { 182 qr.rethrow(sqe, "foo", new Object[] {"bar"}); 183 fail("rethrow didn't throw"); 184 } catch (SQLException expected) {} 185 } 186 187 // indexed bean property 188 public String getIndexed(int index) { 189 return null; 190 } 191 192 public String getThrowsException() { 193 throw new RuntimeException("this getter always throws an exception"); 194 } 195 196 private String getPrivate() { 197 return null; 198 } 199 200 private void assertArrayEquals(String message, Object[] expected, Object[] actual) { 201 assertEquals(message, Arrays.asList(expected).toString(), Arrays.asList(actual).toString()); 202 assertEquals(message, expected.length, actual.length); 203 } 204 205 206 private class NoOpFillStatement extends QueryRunner { 207 Object[] params; 208 public void fillStatement(PreparedStatement stmt, Object[] params) 209 throws SQLException { 210 this.params = params; 211 } 212 } 213 214 }