View Javadoc
1   package org.eclipse.birt.report.data.oda.jdbc;
2   
3   import java.sql.DatabaseMetaData;
4   import java.sql.SQLException;
5   import java.util.ArrayList;
6   import java.util.Collection;
7   import java.util.Enumeration;
8   import java.util.HashMap;
9   import java.util.Map;
10  import java.util.Properties;
11  import java.util.logging.Level;
12  import java.util.logging.Logger;
13  
14  import org.eclipse.birt.report.data.bidi.utils.core.BidiConstants;
15  import org.eclipse.birt.report.data.bidi.utils.core.BidiFormat;
16  import org.eclipse.birt.report.data.bidi.utils.core.BidiTransform;
17  import org.eclipse.birt.report.data.oda.i18n.ResourceConstants;
18  import org.eclipse.birt.report.data.oda.jdbc.bidi.BidiCallStatement;
19  import org.eclipse.birt.report.data.oda.jdbc.bidi.BidiStatement;
20  import org.eclipse.datatools.connectivity.oda.IConnection;
21  import org.eclipse.datatools.connectivity.oda.IDataSetMetaData;
22  import org.eclipse.datatools.connectivity.oda.IQuery;
23  import org.eclipse.datatools.connectivity.oda.OdaException;
24  
25  import com.ibm.icu.util.ULocale;
26  import org.kuali.ole.config.OLEReportDBConfig;
27  
28  /**
29   * Connection implements IConnection interface of ODA. It is a wrapper of JDBC
30   * Connection.
31   *
32   */
33  public class Connection implements IConnection
34  {
35      /** The JDBC Connection instance. */
36      protected java.sql.Connection jdbcConn = null;
37  
38      /** logger */
39      private static Logger logger = Logger.getLogger( Connection.class.getName( ) );
40  
41      // TODO: externalize
42      private static final String advancedDataType = "org.eclipse.birt.report.data.oda.jdbc.SPSelectDataSet";
43  
44      protected Map appContext;
45  
46      private Boolean autoCommit;
47      private int isolationMode = Constants.TRANSCATION_ISOLATION_DEFAULT;
48  
49      OLEReportDBConfig oleReportDBConfig = new OLEReportDBConfig();
50      /*
51       * @see org.eclipse.datatools.connectivity.oda.IConnection#isOpen()
52       */
53      public boolean isOpen( ) throws OdaException
54      {
55          return ( jdbcConn != null );
56      }
57  
58      /*
59       * @see org.eclipse.datatools.connectivity.oda.IConnection#open(java.util.Properties)
60       */
61      public void open( Properties connProperties ) throws OdaException
62      {
63          if ( this.appContext != null )
64          {
65              Object value = this.appContext.get( IConnectionFactory.PASS_IN_CONNECTION );
66              if ( value != null && ( value instanceof java.sql.Connection ) )
67              {
68                  jdbcConn = (java.sql.Connection) value;
69                  logger.logp( Level.FINER,
70                          Connection.class.getName( ),
71                          "open",
72                          jdbcConn.toString( ) );
73                  return;
74              }
75          }
76          if ( connProperties == null )
77          {
78              IllegalArgumentException e = new IllegalArgumentException( "connProperties cannot be null" );
79              logger.logp( Level.FINE,
80                      Connection.class.getName( ),
81                      "open",
82                      e.getMessage( ),
83                      e );
84              throw e;
85          }
86          // Log connection information
87          if ( logger.isLoggable( Level.FINE ) )
88          {
89              StringBuffer logMsg = new StringBuffer( "Connection.open(Properties). connProperties = " ); //$NON-NLS-1$
90              for ( Enumeration enumeration = connProperties.propertyNames( ); enumeration.hasMoreElements( ); )
91              {
92                  String propName = (String) enumeration.nextElement( );
93                  // Don't log value of any property that looks like a password
94                  String lcPropName = propName.toLowerCase( );
95                  String propVal;
96                  if ( lcPropName.indexOf( "password" ) >= 0 //$NON-NLS-1$
97                          || lcPropName.indexOf( "pwd" ) >= 0 ) //$NON-NLS-1$
98                      propVal = "***"; //$NON-NLS-1$
99                  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( 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( 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( 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( 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( 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( 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 }