001    //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/files_template.xml $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     - Department of Geography, University of Bonn -
006     and
007     - lat/lon GmbH -
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    package org.deegree.tools.xml;
037    
038    import java.io.File;
039    
040    import java.io.FileReader;
041    import java.io.StringReader;
042    import java.net.URI;
043    import java.sql.Connection;
044    import java.sql.ResultSet;
045    import java.sql.ResultSetMetaData;
046    import java.sql.Statement;
047    import java.util.ArrayList;
048    import java.util.LinkedHashMap;
049    import java.util.List;
050    import java.util.Map;
051    
052    import org.deegree.framework.log.ILogger;
053    import org.deegree.framework.log.LoggerFactory;
054    import org.deegree.framework.util.Pair;
055    import org.deegree.framework.util.StringTools;
056    import org.deegree.framework.xml.NamespaceContext;
057    import org.deegree.framework.xml.XMLFragment;
058    import org.deegree.framework.xml.XMLTools;
059    import org.deegree.framework.xml.XSLTDocument;
060    import org.deegree.io.DBConnectionPool;
061    import org.deegree.io.IODocument;
062    import org.deegree.io.JDBCConnection;
063    import org.deegree.io.datastore.sql.postgis.PGgeometryAdapter;
064    import org.deegree.model.crs.CoordinateSystem;
065    import org.deegree.model.spatialschema.GMLGeometryAdapter;
066    import org.deegree.model.spatialschema.Geometry;
067    import org.deegree.ogcbase.CommonNamespaces;
068    import org.postgis.PGgeometry;
069    import org.postgis.binary.BinaryParser;
070    import org.w3c.dom.Element;
071    
072    /**
073     * reads a relational database model and transforms it into XML. Via a XSL script it is possible to transform the XML
074     * into any other XML format. The program uses following configuration
075     * <pre>
076     * <?xml version="1.0" encoding="UTF-8"?>
077     * <dor:XMLMapping xmlns:dor="http://www.deegree.org/xmlmapping">
078     *     <JDBCConnection xmlns="http://www.deegree.org/jdbc">
079     *         <Driver>org.postgresql.Driver</Driver>
080     *         <Url>jdbc:postgresql://localhost:5432/csw</Url>
081     *         <User>aUser</User>
082     *         <Password>aPassword</Password>
083     *         <SecurityConstraints/>
084     *         <Encoding>iso-8859-1</Encoding>
085     *     </JDBCConnection>
086     *     <dor:XSLT>e:/temp/test.xsl</dor:XSLT>
087     *     <dor:Table>
088     *         <dor:ElementName>Metadata</dor:ElementName>
089     *         <dor:Select>Select * From cqp_main</dor:Select>
090     *         <dor:Table>
091     *             <dor:ElementName>domainconsistency</dor:ElementName>
092     *             <dor:Select>Select * From cqp_domainconsistency where fk_cqp_main = $ID</dor:Select>
093     *             <dor:Table>
094     *                 <dor:ElementName>specificationdate</dor:ElementName>
095     *                 <dor:Select>Select * From cqp_specificationdate where fk_cqp_domainconsistency = $ID</dor:Select>
096     *             </dor:Table>
097     *         </dor:Table>
098     *         <dor:Table>
099     *             <dor:ElementName>bbox</dor:ElementName>
100     *             <dor:Select>Select * From cqp_bbox where fk_cqp_main = $ID</dor:Select>
101     *             <dor:GeometryColumn crs="EPSG:4326">geom</dor:GeometryColumn>
102     *         </dor:Table>
103     *     </dor:Table>
104     * </dor:XMLMapping>
105     * </pre>
106     * 
107     * @author <a href="mailto:name@deegree.org">Andreas Poth</a>
108     * @author last edited by: $Author: admin $
109     * 
110     * @version $Revision: $, $Date: $
111     */
112    public class DatabaseXMLMapping {
113    
114        private static ILogger LOG = LoggerFactory.getLogger( DatabaseXMLMapping.class );
115    
116        private static URI namespace = URI.create( "http://www.deegree.org/xmlmapping" );
117    
118        private static NamespaceContext nsc;
119    
120        protected Table mainTable;
121    
122        private JDBCConnection jdbc;
123    
124        protected XSLTDocument xslt;
125    
126        /**
127         * 
128         * @param fileName
129         * @throws Exception
130         */
131        public DatabaseXMLMapping( String fileName ) throws Exception {
132            nsc = CommonNamespaces.getNamespaceContext();
133            nsc.addNamespace( "dxm", namespace );
134            readConfig( fileName );
135        }
136    
137        private void readConfig( String fileName )
138                                throws Exception {
139            FileReader fr = new FileReader( fileName );
140            XMLFragment xml = new XMLFragment( fr, XMLFragment.DEFAULT_URL );
141            // parse database connection info
142            Element jdbcElement = XMLTools.getRequiredElement( xml.getRootElement(), "dgjdbc:JDBCConnection", nsc );
143            IODocument ioDoc = new IODocument( jdbcElement );
144            jdbc = ioDoc.parseJDBCConnection();
145    
146            // xslt file to transform result of xml mapping
147            String xsltFileName = XMLTools.getNodeAsString( xml.getRootElement(), "./dxm:XSLT", nsc, null );
148            if ( xsltFileName != null ) {
149                xslt = new XSLTDocument( new File( xsltFileName ).toURL() );
150            }
151    
152            // parse table relations
153            String elementName = XMLTools.getRequiredNodeAsString( xml.getRootElement(), "./dxm:Table/dxm:ElementName", nsc );
154            String select = XMLTools.getRequiredNodeAsString( xml.getRootElement(), "./dxm:Table/dxm:Select", nsc );
155    
156            List<Pair<String, String>> geomFieldList = new ArrayList<Pair<String, String>>();
157            List<Element> elements = XMLTools.getElements( xml.getRootElement(), "./dxm:Table/dxm:GeometryColumn", nsc );
158            for ( Element element : elements ) {
159                String field = XMLTools.getStringValue( element ).toLowerCase();
160                String crs = element.getAttribute( "crs" );
161                Pair<String, String> p = new Pair<String, String>( field, crs );
162                geomFieldList.add( p );
163            }
164            mainTable = new Table( elementName, select, geomFieldList );
165            List<Element> tables = XMLTools.getElements( xml.getRootElement(), "./dxm:Table/dxm:Table", nsc );
166            for ( Element element : tables ) {
167                parseTable( mainTable, element );
168            }
169        }
170    
171        /**
172         * @param table
173         * @param element
174         */
175        private void parseTable( Table table, Element element )
176                                throws Exception {
177            String elementName = XMLTools.getRequiredNodeAsString( element, "dxm:ElementName", nsc );
178            String select = XMLTools.getRequiredNodeAsString( element, "dxm:Select", nsc );
179            List<Pair<String, String>> geomFieldList = new ArrayList<Pair<String, String>>();
180            List<Element> elements = XMLTools.getElements( element, "dxm:GeometryColumn", nsc );
181            for ( Element elem : elements ) {
182                String field = XMLTools.getStringValue( elem ).toLowerCase();
183                String crs = elem.getAttribute( "crs" );
184                Pair<String, String> p = new Pair<String, String>( field, crs );
185                geomFieldList.add( p );
186            }
187            Table subTable = new Table( elementName, select, geomFieldList );
188            table.getTables().add( subTable );
189            List<Element> tables = XMLTools.getElements( element, "dxm:Table", nsc );
190            for ( Element subEelement : tables ) {
191                parseTable( subTable, subEelement );
192            }
193        }
194    
195        public void run()
196                                throws Exception {
197            DBConnectionPool pool = DBConnectionPool.getInstance();
198            Connection conn = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
199            Statement stmt = null;
200            ResultSet rs = null;
201    
202            try {
203                String sql = mainTable.getSelect();
204                stmt = conn.createStatement();
205                rs = stmt.executeQuery( sql );
206                ResultSetMetaData rsmd = rs.getMetaData();
207                int colCount = rsmd.getColumnCount();
208    
209                Map<String, Object> row = new LinkedHashMap<String, Object>();
210                while ( rs.next() ) {
211                    // create one XML document for each row of the main table
212                    XMLFragment result = new XMLFragment();
213                    result.load( new StringReader( "<DatabaseTable/>" ), XMLFragment.DEFAULT_URL );
214                    // append root table element
215                    Element tableElement = XMLTools.appendElement( result.getRootElement(), null, mainTable.getName() );
216                    for ( int i = 0; i < colCount; i++ ) {
217                        String cName = rsmd.getColumnName( i + 1 ).toLowerCase();
218                        Object value = rs.getObject( i + 1 );
219                        row.put( cName, value );
220                        if ( value != null ) {
221                            Pair<String, CoordinateSystem> p = mainTable.getGeometryColumn( cName );
222                            if ( p != null ) {
223                                handleGeometry( tableElement, p, value );
224                            } else {
225                                XMLTools.appendElement( tableElement, null, cName, value.toString() );
226                            }
227                        } else {
228                            XMLTools.appendElement( tableElement, null, cName );
229                        }
230                    }
231                    // add sub tables if available
232                    List<Table> tables = mainTable.getTables();
233                    for ( Table table : tables ) {
234                        appendTable( tableElement, conn, row, table );
235                    }
236                    row.clear();
237                    if ( xslt != null ) {
238                        result = transform( result );
239                    }
240                    performAction( result );
241                }
242            } catch ( Exception e ) {
243                LOG.logError( e );
244                throw e;
245            } finally {
246                //rs.close();
247                stmt.close();
248                pool.releaseConnection( conn, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
249            }
250    
251        }
252    
253        /**
254         * 
255         * @param ps
256         * @param conn
257         * @param targetRow
258         * @param targetTable
259         * @throws Exception
260         */
261        private void appendTable( Element tableElement, Connection conn, Map<String, Object> targetRow, Table subTable )
262                                throws Exception {
263            Statement stmt = null;
264    
265            try {
266                String sql = subTable.getSelect();
267                List<String> variables = subTable.getVariables();
268                // replace variables with real values
269                for ( String variable : variables ) {
270                    Object value = targetRow.get( variable.substring( 1, variable.length() ).toLowerCase() );
271                    if ( value instanceof String ) {
272                        sql = StringTools.replace( sql, variable, "'" + value.toString() + "'", true );
273                    } else if ( value != null ) {
274                        sql = StringTools.replace( sql, variable, value.toString(), true );
275                    } else {
276                        sql = StringTools.replace( sql, variable, "'" + "XXXXXXXdummyXXXXXXX"+ "'", true );
277                    }
278                }
279                LOG.logDebug( sql );
280                stmt = conn.createStatement();
281                ResultSet rs = stmt.executeQuery( sql );
282                ResultSetMetaData rsmd = rs.getMetaData();
283                int colCount = rsmd.getColumnCount();
284    
285                Map<String, Object> row = new LinkedHashMap<String, Object>();
286                while ( rs.next() ) {
287                    // append sub table element
288                    Element subTableElement = XMLTools.appendElement( tableElement, null, subTable.getName() );
289                    for ( int i = 0; i < colCount; i++ ) {
290                        String cName = rsmd.getColumnName( i + 1 ).toLowerCase();
291                        Object value = rs.getObject( i + 1 );
292                        row.put( cName, value );
293                        if ( value != null ) {
294                            Pair<String, CoordinateSystem> p = subTable.getGeometryColumn( cName );
295                            if ( p != null ) {
296                                handleGeometry( subTableElement, p, value );
297                            } else {
298                                XMLTools.appendElement( subTableElement, null, cName, value.toString() );
299                            }
300                        } else {
301                            XMLTools.appendElement( subTableElement, null, cName );
302                        }
303                    }
304                    // recursion!
305                    // append sub tables if available
306                    List<Table> tables = subTable.getTables();
307                    for ( Table table : tables ) {
308                        appendTable( subTableElement, conn, row, table );
309                    }
310                    row.clear();
311                }
312            } catch ( Exception e ) {
313                LOG.logError( e );
314                throw e;
315            } finally {
316                stmt.close();
317            }
318        }
319    
320        /**
321         * @param tableElement
322         * @param p
323         * @param value
324         */
325        private void handleGeometry( Element tableElement, Pair<String, CoordinateSystem> p, Object value )
326                                throws Exception {
327            Geometry geom = null;
328            if ( jdbc.getDriver().toLowerCase().indexOf( "postgres" ) > -1 ) {
329                geom = PGgeometryAdapter.wrap( (PGgeometry) value, p.second );
330            } else if ( jdbc.getDriver().toLowerCase().indexOf( "oracle" ) > -1 ) {
331    //            JGeometry jGeometry = JGeometry.load( (STRUCT) value );
332    //            geom = JGeometryAdapter.wrap( jGeometry, p.second );
333            } else if ( jdbc.getDriver().toLowerCase().indexOf( "sqlserver" ) > -1 ) {
334            } else if ( jdbc.getDriver().toLowerCase().indexOf( "mysql" ) > -1 ) {
335                byte[] wkb = (byte[]) value;
336                org.postgis.Geometry pgGeometry = new BinaryParser().parse( wkb );
337                geom = PGgeometryAdapter.wrap( pgGeometry, p.second );
338            }
339            String s = GMLGeometryAdapter.export( geom ).toString();
340            s = StringTools.replace( s, ">", " xmlns:gml='http://www.opengis.net/gml'>", false );
341            XMLFragment xml = new XMLFragment( new StringReader( s ), XMLFragment.DEFAULT_URL );
342            Element element = XMLTools.appendElement( tableElement, null, p.first );
343            XMLTools.copyNode( xml.getRootElement().getOwnerDocument(), element );
344        }
345    
346        /**
347         * @param xml
348         * @return
349         */
350        protected XMLFragment transform( XMLFragment xml )
351                                throws Exception {        
352            return xslt.transform( xml );
353        }
354    
355        protected void performAction( XMLFragment xml ) {
356            System.out.println( xml.getAsString() );
357        }
358    
359        
360    }