/**
* Copyright 2008-2016 Qualogy Solutions B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qualogy.qafe.bind.business.transaction;
import java.io.Serializable;
import com.qualogy.qafe.bind.Validatable;
import com.qualogy.qafe.bind.ValidationException;
/**
* Holder for transaction settings.
* @author
*
*/
public class TransactionBehaviour implements Serializable, Validatable{
private static final long serialVersionUID = 3519369953608887272L;
/**
* options for management setting
*/
public final static String MANAGEMENT_NO = "no";
public final static String MANAGEMENT_LOCAL = "local";
public final static String MANAGEMENT_GLOBAL = "global";
/**
* Support a current transaction; create a new one if none exists.
* Analogous to the EJB transaction attribute of the same name.
* <p>This is typically the default setting of a transaction definition,
* and typically defines a transaction synchronization scope.
*/
public final static String PROPAGATION_REQUIRED = "required";
/**
* Support a current transaction; execute non-transactionally if none exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> For transaction managers with transaction synchronization,
* <code>PROPAGATION_SUPPORTS</code> is slightly different from no transaction
* at all, as it defines a transaction scope that synchronization might apply to.
* As a consequence, the same resources (a JDBC <code>Connection</code>, a
* Hibernate <code>Session</code>, etc) will be shared for the entire specified
* scope. Note that the exact behavior depends on the actual synchronization
* configuration of the transaction manager!
* <p>In general, use <code>PROPAGATION_SUPPORTS</code> with care! In particular, do
* not rely on <code>PROPAGATION_REQUIRED</code> or <code>PROPAGATION_REQUIRES_NEW</code>
* <i>within</i> a <code>PROPAGATION_SUPPORTS</code> scope (which may lead to
* synchronization conflicts at runtime). If such nesting is unavoidable, make sure
* to configure your transaction manager appropriately (typically switching to
* "synchronization on actual transaction").
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
*/
public final static String PROPAGATION_SUPPORTS = "supported";
/**
* Support a current transaction; throw an exception if no current transaction
* exists. Analogous to the EJB transaction attribute of the same name.
* <p>Note that transaction synchronization within a <code>PROPAGATION_MANDATORY</code>
* scope will always be driven by the surrounding transaction.
*/
public final static String PROPAGATION_MANDATORY = "mandatory";
/**
* Create a new transaction, suspending the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the <code>javax.transaction.TransactionManager</code>
* to be made available it to it (which is server-specific in standard J2EE).
* <p>A <code>PROPAGATION_REQUIRES_NEW</code> scope always defines its own
* transaction synchronizations. Existing synchronizations will be suspended
* and resumed appropriately.
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
public final static String PROPAGATION_REQUIRES_NEW = "requires_new";
/**
* Do not support a current transaction; rather always execute non-transactionally.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the <code>javax.transaction.TransactionManager</code>
* to be made available it to it (which is server-specific in standard J2EE).
* <p>Note that transaction synchronization is <i>not</i> available within a
* <code>PROPAGATION_NOT_SUPPORTED</code> scope. Existing synchronizations
* will be suspended and resumed appropriately.
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
public final static String PROPAGATION_NOT_SUPPORTED = "not_supported";
/**
* Do not support a current transaction; throw an exception if a current transaction
* exists. Analogous to the EJB transaction attribute of the same name.
* <p>Note that transaction synchronization is <i>not</i> available within a
* <code>PROPAGATION_NEVER</code> scope.
*/
public final static String PROPAGATION_NEVER = "never";
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@link #PROPAGATION_REQUIRED} else. There is no analogous
* feature in EJB.
* <p><b>NOTE:</b> Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* when working on a JDBC 3.0 driver. Some JTA providers might support
* nested transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
public final static String PROPAGATION_NESTED = "nested";
/**
* Use the default isolation level of the underlying datastore.
* All other levels correspond to the JDBC isolation levels.
*/
public final static String ISOLATION_DEFAULT = "default";
/**
* Indicates that dirty reads, non-repeatable reads and phantom reads
* can occur.
* <p>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.
*/
public final static String ISOLATION_READ_UNCOMMITTED = "read_uncommited";
/**
* Indicates that dirty reads are prevented; non-repeatable reads and
* phantom reads can occur.
* <p>This level only prohibits a transaction from reading a row
* with uncommitted changes in it.
*/
public final static String ISOLATION_READ_COMMITTED = "read_commited";
/**
* Indicates that dirty reads and non-repeatable reads are prevented;
* phantom reads can occur.
* <p>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 final static String ISOLATION_REPEATABLE_READ = "repeatable_read";
/**
* Indicates that dirty reads, non-repeatable reads and phantom reads
* are prevented.
* <p>This level includes the prohibitions in
* {@link #ISOLATION_REPEATABLE_READ} and further prohibits the
* situation where one transaction reads all rows that satisfy a
* <code>WHERE</code> condition, a second transaction inserts a
* row that satisfies that <code>WHERE</code> condition, and the
* first transaction rereads for the same condition, retrieving
* the additional "phantom" row in the second read.
*/
public final static String ISOLATION_SERIALIZABLE = "serializable";
/**
* options for management setting
*/
public final static String[] MANAGEMENT_OPTIONS = {
MANAGEMENT_NO,
MANAGEMENT_LOCAL,
MANAGEMENT_GLOBAL
};
/**
* options for isolation setting
*/
public final static String[] ISOLATION_OPTIONS = {
ISOLATION_DEFAULT,
ISOLATION_READ_COMMITTED,
ISOLATION_READ_UNCOMMITTED,
ISOLATION_REPEATABLE_READ,
ISOLATION_SERIALIZABLE
};
/**
* options for propagation setting
*/
public final static String[] PROPAGATION_OPTIONS = {
PROPAGATION_MANDATORY,
PROPAGATION_NESTED,
PROPAGATION_NEVER,
PROPAGATION_NOT_SUPPORTED,
PROPAGATION_REQUIRED,
PROPAGATION_REQUIRES_NEW,
PROPAGATION_SUPPORTS
};
/**
* default settings
*/
public final static String DEFAULT_ISOLATION_SETTING = ISOLATION_DEFAULT;
public final static String DEFAULT_MANAGEMENT_SETTING = MANAGEMENT_GLOBAL;
public final static String DEFAULT_PROPAGATION_SETTING = PROPAGATION_REQUIRED;
/**
* Use the default timeout of the underlying transaction system,
* or none if timeouts are not supported.
*/
public final static int DEFAULT_TIMEOUT = -1;
/**
* Holder for management setting.
* Global transactions are managed by the application server, using JTA.
* Local transactions are resource-specific: for example, a transaction associated with a JDBC connection.
* This choice had profound implications.
* Global transactions provide the ability to work with multiple transactional resources.
* (It's worth noting that most applications use a single transaction resource)
* With local transactions, the application server is not involved in transaction management,
* and cannot help ensure correctness across multiple resources.
*/
protected String management;
/**
* Holder for propagation setting.
* Normally all code executed within a transaction scope will run in that transaction.
* However, there are several options specifying behavior if a transactional method
* is executed when a transaction context already exists:
* For example, simply running in the existing transaction (the most common case);
* or suspending the existing transaction and creating a new transaction
*/
protected String propagation;
/**
* holder for isolation setting.
* isolation: The degree of isolation this transaction has from the work of other transactions.
* For example, can this transaction see uncommitted writes from other transactions?
*/
protected String isolation;
/**
* Holder for timeout setting.
* The degree of isolation this transaction has from the work of other transactions.
* For example, can this transaction see uncommitted writes from other transactions?
*/
protected int timeout = -1;
public TransactionBehaviour() {
super();
}
public TransactionBehaviour(String management, String propagation, String isolation, int timeout) {
this.management = management;
this.propagation = propagation;
this.isolation = isolation;
this.timeout = timeout;
}
/**
* Default instance of TransactionBehaviour with all
* properties set to its defaults, being
* MANAGEMENT_GLOBAL, PROPAGATION_REQUIRED, ISOLATION_DEFAULT, TIMEOUT_DEFAULT
*/
public static final TransactionBehaviour createDefault(){
return new TransactionBehaviour(DEFAULT_MANAGEMENT_SETTING,DEFAULT_PROPAGATION_SETTING,DEFAULT_ISOLATION_SETTING,DEFAULT_TIMEOUT);
}
/**
* convenience method to check whether to use a local transaction
* according to the properties
* @return
*/
public boolean useLocal() {
return MANAGEMENT_LOCAL.equals(management);
}
/**
* convenience method to check whether the transaction should behave
* managed or not
* according to the properties
* @return true if the transaction must be managed iow when managed!=MANAGEMENT_NO
*/
public boolean isManaged() {
return !MANAGEMENT_NO.equals(management);
}
/**
* convenience method to check whether to use a global transaction
* according to the properties
* @return
*/
public boolean useGlobal() {
return MANAGEMENT_GLOBAL.equals(management);
}
public String getIsolation() {
return isolation;
}
public void setIsolation(String isolation) {
this.isolation = isolation;
}
public String getManagement() {
return management;
}
public void setManagement(String management) {
this.management = management;
}
public String getPropagation() {
return propagation;
}
public void setPropagation(String propagation) {
this.propagation = propagation;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public void validate() throws ValidationException {
boolean found = false;
for (int i = 0; !found && i < MANAGEMENT_OPTIONS.length; i++) {
found = (MANAGEMENT_OPTIONS[i].equals(this.management));
}
if(!found)
throw new ValidationException("management option ["+management+"] is not applicable");
for (int i = 0; !found && i < PROPAGATION_OPTIONS.length; i++) {
found = (PROPAGATION_OPTIONS[i].equals(this.isolation));
}
if(!found)
throw new ValidationException("propagation option ["+propagation+"] is not applicable");
for (int i = 0; !found && i < ISOLATION_OPTIONS.length; i++) {
found = (ISOLATION_OPTIONS[i].equals(this.isolation));
}
if(!found)
throw new ValidationException("isolation option ["+isolation+"] is not applicable");
if (timeout < DEFAULT_TIMEOUT) {
throw new IllegalArgumentException("Timeout must be a positive integer or TIMEOUT_DEFAULT");
}
}
}