KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > transformation > SQLTransformer


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.transformation;
17
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.io.StringReader JavaDoc;
21 import java.lang.reflect.Field JavaDoc;
22 import java.sql.CallableStatement JavaDoc;
23 import java.sql.Clob JavaDoc;
24 import java.sql.Connection JavaDoc;
25 import java.sql.DriverManager JavaDoc;
26 import java.sql.PreparedStatement JavaDoc;
27 import java.sql.ResultSet JavaDoc;
28 import java.sql.ResultSetMetaData JavaDoc;
29 import java.sql.SQLException JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.TreeMap JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.ArrayList JavaDoc;
36
37 import org.apache.avalon.excalibur.datasource.DataSourceComponent;
38 import org.apache.avalon.framework.activity.Disposable;
39 import org.apache.avalon.framework.configuration.Configurable;
40 import org.apache.avalon.framework.configuration.Configuration;
41 import org.apache.avalon.framework.configuration.ConfigurationException;
42 import org.apache.avalon.framework.logger.AbstractLogEnabled;
43 import org.apache.avalon.framework.parameters.Parameters;
44 import org.apache.avalon.framework.service.ServiceException;
45 import org.apache.avalon.framework.service.ServiceManager;
46 import org.apache.avalon.framework.service.ServiceSelector;
47 import org.apache.cocoon.ProcessingException;
48 import org.apache.cocoon.components.sax.XMLDeserializer;
49 import org.apache.cocoon.components.sax.XMLSerializer;
50 import org.apache.cocoon.environment.SourceResolver;
51 import org.apache.cocoon.xml.IncludeXMLConsumer;
52
53 import org.apache.cocoon.transformation.helpers.TextRecorder;
54
55 import org.apache.commons.lang.StringEscapeUtils;
56 import org.apache.commons.lang.StringUtils;
57 import org.apache.excalibur.xml.sax.SAXParser;
58 import org.xml.sax.Attributes JavaDoc;
59 import org.xml.sax.InputSource JavaDoc;
60 import org.xml.sax.SAXException JavaDoc;
61 import org.xml.sax.helpers.AttributesImpl JavaDoc;
62
63 /**
64  * The <code>SQLTransformer</code> can be plugged into a pipeline to transform
65  * SAX events into updated or queries and responses to/from a SQL interface.
66  *
67  * <p>
68  * It is declared and configured as follows:
69  * <pre>
70  * &lt;map:transformers default="..."&gt;
71  * &lt;map:transformer name="sql" SRC="org.apache.cocoon.transformation.SQLTransformer"&gt;
72  * &lt;old-driver&gt;false&lt;/old-driver&gt;
73  * &lt;connection-attempts&gt;5&lt;/connection-attempts&gt;
74  * &lt;connection-waittime&gt;5000&lt;/connection-waittime&gt;
75  * &lt;/map:transformer&gt;
76  * &lt;/map:transformers&gt;
77  * </pre>
78  * </p>
79  *
80  * <p>
81  * It can be used in the sitemap pipeline as follows:
82  * <code>
83  * &lt;map:transform type="sql"&gt;
84  * <!-- True to force each query to create its own connection: -->
85  * &lt;map:parameter name="own-connection" value="..."/&gt;
86  * <!-- Specify either name of datasource: -->
87  * &lt;map:parameter name="use-connection" value="..."/&gt;
88  * <!-- Or connection parameters: -->
89  * &lt;map:parameter name="dburl" value="..."/&gt;
90  * &lt;map:parameter name="username" value="..."/&gt;
91  * &lt;map:parameter name="password" value="..."/&gt;
92  *
93  * <!-- Default query parameters: -->
94  * &lt;map:parameter name="show-nr-or-rows" value="false"/&gt;
95  * &lt;map:parameter name="doc-element" value="rowset"/&gt;
96  * &lt;map:parameter name="row-element" value="row"/&gt;
97  * &lt;map:parameter name="namespace-uri" value="http://apache.org/cocoon/SQL/2.0"/&gt;
98  * &lt;map:parameter name="namespace-prefix" value="sql"/&gt;
99  * &lt;map:parameter name="clob-encoding" value=""/&gt;
100  * &lt;/map:transform&gt;
101  * </pre>
102  * </p>
103  *
104  * <p>
105  * The following DTD is valid:
106  * <code>
107  * &lt;!ENTITY % param "(own-connection?,(use-connection|(dburl,username,password))?,show-nr-or-rows?,doc-element?,row-element?,namespace-uri?,namespace-prefix?,clob-encoding?)"&gt;<br>
108  * &lt;!ELEMENT execute-query (query,(in-parameter|out-parameter)*,execute-query?, %param;)&gt;<br>
109  * &lt;!ELEMENT own-connection (#PCDATA)&gt;<br>
110  * &lt;!ELEMENT use-connection (#PCDATA)&gt;<br>
111  * &lt;!ELEMENT query (#PCDATA | substitute-value | ancestor-value | escape-string)*&gt;<br>
112  * &lt;!ATTLIST query name CDATA #IMPLIED isstoredprocedure (true|false) "false" isupdate (true|false) "false"&gt;<br>
113  * &lt;!ELEMENT substitute-value EMPTY&gt;<br>
114  * &lt;!ATTLIST substitute-value name CDATA #REQUIRED&gt;<br>
115  * &lt;!ELEMENT ancestor-value EMPTY&gt;<br>
116  * &lt;!ATTLIST ancestor-value name CDATA #REQUIRED level CDATA #REQUIRED&gt;<br>
117  * &lt;!ELEMENT in-parameter EMPTY&gt;<br>
118  * &lt;!ATTLIST in-parameter nr CDATA #REQUIRED type CDATA #REQUIRED&gt;<br>
119  * &lt;!ELEMENT out-parameter EMPTY&gt;<br>
120  * &lt;!ATTLIST out-parameter nr CDATA #REQUIRED name CDATA #REQUIRED type CDATA #REQUIRED&gt;<br>
121  * &lt;!ELEMENT escape-string (#PCDATA)&gt;<br>
122  * </code>
123  * </p>
124  *
125  * <p>
126  * Each query can override default transformer parameters. Nested queries do not inherit parent
127  * query parameters, but only transformer parameters. Each query can have connection to different
128  * database, directly or using the connection pool. If database connection parameters are the same
129  * as for any of the ancestor queries, nested query will re-use ancestor query connection.
130  * </p>
131  *
132  * <p>
133  * Connection sharing between queries can be disabled, globally or on per-query basis, using
134  * <code>own-connection</code> parameter.
135  * </p>
136  *
137  * <p>
138  * TODO: Support inserting of the XML data into the database without need to escape it.
139  * Can be implemented by introducing new &lt;sql:xml/&gt; tag to indicate that
140  * startSerializedXMLRecording(...) should be used.
141  * </p>
142  *
143  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
144  * @author <a HREF="mailto:balld@webslingerZ.com">Donald Ball</a>
145  * @author <a HREF="mailto:giacomo.pati@pwr.ch">Giacomo Pati</a>
146  * (PWR Organisation &amp; Entwicklung)
147  * @author <a HREF="mailto:sven.beauprez@the-ecorp.com">Sven Beauprez</a>
148  * @author <a HREF="mailto:a.saglimbeni@pro-netics.com">Alfio Saglimbeni</a>
149  * @author <a HREF="mailto:pmhahn@titan.lahn.de">Philipp Hahn</a>
150  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
151  * @version $Id: SQLTransformer.java 330529 2005-11-03 12:01:46Z jeremy $
152  */

153 public class SQLTransformer extends AbstractSAXTransformer
154                             implements Disposable, Configurable {
155
156     /** The SQL transformer namespace */
157     public static final String JavaDoc NAMESPACE = "http://apache.org/cocoon/SQL/2.0";
158
159     // The SQL trasformer namespace element names
160
public static final String JavaDoc MAGIC_EXECUTE_QUERY = "execute-query";
161     private static final String JavaDoc MAGIC_OWN_CONNECTION = "own-connection";
162     public static final String JavaDoc MAGIC_CONNECTION = "use-connection";
163     public static final String JavaDoc MAGIC_DBURL = "dburl";
164     public static final String JavaDoc MAGIC_USERNAME = "username";
165     public static final String JavaDoc MAGIC_PASSWORD = "password";
166     public static final String JavaDoc MAGIC_NR_OF_ROWS = "show-nr-of-rows";
167     public static final String JavaDoc MAGIC_QUERY = "query";
168     public static final String JavaDoc MAGIC_VALUE = "value";
169     public static final String JavaDoc MAGIC_COLUMN_CASE = "column-case";
170     public static final String JavaDoc MAGIC_DOC_ELEMENT = "doc-element";
171     public static final String JavaDoc MAGIC_ROW_ELEMENT = "row-element";
172     public static final String JavaDoc MAGIC_IN_PARAMETER = "in-parameter";
173     public static final String JavaDoc MAGIC_IN_PARAMETER_NR_ATTRIBUTE = "nr";
174     public static final String JavaDoc MAGIC_IN_PARAMETER_VALUE_ATTRIBUTE = "value";
175     public static final String JavaDoc MAGIC_OUT_PARAMETER = "out-parameter";
176     public static final String JavaDoc MAGIC_OUT_PARAMETER_NAME_ATTRIBUTE = "name";
177     public static final String JavaDoc MAGIC_OUT_PARAMETER_NR_ATTRIBUTE = "nr";
178     public static final String JavaDoc MAGIC_OUT_PARAMETER_TYPE_ATTRIBUTE = "type";
179     public static final String JavaDoc MAGIC_ESCAPE_STRING = "escape-string";
180     public static final String JavaDoc MAGIC_ERROR = "error";
181
182     public static final String JavaDoc MAGIC_NS_URI_ELEMENT = "namespace-uri";
183     public static final String JavaDoc MAGIC_NS_PREFIX_ELEMENT = "namespace-prefix";
184
185     public static final String JavaDoc MAGIC_ANCESTOR_VALUE = "ancestor-value";
186     public static final String JavaDoc MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE = "level";
187     public static final String JavaDoc MAGIC_ANCESTOR_VALUE_NAME_ATTRIBUTE = "name";
188     public static final String JavaDoc MAGIC_SUBSTITUTE_VALUE = "substitute-value";
189     public static final String JavaDoc MAGIC_SUBSTITUTE_VALUE_NAME_ATTRIBUTE = "name";
190     public static final String JavaDoc MAGIC_NAME_ATTRIBUTE = "name";
191     public static final String JavaDoc MAGIC_STORED_PROCEDURE_ATTRIBUTE = "isstoredprocedure";
192     public static final String JavaDoc MAGIC_UPDATE_ATTRIBUTE = "isupdate";
193     public static final String JavaDoc CLOB_ENCODING = "clob-encoding";
194
195     // The states we are allowed to be in
196
protected static final int STATE_OUTSIDE = 0;
197     protected static final int STATE_INSIDE_EXECUTE_QUERY_ELEMENT = 1;
198     protected static final int STATE_INSIDE_VALUE_ELEMENT = 2;
199     protected static final int STATE_INSIDE_QUERY_ELEMENT = 3;
200     protected static final int STATE_INSIDE_ANCESTOR_VALUE_ELEMENT = 4;
201     protected static final int STATE_INSIDE_SUBSTITUTE_VALUE_ELEMENT = 5;
202     protected static final int STATE_INSIDE_IN_PARAMETER_ELEMENT = 6;
203     protected static final int STATE_INSIDE_OUT_PARAMETER_ELEMENT = 7;
204     protected static final int STATE_INSIDE_ESCAPE_STRING = 8;
205
206     //
207
// Configuration
208
//
209

210     /** Is the old-driver turned on? (default is off) */
211     protected boolean oldDriver;
212
213     /** How many connection attempts to do? (default is 5 times) */
214     protected int connectAttempts;
215
216     /** How long wait between connection attempts? (default is 5000 ms) */
217     protected int connectWaittime;
218
219     //
220
// State
221
//
222

223     /** The current query we are working on */
224     protected Query query;
225
226     /** The current state of the event receiving FSM */
227     protected int state;
228
229     /** The datasource component selector */
230     protected ServiceSelector datasources;
231
232     /** The "name" of the connection shared by top level queries (if configuration allows) */
233     protected String JavaDoc connName;
234
235     /** The connection shared by top level queries (if configuration allows) */
236     protected Connection JavaDoc conn;
237
238     // Used to parse XML from database.
239
protected XMLSerializer compiler;
240     protected XMLDeserializer interpreter;
241     protected SAXParser parser;
242
243     /**
244      * Constructor
245      */

246     public SQLTransformer() {
247         super.defaultNamespaceURI = NAMESPACE;
248     }
249
250     //
251
// Lifecycle Methods
252
//
253

254     /**
255      * Serviceable
256      */

257     public void service(ServiceManager manager) throws ServiceException {
258         super.service(manager);
259         try {
260             this.datasources = (ServiceSelector) manager.lookup(DataSourceComponent.ROLE + "Selector");
261         } catch (ServiceException e) {
262             getLogger().warn("DataSource component selector is not available.", e);
263         }
264     }
265
266     /**
267      * Configure transformer. Supported configuration elements:
268      * <ul>
269      * <li>old-driver</li>
270      * <li>connect-attempts</li>
271      * <li>connect-waittime</li>
272      * </ul>
273      */

274     public void configure(Configuration conf) throws ConfigurationException {
275         super.configure(conf);
276
277         this.oldDriver = conf.getChild("old-driver").getValueAsBoolean(false);
278         if (getLogger().isDebugEnabled()) {
279             getLogger().debug("Value for old-driver is " + this.oldDriver);
280         }
281
282         this.connectAttempts = conf.getChild("connect-attempts").getValueAsInteger(5);
283         this.connectWaittime = conf.getChild("connect-waittime").getValueAsInteger(5000);
284     }
285
286     /**
287      * Setup for the current request.
288      */

289     public void setup(SourceResolver resolver, Map JavaDoc objectModel,
290                       String JavaDoc source, Parameters parameters)
291     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
292         super.setup(resolver, objectModel, source, parameters);
293
294         // Setup instance variables
295
this.state = SQLTransformer.STATE_OUTSIDE;
296         this.connName = name(super.parameters);
297     }
298
299     /**
300      * Recycle this component
301      */

302     public void recycle() {
303         this.query = null;
304         try {
305             // Close the connection used by all top level queries
306
if (this.conn != null) {
307                 this.conn.close();
308                 this.conn = null;
309             }
310         } catch (SQLException JavaDoc e) {
311             getLogger().info("Could not close connection", e);
312         }
313         this.connName = null;
314
315         this.manager.release(this.parser);
316         this.parser = null;
317         this.manager.release(this.compiler);
318         this.compiler = null;
319         this.manager.release(this.interpreter);
320         this.interpreter = null;
321
322         super.recycle();
323     }
324
325     /**
326      * Dispose
327      */

328     public void dispose() {
329         if (this.datasources != null) {
330             this.manager.release(this.datasources);
331             this.datasources = null;
332         }
333         super.dispose();
334     }
335
336     /**
337      * Return attribute value.
338      * First try non-namespaced attribute, then try this transformer namespace.
339      * @param name local attribute name
340      */

341     private String JavaDoc getAttributeValue(Attributes JavaDoc attr, String JavaDoc name) {
342         String JavaDoc value = attr.getValue("", name);
343         if (value == null) {
344             value = attr.getValue(this.namespaceURI, name);
345         }
346
347         return value;
348     }
349
350     //
351
// SAX Events Handlers
352
//
353

354     protected static void throwIllegalStateException(String JavaDoc message) {
355         throw new IllegalStateException JavaDoc("Illegal state: " + message);
356     }
357
358     /** &lt;execute-query&gt; */
359     protected void startExecuteQueryElement() {
360         switch (state) {
361             case SQLTransformer.STATE_OUTSIDE:
362             case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
363                 // Create root query (if query == null), or child query
364
this.query = new Query(this.query);
365                 this.query.enableLogging(getLogger().getChildLogger("query"));
366                 state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
367                 break;
368
369             default:
370                 throwIllegalStateException("Not expecting a start execute query element");
371         }
372     }
373
374     /** &lt;*&gt; */
375     protected void startValueElement(String JavaDoc name)
376     throws SAXException JavaDoc {
377         switch (state) {
378             case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
379                 this.stack.push(name);
380                 startTextRecording();
381                 state = SQLTransformer.STATE_INSIDE_VALUE_ELEMENT;
382                 break;
383
384             default:
385                 throwIllegalStateException("Not expecting a start value element: " + name);
386         }
387     }
388
389     /** &lt;query&gt; */
390     protected void startQueryElement(Attributes JavaDoc attributes)
391     throws SAXException JavaDoc {
392         switch (state) {
393             case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
394                 startTextRecording();
395                 state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
396
397                 String JavaDoc isUpdate = attributes.getValue("", SQLTransformer.MAGIC_UPDATE_ATTRIBUTE);
398                 if (isUpdate != null && !isUpdate.equalsIgnoreCase("false")) {
399                     query.setUpdate(true);
400                 }
401
402                 String JavaDoc isProcedure = attributes.getValue("", SQLTransformer.MAGIC_STORED_PROCEDURE_ATTRIBUTE);
403                 if (isProcedure != null && !isProcedure.equalsIgnoreCase("false")) {
404                     query.setStoredProcedure(true);
405                 }
406
407                 String JavaDoc name = attributes.getValue("", SQLTransformer.MAGIC_NAME_ATTRIBUTE);
408                 if (name != null) {
409                     query.setName(name);
410                 }
411                 break;
412
413             default:
414                 throwIllegalStateException("Not expecting a start query element");
415         }
416     }
417
418     /** &lt;/query&gt; */
419     protected void endQueryElement()
420     throws ProcessingException, SAXException JavaDoc {
421         switch (state) {
422             case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
423                 final String JavaDoc value = endTextRecording();
424                 if (value.length() > 0) {
425                     query.addQueryPart(value);
426                 }
427                 state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
428                 break;
429
430             default:
431                 throwIllegalStateException("Not expecting a stop query element");
432         }
433     }
434
435     /** &lt;/*&gt; */
436     protected void endValueElement()
437     throws SAXException JavaDoc {
438         switch (state) {
439             case SQLTransformer.STATE_INSIDE_VALUE_ELEMENT:
440                 final String JavaDoc name = (String JavaDoc) this.stack.pop();
441                 final String JavaDoc value = endTextRecording();
442                 query.setParameter(name, value);
443                 this.state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
444                 break;
445
446             default:
447                 throwIllegalStateException("Not expecting an end value element");
448         }
449     }
450
451     /** &lt;/execute-query&gt; */
452     protected void endExecuteQueryElement() throws SAXException JavaDoc {
453         switch (state) {
454             case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
455                 if (query.parent == null) {
456                     query.executeQuery();
457                     query = null;
458                     state = SQLTransformer.STATE_OUTSIDE;
459                 } else {
460                     query.parent.addNestedQuery(query);
461                     query = query.parent;
462                     state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
463                 }
464                 break;
465
466             default:
467                 throwIllegalStateException("Not expecting an end execute query element");
468         }
469     }
470
471     /** &lt;ancestor-value&gt; */
472     protected void startAncestorValueElement(Attributes JavaDoc attributes)
473     throws ProcessingException, SAXException JavaDoc {
474         switch (state) {
475             case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
476                 int level = 0;
477                 try {
478                     level = Integer.parseInt(getAttributeValue(attributes, SQLTransformer.MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE));
479                 } catch (Exception JavaDoc e) {
480                     getLogger().debug("Invalid or missing value for " + SQLTransformer.MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE + " attribute", e);
481                     throwIllegalStateException("Ancestor value elements must have a " +
482                                                SQLTransformer.MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE + " attribute");
483                 }
484
485                 String JavaDoc name = getAttributeValue(attributes, SQLTransformer.MAGIC_ANCESTOR_VALUE_NAME_ATTRIBUTE);
486                 if (name == null) {
487                     throwIllegalStateException("Ancestor value elements must have a " +
488                                                SQLTransformer.MAGIC_ANCESTOR_VALUE_NAME_ATTRIBUTE + " attribute");
489                 }
490
491                 final String JavaDoc value = endTextRecording();
492                 if (value.length() > 0) {
493                     query.addQueryPart(value);
494                 }
495                 query.addQueryPart(new AncestorValue(level, name));
496                 startTextRecording();
497
498                 state = SQLTransformer.STATE_INSIDE_ANCESTOR_VALUE_ELEMENT;
499                 break;
500             default:
501                 throwIllegalStateException("Not expecting a start ancestor value element");
502         }
503     }
504
505     /** &lt;/ancestor-value&gt; */
506     protected void endAncestorValueElement() {
507         state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
508     }
509
510     /** &lt;substitute-value&gt; */
511     protected void startSubstituteValueElement(Attributes JavaDoc attributes)
512     throws ProcessingException, SAXException JavaDoc {
513         switch (state) {
514             case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
515                 String JavaDoc name = getAttributeValue(attributes, SQLTransformer.MAGIC_SUBSTITUTE_VALUE_NAME_ATTRIBUTE);
516                 if (name == null) {
517                     throwIllegalStateException("Substitute value elements must have a " +
518                                                SQLTransformer.MAGIC_SUBSTITUTE_VALUE_NAME_ATTRIBUTE + " attribute");
519                 }
520                 String JavaDoc substitute = parameters.getParameter(name, null);
521                 // Escape single quote
522
substitute = StringEscapeUtils.escapeSql(substitute);
523
524                 final String JavaDoc value = endTextRecording();
525                 if (value.length() > 0) {
526                     query.addQueryPart(value);
527                 }
528                 query.addQueryPart(substitute);
529                 startTextRecording();
530
531                 state = SQLTransformer.STATE_INSIDE_SUBSTITUTE_VALUE_ELEMENT;
532                 break;
533
534             default:
535                 throwIllegalStateException("Not expecting a start substitute value element");
536         }
537     }
538
539     /** &lt;/substitute-value&gt; */
540     protected void endSubstituteValueElement() {
541         state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
542     }
543
544     /** &lt;escape-string&gt; */
545     protected void startEscapeStringElement(Attributes JavaDoc attributes)
546     throws ProcessingException, SAXException JavaDoc {
547         switch (state) {
548             case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
549                 final String JavaDoc value = endTextRecording();
550                 if (value.length() > 0) {
551                     query.addQueryPart(value);
552                 }
553                 startTextRecording();
554
555                 state = SQLTransformer.STATE_INSIDE_ESCAPE_STRING;
556                 break;
557
558             default:
559                 throwIllegalStateException("Not expecting a start escape-string element");
560         }
561     }
562
563     /** &lt;/escape-string&gt; */
564     protected void endEscapeStringElement()
565     throws SAXException JavaDoc {
566         switch (state) {
567             case SQLTransformer.STATE_INSIDE_ESCAPE_STRING:
568                 String JavaDoc value = endTextRecording();
569                 if (value.length() > 0) {
570                     value = StringEscapeUtils.escapeSql(value);
571                     value = StringUtils.replace(value, "\\", "\\\\");
572                     query.addQueryPart(value);
573                 }
574                 startTextRecording();
575                 state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
576                 break;
577
578             default:
579                 throwIllegalStateException("Not expecting a end escape-string element");
580         }
581     }
582
583     /** &lt;in-parameter&gt; */
584     protected void startInParameterElement(Attributes JavaDoc attributes) {
585         switch (state) {
586             case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
587                 String JavaDoc nr = getAttributeValue(attributes, SQLTransformer.MAGIC_IN_PARAMETER_NR_ATTRIBUTE);
588                 String JavaDoc value = getAttributeValue(attributes, SQLTransformer.MAGIC_IN_PARAMETER_VALUE_ATTRIBUTE);
589                 if (getLogger().isDebugEnabled()) {
590                     getLogger().debug("IN PARAMETER NR " + nr + "; VALUE " + value);
591                 }
592
593                 int position = Integer.parseInt(nr);
594                 query.setInParameter(position, value);
595                 state = SQLTransformer.STATE_INSIDE_IN_PARAMETER_ELEMENT;
596                 break;
597
598             default:
599                 throwIllegalStateException("Not expecting an in-parameter element");
600         }
601     }
602
603     /** &lt;/in-parameter&gt; */
604     protected void endInParameterElement() {
605         state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
606     }
607
608     /** &lt;out-parameter&gt; */
609     protected void startOutParameterElement(Attributes JavaDoc attributes) {
610         switch (state) {
611             case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
612                 String JavaDoc name = getAttributeValue(attributes, SQLTransformer.MAGIC_OUT_PARAMETER_NAME_ATTRIBUTE);
613                 String JavaDoc nr = getAttributeValue(attributes, SQLTransformer.MAGIC_OUT_PARAMETER_NR_ATTRIBUTE);
614                 String JavaDoc type = getAttributeValue(attributes, SQLTransformer.MAGIC_OUT_PARAMETER_TYPE_ATTRIBUTE);
615                 if (getLogger().isDebugEnabled()) {
616                     getLogger().debug("OUT PARAMETER NAME" + name + ";NR " + nr + "; TYPE " + type);
617                 }
618
619                 int position = Integer.parseInt(nr);
620                 query.setOutParameter(position, type, name);
621                 state = SQLTransformer.STATE_INSIDE_OUT_PARAMETER_ELEMENT;
622                 break;
623
624             default:
625                 throwIllegalStateException("Not expecting an out-parameter element");
626         }
627     }
628
629     /** &lt;/out-parameter&gt; */
630     protected void endOutParameterElement() {
631         state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
632     }
633
634     /**
635      * ContentHandler method
636      */

637     public void startTransformingElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attributes)
638     throws ProcessingException, SAXException JavaDoc {
639         if (name.equals(SQLTransformer.MAGIC_EXECUTE_QUERY)) {
640             startExecuteQueryElement();
641         } else if (name.equals(SQLTransformer.MAGIC_QUERY)) {
642             startQueryElement(attributes);
643         } else if (name.equals(SQLTransformer.MAGIC_ANCESTOR_VALUE)) {
644             startAncestorValueElement(attributes);
645         } else if (name.equals(SQLTransformer.MAGIC_SUBSTITUTE_VALUE)) {
646             startSubstituteValueElement(attributes);
647         } else if (name.equals(SQLTransformer.MAGIC_IN_PARAMETER)) {
648             startInParameterElement(attributes);
649         } else if (name.equals(SQLTransformer.MAGIC_OUT_PARAMETER)) {
650             startOutParameterElement(attributes);
651         } else if (name.equals(SQLTransformer.MAGIC_ESCAPE_STRING)) {
652             startEscapeStringElement(attributes);
653         } else {
654             startValueElement(name);
655         }
656     }
657
658     /**
659      * ContentHandler method
660      */

661     public void endTransformingElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw)
662     throws ProcessingException, IOException JavaDoc, SAXException JavaDoc {
663         if (name.equals(SQLTransformer.MAGIC_EXECUTE_QUERY)) {
664             endExecuteQueryElement();
665         } else if (name.equals(SQLTransformer.MAGIC_QUERY)) {
666             endQueryElement();
667         } else if (name.equals(SQLTransformer.MAGIC_ANCESTOR_VALUE)) {
668             endAncestorValueElement();
669         } else if (name.equals(SQLTransformer.MAGIC_SUBSTITUTE_VALUE)) {
670             endSubstituteValueElement();
671         } else if (name.equals(SQLTransformer.MAGIC_IN_PARAMETER)) {
672             endInParameterElement();
673         } else if (name.equals(SQLTransformer.MAGIC_OUT_PARAMETER)) {
674             endOutParameterElement();
675         } else if (name.equals(SQLTransformer.MAGIC_ESCAPE_STRING)) {
676             endEscapeStringElement();
677         } else {
678             endValueElement();
679         }
680     }
681
682     //
683
// Helper methods for the Query
684
//
685

686     /**
687      * Qualifies an element name by giving it a prefix.
688      * @param name the element name
689      * @param prefix the prefix to qualify with
690      * @return a namespace qualified name that is correct
691      */

692     protected String JavaDoc nsQualify(String JavaDoc name, String JavaDoc prefix) {
693         if (StringUtils.isEmpty(name)) {
694             return name;
695         }
696
697         if (StringUtils.isNotEmpty(prefix)) {
698             return prefix + ":" + name;
699         }
700
701         return name;
702     }
703
704     /**
705      * Helper method for generating SAX events
706      */

707     protected void start(String JavaDoc uri, String JavaDoc prefix, String JavaDoc name, Attributes JavaDoc attr)
708     throws SAXException JavaDoc {
709         try {
710             super.startTransformingElement(uri, name, nsQualify(name, prefix), attr);
711         } catch (IOException JavaDoc e) {
712             throw new SAXException JavaDoc(e);
713         } catch (ProcessingException e) {
714             throw new SAXException JavaDoc(e);
715         }
716     }
717
718     /**
719      * Helper method for generating SAX events
720      */

721     protected void end(String JavaDoc uri, String JavaDoc prefix, String JavaDoc name) throws SAXException JavaDoc {
722         try {
723             super.endTransformingElement(uri, name, nsQualify(name, prefix));
724         } catch (IOException JavaDoc e) {
725             throw new SAXException JavaDoc(e);
726         } catch (ProcessingException e) {
727             throw new SAXException JavaDoc(e);
728         }
729     }
730
731     /**
732      * Helper method for generating SAX events
733      */

734     protected void data(String JavaDoc data) throws SAXException JavaDoc {
735         if (data != null) {
736             super.characters(data.toCharArray(), 0, data.length());
737         }
738     }
739
740     /**
741      * Get 'name' for the connection which can be obtained using provided
742      * connection parameters.
743      */

744     private String JavaDoc name(Parameters params) {
745         final boolean ownConnection = params.getParameterAsBoolean(SQLTransformer.MAGIC_OWN_CONNECTION, false);
746         if (ownConnection) {
747             return null;
748         }
749
750         final String JavaDoc datasourceName = params.getParameter(SQLTransformer.MAGIC_CONNECTION, null);
751         if (datasourceName != null) {
752             return "ds:" + datasourceName;
753         }
754
755         final String JavaDoc dburl = params.getParameter(SQLTransformer.MAGIC_DBURL, null);
756         if (dburl != null) {
757             final String JavaDoc username = params.getParameter(SQLTransformer.MAGIC_USERNAME, null);
758             final String JavaDoc password = params.getParameter(SQLTransformer.MAGIC_PASSWORD, null);
759
760             if (username == null || password == null) {
761                 return "db:@" + dburl;
762             } else {
763                 return "db:" + username + ":" + password + "@" + dburl;
764             }
765         }
766
767         // Nothing configured
768
return "";
769     }
770
771     /**
772      * Open database connection using provided parameters.
773      * Return null if neither datasource nor jndi URL configured.
774      */

775     private Connection JavaDoc open(Parameters params) throws SQLException JavaDoc {
776         Connection JavaDoc result = null;
777
778         // First check datasource name parameter
779
final String JavaDoc datasourceName = params.getParameter(SQLTransformer.MAGIC_CONNECTION, null);
780         if (datasourceName != null) {
781             // Use datasource components
782
if (this.datasources == null) {
783                 throw new SQLException JavaDoc("Unable to get connection from datasource '" + datasourceName + "': " +
784                                        "No datasources configured in cocoon.xconf.");
785             }
786
787             DataSourceComponent datasource = null;
788             try {
789                 datasource = (DataSourceComponent) this.datasources.select(datasourceName);
790                 for (int i = 0; i < this.connectAttempts && result == null; i++) {
791                     try {
792                         result = datasource.getConnection();
793                     } catch (SQLException JavaDoc e) {
794                         if (i + 1 < this.connectAttempts) {
795                             final long waittime = this.connectWaittime;
796                             // Log exception if debug enabled.
797
if (getLogger().isDebugEnabled()) {
798                                 getLogger().info("Unable to get connection; waiting " +
799                                                   waittime + "ms to try again.", e);
800                             } else {
801                                 getLogger().info("Unable to get connection; waiting " +
802                                                   waittime + "ms to try again.");
803                             }
804                             try {
805                                 Thread.sleep(waittime);
806                             } catch (InterruptedException JavaDoc ex) {
807                                 /* ignored */
808                             }
809                         }
810                     }
811                 }
812             } catch (ServiceException e) {
813                 throw new SQLException JavaDoc("Unable to get connection from datasource '" + datasourceName + "': " +
814                                        "No such datasource.");
815             } finally {
816                 if (datasource != null) {
817                     this.datasources.release(datasource);
818                 }
819             }
820
821             if (result == null) {
822                 throw new SQLException JavaDoc("Failed to obtain connection from datasource '" + datasourceName + "'. " +
823                                        "Made " + this.connectAttempts + " attempts with "
824                                        + this.connectWaittime + "ms interval");
825             }
826         } else {
827             // Then, check connection URL parameter
828
final String JavaDoc dburl = params.getParameter(SQLTransformer.MAGIC_DBURL, null);
829             if (dburl != null) {
830                 final String JavaDoc username = params.getParameter(SQLTransformer.MAGIC_USERNAME, null);
831                 final String JavaDoc password = params.getParameter(SQLTransformer.MAGIC_PASSWORD, null);
832
833                 if (username == null || password == null) {
834                     result = DriverManager.getConnection(dburl);
835                 } else {
836                     result = DriverManager.getConnection(dburl, username, password);
837                 }
838             } else {
839                 // Nothing configured
840
}
841         }
842
843         return result;
844     }
845
846     /**
847      * Attempt to parse string value
848      */

849     private void stream(String JavaDoc value) throws ServiceException, SAXException JavaDoc, IOException JavaDoc {
850         try {
851             // Strip off the XML Declaration if there is one!
852
if (value.startsWith("<?xml ")) {
853                 value = value.substring(value.indexOf("?>") + 2);
854             }
855
856             // Lookup components
857
if (this.parser == null) {
858                 this.parser = (SAXParser) manager.lookup(SAXParser.ROLE);
859             }
860             if (this.compiler == null) {
861                 this.compiler = (XMLSerializer) manager.lookup(XMLSerializer.ROLE);
862             }
863             if (this.interpreter == null) {
864                 this.interpreter = (XMLDeserializer) manager.lookup(XMLDeserializer.ROLE);
865             }
866
867             this.parser.parse(new InputSource JavaDoc(new StringReader JavaDoc("<root>" + value + "</root>")),
868                               this.compiler);
869
870             IncludeXMLConsumer filter = new IncludeXMLConsumer(this, this);
871             filter.setIgnoreRootElement(true);
872
873             this.interpreter.setConsumer(filter);
874             this.interpreter.deserialize(this.compiler.getSAXFragment());
875         } finally {
876             // otherwise serializer won't be reset
877
if (this.compiler != null) {
878                 manager.release(this.compiler);
879                 this.compiler = null;
880             }
881         }
882     }
883
884     /**
885      * One of the queries in the query tree formed from nested queries.
886      */

887     private class Query extends AbstractLogEnabled {
888
889         /** Parent query, or null for top level query */
890         protected Query parent;
891
892         /** Nested sub-queries we have. */
893         protected final List JavaDoc nested = new ArrayList JavaDoc();
894
895         /** The parts of the query */
896         protected final List JavaDoc parts = new ArrayList JavaDoc();
897
898         //
899
// Query Configuration
900
//
901

902         /** Name of the query */
903         protected String JavaDoc name;
904
905         /** If this query is actually an update (insert, update, delete) */
906         protected boolean isUpdate;
907
908         /** If this query is actually a stored procedure */
909         protected boolean isStoredProcedure;
910
911         /** Query configuration parameters */
912         protected Parameters params;
913
914         /** The namespace uri of the XML output. Defaults to {@link SQLTransformer#namespaceURI}. */
915         protected String JavaDoc outUri;
916
917         /** The namespace prefix of the XML output. Defaults to 'sql'. */
918         protected String JavaDoc outPrefix;
919
920         /** rowset element name */
921         protected String JavaDoc rowsetElement;
922
923         /** row element name */
924         protected String JavaDoc rowElement;
925
926         /** number of rows attribute name */
927         protected String JavaDoc nrOfRowsAttr = "nrofrows";
928
929         /** Query name attribute name */
930         protected String JavaDoc nameAttr = "name";
931
932         /** Handling of case of column names in results */
933         protected int columnCase;
934
935         /** Registered IN parameters */
936         protected HashMap JavaDoc inParameters;
937
938         /** Registered OUT parameters */
939         protected HashMap JavaDoc outParameters;
940
941         /** Mapping out parameters - objectModel */
942         protected HashMap JavaDoc outParametersNames;
943
944         /** Check if nr of rows need to be written out. */
945         protected boolean showNrOfRows;
946
947         /** Encoding we use for CLOB field */
948         protected String JavaDoc clobEncoding;
949
950         //
951
// Query State
952
//
953

954         /** The connection */
955         protected Connection JavaDoc conn;
956
957         /** The 'name' of the connection */
958         protected String JavaDoc connName;
959
960         /** Is it our own connection? */
961         protected boolean ownConn;
962
963         /** Prepared statement */
964         protected PreparedStatement JavaDoc pst;
965
966         /** Callable statement */
967         protected CallableStatement JavaDoc cst;
968
969         /** The results, of course */
970         protected ResultSet JavaDoc rs;
971
972         /** And the results' metadata */
973         protected ResultSetMetaData JavaDoc md;
974
975         /** If it is an update/etc, the return value (num rows modified) */
976         protected int rv = -1;
977
978
979         protected Query(Query parent) {
980             this.parent = parent;
981             this.params = new Parameters();
982             this.params.merge(SQLTransformer.this.parameters);
983         }
984
985         /** Add nested sub-query. */
986         protected void addNestedQuery(Query query) {
987             nested.add(query);
988         }
989
990         protected void addQueryPart(Object JavaDoc value) {
991             if (getLogger().isDebugEnabled()) {
992                 getLogger().debug("Adding query part \"" + value + "\"");
993             }
994             parts.add(value);
995         }
996
997         protected String JavaDoc getName() {
998             return name;
999         }
1000
1001        protected void setName(String JavaDoc name) {
1002            this.name = name;
1003        }
1004
1005        protected void setParameter(String JavaDoc name, String JavaDoc value) {
1006            if (getLogger().isDebugEnabled()) {
1007                getLogger().debug("Adding parameter name {" + name + "} value {" + value + "}");
1008            }
1009            params.setParameter(name, value);
1010        }
1011
1012        protected void setUpdate(boolean flag) {
1013            isUpdate = flag;
1014        }
1015
1016        protected void setStoredProcedure(boolean flag) {
1017            isStoredProcedure = flag;
1018        }
1019
1020        protected void setInParameter(int pos, String JavaDoc val) {
1021            if (inParameters == null) {
1022                inParameters = new HashMap JavaDoc();
1023            }
1024            inParameters.put(new Integer JavaDoc(pos), val);
1025        }
1026
1027        protected void setOutParameter(int pos, String JavaDoc type, String JavaDoc name) {
1028            if (outParameters == null) {
1029                outParameters = new HashMap JavaDoc();
1030                outParametersNames = new HashMap JavaDoc();
1031            }
1032            outParameters.put(new Integer JavaDoc(pos), type);
1033            outParametersNames.put(new Integer JavaDoc(pos), name);
1034        }
1035
1036        private void setColumnCase(String JavaDoc columnCase) {
1037            if (columnCase.equals("lowercase")) {
1038                this.columnCase = -1;
1039            } else if (columnCase.equals("uppercase")) {
1040                this.columnCase = +1;
1041            } else if (columnCase.equals("preserve")) {
1042                // Do nothing
1043
this.columnCase = 0;
1044            } else {
1045                getLogger().warn("[" + columnCase + "] is not a valid value for <column-case>. " +
1046                                 "Column name retrieved from database will be used.");
1047            }
1048        }
1049
1050        private void registerInParameters() throws SQLException JavaDoc {
1051            if (inParameters == null) {
1052                return;
1053            }
1054
1055            Iterator JavaDoc i = inParameters.keySet().iterator();
1056            while (i.hasNext()) {
1057                Integer JavaDoc counter = (Integer JavaDoc) i.next();
1058                String JavaDoc value = (String JavaDoc) inParameters.get(counter);
1059                try {
1060                    pst.setObject(counter.intValue(), value);
1061                } catch (SQLException JavaDoc e) {
1062                    getLogger().error("Caught a SQLException", e);
1063                    throw e;
1064                }
1065            }
1066        }
1067
1068        private void registerOutParameters(CallableStatement JavaDoc cst) throws SQLException JavaDoc {
1069            if (outParameters == null) {
1070                return;
1071            }
1072
1073            Iterator JavaDoc i = outParameters.keySet().iterator();
1074            while (i.hasNext()) {
1075                Integer JavaDoc counter = (Integer JavaDoc) i.next();
1076                String JavaDoc type = (String JavaDoc) outParameters.get(counter);
1077                int index = type.lastIndexOf(".");
1078
1079                String JavaDoc className, fieldName;
1080                if (index > -1) {
1081                    className = type.substring(0, index);
1082                    fieldName = type.substring(index + 1, type.length());
1083                } else {
1084                    getLogger().error("Invalid SQLType: " + type, null);
1085                    throw new SQLException JavaDoc("Invalid SQLType: " + type);
1086                }
1087                try {
1088                    Class JavaDoc clss = Class.forName(className);
1089                    Field JavaDoc fld = clss.getField(fieldName);
1090                    cst.registerOutParameter(counter.intValue(), fld.getInt(fieldName));
1091                } catch (Exception JavaDoc e) {
1092                    // Lots of different exceptions to catch
1093
getLogger().error("Invalid SQLType: " + className + "." + fieldName, e);
1094                }
1095            }
1096        }
1097
1098        /**
1099         * Open database connection
1100         */

1101        private void open() throws SQLException JavaDoc {
1102            this.connName = SQLTransformer.this.name(this.params);
1103
1104            // Check first if connection sharing disabled
1105
if (this.connName == null) {
1106                this.conn = SQLTransformer.this.open(this.params);
1107                this.ownConn = true;
1108                return;
1109            }
1110
1111            // Iterate through parent queries and get appropriate connection
1112
Query query = this.parent;
1113            while (query != null) {
1114                if (this.connName.equals(query.connName)) {
1115                    this.conn = query.conn;
1116                    this.ownConn = false;
1117                    return;
1118                }
1119                query = query.parent;
1120            }
1121
1122            // Check 'global' connection
1123
if (this.connName.equals(SQLTransformer.this.connName)) {
1124                // Use SQLTransformer configuration: it has same connection parameters
1125
if (SQLTransformer.this.conn == null) {
1126                    SQLTransformer.this.conn = SQLTransformer.this.open(SQLTransformer.this.parameters);
1127                }
1128
1129                this.conn = SQLTransformer.this.conn;
1130                this.ownConn = false;
1131                return;
1132            }
1133
1134            // Create own connection
1135
this.conn = SQLTransformer.this.open(this.params);
1136            this.ownConn = true;
1137        }
1138
1139        /**
1140         * This will be the meat of SQLTransformer, where the query is run.
1141         */

1142        protected void executeQuery() throws SAXException JavaDoc {
1143            if (getLogger().isDebugEnabled()) {
1144                getLogger().debug("Executing query " + this);
1145            }
1146
1147            this.outUri = this.params.getParameter(SQLTransformer.MAGIC_NS_URI_ELEMENT, SQLTransformer.this.namespaceURI);
1148            this.outPrefix = this.params.getParameter(SQLTransformer.MAGIC_NS_PREFIX_ELEMENT, "sql");
1149
1150            this.showNrOfRows = parameters.getParameterAsBoolean(SQLTransformer.MAGIC_NR_OF_ROWS, false);
1151            this.clobEncoding = parameters.getParameter(SQLTransformer.CLOB_ENCODING, "");
1152
1153            // Start prefix mapping for output namespace, only if it's not mapped yet
1154
final String JavaDoc prefix = SQLTransformer.this.findPrefixMapping(this.outUri);
1155            if (prefix == null) {
1156                SQLTransformer.this.startPrefixMapping(this.outPrefix, this.outUri);
1157            } else {
1158                this.outPrefix = prefix;
1159            }
1160
1161            boolean success = false;
1162            try {
1163                try {
1164                    open();
1165                    execute();
1166                    success = true;
1167                } catch (SQLException JavaDoc e) {
1168                    getLogger().info("Failed to execute query " + this, e);
1169                    start(this.rowsetElement, EMPTY_ATTRIBUTES);
1170                    start(MAGIC_ERROR, EMPTY_ATTRIBUTES);
1171                    data(e.getMessage());
1172                    end(MAGIC_ERROR);
1173                    end(this.rowsetElement);
1174                }
1175
1176                if (success) {
1177                    AttributesImpl JavaDoc attr = new AttributesImpl JavaDoc();
1178                    if (showNrOfRows) {
1179                        attr.addAttribute("", this.nrOfRowsAttr, this.nrOfRowsAttr, "CDATA", String.valueOf(getNrOfRows()));
1180                    }
1181                    String JavaDoc name = getName();
1182                    if (name != null) {
1183                        attr.addAttribute("", this.nameAttr, this.nameAttr, "CDATA", name);
1184                    }
1185                    start(this.rowsetElement, attr);
1186
1187                    // Serialize stored procedure output parameters
1188
if (isStoredProcedure) {
1189                        serializeStoredProcedure();
1190                    }
1191
1192                    // Serialize result set
1193
while (next()) {
1194                        start(this.rowElement, EMPTY_ATTRIBUTES);
1195                        serializeRow();
1196                        for (Iterator JavaDoc i = this.nested.iterator(); i.hasNext();) {
1197                            ((Query) i.next()).executeQuery();
1198                        }
1199                        end(this.rowElement);
1200                    }
1201
1202                    end(this.rowsetElement);
1203                }
1204            } catch (SQLException JavaDoc e) {
1205                getLogger().debug("Exception in executeQuery()", e);
1206                throw new SAXException JavaDoc(e);
1207            } finally {
1208                close();
1209            }
1210
1211            if (prefix == null) {
1212                SQLTransformer.this.endPrefixMapping(this.outPrefix);
1213            }
1214        }
1215
1216        /**
1217         * Execute the query. Connection must be set already.
1218         */

1219        private void execute() throws SQLException JavaDoc {
1220            this.rowsetElement = params.getParameter(SQLTransformer.MAGIC_DOC_ELEMENT, "rowset");
1221            this.rowElement = params.getParameter(SQLTransformer.MAGIC_ROW_ELEMENT, "row");
1222            setColumnCase(params.getParameter(SQLTransformer.MAGIC_COLUMN_CASE, "lowercase"));
1223
1224            // Construct query string
1225
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1226            for (Iterator JavaDoc i = parts.iterator(); i.hasNext();) {
1227                Object JavaDoc object = i.next();
1228                if (object instanceof String JavaDoc) {
1229                    sb.append((String JavaDoc) object);
1230                } else if (object instanceof AncestorValue) {
1231                    // Do a lookup into the ancestors' result's values
1232
AncestorValue av = (AncestorValue) object;
1233                    Query query = this;
1234                    for (int k = av.level; k > 0; k--) {
1235                        query = query.parent;
1236                    }
1237                    sb.append(query.getColumnValue(av.name));
1238                }
1239            }
1240
1241            String JavaDoc query = StringUtils.replace(sb.toString().trim(), "\r", " ", -1);
1242            // Test, if this is an update (by comparing with select)
1243
if (!isStoredProcedure && !isUpdate) {
1244                if (query.length() > 6 && !query.substring(0, 6).equalsIgnoreCase("SELECT")) {
1245                    isUpdate = true;
1246                }
1247            }
1248
1249            if (getLogger().isDebugEnabled()) {
1250                getLogger().debug("Executing " + query);
1251            }
1252            if (!isStoredProcedure) {
1253                if (oldDriver) {
1254                    pst = conn.prepareStatement(query);
1255                } else {
1256                    pst = conn.prepareStatement(query,
1257                                                ResultSet.TYPE_SCROLL_INSENSITIVE,
1258                                                ResultSet.CONCUR_READ_ONLY);
1259                }
1260            } else {
1261                if (oldDriver) {
1262                    cst = conn.prepareCall(query);
1263                } else {
1264                    cst = conn.prepareCall(query,
1265                                           ResultSet.TYPE_SCROLL_INSENSITIVE,
1266                                           ResultSet.CONCUR_READ_ONLY);
1267                }
1268                registerOutParameters(cst);
1269                pst = cst;
1270            }
1271
1272            registerInParameters();
1273            boolean result = pst.execute();
1274            if (result) {
1275                rs = pst.getResultSet();
1276                md = rs.getMetaData();
1277            } else {
1278                rv = pst.getUpdateCount();
1279            }
1280        }
1281
1282        protected int getNrOfRows() throws SQLException JavaDoc {
1283            int nr = 0;
1284
1285            if (rs != null) {
1286                if (oldDriver) {
1287                    nr = -1;
1288                } else {
1289                    try {
1290                        rs.last();
1291                        nr = rs.getRow();
1292                        rs.beforeFirst();
1293                    } catch (NullPointerException JavaDoc e) {
1294                        // A NullPointerException here crashes a whole lot of C2 --
1295
// catching it so it won't do any harm for now, but seems like it should be solved seriously
1296
getLogger().error("NPE while getting the nr of rows", e);
1297                    }
1298                }
1299            } else {
1300                if (outParameters != null) {
1301                    nr = outParameters.size();
1302                }
1303            }
1304            return nr;
1305        }
1306
1307        protected String JavaDoc getColumnValue(int i) throws SQLException JavaDoc {
1308            int numberOfChar = 1024;
1309            String JavaDoc retval;
1310
1311            if (rs.getMetaData().getColumnType(i) == java.sql.Types.DOUBLE) {
1312                retval = getStringValue(rs.getBigDecimal(i));
1313            } else if (rs.getMetaData().getColumnType(i) == java.sql.Types.CLOB) {
1314                Clob JavaDoc clob = rs.getClob(i);
1315                InputStream JavaDoc inputStream = clob.getAsciiStream();
1316                byte[] readByte = new byte[numberOfChar];
1317                StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1318                try {
1319                    while (inputStream.read(readByte) > -1) {
1320                        String JavaDoc string = new String JavaDoc(readByte, this.clobEncoding);
1321                        buffer.append(string);
1322                    }
1323                } catch (IOException JavaDoc e) {
1324                    throw new SQLException JavaDoc("Error reading stream from CLOB");
1325                }
1326                retval = buffer.toString();
1327            } else {
1328                retval = getStringValue(rs.getObject(i));
1329            }
1330            return retval;
1331        }
1332
1333        // fix not applied here because there is no metadata from Name -> number and coltype
1334
// for a given "name" versus number. That being said this shouldn't be an issue
1335
// as this function is only called for ancestor lookups.
1336
protected String JavaDoc getColumnValue(String JavaDoc name) throws SQLException JavaDoc {
1337            String JavaDoc retval = getStringValue(rs.getObject(name));
1338            // if (rs.getMetaData().getColumnType( name ) == 8)
1339
// retval = transformer.getStringValue( rs.getBigDecimal( name ) );
1340
return retval;
1341        }
1342
1343        protected boolean next() throws SQLException JavaDoc {
1344            // If rv is not -1, then an SQL insert, update, etc, has
1345
// happened (see JDBC docs - return codes for executeUpdate)
1346
if (rv != -1) {
1347                // Output row with return code. Once.
1348
return true;
1349            }
1350
1351            if (rs == null || !rs.next()) {
1352                // No more rows.
1353
return false;
1354            }
1355
1356            return true;
1357        }
1358
1359        /**
1360         * Closes all the resources, ignores (but logs) exceptions.
1361         */

1362        protected void close() {
1363            if (rs != null) {
1364                try {
1365                    rs.close();
1366                } catch (SQLException JavaDoc e) {
1367                    getLogger().info("Unable to close the result set.", e);
1368                }
1369                // This prevents us from using the resultset again.
1370
rs = null;
1371            }
1372
1373            if (pst != null && pst != cst) {
1374                try {
1375                    pst.close();
1376                } catch (SQLException JavaDoc e) {
1377                    getLogger().info("Unable to close the statement.", e);
1378                }
1379            }
1380            // Prevent using pst again.
1381
pst = null;
1382
1383            if (cst != null) {
1384                try {
1385                    cst.close();
1386                } catch (SQLException JavaDoc e) {
1387                    getLogger().info("Unable to close the statement.", e);
1388                }
1389                // Prevent using cst again.
1390
cst = null;
1391            }
1392
1393            try {
1394                if (ownConn && conn != null) {
1395                    conn.close();
1396                }
1397            } catch (SQLException JavaDoc e) {
1398                getLogger().info("Unable to close the connection", e);
1399            }
1400            // Prevent using conn again.
1401
conn = null;
1402        }
1403
1404        protected void serializeData(String JavaDoc value)
1405        throws SQLException JavaDoc, SAXException JavaDoc {
1406            if (value != null) {
1407                value = value.trim();
1408                // Could this be XML ?
1409
if (value.length() > 0 && value.charAt(0) == '<') {
1410                    try {
1411                        stream(value);
1412                    } catch (Exception JavaDoc ignored) {
1413                        // FIXME: bad coding "catch(Exception)"
1414
// If an exception occured the data was not (valid) xml
1415
data(value);
1416                    }
1417                } else {
1418                    data(value);
1419                }
1420            }
1421        }
1422
1423        protected void serializeRow()
1424        throws SQLException JavaDoc, SAXException JavaDoc {
1425            if (rv != -1) {
1426                start("returncode", EMPTY_ATTRIBUTES);
1427                serializeData(String.valueOf(rv));
1428                end("returncode");
1429                // We only want the return code shown once.
1430
// Reset rv so next() returns false next time.
1431
rv = -1;
1432            } else {
1433                for (int i = 1; i <= md.getColumnCount(); i++) {
1434                    String JavaDoc columnName = getColumnName(md.getColumnName(i));
1435                    start(columnName, EMPTY_ATTRIBUTES);
1436                    serializeData(getColumnValue(i));
1437                    end(columnName);
1438                }
1439            }
1440        }
1441
1442        protected void serializeStoredProcedure()
1443        throws SQLException JavaDoc, SAXException JavaDoc {
1444            if (outParametersNames == null || cst == null) {
1445                return;
1446            }
1447
1448            // make sure output follows order as parameter order in stored procedure
1449
Iterator JavaDoc itOutKeys = new TreeMap JavaDoc(outParameters).keySet().iterator();
1450            while (itOutKeys.hasNext()) {
1451                final Integer JavaDoc counter = (Integer JavaDoc) itOutKeys.next();
1452                try {
1453                    final Object JavaDoc obj = cst.getObject(counter.intValue());
1454                    final String JavaDoc name = (String JavaDoc) outParametersNames.get(counter);
1455                    start(name, EMPTY_ATTRIBUTES);
1456
1457                    if (!(obj instanceof ResultSet JavaDoc)) {
1458                        serializeData(getStringValue(obj));
1459                    } else {
1460                        final ResultSet JavaDoc rs = (ResultSet JavaDoc) obj;
1461                        try {
1462                            ResultSetMetaData JavaDoc md = rs.getMetaData();
1463                            while (rs.next()) {
1464                                start(this.rowElement, EMPTY_ATTRIBUTES);
1465                                for (int i = 1; i <= md.getColumnCount(); i++) {
1466                                    String JavaDoc columnName = getColumnName(md.getColumnName(i));
1467                                    start(columnName, EMPTY_ATTRIBUTES);
1468                                    if (md.getColumnType(i) == 8) { // prevent nasty exponent notation
1469
serializeData(getStringValue(rs.getBigDecimal(i)));
1470                                    } else {
1471                                        serializeData(getStringValue(rs.getObject(i)));
1472                                    }
1473                                    end(columnName);
1474                                }
1475                                end(this.rowElement);
1476                            }
1477                        } finally {
1478                            try {
1479                                rs.close();
1480                            } catch (SQLException JavaDoc e) {
1481                                /* ignored */
1482                            }
1483                        }
1484                    }
1485
1486                    end(name);
1487                } catch (SQLException JavaDoc e) {
1488                    getLogger().error("Caught a SQLException", e);
1489                    throw e;
1490                }
1491            }
1492        }
1493
1494        private String JavaDoc getColumnName(String JavaDoc columnName) {
1495            switch (this.columnCase) {
1496                case -1:
1497                    columnName = columnName.toLowerCase();
1498                    break;
1499                case +1:
1500                    columnName = columnName.toUpperCase();
1501                    break;
1502                default:
1503                    // Do nothing
1504
}
1505            return columnName;
1506        }
1507
1508        /**
1509         * Convert object to string represenation
1510         */

1511        private String JavaDoc getStringValue(Object JavaDoc object) {
1512            if (object instanceof byte[]) {
1513                // FIXME Encoding?
1514
return new String JavaDoc((byte[]) object);
1515            } else if (object instanceof char[]) {
1516                return new String JavaDoc((char[]) object);
1517            } else if (object != null) {
1518                return object.toString();
1519            }
1520
1521            return "";
1522        }
1523
1524        private void start(String JavaDoc name, Attributes JavaDoc attr)
1525        throws SAXException JavaDoc {
1526            SQLTransformer.this.start(this.outUri, this.outPrefix, name, attr);
1527        }
1528
1529        private void end(String JavaDoc name) throws SAXException JavaDoc {
1530            SQLTransformer.this.end(this.outUri, this.outPrefix, name);
1531        }
1532
1533        private void data(String JavaDoc data) throws SAXException JavaDoc {
1534            SQLTransformer.this.data(data);
1535        }
1536    }
1537
1538    private static class AncestorValue {
1539        protected int level;
1540        protected String JavaDoc name;
1541
1542        protected AncestorValue(int level, String JavaDoc name) {
1543            this.level = level;
1544            this.name = name;
1545        }
1546
1547        public String JavaDoc toString() {
1548            return "<ancestor level " + level + ", name " + name + ">";
1549        }
1550    }
1551    
1552    
1553    /**
1554     * Stop recording of text and return the recorded information.
1555     * @return The String, trimmed.
1556     *
1557     * NB. SQLTransformer needs to have a special version of this method
1558     * It needs the TextRecorder to not trim whitespace from the queries it is building
1559     *
1560     */

1561    public String JavaDoc endTextRecording()
1562    throws SAXException JavaDoc {
1563        sendEndPrefixMapping();
1564
1565        TextRecorder recorder = (TextRecorder) removeRecorder();
1566        String JavaDoc text = recorder.getAllText();
1567        if (getLogger().isDebugEnabled()) {
1568            getLogger().debug("End text recording. Text=" + text);
1569        }
1570        return text;
1571    }
1572
1573}
1574
Popular Tags