Author: ggrzybek
Date: Tue May 9 09:03:56 2017
New Revision: 1794508
URL: http://svn.apache.org/viewvc?rev=1794508&view=rev
Log:
[ARIES-1719] Adjust HOWL log on configuration change
Added:
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionLogUtils.java
aries/trunk/transaction/transaction-manager/src/test/java/org/apache/aries/transaction/internal/LogConversionTest.java
Modified:
aries/trunk/transaction/transaction-itests/src/test/java/org/apache/aries/transaction/itests/AbstractIntegrationTest.java
aries/trunk/transaction/transaction-manager/pom.xml
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/Activator.java
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionManagerService.java
aries/trunk/transaction/transaction-manager/src/main/resources/org/apache/aries/transaction/internal/txManager.properties
Modified:
aries/trunk/transaction/transaction-itests/src/test/java/org/apache/aries/transaction/itests/AbstractIntegrationTest.java
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-itests/src/test/java/org/apache/aries/transaction/itests/AbstractIntegrationTest.java?rev=1794508&r1=1794507&r2=1794508&view=diff
==============================================================================
---
aries/trunk/transaction/transaction-itests/src/test/java/org/apache/aries/transaction/itests/AbstractIntegrationTest.java
(original)
+++
aries/trunk/transaction/transaction-itests/src/test/java/org/apache/aries/transaction/itests/AbstractIntegrationTest.java
Tue May 9 09:03:56 2017
@@ -74,6 +74,8 @@ public abstract class AbstractIntegratio
// this is how you set the default log level when using pax
// logging (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ // this option helps with debugging
+
//vmOption("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006"),
when(localRepo !=
null).useOptions(vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" +
localRepo))
);
}
Modified: aries/trunk/transaction/transaction-manager/pom.xml
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/pom.xml?rev=1794508&r1=1794507&r2=1794508&view=diff
==============================================================================
--- aries/trunk/transaction/transaction-manager/pom.xml (original)
+++ aries/trunk/transaction/transaction-manager/pom.xml Tue May 9 09:03:56 2017
@@ -135,6 +135,12 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.5</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
Modified:
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/Activator.java
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/Activator.java?rev=1794508&r1=1794507&r2=1794508&view=diff
==============================================================================
---
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/Activator.java
(original)
+++
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/Activator.java
Tue May 9 09:03:56 2017
@@ -16,6 +16,7 @@
*/
package org.apache.aries.transaction.internal;
+import java.io.IOException;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
@@ -31,6 +32,9 @@ import org.osgi.service.cm.ManagedServic
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static
org.apache.aries.transaction.internal.TransactionManagerService.DEFAULT_RECOVERABLE;
+import static
org.apache.aries.transaction.internal.TransactionManagerService.RECOVERABLE;
+
/**
*/
public class Activator implements BundleActivator, ManagedService {
@@ -41,12 +45,12 @@ public class Activator implements Bundle
private BundleContext bundleContext;
private TransactionManagerService manager;
- private Dictionary properties;
+ private Dictionary<String, ?> properties;
public void start(BundleContext bundleContext) throws Exception {
this.bundleContext = bundleContext;
// Make sure TransactionManager comes up even if no config admin is
installed
- Dictionary properties = getInitialConfig();
+ Dictionary<String, Object> properties = getInitialConfig();
updated(properties);
bundleContext.registerService(ManagedService.class.getName(), this,
getProps());
}
@@ -81,12 +85,25 @@ public class Activator implements Bundle
deleted();
}
- public synchronized void updated(@SuppressWarnings("rawtypes") Dictionary
properties) throws ConfigurationException {
+ @SuppressWarnings("unchecked")
+ public synchronized void updated(Dictionary<String, ?> properties) throws
ConfigurationException {
if (properties == null) {
properties = getProps();
}
if (!equals(this.properties, properties)) {
deleted();
+
+ // ARIES-1719 - copy tx log with different configuration
+ // we can move active transactions (LogRecordType.XACOMMIT without
XADONE)
+ // to different tx log
+ try {
+ if (TransactionManagerService.getBool(properties, RECOVERABLE,
DEFAULT_RECOVERABLE)) {
+
TransactionLogUtils.copyActiveTransactions((Dictionary<String, Object>)
this.properties, properties);
+ }
+ } catch (IOException e) {
+
log.error(NLS.MESSAGES.getMessage("exception.tx.manager.start"), e);
+ }
+
this.properties = properties;
manager = new TransactionManagerService(PID, properties,
bundleContext);
try {
@@ -97,7 +114,7 @@ public class Activator implements Bundle
}
}
- private boolean equals(Dictionary<String, Object> d1, Dictionary<String,
Object> d2) {
+ private boolean equals(Dictionary<String, ?> d1, Dictionary<String, ?> d2)
{
if (d1 == d2) {
return true;
} else if (d1 == null ^ d2 == null) {
Added:
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionLogUtils.java
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionLogUtils.java?rev=1794508&view=auto
==============================================================================
---
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionLogUtils.java
(added)
+++
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionLogUtils.java
Tue May 9 09:03:56 2017
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.aries.transaction.internal;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.geronimo.transaction.log.HOWLLog;
+import org.apache.geronimo.transaction.manager.Recovery;
+import org.apache.geronimo.transaction.manager.TransactionBranchInfo;
+import org.apache.geronimo.transaction.manager.XidFactory;
+import org.objectweb.howl.log.LogRecordType;
+import org.osgi.service.cm.ConfigurationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static
org.apache.aries.transaction.internal.TransactionManagerService.*;
+
+public class TransactionLogUtils {
+
+ public static Logger log =
LoggerFactory.getLogger(TransactionLogUtils.class);
+ private static Pattern TX_FILE_NAME =
Pattern.compile("(.*)_([0-9]+)\\.([^.]+)");
+
+ /**
+ * <p>When <code>org.apache.aries.transaction</code> PID changes, there
may be a need to copy
+ * entries from transaction log when some important configuration changed
(like block size)</p>
+ * @param oldConfiguration previous configuration when configuration
changed, may be <code>null</code> when starting bundle
+ * @param newConfiguration configuration to create new transaction manager
+ * @return <code>true</code> if there was conversion performed
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean copyActiveTransactions(Dictionary<String, Object>
oldConfiguration, Dictionary<String, ?> newConfiguration)
+ throws ConfigurationException, IOException {
+ boolean initialConfiguration = false;
+ if (oldConfiguration == null) {
+ oldConfiguration = new Hashtable<String, Object>();
+ // initialConfiguration means we don't know the location of "old"
logs (if there are any) and
+ // assume there may be logs in newLogDirectory
+ initialConfiguration = true;
+ }
+ if (oldConfiguration.get(HOWL_LOG_FILE_DIR) == null) {
+ // we will be adjusting oldConfiguration to be able to create "old
HOWLLog"
+ oldConfiguration.put(HOWL_LOG_FILE_DIR,
newConfiguration.get(HOWL_LOG_FILE_DIR));
+ }
+ String oldLogDirectory = (String)
oldConfiguration.get(HOWL_LOG_FILE_DIR);
+ String newLogDirectory = (String)
newConfiguration.get(HOWL_LOG_FILE_DIR);
+
+ if (newLogDirectory == null || oldLogDirectory == null) {
+ // handle with exceptions at TM creation time
+ return false;
+ }
+
+ File oldDir = new File(oldLogDirectory);
+ File newDir = new File(newLogDirectory);
+ // a file which may tell us what's the previous configuation
+ File transaction_1 = null;
+
+ if (!oldDir.equals(newDir)) {
+ // recent logs are in oldDir, so even if newDir contains some
logs, we will remove them
+ deleteDirectory(newDir);
+ transaction_1 = new File(oldDir,
configuredTransactionLogName(oldConfiguration, 1));
+ } else {
+ // we may need to move oldDir to some temporary location, if the
configuration is changed
+ // we'll then have to copy old tx log to new one
+ transaction_1 = new File(oldDir,
configuredTransactionLogName(oldConfiguration, 1));
+ if (!transaction_1.exists() || transaction_1.length() == 0L) {
+ oldConfiguration.put(HOWL_LOG_FILE_NAME,
getString(newConfiguration, HOWL_LOG_FILE_NAME, "transaction"));
+ oldConfiguration.put(HOWL_LOG_FILE_EXT,
getString(newConfiguration, HOWL_LOG_FILE_EXT, "log"));
+ transaction_1 = new File(oldDir,
configuredTransactionLogName(newConfiguration, 1));
+ }
+ }
+
+ if (!transaction_1.exists() || transaction_1.length() == 0L) {
+ // no need to copy anything
+ return false;
+ }
+
+ BaseTxLogConfig oldTxConfig = transactionLogFileConfig(transaction_1);
+ BaseTxLogConfig newTxConfig =
transactionLogFileConfig(newConfiguration);
+
+ if (oldTxConfig == null || oldTxConfig.equals(newTxConfig)) {
+ // old log files compatible, but maybe we have to copy them
+ if (!oldDir.equals(newDir)) {
+ if (!oldDir.renameTo(newDir)) {
+
log.warn(NLS.MESSAGES.getMessage("tx.log.problem.renaming",
oldDir.getAbsolutePath()));
+ return false;
+ }
+ }
+ // files are compatible - we'll check one more thing -
name_N.extension
+ String oldName = configuredTransactionLogName(oldConfiguration, 1);
+ String newName = configuredTransactionLogName(newConfiguration, 1);
+ if (!oldName.equals(newName)) {
+ final Dictionary<String, Object> finalOldConfiguration =
oldConfiguration;
+ final Dictionary<String, ?> finalNewConfiguration =
newConfiguration;
+ final Map<String, String> changes = new HashMap<String,
String>();
+ newDir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ Matcher matcher = TX_FILE_NAME.matcher(name);
+ if (matcher.matches()) {
+ if
(matcher.group(1).equals(getString(finalOldConfiguration, HOWL_LOG_FILE_NAME,
"transaction"))
+ &&
matcher.group(3).equals(getString(finalOldConfiguration, HOWL_LOG_FILE_EXT,
"log"))) {
+ changes.put(name, String.format("%s_%d.%s",
+ getString(finalNewConfiguration,
HOWL_LOG_FILE_NAME, "transaction"),
+ Integer.parseInt(matcher.group(2)),
+ getString(finalNewConfiguration,
HOWL_LOG_FILE_EXT, "log")));
+ }
+ }
+ return false;
+ }
+ });
+
+ for (String old : changes.keySet()) {
+ new File(newDir, old).renameTo(new File(newDir,
changes.get(old)));
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ File backupDir = null;
+ if (oldDir.equals(newDir)) {
+ // move old dir to backup dir
+ backupDir = new File(newLogDirectory + String.format("-%016x",
System.currentTimeMillis()));
+ if (!oldDir.renameTo(backupDir)) {
+ log.warn(NLS.MESSAGES.getMessage("tx.log.problem.renaming",
oldDir.getAbsolutePath()));
+ return false;
+ }
+ oldConfiguration = copy(oldConfiguration);
+ oldConfiguration.put(HOWL_LOG_FILE_DIR,
backupDir.getAbsolutePath());
+ }
+
+ log.info(NLS.MESSAGES.getMessage("tx.log.conversion",
oldDir.getAbsolutePath(), newDir.getAbsolutePath()));
+
+ oldConfiguration.put(RECOVERABLE, newConfiguration.get(RECOVERABLE));
+ oldConfiguration.put(HOWL_MAX_LOG_FILES,
Integer.toString(oldTxConfig.maxLogFiles));
+ oldConfiguration.put(HOWL_MAX_BLOCKS_PER_FILE,
Integer.toString(oldTxConfig.maxBlocksPerFile));
+ oldConfiguration.put(HOWL_BUFFER_SIZE,
Integer.toString(oldTxConfig.bufferSizeKBytes));
+
+ String tmid1 = TransactionManagerService.getString(oldConfiguration,
TMID, Activator.PID);
+ XidFactory xidFactory1 = new XidFactoryImpl(tmid1.substring(0,
Math.min(tmid1.length(), 64)).getBytes());
+ String tmid2 = TransactionManagerService.getString(newConfiguration,
TMID, Activator.PID);
+ XidFactory xidFactory2 = new XidFactoryImpl(tmid2.substring(0,
Math.min(tmid2.length(), 64)).getBytes());
+
+ org.apache.geronimo.transaction.manager.TransactionLog oldLog = null;
+ org.apache.geronimo.transaction.manager.TransactionLog newLog = null;
+ try {
+ oldLog =
TransactionManagerService.createTransactionLog(oldConfiguration, xidFactory1);
+ newLog =
TransactionManagerService.createTransactionLog(newConfiguration, xidFactory2);
+
+ if (!(oldLog instanceof HOWLLog)) {
+ log.info(NLS.MESSAGES.getMessage("tx.log.notrecoverable",
oldLogDirectory));
+ return false;
+ }
+ if (!(newLog instanceof HOWLLog)) {
+ log.info(NLS.MESSAGES.getMessage("tx.log.notrecoverable",
newLogDirectory));
+ return false;
+ }
+
+ HOWLLog from = (HOWLLog) oldLog;
+ HOWLLog to = (HOWLLog) newLog;
+
+ Collection<Recovery.XidBranchesPair> pairs =
from.recover(xidFactory1);
+ for (Recovery.XidBranchesPair xidBranchesPair : pairs) {
+ log.info(NLS.MESSAGES.getMessage("tx.log.migrate.xid",
xidBranchesPair.getXid()));
+ for (TransactionBranchInfo branchInfo :
xidBranchesPair.getBranches()) {
+
log.info(NLS.MESSAGES.getMessage("tx.log.migrate.xid.branch",
branchInfo.getBranchXid(), branchInfo.getResourceName()));
+ }
+ to.prepare(xidBranchesPair.getXid(), new
ArrayList<TransactionBranchInfo>(xidBranchesPair.getBranches()));
+ }
+ log.info(NLS.MESSAGES.getMessage("tx.log.migrate.complete"));
+ deleteDirectory(backupDir);
+
+ return !pairs.isEmpty();
+ } catch (Exception e) {
+ log.error(NLS.MESSAGES.getMessage("exception.tx.log.migration"),
e);
+ if (backupDir != null) {
+ deleteDirectory(newDir);
+ backupDir.renameTo(oldDir);
+ }
+ return false;
+ } finally {
+ try {
+ if (oldLog instanceof HOWLLog) {
+ ((HOWLLog)oldLog).doStop();
+ }
+ if (newLog instanceof HOWLLog) {
+ ((HOWLLog)newLog).doStop();
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Retrieves 3 important configuration parameters from single HOWL
transaction log file
+ * @param txFile existing HOWL file
+ * @return
+ */
+ private static BaseTxLogConfig transactionLogFileConfig(File txFile)
throws IOException {
+ FileChannel channel = new RandomAccessFile(txFile, "r").getChannel();
+ try {
+ ByteBuffer bb = ByteBuffer.wrap(new byte[1024]);
+ int read = channel.read(bb);
+ if (read < 0x47) { // enough data to have HOWL block header and
FILE_HEADER record
+ return null;
+ }
+
+ bb.rewind();
+ if (bb.getInt() != 0x484f574c) { // HOWL
+ return null;
+ }
+ bb.getInt(); // BSN
+ int bufferSizeKBytes = bb.getInt() / 1024;
+ bb.getInt(); // size
+ bb.getInt(); // checksum
+ bb.getLong(); // timestamp
+ bb.getShort(); // 0x0d0a
+ if (bb.getShort() != LogRecordType.FILE_HEADER) {
+ return null;
+ }
+ bb.getShort(); // size
+ bb.getShort(); // size
+ bb.get(); // automark
+ bb.getLong(); // active mark
+ bb.getLong(); // log key
+ bb.getLong(); // timestamp
+ int maxLogFiles = bb.getInt();
+ int maxBlocksPerFile = bb.getInt();
+ if (maxBlocksPerFile == Integer.MAX_VALUE) {
+ maxBlocksPerFile = -1;
+ }
+ bb.getShort(); // 0x0d0a
+
+ return new BaseTxLogConfig(maxLogFiles, maxBlocksPerFile,
bufferSizeKBytes);
+ } finally {
+ channel.close();
+ }
+ }
+
+ /**
+ * Retrieves 3 important configuration parameters from configuration
+ * @param configuration
+ * @return
+ */
+ private static BaseTxLogConfig transactionLogFileConfig(Dictionary<String,
?> configuration) throws ConfigurationException {
+ BaseTxLogConfig result = new BaseTxLogConfig();
+ result.maxLogFiles = getInt(configuration, HOWL_MAX_LOG_FILES, 2);
+ result.maxBlocksPerFile = getInt(configuration,
HOWL_MAX_BLOCKS_PER_FILE, -1);
+ result.bufferSizeKBytes = getInt(configuration, HOWL_BUFFER_SIZE, 4);
+ return result;
+ }
+
+ private static String configuredTransactionLogName(Dictionary<String, ?>
configuration, int number) throws ConfigurationException {
+ String logFileName = getString(configuration, HOWL_LOG_FILE_NAME,
"transaction");
+ String logFileExt = getString(configuration, HOWL_LOG_FILE_EXT, "log");
+ return String.format("%s_%d.%s", logFileName, number, logFileExt);
+ }
+
+ private static Dictionary<String, Object> copy(Dictionary<String, Object>
configuration) {
+ Dictionary<String, Object> result = new Hashtable<String, Object>();
+ for (Enumeration<String> keys = configuration.keys();
keys.hasMoreElements(); ) {
+ String k = keys.nextElement();
+ result.put(k, configuration.get(k));
+ }
+ return result;
+ }
+
+ /**
+ * Recursively delete directory with content
+ * @param file
+ */
+ private static boolean deleteDirectory(File file) {
+ if (file == null) {
+ return false;
+ }
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (File f : files) {
+ deleteDirectory(f);
+ }
+ }
+ return file.delete();
+ } else {
+ if (!file.delete()) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private static class BaseTxLogConfig {
+
+ public int maxLogFiles;
+ public int maxBlocksPerFile;
+ public int bufferSizeKBytes;
+
+ public BaseTxLogConfig() {
+ }
+
+ public BaseTxLogConfig(int maxLogFiles, int maxBlocksPerFile, int
bufferSizeKBytes) {
+ this.maxLogFiles = maxLogFiles;
+ this.maxBlocksPerFile = maxBlocksPerFile;
+ this.bufferSizeKBytes = bufferSizeKBytes;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BaseTxLogConfig that = (BaseTxLogConfig) o;
+
+ if (maxLogFiles != that.maxLogFiles) return false;
+ if (maxBlocksPerFile != that.maxBlocksPerFile) return false;
+ if (bufferSizeKBytes != that.bufferSizeKBytes) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = maxLogFiles;
+ result = 31 * result + maxBlocksPerFile;
+ result = 31 * result + bufferSizeKBytes;
+ return result;
+ }
+
+ }
+
+}
Modified:
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionManagerService.java
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionManagerService.java?rev=1794508&r1=1794507&r2=1794508&view=diff
==============================================================================
---
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionManagerService.java
(original)
+++
aries/trunk/transaction/transaction-manager/src/main/java/org/apache/aries/transaction/internal/TransactionManagerService.java
Tue May 9 09:03:56 2017
@@ -78,67 +78,16 @@ public class TransactionManagerService {
this.properties = properties;
this.bundleContext = bundleContext;
// Transaction timeout
- int transactionTimeout = getInt(TRANSACTION_TIMEOUT,
DEFAULT_TRANSACTION_TIMEOUT);
+ int transactionTimeout = getInt(this.properties, TRANSACTION_TIMEOUT,
DEFAULT_TRANSACTION_TIMEOUT);
if (transactionTimeout <= 0) {
throw new ConfigurationException(TRANSACTION_TIMEOUT,
NLS.MESSAGES.getMessage("tx.timeout.greaterthan.zero"));
}
- final String tmid = getString(TMID, pid);
+ final String tmid = getString(this.properties, TMID, pid);
// the max length of the factory should be 64
XidFactory xidFactory = new XidFactoryImpl(tmid.substring(0,
Math.min(tmid.length(), 64)).getBytes());
// Transaction log
- if (getBool(RECOVERABLE, DEFAULT_RECOVERABLE)) {
- String bufferClassName = getString(HOWL_BUFFER_CLASS_NAME,
"org.objectweb.howl.log.BlockLogBuffer");
- int bufferSizeKBytes = getInt(HOWL_BUFFER_SIZE, 4);
- if (bufferSizeKBytes < 1 || bufferSizeKBytes > 32) {
- throw new ConfigurationException(HOWL_BUFFER_SIZE,
NLS.MESSAGES.getMessage("buffer.size.between.one.and.thirtytwo"));
- }
- boolean checksumEnabled = getBool(HOWL_CHECKSUM_ENABLED, true);
- boolean adler32Checksum = getBool(HOWL_ADLER32_CHECKSUM, true);
- int flushSleepTimeMilliseconds = getInt(HOWL_FLUSH_SLEEP_TIME, 50);
- String logFileExt = getString(HOWL_LOG_FILE_EXT, "log");
- String logFileName = getString(HOWL_LOG_FILE_NAME, "transaction");
- int maxBlocksPerFile = getInt(HOWL_MAX_BLOCKS_PER_FILE, -1);
- int maxLogFiles = getInt(HOWL_MAX_LOG_FILES, 2);
- int minBuffers = getInt(HOWL_MIN_BUFFERS, 4);
- if (minBuffers < 0) {
- throw new ConfigurationException(HOWL_MIN_BUFFERS,
NLS.MESSAGES.getMessage("min.buffers.greaterthan.zero"));
- }
- int maxBuffers = getInt(HOWL_MAX_BUFFERS, 0);
- if (maxBuffers > 0 && minBuffers < maxBuffers) {
- throw new ConfigurationException(HOWL_MAX_BUFFERS,
NLS.MESSAGES.getMessage("max.buffers.greaterthan.min.buffers"));
- }
- int threadsWaitingForceThreshold =
getInt(HOWL_THREADS_WAITING_FORCE_THRESHOLD, -1);
- boolean flushPartialBuffers = getBool(HOWL_FLUSH_PARTIAL_BUFFERS,
true);
- String logFileDir = getString(HOWL_LOG_FILE_DIR, null);
- if (logFileDir == null || logFileDir.length() == 0 || !new
File(logFileDir).isAbsolute()) {
- throw new ConfigurationException(HOWL_LOG_FILE_DIR,
NLS.MESSAGES.getMessage("log.file.dir"));
- }
- try {
- transactionLog = new HOWLLog(bufferClassName,
- bufferSizeKBytes,
- checksumEnabled,
- adler32Checksum,
- flushSleepTimeMilliseconds,
- logFileDir,
- logFileExt,
- logFileName,
- maxBlocksPerFile,
- maxBuffers,
- maxLogFiles,
- minBuffers,
- threadsWaitingForceThreshold,
- flushPartialBuffers,
- xidFactory,
- null);
- ((HOWLLog) transactionLog).doStart();
- } catch (Exception e) {
- // This should not really happen as we've checked properties
earlier
- throw new ConfigurationException(null, e.getMessage(), e);
- }
- } else {
- transactionLog = new UnrecoverableLog();
- }
+ transactionLog = createTransactionLog(this.properties, xidFactory);
// Create transaction manager
try {
try {
@@ -180,7 +129,7 @@ public class TransactionManagerService {
}
}
- private String getString(String property, String dflt) throws
ConfigurationException {
+ static String getString(Dictionary properties, String property, String
dflt) {
String value = (String) properties.get(property);
if (value != null) {
return value;
@@ -188,7 +137,7 @@ public class TransactionManagerService {
return dflt;
}
- private int getInt(String property, int dflt) throws
ConfigurationException {
+ static int getInt(Dictionary properties, String property, int dflt) throws
ConfigurationException {
String value = (String) properties.get(property);
if (value != null) {
try {
@@ -200,7 +149,7 @@ public class TransactionManagerService {
return dflt;
}
- private boolean getBool(String property, boolean dflt) throws
ConfigurationException {
+ static boolean getBool(Dictionary properties, String property, boolean
dflt) throws ConfigurationException {
String value = (String) properties.get(property);
if (value != null) {
try {
@@ -212,6 +161,64 @@ public class TransactionManagerService {
return dflt;
}
+ static TransactionLog createTransactionLog(Dictionary properties,
XidFactory xidFactory) throws ConfigurationException {
+ TransactionLog result = null;
+ if (getBool(properties, RECOVERABLE, DEFAULT_RECOVERABLE)) {
+ String bufferClassName = getString(properties,
HOWL_BUFFER_CLASS_NAME, "org.objectweb.howl.log.BlockLogBuffer");
+ int bufferSizeKBytes = getInt(properties, HOWL_BUFFER_SIZE, 4);
+ if (bufferSizeKBytes < 1 || bufferSizeKBytes > 32) {
+ throw new ConfigurationException(HOWL_BUFFER_SIZE,
NLS.MESSAGES.getMessage("buffer.size.between.one.and.thirtytwo"));
+ }
+ boolean checksumEnabled = getBool(properties,
HOWL_CHECKSUM_ENABLED, true);
+ boolean adler32Checksum = getBool(properties,
HOWL_ADLER32_CHECKSUM, true);
+ int flushSleepTimeMilliseconds = getInt(properties,
HOWL_FLUSH_SLEEP_TIME, 50);
+ String logFileExt = getString(properties, HOWL_LOG_FILE_EXT,
"log");
+ String logFileName = getString(properties, HOWL_LOG_FILE_NAME,
"transaction");
+ int maxBlocksPerFile = getInt(properties,
HOWL_MAX_BLOCKS_PER_FILE, -1);
+ int maxLogFiles = getInt(properties, HOWL_MAX_LOG_FILES, 2);
+ int minBuffers = getInt(properties, HOWL_MIN_BUFFERS, 4);
+ if (minBuffers < 0) {
+ throw new ConfigurationException(HOWL_MIN_BUFFERS,
NLS.MESSAGES.getMessage("min.buffers.greaterthan.zero"));
+ }
+ int maxBuffers = getInt(properties, HOWL_MAX_BUFFERS, 0);
+ if (maxBuffers > 0 && minBuffers < maxBuffers) {
+ throw new ConfigurationException(HOWL_MAX_BUFFERS,
NLS.MESSAGES.getMessage("max.buffers.greaterthan.min.buffers"));
+ }
+ int threadsWaitingForceThreshold = getInt(properties,
HOWL_THREADS_WAITING_FORCE_THRESHOLD, -1);
+ boolean flushPartialBuffers = getBool(properties,
HOWL_FLUSH_PARTIAL_BUFFERS, true);
+ String logFileDir = getString(properties, HOWL_LOG_FILE_DIR, null);
+ if (logFileDir == null || logFileDir.length() == 0 || !new
File(logFileDir).isAbsolute()) {
+ throw new ConfigurationException(HOWL_LOG_FILE_DIR,
NLS.MESSAGES.getMessage("log.file.dir"));
+ }
+ try {
+ result = new HOWLLog(bufferClassName,
+ bufferSizeKBytes,
+ checksumEnabled,
+ adler32Checksum,
+ flushSleepTimeMilliseconds,
+ logFileDir,
+ logFileExt,
+ logFileName,
+ maxBlocksPerFile,
+ maxBuffers,
+ maxLogFiles,
+ minBuffers,
+ threadsWaitingForceThreshold,
+ flushPartialBuffers,
+ xidFactory,
+ null);
+ ((HOWLLog) result).doStart();
+ } catch (Exception e) {
+ // This should not really happen as we've checked properties
earlier
+ throw new ConfigurationException(null, e.getMessage(), e);
+ }
+ } else {
+ result = new UnrecoverableLog();
+ }
+
+ return result;
+ }
+
/**
* We use an inner static class to decouple this class from the spring-tx
classes
* in order to not have NoClassDefFoundError if those are not present.
Modified:
aries/trunk/transaction/transaction-manager/src/main/resources/org/apache/aries/transaction/internal/txManager.properties
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/src/main/resources/org/apache/aries/transaction/internal/txManager.properties?rev=1794508&r1=1794507&r2=1794508&view=diff
==============================================================================
---
aries/trunk/transaction/transaction-manager/src/main/resources/org/apache/aries/transaction/internal/txManager.properties
(original)
+++
aries/trunk/transaction/transaction-manager/src/main/resources/org/apache/aries/transaction/internal/txManager.properties
Tue May 9 09:03:56 2017
@@ -32,5 +32,10 @@ prop.value.not.int=The property {0} shou
# {1} The property value
prop.value.not.boolean=The property {0} should have an boolean value, but the
value {1} is not a boolean.
-
-
+tx.log.problem.renaming=Can't backup old transaction logs directory: {0}
+tx.log.conversion=Copying transaction log from {0} to {1}
+tx.log.notrecoverable=TransactionLog {0} is not recoverable
+tx.log.migrate.xid=Copying active transaction with XID {0}
+tx.log.migrate.xid.branch=- Copying branch {0} for resource {1}
+tx.log.migrate.complete=Migration of active transactions finished
+exception.tx.log.migration=An exception occurred while trying to migrate
transaction log after changing configuration.
Added:
aries/trunk/transaction/transaction-manager/src/test/java/org/apache/aries/transaction/internal/LogConversionTest.java
URL:
http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/src/test/java/org/apache/aries/transaction/internal/LogConversionTest.java?rev=1794508&view=auto
==============================================================================
---
aries/trunk/transaction/transaction-manager/src/test/java/org/apache/aries/transaction/internal/LogConversionTest.java
(added)
+++
aries/trunk/transaction/transaction-manager/src/test/java/org/apache/aries/transaction/internal/LogConversionTest.java
Tue May 9 09:03:56 2017
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.aries.transaction.internal;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import javax.transaction.xa.Xid;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.geronimo.transaction.log.HOWLLog;
+import org.apache.geronimo.transaction.manager.TransactionBranchInfo;
+import org.apache.geronimo.transaction.manager.XidFactory;
+import org.apache.geronimo.transaction.manager.XidImpl;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class LogConversionTest {
+
+ public static Logger LOG =
LoggerFactory.getLogger(LogConversionTest.class);
+ private static XidFactory xidFactory = new
TestXidFactoryImpl("org.apache.aries.transaction.test".getBytes());
+ private static File BASE = new File(System.getProperty("user.dir"),
"txlogs");
+
+ private static long start = 42L;
+ private static long count = start;
+
+ @Test
+ public void initialConfiguration() throws Exception {
+ File logDir = new File(BASE, "initialConfiguration");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Dictionary<String, Object> properties = new Hashtable<String,
Object>();
+ HOWLLog txLog = createLog("initialConfiguration", "transaction", 2,
-1, 1, properties);
+
+ assertFalse(TransactionLogUtils.copyActiveTransactions(null,
properties));
+ }
+
+ @Test
+ public void initialConfigurationEmptyTransactionLog() throws Exception {
+ File logDir = new File(BASE,
"initialConfigurationEmptyTransactionLog");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Dictionary<String, Object> properties = new Hashtable<String,
Object>();
+ HOWLLog txLog = createLog("initialConfigurationEmptyTransactionLog",
"transaction", 2, -1, 1, properties);
+ new RandomAccessFile(new File(logDir, "transaction_1.log"),
"rw").close();
+ new RandomAccessFile(new File(logDir, "transaction_2.log"),
"rw").close();
+
+ assertFalse(TransactionLogUtils.copyActiveTransactions(null,
properties));
+ }
+
+ @Test
+ public void initialConfigurationExistingTransactionLogNoChanges() throws
Exception {
+ File logDir = new File(BASE,
"initialConfigurationExistingTransactionLogNoChanges");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Dictionary<String, Object> properties = new Hashtable<String,
Object>();
+ HOWLLog txLog =
createLog("initialConfigurationExistingTransactionLogNoChanges", "transaction",
2, -1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ assertFalse(TransactionLogUtils.copyActiveTransactions(null,
properties));
+ assertThat("Transaction log should not be touched", new File(logDir,
"transaction_1.log").lastModified(), equalTo(lm));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void existingTransactionLogChangedLogDir() throws Exception {
+ File logDir = new File(BASE, "existingTransactionLogChangedLogDir");
+ File newLogDir = new File(BASE,
"existingTransactionLogChangedLogDir-new");
+ FileUtils.deleteDirectory(logDir);
+ FileUtils.deleteDirectory(newLogDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog = createLog("existingTransactionLogChangedLogDir",
"transaction", 2, -1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.logFileDir",
newLogDir.getAbsolutePath());
+
+ assertFalse(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertThat("Transaction log should not be touched", new
File(newLogDir, "transaction_1.log").lastModified(), equalTo(lm));
+ assertFalse("Old transaction log should be moved", logDir.exists());
+ assertTrue("New transaction log should be created",
newLogDir.exists());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void unknownExistingTransactionLogChangedLogDir() throws Exception {
+ File logDir = new File(BASE,
"unknownExistingTransactionLogChangedLogDir");
+ File newLogDir = new File(BASE,
"unknownExistingTransactionLogChangedLogDir-new");
+ FileUtils.deleteDirectory(logDir);
+ FileUtils.deleteDirectory(newLogDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog =
createLog("unknownExistingTransactionLogChangedLogDir", "transaction", 2, -1,
1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.logFileDir",
newLogDir.getAbsolutePath());
+
+ assertFalse(TransactionLogUtils.copyActiveTransactions(null,
newConfig));
+ assertFalse("Transaction log should not exist", new File(newLogDir,
"transaction_1.log").exists());
+ assertTrue("Old transaction log should not be removed",
logDir.exists());
+ assertFalse("New transaction log should not be created (will be
created on TX Manager startup)", newLogDir.exists());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void changedNumberOfFiles() throws Exception {
+ File logDir = new File(BASE, "changedNumberOfFiles");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog = createLog("changedNumberOfFiles", "transaction", 2,
-1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.maxLogFiles", "20");
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertTrue("Transaction log should exist", new File(logDir,
"transaction_1.log").exists());
+ assertTrue("There should be 20 transaction log files", new
File(logDir, "transaction_20.log").exists());
+ assertThat("Transaction log should be processed", new File(logDir,
"transaction_1.log").lastModified(), not(equalTo(lm)));
+ }
+
+ private long earlierLastModified(File file) {
+ file.setLastModified(file.lastModified() - 10000L);
+ return file.lastModified();
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void changedMaxBlocksPerFile() throws Exception {
+ File logDir = new File(BASE, "changedMaxBlocksPerFile");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog = createLog("changedMaxBlocksPerFile", "transaction", 3,
-1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.maxBlocksPerFile", "20");
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertTrue("Transaction log should exist", new File(logDir,
"transaction_1.log").exists());
+ assertFalse("There should be 3 transaction log files", new
File(logDir, "transaction_4.log").exists());
+ assertThat("Transaction log should be processed", new File(logDir,
"transaction_1.log").lastModified(), not(equalTo(lm)));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void changedBlockSize() throws Exception {
+ File logDir = new File(BASE, "changedBlockSize");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog = createLog("changedBlockSize", "transaction", 3, -1, 1,
properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.bufferSize", "32");
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertTrue("Transaction log should exist", new File(logDir,
"transaction_1.log").exists());
+ assertThat("Transaction log should be processed", new File(logDir,
"transaction_1.log").lastModified(), not(equalTo(lm)));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void changed3ParametersAndLogDir() throws Exception {
+ File logDir = new File(BASE, "changed3ParametersAndLogDir");
+ File newLogDir = new File(BASE, "changed3ParametersAndLogDir-new");
+ FileUtils.deleteDirectory(logDir);
+ FileUtils.deleteDirectory(newLogDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog = createLog("changed3ParametersAndLogDir",
"transaction", 3, -1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.maxLogFiles", "4");
+ newConfig.put("aries.transaction.howl.maxBlocksPerFile", "4");
+ newConfig.put("aries.transaction.howl.bufferSize", "4");
+ newConfig.put("aries.transaction.howl.logFileDir",
newLogDir.getAbsolutePath());
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertTrue("Old transaction log should exist", new File(logDir,
"transaction_1.log").exists());
+ assertTrue("New transaction log should exist", new File(newLogDir,
"transaction_1.log").exists());
+ assertThat("Old transaction log should be touched (HOWL Log opened)",
new File(logDir, "transaction_1.log").lastModified(), not(equalTo(lm)));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void existingTransactionLogChangedLogFileName() throws Exception {
+ File logDir = new File(BASE,
"existingTransactionLogChangedLogFileName");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog = createLog("existingTransactionLogChangedLogFileName",
"transaction", 2, -1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.logFileName", "megatransaction");
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertFalse("Old transaction log should not exist", new File(logDir,
"transaction_1.log").exists());
+ assertTrue("New transaction log should exist", new File(logDir,
"megatransaction_1.log").exists());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void existingTransactionLogChangedLogFileNameAndLogDir() throws
Exception {
+ File logDir = new File(BASE,
"existingTransactionLogChangedLogFileNameAndLogDir");
+ File newLogDir = new File(BASE,
"existingTransactionLogChangedLogFileNameAndLogDir-new");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog =
createLog("existingTransactionLogChangedLogFileNameAndLogDir", "transaction",
2, -1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.logFileName", "megatransaction");
+ newConfig.put("aries.transaction.howl.logFileDir",
newLogDir.getAbsolutePath());
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertFalse("Old transaction log should not exist", new File(logDir,
"transaction_1.log").exists());
+ assertTrue("New transaction log should exist", new File(newLogDir,
"megatransaction_1.log").exists());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void existingTransactionLogChangedLogFileNameAndBlockSize() throws
Exception {
+ File logDir = new File(BASE,
"existingTransactionLogChangedLogFileNameAndBlockSize");
+ FileUtils.deleteDirectory(logDir);
+ logDir.mkdirs();
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ HOWLLog txLog =
createLog("existingTransactionLogChangedLogFileNameAndBlockSize",
"transaction", 2, -1, 1, properties);
+ txLog.doStart();
+ transaction(txLog, 1, false);
+ txLog.doStop();
+
+ long lm = earlierLastModified(new File(logDir, "transaction_1.log"));
+
+ Hashtable<String, Object> newConfig = (Hashtable<String, Object>)
properties.clone();
+ newConfig.put("aries.transaction.howl.logFileName", "megatransaction");
+ newConfig.put("aries.transaction.howl.bufferSize", "4");
+
+ assertTrue(TransactionLogUtils.copyActiveTransactions(properties,
newConfig));
+ assertThat("Old transaction log should be touched (HOWL Log opened)",
new File(logDir, "transaction_1.log").lastModified(), not(equalTo(lm)));
+ assertTrue("New transaction log should exist", new File(logDir,
"megatransaction_1.log").exists());
+ }
+
+ private HOWLLog createLog(String logFileDir, String logFileName,
+ int maxLogFiles, int maxBlocksPerFile, int
bufferSizeInKB,
+ Dictionary<String, Object> properties) throws
Exception {
+ properties.put("aries.transaction.recoverable", "true");
+ properties.put("aries.transaction.howl.bufferClassName",
"org.objectweb.howl.log.BlockLogBuffer");
+ properties.put("aries.transaction.howl.checksumEnabled", "true");
+ properties.put("aries.transaction.howl.adler32Checksum", "true");
+ properties.put("aries.transaction.howl.flushSleepTime", "50");
+ properties.put("aries.transaction.howl.logFileExt", "log");
+ properties.put("aries.transaction.howl.logFileName", logFileName);
+ properties.put("aries.transaction.howl.minBuffers", "1");
+ properties.put("aries.transaction.howl.maxBuffers", "0");
+ properties.put("aries.transaction.howl.threadsWaitingForceThreshold",
"-1");
+ properties.put("aries.transaction.flushPartialBuffers", "true");
+ String absoluteLogFileDir = new File(BASE,
logFileDir).getAbsolutePath();
+ properties.put("aries.transaction.howl.logFileDir",
absoluteLogFileDir);
+ properties.put("aries.transaction.howl.bufferSize",
Integer.toString(bufferSizeInKB));
+ properties.put("aries.transaction.howl.maxBlocksPerFile",
Integer.toString(maxBlocksPerFile));
+ properties.put("aries.transaction.howl.maxLogFiles",
Integer.toString(maxLogFiles));
+
+ return new HOWLLog("org.objectweb.howl.log.BlockLogBuffer",
bufferSizeInKB,
+ true, true, 50,
+ absoluteLogFileDir, "log", logFileName,
+ maxBlocksPerFile, 0, maxLogFiles, 1, -1, true, xidFactory,
null);
+ }
+
+ private void transaction(HOWLLog log, int transactionBranchCount, boolean
commit) throws Exception {
+ Xid xid = xidFactory.createXid();
+ List<TransactionBranchInfo> txBranches = new
LinkedList<TransactionBranchInfo>();
+ for (int b = 1; b <= transactionBranchCount; b++) {
+ // TransactionImpl.enlistResource()
+ Xid branchXid = xidFactory.createBranch(xid, b);
+ txBranches.add(new TestTransactionBranchInfo(branchXid,
String.format("res-%02d", b)));
+ }
+
+ //
org.apache.geronimo.transaction.manager.TransactionImpl.internalPrepare()
+ Object logMark = log.prepare(xid, txBranches);
+ if (commit) {
+ // org.apache.geronimo.transaction.manager.CommitTask.run()
+ log.commit(xid, logMark);
+ }
+ }
+
+ private static class TestTransactionBranchInfo implements
TransactionBranchInfo {
+
+ private final Xid xid;
+ private final String name;
+
+ public TestTransactionBranchInfo(Xid xid, String name) {
+ this.xid = xid;
+ this.name = name;
+ }
+
+ @Override
+ public String getResourceName() {
+ return name;
+ }
+
+ @Override
+ public Xid getBranchXid() {
+ return xid;
+ }
+
+ }
+
+ private static class TestXidFactoryImpl extends XidFactoryImpl {
+
+ private final byte[] baseId = new byte[Xid.MAXGTRIDSIZE];
+
+ public TestXidFactoryImpl(byte[] tmId) {
+ super(tmId);
+ System.arraycopy(tmId, 0, baseId, 8, tmId.length);
+ }
+
+ @Override
+ public Xid createXid() {
+ byte[] globalId = baseId.clone();
+ long id;
+ synchronized (this) {
+ id = count++;
+ }
+ insertLong(id, globalId, 0);
+ return new XidImpl(globalId);
+ }
+
+ @Override
+ public Xid createBranch(Xid globalId, int branch) {
+ byte[] branchId = baseId.clone();
+ branchId[0] = (byte) branch;
+ branchId[1] = (byte) (branch >>> 8);
+ branchId[2] = (byte) (branch >>> 16);
+ branchId[3] = (byte) (branch >>> 24);
+ insertLong(start, branchId, 4);
+ return new XidImpl(globalId, branchId);
+ }
+
+ }
+
+}