/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.jdbc; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection; import javax.sql.XAConnection; import javax.sql.XADataSource; import org.teiid.client.RequestMessage; import org.teiid.net.TeiidURL; /** * The Teiid JDBC DataSource implementation class of {@link javax.sql.DataSource} and * {@link javax.sql.XADataSource}. * <p> * The {@link javax.sql.DataSource} interface follows the JavaBean design pattern, * meaning the implementation class has <i>properties</i> that are accessed with getter methods * and set using setter methods, and where the getter and setter methods follow the JavaBean * naming convention (e.g., <code>get</code><i>PropertyName</i><code>() : </code><i>PropertyType</i> * and <code>set</code><i>PropertyName</i><code>(</code><i>PropertyType</i><code>) : void</code>). * </p> * The {@link javax.sql.XADataSource} interface is almost identical to the {@link javax.sql.DataSource} * interface, but rather than returning {@link java.sql.Connection} instances, there are methods that * return {@link javax.sql.XAConnection} instances that can be used with distributed transactions. */ public abstract class BaseDataSource extends WrapperImpl implements javax.sql.DataSource, XADataSource, ConnectionPoolDataSource, java.io.Serializable { public static final String DEFAULT_APP_NAME = "JDBC"; //$NON-NLS-1$ // constant indicating Virtual database name public static final String VDB_NAME = TeiidURL.JDBC.VDB_NAME; // constant indicating Virtual database version public static final String VDB_VERSION = TeiidURL.JDBC.VDB_VERSION; // constant for vdb version part of serverURL public static final String VERSION = TeiidURL.JDBC.VERSION; // name of the application which is obtaining connection public static final String APP_NAME = TeiidURL.CONNECTION.APP_NAME; // constant for username part of url public static final String USER_NAME = TeiidURL.CONNECTION.USER_NAME; // constant for password part of url public static final String PASSWORD = TeiidURL.CONNECTION.PASSWORD; protected static final int DEFAULT_TIMEOUT = 0; protected static final int DEFAULT_LOG_LEVEL = 0; /** * The name of the virtual database on a particular Teiid Server. * This property name is one of the standard property names defined by the JDBC 2.0 specification, * and is <i>required</i>. */ private String databaseName; /** * The logical name for the underlying <code>XADataSource</code> or * <code>ConnectionPoolDataSource</code>; * used only when pooling connections or distributed transactions are implemented. * This property name is one of the standard property names defined by the JDBC 2.0 specification, * and is <i>optional</i>. */ private String dataSourceName; /** * The description of this data source. * This property name is one of the standard property names defined by the JDBC 2.0 specification, * and is <i>optional</i>. */ private String description; /** * The user's name. * This property name is one of the standard property names defined by the JDBC 2.0 specification, * and is <i>required</i>. */ private String user; /** * The user's password. * This property name is one of the standard property names defined by the JDBC 2.0 specification, * and is <i>required</i>. */ private String password; /** * The version number of the virtual database to which a connection is to be established. * This property is <i>optional</i>; if not specified, the assumption is that the latest version * on the Teiid Server is to be used. */ private String databaseVersion; /** * The name of the application. Supplying this property may allow an administrator of a * Teiid Server to better identify individual connections and usage patterns. * This property is <i>optional</i>. */ private String applicationName; /** Support partial results mode or not.*/ private String partialResultsMode; /** Default fetch size, <= 0 indicates not set. */ private int fetchSize = BaseDataSource.DEFAULT_FETCH_SIZE; /** Whether to use result set cache if it available **/ private String resultSetCacheMode; /** * The number of milliseconds before timing out. * This property is <i>optional</i> and defaults to "0" (meaning no time out). */ private int loginTimeout; private String showPlan; private boolean noExec; private String disableLocalTxn; private String transactionAutoWrap; private boolean ansiQuotedIdentifiers = true; private int queryTimeout; private boolean useJDBC4ColumnNameAndLabelSemantics = true; /** * Reference to the logWriter, which is transient and is therefore not serialized with the DataSource. */ private transient PrintWriter logWriter; public static final String JDBC = "jdbc:"; //$NON-NLS-1$ // Default execution property constants protected static final int DEFAULT_FETCH_SIZE = RequestMessage.DEFAULT_FETCH_SIZE; protected static final String DEFAULT_PARTIAL_RESULTS_MODE = "FALSE"; //$NON-NLS-1$ protected static final String DEFAULT_RESULT_SET_CACHE_MODE = "FALSE"; //$NON-NLS-1$ /** * Transaction auto wrap constant - never wrap a command execution in a transaction * and allow multi-source updates to occur outside of a transaction. */ public static final String TXN_WRAP_OFF = ExecutionProperties.TXN_WRAP_OFF; /** * Transaction auto wrap constant - always wrap every non-transactional command * execution in a transaction. */ public static final String TXN_WRAP_ON = ExecutionProperties.TXN_WRAP_ON; /** * Transaction auto wrap constant - checks if a command * requires a transaction and will be automatically wrap it. */ public static final String TXN_WRAP_AUTO = ExecutionProperties.TXN_WRAP_DETECT; /** * String to hold additional properties that are not represented with an explicit getter/setter */ private String additionalProperties; /** * Constructor for MMDataSource. */ public BaseDataSource() { this.loginTimeout = DEFAULT_TIMEOUT; } // -------------------------------------------------------------------------------------------- // H E L P E R M E T H O D S // -------------------------------------------------------------------------------------------- protected Properties buildProperties(final String userName, final String password) { Properties props = new Properties(); props.setProperty(BaseDataSource.VDB_NAME,this.getDatabaseName()); if ( this.getDatabaseVersion() != null && this.getDatabaseVersion().trim().length() != 0 ) { props.setProperty(BaseDataSource.VDB_VERSION,this.getDatabaseVersion()); } if ( userName != null && userName.trim().length() != 0) { props.setProperty(BaseDataSource.USER_NAME, userName); } else if ( this.getUser() != null && this.getUser().trim().length() != 0) { props.setProperty(BaseDataSource.USER_NAME, this.getUser()); } if ( password != null && password.trim().length() != 0) { props.setProperty(BaseDataSource.PASSWORD, password); } else if ( this.getPassword() != null && this.getPassword().trim().length() != 0) { props.setProperty(BaseDataSource.PASSWORD, this.getPassword()); } if ( this.getApplicationName() != null && this.getApplicationName().trim().length() != 0 ) { props.setProperty(BaseDataSource.APP_NAME,this.getApplicationName()); } if (this.getPartialResultsMode() != null && this.getPartialResultsMode().trim().length() != 0) { props.setProperty(ExecutionProperties.PROP_PARTIAL_RESULTS_MODE, this.getPartialResultsMode()); } if(this.getFetchSize() > 0) { props.setProperty(ExecutionProperties.PROP_FETCH_SIZE, String.valueOf(this.getFetchSize())); } if (this.getQueryTimeout() > 0) { props.setProperty(ExecutionProperties.QUERYTIMEOUT, String.valueOf(this.getQueryTimeout())); } if (this.getResultSetCacheMode() != null && this.getResultSetCacheMode().trim().length() != 0) { props.setProperty(ExecutionProperties.RESULT_SET_CACHE_MODE, this.getResultSetCacheMode()); } if (this.getShowPlan() != null) { props.setProperty(ExecutionProperties.SQL_OPTION_SHOWPLAN, this.getShowPlan()); } if (this.isNoExec()) { props.setProperty(ExecutionProperties.NOEXEC, String.valueOf(this.isNoExec())); } if ( this.getAutoCommitTxn() != null && this.getAutoCommitTxn().trim().length() != 0 ) { props.setProperty(ExecutionProperties.PROP_TXN_AUTO_WRAP, this.getAutoCommitTxn()); } if (this.getDisableLocalTxn() != null) { props.setProperty(ExecutionProperties.DISABLE_LOCAL_TRANSACTIONS, this.getDisableLocalTxn()); } if (!this.getUseJDBC4ColumnNameAndLabelSemantics()) { props.setProperty(ExecutionProperties.JDBC4COLUMNNAMEANDLABELSEMANTICS, Boolean.FALSE.toString()); } if (this.additionalProperties != null) { JDBCURL.parseConnectionProperties(this.additionalProperties, props); } return props; } protected void validateProperties( final String userName, final String password) throws java.sql.SQLException { String reason = reasonWhyInvalidApplicationName(this.applicationName); if ( reason != null ) { throw new SQLException(reason); } reason = reasonWhyInvalidDatabaseName(this.databaseName); if ( reason != null ) { throw new SQLException(reason); } reason = reasonWhyInvalidDatabaseVersion(this.databaseVersion); if ( reason != null ) { throw new SQLException(reason); } reason = reasonWhyInvalidDataSourceName(this.dataSourceName); if ( reason != null ) { throw new SQLException(reason); } reason = reasonWhyInvalidDescription(this.description); if ( reason != null ) { throw new SQLException(reason); } final String pwd = password != null ? password : getPassword(); reason = reasonWhyInvalidPassword(pwd); if ( reason != null ) { throw new SQLException(reason); } reason = reasonWhyInvalidPartialResultsMode(this.partialResultsMode); if (reason != null) { throw new SQLException(reason); } reason = reasonWhyInvalidFetchSize(this.fetchSize); if (reason != null) { throw new SQLException(reason); } final String user = userName != null ? userName : getUser(); reason = reasonWhyInvalidUser(user); if ( reason != null ) { throw new SQLException(reason); } reason = reasonWhyInvalidTransactionAutoWrap(this.transactionAutoWrap); if ( reason != null ) { throw new SQLException(reason); } if (this.queryTimeout < 0) { throw new TeiidSQLException(JDBCPlugin.Util.getString("MMStatement.Bad_timeout_value")); //$NON-NLS-1$ } } // -------------------------------------------------------------------------------------------- // D A T A S O U R C E M E T H O D S // -------------------------------------------------------------------------------------------- /** * Attempt to establish a database connection. * @return a Connection to the database * @throws java.sql.SQLException if a database-access error occurs * @see javax.sql.DataSource#getConnection() */ public Connection getConnection() throws java.sql.SQLException { return getConnection(null,null); } /** * @see javax.sql.XADataSource#getXAConnection() */ public XAConnection getXAConnection() throws SQLException { return getXAConnection(null,null); } public PooledConnection getPooledConnection() throws SQLException { return getPooledConnection(null, null); } public PooledConnection getPooledConnection(final String userName, final String password) throws SQLException { return getXAConnection(userName, password); } // -------------------------------------------------------------------------------------------- // P R O P E R T Y M E T H O D S // -------------------------------------------------------------------------------------------- public String getDisableLocalTxn() { return disableLocalTxn; } public void setDisableLocalTxn(String disableLocalTxn) { this.disableLocalTxn = disableLocalTxn; } /** * Get the log writer for this data source. * <p> * The log writer is a character output stream to which all logging and tracing * messages for this data source object instance will be printed. This includes * messages printed by the methods of this object, messages printed by methods * of other objects manufactured by this object, and so on. Messages printed * to a data source specific log writer are not printed to the log writer * associated with the {@link java.sql.DriverManager} class. When a DataSource object is * created the log writer is initially null, in other words, logging is disabled. * @return the log writer for this data source, null if disabled * @throws java.sql.SQLException if a database-access error occurs * @see javax.sql.DataSource#getLogWriter() */ public PrintWriter getLogWriter() throws java.sql.SQLException{ return this.logWriter; } /** * Gets the maximum time in seconds that this data source can wait while attempting * to connect to a database. A value of zero means that the timeout is the default * system timeout if there is one; otherwise it means that there is no timeout. * When a DataSource object is created the login timeout is initially zero. * @return the data source login time limit * @throws java.sql.SQLException if a database-access error occurs * @see javax.sql.DataSource#getLoginTimeout() */ public int getLoginTimeout() { return this.loginTimeout; } /** * Set the log writer for this data source. * <p> * The log writer is a character output stream to which all logging and tracing * messages for this data source object instance will be printed. This includes * messages printed by the methods of this object, messages printed by methods * of other objects manufactured by this object, and so on. Messages printed * to a data source specific log writer are not printed to the log writer * associated with the {@link java.sql.DriverManager} class. When a DataSource object is * created the log writer is initially null, in other words, logging is disabled. * @param writer the log writer for this data source, null if disabled * @throws java.sql.SQLException if a database-access error occurs * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter) */ public void setLogWriter( final PrintWriter writer) throws java.sql.SQLException{ this.logWriter = writer; } /** * Sets the maximum time in seconds that this data source can wait while attempting * to connect to a database. A value of zero means that the timeout is the default * system timeout if there is one; otherwise it means that there is no timeout. * When a DataSource object is created the login timeout is initially zero. * @param timeOut the data source login time limit * @throws java.sql.SQLException if a database-access error occurs * @see javax.sql.DataSource#setLoginTimeout(int) */ public void setLoginTimeout( final int timeOut) throws java.sql.SQLException { this.loginTimeout = timeOut; } /** * Returns the name of the application. Supplying this property may allow an administrator of a * Teiid Server to better identify individual connections and usage patterns. * This property is <i>optional</i>. * @return String the application name; may be null or zero-length */ public String getApplicationName() { return applicationName!=null?applicationName:DEFAULT_APP_NAME; } /** * Returns the name of the virtual database on a particular Teiid Server. * @return String */ public String getDatabaseName() { return databaseName; } /** * Returns the databaseVersion. * @return String */ public String getDatabaseVersion() { return databaseVersion; } /** * Returns the logical name for the underlying <code>XADataSource</code> or * <code>ConnectionPoolDataSource</code>; * used only when pooling connections or distributed transactions are implemented. * @return the logical name for the underlying data source; may be null */ public String getDataSourceName() { return dataSourceName; } /** * Returns the description of this data source. * @return the description; may be null */ public String getDescription() { return description; } /** * Returns the user. * @return the name of the user for this data source */ public String getUser() { return user; } /** * Returns the password. * @return the password for this data source. */ public String getPassword() { return password; } /** < * Sets the name of the application. Supplying this property may allow an administrator of a * Teiid Server to better identify individual connections and usage patterns. * This property is <i>optional</i>. * @param applicationName The applicationName to set */ public void setApplicationName(final String applicationName) { this.applicationName = applicationName; } /** * Sets the name of the virtual database on a particular Teiid Server. * @param databaseName The name of the virtual database */ public void setDatabaseName(final String databaseName) { this.databaseName = databaseName; } /** * Sets the databaseVersion. * @param databaseVersion The version of the virtual database */ public void setDatabaseVersion(final String databaseVersion) { this.databaseVersion = databaseVersion; } /** * Sets the logical name for the underlying <code>XADataSource</code> or * <code>ConnectionPoolDataSource</code>; * used only when pooling connections or distributed transactions are implemented. * @param dataSourceName The dataSourceName for this data source; may be null */ public void setDataSourceName(final String dataSourceName) { this.dataSourceName = dataSourceName; } /** * Sets the user. * @param user The user to set */ public void setUser(final String user) { this.user = user; } /** * Sets the password. * @param password The password for this data source */ public void setPassword(final String password) { this.password = password; } /** * Sets the description of this data source. * @param description The description for this data source; may be null */ public void setDescription(final String description) { this.description = description; } public void setPartialResultsMode(String partialResultsMode) { this.partialResultsMode = partialResultsMode; } public String getPartialResultsMode() { return this.partialResultsMode; } public void setFetchSize(int fetchSize) { this.fetchSize = fetchSize; } public int getFetchSize() { return this.fetchSize; } public void setResultSetCacheMode(String resultSetCacheMode) { this.resultSetCacheMode = resultSetCacheMode; } public String getResultSetCacheMode() { return this.resultSetCacheMode; } public String getShowPlan() { return showPlan; } public void setShowPlan(String showPlan) { this.showPlan = showPlan; } public void setNoExec(boolean noExec) { this.noExec = noExec; } public boolean isNoExec() { return noExec; } /** * @deprecated * @see #getAutoCommitTxn() * @return */ public String getTransactionAutoWrap() { return transactionAutoWrap; } /** * @deprecated * @see #setAutoCommitTxn(String) * @param transactionAutoWrap */ public void setTransactionAutoWrap(String transactionAutoWrap) { this.transactionAutoWrap = transactionAutoWrap; } /** * Returns the current setting for how connections are created by this DataSource manage transactions * for client requests when client applications do not use transactions. * Because a virtual database will likely deal with multiple underlying information sources, * Teiid will execute all client requests within the contexts of transactions. * This method determines the semantics of creating such transactions when the client does not * explicitly do so. * @return the current setting, or null if the property has not been set and the default mode will * be used. */ public String getAutoCommitTxn() { return this.transactionAutoWrap; } /** * Sets the setting for how connections are created by this DataSource manage transactions * for client requests with autoCommit = true. * Because a virtual database will likely deal with multiple underlying information sources, * Teiid will execute all client requests within the contexts of transactions. * This method determines the semantics of creating such transactions when the client does not * explicitly do so. * <p> * The allowable values for this property are: * <ul> * <li>"<code>OFF</code>" - Nothing is ever wrapped in a transaction and the server will execute * multi-source updates happily but outside a transaction. This is least safe but highest performance. * The {@link #TXN_WRAP_OFF} constant value is provided for convenience.</li> * <li>"<code>ON</code>" - Always wrap every command in a transaction. This is most safe but lowest * performance. * The {@link #TXN_WRAP_ON} constant value is provided for convenience.</li> * <li>"<code>AUTO</code>" - checks if a command requires a transaction and will be automatically wrap it. * This is the default mode. * The {@link #TXN_WRAP_AUTO} constant value is provided for convenience.</li> * </ul> * </p> * @param transactionAutoWrap The transactionAutoWrap to set */ public void setAutoCommitTxn(String transactionAutoWrap) { this.transactionAutoWrap = transactionAutoWrap; } public boolean getUseJDBC4ColumnNameAndLabelSemantics() { return useJDBC4ColumnNameAndLabelSemantics; } public void setUseJDBC4ColumnNameAndLabelSemantics(boolean useJDBC4ColumnNameAndLabelSemantics) { this.useJDBC4ColumnNameAndLabelSemantics = useJDBC4ColumnNameAndLabelSemantics; } // -------------------------------------------------------------------------------------------- // V A L I D A T I O N M E T H O D S // -------------------------------------------------------------------------------------------- /** * Return the reason why the supplied application name may be invalid, or null * if it is considered valid. * @param applicationName a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setApplicationName(String) */ public static String reasonWhyInvalidApplicationName( final String applicationName ) { return null; // anything is valid } /** * Return the reason why the supplied virtual database name may be invalid, or null * if it is considered valid. * @param databaseName a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setDatabaseName(String) */ public static String reasonWhyInvalidDatabaseName( final String databaseName ) { if ( databaseName == null || databaseName.trim().length() == 0 ) { return JDBCPlugin.Util.getString("MMDataSource.Virtual_database_name_must_be_specified"); //$NON-NLS-1$ } return null; } /** * Return the reason why the supplied user name may be invalid, or null * if it is considered valid. * @param userName a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setUser(String) */ public static String reasonWhyInvalidUser( final String userName ) { return null; } /** * Return the reason why the supplied transaction auto wrap value may be invalid, or null * if it is considered valid. * <p> * This method checks to see that the value is one of the allowable values. * </p> * @param autoWrap a possible value for the auto wrap property. * @return the reason why the property is invalid, or null if it is considered valid * @see #setTransactionAutoWrap(String) */ public static String reasonWhyInvalidTransactionAutoWrap( final String autoWrap ) { if ( autoWrap == null || autoWrap.trim().length() == 0 ) { return null; // no longer require an app server name, 'cause will look on classpath } final String trimmedAutoWrap = autoWrap.trim(); if( TXN_WRAP_ON.equals(trimmedAutoWrap) ) { return null; } if( TXN_WRAP_OFF.equals(trimmedAutoWrap) ) { return null; } if( TXN_WRAP_AUTO.equals(trimmedAutoWrap) ) { return null; } Object[] params = new Object[] { TXN_WRAP_ON, TXN_WRAP_OFF, TXN_WRAP_AUTO }; return JDBCPlugin.Util.getString("MMDataSource.Invalid_trans_auto_wrap_mode", params); //$NON-NLS-1$ } /** * Return the reason why the supplied virtual database version may be invalid, or null * if it is considered valid. * @param databaseVersion a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setDatabaseVersion(String) */ public static String reasonWhyInvalidDatabaseVersion( final String databaseVersion ) { return null; // anything is valid (let server validate) } /** * Return the reason why the supplied data source name may be invalid, or null * if it is considered valid. * @param dataSourceName a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setDataSourceName(String) */ public static String reasonWhyInvalidDataSourceName( final String dataSourceName) { return null; // anything is valid } /** * Return the reason why the supplied password may be invalid, or null * if it is considered valid. * @param pwd a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setPassword(String) */ public static String reasonWhyInvalidPassword( final String pwd ) { return null; } /** * Return the reason why the supplied description may be invalid, or null * if it is considered valid. * @param description a possible value for the property * @return the reason why the property is invalid, or null if it is considered valid * @see #setDescription(String) */ public static String reasonWhyInvalidDescription( final String description ) { return null; // anything is valid } /** * The reason why partialResultsMode is invalid. * @param partialMode boolean flag * @return String reason */ public static String reasonWhyInvalidPartialResultsMode( final String partialMode) { if ( partialMode != null ) { if (partialMode.equalsIgnoreCase("true") || partialMode.equalsIgnoreCase("false")) { //$NON-NLS-1$ //$NON-NLS-2$ return null; } return JDBCPlugin.Util.getString("MMDataSource.The_partial_mode_must_be_boolean._47"); //$NON-NLS-1$ } return null; } /** * The reason why fetchSize is invalid. * @param fetchSize Number of rows per batch * @return the reason why the property is invalid, or null if it is considered valid */ public static String reasonWhyInvalidFetchSize( final int fetchSize) { if ( fetchSize <= 0 ) { return JDBCPlugin.Util.getString("MMDataSource.The_fetch_size_must_be_greater_than_zero"); //$NON-NLS-1$ } return null; } public void setAdditionalProperties(String additionalProperties) { this.additionalProperties = additionalProperties; } public String getAdditionalProperties() { return additionalProperties; } public void setAnsiQuotedIdentifiers(boolean ansiQuotedIdentifiers) { this.ansiQuotedIdentifiers = ansiQuotedIdentifiers; } public boolean isAnsiQuotedIdentifiers() { return ansiQuotedIdentifiers; } public int getQueryTimeout() { return queryTimeout; } public void setQueryTimeout(int queryTimeout) { this.queryTimeout = queryTimeout; } }