KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derbyTesting > functionTests > tests > lang > xmlBinding


1 /*
2
3    Derby - Class org.apache.derbyTesting.functionTests.tests.lang.xmlBinding
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to You under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derbyTesting.functionTests.tests.lang;
23
24 import java.io.FileReader JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.InputStreamReader JavaDoc;
27
28 import java.sql.Connection JavaDoc;
29 import java.sql.PreparedStatement JavaDoc;
30 import java.sql.ResultSet JavaDoc;
31 import java.sql.Statement JavaDoc;
32 import java.sql.SQLException JavaDoc;
33 import java.sql.Types JavaDoc;
34
35 import org.apache.derby.tools.ij;
36 import org.apache.derby.tools.JDBCDisplayUtil;
37
38 /**
39  * This class checks to make sure that the XML data type and
40  * the corresponding XML operations all work as expected
41  * from the JDBC side of things. In particular, this test
42  * verifies that 1) it is NOT possible to bind to/from an XML
43  * datatype (because the JDBC specification doesn't indicate
44  * how that should be done), and 2) the correct behavior
45  * occurs when null values (both Java and SQL) are bound
46  * into the bindable parameters for the XML operators.
47  * This file also checks that insertion from XML files
48  * via a character stream works, which is important since
49  * XML files can be arbitrarily long and thus stream-based
50  * processing is a must.
51  */

52 public class xmlBinding
53 {
54     /**
55      * Create an instance of this class and do the test.
56      */

57     public static void main(String JavaDoc [] args)
58     {
59         new xmlBinding().go(args);
60     }
61
62     /**
63      * Create a JDBC connection using the arguments passed
64      * in from the harness, and then run the binding
65      * tests.
66      * @param args Arguments from the harness.
67      */

68     public void go(String JavaDoc [] args)
69     {
70         try {
71
72             // use the ij utility to read the property file and
73
// make the initial connection.
74
ij.getPropertyArg(args);
75             Connection JavaDoc conn = ij.startJBMS();
76
77             // Create our test table.
78
Statement JavaDoc st = conn.createStatement();
79             st.execute("create table xTable.t1 " +
80                 "(i int generated always as identity, x xml)");
81
82             // Do the tests.
83
doBindTests(conn);
84             doXMLParseTests(conn);
85             doXMLSerializeTests(conn);
86             doXMLExistsTests(conn);
87
88             // Clean up.
89
st.close();
90             conn.close();
91
92             System.out.println("[ Done. ]\n");
93
94         } catch (Exception JavaDoc e) {
95
96             System.out.println("Unexpected error: ");
97             e.printStackTrace(System.out);
98
99         }
100     }
101
102     /**
103      * Performs a series of binding checks to make sure
104      * binding to an XML value never works.
105      * @param conn A connection to the test database.
106      */

107     private void doBindTests(Connection JavaDoc conn)
108     {
109         // Make sure that attempts to bind _to_ XML will fail.
110
System.out.println("\n[ Beginning XML binding tests. ]\n");
111
112         // Binding to an XML column.
113
PreparedStatement JavaDoc pSt = null;
114         try {
115
116             // If we're running in embedded mode or else with
117
// the Derby Client, then the next line will fail
118
// because there is NO deferred prepare. If, however,
119
// we're running with JCC, the default is to defer
120
// the prepare until execution, so the next line will
121
// be fine, but the following four checks will fail.
122
// This difference in behavior okay--it requires two
123
// different masters, but ultimately it's a good way
124
// to check behavior in both cases.
125
pSt = conn.prepareStatement(
126                 "insert into xTable.t1(x) values (?)");
127
128             System.out.print("XML column -- bind String to XML: ");
129             bindAndExecute(pSt, 1, Types.VARCHAR, "shouldn't work", "42Z70", false);
130
131             System.out.print("XML column -- bind Java null to XML: ");
132                bindAndExecute(pSt, 1, Types.VARCHAR, null, "42Z70", false);
133
134             System.out.print("XML column -- bind SQL NULL to XML: ");
135             bindAndExecute(pSt, 1, Types.VARCHAR, null, "42Z70", true);
136
137             System.out.print("XML column -- bind integer to XML: ");
138             bindAndExecute(pSt, 1, Types.INTEGER, new Integer JavaDoc(8), "42Z70", false);
139
140         } catch (SQLException JavaDoc se) {
141             // Must be running with embedded or Derby Network Client.
142
System.out.print("XML column -- insertion via parameter: ");
143             checkException(se, "42Z70");
144         }
145
146         // Binding to an XML value in the XMLSERIALIZE operator.
147
// Should get compile-time error saying that
148
// parameters aren't allowed for XML data values.
149
System.out.print("Trying to bind to XML in XMLSERIALIZE: ");
150         try {
151             pSt = conn.prepareStatement(
152                 "select XMLSERIALIZE(? AS CLOB) FROM XTABLE.T1");
153             bindAndExecute(pSt, 1, Types.VARCHAR, null, "42Z70", true);
154         } catch (SQLException JavaDoc se) {
155             checkException(se, "42Z70");
156         }
157
158         // Binding to an XML value in the XMLEXISTS operator.
159
// Should get compile-time error saying that
160
// parameters aren't allowed for XML data values.
161
System.out.print("Trying to bind to XML in XMLEXISTS: ");
162         try {
163             pSt = conn.prepareStatement(
164                 "select i from xTable.t1 where " +
165                 "XMLEXISTS('//*' PASSING BY REF ?)");
166             bindAndExecute(pSt, 1, Types.VARCHAR, null, "42Z70", true);
167         } catch (SQLException JavaDoc se) {
168             checkException(se, "42Z70");
169         }
170
171         // Make sure that attempts to bind _from_ XML will fail.
172
// We should fail at compile time, even before
173
// we get a chance to execute the query.
174
System.out.print("XML value in result set: ");
175         try {
176             pSt = conn.prepareStatement("select x from xTable.t1");
177             pSt.execute();
178         } catch (SQLException JavaDoc se) {
179             checkException(se, "42Z71");
180         }
181
182         System.out.println("\n[ End XML binding tests. ]\n");
183     }
184
185     /**
186      * Test insertion of documents larger than 32K (this
187      * will test stream processing of XML data), and
188      * test binding of null values in the XMLPARSE
189      * operator.
190      * @param conn A connection to the test database.
191      */

192     private void doXMLParseTests(Connection JavaDoc conn)
193     {
194         System.out.println("\n[ Beginning XMLPARSE tests. ]\n");
195
196         System.out.println("Test insertions from file: ");
197         try {
198
199             // Test parsing of > 32K XML documents.
200
insertFiles(conn, "xTable.t1", "xmlTestFiles/wide40k.xml", 1);
201             insertFiles(conn, "xTable.t1", "xmlTestFiles/deep40k.xml", 1);
202
203             // Test parsing of docs that use schemas. Since server
204
// and client tests run in a subdirectory, we have to modify
205
// the XML documents that use DTDs so that they can find
206
// the DTD files.
207

208             insertDocWithDTD(conn, "xTable.t1", "xmlTestFiles/dtdDoc.xml",
209                 "personal.dtd", 1);
210             insertFiles(conn, "xTable.t1", "xmlTestFiles/xsdDoc.xml", 1);
211
212             // XMLPARSE is not supposed to validate, so the following
213
// inserts should SUCCEED, even though the documents
214
// don't adhere to their schemas.
215
insertDocWithDTD(conn, "xTable.t1",
216                 "xmlTestFiles/dtdDoc_invalid.xml", "personal.dtd", 1);
217             insertFiles(conn, "xTable.t1",
218                 "xmlTestFiles/xsdDoc_invalid.xml", 1);
219
220             System.out.println("--> Insertions all PASS.");
221
222         } catch (SQLException JavaDoc se) {
223             System.out.println("FAIL: Unexpected exception: ");
224             while (se != null) {
225                 se.printStackTrace(System.out);
226                 se = se.getNextException();
227             }
228         } catch (Exception JavaDoc e) {
229             System.out.println("FAIL: Unexpected exception: ");
230             e.printStackTrace(System.out);
231         }
232
233         // Test binding nulls to the XMLPARSE operand.
234

235         try {
236
237             PreparedStatement JavaDoc pSt = conn.prepareStatement(
238                 "insert into xTable.t1(x) values " +
239                 "(XMLPARSE (DOCUMENT CAST (? as CLOB) PRESERVE WHITESPACE))");
240
241             // This should work. Note we check binding to
242
// a character stream method in "insertFiles".
243
System.out.print("Binding string in XMLPARSE: ");
244             bindAndExecute(pSt, 1, Types.CHAR, "<simple> doc </simple>",
245                 null, false);
246
247             // Null should work, too.
248
System.out.print("Binding Java null string in XMLPARSE: ");
249             bindAndExecute(pSt, 1, Types.CHAR, null, null, false);
250             System.out.print("Binding SQL NULL string in XMLPARSE: ");
251             bindAndExecute(pSt, 1, Types.CLOB, null, null, true);
252
253         } catch (Exception JavaDoc e) {
254             System.out.println("Unexpected exception: ");
255             e.printStackTrace(System.out);
256         }
257
258         System.out.println("\n[ End XMLPARSE tests. ]\n");
259     }
260
261     /**
262      * Test serialization of the XML values inserted by
263      * the doXMLParseTests() method above. For the documents
264      * that are larger than 32K, this tests that they can
265      * be correctly read from disk as a stream (instead of
266      * just as as string).
267      * @param conn A connection to the test database.
268      */

269     private void doXMLSerializeTests(Connection JavaDoc conn)
270     {
271         System.out.println("\n[ Beginning XMLSERIALIZE tests. ]\n");
272
273         try {
274
275             PreparedStatement JavaDoc pSt = conn.prepareStatement(
276                 "select i, XMLSERIALIZE(X AS CLOB) FROM xTable.t1");
277             ResultSet JavaDoc rs = pSt.executeQuery();
278
279             String JavaDoc xResult = null;
280             int rowCount = 0;
281             while (rs.next()) {
282                 xResult = rs.getString(2);
283                 if (!rs.wasNull()) {
284                     System.out.println(rs.getInt(1) + ", " +
285                         "[ roughly " + (xResult.length() / 1000) + "k ]");
286                 }
287                 else
288                     System.out.println(rs.getInt(1) + ", NULL");
289                 rowCount++;
290             }
291
292         } catch (Exception JavaDoc e) {
293             System.out.println("Unexpected exception: ");
294             e.printStackTrace(System.out);
295         }
296
297         // Test binding to the XMLSERIALIZE operand. Since
298
// the operand is an XML value, and since we don't
299
// allow binding to an XML value (see "doBindTests()"
300
// above), there's nothing more to do here.
301

302         System.out.println("\n[ End XMLSERIALIZE tests. ]\n");
303     }
304
305     /**
306      * Run some simple XPath queries against the documents
307      * inserted in doXMLParseTests() above, and then test
308      * binding of null values in the XMLEXISTS operator.
309      * @param conn A connection to the test database.
310      */

311     private void doXMLExistsTests(Connection JavaDoc conn)
312     {
313         System.out.println("\n[ Begin XMLEXISTS tests. ]\n");
314
315         // Run some sample queries.
316
try {
317
318             existsQuery(conn, "xTable.t1", "//abb");
319             existsQuery(conn, "xTable.t1", "//d50");
320             existsQuery(conn, "xTable.t1", "//person/email");
321             existsQuery(conn, "xTable.t1", "/personnel");
322             existsQuery(conn, "xTable.t1", "//person/@id");
323
324             // This next one is important because it verifies
325
// that implicit/default values which are defined
326
// in a DTD _are_ actually processed, even though
327
// we don't perform validation. Thus this next
328
// query _should_ return a match.
329
int rowCount = existsQuery(conn, "xTable.t1", "//person/@noteTwo");
330             if (rowCount == 0) {
331                 System.out.println("FAILED: Query on DTD default didn't " +
332                     "return any matches.");
333             }
334
335         } catch (Exception JavaDoc e) {
336             System.out.println("Unexpected exception: ");
337             e.printStackTrace(System.out);
338         }
339
340         // Test binding to the XMLEXISTS operands. Binding
341
// of the second (XML) operand is not allowed and was
342
// checked in "doBindTests()" above. Here we check
343
// binding of the first operand, which should fail
344
// because SQL/XML spec says the first operand must
345
// be a string literal.
346
try {
347
348             System.out.print("Parameter as first operand in XMLEXISTS: ");
349
350             // If we're running in embedded mode or else with
351
// the Derby Client, then the next line will fail
352
// because there is NO deferred prepare. If, however,
353
// we're running with JCC, the default is to defer
354
// the prepare until execution, so the next line will
355
// be fine, but the subsequent "execute" should fail.
356
PreparedStatement JavaDoc pSt = conn.prepareStatement(
357                 "select i from xTable.t1 where " +
358                 "XMLEXISTS (? PASSING BY REF x)");
359             pSt.setString(1, "//*");
360             pSt.execute();
361
362         } catch (SQLException JavaDoc se) {
363             checkException(se, "42Z75");
364         }
365
366         System.out.println("\n[ End XMLEXISTS tests. ]\n");
367     }
368
369     /**
370      * Helper method. Inserts the contents of a file into
371      * the received table using "setCharacterStream".
372      * @param conn A connection to the test database.
373      * @param tableName Name of the target table
374      * @param fName Name of the file whose content we
375      * want to insert.
376      * @param numRows Number of times we should insert
377      * the received file's content.
378      */

379     private void insertFiles(Connection JavaDoc conn,
380         String JavaDoc tableName, String JavaDoc fName, int numRows)
381         throws Exception JavaDoc
382     {
383         // First we have to figure out many chars long the
384
// file is.
385
InputStream JavaDoc iS = this.getClass().getResourceAsStream(fName);
386         InputStreamReader JavaDoc reader = new InputStreamReader JavaDoc(iS);
387         char [] cA = new char[1024];
388         int charCount = 0;
389         for (int len = reader.read(cA, 0, cA.length); len != -1;
390             charCount += len, len = reader.read(cA, 0, cA.length));
391
392         reader.close();
393
394         // Now that we know the number of characters, we can
395
// insert using a stream.
396

397         PreparedStatement JavaDoc pSt = conn.prepareStatement(
398             "insert into xTable.t1(x) values (" +
399             "xmlparse(document cast (? as clob) preserve whitespace))");
400
401         for (int i = 0; i < numRows; i++) {
402
403             iS = this.getClass().getResourceAsStream(fName);
404             reader = new InputStreamReader JavaDoc(iS);
405             pSt.setCharacterStream(1, reader, charCount);
406             pSt.execute();
407             reader.close();
408             System.out.println("Inserted roughly " +
409                 (charCount / 1000) + "k of data.");
410
411         }
412     }
413
414     /**
415      * Helper method. Inserts an XML document into the
416      * received table using setString. This method
417      * parallels "insertFiles" above, except that it
418      * should be used for documents that require a DTD
419      * in order to be complete. In that case, the
420      * location of the DTD has to modified _in_ the
421      * document so that it can be found regardless of
422      * whether we're running in embedded mode or in
423      * server/client mode.
424      * @param conn A connection to the test database.
425      * @param tableName Name of the target table
426      * @param fName Name of the file whose content we
427      * want to insert.
428      * @param dtdName Name of the DTD file that the
429      * received file uses.
430      * @param numRows Number of times we should insert
431      * the received file's content.
432      */

433     private void insertDocWithDTD(Connection JavaDoc conn,
434         String JavaDoc tableName, String JavaDoc fName, String JavaDoc dtdName,
435         int numRows) throws Exception JavaDoc
436     {
437         boolean needsUpdate = true;
438         String JavaDoc currPath = System.getProperty("user.dir");
439         String JavaDoc fileSep = System.getProperty("file.separator");
440
441         String JavaDoc dtdPath = currPath;
442         boolean foundDTD = false;
443         while (!foundDTD) {
444
445             try {
446
447                 FileReader JavaDoc fR = new FileReader JavaDoc(dtdPath +
448                     fileSep + dtdName);
449
450                 // If we get here, then we found the DTD in
451
// the current path, so we're done.
452
foundDTD = true;
453                 dtdPath = "file:///" + dtdPath + fileSep + dtdName;
454                 break;
455
456             } catch (java.io.IOException JavaDoc ie) {
457
458                 // Couldn't find the DTD in the current path.
459
// The harness uses a lot of subdirectories when
460
// running tests (for client, or server, or
461
// suites, or nested suites...etc.), so we
462
// back up one directory and try again.
463

464                 int pos = dtdPath.lastIndexOf(fileSep);
465                 if (pos == -1) {
466                 // we're at the top of the path and haven't
467
// found the DTD yet. This shouldn't happen.
468
throw new Exception JavaDoc("Couldn't find DTD '" +
469                         dtdName + "' for insertion of file '" +
470                         fName + "'.");
471                 }
472                 dtdPath = dtdPath.substring(0, pos);
473
474             }
475         }
476
477         // Read the file into memory so we can update it.
478
InputStream JavaDoc iS = this.getClass().getResourceAsStream(fName);
479         InputStreamReader JavaDoc reader = new InputStreamReader JavaDoc(iS);
480         char [] cA = new char[1024];
481         StringBuffer JavaDoc sBuf = new StringBuffer JavaDoc();
482         int charCount = 0;
483         for (int len = reader.read(cA, 0, cA.length); len != -1;
484             charCount += len, len = reader.read(cA, 0, cA.length))
485         {
486             sBuf.append(cA, 0, len);
487         }
488
489         reader.close();
490
491         // Now replace the DTD location, if needed.
492
String JavaDoc docAsString = sBuf.toString();
493         int pos = docAsString.indexOf(dtdName);
494         if (pos != -1)
495             sBuf.replace(pos, pos + dtdName.length(), dtdPath);
496
497         // Now (finally) do the insert using the in-memory
498
// document with the correct DTD location.
499
docAsString = sBuf.toString();
500         PreparedStatement JavaDoc pSt = conn.prepareStatement(
501             "insert into xTable.t1(x) values (" +
502             "xmlparse(document cast (? as clob) preserve whitespace))");
503
504         charCount = docAsString.length();
505         for (int i = 0; i < numRows; i++) {
506
507             pSt.setString(1, docAsString);
508             pSt.execute();
509             System.out.println("Inserted roughly " +
510                 (charCount / 1000) + "k of data.");
511
512         }
513     }
514
515     /**
516      * Helper method. Selects all rows from the received
517      * table name that have at least one node matching
518      * the received XPath expression. Does this query
519      * using the XMLEXISTS operator.
520      * @param conn A connection to the test database.
521      * @param tableName Table to query.
522      * @param xPath The XPath expression to evaluate.
523      * @return The number of rows that match the
524      * XPath expression.
525      */

526     private int existsQuery(Connection JavaDoc conn,
527         String JavaDoc tableName, String JavaDoc xPath) throws Exception JavaDoc
528     {
529         PreparedStatement JavaDoc pSt = conn.prepareStatement(
530             "select i from " + tableName + " where " +
531             "xmlexists('" + xPath + "' passing by ref x)");
532
533         System.out.println("Running XMLEXISTS with: " + xPath);
534         ResultSet JavaDoc rs = pSt.executeQuery();
535         String JavaDoc xResult = null;
536         int rowCount = 0;
537         while (rs.next()) {
538             rowCount++;
539         }
540
541         System.out.println("--> Matching rows: " + rowCount);
542         return rowCount;
543     }
544
545     /**
546      * Helper method. Attempts to bind a parameter to a
547      * given value using the given type, and then prints
548      * the result of that attempt (PASS/FAIL).
549      * @param pSt The prepared statement holding the parameter
550      * that we want to bind.
551      * @param paramNum Which parameter in pSt we want to bind.
552      * @param paramType The type of the value to be bound.
553      * @param bindValue The value to be used for binding.
554      * @param sqlState The expected SQLState for the binding
555      * error, if one is expected. Null if the bind is expected
556      * to succeed.
557      * @param bindSqlNull True if we should bind using a SQL
558      * NULL (i.e. "setNull()").
559      */

560     private void bindAndExecute(PreparedStatement JavaDoc pSt, int paramNum,
561         int paramType, Object JavaDoc bindValue, String JavaDoc sqlState,
562         boolean bindSqlNull)
563     {
564         SQLException JavaDoc actualException = null;
565         try {
566
567             // First try to bind.
568
if (bindSqlNull) {
569                 pSt.setNull(paramNum, paramType);
570             }
571             else {
572                 switch (paramType)
573                 {
574                     case Types.CHAR:
575                     case Types.VARCHAR:
576
577                         pSt.setString(paramNum, (String JavaDoc)bindValue);
578                         break;
579
580                     case Types.INTEGER:
581
582                         pSt.setInt(paramNum, ((Integer JavaDoc)bindValue).intValue());
583                         break;
584
585                     default:
586
587                         System.out.println("ERROR: Unexpected bind type (" +
588                             paramType + ") in call to doBind.");
589                         break;
590                 }
591             }
592
593             // Now try to execute.
594
pSt.execute();
595
596         } catch (SQLException JavaDoc e) {
597             actualException = e;
598         }
599
600         checkException(actualException, sqlState);
601     }
602
603     /**
604      * Helper method. Checks to see if the received SQLException
605      * has a SQLState that matches the target/expected SQLState.
606      * Prints out a message saying the result of this check, and
607      * in the case where the actual error is NOT the expected
608      * error, prints a full stack trace to System.out.
609      * @param se The SQLException to be checked.
610      * @param targetState The expected SQLState; null if no
611      * error was expected.
612      */

613     private void checkException(SQLException JavaDoc se,
614         String JavaDoc targetState)
615     {
616         if (targetState == null) {
617             if (se == null) {
618                 System.out.println("PASS -- Completed without exception, " +
619                     "as expected.");
620             }
621             else {
622                 System.out.println("FAIL -- Was expected to succeed, but " +
623                     "failed with error " + se.getSQLState() + ".");
624                 se.printStackTrace(System.out);
625             }
626             return;
627         }
628
629         if (se == null) {
630             System.out.println("FAIL -- Completed without exception when " +
631                 "error " + targetState + " was expected.");
632             return;
633         }
634
635         if (!targetState.equals(se.getSQLState())) {
636             System.out.println("FAIL: Caught error " + se.getSQLState() +
637                 " when was expecting error " + targetState + ".");
638             se.printStackTrace(System.out);
639             return;
640         }
641
642         System.out.println("PASS -- caught expected error " +
643             targetState + ".");
644     }
645 }
646
Popular Tags