View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.beanutils2.sql;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  
26  import java.math.BigDecimal;
27  import java.sql.Date;
28  import java.sql.ResultSet;
29  import java.sql.ResultSetMetaData;
30  import java.sql.SQLException;
31  import java.sql.Types;
32  import java.util.List;
33  
34  import org.apache.commons.beanutils2.DynaBean;
35  import org.apache.commons.beanutils2.DynaProperty;
36  import org.junit.jupiter.api.AfterEach;
37  import org.junit.jupiter.api.BeforeEach;
38  import org.junit.jupiter.api.Test;
39  
40  /**
41   * Test accessing RowSets via DynaBeans.
42   */
43  public class DynaRowSetTest {
44  
45      private static class CustomTimestamp {
46          private final long timestamp = new java.util.Date().getTime();
47  
48          @Override
49          public String toString() {
50              return "CustomTimestamp[" + timestamp + "]";
51          }
52      }
53  
54      /**
55       * A proxy ResultSet implementation that returns Timstamp for a date column.
56       *
57       * See issue# https://issues.apache.org/jira/browse/BEANUTILS-142
58       */
59      private static class TestResultSetInconsistent extends TestResultSet {
60  
61          public TestResultSetInconsistent(final ResultSetMetaData metaData) {
62              super(metaData);
63          }
64  
65          /**
66           * Gets an columns's value
67           *
68           * @param columnName Name of the column
69           * @return the column value
70           * @throws SQLException if an error occurs
71           */
72          @Override
73          public Object getObject(final String columnName) throws SQLException {
74              if ("timestampProperty".equals(columnName)) {
75                  return new CustomTimestamp();
76              }
77              return super.getObject(columnName);
78          }
79  
80      }
81  
82      /**
83       * A proxy ResultSetMetaData implementation that returns a class name that is inconsistent with the type returned by the ResultSet.getObject() method.
84       *
85       * See issue# https://issues.apache.org/jira/browse/BEANUTILS-142
86       */
87      private static class TestResultSetMetaDataInconsistent extends TestResultSetMetaData {
88  
89          /**
90           * This method substitues class names of "java.sql.Timestamp" with "java.sql.Date" to test inconsistent JDBC drivers.
91           *
92           * @param columnIndex The column index
93           * @return The column class name
94           * @throws SQLException if an error occurs
95           */
96          @Override
97          public String getColumnClassName(final int columnIndex) throws SQLException {
98              final String columnName = getColumnName(columnIndex);
99              if (columnName.equals("dateProperty")) {
100                 return java.sql.Timestamp.class.getName();
101             }
102             if (columnName.equals("timestampProperty")) {
103                 return CustomTimestamp.class.getName();
104             }
105             return super.getColumnClassName(columnIndex);
106         }
107     }
108 
109     /**
110      * The mock result set DynaClass to be tested.
111      */
112     protected RowSetDynaClass dynaClass;
113 
114     /**
115      * Names of the columns for this test. Must match the order they are defined in {@link TestResultSetMetaData}, and must be all lower case.
116      */
117     protected String[] columns = { "bigdecimalproperty", "booleanproperty", "byteproperty", "dateproperty", "doubleproperty", "floatproperty", "intproperty",
118             "longproperty", "nullproperty", "shortproperty", "stringproperty", "timeproperty", "timestampproperty" };
119 
120     /**
121      * Sets up instance variables required by this test case.
122      */
123     @BeforeEach
124     public void setUp() throws Exception {
125 
126         dynaClass = new RowSetDynaClass(TestResultSet.createProxy());
127 
128     }
129 
130     /**
131      * Tear down instance variables required by this test case.
132      */
133     @AfterEach
134     public void tearDown() {
135 
136         dynaClass = null;
137 
138     }
139 
140     @Test
141     public void testGetDynaProperties() {
142 
143         final DynaProperty[] dynaProps = dynaClass.getDynaProperties();
144         assertNotNull(dynaProps, "dynaProps exists");
145         assertEquals(columns.length, dynaProps.length, "dynaProps length");
146         for (int i = 0; i < columns.length; i++) {
147             assertEquals(columns[i], dynaProps[i].getName(), "Property " + columns[i]);
148         }
149 
150     }
151 
152     @Test
153     public void testGetDynaProperty() {
154         // Invalid argument test
155         assertThrows(NullPointerException.class, () -> dynaClass.getDynaProperty(null));
156         // Negative test
157         DynaProperty dynaProp = dynaClass.getDynaProperty("unknownProperty");
158         assertNull(dynaProp, "unknown property returns null");
159         // Positive test
160         dynaProp = dynaClass.getDynaProperty("stringproperty");
161         assertNotNull(dynaProp, "string property exists");
162         assertEquals("stringproperty", dynaProp.getName(), "string property name");
163         assertEquals(String.class, dynaProp.getType(), "string property class");
164     }
165 
166     @Test
167     public void testGetName() {
168         assertEquals("org.apache.commons.beanutils2.sql.RowSetDynaClass", dynaClass.getName(), "DynaClass name");
169     }
170 
171     /**
172      * Test issues associated with Oracle JDBC driver.
173      *
174      * See issue# https://issues.apache.org/jira/browse/BEANUTILS-142
175      *
176      * @throws Exception if an error occurs
177      */
178     @Test
179     public void testInconsistentOracleDriver() throws Exception {
180 
181         final ResultSetMetaData metaData = TestResultSetMetaData.createProxy(new TestResultSetMetaDataInconsistent());
182         final ResultSet resultSet = TestResultSet.createProxy(new TestResultSetInconsistent(metaData));
183 
184         // Date Column returns "java.sql.Timestamp" for the column class name but ResultSet getObject
185         // returns a java.sql.Date value
186         final int dateColIdx = 4;
187         assertEquals("dateProperty", metaData.getColumnName(dateColIdx), "Date Meta Name");
188         assertEquals("java.sql.Timestamp", metaData.getColumnClassName(dateColIdx), "Date Meta Class");
189         assertEquals(Types.DATE, metaData.getColumnType(dateColIdx), "Date Meta Type");
190         assertEquals(Date.class, resultSet.getObject("dateProperty").getClass(), "Date ResultSet Value");
191 
192         // Timestamp column class returns a custom Timestamp impl for the column class name and ResultSet getObject
193         final int timestampColIdx = 13;
194         assertEquals("timestampProperty", metaData.getColumnName(timestampColIdx), "Timestamp Meta Name");
195         assertEquals(CustomTimestamp.class.getName(), metaData.getColumnClassName(timestampColIdx), "Timestamp Meta Class");
196         assertEquals(Types.TIMESTAMP, metaData.getColumnType(timestampColIdx), "Timestamp Meta Type");
197         assertEquals(CustomTimestamp.class, resultSet.getObject("timestampProperty").getClass(), "Timestamp ResultSet Value");
198 
199         final RowSetDynaClass inconsistentDynaClass = new RowSetDynaClass(resultSet);
200         final DynaBean firstRow = inconsistentDynaClass.getRows().get(0);
201         Class<?> expectedType;
202         DynaProperty property;
203 
204         // Test Date
205         property = firstRow.getDynaClass().getDynaProperty("dateproperty");
206         expectedType = java.sql.Date.class;
207         assertEquals(expectedType, property.getType(), "Date Class");
208         assertEquals(expectedType, firstRow.get(property.getName()).getClass(), "Date Value");
209 
210         // Test Timestamp
211         property = firstRow.getDynaClass().getDynaProperty("timestampproperty");
212         expectedType = java.sql.Timestamp.class;
213         assertEquals(expectedType, property.getType(), "Timestamp Class");
214         assertEquals(expectedType, firstRow.get(property.getName()).getClass(), "Timestamp Value");
215     }
216 
217     @Test
218     public void testLimitedRows() throws Exception {
219 
220         // created one with low limit
221         final RowSetDynaClass limitedDynaClass = new RowSetDynaClass(TestResultSet.createProxy(), 3);
222         final List<DynaBean> rows = limitedDynaClass.getRows();
223         assertNotNull(rows, "list exists");
224         assertEquals(3, rows.size(), "limited row count");
225 
226     }
227 
228     @Test
229     public void testListCount() {
230 
231         final List<DynaBean> rows = dynaClass.getRows();
232         assertNotNull(rows, "list exists");
233         assertEquals(5, rows.size(), "list row count");
234 
235     }
236 
237     @Test
238     public void testListResults() {
239 
240         // Grab the third row
241         final List<DynaBean> rows = dynaClass.getRows();
242         final DynaBean row = rows.get(2);
243 
244         // Invalid argument test
245         assertThrows(IllegalArgumentException.class, () -> row.get("unknownProperty"));
246 
247         // Verify property values
248 
249         final Object bigDecimalProperty = row.get("bigdecimalproperty");
250         assertNotNull(bigDecimalProperty, "bigDecimalProperty exists");
251         assertInstanceOf(BigDecimal.class, bigDecimalProperty, "bigDecimalProperty type");
252         assertEquals(123.45, ((BigDecimal) bigDecimalProperty).doubleValue(), 0.005, "bigDecimalProperty value");
253 
254         final Object intProperty = row.get("intproperty");
255         assertNotNull(intProperty, "intProperty exists");
256         assertInstanceOf(Integer.class, intProperty, "intProperty type");
257         assertEquals(103, ((Integer) intProperty).intValue(), "intProperty value");
258 
259         final Object nullProperty = row.get("nullproperty");
260         assertNull(nullProperty, "nullProperty null");
261 
262         final Object stringProperty = row.get("stringproperty");
263         assertNotNull(stringProperty, "stringProperty exists");
264         assertInstanceOf(String.class, stringProperty, "stringProperty type");
265         assertEquals("This is a string", (String) stringProperty, "stringProperty value");
266 
267     }
268 
269     /**
270      * Test normal case column names (i.e. not converted to lower case)
271      */
272     @Test
273     public void testListResultsNormalCase() throws Exception {
274         final RowSetDynaClass dynaClass = new RowSetDynaClass(TestResultSet.createProxy(), false);
275 
276         // Grab the third row
277         final List<DynaBean> rows = dynaClass.getRows();
278         final DynaBean row = rows.get(2);
279 
280         // Invalid argument test
281         assertThrows(IllegalArgumentException.class, () -> row.get("unknownProperty"));
282 
283         // Verify property values
284 
285         final Object bigDecimalProperty = row.get("bigDecimalProperty");
286         assertNotNull(bigDecimalProperty, "bigDecimalProperty exists");
287         assertInstanceOf(BigDecimal.class, bigDecimalProperty, "bigDecimalProperty type");
288         assertEquals(123.45, ((BigDecimal) bigDecimalProperty).doubleValue(), 0.005, "bigDecimalProperty value");
289 
290         final Object intProperty = row.get("intProperty");
291         assertNotNull(intProperty, "intProperty exists");
292         assertInstanceOf(Integer.class, intProperty, "intProperty type");
293         assertEquals(103, ((Integer) intProperty).intValue(), "intProperty value");
294 
295         final Object nullProperty = row.get("nullProperty");
296         assertNull(nullProperty, "nullProperty null");
297 
298         final Object stringProperty = row.get("stringProperty");
299         assertNotNull(stringProperty, "stringProperty exists");
300         assertInstanceOf(String.class, stringProperty, "stringProperty type");
301         assertEquals("This is a string", (String) stringProperty, "stringProperty value");
302 
303     }
304 
305     @Test
306     public void testNewInstance() {
307         assertThrows(UnsupportedOperationException.class, () -> dynaClass.newInstance());
308     }
309 }