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);
+        }
+
+    }
+
+}


Reply via email to