001package org.eclipse.birt.report.data.oda.jdbc; 002 003import java.sql.DatabaseMetaData; 004import java.sql.SQLException; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.Enumeration; 008import java.util.HashMap; 009import java.util.Map; 010import java.util.Properties; 011import java.util.logging.Level; 012import java.util.logging.Logger; 013 014import org.eclipse.birt.report.data.bidi.utils.core.BidiConstants; 015import org.eclipse.birt.report.data.bidi.utils.core.BidiFormat; 016import org.eclipse.birt.report.data.bidi.utils.core.BidiTransform; 017import org.eclipse.birt.report.data.oda.i18n.ResourceConstants; 018import org.eclipse.birt.report.data.oda.jdbc.bidi.BidiCallStatement; 019import org.eclipse.birt.report.data.oda.jdbc.bidi.BidiStatement; 020import org.eclipse.datatools.connectivity.oda.IConnection; 021import org.eclipse.datatools.connectivity.oda.IDataSetMetaData; 022import org.eclipse.datatools.connectivity.oda.IQuery; 023import org.eclipse.datatools.connectivity.oda.OdaException; 024 025import com.ibm.icu.util.ULocale; 026import org.kuali.ole.config.OLEReportDBConfig; 027 028/** 029 * Connection implements IConnection interface of ODA. It is a wrapper of JDBC 030 * Connection. 031 * 032 */ 033public class Connection implements IConnection 034{ 035 /** The JDBC Connection instance. */ 036 protected java.sql.Connection jdbcConn = null; 037 038 /** logger */ 039 private static Logger logger = Logger.getLogger( Connection.class.getName( ) ); 040 041 // TODO: externalize 042 private static final String advancedDataType = "org.eclipse.birt.report.data.oda.jdbc.SPSelectDataSet"; 043 044 protected Map appContext; 045 046 private Boolean autoCommit; 047 private int isolationMode = Constants.TRANSCATION_ISOLATION_DEFAULT; 048 049 OLEReportDBConfig oleReportDBConfig = new OLEReportDBConfig(); 050 /* 051 * @see org.eclipse.datatools.connectivity.oda.IConnection#isOpen() 052 */ 053 public boolean isOpen( ) throws OdaException 054 { 055 return ( jdbcConn != null ); 056 } 057 058 /* 059 * @see org.eclipse.datatools.connectivity.oda.IConnection#open(java.util.Properties) 060 */ 061 public void open( Properties connProperties ) throws OdaException 062 { 063 if ( this.appContext != null ) 064 { 065 Object value = this.appContext.get( IConnectionFactory.PASS_IN_CONNECTION ); 066 if ( value != null && ( value instanceof java.sql.Connection ) ) 067 { 068 jdbcConn = (java.sql.Connection) value; 069 logger.logp( Level.FINER, 070 Connection.class.getName( ), 071 "open", 072 jdbcConn.toString( ) ); 073 return; 074 } 075 } 076 if ( connProperties == null ) 077 { 078 IllegalArgumentException e = new IllegalArgumentException( "connProperties cannot be null" ); 079 logger.logp( java.util.logging.Level.FINE, 080 Connection.class.getName( ), 081 "open", 082 e.getMessage( ), 083 e ); 084 throw e; 085 } 086 // Log connection information 087 if ( logger.isLoggable( Level.FINE ) ) 088 { 089 StringBuffer logMsg = new StringBuffer( "Connection.open(Properties). connProperties = " ); //$NON-NLS-1$ 090 for ( Enumeration enumeration = connProperties.propertyNames( ); enumeration.hasMoreElements( ); ) 091 { 092 String propName = (String) enumeration.nextElement( ); 093 // Don't log value of any property that looks like a password 094 String lcPropName = propName.toLowerCase( ); 095 String propVal; 096 if ( lcPropName.indexOf( "password" ) >= 0 //$NON-NLS-1$ 097 || lcPropName.indexOf( "pwd" ) >= 0 ) //$NON-NLS-1$ 098 propVal = "***"; //$NON-NLS-1$ 099 else 100 { 101 propVal = connProperties.getProperty( propName ); 102 if ( lcPropName.equals( "odaurl" ) ) //$NON-NLS-1$ 103 { 104 propVal = LogUtil.encryptURL( propVal ); 105 } 106 } 107 108 logMsg.append( propName ) 109 .append( "=" ).append( propVal ).append( ";" ); //$NON-NLS-1$//$NON-NLS-2$ 110 } 111 logger.logp( Level.FINE, 112 Connection.class.getName( ), 113 "open", //$NON-NLS-1$ 114 logMsg.toString( ) ); 115 } 116 117 close( ); 118 119 String dataSource = connProperties.getProperty( Constants.ODADataSource ); 120 if ( dataSource != null ) 121 { 122 // TODO connect by DataSource 123 UnsupportedOperationException e = new UnsupportedOperationException( "Oda-jdbc:connect by data source" ); //$NON-NLS-1$ 124 logger.logp( java.util.logging.Level.FINE, 125 Connection.class.getName( ), 126 "open", //$NON-NLS-1$ 127 e.getMessage( ), 128 e ); 129 throw e; 130 } 131 else 132 { 133 if (hasBidiProperties (connProperties)){ 134 connProperties = bidiTransform (connProperties); 135 } 136 String url = oleReportDBConfig.getPropertyByKey(Constants.REPORT_DBA_URL); 137 String jndiName = connProperties.getProperty( Constants.ODAJndiName ); 138 139 String autoCommit = connProperties.getProperty( Constants.CONNECTION_AUTO_COMMIT ); 140 if ( autoCommit != null ) 141 { 142 this.autoCommit = Boolean.valueOf( autoCommit ); 143 } 144 145 String isolationMode = connProperties.getProperty( Constants.CONNECTION_ISOLATION_MODE ); 146 this.isolationMode = Constants.getIsolationMode( isolationMode ); 147 148 if ( (url == null || url.length( ) == 0) && (jndiName == null || 149 jndiName.length() == 0) ) 150 { 151 throw new JDBCException( ResourceConstants.DRIVER_MISSING_PROPERTIES, 152 ResourceConstants.ERROR_MISSING_PROPERTIES ); 153 } 154 connectByUrl( url, connProperties ); 155 } 156 logger.log(Level.FINER, "JDBC connection: " + jdbcConn + " is opened"); 157 updateAppContext (connProperties); 158 } 159 160 private boolean hasBidiProperties(Properties connProperties) { 161 if ((connProperties.containsKey(BidiConstants.CONTENT_FORMAT_PROP_NAME)) || 162 (connProperties.containsKey(BidiConstants.METADATA_FORMAT_PROP_NAME))) 163 return true; 164 return false; 165 } 166 167 private void updateAppContext(Properties connProperties) { 168 if (appContext == null) 169 appContext = new HashMap(); 170 appContext.put(Constants.CONNECTION_PROPERTIES_STR, connProperties); 171 } 172 173 /** 174 * Opens a JDBC connection using the specified url and connection properties 175 * @param connProperties 176 */ 177 protected void connectByUrl( String url, Properties connProperties ) 178 throws OdaException 179 { 180 assert connProperties != null; 181 assert url != null; 182 183 // Copy connProperties to props; skip property starting with 184 // "oda"; those are properties read by this driver 185 Properties props = new Properties( ); 186 for ( Enumeration enumeration = connProperties.propertyNames( ); enumeration.hasMoreElements( ); ) 187 { 188 String propName = (String) enumeration.nextElement( ); 189 if ( ! propName.startsWith( "oda" ) && ! propName.startsWith( "Oda" ) ) 190 { 191 props.setProperty( propName, 192 connProperties.getProperty( propName ) ); 193 } 194 } 195 196 // Read user name and password 197 String user = oleReportDBConfig.getPropertyByKey(Constants.REPORT_DBA_USERNAME); 198 String pwd = oleReportDBConfig.getPropertyByKey(Constants.REPORT_DBA_PASSWORD); 199 props = JDBCDriverManager.addUserAuthenticationProperties( 200 props, user, pwd ); 201 202 String driverClass = oleReportDBConfig.getPropertyByKey(Constants.REPORT_DBA_DRIVER); 203 String jndiNameUrl = connProperties.getProperty( Constants.ODAJndiName ); 204 205 try 206 { 207 if ( ( jndiNameUrl == null || jndiNameUrl.trim( ).length( ) == 0 ) 208 && ConnectionPoolFactory.getInstance( ) != null ) 209 { 210 jdbcConn = ConnectionPoolFactory.getInstance( ) 211 .getConnection( driverClass, 212 url, 213 props, 214 getDriverClassPath( ), 215 this.appContext ); 216 populateConnectionProp( ); 217 logger.log(Level.FINE, "JDBC connection success : " + jdbcConn ); 218 } 219 } 220 catch ( Exception e ) 221 { 222 if( e instanceof SQLException ) 223 { 224 SQLException e1 = (SQLException)e; 225 logger.log( Level.SEVERE, 226 "JDBC connection throws exception: Error Code " 227 + e1.getErrorCode( ) + " Message:" 228 + e1.getLocalizedMessage( ) ); 229 //First try to identify the authorization info. 28000 is xOpen standard for login failure 230 if( "28000".equals( e1.getSQLState( ))) 231 throw new JDBCException( ResourceConstants.CONN_CANNOT_GET, e1 ); 232 } 233 else 234 { 235 logger.log( Level.SEVERE, "JDBC connection throws exception: " + e.getLocalizedMessage( ) ); 236 } 237 } 238 try 239 { 240 241 if ( jdbcConn == null ) 242 { 243 jdbcConn = JDBCDriverManager.getInstance( ) 244 .getConnection( driverClass, 245 url, 246 jndiNameUrl, 247 props, 248 getDriverClassPath( ) ); 249 250 populateConnectionProp( ); 251 } 252 } 253 catch ( SQLException e ) 254 { 255 throw new JDBCException( ResourceConstants.CONN_CANNOT_GET, e ); 256 } 257 } 258 259 private void populateConnectionProp( ) throws SQLException 260 { 261 if( jdbcConn!= null ) 262 { 263 if( this.autoCommit != null ) 264 jdbcConn.setAutoCommit( this.autoCommit ); 265 else 266 { 267 if (DBConfig.getInstance().qualifyPolicy( 268 jdbcConn.getMetaData().getDriverName(), 269 DBConfig.SET_COMMIT_TO_FALSE) ) { 270 this.autoCommit = false; 271 jdbcConn.setAutoCommit( false ); 272 } 273 } 274 if( this.isolationMode!= Constants.TRANSCATION_ISOLATION_DEFAULT) 275 jdbcConn.setTransactionIsolation( this.isolationMode ); 276 } 277 } 278 279 @SuppressWarnings("unchecked") 280 protected Collection<String> getDriverClassPath( ) 281 { 282 if ( this.appContext == null ) 283 return null; 284 285 if ( this.appContext.get( IConnectionFactory.DRIVER_CLASSPATH ) == null ) 286 return null; 287 288 Object classPath = this.appContext.get( IConnectionFactory.DRIVER_CLASSPATH ); 289 290 if ( classPath instanceof String ) 291 { 292 ArrayList<String> result = new ArrayList<String>(); 293 result.add( classPath.toString( ) ); 294 return result; 295 } 296 else if ( classPath instanceof Collection ) 297 { 298 ArrayList<String> result = new ArrayList<String>(); 299 for( Object aClassPath: (Collection)classPath ) 300 { 301 if( aClassPath!= null ) 302 result.add( aClassPath.toString( ) ); 303 } 304 return result; 305 } 306 307 return null; 308 } 309 310 /* 311 * @see org.eclipse.datatools.connectivity.oda.IConnection#getMetaData(java.lang.String) 312 */ 313 public IDataSetMetaData getMetaData( String dataSetType ) 314 throws OdaException 315 { 316 logger.logp( java.util.logging.Level.FINEST, 317 Connection.class.getName( ), 318 "getMetaData", 319 "Connection.getMetaData(" + dataSetType + ")" ); 320 // Only one data source type, ignoring the argument. 321 DatabaseMetaData dbMetadata = null; 322 if ( jdbcConn != null ) 323 { 324 try 325 { 326 dbMetadata = jdbcConn.getMetaData( ); 327 } 328 catch ( SQLException e ) 329 { 330 throw new JDBCException( ResourceConstants.CONN_CANNOT_GET_METADATA, 331 e ); 332 } 333 } 334 return new DataSourceMetaData( this, dbMetadata ); 335 } 336 337 /* 338 * @see org.eclipse.datatools.connectivity.oda.IConnection#newQuery(java.lang.String) 339 */ 340 public IQuery newQuery( String dataSourceType ) throws OdaException 341 { 342 logger.logp( java.util.logging.Level.FINER, 343 Connection.class.getName( ), 344 "createStatement", 345 "Connection.createStatement(" + dataSourceType + ")" ); 346 347 // only one data source type, ignoring the argument. 348 assertOpened( ); 349 if ( dataSourceType != null 350 && dataSourceType.equalsIgnoreCase( advancedDataType ) ) 351 return createCallStatement( jdbcConn ); 352 else 353 return createStatement( jdbcConn ); 354 } 355 356 private IQuery createCallStatement(java.sql.Connection jdbcConn2) throws OdaException { 357 if ((appContext != null) && 358 (appContext.get(Constants.CONNECTION_PROPERTIES_STR) != null)){ 359 Properties props = (Properties)appContext.get(Constants.CONNECTION_PROPERTIES_STR); 360 if (hasBidiProperties(props)) 361 return new BidiCallStatement(jdbcConn, props); 362 } 363 return new CallStatement( jdbcConn ); 364 } 365 366 protected IQuery createStatement(java.sql.Connection jdbcConn) throws OdaException { 367 if ((appContext != null) && 368 (appContext.get(Constants.CONNECTION_PROPERTIES_STR) != null)){ 369 Properties props = (Properties)appContext.get(Constants.CONNECTION_PROPERTIES_STR); 370 if (hasBidiProperties(props)) 371 return new BidiStatement(jdbcConn, props); 372 } 373 return new Statement( jdbcConn ); 374 } 375 376 /* 377 * @see org.eclipse.datatools.connectivity.oda.IConnection#commit() 378 */ 379 public void commit( ) throws OdaException 380 { 381 logger.logp( java.util.logging.Level.FINEST, 382 Connection.class.getName( ), 383 "commit", 384 "Connection.commit()" ); 385 assertOpened( ); 386 try 387 { 388 jdbcConn.commit( ); 389 } 390 catch ( SQLException e ) 391 { 392 throw new JDBCException( ResourceConstants.CONN_COMMIT_ERROR, e ); 393 } 394 } 395 396 /* 397 * @see org.eclipse.datatools.connectivity.oda.IConnection#rollback() 398 */ 399 public void rollback( ) throws OdaException 400 { 401 logger.logp( java.util.logging.Level.FINEST, 402 Connection.class.getName( ), 403 "rollback", 404 "Connection.rollback()" ); 405 assertOpened( ); 406 try 407 { 408 jdbcConn.rollback( ); 409 } 410 catch ( SQLException e ) 411 { 412 throw new JDBCException( ResourceConstants.CONN_ROLLBACK_ERROR, e ); 413 } 414 } 415 416 /* 417 * @see org.eclipse.datatools.connectivity.oda.IConnection#getMaxQueries() 418 */ 419 public int getMaxQueries( ) throws OdaException 420 { 421 if ( jdbcConn != null ) 422 { 423 try 424 { 425 DatabaseMetaData dbMetadata = jdbcConn.getMetaData( ); 426 int maxstmts = dbMetadata.getMaxStatements( ); 427 int maxconns = dbMetadata.getMaxConnections( ); 428 if ( maxstmts == 0 && maxconns == 0 ) 429 return 0; 430 else if ( maxconns == 0 || maxstmts < maxconns ) 431 return 1; 432 else 433 return maxstmts / maxconns; 434 } 435 catch ( SQLException e ) 436 { 437 return 1; 438 } 439 } 440 441 return 0; 442 } 443 444 /* 445 * @see org.eclipse.datatools.connectivity.oda.IConnection#close() 446 */ 447 public void close( ) throws OdaException 448 { 449 logger.logp( java.util.logging.Level.FINEST, 450 Connection.class.getName( ), 451 "close", //$NON-NLS-1$ 452 "Connection closed." ); //$NON-NLS-1$ 453 if ( jdbcConn == null ) 454 { 455 return; 456 } 457 try 458 { 459 if ( this.appContext != null && jdbcConn != null ) 460 { 461 Object option = this.appContext.get( IConnectionFactory.CLOSE_PASS_IN_CONNECTION ); 462 boolean closePassInConnection = ( option instanceof Boolean ) 463 ? ( (Boolean) option ).booleanValue( ) : true; 464 if ( !closePassInConnection ) 465 return; 466 } 467 468 if ( jdbcConn.isClosed( ) == false ) 469 { 470 jdbcConn.close( ); 471 logger.log(Level.FINE, "JDBC connection: " + jdbcConn + " is closed"); 472 } 473 else 474 { 475 logger.log(Level.FINER, "JDBC connection: " + jdbcConn + " is already closed outside of JDBC ODA driver"); 476 } 477 } 478 catch ( SQLException e ) 479 { 480 try 481 { 482 if (DBConfig.getInstance().qualifyPolicy( 483 jdbcConn.getMetaData().getDriverName(), 484 DBConfig.IGNORE_UNIMPORTANT_EXCEPTION)) 485 return; 486 if ( this.autoCommit == Boolean.FALSE && DBConfig.getInstance().qualifyPolicy( jdbcConn.getMetaData().getDriverName(), DBConfig.TRY_COMMIT_THEN_CLOSE )) 487 { 488 jdbcConn.commit(); 489 jdbcConn.close(); 490 return; 491 } 492 } 493 catch (SQLException e1) { 494 495 } 496 throw new JDBCException( ResourceConstants.CONN_CANNOT_CLOSE, e ); 497 } 498 jdbcConn = null; 499 } 500 501 /* 502 * @see org.eclipse.datatools.connectivity.oda.IConnection#setAppContext(java.lang.Object) 503 */ 504 public void setAppContext( Object context ) throws OdaException 505 { 506 if( context instanceof Map ) 507 this.appContext = (Map) context; 508 } 509 510 /** 511 * Returns the application context Map set by {@link #setAppContext(Object)}. 512 * @return the application context Map; may be null if none was set 513 * @since 3.7.2 514 */ 515 protected Map<?,?> getAppContextMap() 516 { 517 return this.appContext; 518 } 519 520 /** 521 * Assert the connection is opened. 522 * 523 * @throws JDBCException 524 */ 525 private void assertOpened( ) throws OdaException 526 { 527 if ( jdbcConn == null ) 528 { 529 throw new JDBCException( ResourceConstants.DRIVER_NO_CONNECTION, 530 ResourceConstants.ERROR_NO_CONNECTION ); 531 } 532 } 533 534 /* (non-Javadoc) 535 * @see org.eclipse.datatools.connectivity.oda.IConnection#setLocale(com.ibm.icu.util.ULocale) 536 */ 537 public void setLocale( ULocale locale ) throws OdaException 538 { 539 // TODO Auto-generated method stub 540 throw new UnsupportedOperationException(); 541 } 542 543 private Properties bidiTransform( Properties connectionProperties ) 544 { 545 if ( connectionProperties == null ) 546 { 547 return null; 548 } 549 Properties p = new Properties( ); 550 String metadataBidiFormatStr = connectionProperties.getProperty(BidiConstants.METADATA_FORMAT_PROP_NAME); 551 if (!BidiFormat.isValidBidiFormat(metadataBidiFormatStr)) 552 return connectionProperties; 553 554 for ( Enumeration enumeration = connectionProperties.propertyNames( ); enumeration.hasMoreElements( ); ) 555 { 556 String propName = (String) enumeration.nextElement( ); 557 String propValue = connectionProperties.getProperty( propName ); 558 if ( (Constants.ODAUser.equals( propName ) || Constants.ODAPassword.equals( propName )) 559 && propValue != null ) 560 { 561 p.put( propName, BidiTransform.transform( propValue, BidiConstants.DEFAULT_BIDI_FORMAT_STR, metadataBidiFormatStr) ); 562 } 563 else if (Constants.ODAURL.equals( propName )) 564 { 565 p.put( propName, BidiTransform.transformURL( propValue, BidiConstants.DEFAULT_BIDI_FORMAT_STR, metadataBidiFormatStr) ); 566 } 567 else 568 { 569 p.put( propName, propValue ); 570 } 571 } 572 return p; 573 } 574 575 /** 576 * define constants ODAURL, ODAPassword, ODAUser, ODADriverClass, ODADataSource 577 */ 578 public static class Constants 579 { 580 public static final String REPORT_DBA_USERNAME = "report.dba.username"; 581 public static final String REPORT_DBA_PASSWORD = "report.dba.password"; 582 public static final String REPORT_DBA_DRIVER = "report.dba.driver"; 583 public static final String REPORT_DBA_URL = "report.dba.url"; 584 public static final String ODAURL = "odaURL"; 585 public static final String ODAPassword = "odaPassword"; 586 public static final String ODAUser = "odaUser"; 587 public static final String ODADriverClass = "odaDriverClass"; 588 public static final String ODADataSource = "odaDataSource"; 589 public static final String ODAJndiName = "odaJndiName"; 590 public static final String CONNECTION_AUTO_COMMIT = "odaAutoCommit"; 591 public static final String CONNECTION_ISOLATION_MODE = "odaIsolationMode"; 592 public static final int TRANSCATION_ISOLATION_DEFAULT = -1; 593 public static final String TRANSACTION_READ_COMMITTED = "read-committed"; 594 public static final String TRANSACTION_READ_UNCOMMITTED = "read-uncommitted"; 595 public static final String TRANSACTION_REPEATABLE_READ = "repeatable-read"; 596 public static final String TRANSACTION_SERIALIZABLE = "serializable"; 597 public static final String CONNECTION_PROPERTIES_STR = "connectionProperties"; 598 public static final String ODAResourceIdentiers = "odaResourceIdentifiers"; 599 600 public static int getIsolationMode( String value ) 601 { 602 if( value == null ) 603 return TRANSCATION_ISOLATION_DEFAULT; 604 if( TRANSACTION_READ_COMMITTED.equals( value )) 605 return java.sql.Connection.TRANSACTION_READ_COMMITTED; 606 if( TRANSACTION_READ_UNCOMMITTED.equals( value )) 607 return java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; 608 if( TRANSACTION_REPEATABLE_READ.equals( value )) 609 return java.sql.Connection.TRANSACTION_REPEATABLE_READ; 610 if( TRANSACTION_SERIALIZABLE.equals( value )) 611 return java.sql.Connection.TRANSACTION_SERIALIZABLE; 612 return Integer.parseInt( value ); 613 } 614 } 615 616}