/*
* Copyright 2012-2017 the original author or authors.
*
* 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 org.springframework.boot.jta.atomikos;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Bean friendly variant of
* <a href="http://www.atomikos.com/Documentation/JtaProperties">Atomikos configuration
* properties</a>. Allows for setter based configuration and is amiable to relaxed data
* binding.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
* @see #asProperties()
*/
@ConfigurationProperties(prefix = "spring.jta.atomikos.properties")
public class AtomikosProperties {
private final Map<String, String> values = new TreeMap<>();
/**
* Transaction manager implementation that should be started.
*/
private String service;
/**
* Maximum timeout (in milliseconds) that can be allowed for transactions.
*/
private long maxTimeout = 300000;
/**
* Default timeout for JTA transactions.
*/
private long defaultJtaTimeout = 10000;
/**
* Maximum number of active transactions.
*/
private int maxActives = 50;
/**
* Enable disk logging.
*/
private boolean enableLogging = true;
/**
* Transaction manager's unique name. Defaults to the machine's IP address. If you
* plan to run more than one transaction manager against one database you must set
* this property to a unique value.
*/
private String transactionManagerUniqueName;
/**
* Specify if sub-transactions should be joined when possible.
*/
private boolean serialJtaTransactions = true;
/**
* Specify if a VM shutdown should trigger forced shutdown of the transaction core.
*/
private boolean forceShutdownOnVmExit;
/**
* Transactions log file base name.
*/
private String logBaseName = "tmlog";
/**
* Directory in which the log files should be stored. Defaults to the current working
* directory.
*/
private String logBaseDir;
/**
* Interval between checkpoints. A checkpoint reduces the log file size at the expense
* of adding some overhead in the runtime.
*/
private long checkpointInterval = 500;
/**
* Console log level.
*/
private AtomikosLoggingLevel consoleLogLevel = AtomikosLoggingLevel.WARN;
/**
* Directory in which to store the debug log files. Defaults to the current working
* directory.
*/
private String outputDir;
/**
* Debug logs file name.
*/
private String consoleFileName = "tm.out";
/**
* Number of debug logs files that can be created.
*/
private int consoleFileCount = 1;
/**
* How many bytes can be stored at most in debug logs files. Negative values means
* unlimited.
*/
private int consoleFileLimit = -1;
/**
* Use different (and concurrent) threads for two-phase commit on the participating
* resources.
*/
private boolean threadedTwoPhaseCommit = true;
/**
* Specifies the transaction manager implementation that should be started. There is
* no default value and this must be set. Generally,
* {@literal com.atomikos.icatch.standalone.UserTransactionServiceFactory} is the
* value you should set.
* @param service the service
*/
public void setService(String service) {
this.service = service;
set("service", service);
}
public String getService() {
return this.service;
}
/**
* Specifies the maximum timeout (in milliseconds) that can be allowed for
* transactions. Defaults to {@literal 300000}. This means that calls to
* UserTransaction.setTransactionTimeout() with a value higher than configured here
* will be max'ed to this value.
* @param maxTimeout the max timeout
*/
public void setMaxTimeout(long maxTimeout) {
this.maxTimeout = maxTimeout;
set("max_timeout", maxTimeout);
}
public long getMaxTimeout() {
return this.maxTimeout;
}
/**
* The default timeout for JTA transactions (optional, defaults to {@literal 10000}
* ms).
* @param defaultJtaTimeout the default JTA timeout
*/
public void setDefaultJtaTimeout(long defaultJtaTimeout) {
this.defaultJtaTimeout = defaultJtaTimeout;
set("default_jta_timeout", defaultJtaTimeout);
}
public long getDefaultJtaTimeout() {
return this.defaultJtaTimeout;
}
/**
* Specifies the maximum number of active transactions. Defaults to {@literal 50}. A
* negative value means infinite amount. You will get an {@code IllegalStateException}
* with error message "Max number of active transactions reached" if you call
* {@code UserTransaction.begin()} while there are already n concurrent transactions
* running, n being this value.
* @param maxActives the max activities
*/
public void setMaxActives(int maxActives) {
this.maxActives = maxActives;
set("max_actives", maxActives);
}
public int getMaxActives() {
return this.maxActives;
}
/**
* Specifies if disk logging should be enabled or not. Defaults to true. It is useful
* for JUnit testing, or to profile code without seeing the transaction manager's
* activity as a hot spot but this should never be disabled on production or data
* integrity cannot be guaranteed.
* @param enableLogging if logging is enabled
*/
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
set("enable_logging", enableLogging);
}
public boolean isEnableLogging() {
return this.enableLogging;
}
/**
* Specifies the transaction manager's unique name. Defaults to the machine's IP
* address. If you plan to run more than one transaction manager against one database
* you must set this property to a unique value or you might run into duplicate
* transaction ID (XID) problems that can be quite subtle (example:
* {@literal http://fogbugz.atomikos.com/default.asp?community.6.2225.7}). If multiple
* instances need to use the same properties file then the easiest way to ensure
* uniqueness for this property is by referencing a system property specified at VM
* startup.
* @param uniqueName the unique name
*/
public void setTransactionManagerUniqueName(String uniqueName) {
this.transactionManagerUniqueName = uniqueName;
set("tm_unique_name", uniqueName);
}
public String getTransactionManagerUniqueName() {
return this.transactionManagerUniqueName;
}
/**
* Specifies if subtransactions should be joined when possible. Defaults to true. When
* false, no attempt to call {@code XAResource.start(TM_JOIN)} will be made for
* different but related subtransactions. This setting has no effect on resource
* access within one and the same transaction. If you don't use subtransactions then
* this setting can be ignored.
* @param serialJtaTransactions if serial JTA transaction are supported
*/
public void setSerialJtaTransactions(boolean serialJtaTransactions) {
this.serialJtaTransactions = serialJtaTransactions;
set("serial_jta_transactions", serialJtaTransactions);
}
public boolean isSerialJtaTransactions() {
return this.serialJtaTransactions;
}
/**
* Specifies whether VM shutdown should trigger forced shutdown of the transaction
* core. Defaults to false.
* @param forceShutdownOnVmExit if VM shutdown should be forced
*/
public void setForceShutdownOnVmExit(boolean forceShutdownOnVmExit) {
this.forceShutdownOnVmExit = forceShutdownOnVmExit;
set("force_shutdown_on_vm_exit", forceShutdownOnVmExit);
}
public boolean isForceShutdownOnVmExit() {
return this.forceShutdownOnVmExit;
}
/**
* Specifies the transactions log file base name. Defaults to {@literal tmlog}. The
* transactions logs are stored in files using this name appended with a number and
* the extension {@literal .log}. At checkpoint, a new transactions log file is
* created and the number is incremented.
* @param logBaseName the log base name
*/
public void setLogBaseName(String logBaseName) {
this.logBaseName = logBaseName;
set("log_base_name", logBaseName);
}
public String getLogBaseName() {
return this.logBaseName;
}
/**
* Specifies the directory in which the log files should be stored. Defaults to the
* current working directory. This directory should be a stable storage like a SAN,
* RAID or at least backed up location. The transactions logs files are as important
* as the data themselves to guarantee consistency in case of failures.
* @param logBaseDir the log base dir
*/
public void setLogBaseDir(String logBaseDir) {
this.logBaseDir = logBaseDir;
set("log_base_dir", logBaseDir);
}
public String getLogBaseDir() {
return this.logBaseDir;
}
/**
* Specifies the interval between checkpoints. A checkpoint reduces the log file size
* at the expense of adding some overhead in the runtime. Defaults to {@literal 500}.
* @param checkpointInterval the checkpoint interval
*/
public void setCheckpointInterval(long checkpointInterval) {
this.checkpointInterval = checkpointInterval;
set("checkpoint_interval", checkpointInterval);
}
public long getCheckpointInterval() {
return this.checkpointInterval;
}
/**
* Specifies the console log level. Defaults to {@link AtomikosLoggingLevel#WARN}.
* @param consoleLogLevel the console log level
*/
public void setConsoleLogLevel(AtomikosLoggingLevel consoleLogLevel) {
this.consoleLogLevel = consoleLogLevel;
set("console_log_level", consoleLogLevel);
}
public AtomikosLoggingLevel getConsoleLogLevel() {
return this.consoleLogLevel;
}
/**
* Specifies the directory in which to store the debug log files. Defaults to the
* current working directory.
* @param outputDir the output dir
*/
public void setOutputDir(String outputDir) {
this.outputDir = outputDir;
set("output_dir", outputDir);
}
public String getOutputDir() {
return this.outputDir;
}
/**
* Specifies the debug logs file name. Defaults to {@literal tm.out}.
* @param consoleFileName the console file name
*/
public void setConsoleFileName(String consoleFileName) {
this.consoleFileName = consoleFileName;
set("console_file_name", consoleFileName);
}
public String getConsoleFileName() {
return this.consoleFileName;
}
/**
* Specifies how many debug logs files can be created. Defaults to {@literal 1}.
* @param consoleFileCount the console file count
*/
public void setConsoleFileCount(int consoleFileCount) {
this.consoleFileCount = consoleFileCount;
set("console_file_count", consoleFileCount);
}
public int getConsoleFileCount() {
return this.consoleFileCount;
}
/**
* Specifies how many bytes can be stored at most in debug logs files. Defaults to
* {@literal -1}. Negative values means unlimited.
* @param consoleFileLimit the console file limit
*/
public void setConsoleFileLimit(int consoleFileLimit) {
this.consoleFileLimit = consoleFileLimit;
set("console_file_limit", consoleFileLimit);
}
public int getConsoleFileLimit() {
return this.consoleFileLimit;
}
/**
* Specifies whether or not to use different (and concurrent) threads for two-phase
* commit on the participating resources. Setting this to {@literal true} implies that
* the commit is more efficient since waiting for acknowledgements is done in
* parallel. Defaults to {@literal true}. If you set this to {@literal false}, then
* commits will happen in the order that resources are accessed within the
* transaction.
* @param threadedTwoPhaseCommit if threaded two phase commits should be used
*/
public void setThreadedTwoPhaseCommit(boolean threadedTwoPhaseCommit) {
this.threadedTwoPhaseCommit = threadedTwoPhaseCommit;
set("threaded_2pc", threadedTwoPhaseCommit);
}
public boolean isThreadedTwoPhaseCommit() {
return this.threadedTwoPhaseCommit;
}
private void set(String key, Object value) {
set("com.atomikos.icatch.", key, value);
}
private void set(String keyPrefix, String key, Object value) {
if (value != null) {
this.values.put(keyPrefix + key, value.toString());
}
else {
this.values.remove(keyPrefix + key);
}
}
/**
* Returns the properties as a {@link Properties} object that can be used with
* Atomikos.
* @return the properties
*/
public Properties asProperties() {
Properties properties = new Properties();
properties.putAll(this.values);
return properties;
}
}