Package org.apache.cocoon.transformation

Source Code of org.apache.cocoon.transformation.SQLTransformer$Query

/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.        *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included  with this distribution in *
* the LICENSE file.                                                         *
*****************************************************************************/
package org.apache.cocoon.transformation;

import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.excalibur.pool.Recyclable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.*;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.util.Tokenizer;
import org.apache.log.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;

/**
*
* @author <a href="mailto:balld@webslingerZ.com">Donald Ball</a>
* @author <a href="mailto:giacomo.pati@pwr.ch">Giacomo Pati</a>
*         (PWR Organisation & Entwicklung)
* @author <a href="mailto:sven.beauprez@the-ecorp.com">Sven Beauprez</a>
* @version CVS $Revision: 1.1 $ $Date: 2002/01/03 12:31:20 $ $Author: giacomo $
*/

public class SQLTransformer extends AbstractTransformer implements Composable, Recyclable, Disposable, Configurable {

    /** The SQL namespace **/
    public static final String my_uri = "http://apache.org/cocoon/SQL/2.0";
    public static final String my_name = "SQLTransformer";

    /** The SQL namespace element names **/
    public static final String MAGIC_EXECUTE_QUERY = "execute-query";
    public static final String MAGIC_CONNECTION = "use-connection";
    public static final String MAGIC_DBURL = "dburl";
    public static final String MAGIC_USERNAME = "username";
    public static final String MAGIC_PASSWORD = "password";
    public static final String MAGIC_NR_OF_ROWS = "show-nr-of-rows";
    public static final String MAGIC_QUERY = "query";
    public static final String MAGIC_VALUE = "value";
    public static final String MAGIC_DOC_ELEMENT = "doc-element";
    public static final String MAGIC_ROW_ELEMENT = "row-element";
    public static final String MAGIC_IN_PARAMETER = "in-parameter";
    public static final String MAGIC_IN_PARAMETER_NR_ATTRIBUTE = "nr";
    public static final String MAGIC_IN_PARAMETER_VALUE_ATTRIBUTE = "value";
    public static final String MAGIC_OUT_PARAMETER = "out-parameter";
    public static final String MAGIC_OUT_PARAMETER_NAME_ATTRIBUTE = "name";
    public static final String MAGIC_OUT_PARAMETER_NR_ATTRIBUTE = "nr";
    public static final String MAGIC_OUT_PARAMETER_TYPE_ATTRIBUTE = "type";
    public static final String MAGIC_ESCAPE_STRING = "escape-string";
    public static final String MAGIC_ERROR = "error";

    public static final String MAGIC_NS_URI_ELEMENT = "namespace-uri";
    public static final String MAGIC_NS_PREFIX_ELEMENT = "namespace-prefix";

    public static final String MAGIC_ANCESTOR_VALUE = "ancestor-value";
    public static final String MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE = "level";
    public static final String MAGIC_ANCESTOR_VALUE_NAME_ATTRIBUTE = "name";
    public static final String MAGIC_SUBSTITUTE_VALUE = "substitute-value";
    public static final String MAGIC_SUBSTITUTE_VALUE_NAME_ATTRIBUTE = "name";
    public static final String MAGIC_NAME_ATTRIBUTE = "name";
    public static final String MAGIC_STORED_PROCEDURE_ATTRIBUTE = "isstoredprocedure";
    public static final String MAGIC_UPDATE_ATTRIBUTE = "isupdate";

    /** The states we are allowed to be in **/
    public static final int STATE_OUTSIDE = 0;
    public static final int STATE_INSIDE_EXECUTE_QUERY_ELEMENT = 1;
    public static final int STATE_INSIDE_VALUE_ELEMENT = 2;
    public static final int STATE_INSIDE_QUERY_ELEMENT = 3;
    public static final int STATE_INSIDE_ANCESTOR_VALUE_ELEMENT = 4;
    public static final int STATE_INSIDE_SUBSTITUTE_VALUE_ELEMENT = 5;
    public static final int STATE_INSIDE_IN_PARAMETER_ELEMENT = 6;
    public static final int STATE_INSIDE_OUT_PARAMETER_ELEMENT = 7;
    public static final int STATE_INSIDE_ESCAPE_STRING = 8;

    /** Default parameters that might apply to all queries **/
    protected Properties default_properties;

    /** The list of queries that we're currently working on **/
    protected Vector queries;

    /** The offset of the current query in the queries list **/
    protected int current_query_index;

    /** The name of the value element we're currently receiving **/
    protected String current_name;

    /** The current state of the event receiving FSM **/
    protected int current_state;

    /** The value of the value element we're currently receiving **/
    protected StringBuffer current_value;

    /** Check if nr of rows need to be written out. **/
    protected String showNrOfRows;

    /** Is the old-driver turned on? (default is off) */
    private boolean oldDriver = false;

    /** Namespace prefix to output */
    protected String outPrefix;

    /** Namespace uri to output */
    protected String outUri;

    /** The prefix of our namespace to listen to */
    protected String inPrefix;

    protected ComponentSelector dbSelector = null;
    protected ComponentManager manager;

    protected Parameters parameters;
    protected Map objectModel;

    public void compose( ComponentManager manager ) {
        try {
            this.manager = manager;
            this.dbSelector = (ComponentSelector) manager.lookup( DataSourceComponent.ROLE + "Selector" );
        } catch ( ComponentException cme ) {
            getLogger().warn( "Could not get the DataSource Selector", cme );
        }
    }

    /**
     * Recycle this component
     */
    public void recycle() {
        super.recycle();
        this.queries = null;
        this.current_value = null;
        this.current_name = null;
        this.default_properties = null;
    }

    /**
     * dispose
     */
    public void dispose() {
        if ( this.dbSelector != null ) this.manager.release( (Component) this.dbSelector );
    }

    /**
     * configure
     */
    public void configure( Configuration conf ) throws ConfigurationException {
        if ( conf != null ) {
            Configuration child = conf.getChild( "old-driver" );
            this.oldDriver = child.getValueAsBoolean( false );
            getLogger().debug( "old-driver is " + this.oldDriver + " for " + this );
        }
    }

    /** BEGIN SitemapComponent methods **/

    public void setup( SourceResolver resolver, Map objectModel,
                       String source, Parameters parameters )
            throws ProcessingException, SAXException, IOException {
        // setup instance variables
        this.queries = new Vector();
        this.current_value = new StringBuffer();
        this.current_name = null;
        this.current_query_index = -1;
        this.default_properties = new Properties();
        this.current_state = SQLTransformer.STATE_OUTSIDE;

        this.parameters = parameters;
        this.objectModel = objectModel;

        current_state = SQLTransformer.STATE_OUTSIDE;

        // Check for connection
        String parameter = parameters.getParameter( SQLTransformer.MAGIC_CONNECTION, null );
        if ( parameter != null ) {
            getLogger().debug( "CONNECTION: " + parameter );
            default_properties.setProperty( SQLTransformer.MAGIC_CONNECTION, parameter );
        } else {
            // Check the dburl
            parameter = parameters.getParameter( SQLTransformer.MAGIC_DBURL, null );
            if ( parameter != null ) {
                getLogger().debug( "DBURL: " + parameter );
                default_properties.setProperty( SQLTransformer.MAGIC_DBURL, parameter );
            }
            // Check the username
            parameter = parameters.getParameter( SQLTransformer.MAGIC_USERNAME, null );
            if ( parameter != null ) {
                getLogger().debug( "USERNAME: " + parameter );
                default_properties.setProperty( SQLTransformer.MAGIC_USERNAME, parameter );
            }

            // Check the password
            parameter = parameters.getParameter( SQLTransformer.MAGIC_PASSWORD, null );
            if ( parameter != null ) {
                default_properties.setProperty( SQLTransformer.MAGIC_PASSWORD, parameter );
            }
        }
        showNrOfRows = parameters.getParameter( SQLTransformer.MAGIC_NR_OF_ROWS, null );

        // Check for row-element and doc-element
        parameter = parameters.getParameter( SQLTransformer.MAGIC_DOC_ELEMENT, null );
        if ( parameter != null ) {
            getLogger().debug( "DOC-ELEMENT: " + parameter );
            default_properties.setProperty( SQLTransformer.MAGIC_DOC_ELEMENT, parameter );
        }
        parameter = parameters.getParameter( SQLTransformer.MAGIC_ROW_ELEMENT, null );
        if ( parameter != null ) {
            getLogger().debug( "ROW-ELEMENT: " + parameter );
            default_properties.setProperty( SQLTransformer.MAGIC_ROW_ELEMENT, parameter );
        }

        // Check namespace-prefix and namespace-uri
        parameter = parameters.getParameter( SQLTransformer.MAGIC_NS_URI_ELEMENT, null );
        if ( parameter != null ) {
            getLogger().debug( "NS-URI: " + parameter );
            default_properties.setProperty( SQLTransformer.MAGIC_NS_URI_ELEMENT, parameter );
        }

        parameter = parameters.getParameter( SQLTransformer.MAGIC_NS_PREFIX_ELEMENT, null );
        if ( parameter != null ) {
            getLogger().debug( "NS-PREFIX: " + parameter );
            default_properties.setProperty( SQLTransformer.MAGIC_NS_PREFIX_ELEMENT, parameter );
        }
    }

    /** END SitemapComponent methods **/

    /** BEGIN my very own methods **/

    /**
     * This will be the meat of SQLTransformer, where the query is run.
     */
    protected void executeQuery( int index ) throws SAXException {


        // First set up the namespace handling
        if ( getCurrentQuery().properties.getProperty( SQLTransformer.MAGIC_NS_URI_ELEMENT ) != null ) {
            outUri = getCurrentQuery().properties.getProperty( SQLTransformer.MAGIC_NS_URI_ELEMENT );
        } else {
            outUri = my_uri;
        }

        if ( getCurrentQuery().properties.getProperty( SQLTransformer.MAGIC_NS_PREFIX_ELEMENT ) != null ) {
            outPrefix = getCurrentQuery().properties.getProperty( SQLTransformer.MAGIC_NS_PREFIX_ELEMENT );
        } else {
            outPrefix = "";
        }
        if ( !"".equals( outPrefix ) && !"".equals( outUri ) ) {
            this.contentHandler.startPrefixMapping( outPrefix, outUri );
        }

        getLogger().debug( "SQLTransformer executing query nr " + index );
        AttributesImpl attr = new AttributesImpl();
        Query query = (Query) queries.elementAt( index );
        boolean query_failure = false;
        try {
            try {
                query.execute();
            } catch ( SQLException e ) {
                getLogger().debug( "SQLTransformer:.executeQuery() query.execute failed ", e );
                AttributesImpl my_attr = new AttributesImpl();
                this.start( query.rowset_name, my_attr );
                this.start( MAGIC_ERROR, my_attr);
                this.data( e.getMessage());
                this.end( MAGIC_ERROR );
                this.end( query.rowset_name );
                query_failure = true;
            }
            if ( !query_failure ) {

                if ( showNrOfRows != null && showNrOfRows.equalsIgnoreCase( "true" ) ) {
                    attr.addAttribute( my_uri, query.nr_of_rows, query.nr_of_rows, "CDATA",
                       String.valueOf( query.getNrOfRows() ) );
                }
                String name = query.getName();
                if ( name != null ) {
                    attr.addAttribute( my_uri, query.name_attribute, query.name_attribute, "CDATA",
                       name );
                }
                this.start( query.rowset_name, attr );
                attr = new AttributesImpl();
                if ( !query.isStoredProcedure() ) {
                    while ( query.next() ) {
                        this.start( query.row_name, attr );
                        query.serializeRow();
                        if ( index + 1 < queries.size() ) {
                            executeQuery( index + 1 );
                        }
                        this.end( query.row_name );
                    }
                } else {
                    query.serializeStoredProcedure();
                }
                this.end( query.rowset_name );
            }
        } catch ( SQLException e ) {
            getLogger().debug( "SQLTransformer.executeQuery()", e );
            throw new SAXException( e );
        } finally {
            try {
                query.close();
            } catch ( SQLException e ) {
                getLogger().warn( "SQLTransformer: Could not close JDBC connection", e );
            }
        }
        if ( !"".equals( outPrefix ) && !"".equals( outUri ) ) {
            this.contentHandler.endPrefixMapping( outPrefix );
        }
    }

    protected static void throwIllegalStateException( String message ) {
        throw new IllegalStateException( my_name + ": " + message );
    }

    protected void startExecuteQueryElement() {
        switch ( current_state ) {
            case SQLTransformer.STATE_OUTSIDE:
            case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
                current_query_index = queries.size();
                Query query = new Query( this, current_query_index );
                queries.addElement( query );
                current_state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting a start execute query element" );
        }
    }

    protected void startValueElement( String name ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
                current_name = name;
                current_value.setLength( 0 );
                current_state = SQLTransformer.STATE_INSIDE_VALUE_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting a start value element: " +
                                            name );
        }
    }

    protected void startQueryElement( Attributes attributes ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
                current_value.setLength( 0 );
                Query q = getCurrentQuery();
                current_state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
                String isupdate =
                        attributes.getValue( "", SQLTransformer.MAGIC_UPDATE_ATTRIBUTE );
                if ( isupdate != null && !isupdate.equalsIgnoreCase( "false" ) )
                    q.setUpdate( true );
                String isstoredprocedure =
                        attributes.getValue( "", SQLTransformer.MAGIC_STORED_PROCEDURE_ATTRIBUTE );
                if ( isstoredprocedure != null && !isstoredprocedure.equalsIgnoreCase( "false" ) )
                    q.setStoredProcedure( true );
                String name =
                        attributes.getValue( "", SQLTransformer.MAGIC_NAME_ATTRIBUTE );
                if ( name != null ) {
                    q.setName( name );
                }
                break;
            default:
                throwIllegalStateException( "Not expecting a start query element" );
        }
    }

    protected void endQueryElement() {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
                if ( current_value.length() > 0 ) {
                    getCurrentQuery().addQueryPart(
                            current_value.toString() );
                    getLogger().debug( "QUERY IS \"" +
                                       current_value.toString() + "\"" );
                    current_value.setLength( 0 );
                }
                current_state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting a stop query element" );
        }
    }

    protected void endValueElement() {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_VALUE_ELEMENT:
                getCurrentQuery().setParameter( current_name,
                                                current_value.toString() );
                getLogger().debug( "SETTING VALUE ELEMENT name {" +
                                   current_name + "} value {" +
                                   current_value.toString() + "}" );

                current_state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting an end value element" );
        }
    }

    protected void endExecuteQueryElement() throws SAXException {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
                if ( current_query_index == 0 ) {
                    executeQuery( 0 );
                    queries.removeAllElements();
                    current_state = SQLTransformer.STATE_OUTSIDE;
                } else {
                    current_query_index--;
                    current_state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
                }
                break;
            default:
                throwIllegalStateException( "Not expecting an end execute query element" );
        }
    }

    protected void startAncestorValueElement( Attributes attributes ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
                int level = 0;
                try {
                    level = Integer.parseInt( attributes.getValue( my_uri,
                                                                   SQLTransformer.MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE ) );
                } catch ( Exception e ) {
                    getLogger().debug( "SQLTransformer", e );
                    throwIllegalStateException( "Ancestor value elements must have a " +
                                                SQLTransformer.MAGIC_ANCESTOR_VALUE_LEVEL_ATTRIBUTE + " attribute" );
                }
                String name = attributes.getValue( my_uri,
                                                   SQLTransformer.MAGIC_ANCESTOR_VALUE_NAME_ATTRIBUTE );
                if ( name == null ) {
                    throwIllegalStateException( "Ancestor value elements must have a " +
                                                SQLTransformer.MAGIC_ANCESTOR_VALUE_NAME_ATTRIBUTE + " attribute" );
                }
                AncestorValue av = new AncestorValue( level, name );
                getLogger().debug( "ANCESTOR VALUE " + level + " " + name );

                if ( current_value.length() > 0 ) {
                    getCurrentQuery().addQueryPart(
                            current_value.toString() );
                    getLogger().debug( "QUERY IS \"" +
                                       current_value.toString() + "\"" );

                    current_value.setLength( 0 );
                }
                getCurrentQuery().addQueryPart( av );
                current_state = SQLTransformer.STATE_INSIDE_ANCESTOR_VALUE_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting a start ancestor value element" );
        }
    }

    protected void endAncestorValueElement() {
        current_state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
    }

    protected void startSubstituteValueElement( Attributes attributes ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
                String name = attributes.getValue( my_uri,
                                                   SQLTransformer.MAGIC_SUBSTITUTE_VALUE_NAME_ATTRIBUTE );
                if ( name == null ) {
                    throwIllegalStateException( "Substitute value elements must have a " +
                                                SQLTransformer.MAGIC_SUBSTITUTE_VALUE_NAME_ATTRIBUTE + " attribute" );
                }
                String substitute = parameters.getParameter( name, null );
                //escape single quote
                substitute = replaceCharWithString( substitute, '\'', "''" );
                getLogger().debug( "SUBSTITUTE VALUE " + substitute );
                if ( current_value.length() > 0 ) {
                    getCurrentQuery().addQueryPart( current_value.toString() );
                    getLogger().debug( "QUERY IS \"" +
                                       current_value.toString() + "\"" );

                    current_value.setLength( 0 );
                }
                getCurrentQuery().addQueryPart( substitute );
                current_state = SQLTransformer.STATE_INSIDE_SUBSTITUTE_VALUE_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting a start substitute value element" );
        }
    }

    protected void endSubstituteValueElement() {
        current_state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
    }

    protected void startEscapeStringElement( Attributes attributes ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_QUERY_ELEMENT:
                if ( current_value.length() > 0 ) {
                    getCurrentQuery().addQueryPart( current_value.toString() );
                    getLogger().debug( "QUERY IS \"" +
                                       current_value.toString() + "\"" );
                    current_value.setLength( 0 );
                }

                current_state = SQLTransformer.STATE_INSIDE_ESCAPE_STRING;
                break;
            default:
                throwIllegalStateException( "Not expecting a start escape-string element" );
        }
    }

    protected void endEscapeStringElement() {
        switch ( current_state) {
        case SQLTransformer.STATE_INSIDE_ESCAPE_STRING:
            if ( current_value.length() > 0 ) {
                String escape = current_value.toString();
                escape = replaceCharWithString( escape, '\'', "''" );
                escape = replaceCharWithString( escape, '\\', "\\\\" );
                getCurrentQuery().addQueryPart( escape );
                getLogger().debug( "QUERY IS \"" +
                   current_value.toString() + "\"" );
                current_value.setLength( 0 );
            }
            current_state = SQLTransformer.STATE_INSIDE_QUERY_ELEMENT;
            break;
        default:
                throwIllegalStateException( "Not expecting a end escape-string element" );
        }
    }

    protected void startInParameterElement( Attributes attributes ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
                String nr = attributes.getValue( my_uri,
                                                 SQLTransformer.MAGIC_IN_PARAMETER_NR_ATTRIBUTE );
                String value = attributes.getValue( my_uri,
                                                    SQLTransformer.MAGIC_IN_PARAMETER_VALUE_ATTRIBUTE );
                getLogger().debug( "IN PARAMETER NR " + nr + "; VALUE " + value );
                int position = Integer.parseInt( nr );
                getCurrentQuery().setInParameter( position, value );
                current_state = SQLTransformer.STATE_INSIDE_IN_PARAMETER_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting an in-parameter element" );
        }
    }

    protected void endInParameterElement() {
        current_state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
    }

    protected void startOutParameterElement( Attributes attributes ) {
        switch ( current_state ) {
            case SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT:
                String name = attributes.getValue( my_uri,
                                                   SQLTransformer.MAGIC_OUT_PARAMETER_NAME_ATTRIBUTE );
                String nr = attributes.getValue( my_uri,
                                                 SQLTransformer.MAGIC_OUT_PARAMETER_NR_ATTRIBUTE );
                String type = attributes.getValue( my_uri,
                                                   SQLTransformer.MAGIC_OUT_PARAMETER_TYPE_ATTRIBUTE );
                getLogger().debug( "OUT PARAMETER NAME" + name + ";NR " + nr + "; TYPE " + type );
                int position = Integer.parseInt( nr );
                getCurrentQuery().setOutParameter( position, type, name );
                current_state = SQLTransformer.STATE_INSIDE_OUT_PARAMETER_ELEMENT;
                break;
            default:
                throwIllegalStateException( "Not expecting an out-parameter element" );
        }
    }

    protected void endOutParameterElement() {
        current_state = SQLTransformer.STATE_INSIDE_EXECUTE_QUERY_ELEMENT;
    }

    protected Query getCurrentQuery() {
        return (Query) queries.elementAt( current_query_index );
    }

    protected Query getQuery( int i ) {
        return (Query) queries.elementAt( i );
    }

    private String replaceCharWithString( String in, char c, String with ) {
        Tokenizer tok;
        StringBuffer replaced = null;
        if ( in.indexOf( c ) > -1 ) {
            tok = new Tokenizer( in, c );
            replaced = new StringBuffer();
            while ( tok.hasMoreTokens() ) {
                replaced.append( tok.nextToken() );
                if ( tok.hasMoreTokens() )
                    replaced.append( with );
            }
        }
        if ( replaced != null ) {
            return replaced.toString();
        } else {
            return in;
        }
    }

    /**
     Qualifies an element name by giving it a prefix.
     @param name the element name
     @param prefix the prefix to qualify with
     @return a namespace qualified name that is correct
     */
    protected String nsQualify( String name, String prefix ) {
        if ( name == null || "".equals( name ) ) {
            return name;
        }
        if ( prefix != null && !"".equals( prefix ) ) {
            return new StringBuffer( prefix ).append( ":" ).append( name ).toString();
        } else {
            return name;
        }
    }

    /** END my very own methods **/

    /** BEGIN SAX ContentHandler handlers **/

    public void startPrefixMapping( String prefix, String uri ) throws SAXException {
        if ( uri.equals( my_uri ) ) {
            inPrefix = prefix;
        } else {
            super.contentHandler.startPrefixMapping( prefix, uri );
        }
    }

    public void endPrefixMapping( String prefix ) throws SAXException {
        if ( !prefix.equals( inPrefix ) ) {
            super.contentHandler.endPrefixMapping( prefix );
        }
    }


    public void setDocumentLocator( Locator locator ) {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug( "PUBLIC ID: " + locator.getPublicId() );
            getLogger().debug( "SYSTEM ID: " + locator.getSystemId() );
        }
        if ( super.contentHandler != null )
            super.contentHandler.setDocumentLocator( locator );
    }

    public void startElement( String uri, String name, String raw,
                              Attributes attributes ) throws SAXException {
        if ( !uri.equals( my_uri ) ) {
            super.startElement( uri, name, raw, attributes );
            return;
        }
        getLogger().debug( "RECEIVED START ELEMENT " + name );

        if ( name.equals( SQLTransformer.MAGIC_EXECUTE_QUERY ) ) {
            startExecuteQueryElement();
        } else if ( name.equals( SQLTransformer.MAGIC_QUERY ) ) {
            startQueryElement( attributes );
        } else if ( name.equals( SQLTransformer.MAGIC_ANCESTOR_VALUE ) ) {
            startAncestorValueElement( attributes );
        } else if ( name.equals( SQLTransformer.MAGIC_SUBSTITUTE_VALUE ) ) {
            startSubstituteValueElement( attributes );
        } else if ( name.equals( SQLTransformer.MAGIC_IN_PARAMETER ) ) {
            startInParameterElement( attributes );
        } else if ( name.equals( SQLTransformer.MAGIC_OUT_PARAMETER ) ) {
            startOutParameterElement( attributes );
        } else if ( name.equals( SQLTransformer.MAGIC_ESCAPE_STRING ) ) {
            startEscapeStringElement( attributes );
        } else {
            startValueElement( name );
        }
    }

    public void endElement( String uri, String name,
                            String raw ) throws SAXException {
        if ( !uri.equals( my_uri ) ) {
            super.endElement( uri, name, raw );
            return;
        }
        getLogger().debug( "RECEIVED END ELEMENT " + name + "(" + uri + ")" );

        if ( name.equals( SQLTransformer.MAGIC_EXECUTE_QUERY ) ) {
            endExecuteQueryElement();
        } else if ( name.equals( SQLTransformer.MAGIC_QUERY ) ) {
            endQueryElement();
        } else if ( name.equals( SQLTransformer.MAGIC_ANCESTOR_VALUE ) ) {
            endAncestorValueElement();
        } else if ( name.equals( SQLTransformer.MAGIC_SUBSTITUTE_VALUE ) ) {
            endSubstituteValueElement();
        } else if ( name.equals( SQLTransformer.MAGIC_IN_PARAMETER ) ) {
            endInParameterElement();
        } else if ( name.equals( SQLTransformer.MAGIC_OUT_PARAMETER ) ) {
            endOutParameterElement();
        } else if ( name.equals( SQLTransformer.MAGIC_VALUE ) || current_state == SQLTransformer.STATE_INSIDE_VALUE_ELEMENT ) {
            endValueElement();
        } else  if ( name.equals( SQLTransformer.MAGIC_ESCAPE_STRING ) ) {
            endEscapeStringElement();
        } else {
            super.endElement( uri, name, raw );
        }
    }

    public void characters( char ary[], int start,
                            int length ) throws SAXException {
        if ( current_state != SQLTransformer.STATE_INSIDE_VALUE_ELEMENT &&
             current_state != SQLTransformer.STATE_INSIDE_QUERY_ELEMENT &&
             current_state != SQLTransformer.STATE_INSIDE_ESCAPE_STRING ) {
            super.characters( ary, start, length );
        }
        getLogger().debug( "RECEIVED CHARACTERS: " +
                           new String( ary, start, length ) );
        current_value.append( ary, start, length );
    }

    private void attribute( AttributesImpl attr, String name, String value ) {
        attr.addAttribute( outUri, name, nsQualify( name, outPrefix ), "CDATA", value );
    }

    private void start( String name, AttributesImpl attr ) throws SAXException {
        super.contentHandler.startElement( outUri, name, nsQualify( name, outPrefix ), attr );
        attr.clear();
    }

    private void end( String name ) throws SAXException {
        super.contentHandler.endElement( outUri, name, nsQualify( name, outPrefix ) );
    }

    private void data( String data ) throws SAXException {
        if ( data != null ) {
            super.contentHandler.characters( data.toCharArray(), 0, data.length() );
        }
    }

    protected static String getStringValue( Object object ) {
        if ( object instanceof byte[] ) {
            return new String( (byte[]) object );
        } else if ( object instanceof char[] ) {
            return new String( (char[]) object );
        } else if ( object != null ) {
            return object.toString();
        } else {
            return "";
        }
    }

    public final Logger getTheLogger() {
        return getLogger();
    }


    class Query {

        /** Who's your daddy? **/
        protected SQLTransformer transformer;

        /** What index are you in daddy's queries list **/
        protected int query_index;

        /** SQL configuration information **/
        protected Properties properties;

        /** Dummy static variables for the moment **/
        protected String rowset_name = "rowset";
        protected String nr_of_rows = "nrofrows";
        protected String row_name = "row";
        protected String name_attribute = "name";

        /** The connection, once opened **/
        protected Connection conn;

        /** And the statements **/
        protected PreparedStatement pst;
        protected CallableStatement cst;

        /** The results, of course **/
        protected ResultSet rs = null;

        /** And the results' metadata **/
        protected ResultSetMetaData md = null;

        /** If this query is actually an update (insert, update, delete) **/
        protected boolean isupdate = false;

        /** If this query is actually a stored procedure **/
        protected boolean isstoredprocedure = false;

        protected String name = null;

        /** If it is an update/etc, the return value (num rows modified) **/
        protected int rv = -1;

        /** The parts of the query **/
        protected Vector query_parts = new Vector();

        /** In parameters **/
        protected HashMap inParameters = null;

        /** Out parameters **/
        protected HashMap outParameters = null;

        /** Mapping out parameters - objectModel **/
        protected HashMap outParametersNames = null;

        protected Query( SQLTransformer transformer, int query_index ) {
            this.transformer = transformer;
            this.query_index = query_index;
            this.properties = new Properties( transformer.default_properties );
        }

        protected void setParameter( String name, String value ) {
            properties.setProperty( name, value );
        }

        protected void setUpdate( boolean flag ) {
            isupdate = flag;
        }

        protected void setStoredProcedure( boolean flag ) {
            isstoredprocedure = flag;
        }

        protected boolean isStoredProcedure() {
            return isstoredprocedure;
        }

        protected void setName( String name ) {
            this.name = name;
        }

        protected String getName() {
            return name;
        }

        protected void setInParameter( int pos, String val ) {
            if ( inParameters == null ) {
                inParameters = new HashMap();
            }
            inParameters.put( new Integer( pos ), val );
        }

        protected void setOutParameter( int pos, String type, String name ) {
            if ( outParameters == null ) {
                outParameters = new HashMap();
                outParametersNames = new HashMap();
            }
            outParameters.put( new Integer( pos ), type );
            outParametersNames.put( new Integer( pos ), name );
        }

        private void registerInParameters( PreparedStatement pst ) throws SQLException {
            if ( inParameters == null )
                return;
            Iterator itInKeys = inParameters.keySet().iterator();
            Integer counter;
            String value;
            while ( itInKeys.hasNext() ) {
                counter = (Integer) itInKeys.next();
                value = (String) inParameters.get( counter );
                try {
                    pst.setObject( counter.intValue(), value );
                } catch ( SQLException e ) {
                    transformer.getTheLogger().error( "Caught a SQLException", e );
                    throw e;
                }
            }
        }

        private void registerOutParameters( CallableStatement cst ) throws SQLException {
            if ( outParameters == null )
                return;
            Iterator itOutKeys = outParameters.keySet().iterator();
            Integer counter;
            int index;
            String type, className, fieldName;
            Class clss;
            Field fld;
            while ( itOutKeys.hasNext() ) {
                counter = (Integer) itOutKeys.next();
                type = (String) outParameters.get( counter );
                index = type.lastIndexOf( "." );
                if ( index > -1 ) {
                    className = type.substring( 0, index );
                    fieldName = type.substring( index + 1, type.length() );
                } else {
                    transformer.getTheLogger().error( "Invalid SQLType: " + type, null );
                    throw new SQLException( "Wrong SQLType" );
                }
                try {
                    clss = Class.forName( className );
                    fld = clss.getField( fieldName );
                    cst.registerOutParameter( counter.intValue(), fld.getInt( fieldName ) );
                } catch ( Exception e ) {
                    //lots of different exceptions to catch
                    transformer.getTheLogger().error( "Invalid SQLType: " +
                                                      className + "." + fieldName, e );
                }
            }
        }

        /** Get a Connection. Made this a separate method to separate the logic from the actual execution. */
        protected Connection getConnection() throws SQLException {
            Connection result = null;

            String connection = properties.getProperty( SQLTransformer.MAGIC_CONNECTION );
            String dburl = properties.getProperty( SQLTransformer.MAGIC_DBURL );
            String username = properties.getProperty( SQLTransformer.MAGIC_USERNAME );
            String password = properties.getProperty( SQLTransformer.MAGIC_PASSWORD );

            DataSourceComponent datasource = null;
            try {
                if ( connection != null ) {
                    datasource = (DataSourceComponent) dbSelector.select( connection );
                    while ( result == null ) {
                        try {
                            result = datasource.getConnection();
                        } catch ( Exception e ) {
                            long waittime = 5000 * (long) Math.random();
                            getTheLogger().debug( "SQLTransformer$Query: could not acquire a Connection -- waiting " + waittime + " ms to try again." );
                            try {
                                Thread.sleep( waittime );
                            } catch ( InterruptedException ie ) {
                            }
                        }
                    }
                } else {
                    if ( username == null || password == null ) {
                        result = DriverManager.getConnection( dburl );
                    } else {
                        result = DriverManager.getConnection( dburl, username,
                                                              password );
                    }
                }
            } catch ( ComponentException cme ) {
                transformer.getTheLogger().error( "Could not use connection: " + connection, cme );
            } catch ( SQLException e ) {
                transformer.getTheLogger().error( "Caught a SQLException", e );
                throw e;
            } finally {
                if ( datasource != null ) dbSelector.release( datasource );
            }

            return result;
        }

        protected void execute() throws SQLException {
            if ( null != properties.getProperty( SQLTransformer.MAGIC_DOC_ELEMENT ) ) {
                this.rowset_name = properties.getProperty( SQLTransformer.MAGIC_DOC_ELEMENT );
            }
            if ( null != properties.getProperty( SQLTransformer.MAGIC_ROW_ELEMENT ) ) {
                this.row_name = properties.getProperty( SQLTransformer.MAGIC_ROW_ELEMENT );
            }


            Enumeration enum = query_parts.elements();
            StringBuffer sb = new StringBuffer();
            while ( enum.hasMoreElements() ) {
                Object object = enum.nextElement();
                if ( object instanceof String ) {
                    sb.append( (String) object );
                } else if ( object instanceof AncestorValue ) {
                    /** Do a lookup into the ancestors' result's values **/
                    AncestorValue av = (AncestorValue) object;
                    Query query = transformer.getQuery( query_index - av.level );
                    sb.append( query.getColumnValue( av.name ) );
                }
            }
            String query = sb.toString();
            transformer.getTheLogger().debug( "EXECUTING " + query );

            conn = getConnection();

            try {
                if ( !isstoredprocedure ) {
                    if ( oldDriver ) {
                        pst = conn.prepareStatement( query );
                    } else {
                        pst = conn.prepareStatement( query,
                                                     ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                     ResultSet.CONCUR_READ_ONLY );
                    }
                } else {
                    if ( oldDriver ) {
                        cst = conn.prepareCall( query );
                    } else {
                        cst = conn.prepareCall( query,
                                                ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY );
                    }
                    registerOutParameters( cst );
                    pst = cst;
                }

                registerInParameters( pst );
                boolean result = pst.execute();
                if ( result ) {
                    rs = pst.getResultSet();
                    md = rs.getMetaData();
                } else {
                    rv = pst.getUpdateCount();
                }
            } catch ( SQLException e ) {
                transformer.getTheLogger().error( "Caught a SQLException", e );
                throw e;
            } finally {
                conn.close();
                conn = null;        // To make sure we don't use this connection again.
            }
        }

        protected int getNrOfRows() throws SQLException {
            int nr = 0;
            if ( rs != null ) {
                if ( oldDriver ) {
                    nr = -1;
                } else {
                    try {
                        rs.last();
                        nr = rs.getRow();
                        rs.beforeFirst();
                    } catch ( NullPointerException e ) {
                        // A NullPointerException here crashes a whole lot of C2 -- catching it so it won't do any harm for now, but seems like it should be solved seriously
                        getTheLogger().error( "NPE while getting the nr of rows", e );
                    }
                }
            } else {
                if ( outParameters != null ) {
                    nr = outParameters.size();
                }
            }
            return nr;
        }

        protected String getColumnValue( int i ) throws SQLException {
            return transformer.getStringValue( rs.getObject( i ) );
        }

        protected String getColumnValue( String name ) throws SQLException {
            return transformer.getStringValue( rs.getObject( name ) );
        }

        protected boolean next() throws SQLException {
            // if rv is not -1, then an SQL insert, update, etc, has
            // happened (see JDBC docs - return codes for executeUpdate)
            if ( rv != -1 )
                return true;
            try {
                if ( rs == null || !rs.next() ) {
                    //close();
                    return false;
                }
            } catch ( NullPointerException e ) {
                getTheLogger().debug( "NullPointerException, returning false.", e );
                return false;
            }
            return true;
        }

        protected void close() throws SQLException {
            try {
                if ( rs != null )
                    try {
                        //getTheLogger().debug("Trying to close resultset "+rs.toString());
                        rs.close();
                        rs = null;      // This prevents us from using the resultset again.
                        //250getTheLogger().debug("Really closed the resultset now.");
                    } catch ( NullPointerException e ) {
                        getTheLogger().debug( "NullPointer while closing the resultset.", e );
                    }
                if ( pst != null )
                    pst.close();
                pst = null;        // Prevent using pst again.
                if ( cst != null )
                    cst.close();
                cst = null;        // Prevent using cst again.
            } finally {
                if ( conn != null )
                    conn.close();
                conn = null;
            }
        }

        protected void addQueryPart( Object object ) {
            query_parts.addElement( object );
        }

        protected void serializeRow() throws SQLException, SAXException {
            AttributesImpl attr = new AttributesImpl();
            if ( !isupdate && !isstoredprocedure ) {
                for ( int i = 1; i <= md.getColumnCount(); i++ ) {
                    transformer.start( md.getColumnName( i ).toLowerCase(), attr );
                    transformer.data( getColumnValue( i ) );
                    transformer.end( md.getColumnName( i ).toLowerCase() );
                }
            } else if ( isupdate && !isstoredprocedure ) {
                transformer.start( "returncode", attr );
                transformer.data( String.valueOf( rv ) );
                transformer.end( "returncode" );
                rv = -1; // we only want the return code shown once.
            }
        }

        protected void serializeStoredProcedure() throws SQLException, SAXException {
            if ( outParametersNames == null || cst == null )
                return;
            //make sure output follows order as parameter order in stored procedure
            Iterator itOutKeys = ( new TreeMap( outParameters ) ).keySet().iterator();
            Integer counter;
            AttributesImpl attr = new AttributesImpl();
            try {
                while ( itOutKeys.hasNext() ) {
                    counter = (Integer) itOutKeys.next();
                    try {
                        if ( cst == null ) getTheLogger().debug( "SQLTransformer: cst is null" );
                        if ( counter == null ) getTheLogger().debug( " SQLTransformer: counter is null" );
                        Object obj = cst.getObject( counter.intValue() );
                        if ( !( obj instanceof ResultSet ) ) {
                            transformer.start( (String) outParametersNames.get( counter ), attr );
                            transformer.data( transformer.getStringValue( obj ) );
                            transformer.end( (String) outParametersNames.get( counter ) );
                        } else {
                            ResultSet rs = (ResultSet) obj;
                            try {
                                transformer.start( (String) outParametersNames.get( counter ), attr );
                                ResultSetMetaData md = rs.getMetaData();
                                while ( rs.next() ) {
                                    transformer.start( this.row_name, attr );
                                    for ( int i = 1; i <= md.getColumnCount(); i++ ) {
                                        transformer.start( md.getColumnName( i ).toLowerCase(), attr );
                                        transformer.data( transformer.getStringValue( rs.getObject( i ) ) );
                                        transformer.end( md.getColumnName( i ).toLowerCase() );
                                    }
                                    transformer.end( this.row_name );
                                }
                            } finally {
                                rs.close();
                                rs = null;
                            }
                            transformer.end( (String) outParametersNames.get( counter ) );
                        }
                    } catch ( SQLException e ) {
                        transformer.getTheLogger().error( "Caught a SQLException", e );
                        throw e;
                    }
                }
            } finally {
                //close();
            }
        }
    }

    private class AncestorValue {
        protected int level;
        protected String name;

        protected AncestorValue( int level, String name ) {
            this.level = level;
            this.name = name;
        }
    }

}
TOP

Related Classes of org.apache.cocoon.transformation.SQLTransformer$Query

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.