001 //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/tools/datastore/DBSchemaToDatastoreConf.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53177 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.tools.datastore; 044 045 import java.io.BufferedReader; 046 import java.io.File; 047 import java.io.FileOutputStream; 048 import java.io.IOException; 049 import java.io.InputStreamReader; 050 import java.io.StringReader; 051 import java.net.URL; 052 import java.security.InvalidParameterException; 053 import java.sql.Connection; 054 import java.sql.ResultSet; 055 import java.sql.ResultSetMetaData; 056 import java.sql.SQLException; 057 import java.sql.Statement; 058 import java.util.Properties; 059 060 import javax.xml.transform.TransformerException; 061 062 import org.deegree.datatypes.Types; 063 import org.deegree.datatypes.UnknownTypeException; 064 import org.deegree.framework.log.ILogger; 065 import org.deegree.framework.log.LoggerFactory; 066 import org.deegree.framework.util.FileUtils; 067 import org.deegree.framework.util.StringTools; 068 import org.deegree.framework.xml.XMLFragment; 069 import org.deegree.io.DBConnectionPool; 070 import org.deegree.io.DBPoolException; 071 import org.deegree.io.dbaseapi.DBaseException; 072 import org.deegree.io.shpapi.HasNoDBaseFileException; 073 import org.deegree.io.shpapi.ShapeFile; 074 import org.xml.sax.SAXException; 075 076 /** 077 * Example: java -classpath .;deegree.jar;$databasedriver.jar 078 * org.deegree.tools.datastore.DBSchemaToDatastoreConf -tables mytable,myothertable -user dev 079 * -password dev -driver oracle.jdbc.OracleDriver -url jdbc:oracle:thin:@localhost:1521:devs -output 080 * e:/temp/schema.xsd<br> 081 * or for shapefile:<br> 082 * java -classpath .;deegree.jar org.deegree.tools.datastore.DBSchemaToDatastoreConf -url 083 * c:/data/myshape -driver SHAPE -output e:/temp/schema.xsd<br> 084 * 085 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 086 * @author last edited by: $Author: apoth $ 087 * 088 * @version $Revision: 8049 $, $Date: 2007-08-23 10:19:28 +0200 (Do, 23 Aug 2007) $ 089 */ 090 public class DBSchemaToDatastoreConf { 091 092 private static final ILogger LOG = LoggerFactory.getLogger( DBSchemaToDatastoreConf.class ); 093 094 private String[] tables; 095 096 private String user; 097 098 private String pw; 099 100 private String driver; 101 102 private String logon; 103 104 private String backend; 105 106 private String vendor; 107 108 private String srs; 109 110 /** 111 * 112 * @param tables 113 * list of table names used for one featuretype 114 * @param user 115 * database user 116 * @param pw 117 * users password 118 * @param driver 119 * database driver 120 * @param logon 121 * database URL/logon 122 * @param srs 123 * @throws IOException 124 */ 125 public DBSchemaToDatastoreConf( String[] tables, String user, String pw, String driver, String logon, String srs ) 126 throws IOException { 127 this.driver = driver; 128 this.logon = logon; 129 this.pw = pw; 130 this.user = user; 131 this.tables = tables; 132 if ( srs != null ) { 133 this.srs = srs; 134 } else { 135 this.srs = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERSRS" ), false ); 136 } 137 138 if ( driver.toUpperCase().indexOf( "ORACLE" ) > -1 ) { 139 backend = "ORACLE"; 140 vendor = backend; 141 } else if ( driver.toUpperCase().indexOf( "POSTGRES" ) > -1 ) { 142 backend = "POSTGIS"; 143 vendor = backend; 144 } else if ( driver.toUpperCase().contains( "SHAPE" ) ) { 145 backend = "SHAPE"; 146 vendor = backend; 147 } else { 148 backend = "GENERICSQL"; 149 vendor = getVendor( driver ); 150 } 151 152 } 153 154 private String getVendor( String driver ) { 155 // find out which database is used 156 String vendor = null; 157 if ( driver.toUpperCase().contains( "POSTGRES" ) ) { 158 backend = "POSTGRES"; 159 } else if ( driver.toUpperCase().contains( "SQLSERVER" ) ) { 160 backend = "SQLSERVER"; 161 } else if ( driver.toUpperCase().contains( "INGRES" ) || driver.equals( "ca.edbc.jdbc.EdbcDriver" ) ) { 162 backend = "INGRES"; 163 } else if ( driver.toUpperCase().contains( "HSQLDB" ) ) { 164 backend = "HSQLDB"; 165 } else { 166 backend = "SHAPE"; 167 } 168 return vendor; 169 } 170 171 /** 172 * creates a schema/datastore configuration for accessin database table through deegree WFS 173 * 174 * @return a schema/datastore configuration for accessin database table through deegree WFS 175 * @throws Exception 176 */ 177 public String run() 178 throws Exception { 179 StringBuffer sb = new StringBuffer( 5000 ); 180 181 if ( backend.equals( "SHAPE" ) ) { 182 handleShape( sb ); 183 } else { 184 handleDatabase( sb ); 185 } 186 printFooter( sb ); 187 188 return sb.toString(); 189 } 190 191 /** 192 * creates a datastore configuration for a database backend 193 * 194 * @param sb 195 * @throws DBPoolException 196 * @throws SQLException 197 * @throws Exception 198 * @throws UnknownTypeException 199 * @throws IOException 200 */ 201 private void handleDatabase( StringBuffer sb ) 202 throws DBPoolException, SQLException, Exception, UnknownTypeException, IOException { 203 printHeader( sb ); 204 205 for ( int k = 0; k < tables.length; k++ ) { 206 LOG.logInfo( "Opening JDBC connection with driver: " + driver ); 207 LOG.logInfo( "Opening JDBC connection to database : " + logon ); 208 209 Connection con = DBConnectionPool.getInstance().acquireConnection( driver, logon, user, pw ); 210 Statement stmt = con.createStatement(); 211 // ensure that we do not get a filled resultset because we just 212 // need the metainformation 213 LOG.logDebug( "read table: ", tables[k] ); 214 ResultSet rs = stmt.executeQuery( "select * from " + tables[k] + " where 1 = 2" ); 215 216 ResultSetMetaData rsmd = rs.getMetaData(); 217 int cols = rsmd.getColumnCount(); 218 219 printComplexHeader( sb, tables[k] ); 220 for ( int i = 0; i < cols; i++ ) { 221 if ( rsmd.getColumnType( i + 1 ) != 2004 ) { 222 int tp = rsmd.getColumnType( i + 1 ); 223 String tpn = Types.getTypeNameForSQLTypeCode( tp ); 224 LOG.logDebug( tables[k] + '.' + rsmd.getColumnName( i + 1 ) + ": " + tpn ); 225 // add property just if type != BLOB 226 printProperty( tables[k], rsmd.getColumnName( i + 1 ), rsmd.getColumnType( i + 1 ), tpn, 227 rsmd.getPrecision( i + 1 ), sb ); 228 } else { 229 String msg = StringTools.concat( 200, "skiped: ", tables[k], '.', rsmd.getColumnName( i + 1 ), 230 ": ", rsmd.getColumnTypeName( i + 1 ) ); 231 LOG.logDebug( msg ); 232 } 233 } 234 235 DBConnectionPool.getInstance().releaseConnection( con, driver, logon, user, pw ); 236 printComplexFooter( sb ); 237 } 238 } 239 240 /** 241 * creates a datastore configuration for a shapefile backend 242 * 243 * @param sb 244 * @throws IOException 245 * @throws Exception 246 * @throws HasNoDBaseFileException 247 * @throws DBaseException 248 * @throws DBPoolException 249 * @throws SQLException 250 * @throws UnknownTypeException 251 */ 252 private void handleShape( StringBuffer sb ) 253 throws IOException, Exception, HasNoDBaseFileException, DBaseException, DBPoolException, 254 SQLException, UnknownTypeException { 255 // TODO throw RE if tbl.len != 1 256 257 printShapeHeader( sb, tables[0] ); 258 259 File f = new File( tables[0] ); 260 ShapeFile shp = new ShapeFile( f.getAbsolutePath() ); 261 262 printComplexHeader( sb, f.getName() ); 263 264 String[] dataTypes = shp.getDataTypes(); 265 266 printProperty( f.getName(), "GEOM", 2002, "GEOM", -9999, sb ); 267 268 String[] props = shp.getProperties(); 269 for ( int i = 0; i < props.length; i++ ) { 270 int sqlCode = toSQLCode( dataTypes[i] ); 271 printProperty( tables[0], props[i], sqlCode, Types.getTypeNameForSQLTypeCode( sqlCode ), 272 toPrecision( dataTypes[i] ), sb ); 273 } 274 275 printComplexFooter( sb ); 276 277 shp.close(); 278 } 279 280 /** 281 * returns precision for a dBase numerical type 282 * 283 * @param dbfType 284 * @return 285 */ 286 private int toPrecision( String dbfType ) { 287 int precision = 0; 288 289 if ( dbfType.equalsIgnoreCase( "N" ) ) { 290 precision = 1; 291 } else if ( dbfType.equalsIgnoreCase( "F" ) ) { 292 precision = 2; 293 } 294 295 return precision; 296 } 297 298 /** 299 * returns the SQL type code for a dBase type char 300 * 301 * @param dbfType 302 * @return 303 */ 304 private int toSQLCode( String dbfType ) { 305 306 int type = -9999; 307 308 if ( dbfType.equalsIgnoreCase( "C" ) ) { 309 type = Types.VARCHAR; 310 } else if ( dbfType.equalsIgnoreCase( "F" ) || dbfType.equalsIgnoreCase( "N" ) ) { 311 type = Types.NUMERIC; 312 } else if ( dbfType.equalsIgnoreCase( "D" ) || dbfType.equalsIgnoreCase( "M" ) ) { 313 type = Types.DATE; 314 } else if ( dbfType.equalsIgnoreCase( "L" ) ) { 315 type = Types.BOOLEAN; 316 } else if ( dbfType.equalsIgnoreCase( "B" ) ) { 317 type = Types.BLOB; 318 } 319 320 if ( type == -9999 ) { 321 throw new RuntimeException( "Type '" + dbfType + "' is not suported." ); 322 } 323 324 return type; 325 } 326 327 /** 328 * adds the header of the configuration/schema for a database datastore 329 * 330 * @param sb 331 */ 332 private void printHeader( StringBuffer sb ) { 333 334 String s = DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "HEADER", backend, srs, driver, logon, user, pw ); 335 sb.append( s ); 336 337 } 338 339 /** 340 * adds the header of the configuration/schema for a shapefile datastore 341 * 342 * @param sb 343 * @param filename 344 * path to the shapefile 345 */ 346 private void printShapeHeader( StringBuffer sb, String filename ) { 347 348 String s = DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "SHAPEHEADER", filename, srs ); 349 sb.append( s ); 350 351 } 352 353 /** 354 * adds a header for a feature type to the schema 355 * 356 * @param sb 357 * @param table 358 * name of the table the feature type is assigned to 359 * @throws Exception 360 */ 361 private void printComplexHeader( StringBuffer sb, String table ) 362 throws Exception { 363 String idField = getPKeyName( table ); 364 String tp = "INTEGER"; 365 if ( backend.equals( "GENERICSQL" ) ) { 366 tp = "VARCHAR"; 367 } 368 String s = DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "COMPLEXHEADER", table, table, table, idField, 369 tp, table ); 370 sb.append( s ); 371 372 } 373 374 /** 375 * adds the footer of a feature type definition 376 * 377 * @param sb 378 */ 379 private void printComplexFooter( StringBuffer sb ) { 380 sb.append( DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "COMPLEXFOOTER" ) ); 381 } 382 383 /** 384 * prints XSD footer 385 * 386 * @param sb 387 */ 388 private void printFooter( StringBuffer sb ) { 389 sb.append( DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "FOOTER" ) ); 390 } 391 392 /** 393 * adds a property assigned to a database table field to the schema 394 * 395 * @param tableName 396 * table name 397 * @param name 398 * property name 399 * @param type 400 * xsd type name 401 * @param typeName 402 * SQL type name 403 * @param precision 404 * number precision if type is a number 405 * @param sb 406 * @throws SQLException 407 * @throws DBPoolException 408 * @throws IOException 409 */ 410 private void printProperty( String tableName, String name, int type, String typeName, int precision, StringBuffer sb ) 411 throws DBPoolException, SQLException, IOException { 412 413 String tp = Types.getXSDTypeForSQLType( type, precision ); 414 if ( !tp.startsWith( "gml:" ) ) { 415 tp = "xsd:" + tp; 416 } 417 418 if ( tp.equals( "gml:GeometryPropertyType" ) ) { 419 int srid = getSRID( tableName, name ); 420 String s = DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "GEOMPROPERTY", name.toLowerCase(), tp, 421 name, ""+srid ); 422 sb.append( s ); 423 } else { 424 String s = DBSchemaToDatastoreConfSQLXSDAccess.getXSDFragment( "PROPERTY", name.toLowerCase(), tp, name, 425 typeName.toUpperCase() ); 426 sb.append( s ); 427 } 428 } 429 430 /** 431 * 432 * @param tableName 433 * @param columnName 434 * @return 435 * @throws SQLException 436 * @throws DBPoolException 437 * @throws IOException 438 */ 439 private int getSRID( String tableName, String columnName ) 440 throws SQLException, DBPoolException, IOException { 441 int srid = -1; 442 String query = DBSchemaToDatastoreConfSQLSQLAccess.getSQLStatement( vendor + "_SRID", tableName.toUpperCase(), 443 columnName.toUpperCase() ); 444 LOG.logInfo( query ); 445 Connection con = null; 446 Statement stmt = null; 447 ResultSet rs = null; 448 if ( query != null && query.indexOf( "not found$" ) < 0 ) { 449 try { 450 con = DBConnectionPool.getInstance().acquireConnection( driver, logon, user, pw ); 451 stmt = con.createStatement(); 452 rs = stmt.executeQuery( query ); 453 454 while ( rs.next() ) { 455 srid = rs.getInt( 1 ); 456 } 457 458 if ( srid == 0 ) { 459 srid = -1; 460 } 461 462 } catch ( SQLException e ) { 463 System.out.println( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ERRORSRID" ) + e.getMessage() ); 464 System.out.println( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERFIELD" ) ); 465 } finally { 466 rs.close(); 467 stmt.close(); 468 DBConnectionPool.getInstance().releaseConnection( con, driver, logon, user, pw ); 469 } 470 } else { 471 System.out.println( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "NOSRIDQUERY" ) ); 472 } 473 if ( srid == -1 ) { 474 String tmp = DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERSRID" ); 475 srid = Integer.parseInt( readUserInput( tmp, false ) ); 476 } 477 return srid; 478 } 479 480 /** 481 * returns the name of the primary key of the passed table 482 * 483 * @param table 484 * @return the name of the primary key of the passed table 485 * @throws DBPoolException 486 * @throws SQLException 487 * @throws IOException 488 */ 489 private String getPKeyName( String table ) 490 throws DBPoolException, SQLException, IOException { 491 492 String query = DBSchemaToDatastoreConfSQLSQLAccess.getSQLStatement( vendor + "_ID", table.toUpperCase() ); 493 LOG.logInfo( query ); 494 Object id = null; 495 Statement stmt = null; 496 ResultSet rs = null; 497 if ( query != null && query.indexOf( "not found$" ) < 0 ) { 498 Connection con = DBConnectionPool.getInstance().acquireConnection( driver, logon, user, pw ); 499 try { 500 stmt = con.createStatement(); 501 rs = stmt.executeQuery( query ); 502 503 if ( rs.next() ) { 504 id = rs.getObject( 1 ); 505 } 506 } catch ( Exception e ) { 507 System.out.println( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ERRORPK" ) + e.getMessage() ); 508 System.out.println( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERFIELD" ) ); 509 } finally { 510 rs.close(); 511 stmt.close(); 512 DBConnectionPool.getInstance().releaseConnection( con, driver, logon, user, pw ); 513 } 514 } else { 515 System.out.println( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "NOPKQUERY" ) ); 516 } 517 if ( id == null ) { 518 id = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERPK" ), false ); 519 } 520 return id.toString(); 521 } 522 523 private static void validate( Properties map ) 524 throws InvalidParameterException, IOException { 525 if ( map.get( "-?" ) != null || map.get( "-h" ) != null || map.get( "-help" ) != null ) { 526 printHelp(); 527 System.exit( 1 ); 528 } 529 if ( map.get( "-tables" ) == null ) { 530 String s = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERTABLES" ), false ); 531 map.put( "-tables", s ); 532 } 533 534 if ( map.get( "-driver" ) == null ) { 535 String s = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERDRIVER" ), false ); 536 map.put( "-driver", s ); 537 } 538 539 if ( map.get( "-user" ) == null ) { 540 if ( !"SHAPE".equals( map.get( "-driver" ) ) ) { 541 String s = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERUSER" ), false ); 542 map.put( "-user", s ); 543 } 544 } 545 546 if ( map.get( "-password" ) == null ) { 547 map.put( "-password", " " ); 548 } 549 550 if ( map.get( "-url" ) == null && !"SHAPE".equalsIgnoreCase( (String) map.get( "-driver" ) ) ) { 551 String s = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTERURL" ), false ); 552 map.put( "-url", s ); 553 } 554 if ( map.get( "-output" ) == null ) { 555 String s = readUserInput( DBSchemaToDatastoreConfSQLMSGAccess.getMessage( "ENTEROUTPUT" ), false ); 556 map.put( "-output", s ); 557 } 558 } 559 560 private static void printHelp() 561 throws IOException { 562 URL url = DBSchemaToDatastoreConf.class.getResource( "DBSchemaToDatastoreConfHelp.txt" ); 563 System.out.println( FileUtils.readTextFile( url ) ); 564 } 565 566 /** 567 * @param args 568 * @throws IOException 569 * @throws SAXException 570 * @throws TransformerException 571 * @throws Exception 572 */ 573 public static void main( String[] args ) 574 throws Exception { 575 576 Properties map = new Properties(); 577 for ( int i = 0; i < args.length; ) { 578 String first = args[i++]; 579 if ( "?".equals( first ) || "-h".equals( first ) || "-help".equals( first ) ) { 580 printHelp(); 581 System.exit( 0 ); 582 } 583 map.put( first, args[i++] ); 584 } 585 586 try { 587 validate( map ); 588 } catch ( InvalidParameterException ipe ) { 589 LOG.logError( ipe.getMessage() ); 590 printHelp(); 591 System.exit( 1 ); 592 } 593 LOG.logDebug( "Resulting commandline arguments and their values {argument=value, ...}: " + map.toString() ); 594 String tmp = (String) map.get( "-tables" ); 595 String[] tables = StringTools.toArray( tmp, ",;|", true ); 596 String user = (String) map.get( "-user" ); 597 String pw = (String) map.get( "-password" ); 598 String driver = (String) map.get( "-driver" ); 599 String url = (String) map.get( "-url" ); 600 String output = (String) map.get( "-output" ); 601 String srs = (String) map.get( "-srs" ); 602 603 DBSchemaToDatastoreConf stc = new DBSchemaToDatastoreConf( tables, user, pw, driver, url, srs ); 604 String conf = null; 605 try { 606 conf = stc.run(); 607 } catch ( Exception e ) { 608 LOG.logError( e.getMessage(), e ); 609 System.exit( 1 ); 610 } 611 storeSchema( output, conf ); 612 System.exit( 0 ); 613 } 614 615 /** 616 * 617 * @param output 618 * @param conf 619 * @throws IOException 620 * @throws SAXException 621 * @throws TransformerException 622 */ 623 private static void storeSchema( String output, String conf ) 624 throws SAXException, IOException, TransformerException { 625 if ( conf != null ) { 626 XMLFragment xml = new XMLFragment(); 627 xml.load( new StringReader( conf ), XMLFragment.DEFAULT_URL ); 628 FileOutputStream fos = new FileOutputStream( output ); 629 xml.prettyPrint( fos ); 630 fos.close(); 631 } 632 } 633 634 /** 635 * This function prints a message on the command line and asks the user for an input, returns 636 * the text the User has typed, null otherwise 637 * 638 * @param describtion 639 * The message to be displayed to the user asking for a certain text to type 640 * @return the read text, or null if nothing was read 641 * @throws IOException 642 */ 643 private static String readUserInput( String describtion, boolean acceptNull ) 644 throws IOException { 645 646 String result = null; 647 do { 648 System.out.print( describtion ); 649 System.out.println( ':' ); 650 BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) ); 651 result = reader.readLine(); 652 } while ( !acceptNull && ( result == null || result.trim().length() == 0 ) ); 653 return result; 654 655 } 656 }