OracleApplicationServerXMLDataSourceFactory -- "unexpected text" parsing error

From: Mike Kienenberger (mkienen..laska.net)
Date: Tue Jul 27 2004 - 19:49:28 EDT

  • Next message: Mike Kienenberger: "Re: OracleApplicationServerXMLDataSourceFactory -- "unexpected text" parsing error"

    I've now created a subclass of DriverDataSourceFactory called
    OracleApplicationServerXMLDataSourceFactory which will initialize from the
    Oracle Application Server datasources.xml JNDI definition file. (The
    advantage of this over cdeploy is that authentication information can be
    stored locally on each [dev/production] application server without needing
    to incorporate it into the application project build stream somehow).

    I have two questions:

    1) Is there any interest in adding this to a cayenne contrib directory? I'm
    not sure if it's relevent enough to put into the cayenne core. On the other
    hand, it's an example of how to subclass DriverDataSourceFactory to read
    driver information from an alternate xml format, and does have use for some
    Oracle Application Server JNDI deployers.

    2) I am having problems parsing a text tag in the xml file. Each datasource
    element can have an optional <description> child element (and maybe other
    elements) which I just need to ignore. Originally I thought I could just
    ignore the information I wasn't using. When that didn't work, I tried
    adding a DefaultHandler instance for children. When that failed, I created
    an IgnoreNestedElementsHandler which is really just a nested DefaultHandler.
     However, I get an error that the text between <description> tags is
    unexpected. Removing all text between <description> and </description>
    causes everything to work perfectly, but I can't really do this outside of
    my own test environment. There's nothing special about the xml as far as I
    can tell. I looked at the source for MapLoader which handles similar
    nested text elements (for example, db-generator-type), and I see no special
    treatment being done there. However, since my total SAX experience is with
    modifying Cayenne code, maybe I'm missing something fundamental somewhere.

    Error:

    ========================================================================
    org.objectstyle.cayenne.ConfigurationException: [v.1.1-dev April 14 2004]
    Error during Configuration initialization. [v.1.1-dev April 14 2004] Load
    failures. Main configuration class:
    org.objectstyle.cayenne.conf.FileConfiguration, details:
            domain.node.name=OnlineBillingNode,
    domain.node.datasource=/gvea/data-sources.xml:jdbc/CachedDS, reason:
    DataSource load failed -
    com.gvea.cayenne.OracleApplicationServerXMLDataSourceFactory$DataSourceHandler:
    unexpected text "Datasource used for connection cache."
            at
    org.objectstyle.cayenne.conf.Configuration.initializeSharedConfiguration(Configuration.java:304)
            at
    com.gvea.struts.ebpp.business.batch.DemandBasedTaskScheduler.main(DemandBasedTaskScheduler.java:310)
    Caused by: org.objectstyle.cayenne.ConfigurationException: [v.1.1-dev April
    14 2004] Load failures. Main configuration class:
    org.objectstyle.cayenne.conf.FileConfiguration, details:
            domain.node.name=OnlineBillingNode,
    domain.node.datasource=/gvea/data-sources.xml:jdbc/CachedDS, reason:
    DataSource load failed -
    com.gvea.cayenne.OracleApplicationServerXMLDataSourceFactory$DataSourceHandler:
    unexpected text "Datasource used for connection cache."
            at
    org.objectstyle.cayenne.conf.RuntimeLoadDelegate.finishedLoading(RuntimeLoadDelegate.java:589)
            at
    org.objectstyle.cayenne.conf.ConfigLoader.loadDomains(ConfigLoader.java:117)
            at
    org.objectstyle.cayenne.conf.DefaultConfiguration.initialize(DefaultConfiguration.java:185)
            at
    org.objectstyle.cayenne.conf.Configuration.initializeSharedConfiguration(Configuration.java:296)
            ... 1 more
    ========================================================================

    This is the raw xml being parsed.

    ========================================================================
    <data-source location="jdbc/CachedDS"
    class="oracle.jdbc.pool.OracleConnectionCacheImpl" password="xxx"
    max-connect-attempts="5" wait-timeout="30" connection-retry-interval="30"
    connection-driver="oracle.jdbc.driver.OracleDriver" username="xxx"
    min-connections="1" max-connections="30"
    url="jdbc:oracle:thin..xx:1521:xxx" inactivity-timeout="15" name="CachedDS">
    <description>Datasource used for connection cache.</description>
    </data-source>
    ========================================================================

    Same thing hand-formatted.

    ========================================================================
    <data-source
        location="jdbc/CachedDS"
        class="oracle.jdbc.pool.OracleConnectionCacheImpl"
        password="xxx"
        max-connect-attempts="5"
        wait-timeout="30"
        connection-retry-interval="30"
        connection-driver="oracle.jdbc.driver.OracleDriver"
        username="xxx"
        min-connections="1"
        max-connections="30"
        url="jdbc:oracle:thin..xx:1521:xxx"
        inactivity-timeout="15"
        name="CachedDS">

        <description>Datasource used for connection cache.</description>

    </data-source>
    ========================================================================

    Code:

    ========================================================================
    package com.gvea.cayenne;

    import java.io.FileInputStream;
    import java.io.InputStream;

    import org.apache.log4j.Level;
    import org.apache.log4j.Logger;
    import org.objectstyle.cayenne.ConfigurationException;
    import org.objectstyle.cayenne.conf.DriverDataSourceFactory;
    import org.objectstyle.cayenne.conn.DataSourceInfo;
    import org.objectstyle.cayenne.util.AbstractHandler;
    import org.xml.sax.Attributes;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.SAXParseException;
    import org.xml.sax.XMLReader;
    import org.xml.sax.helpers.DefaultHandler;

    /**
     * Creates DataSource objects from Oracle Application Server datasource.XML
    configuration files that
     * describe a JDBC driver. Wraps JDBC driver in a generic DataSource
     * implementation.
     *
     *..uthor Mike Kienenberger
     */
    public class OracleApplicationServerXMLDataSourceFactory extends
    DriverDataSourceFactory {
        private static Logger logObj =
            Logger.getLogger(OracleApplicationServerXMLDataSourceFactory.class);

        protected String jndiLocation = null;
        protected int jndiDatasourcesFoundCount = 0;

        /**
         * Default constructor
         *..hrows Exception
         */
        public OracleApplicationServerXMLDataSourceFactory() throws Exception {
            super();
        }

        /**
         * Loads driver information from the file at <code>location</code>.
         * Called internally from "getDataSource"
         */
        protected void load(String datasourceLocation) throws Exception {
            logObj.log(
                logLevel,
                "loading driver information from '" + datasourceLocation +
    "'.");

            String datasourceFileLocation = null;
            int colonPos = datasourceLocation.indexOf(':');
            if (-1 != colonPos)
            {
                datasourceFileLocation = datasourceLocation.substring(0,
    colonPos);
                if (colonPos+1 >= datasourceLocation.length())
                {
                    logObj.log(
                        logLevel,
                        "Error: location '" + datasourceLocation + "' contains
    no JNDI location after ':'.");
                    throw new ConfigurationException(
                        "Can't find DataSource configuration file at " +
    datasourceLocation + ": location '" + datasourceLocation + "' contains no
    data after ':'.");
                }
                jndiLocation = datasourceLocation.substring(colonPos + 1);
            }
            else
            {
                datasourceFileLocation =
    "/ora/Ora10gAS/j2ee/home/config/data-sources.xml";
                jndiLocation = datasourceLocation;
            }
            
            InputStream in = new FileInputStream(datasourceFileLocation);
            if (in == null) {
                logObj.log(
                    logLevel,
                    "Error: location '" + datasourceFileLocation + "' not
    found.");
                throw new ConfigurationException(
                    "Can't find DataSource configuration file at " +
    datasourceFileLocation);
            }

            RootHandler handler = new RootHandler();
            handler.init(logObj, logLevel, parser);
            parser.setContentHandler(handler);
            parser.setErrorHandler(handler);
            parser.parse(new InputSource(in));
            
            if (0 == jndiDatasourcesFoundCount)
            {
                logObj.log(
                    logLevel,
                    "Error: Can't find datasource definition " + jndiLocation +
    " in " + datasourceFileLocation);
                throw new ConfigurationException(
                    "Can't find datasource definition " + jndiLocation + " in "
    + datasourceFileLocation);
            }
            else if (1 < jndiDatasourcesFoundCount)
            {
                logObj.log(
                    logLevel,
                    "Error: Found " + jndiDatasourcesFoundCount + " datasource
    definitions " + jndiLocation + " in " + datasourceFileLocation);
                throw new ConfigurationException(
                    "Found " + jndiDatasourcesFoundCount + " datasource
    definitions " + jndiLocation + " in " + datasourceFileLocation);
            }
        }

        protected void incrementJndiDatasourcesFoundCount()
        {
            ++jndiDatasourcesFoundCount;
        }
        
        // SAX handlers start below

        /** Handler for the root element. Its only child must be the
    "data-sources" element. */
        private class RootHandler extends DefaultHandler {
            private Logger logObj;
            private Level logLevel;
            private XMLReader parser;
            
            public void init(Logger logObj, Level logLevel, XMLReader parser)
    throws SAXException {
                this.logObj = logObj;
                this.logLevel = logLevel;
                this.parser = parser;
            }
            /**
             * Handles the start of a "data-sources" element. A data-sources
    handler is created
             * and initialized with the element name and attributes.
             *
             *..xception SAXException if the tag given is not
             * <code>"data-sources"</code>
             */
            public void startElement(
                String namespaceURI,
                String localName,
                String qName,
                Attributes atts)
                throws SAXException {
                if (localName.equals("data-sources")) {
                    new DataSourcesHandler(parser, this).init(localName,
                        atts, logObj, logLevel);
                } else {
                    logObj.log(
                        logLevel,
                        "<data-sources> must be the root element. <"
                            + localName
                            + "> is unexpected.");
                    throw new SAXException(
                        "Config file is not of expected XML type. '"
                            + localName
                            + "' unexpected.");
                }
            }
        }

        /** Handler for the "data-sources" element. */
        private class DataSourcesHandler extends AbstractHandler {
            private Logger logObj;
            private Level logLevel;
            
            public DataSourcesHandler(XMLReader parser, ContentHandler
    parentHandler) {
                super(parser, parentHandler);
            }

            public void init(String name, Attributes attrs, Logger logObj, Level
    logLevel) throws SAXException {
                this.logObj = logObj;
                this.logLevel = logLevel;
            }

            /**
              * Handles the start of a data-source child element. An appropriate
    handler
              * is created and initialized with the element name and attributes.
              *
              *..xception SAXException if the tag given is not recognized.
              */
            public void startElement(
                String namespaceURI,
                String localName,
                String qName,
                Attributes atts)
                throws SAXException {
                    new DataSourceHandler(this.parser, this).init(
                        localName,
                        atts,
                        logObj, logLevel);
            }

        }

        /** Handler for the "data-source" element. */
        private class DataSourceHandler extends AbstractHandler {
            public DataSourceHandler(XMLReader parser, ContentHandler
    parentHandler) {
                super(parser, parentHandler);
            }

            public void init(String name, Attributes attrs,
            Logger logObj, Level logLevel) throws SAXException {
                String location = attrs.getValue("", "location");
                logObj.log(logLevel, "loading driver location " + location);
                
                if (jndiLocation.equals(location))
                {
                    incrementJndiDatasourcesFoundCount();
                    driverInfo = new DataSourceInfo();

                    String connectionDriver = attrs.getValue("",
    "connection-driver");
                    logObj.log(logLevel, "loading driver class " +
    connectionDriver);
                    driverInfo.setJdbcDriver(connectionDriver);

                    String username = attrs.getValue("", "username");
                    logObj.log(logLevel, "loading driver username " + username);
                    driverInfo.setUserName(username);
                
                    String password = attrs.getValue("", "password");
                    logObj.log(logLevel, "loading driver password " + password);
                    driverInfo.setPassword(password);
                
                    String url = attrs.getValue("", "url");
                    logObj.log(logLevel, "loading driver url " + url);
                    driverInfo.setDataSourceUrl(url);
                
                    String min = attrs.getValue("", "min");
                    logObj.log(logLevel, "loading driver min " + min);
                    if (min != null)
                        driverInfo.setMinConnections(Integer.parseInt(min));
                
                    String max = attrs.getValue("", "max");
                    logObj.log(logLevel, "loading driver max " + max);
                    if (max != null)
                        driverInfo.setMaxConnections(Integer.parseInt(max));
                }
            }

            /**
              * Handles the start of a child element (like "description").
    Currently ignored.
              *
              *..xception SAXException if the tag given is not recognized.
              */
            public void startElement(
                String namespaceURI,
                String localName,
                String qName,
                Attributes atts)
                throws SAXException {
                // ignore any children elements
                new IgnoreNestedElementsHandler();
            }
        }

        /** Handler for the "data-source" element. */
        private class IgnoreNestedElementsHandler extends DefaultHandler {
            public IgnoreNestedElementsHandler() {
                super();
            }

            /**
              * Handles the start of a child element (like "description").
    Currently ignored.
              *
              *..xception SAXException if the tag given is not recognized.
              */
            public void startElement(
                String namespaceURI,
                String localName,
                String qName,
                Attributes atts)
                throws SAXException {
                // ignore any children elements
                new IgnoreNestedElementsHandler();
            }
        }
    }
    ========================================================================



    This archive was generated by hypermail 2.0.0 : Tue Jul 27 2004 - 19:49:03 EDT