001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jdbc.datasource.lookup;
018:
019: import org.springframework.core.Constants;
020: import org.springframework.transaction.TransactionDefinition;
021: import org.springframework.transaction.support.DefaultTransactionDefinition;
022: import org.springframework.transaction.support.TransactionSynchronizationManager;
023:
024: /**
025: * DataSource that routes to one of various target DataSources based on the
026: * current transaction isolation level. The target DataSources need to be
027: * configured with the isolation level name as key, as defined on the
028: * {@link org.springframework.transaction.TransactionDefinition TransactionDefinition interface}.
029: *
030: * <p>This is particularly useful in combination with JTA transaction management
031: * (typically through Spring's {@link org.springframework.transaction.jta.JtaTransactionManager}).
032: * Standard JTA does not support transaction-specific isolation levels. Some JTA
033: * providers support isolation levels as a vendor-specific extension (e.g. WebLogic),
034: * which is the preferred way of addressing this. As alternative (e.g. on WebSphere),
035: * the target database can be represented through multiple JNDI DataSources, each
036: * configured with a different isolation level (for the entire DataSource).
037: * The present DataSource router allows to transparently switch to the
038: * appropriate DataSource based on the current transaction's isolation level.
039: *
040: * <p>The configuration can for example look like this, assuming that the target
041: * DataSources are defined as individual Spring beans with names
042: * "myRepeatableReadDataSource", "mySerializableDataSource" and "myDefaultDataSource":
043: *
044: * <pre>
045: * <bean id="dataSourceRouter" class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter">
046: * <property name="targetDataSources">
047: * <map>
048: * <entry key="ISOLATION_REPEATABLE_READ" value-ref="myRepeatableReadDataSource"/>
049: * <entry key="ISOLATION_SERIALIZABLE" value-ref="mySerializableDataSource"/>
050: * </map>
051: * </property>
052: * <property name="defaultTargetDataSource" ref="myDefaultDataSource"/>
053: * </bean></pre>
054: *
055: * Alternatively, the keyed values can also be data source names, to be resolved
056: * through a {@link #setDataSourceLookup DataSourceLookup}: by default, JNDI
057: * names for a standard JNDI lookup. This allows for a single concise definition
058: * without the need for separate DataSource bean definitions.
059: *
060: * <pre>
061: * <bean id="dataSourceRouter" class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter">
062: * <property name="targetDataSources">
063: * <map>
064: * <entry key="ISOLATION_REPEATABLE_READ" value="java:comp/env/jdbc/myrrds"/>
065: * <entry key="ISOLATION_SERIALIZABLE" value="java:comp/env/jdbc/myserds"/>
066: * </map>
067: * </property>
068: * <property name="defaultTargetDataSource" value="java:comp/env/jdbc/mydefds"/>
069: * </bean></pre>
070: *
071: * Note: If you are using this router in combination with Spring's
072: * {@link org.springframework.transaction.jta.JtaTransactionManager},
073: * don't forget to switch the "allowCustomIsolationLevels" flag to "true".
074: * (By default, JtaTransactionManager will only accept a default isolation level
075: * because of the lack of isolation level support in standard JTA itself.)
076: *
077: * <pre>
078: * <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
079: * <property name="allowCustomIsolationLevels" value="true"/>
080: * </bean></pre>
081: *
082: * @author Juergen Hoeller
083: * @since 2.0.1
084: * @see #setTargetDataSources
085: * @see #setDefaultTargetDataSource
086: * @see org.springframework.transaction.TransactionDefinition#ISOLATION_READ_UNCOMMITTED
087: * @see org.springframework.transaction.TransactionDefinition#ISOLATION_READ_COMMITTED
088: * @see org.springframework.transaction.TransactionDefinition#ISOLATION_REPEATABLE_READ
089: * @see org.springframework.transaction.TransactionDefinition#ISOLATION_SERIALIZABLE
090: * @see org.springframework.transaction.jta.JtaTransactionManager
091: */
092: public class IsolationLevelDataSourceRouter extends
093: AbstractRoutingDataSource {
094:
095: /** Constants instance for TransactionDefinition */
096: private static final Constants constants = new Constants(
097: TransactionDefinition.class);
098:
099: /**
100: * Supports Integer values for the isolation level constants
101: * as well as isolation level names as defined on the
102: * {@link org.springframework.transaction.TransactionDefinition TransactionDefinition interface}.
103: */
104: protected Object resolveSpecifiedLookupKey(Object lookupKey) {
105: if (lookupKey instanceof Integer) {
106: return (Integer) lookupKey;
107: } else if (lookupKey instanceof String) {
108: String constantName = (String) lookupKey;
109: if (constantName == null
110: || !constantName
111: .startsWith(DefaultTransactionDefinition.PREFIX_ISOLATION)) {
112: throw new IllegalArgumentException(
113: "Only isolation constants allowed");
114: }
115: return constants.asNumber(constantName);
116: } else {
117: throw new IllegalArgumentException(
118: "Invalid lookup key - needs to be isolation level Integer or isolation level name String: "
119: + lookupKey);
120: }
121: }
122:
123: protected Object determineCurrentLookupKey() {
124: return TransactionSynchronizationManager
125: .getCurrentTransactionIsolationLevel();
126: }
127:
128: }
|