org.firebirdsql.jdbc.FBTpbMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird-jdk15 Show documentation
Show all versions of jaybird-jdk15 Show documentation
JDBC Driver for the Firebird RDBMS
/*
* $Id: FBTpbMapper.java 56953 2012-04-09 11:50:52Z mrotteveel $
* Firebird Open Source J2ee connector - jdbc driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program 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
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a CVS history command.
*
* All rights reserved.
*/
package org.firebirdsql.jdbc;
import org.firebirdsql.gds.GDS;
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.gds.TransactionParameterBuffer;
import org.firebirdsql.jca.FBResourceException;
import java.io.Serializable;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
/**
* This class is provides mapping capabilities between standard JDBC
* transaction isolation level and Firebird Transaction Parameters Block (TPB).
*
* @author Roman Rokytskyy
*/
public class FBTpbMapper implements Serializable, Cloneable {
private static final long serialVersionUID = 1690658870275668176L;
public static final String DEFAULT_MAPPING_RESOURCE = "isc_tpb_mapping";
public static FBTpbMapper getDefaultMapper(GDS gds) {
return new FBTpbMapper(gds);
}
/**
* Dirty reads, non-repeatable reads and phantom reads are prevented. This
* level includes the prohibitions in TRANSACTION_REPEATABLE_READ and
* further prohibits the situation where one transaction reads all rows that
* satisfy a WHERE condition, a second transaction inserts a row that
* satisfies that WHERE condition, and the first transaction rereads for the
* same condition, retrieving the additional "phantom" row in the second
* read.
*/
public static final String TRANSACTION_SERIALIZABLE = "TRANSACTION_SERIALIZABLE";
/**
* Dirty reads and non-repeatable reads are prevented; phantom reads can
* occur. This level prohibits a transaction from reading a row with
* uncommitted changes in it, and it also prohibits the situation where one
* transaction reads a row, a second transaction alters the row, and the
* first transaction rereads the row, getting different values the second
* time (a "non-repeatable read").
*/
public static final String TRANSACTION_REPEATABLE_READ = "TRANSACTION_REPEATABLE_READ";
/**
* Dirty reads are prevented; non-repeatable reads and phantom reads can
* occur. This level only prohibits a transaction from reading a row with
* uncommitted changes in it.
*/
public static final String TRANSACTION_READ_COMMITTED = "TRANSACTION_READ_COMMITTED";
// read uncommitted actually not supported
/**
* Dirty reads, non-repeatable reads and phantom reads can occur. This level
* allows a row changed by one transaction to be read by another transaction
* before any changes in that row have been committed (a "dirty read"). If
* any of the changes are rolled back, the second transaction will have
* retrieved an invalid row. This level is not actually supported
*/
public static final String TRANSACTION_READ_UNCOMMITTED = "TRANSACTION_READ_UNCOMMITTED";
/**
* Indicates that transactions are not supported. This level is not
* supported
*/
public static final String TRANSACTION_NONE = "TRANSACTION_NONE";
/**
* Convert transaction isolation level into string.
*
* @param isolationLevel transaction isolation level as integer constant.
*
* @return corresponding string representation.
*/
public static String getTransactionIsolationName(int isolationLevel) {
switch (isolationLevel) {
case Connection.TRANSACTION_NONE:
return TRANSACTION_NONE;
case Connection.TRANSACTION_READ_UNCOMMITTED:
return TRANSACTION_READ_UNCOMMITTED;
case Connection.TRANSACTION_READ_COMMITTED:
return TRANSACTION_READ_COMMITTED;
case Connection.TRANSACTION_REPEATABLE_READ:
return TRANSACTION_REPEATABLE_READ;
case Connection.TRANSACTION_SERIALIZABLE:
return TRANSACTION_SERIALIZABLE;
default:
throw new IllegalArgumentException("Incorrect transaction isolation level.");
}
}
/**
* Convert transaction isolation level name into a corresponding constant.
*
* @param isolationName name of the transaction isolation.
*
* @return corresponding constant.
*/
public static int getTransactionIsolationLevel(String isolationName) {
if (TRANSACTION_NONE.equals(isolationName))
return Connection.TRANSACTION_NONE;
else if (TRANSACTION_READ_UNCOMMITTED.equals(isolationName))
return Connection.TRANSACTION_READ_UNCOMMITTED;
else if (TRANSACTION_READ_COMMITTED.equals(isolationName))
return Connection.TRANSACTION_READ_COMMITTED;
else if (TRANSACTION_REPEATABLE_READ.equals(isolationName))
return Connection.TRANSACTION_REPEATABLE_READ;
else if (TRANSACTION_SERIALIZABLE.equals(isolationName))
return Connection.TRANSACTION_SERIALIZABLE;
else
throw new IllegalArgumentException("Invalid isolation name.");
}
private HashMap mapping = new HashMap();
private int defaultIsolationLevel = Connection.TRANSACTION_READ_COMMITTED;
/**
* Create instance of this class with the default mapping of JDBC
* transaction isolation levels to Firebird TPB.
*/
public FBTpbMapper(GDS gds) {
TransactionParameterBuffer serializableTpb = gds.newTransactionParameterBuffer();
serializableTpb.addArgument(ISCConstants.isc_tpb_write);
serializableTpb.addArgument(ISCConstants.isc_tpb_wait);
serializableTpb.addArgument(ISCConstants.isc_tpb_consistency);
TransactionParameterBuffer repeatableReadTpb = gds.newTransactionParameterBuffer();
repeatableReadTpb.addArgument(ISCConstants.isc_tpb_write);
repeatableReadTpb.addArgument(ISCConstants.isc_tpb_wait);
repeatableReadTpb.addArgument(ISCConstants.isc_tpb_concurrency);
TransactionParameterBuffer readCommittedTpb = gds.newTransactionParameterBuffer();
readCommittedTpb.addArgument(ISCConstants.isc_tpb_write);
readCommittedTpb.addArgument(ISCConstants.isc_tpb_wait);
readCommittedTpb.addArgument(ISCConstants.isc_tpb_read_committed);
readCommittedTpb.addArgument(ISCConstants.isc_tpb_rec_version);
mapping.put(Integer.valueOf(Connection.TRANSACTION_SERIALIZABLE), serializableTpb);
mapping.put(Integer.valueOf(Connection.TRANSACTION_REPEATABLE_READ), repeatableReadTpb);
mapping.put(Integer.valueOf(Connection.TRANSACTION_READ_COMMITTED), readCommittedTpb);
}
/**
* Create instance of this class for the specified string mapping.
*
* @param stringMapping mapping of JDBC transaction isolation to Firebird
* mapping. Keys and values of this map must be strings. Keys can have
* following values:
*
* "TRANSACTION_SERIALIZABLE"
* "TRANSACTION_REPEATABLE_READ"
* "TRANSACTION_READ_COMMITTED"
* "TRANSACTION_READ_UNCOMMITTED"
*
* Values are specified as comma-separated list of following keywords:
*
* "isc_tpb_consistency"
* "isc_tpb_concurrency"
* "isc_tpb_read_committed"
* "isc_tpb_rec_version"
* "isc_tpb_no_rec_version"
* "isc_tpb_wait"
* "isc_tpb_nowait"
* "isc_tpb_read"
* "isc_tpb_write"
* "isc_tpb_lock_read"
* "isc_tpb_lock_write"
* "isc_tpb_shared"
* "isc_tpb_protected"
*
* It is also allowed to strip "isc_tpb_" prefix from above shown constans.
* Meaning of these constants and possible combinations you can find in a
* documentation.
*
* @throws FBResourceException if mapping contains incorrect values.
*/
public FBTpbMapper(GDS gds, Map stringMapping) throws FBResourceException {
this(gds);
processMapping(gds, stringMapping);
}
/**
* Process specified string mapping. This method updates default mapping
* with values specified in a stringMapping
.
*
* @param stringMapping mapping to process.
*
* @throws FBResourceException if mapping contains incorrect values.
*/
private void processMapping(GDS gds, Map stringMapping) throws FBResourceException {
Iterator iter = stringMapping.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String jdbcTxIsolation = (String) entry.getKey();
if (TRANSACTION_SERIALIZABLE.equalsIgnoreCase(jdbcTxIsolation))
mapping.put(
Integer.valueOf(Connection.TRANSACTION_SERIALIZABLE),
processMapping(gds, (String) entry.getValue()));
else if (TRANSACTION_REPEATABLE_READ.equalsIgnoreCase(jdbcTxIsolation))
mapping.put(
Integer.valueOf(Connection.TRANSACTION_REPEATABLE_READ),
processMapping(gds, (String) entry.getValue()));
else if (TRANSACTION_READ_COMMITTED.equalsIgnoreCase(jdbcTxIsolation))
mapping.put(
Integer.valueOf(Connection.TRANSACTION_READ_COMMITTED),
processMapping(gds, (String) entry.getValue()));
else if (TRANSACTION_READ_UNCOMMITTED.equalsIgnoreCase(jdbcTxIsolation))
mapping.put(
Integer.valueOf(Connection.TRANSACTION_READ_UNCOMMITTED),
processMapping(gds, (String) entry.getValue()));
else
throw new FBResourceException(
"Transaction isolation " + jdbcTxIsolation +
" is not supported.");
}
}
/**
* Create instance of this class and load mapping from the specified
* resource.
*
* @param mappingResource name of the resource to load.
* @param cl class loader that should be used to load specified resource.
*
* @throws FBResourceException if resource cannot be loaded or contains
* incorrect values.
*/
public FBTpbMapper(GDS gds, String mappingResource, ClassLoader cl) throws FBResourceException {
this(gds);
try {
ResourceBundle res = ResourceBundle.getBundle(
mappingResource, Locale.getDefault(), cl);
HashMap mapping = new HashMap();
Enumeration en = res.getKeys();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String value = res.getString(key);
mapping.put(key, value);
}
processMapping(gds, mapping);
} catch (MissingResourceException mrex) {
throw new FBResourceException(
"Cannot load TPB mapping." + mrex.getMessage());
}
}
/**
* Process comma-separated list of keywords and convert them into TPB
* values.
*
* @param mapping comma-separated list of keywords.
*
* @return set containing values corresponding to the specified keywords.
*
* @throws FBResourceException if mapping contains keyword that is not
* a TPB parameter.
*/
public static TransactionParameterBuffer processMapping(GDS gds, String mapping) throws FBResourceException {
TransactionParameterBuffer result = gds.newTransactionParameterBuffer();
StringTokenizer st = new StringTokenizer(mapping, ",");
while (st.hasMoreTokens()) {
String token = st.nextToken();
Integer value = FBConnectionHelper.getTpbParam(token);
if (value == null)
throw new FBResourceException(
"Keyword " + token + " unknown. Please check your mapping.");
result.addArgument(value.intValue());
}
return result;
}
/**
* Get mapping for the specified transaction isolation level.
*
* @param transactionIsolation transaction isolation level.
*
* @return set with TPB parameters.
*
* @throws IllegalArgumentException if specified transaction isolation level
* is unknown.
*/
public TransactionParameterBuffer getMapping(int transactionIsolation) {
switch (transactionIsolation) {
case Connection.TRANSACTION_SERIALIZABLE:
case Connection.TRANSACTION_REPEATABLE_READ:
case Connection.TRANSACTION_READ_COMMITTED:
return ((TransactionParameterBuffer) mapping.get(
Integer.valueOf(transactionIsolation))).deepCopy();
// promote transaction
case Connection.TRANSACTION_READ_UNCOMMITTED:
return ((TransactionParameterBuffer) mapping.get(
Integer.valueOf(Connection.TRANSACTION_READ_COMMITTED))).deepCopy();
case Connection.TRANSACTION_NONE:
default:
throw new IllegalArgumentException(
"Transaction isolation level " + transactionIsolation +
" is not supported.");
}
}
/**
* Set mapping for the specified transaction isolation.
*
* @param transactionIsolation transaction isolation level.
* @param tpb TPB parameters.
*
* @throws IllegalArgumentException if incorrect isolation level is specified.
*/
public void setMapping(int transactionIsolation, TransactionParameterBuffer tpb) {
switch (transactionIsolation) {
case Connection.TRANSACTION_SERIALIZABLE:
case Connection.TRANSACTION_REPEATABLE_READ:
case Connection.TRANSACTION_READ_COMMITTED:
mapping.put(Integer.valueOf(transactionIsolation), tpb);
break;
case Connection.TRANSACTION_READ_UNCOMMITTED:
case Connection.TRANSACTION_NONE:
default:
throw new IllegalArgumentException(
"Transaction isolation level " + transactionIsolation +
" is not supported.");
}
}
/**
* Get default mapping. Default mapping represents a TPB mapping for the
* default transaction isolation level (read committed).
*
* @return mapping for the default transaction isolation level.
*/
public TransactionParameterBuffer getDefaultMapping() {
return (TransactionParameterBuffer) mapping.get(Integer.valueOf(defaultIsolationLevel));
}
public int getDefaultTransactionIsolation() {
return defaultIsolationLevel;
}
public void setDefaultTransactionIsolation(int isolationLevel) {
this.defaultIsolationLevel = isolationLevel;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof FBTpbMapper)) {
return false;
}
FBTpbMapper that = (FBTpbMapper) obj;
boolean result = true;
result &= this.mapping.equals(that.mapping);
result &= (this.defaultIsolationLevel == that.defaultIsolationLevel);
return result;
}
public int hashCode() {
int result = 31;
result = result * 83 + mapping.hashCode();
result = result * 83 + defaultIsolationLevel;
return result;
}
public Object clone() {
try {
FBTpbMapper clone = (FBTpbMapper) super.clone();
clone.mapping = (HashMap) mapping.clone();
return clone;
} catch (CloneNotSupportedException ex) {
throw new Error("Assertion failure: clone not supported"); // Can't
// happen
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy