Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package org.noorm.jdbc;
  
  import org.slf4j.Logger;
  
 import java.util.Map;

DataSourceProvider manages data sources and controls transactions in the NoORM runtime engine. Data sources are typically configured in the NoORM configuration file ("noorm.xml" or "noorm.properties"). Alternatively, data sources can be added using static method addDataSource. Due to the complex nature of database resource handling in a running system, data sources, which have been added to the system cannot be removed. DataSourceProvider is a Java singleton, which takes over full control over the database connections. Direct usage of database connections is possible for dedicated purposes (e.g. integration with AQ), but discouraged for typical database access operations. The public API for DataSourceProvider is simple to use and primarily provides the required functionality for explicit transaction handling using the methods begin(), commit() and rollback(). Without explicit transaction handling, DataSourceProvider manages transactions automatically by issuing an auto-commit after every database call. Issuing a commit even for read-only database access imposes a little performance overhead. However, for most usage scenarios, this overhead is not significant. Explicit transaction handling (using begin(), commit(), rollback()) is managed by maintaining the JDBC connection in a ThreadLocal variable. This removes the burden of connection control from the application programmer, since DataSourceProvider will keep track of the current transaction beyond class and method boundaries in the application. Using explicit transaction control requires the application programmer to control transaction termination properly, i.e., the application must guarantee that a transaction started with begin() will always be terminated with commit() or rollback().

Author(s):
Ulf Pietruschka / ulf.pietruschka@etenso.com
 
 public class DataSourceProvider {
 
 	private static final Logger log = LoggerFactory.getLogger(DataSourceProvider.class);
 
     private static final Map<StringActiveDataSourceactiveDataSourceMap = new HashMap<StringActiveDataSource>();
     private static final DataSourceProvider dataSourceProvider = new DataSourceProvider();
     private static IPlatform platform;
 
 	private DataSourceProvider() {
 
         .info("Instantiating NoORM DataSourceProvider.");
         final ConfigurationInitializer configurationInitializer = new ConfigurationInitializer();
         final Map<StringDataSourceConfigurationdataSourceConfigurations = configurationInitializer.init();
         for (final String dataSourceName : dataSourceConfigurations.keySet()) {
             final DataSourceConfiguration dataSourceConfiguration = dataSourceConfigurations.get(dataSourceName);
             final ActiveDataSource activeDataSource = new ActiveDataSource();
             activeDataSource.setName(dataSourceName);
             activeDataSource.setConfiguration(dataSourceConfiguration);
             .put(dataSourceNameactiveDataSource);
         }
     }
 
     private static DataSourceConfiguration getActiveConfiguration() {
 
     }

    
The active connection data contains the currently active data source and the database connection, if the connection is retained for an explicitly managed transaction. The active connection data is stored in a ThreadLocal variable. When a new thread acquires a database connection, a new ActiveConnectionData object is instantiated. The active data source is automatically assigned, when only one data source has been configured or when the associated ActiveDataSource object is provided.

Returns:
the currently active connection data
 
     private static ActiveConnectionData getActiveConnectionData() {
 
         ActiveConnectionData activeConnectionData = .get();
         if (activeConnectionData == null) {
             activeConnectionData = new ActiveConnectionData();
             // In case of a single configured data source, we activate this data source as default data source.
             if (.size() == 1) {
                 .info("Activating default data source.");
                 ActiveDataSource activeDataSource = .values().iterator().next();
                 activeConnectionData.setActiveDataSource(activeDataSource);
             } else {
                 // In case of multiple configured data sources (or none), the caller must activate the
                 // data source to decide, which one to use.
                 throw new DataAccessException(..);
             }
             .set(activeConnectionData);
         }
         return activeConnectionData;
     }

    
Activates the data source with the given name. The name of the data source is either the name provided for the data source in the NoORM configuration file (noorm.xml or noorm.properties) or the name provided, when the data source been added using method addDataSource.

Parameters:
pDataSourceName the name of the data source to be activated.
    public static void setActiveDataSource(final String pDataSourceName) {
        final ActiveDataSource activeDataSource = .get(pDataSourceName);
        if (activeDataSource == null) {
        }
        ActiveConnectionData activeConnectionData = .get();
        if (activeConnectionData != null) {
            // Switching the data source in the middle of user-managed transaction may lead to
            // unpredictable results and is not supported.
            final String oldDataSourceName = activeConnectionData.getActiveDataSource().getName();
            if (!oldDataSourceName.equals(pDataSourceName)) {
                final Long tsStackThreadLocal = activeConnectionData.getTsStack();
                if (tsStackThreadLocal != null && tsStackThreadLocal > 0L) {
                    throw new DataAccessException(..);
                }
            }
        } else {
            activeConnectionData = new ActiveConnectionData();
            .set(activeConnectionData);
        }
        activeConnectionData.setActiveDataSource(activeDataSource);
    }

    
Adds and optionally activates a new data source to be controlled by the DataSourceProvider.

Parameters:
pDataSource the pre-configured data source.
pDataSourceName the name of the data source. Used by method setActiveDataSource.
pPlatformName the database platform name
pActivate determines, whether the data source should be activated right after it has been added.
	public static void addDataSource(final DataSource pDataSource,
                                     final String pDataSourceName,
                                     final String pPlatformName,
                                     final boolean pActivate) {
        ActiveDataSource activeDataSource = .get(pDataSourceName);
        if (activeDataSource != null) {
        }
        validateDataSource(pDataSourcepPlatformName);
        activeDataSource = new ActiveDataSource();
        final DataSourceConfiguration configuration = new DataSourceConfiguration();
        activeDataSource.setName(pDataSourceName);
        activeDataSource.setDataSource(pDataSource);
        activeDataSource.setConfiguration(configuration);
        .put(pDataSourceNameactiveDataSource);
        if (pActivate) {
            setActiveDataSource(pDataSourceName);
        }
    }

Validate the data source. Either for the data source submitted using setDataSource or for the data source constructed using the NoORM properties, a connection is established to validate the data source.
	private static void validateDataSource(final DataSource dataSourcefinal String pPlatform) {
		try {
            if ( == null) {
                 = PlatformFactory.createPlatform(dataSource.getClass(), pPlatform);
            }
            .info(.validateDataSource(dataSource));
            java.sql.Connection con = dataSource.getConnection();
            con.close();
catch (Exception e) {
		}
	}
    public static IPlatform getPlatform() {
        return ;
    }
	private static DataSource getDataSource() throws SQLException {
        final ActiveConnectionData activeConnectionData = getActiveConnectionData();
        final ActiveDataSource activeDataSource = activeConnectionData.getActiveDataSource();
        synchronized (activeDataSource) {
            if (activeDataSource.getDataSource() == null) {
                final DataSource dataSource = .initDataSource(activeDataSource.getConfiguration());
                activeDataSource.setDataSource(dataSource);
            }
        }
        return activeDataSource.getDataSource();
	}
	/*
     * The data source parameters in the NoORM properties can either be specified using JNDI or directly
	 * by providing the required parameters for setting up an DataSource. When both types of
	 * configuration settings are available, the JNDI configuration has precedence.
	 */
	private DataSource initDataSource(final DataSourceConfiguration pDataSourceConfigurationthrows SQLException {
        .debug("Initializing data source.");
        DataSource dataSource;
        pDataSourceConfiguration.validate();
		final String jndiName = pDataSourceConfiguration.getDatabaseJNDIName();
		if (jndiName != null) {
			try {
				.info("Trying to establish data source using JNDI name ".concat(jndiName));
				final Context initCtx = new InitialContext();
                dataSource = (DataSourceinitCtx.lookup(jndiName);
				.info("JNDI data source lookup successful.");
catch (NamingException e) {
				throw new DataAccessException("JNDI data source lookup failed."e);
			}
else {
            .info("Trying to establish data source using NoORM configuration.");
             = PlatformFactory.createPlatform(pDataSourceConfiguration.getDatabasePlatform());
            final String url = pDataSourceConfiguration.getDatabaseURL();
            final String username = pDataSourceConfiguration.getDatabaseUsername();
            final String password = pDataSourceConfiguration.getDatabasePassword();
            dataSource = .getDataSource(urlusernamepassword);
		}
		validateDataSource(dataSourcepDataSourceConfiguration.getDatabasePlatform());
        return dataSource;
	}
	static void returnConnection(final boolean pSuccess) {
		try {
            if (con == null || con.isClosed()) {
                throw new DataAccessException(..);
            }
					.debug("Returning connection to connection pool.");
				}
				// When closing the connection (resp. returning it to the connection pool, we
				// always issue a commit, since in any case, there is no meaningful transaction
				// scope beyond this point in time.
				// Committing even in case of a read only transaction may incur some additional
				// cost, since from a technical perspective, the commit is not necessary. On
				// the other hand, the DB reduces the cost in this case to a minimum, so that
				// the advantage of this way of transaction automation outweighs a possible
				// minimal performance reduction.
				if (pSuccess) {
                    con.commit();
else {
                    con.rollback();
				}
                con.close();
			}
catch (SQLException e) {
		}
	}

Returns the database connection currently managed by the DataSourceProvider. Note that the life-cycle of the connection is managed by the DataSourceProvider, i.e. acquiring a connection, closing a connection, resp. returning a connection to the connection pool and managing transaction boundaries SHOULD NOT BE DONE in the application code. Providing the database connection directly by this method has the sole purpose to support additional Java client software, which requires access to the connection (e.g. Streams Advanced Queueing, which requires the connection to create an AQSession).

Returns:
the database connection currently managed by the DataSourceProvider.
Throws:
java.sql.SQLException JDBC driver exception
	public static Connection getConnection() throws SQLException {
		return getConnection(false);
	}

Retaining the connection is only required when starting a new transaction explicitly (indicating that the transaction is beyond the scope of a single PL/SQL call or insert or update operation). Having already a retained connection for this thread indicates that this transaction is beyond the scope of the caller.

Parameters:
pRetain indicates, if the connection should be retained.
Returns:
the acquired connection.
Throws:
java.sql.SQLException
	private static Connection getConnection(final boolean pRetainthrows SQLException {
				if (pRetain) {
					.debug("Acquiring retainable connection from connection pool.");
else {
					.debug("Acquiring new connection from connection pool.");
				}
			}
		    con = dataSourceConn;
            if (.isDebugEnabled()) {
                .debug("Acquired connection : ".concat(con.toString()));
            }
			con.setAutoCommit(false);
            if (getActiveConfiguration().isDebugMode()) {
			}
            getActiveConnectionData().setConnection(con);
		}
		if (pRetain) {
			Long tsStackThreadLocal0 = getActiveConnectionData().getTsStack();
			if (tsStackThreadLocal0 == null || tsStackThreadLocal0 == 0L) {
                getActiveConnectionData().setTsStack(1L);
else {
                getActiveConnectionData().setTsStack(tsStackThreadLocal0 + 1L);
			}
				.debug("Setting transaction call stack level to "
                        .concat(getActiveConnectionData().getTsStack().toString()));
			}
		}
		return con;
	}

    
Verifies, whether an explicit (user managed) transaction is currently in progress, i.e., the application has explicitly issued a "begin", but has not yet terminated the transaction with "commit" or "rollback".

Returns:
true, when a transaction is in progress
    public static boolean activeUserManagedTransaction() {
        return getActiveConnectionData().getTsStack() > 0L;
    }

To control transactions in the calling application, the connection in use must not turned back to the connection pool, but should be preserved in a ThreadLocal variable. This method is used to indicate that the calling application intends to broaden the transaction boundaries beyond the scope of a single PL/SQL or DML call. JDBC does not know about the classical "begin" call, since a database session has always an active transaction (after issuing a "commit" or "rollback", a new transaction is started implicitly).
	public static void begin() {
		try {
catch (Exception e) {
		}
	}

Commits a user managed transaction. Connections are maintained in the connection pool. Note that using this method requires that a user managed transaction has been started using method begin(), other a DataAccessException is thrown. Transactions, which are not user managed are always committed automatically, when the connection is returned to the connection pool (restricting automated transaction management to transactions with a scope limited to a single call to PL/SQL, an insert, update or an delete operation.
	public static void commit() {
		Connection con = null;
		try {
			if (con == null || con.isClosed()) {
			}
			// When the transaction stack counter indicates that the transaction is beyond the scope of
			// the caller, reduce the transactions stack counter and remain the connection open.
			Long tsStackThreadLocal0 = getActiveConnectionData().getTsStack();
			if (tsStackThreadLocal0 == null) {
else {
                getActiveConnectionData().setTsStack(tsStackThreadLocal0 - 1L);
			}
				.debug("Setting transaction call stack level to "
                        .concat(getActiveConnectionData().getTsStack().toString()));
			}
					.debug("Committing transaction");
				}
				con.commit();
                getActiveConnectionData().reset();
else {
					.debug("Delegating transaction termination to caller.");
				}
			}
catch (SQLException e) {
finally {
			try {
				if (con != null && !con.isClosed()) {
					// Although the connection may get reused in the current thread, we do not know
					// here, if the thread will be closed. In the latter case we cannot influence,
					// when the connection is returned to the pool, so we do it here.
							.debug("Returning retained connection to connection pool.");
						}
						con.close();
					}
				}
catch (SQLException ignored) {
// Nothing to do
		}
	}

Issues a rollback on a user managed transaction. See DataSourceProvider.commit();
	public static void rollback() {
		Connection con = null;
		try {
			if (con == null || con.isClosed()) {
			}
			// When the transaction stack counter indicates that the transaction is beyond the scope of
			// the caller, reduce the transactions stack counter and remain the connection open.
			Long tsStackThreadLocal0 = getActiveConnectionData().getTsStack();
			if (tsStackThreadLocal0 == null) {
else {
                getActiveConnectionData().setTsStack(tsStackThreadLocal0 - 1L);
			}
				.debug("Setting transaction call stack level to "
                        .concat(getActiveConnectionData().getTsStack().toString()));
			}
					.debug("Rolling back transaction");
				}
				con.rollback();
                getActiveConnectionData().reset();
else {
					.debug("Delegating transaction termination to caller.");
				}
                throw new DataAccessException(..);
            }
catch (SQLException e) {
finally {
			try {
				if (con != null && !con.isClosed()) {
					// Although the connection may get reused in the current thread, we do not know
					// here, if the thread will be closed. In the latter case we cannot influence,
					// when the connection is returned to the pool, so we do it here.
							.debug("Returning retained connection to connection pool.");
						}
						con.close();
					}
				}
catch (SQLException ignored) {
// Nothing to do
		}
	}
    static Number getNextSequenceValue(final String pSequenceName,
                                       final Long pSequenceIncrement,
                                       final Class pType) {
        return getActiveConnectionData().getNextSequenceValue(pSequenceNamepSequenceIncrementpType);
    }

Returns the currently size of JDBC update and insert batches. The default is 100.

Returns:
the batch size.
	public static int getBatchUpdateSize() {
	}

Sets the size of JDBC update and insert batches. The default is 100.

Parameters:
pBatchUpdateSize the number of records processed in one DML batch operation
	public static void setBatchUpdateSize(final int pBatchUpdateSize) {
        getActiveConfiguration().setDatabaseBatchUpdateSize(pBatchUpdateSize);
	}

Shows whether debug mode has been enabled.

Returns:
true, when debug mode has been enabled, false otherwise.
	public boolean isDebugOn() {
        return getActiveConfiguration().isDebugMode();
	}

Debug mode is enabled by establishing a JPDA/JDWP connection to a listening source code debugger, which listens on the configured host and port for database session debugging. DO NOT enable debugging without an active listening source code debugger.

Parameters:
pDebugMode debug mode toggle.
	public static void setDebugMode(final boolean pDebugMode) {
        getActiveConfiguration().setDebugMode(pDebugMode);
	}
	// There is no easy configurable way to control a clean disconnect for the established debug
	// connection, thus clean-up of the associated socket is left the Connection, resp. Pool.
	static private void enableDebugMode(final Connection pConthrows SQLException {
		StringBuilder logMessage = new StringBuilder();
		logMessage.append("Enabling PL/SQL debugging. Connecting to host : ");
		logMessage.append(", port : ");
		.debug(logMessage.toString());
		final String procedureCall = "{ call dbms_debug_jdwp.connect_tcp(host => :host, port => :port)";
		final CallableStatement cstmt = pCon.prepareCall(procedureCall);
		cstmt.execute();
	}
    static class ActiveDataSource {
        private String name;
        private DataSourceConfiguration configuration;
        private DataSource dataSource;
        public String getName() {
            return ;
        }
        public void setName(String name) {
            this. = name;
        }
        public DataSourceConfiguration getConfiguration() {
            return ;
        }
        public void setConfiguration(final DataSourceConfiguration pConfiguration) {
             = pConfiguration;
        }
        public DataSource getDataSource() {
            return ;
        }
        public void setDataSource(final DataSource pDataSource) {
             = pDataSource;
        }
    }
    static class ActiveConnectionData {
        private ActiveDataSource activeDataSource;
        private Connection connection;
        private Long tsStack = 0L;
        private Map<StringNumberlastSequenceDBValues = new HashMap<StringNumber>();
        private Map<StringNumbersequenceCache = new HashMap<StringNumber>();
        public ActiveDataSource getActiveDataSource() {
            return ;
        }
        public void setActiveDataSource(ActiveDataSource pActiveDataSource) {
             = pActiveDataSource;
        }
        public Connection getConnection() {
            return ;
        }
        public void setConnection(final Connection pConnection) {
             = pConnection;
        }
        public Long getTsStack() {
            return ;
        }
        public void setTsStack(final Long pTSStack) {
             = pTSStack;
        }
        public void reset() {
             = null;
             = 0L;
        }
        public synchronized Number getNextSequenceValue(final String pSequenceName,
                                                        final Long pSequenceIncrement,
                                                        final Class pType) {
            Number lastSequenceDBValue = .get(pSequenceName);
            final Number currentSequenceValue = .get(pSequenceName);
            final boolean useCachedValues = lastSequenceDBValue != null && currentSequenceValue.longValue()
                    < lastSequenceDBValue.longValue() + pSequenceIncrement.longValue() - 1;
            if (useCachedValues) {
                Number nextSequenceValue = null;
                if (pType.equals(Long.class)) {
                    nextSequenceValue = currentSequenceValue.longValue() + 1;
                }
                if (pType.equals(Integer.class)) {
                    nextSequenceValue = currentSequenceValue.intValue() + 1;
                }
                if (pType.equals(Short.class)) {
                    nextSequenceValue = currentSequenceValue.shortValue() + 1;
                }
                .put(pSequenceNamenextSequenceValue);
                return nextSequenceValue;
            } else {
                lastSequenceDBValue = getLastSequenceDBValue(pSequenceNamepType);
                .put(pSequenceNamelastSequenceDBValue);
                .put(pSequenceNamelastSequenceDBValue);
                return lastSequenceDBValue;
            }
        }
        private Number getLastSequenceDBValue(final String pSequenceNamefinal Class pType) {
            String sequenceName = pSequenceName;
            if (!pSequenceName.equals(pSequenceName.toUpperCase())) {
                sequenceName = "\"".concat(pSequenceName).concat("\"");
            }
            final String sequenceQuery = .getSequenceQuery(sequenceName);
            PreparedStatement pstmt = null;
            boolean success = true;
            boolean conAlreadyEstablished = true;
            try {
                if ( == null) {
                    // The connection used here will usually be just the connection held by this subclass, but when
                    // it has not been initialized yet, we will run into a NPE, thus we go the standard way be using
                    // DataSourceProvider.getConnection()
                    DataSourceProvider.getConnection();
                    conAlreadyEstablished = false;
                }
                pstmt = .prepareStatement(sequenceQuery);
                final ResultSet resultSet = pstmt.executeQuery();
                resultSet.next();
                Number sequenceValue = null;
                if (pType.equals(Long.class)) {
                    sequenceValue = resultSet.getLong(1);
                }
                if (pType.equals(Integer.class)) {
                    sequenceValue = resultSet.getInt(1);
                }
                if (pType.equals(Short.class)) {
                    sequenceValue = resultSet.getShort(1);
                }
                return sequenceValue;
            } catch (Exception e) {
                success = false;
                throw new DataAccessException(..e);
            } finally {
                try {
                    if (pstmt != null) {
                        pstmt.close();
                    }
                    if ( != null && !.isClosed() && !conAlreadyEstablished) {
                        DataSourceProvider.returnConnection(success);
                    }
                } catch (SQLException ignored) {
                } // Nothing to do
            }
        }
    }
New to GrepCode? Check out our FAQ X