001package org.apache.commons.digester3.examples.api.dbinsert;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements.  See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License.  You may obtain a copy of the License at
010 * 
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 * 
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */ 
019
020import java.io.File;
021import java.io.IOException;
022import java.sql.Connection;
023
024import org.apache.commons.digester3.Digester;
025import org.xml.sax.SAXException;
026
027/**
028 * A simple program to demonstrate that the Commons Digester module can be
029 * used to trigger actions as the xml is parsed, rather than just build
030 * up in-memory representations of the parsed data. This example also shows
031 * how to write a custom Rule class.
032 * <p>
033 * This code will parse the provided "example.xml" file, and immediately 
034 * insert the processed data into a database as each row tag is parsed,
035 * instead of building up an in-memory representation. Actually, in order 
036 * to keep this example simple and easy to run, sql insert statements are 
037 * printed out rather than actually performing database inserts, but the 
038 * principle remains.
039 * <p> 
040 * Very verbose comments are included here, as this class is intended
041 * as a tutorial; if you look closely at method "addRules", you will
042 * see that the amount of code required to use the Digester is actually
043 * quite low.
044 * <p>
045 * Usage: java Main example.xml
046 */
047public class Main
048{
049
050    /**
051     * Main method : entry point for running this example program.
052     * <p>
053     * Usage: java Main example.xml
054     */
055    public static void main( String[] args )
056    {
057        if ( args.length != 1 )
058        {
059            usage();
060            System.exit( -1 );
061        }
062
063        String filename = args[0];
064
065        // Create a Digester instance
066        Digester d = new Digester();
067
068        // Here you would establish a real connection.
069        // There would also be a finally clause to ensure it is
070        // closed after parsing terminates, etc.
071        Connection connection = null;
072
073        // Add rules to the digester that will be triggered while
074        // parsing occurs.
075        addRules( d, connection );
076
077        // Process the input file.
078        System.out.println( "Parsing commencing..." );
079        try
080        {
081            File srcfile = new File( filename );
082            d.parse( srcfile );
083        }
084        catch ( IOException ioe )
085        {
086            System.out.println( "Error reading input file:" + ioe.getMessage() );
087            System.exit( -1 );
088        }
089        catch ( SAXException se )
090        {
091            System.out.println( "Error parsing input file:" + se.getMessage() );
092            System.exit( -1 );
093        }
094
095        // And here there is nothing to do. The digester rules have
096        // (deliberately) not built a representation of the input, but
097        // instead processed the data as it was read.
098        System.out.println( "Parsing complete." );
099    }
100
101    private static void addRules( Digester d, java.sql.Connection conn )
102    {
103
104        // --------------------------------------------------
105        // when we encounter a "table" tag, do the following:
106
107        // Create a new instance of class Table, and push that
108        // object onto the digester stack of objects. We only need
109        // this so that when a row is inserted, it can find out what
110        // the enclosing tablename was.
111        //
112        // Note that the object is popped off the stack at the end of the
113        // "table" tag (normal behaviour for ObjectCreateRule). Because we
114        // never added the table object to some parent object, when it is
115        // popped off the digester stack it becomes garbage-collected. That
116        // is fine in this situation; we've done all the necessary work and
117        // don't need the table object any more.
118        d.addObjectCreate( "database/table", Table.class );
119
120        // Map *any* attributes on the table tag to appropriate
121        // setter-methods on the top object on the stack (the Table
122        // instance created by the preceeding rule). We only expect one
123        // attribute, though: a 'name' attribute specifying what table
124        // we are inserting rows into.
125        d.addSetProperties( "database/table" );
126
127        // --------------------------------------------------
128        // When we encounter a "row" tag, invoke methods on the provided
129        // RowInserterRule instance.
130        //
131        // This rule creates a Row instance and pushes it on the digester
132        // object stack, rather like ObjectCreateRule, so that the column
133        // tags have somewhere to store their information. And when the
134        // </row> end tag is found, the rule will trigger to remove this
135        // object from the stack, and also do an actual database insert.
136        //
137        // Note that the rule instance we are passing to the digester has
138        // been initialised with some useful data (the SQL connection).
139        //
140        // Note also that in this case we are not using the digester's
141        // factory methods to create the rule instance; that's just a
142        // convenience - and obviously not an option for Rule classes
143        // that are not part of the digester core implementation.
144        RowInserterRule rowInserterRule = new RowInserterRule( conn );
145        d.addRule( "database/table/row", rowInserterRule );
146
147        // --------------------------------------------------
148        // when we encounter a "column" tag, call setColumn on the top
149        // object on the stack, passing two parameters: the "name"
150        // attribute, and the text within the tag body.
151        d.addCallMethod( "database/table/row/column", "addColumn", 2 );
152        d.addCallParam( "database/table/row/column", 0, "name" );
153        d.addCallParam( "database/table/row/column", 1 );
154    }
155
156    private static void usage()
157    {
158        System.out.println( "Usage: java Main example.xml" );
159    }
160
161}