[jboss-svn-commits] JBL Code SVN: r21957 - in labs/jbosstm/workspace/transactionalFileIO: trunk and 25 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Thu Aug 28 11:21:54 EDT 2008
Author: jhalliday
Date: 2008-08-28 11:21:53 -0400 (Thu, 28 Aug 2008)
New Revision: 21957
Added:
labs/jbosstm/workspace/transactionalFileIO/trunk/
labs/jbosstm/workspace/transactionalFileIO/trunk/build.xml
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txdirs/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txdirs/demo/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txdirs/demo/TXDirsDemoApp.java
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txfiles/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txfiles/demo/
labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txfiles/demo/TXFilesDemoApp.java
labs/jbosstm/workspace/transactionalFileIO/trunk/docs/
labs/jbosstm/workspace/transactionalFileIO/trunk/docs/UserGuide_TXFileInJava_v1.0.odt
labs/jbosstm/workspace/transactionalFileIO/trunk/src/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/DataOutputStream.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/ObjectOutputStreamAppend.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/Globals.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADir.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADirFile.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XAFileResourceManager.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/IncompleteTransactionsException.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/NotDirectoryException.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/DuplicateTransactionsException.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/LockRefusedException.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/DataRecord.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAFile.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAResourceManager.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALock.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALockManager.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/LogEntry.java
labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/RecordsLogger.java
Log:
Added initial release of the Transaction File I/O project.
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/build.xml
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/build.xml (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/build.xml 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ JBoss, Home of Professional Open Source
+ Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ as indicated by the @author tags.
+ See the copyright.txt in the distribution for a full listing
+ of individual contributors.
+ This copyrighted material is made available to anyone wishing to use,
+ modify, copy, or redistribute it subject to the terms and conditions
+ of the GNU Lesser General Public License, v. 2.1.
+ This program is distributed in the hope that it will be useful, but WITHOUT A
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public License,
+ v.2.1 along with this distribution; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA.
+
+ (C) 2008,
+ @author Red Hat Middleware LLC.
+-->
+<project name="jbossts-fileio" default="compile">
+
+ <!-- set to the dir in which JBossTS 4.4 is installed. http://www.jboss.org/jbosstm/ -->
+ <property name="jbossts.home" value="/put/something/here"/>
+
+ <!-- set to the dir in which Apache Commons Transaction is installed. http://commons.apache.org/transaction/
+ This is required only for transaction directory support. -->
+ <property name="apache.home" value="/put/something/here"/>
+
+ <!-- you should not need to change anything below this point -->
+
+ <property name="build" value="build"/>
+ <property name="debug" value="true"/>
+
+ <path id="txfiles.classpath">
+ <pathelement location="${jbossts.home}/lib/ext/jta-1_1-classes.zip"/>
+ <pathelement location="${jbossts.home}/lib/jbossjta.jar"/>
+ </path>
+
+ <path id="txdirs.classpath">
+ <pathelement location="${apache.home}/commons-transaction-1.2.jar"/>
+ <pathelement location="${apache.home}/lib/commons-logging-1.1.jar"/>
+ </path>
+
+ <path id="demo.runtime.classpath">
+ <pathelement location="${build}/classes"/>
+ <fileset dir="${jbossts.home}/lib/">
+ <include name="*.jar"/>
+ <include name="ext/*.jar"/>
+ <include name="ext/*.zip"/>
+ </fileset>
+ <pathelement location="${jbossts.home}/etc"/>
+ </path>
+
+ <target name="clean" description="cleanup module">
+ <delete dir="${build}"/>
+ </target>
+
+ <target name="clean-demo" description="cleanup files used by the demo app">
+ <delete dir="PutObjectStoreDirHere"/>
+ <delete dir="Logging"/>
+ <delete dir="Locks"/>
+ <delete file="entries.txt"/>
+ <delete file="transaction.log"/>
+ <delete dir="businesstxdir"/>
+ </target>
+
+ <target name="init" description="initialize build">
+ <mkdir dir="${build}"/>
+ <mkdir dir="${build}/classes"/>
+ <available file="${apache.home}" property="have.apache.commons.tx" value="true"/>
+ </target>
+
+ <target name="compile" depends="init" description="compile package">
+ <javac destdir="${build}/classes" debug="${debug}" srcdir="src">
+ <exclude name="**/txdirs/**" unless="have.apache.commons.tx"/>
+ <classpath refid="txfiles.classpath"/>
+ <classpath refid="txdirs.classpath"/>
+ </javac>
+ <javac destdir="${build}/classes" debug="${debug}" srcdir="demo-src">
+ <exclude name="**/txdirs/**" unless="have.apache.commons.tx"/>
+ <classpath refid="txfiles.classpath"/>
+ <classpath refid="txdirs.classpath"/>
+ </javac>
+ </target>
+
+ <target name="jar" depends="compile" description="build lib jar file">
+ <jar basedir="${build}/classes" file="${build}/jbossts-fileio.jar">
+ <exclude name="**/demo/**"/>
+ </jar>
+ </target>
+
+ <target name="javadoc" depends="init" description="generate the javadoc for the lib">
+ <javadoc destdir="${build}/docs" sourcepath="src"/>
+ </target>
+
+ <target name="txfiles-demo" depends="compile" description="run the txfiles demo">
+ <java classname="org.jboss.jbossts.fileio.xalib.txfiles.demo.TXFilesDemoApp" fork="true">
+ <classpath refid="demo.runtime.classpath"/>
+ </java>
+ </target>
+
+ <target name="txdirs-demo" depends="compile" description="run the txdirs demo">
+ <java classname="org.jboss.jbossts.fileio.xalib.txdirs.demo.TXDirsDemoApp" fork="true">
+ <classpath>
+ <path refid="demo.runtime.classpath"/>
+ <path refid="txdirs.classpath"/>
+ </classpath>
+ </java>
+ </target>
+
+</project>
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txdirs/demo/TXDirsDemoApp.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txdirs/demo/TXDirsDemoApp.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txdirs/demo/TXDirsDemoApp.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,140 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txdirs.demo;
+
+import org.jboss.jbossts.fileio.xalib.txdirs.dir.XADir;
+import org.jboss.jbossts.fileio.xalib.txdirs.dir.XADirFile;
+import javax.transaction.TransactionManager;
+import java.io.File;
+import java.io.IOException;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
+
+/**
+ * This is a demo class.
+ * Demonstrates a file operation applied transactionally on a file
+ * under some directory.
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 23, 2008
+ */
+public class TXDirsDemoApp
+{
+ final String EXISTING_FILENAME = "file.txt";
+ final String NEW_FILENAME = "new_file.txt";
+ final String TX_FOLDER_NAME = "businesstxdir";
+ XADir xadir;
+ TransactionManager txMngr;
+
+ public TXDirsDemoApp() throws Exception {
+ initialiseFiles();
+
+ xadir = new XADir(new File(TX_FOLDER_NAME));
+ txMngr = new TransactionManagerImple();
+
+ executeDemo();
+ }
+
+ /**
+ * Creates a directory if it does not exists and also
+ * creates a file in it. The file will be used to apply
+ * file operations on it.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ private void initialiseFiles() throws IOException {
+ System.out.println("######### Initialise:Begin #########");
+ File folder = new File(TX_FOLDER_NAME);
+ if (folder.exists()) {
+ deleteFiles(folder.listFiles());
+ folder.delete();
+ }
+ // Create the folder and a file in it
+ folder.mkdir();
+ File file = new File(TX_FOLDER_NAME + "/" + EXISTING_FILENAME);
+ file.createNewFile();
+ System.out.println("--- File name in the tx directory: <" + file.getName() + "> ---");
+ System.out.println("######### Initialise:End #########\n");
+ }
+
+ /**
+ * Deletes all the given <code>files</code>
+ *
+ * @param files the files to delete
+ */
+ private void deleteFiles(File[] files) {
+ for (File file : files) {
+ if (file.isDirectory())
+ deleteFiles(file.listFiles());
+ file.delete();
+ }
+ }
+
+ /**
+ * The method performs a simple rename operation to an existing
+ * file in the transactional directory during the execution of a
+ * transaction.
+ *
+ * @exception Exception if an error occurs during the execution of
+ * the transaction
+ */
+ private void executeDemo() throws Exception {
+ try {
+ txMngr.begin();
+ {
+ System.out.println("######### TransactionManager:Started #########");
+ xadir.startTransactionOn(txMngr);
+
+ // Get a list of all the files in the directory
+ XADirFile[] files = xadir.listTXFiles();
+
+ // Apply a file operation - rename old file to something else
+ files[0].renameTo(new File(NEW_FILENAME));
+ System.out.println("------ TX renamed old file ------");
+ System.out.println("------ TX reads new filename: <" + files[0].getName() + "> ------");
+
+ System.out.println("--- Actual filename: <" + getFileName() + "> ---");
+ }
+ txMngr.commit();
+ System.out.println("######### TransactionManager:Committed #########\n");
+ System.out.println("--- Actual filename: <" + getFileName() + "> ---");
+ } catch (Exception e) {
+ txMngr.rollback();
+ System.out.println("XXXXXXXX TransactionManager:Rolled-back XXXXXXXX\n");
+ System.out.println("--- Actual filename: <" + getFileName() + "> ---");
+ }
+ xadir.close();
+ }
+
+ /**
+ * Returns the name of the file in the folder
+ *
+ * @return a <code>String</code> representing the name of the
+ * file in the folder
+ */
+ private String getFileName() {
+ File folder = new File(TX_FOLDER_NAME);
+ return folder.list()[0]; // return the filename
+ }
+
+ public static void main (String[] args) throws Exception {
+ new TXDirsDemoApp();
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txfiles/demo/TXFilesDemoApp.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txfiles/demo/TXFilesDemoApp.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/demo-src/org/jboss/jbossts/fileio/xalib/txfiles/demo/TXFilesDemoApp.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,129 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.demo;
+
+import org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile;
+import javax.transaction.TransactionManager;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * This is a demo class.
+ * It demonstrates how to apply read and write operations transactionally
+ * to a file
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 20, 2008
+ */
+public class TXFilesDemoApp
+{
+ final String FILENAME = "entries.txt";
+ final String MSG = "The result of the computation is: ";
+ final byte COMPUTATION_RESULT = 49;
+ XAFile xaFile;
+ TransactionManager txMngr;
+
+ public TXFilesDemoApp() throws Exception {
+ initialiseData();
+ xaFile = new XAFile(FILENAME, "rw", true);
+ txMngr = new TransactionManagerImple();
+
+ executeDemo();
+
+ }
+
+ /**
+ * Crreates a file if it does not exist, and writes some
+ * data to it.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ private void initialiseData() throws IOException {
+ System.out.println("######### Initialise:Begin #########");
+ RandomAccessFile raf = new RandomAccessFile(FILENAME, "rw");
+ raf.writeBytes(MSG);
+ raf.write(COMPUTATION_RESULT);
+ raf.seek(0);
+ System.out.println(raf.readLine());
+ raf.close();
+ System.out.println("######### Initialise:End #########\n");
+ }
+
+ /**
+ * Applies simple write and read operations to demonstrate the behaviour
+ * of the {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile}.
+ *
+ * @exception Exception if an error occurs during the transaction
+ */
+ private void executeDemo() throws Exception {
+ try {
+ txMngr.begin();
+ {
+ System.out.println("######### TransactionManager:Started #########");
+ xaFile.newTransaction(txMngr);
+ // ---------- Read the current value ----------
+ xaFile.seek(MSG.length()); // seek to the end of the message
+ byte result = xaFile.readByte();
+
+ // ---------- Modify the current value ----------
+ result++; // increment by 1
+
+ // ---------- Write the new value to the file ----------
+ xaFile.seek(MSG.length());
+ xaFile.write(result);
+ System.out.println("------ TX modifies data in the file ------");
+
+ // Request a read operation whilst within a tx
+ xaFile.seek(0);
+ System.out.println("------ TX reads data in the file: <" + xaFile.readLine() + "> ------");
+
+ System.out.println("--- Actual data in file: <" + readActualData() + "> ---");
+ }
+ txMngr.commit();
+ System.out.println("######### TransactionManager:Committed #########\n");
+ System.out.println("--- Actual data in file: <" + readActualData() + "> ---");
+ } catch (Exception e) {
+ txMngr.rollback();
+ System.out.println("XXXXXXXX TransactionManager:Rolled-back XXXXXXXX\n");
+ System.out.println("--- Actual data in file: <" + readActualData() + "> ---");
+ e.printStackTrace();
+ }
+ xaFile.close();
+ }
+
+ /**
+ * Reads data from the file.
+ *
+ * @return a <code>String</code> containing the data in the file. *
+ * @exception IOException if an I/O error occurs
+ */
+ private String readActualData() throws IOException {
+ RandomAccessFile raf = new RandomAccessFile(FILENAME, "r");
+ String line = raf.readLine();
+ raf.close();
+ return line;
+ }
+
+ public static void main(String[] args) throws Exception {
+ new TXFilesDemoApp();
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/docs/UserGuide_TXFileInJava_v1.0.odt
===================================================================
(Binary files differ)
Property changes on: labs/jbosstm/workspace/transactionalFileIO/trunk/docs/UserGuide_TXFileInJava_v1.0.odt
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/DataOutputStream.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/DataOutputStream.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/DataOutputStream.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,135 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.UTFDataFormatException;
+import java.io.OutputStream;
+
+/**
+ * User: Ioannis Ganotis
+ * Date: Aug 5, 2008
+ */
+public class DataOutputStream extends java.io.DataOutputStream
+{
+ /**
+ * bytearr is initialized on demand by writeUTF
+ */
+ private byte[] bytearr = null;
+
+ /**
+ * Creates a new data output stream to write data to the specified
+ * underlying output stream. The counter <code>written</code> is
+ * set to zero.
+ *
+ * @param out the underlying output stream, to be saved for later
+ * use.
+ * @see java.io.FilterOutputStream#out
+ */
+ public DataOutputStream(OutputStream out) {
+ super(out);
+ }
+
+
+ /**
+ * Writes a string to the specified DataOutput using
+ * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
+ * encoding in a machine-independent manner.
+ * <p/>
+ * First, two bytes are written to out as if by the <code>writeShort</code>
+ * method giving the number of bytes to follow. This value is the number of
+ * bytes actually written out, not the length of the string. Following the
+ * length, each character of the string is output, in sequence, using the
+ * modified UTF-8 encoding for the character. If no exception is thrown, the
+ * counter <code>written</code> is incremented by the total number of
+ * bytes written to the output stream. This will be at least two
+ * plus the length of <code>str</code>, and at most two plus
+ * thrice the length of <code>str</code>.
+ *
+ * @param str a string to be written.
+ * @param out destination to write to
+ * @return The number of bytes written out.
+ * @throws java.io.IOException if an I/O error occurs.
+ */
+ public static int writeUTF(String str, DataOutput out) throws IOException {
+ int strlen = str.length();
+ int utflen = 0;
+ int c, count = 0;
+
+ /* use charAt instead of copying String to char array */
+ for (int i = 0; i < strlen; i++) {
+ c = str.charAt(i);
+ if ((c >= 0x0001) && (c <= 0x007F)) {
+ utflen++;
+ } else if (c > 0x07FF) {
+ utflen += 3;
+ } else {
+ utflen += 2;
+ }
+ }
+
+ if (utflen > 65535) {
+ throw new UTFDataFormatException(
+ "encoded string too long: " + utflen + " bytes");
+ }
+
+ byte[] bytearr;
+ if (out instanceof DataOutputStream) {
+ DataOutputStream dos = (DataOutputStream) out;
+ if (dos.bytearr == null || (dos.bytearr.length < (utflen + 2))) {
+ dos.bytearr = new byte[(utflen * 2) + 2];
+ }
+ bytearr = dos.bytearr;
+ } else {
+ bytearr = new byte[utflen + 2];
+ }
+
+ bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
+ bytearr[count++] = (byte) ((utflen) & 0xFF);
+
+ int i;
+ for (i = 0; i < strlen; i++) {
+ c = str.charAt(i);
+ if (!((c >= 0x0001) && (c <= 0x007F))) {
+ break;
+ }
+ bytearr[count++] = (byte) c;
+ }
+
+ for (; i < strlen; i++) {
+ c = str.charAt(i);
+ if ((c >= 0x0001) && (c <= 0x007F)) {
+ bytearr[count++] = (byte) c;
+
+ } else if (c > 0x07FF) {
+ bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
+ bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
+ bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
+ } else {
+ bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
+ bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
+ }
+ }
+ out.write(bytearr, 0, utflen + 2);
+ return utflen + 2;
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/ObjectOutputStreamAppend.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/ObjectOutputStreamAppend.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/ObjectOutputStreamAppend.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio;
+
+import java.io.OutputStream;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+
+/**
+ * User: igan
+ * Date: Jul 27, 2008
+ */
+public class ObjectOutputStreamAppend extends ObjectOutputStream
+{
+ public ObjectOutputStreamAppend(OutputStream os) throws IOException {
+ super(os);
+ }
+
+ @Override
+ protected void writeStreamHeader() throws IOException {
+ reset();
+ }
+}
+
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/Globals.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/Globals.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/Globals.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib;
+
+/**
+ * This class defines some global constants used by the
+ * <code>XA_lib</code>.
+ *
+ * @author Ioannis Ganotis
+ * @version Jun 12, 2008
+ */
+public class Globals
+{
+ //-------------------- txfiles --------------------
+ public static final String LOG_FOLDER_PATH = "Logging";
+ public static final String LOCKS_FOLDER_PATH = "Locks/";
+ public static final int THREAD_TIMEOUT = 15000;
+ public static final int TX_GROUPS = 20;
+ // Used in Lock policies
+ public static final int NO_MOD_LOCK = 100;
+ public static final int REFUSE_LOCK = -100;
+ public static final int UPDATE_OLD_LOCK = 50;
+ public static final int ADD_NEW_LOCK = 1;
+ public static final int MOVE_LOCK_BOUNDS = 200;
+ // Recovery
+ public static final long RECOVERY_ID = -100;
+
+ // -------------------- txdirs --------------------
+ public static final String WORK_DIR_NAME = "txDir_work";
+}
\ No newline at end of file
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADir.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADir.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADir.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,206 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txdirs.dir;
+
+import org.apache.commons.transaction.file.FileResourceManager;
+import org.apache.commons.transaction.file.ResourceManagerException;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.PrintWriterLogger;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.jbossts.fileio.xalib.txdirs.exceptions.NotDirectoryException;
+import org.jboss.jbossts.fileio.xalib.txdirs.exceptions.IncompleteTransactionsException;
+import org.jboss.jbossts.fileio.xalib.Globals;
+import javax.transaction.TransactionManager;
+import javax.transaction.Transaction;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import java.util.LinkedList;
+import java.io.*;
+
+/**
+ * Instances of this class represent a transactional directory on which
+ * file operations such as create, rename and delete can be applied. The
+ * contents (files) of this directory are represented by {@link XADirFile}
+ * objects. Through these objects the application programmer can access the
+ * file operations.
+ * <p>
+ * It provides methods to list the files in the directory and access the
+ * {@link org.apache.commons.transaction.file.FileResourceManager} of the Apache
+ * commons transaction project.
+ *
+ * @see XADirFile
+ * @see org.apache.commons.transaction.file.FileResourceManager
+ * @see org.jboss.jbossts.fileio.xalib.txdirs.dir.XAFileResourceManager
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 6, 2008
+ */
+public class XADir implements Serializable, Closeable
+{
+ private String curTxId;
+ transient private FileResourceManager freMngr;
+ private long length;
+
+ /**
+ * Constructor to create objects that represent a transactional directory.
+ * <p>
+ * The constructor checks if the <code>storeDir</code> is a directory or not.
+ * If it is not then {@link org.jboss.jbossts.fileio.xalib.txdirs.exceptions.NotDirectoryException}
+ * exception is thrown. Otherwise, a new <code>FileResourceManager</code> object
+ * is created to allow access to transactional methods (e.g. start, commit, rollback
+ * a transaction)
+ *
+ * @param storeDir directory where main data should go after commit
+ * @exception org.jboss.jbossts.fileio.xalib.txdirs.exceptions.NotDirectoryException
+ * if the <code>storeDir</code> is not a directory
+ * @exception org.apache.commons.transaction.file.ResourceManagerException
+ * if an error in the <code>FileResourceManager</code> occurs
+ */
+ public XADir(File storeDir) throws IOException, ResourceManagerException {
+ if (!storeDir.exists()) {
+ storeDir.mkdir();
+ } else {
+ if (!storeDir.isDirectory())
+ throw new NotDirectoryException("The file given is not a directory.");
+ }
+
+ length = storeDir.list().length;
+ String workDir = storeDir.getCanonicalPath() + "/" + Globals.WORK_DIR_NAME;
+ freMngr = new FileResourceManager(storeDir.getCanonicalPath(), workDir, false,
+ new PrintWriterLogger(new PrintWriter(System.out),
+ XADirFile.class.getName(), false));
+ freMngr.start(); // start the FileResourceManager service, must be started
+ } // before using any of its methods
+
+ /**
+ * This method lists all the files under the transactional directory.
+ * It lists only files and not <code>File</code>s as in Java this may
+ * also mean directories.
+ * @return a list of <code>XADirFile</code> objects which represent
+ * the files within the directory
+ */
+ public synchronized XADirFile[] listTXFiles() {
+ File dir = new File(freMngr.getStoreDir());
+ File[] files = dir.listFiles();
+ LinkedList<XADirFile> xaDirFileList = new LinkedList<XADirFile>();
+
+ for (File f : files) {
+ if (!f.isDirectory()) {
+ xaDirFileList.add(new XADirFile(f, this));
+ }
+ }
+ XADirFile[] xaDirFiles = new XADirFile[xaDirFileList.size()];
+ xaDirFileList.toArray(xaDirFiles);
+ return xaDirFiles;
+ }
+
+ /**
+ * This method must be used after a <code>TransactionManager</code>
+ * has begun and within the boundaries of a transaction (<code>begin,
+ * commit/rollback</code>).
+ * <p>
+ * The method also creates a new {@link XAFileResourceManager} and
+ * enlists it to the transaction obtained by the <code>txnMngr</code>.
+ *
+ * @param txnMngr the <code>TransactionManager</code> used to commit
+ * or rollback the file operations
+ * @exception javax.transaction.RollbackException
+ * if an error occurs while enlisting the <code>XAResource</code>
+ * @exception javax.transaction.SystemException
+ * if there is a problem enlisting the XAResource created
+ * within this method or when the <code>getTransaction</code> in the
+ * <code>TransactionManager</code> fails or when the TransactionManager
+ * is not in ready (<code>Status.ACTIVE</code>) mode
+ */
+ public synchronized void startTransactionOn(TransactionManager txnMngr)
+ throws SystemException, RollbackException {
+ curTxId = "txDir-" + freMngr.getWorkDir().replace('/', '_') + "_" +
+ Thread.currentThread().getId() + "!" + System.nanoTime();
+ XAFileResourceManager xafre = new XAFileResourceManager(freMngr, curTxId);
+ Transaction tx = txnMngr.getTransaction();
+ tx.enlistResource(xafre);
+ }
+
+ /**
+ * Returns the <code>FileResourceManager</code> object used to access
+ * transaction and file operations (e.g. startTransaction, copyResource).
+ *
+ * @return the <code>FileResourceManager</code> object
+ */
+ protected FileResourceManager getFreMngr() {
+ return freMngr;
+ }
+
+ /**
+ * This is the name of the "shadow" folder which keeps the changes whilst
+ * a transaction is still in progress.
+ *
+ * @return a <code>String</code> which contains the name of the "shadow"
+ * folder
+ */
+ protected String getCurTxId() {
+ return curTxId;
+ }
+
+ /**
+ * Increases the counter of files in the directory by one
+ */
+ protected void increaseLength() {
+ length++;
+ }
+
+ /**
+ * Decreases the counter of files in the directory by one
+ */
+ protected void decreaseLength() {
+ length--;
+ }
+
+ /**
+ * Returns the number of files (not directories) under this
+ * transactional directory.
+ *
+ * @return a <code>long</code> representing the number of files
+ * within the transactional directory
+ */
+ public long length() {
+ return length;
+ }
+
+ /**
+ * As this class represents a transactional directory, the application
+ * programmer must call this method after his transactional work is over.
+ * The method will release and remove any of the "shadow" files/folders
+ * used whilist the transaction was in progress.
+ * <p>
+ * If there are pending transactions the method will fail to close.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ public void close() throws IOException {
+ File store = new File (freMngr.getWorkDir());
+ if (store.list().length != 0) // pending txs exist
+ throw new IncompleteTransactionsException();
+
+ store.delete();
+ freMngr = null;
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADirFile.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADirFile.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XADirFile.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,170 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txdirs.dir;
+
+import org.apache.commons.transaction.file.ResourceManagerException;
+import org.apache.commons.transaction.file.FileResourceManager;
+import org.apache.commons.transaction.locking.GenericLock;
+import org.apache.commons.transaction.locking.LockManager;
+import org.apache.commons.transaction.locking.ReadWriteLockManager;
+import org.apache.commons.transaction.locking.ReadWriteUpgradeLockManager;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.PrintWriterLogger;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.*;
+
+/**
+ * Instances of this class are used by {@link XADir} objects to
+ * apply file operations. These operations can include creating
+ * new, deletinig and renaming files.
+ * <p>
+ * Each of these file operations invoke methods from the Apache's
+ * {@link org.apache.commons.transaction.file.FileResourceManager} object
+ * to manipulate operations on the files.
+ *
+ * @see XADir
+ * @see org.apache.commons.transaction.file.FileResourceManager
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 7, 2008
+ */
+public class XADirFile implements Serializable
+{
+ transient private FileResourceManager freMngr;
+ private String curTxId;
+ private String curObjId;
+ private XADir xadir;
+ private String filename;
+
+ /**
+ * Constructor to create <code>XADirFile</code> objects.
+ * These objects can be used to apply file operations.
+ *
+ * @param file the <code>File</code> object that represents
+ * the file in the transactional directory
+ * @param xadir the transactional directory
+ */
+ public XADirFile(File file, XADir xadir) {
+ filename = file.getName();
+ this.freMngr = xadir.getFreMngr();
+ this.curTxId = xadir.getCurTxId();
+ curObjId = "/" + file.getName();
+ this.xadir = xadir;
+ }
+
+ /**
+ * Returns the name of the file.
+ *
+ * @return <code>String</code> representing the name of the
+ * file in the transactional directory
+ */
+ public String getName() {
+ return filename;
+ }
+
+ /**
+ * Returns an InputStream containing the bytes of this
+ * <code>XADirFile</code> file.
+ *
+ * @return an <code>InputStream</code> object
+ * @exception ResourceManagerException ]
+ * if an error in the <code>ResourceManager</code> occurs
+ */
+ public InputStream readResource() throws ResourceManagerException {
+ try {
+ String obj = freMngr.getStoreDir() + curObjId;
+ return new FileInputStream(new File(obj));
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Returns an OutputStream containing the bytes written to this
+ * <code>XADirFile</code> file.
+ *
+ * @return an <code>OutputStream</code>
+ * @throws ResourceManagerException
+ * if an error in the <code>ResourceManager</code> occurs
+ */
+ public OutputStream writeResource() throws ResourceManagerException {
+ return freMngr.writeResource(curTxId, curObjId);
+ }
+
+ /**
+ * Renames this file to the name of the file given by <code>file</code>.
+ *
+ * @param file the <codeFile</code> object that contains the new file name
+ * @return true if the rename operations completed successfully;false otherwise
+ *
+ */
+ public synchronized boolean renameTo(File file) {
+ String resId = "/" + file.getName();
+ try {
+ freMngr.lockResource(curObjId, curTxId, false, false, 0, true);
+ freMngr.copyResource(curTxId, curObjId, resId, true);
+ freMngr.deleteResource(curTxId, curObjId);
+ curObjId = resId;
+ filename = file.getName();
+ return true;
+ } catch (ResourceManagerException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Create a new file in the disk. If the file already exists the method
+ * will return false; otherwise true.
+ *
+ * @return true if the file was created successfully; false otherwise
+ */
+ public boolean createNewFile() {
+ try {
+ freMngr.createResource(curTxId, curObjId, false);
+ xadir.increaseLength();
+ return true;
+ } catch (ResourceManagerException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * This method will delete the file. If the file does not
+ * exist it will return false; Otherwise true.
+ *
+ * @return true if the file could be deleted successfully;
+ * false otherwise.
+ */
+ public boolean delete() {
+ try {
+ freMngr.deleteResource(curTxId, curObjId, false);
+ xadir.decreaseLength();
+ return true;
+ } catch (ResourceManagerException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XAFileResourceManager.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XAFileResourceManager.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/dir/XAFileResourceManager.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,295 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txdirs.dir;
+
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import javax.transaction.xa.XAException;
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+import java.io.File;
+
+import org.apache.commons.transaction.file.*;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.jbossts.fileio.xalib.Globals;
+
+/**
+ * This class implements methods of the standard {@link javax.transaction.xa.XAResource} interface
+ * which acts like a contract between a Resource Manager and a Transaction
+ * Manager.
+ * <p>
+ * Each instance of an <code>XAFileResourceManager</code> has its own {@link javax.transaction.xa.Xid}
+ * which distinguishes by the other objects of this class and is associated
+ * with a Transaction. It also has a unique transaction id which is represented by
+ * a <code>String</code> and is used by {@link org.apache.commons.transaction.file.FileResourceManager}
+ * to know which transaction to start, commit/rollback etc.
+ * <p>
+ * Any changes made within a transaction specified by the <code>Xid</code> mentioned above
+ * will be applied to the transactional directory only after this <code>XAFileResourceManager</code>
+ * has commited. If the decision is to rollback then all the modifications made within the transaction
+ * are removed.
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 6, 2008
+ */
+public class XAFileResourceManager implements XAResource, Serializable
+{
+ private Xid currentXid;
+ private String curTxId;
+ transient private FileResourceManager freMngr; // todo must be setializable
+ private boolean recovers;
+ private String storeDir;
+
+ /**
+ * Constructor to create Resource Manager objects. Each of these
+ * objects, at transaction time, are informed by the Transaction
+ * Manager to prepare, commit or rollback (depending on the outcome
+ * of the 2PC protocol).
+ *
+ * @param freMngr the Resource Manager on which methods like start, commit
+ * or rollback a transaction will be applied
+ * @param curTxId the unique transaction id used by <code>freMngr</code> to
+ * know on which transaction to work
+ */
+ public XAFileResourceManager(FileResourceManager freMngr, String curTxId) {
+ this.freMngr = freMngr;
+ this.curTxId = curTxId;
+ storeDir = freMngr.getStoreDir();
+ recovers = false;
+ }
+
+ /**
+ * Acts like the {@link org.apache.commons.transaction.file.FileResourceManager#prepareTransaction(Object)}
+ * method does.
+ *
+ * @param xid the global transaction id
+ * @throws XAException
+ * if a <code>ResourceManagerException</code> is thrown
+ */
+ public int prepare(Xid xid) throws XAException {
+ // flush data on disk here
+ System.out.println("XAFileResourceManager.prepare(Xid=" + xid + ")");
+ try {
+ return freMngr.prepareTransaction(curTxId);
+ } catch (ResourceManagerException rme) {
+ throw new XAException(rme.getMessage());
+ }
+ }
+
+ /**
+ * Method to commit the global transaction with the given <code>xid</code>.
+ * <p/>
+ * It also acts like the {@link org.apache.commons.transaction.file.FileResourceManager#commitTransaction(Object)}
+ * does.
+ *
+ * @param xid a global Transaction id
+ * @param onePhase If true, the resource manager should use a one-phase
+ * commit protocol to commit the work done on behalf of xid
+ * @throws XAException
+ * if a <code>ResourceManagerException</code> is thrown
+ */
+ public void commit(Xid xid, boolean onePhase) throws XAException {
+ System.out.println("XAFileResourceManager.commit(Xid=" + xid + ", onePhase=" +
+ onePhase + ")");
+ if (!xid.equals(currentXid)) {
+ System.out.println("XAFileResourceManager.commit - wrong Xid!");
+ }
+ try {
+ if (!recovers) {
+ freMngr.commitTransaction(curTxId);
+ } else {
+ // the initFREM() method will take care of incomplete txs
+ }
+ } catch (ResourceManagerException rme) {
+ throw new XAException(rme.getMessage());
+ }
+ currentXid = null;
+ }
+
+ /**
+ * Ends the work performed on behalf of a transaction branch.
+ *
+ * @param xid a global transaction identifier that is the same as what was used
+ * previously in the start method
+ * @param flags (can be anything)
+ */
+ public void end(Xid xid, int flags) {
+ System.out.println("XAFileResourceManager.end(Xid=" + xid + ", flags=" + flags + ")");
+ }
+
+ /**
+ * Forget about a heuristically completed transaction branch.
+ * The <code>currentXid</code> is set to null
+ *
+ * @param xid a global Transaction id
+ */
+ public void forget(Xid xid) {
+ System.out.println("XAFileResourceManager.forget(Xid=" + xid + ")");
+ if (!xid.equals(currentXid)) {
+ System.out.println("XAFileResourceManager.forget - wrong Xid!");
+ }
+ currentXid = null;
+ }
+
+ /**
+ * Obtain the current transaction timeout value set for this
+ * <code>XAFileResourceManager</code> instance. If
+ * <code>XAFileResourceManager.setTransactionTimeout</code> was not used prior to
+ * invoking this method, the return value is the default timeout set for
+ * the resource manager; otherwise, the value used in the previous
+ * <code>setTransactionTimeout</code> call is returned.
+ *
+ * @return the transaction timeout value in seconds
+ */
+ public int getTransactionTimeout() throws XAException {
+ try {
+ int timeout = (int) freMngr.getTransactionTimeout(curTxId) / 1000; // ms -> secs
+ System.out.println("XAFileResourceManager.getTransactionTimeout() [returning " + timeout + "]");
+ return timeout;
+ } catch (ResourceManagerException rme) {
+ throw new XAException(rme.getMessage());
+ }
+ }
+
+ /*
+ * No implementation.
+ * @param xares an XAResource object whose resource manager instance is to
+ * be compared with the resource manager instance of the target
+ * object
+ * @return always false
+ */
+ public boolean isSameRM(XAResource xares) throws XAException {
+ System.out.println("XAFileResourceManager.isSameRM(xaresMngr=" + xares + ")");
+ return false;
+ }
+
+ /*
+ * No implementation.
+ * @param flag
+ * @return always zero xids
+ */
+ public Xid[] recover(int flag) throws XAException {
+ System.out.println("XAFileResourceManager.recover(flag=" + flag + ")");
+ return new Xid[0];
+ }
+
+ /**
+ * Rollback any updates attempted to be written to the directory.
+ * <p/>
+ * The method acts like the
+ * {@link org.apache.commons.transaction.file.FileResourceManager#rollbackTransaction(Object)}
+ * does.
+ * @param xid a global Transaction id
+ * @throws XAException
+ * if a <code>ResourceManagerException</code> is thrown
+ */
+ public void rollback(Xid xid) throws XAException {
+ System.out.println("XAFileResourceManager.rollback(Xid=" + xid + ")");
+ try {
+ freMngr.rollbackTransaction(curTxId);
+ } catch (ResourceManagerException rme) {
+ throw new XAException(rme.getMessage());
+ }
+ currentXid = null;
+ }
+
+ /**
+ * Set the current transaction <code>timeout</code> value for this
+ * <code>XAFileResourceManager</code> instance. Once set, this timeout value is
+ * effective until <code>setTransactionTimeout</code> is invoked again with
+ * a different value.
+ * The method acts like the
+ * {@link org.apache.commons.transaction.file.FileResourceManager#setTransactionTimeout(Object, long)}
+ * does.
+ *
+ * @param seconds transaction timeout value in seconds
+ * @return true if transaction timeout value is set successfully;
+ * otherwise false
+ */
+ public boolean setTransactionTimeout(int seconds) throws XAException {
+ System.out.println("XAFileResourceManager.setTransactionTimeout(timeout=" + seconds + ")");
+ try {
+ freMngr.setTransactionTimeout(curTxId, seconds * 1000);
+ } catch (ResourceManagerException rme) {
+ throw new XAException(rme.getMessage());
+ }
+ return false;
+ }
+
+ /**
+ * Start work on behalf of a transaction branch specified in <code>xid</code>.
+ * The method act like the
+ * {@link org.apache.commons.transaction.file.FileResourceManager#startTransaction(Object)}
+ * does.
+ * @param xid a global Transaction id to be associated with this
+ * Resource Manager instance
+ * @param flags (can be anything)
+ * @throws XAException if there is already a Transaction
+ */
+ public void start(Xid xid, int flags) throws XAException {
+ System.out.println("XAFileResourceManager.start(Xid=" + xid + ", flags=" +
+ flags + ")");
+ if (currentXid != null) {
+ System.out.println("XAFileResourceManager.start - wrong Xid!");
+ throw new XAException("Current Transaction is: <" + currentXid +
+ ">\nCannot start the new Transaction.");
+ }
+ try {
+ freMngr.startTransaction(curTxId);
+ } catch (ResourceManagerException rme) {
+ throw new XAException(rme.getMessage());
+ }
+ currentXid = xid;
+ }
+
+ /**
+ * Returns the global Transaction id associated with this
+ * <code>XAFileResourceManager</code>
+ * @return the global Transaction id associated with this
+ * <code>XAFileResourceManager</code>
+ */
+ public Xid getCurrentXid() {
+ return currentXid;
+ }
+
+ /**
+ * After a system crash and upon recovery phase the <code>FileResourceManager</code>
+ * object needs to be re-initialised.
+ */
+ private void initFREM() {
+ String workDir = storeDir + "/" + Globals.WORK_DIR_NAME;
+ freMngr = new FileResourceManager(storeDir, workDir, false,
+ new CommonsLoggingLogger(LogFactory.getLog(XADir.class.getName())));
+ try {
+ freMngr.start(); // will automatically recover incomplete txs
+ } catch (ResourceManagerSystemException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ recovers = true; // indicate that the XAFileResourceManager recovers now
+ initFREM();
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/IncompleteTransactionsException.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/IncompleteTransactionsException.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/IncompleteTransactionsException.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txdirs.exceptions;
+
+import java.io.IOException;
+
+/**
+ * Signals that there are incomplete transactions
+ * <p>
+ * The exception is mainly used by the
+ * {@link org.jboss.jbossts.fileio.xalib.txdirs.dir.XADir#close()} method when
+ * invoked by the application programmer while there are active transactions.
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 23, 2008
+ */
+public class IncompleteTransactionsException extends IOException
+{
+ /**
+ * Constructs a <code>IncompleteTransactionsException</code> with
+ * <code>null</code> as its error detail message.
+ */
+ public IncompleteTransactionsException() {
+ super();
+ }
+
+ /**
+ * Constructs a <code>IncompleteTransactionsException</code> with
+ * the given <code>msg</code> as its detail message.
+ * <p>
+ * The string <code>msg</code> may later be retrieved by the
+ * {@link Throwable#getMessage()} method of class <code>java.lang.Throwable</code>
+ * @param msg the detail message
+ */
+ public IncompleteTransactionsException(String msg) {
+ super(msg);
+ }
+
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/NotDirectoryException.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/NotDirectoryException.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txdirs/exceptions/NotDirectoryException.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txdirs.exceptions;
+
+import java.io.IOException;
+
+/**
+ * Signals that a <ccode>File</code> is not a directory.
+ * <p>
+ * The exception is mainly used by the
+ * {@link org.jboss.jbossts.fileio.xalib.txdirs.dir.XADir} class when
+ * craeting new objects to ensure they are directories and not files.
+ *
+ * @author Ioannis Ganotis
+ * @version Aug 7, 2008
+ */
+public class NotDirectoryException extends IOException
+{
+ /**
+ * Constructs a <code>NotDirectoryException</code> with
+ * <code>null</code> as its error detail message.
+ */
+ public NotDirectoryException() {
+ super();
+ }
+
+ /**
+ * Constructs a <code>NotDirectoryException</code> with
+ * the given <code>msg</code> as its detail message.
+ * <p>
+ * The string <code>msg</code> may later be retrieved by the
+ * {@link Throwable#getMessage()} method of class <code>java.lang.Throwable</code>
+ * @param msg the detail message
+ */
+ public NotDirectoryException(String msg) {
+ super(msg);
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/DuplicateTransactionsException.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/DuplicateTransactionsException.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/DuplicateTransactionsException.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.exceptions;
+
+import javax.transaction.xa.XAException;
+
+/**
+ * Signals that a Transaction with the same thread id already exists.
+ * <p>
+ * The exception is mainly used by the {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile} to disallow
+ * associating the same thread id with two different Transactions
+ * and two different {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAResourceManager}s.
+ *
+ * @author Ioannis Ganotis
+ * @version Jul 30, 2008
+ */
+public class DuplicateTransactionsException extends XAException {
+ /**
+ * Constructs a <code>DuplicateTransactionsException</code> with
+ * <code>null</code> as its error detail message.
+ */
+ public DuplicateTransactionsException() {
+ super();
+ }
+
+ /**
+ * Constructs a <code>DuplicateTransactionsException</code> with
+ * the given <code>msg</code> as its detail message.
+ * <p>
+ * The string <code>msg</code> may later be retrieved by the
+ * {@link Throwable#getMessage()} method of class <code>java.lang.Throwable</code>
+ * @param msg the detail message
+ */
+ public DuplicateTransactionsException(String msg) {
+ super(msg);
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/LockRefusedException.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/LockRefusedException.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/exceptions/LockRefusedException.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.exceptions;
+
+import java.io.IOException;
+
+/**
+ * Signals that a lock cannot be granted to either read from or write
+ * to a Transactional file.
+ * <p>
+ * The exception is mainly used by {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile} in the attempt of
+ * reading from or writing to a file.
+ *
+ * @author Ioannis Ganotis
+ * @version Jul 28, 2008
+ */
+public class LockRefusedException extends IOException {
+ /**
+ * Constructs a <code>LockRefusedException</code> with <code>null</code>
+ * as its error detail message.
+ */
+ public LockRefusedException() {
+ super();
+ }
+
+ /**
+ * Constructs a <code>LockRefusedException</code> with the
+ * given <code>msg</code> as its detail message.
+ * <p>
+ * The string <code>msg</code> may later be retrieved by the
+ * {@link Throwable#getMessage()} method of class <code>java.lang.Throwable</code>
+ * @param msg the detail message
+ */
+ public LockRefusedException(String msg) {
+ super(msg);
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/DataRecord.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/DataRecord.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/DataRecord.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.file;
+
+import org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile;
+
+import java.io.Serializable;
+import java.io.IOException;
+
+/**
+ * This class represents a record when reading or writing from an
+ * {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile}.
+ *
+ * @author Ioannis Ganotis
+ * @version Jun 12, 2008
+ */
+public class DataRecord implements Serializable
+{
+ private long startPoint;
+ private int recordLength;
+ private int[] recordBytes;
+
+ /**
+ * Constructor to create <code>DataRecord</code> objects to be used while
+ * read/write operations are used in the <code>XAFile</code>. It is also used
+ * by the {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAResourceManager} after reading a log file and while trying
+ * to re-construct the records' information to be committed to the <code>XAFile</code>.
+ *
+ * @param startPoint the position in the file of the first byte in the <code>recordBytes</code>
+ * array
+ * @param recordLength the length of the updated bytes to commit
+ * @param recordBytes the actual updated bytes
+ * @exception IOException if an I/O error occurs
+ */
+ protected DataRecord(long startPoint, int recordLength, int[] recordBytes)
+ throws IOException {
+ this.startPoint = startPoint;
+ this.recordLength = recordLength;
+ this.recordBytes = recordBytes;
+ }
+
+ /**
+ * Returns the updated bytes as an array of <code>Integer</code>s.
+ * @return an array of <code>int</code>s that contains the updated
+ * bytes
+ */
+ protected int[] getIntBytes() {
+ return recordBytes;
+ }
+
+ /**
+ * Returns the position in the file of the first byte in the
+ * <code>recordBytes</code> array.
+ * @return a <code>long</code> which represents the starting
+ * position of this record in the file
+ */
+ protected long getStartPosition() {
+ return startPoint;
+ }
+
+ /**
+ * Returns the length of this record
+ * @return an <code>int</code> which represents the length of
+ * this record
+ */
+ protected int getRecordLength() {
+ return recordLength;
+ }
+
+ /**
+ * Returns a <code>String</code> which contains all the updated bytes
+ * of this record.
+ * @return a <code>String</code> which represents the updated bytes of
+ * this record
+ */
+ protected String getRecordStr() {
+ return toString(getIntBytes());
+ }
+
+ /**
+ * Returns a <code>byte</code> array which contains the updated bytes
+ * held by this record.
+ * @return an array of bytes which are the updates held by this record
+ */
+ protected byte[] getRecordBytes() {
+ return XAFile.getBytesFromInts(recordBytes);
+ }
+
+ /**
+ * Converts the given <code>ints</code> array into a <code>String</code>
+ * used to display a more readable representation of the updated bytes
+ * held by this record.
+ * @param ints the array of updated bytes to convert
+ * @return a <code>String</code> that incluldes the updated bytes in a
+ * readable format
+ */
+ private String toString(int[] ints) {
+ String txt = "[ ";
+ for (int i : ints) {
+ txt += i + ", ";
+ }
+ return txt.substring(0, txt.length() - 2) + " ]";
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAFile.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAFile.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAFile.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,1390 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.file;
+
+import com.arjuna.ats.txoj.LockMode;
+import com.arjuna.ats.txoj.LockResult;
+import javax.transaction.*;
+import javax.transaction.xa.Xid;
+import java.util.Hashtable;
+import java.io.Closeable;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.io.UTFDataFormatException;
+import org.jboss.jbossts.fileio.DataOutputStream;
+import org.jboss.jbossts.fileio.xalib.txfiles.exceptions.LockRefusedException;
+import org.jboss.jbossts.fileio.xalib.txfiles.exceptions.DuplicateTransactionsException;
+import org.jboss.jbossts.fileio.xalib.txfiles.logging.RecordsLogger;
+import org.jboss.jbossts.fileio.xalib.txfiles.locking.XALockManager;
+import org.jboss.jbossts.fileio.xalib.txfiles.locking.XALock;
+import org.jboss.jbossts.fileio.xalib.Globals;
+
+/**
+ * Instances of this class support both reading/writing to a
+ * random access file and using the random access file with
+ * the support of Transactions. If <code>newTransaction</code> is
+ * invoked after a <code>TransactionManager</code> has been created
+ * and begun, the XAFile will automatically act as a Transactional File,
+ * otherwise it will act as a normal random access file with operations
+ * acting directly on the source file. If the XAFile is in Transactional
+ * mode then any changes (writes) will not affect directly the file, but
+ * will only take effect right after the <code>TransactionManager</code> invokes
+ * its own </em>commit</em> method which will cause the <code>commitUpdates</code>
+ * apply (write) the updated bytes to the source file.
+ * <p>
+ * Calling the <code>newTransaction</code> method twice from the same
+ * thread will cause a <code>DuplicateTransactionsException</code> to
+ * be thrown. As this XAFile class implements the <code>DataInput, DataOutput</code>
+ * interfaces all of their implemented read or write methods will throw an
+ * <code>IOException</code> like specified in the above interfaces and
+ * the read/write methods of the random access file used.
+ *
+ * @author Ioannis Ganotis
+ * @version Jun 10, 2008
+ *
+ * @see org.jboss.jbossts.fileio.xalib.txfiles.locking.XALockManager
+ * @see XAResourceManager
+ * @see RecordsLogger
+ * @see DataRecord
+ */
+public class XAFile implements DataInput, DataOutput, Serializable, Closeable
+{
+ private String filename;
+ private String mode;
+ transient private RandomAccessFile raf;
+ private Hashtable<Long, XAResourceManager> xares;
+ transient private XALockManager xaLockManager;
+ transient private File loggingFolder;
+ transient private File locksFolder;
+ transient private boolean transactionsEnabled;
+
+ /**
+ * Constructor to create objects that represent a Transactional
+ * File on which read and write operations can be applied with the
+ * presence of ACID semantics.
+ *
+ * @param filename the name of the source file
+ * @param mode the access mode (as specified in the {@link java.io.RandomAccessFile})
+ * @param transactionsEnabled if true the XAFile will behave Transactionally and will
+ * allow commit/rollback operations, otherwise will behave
+ * as a normal random access file with read/write operations
+ *
+ * @exception FileNotFoundException
+ * if the mode is <tt>"r"</tt> but the given string does not
+ * denote an existing regular file, or if the mode begins with
+ * <tt>"rw"</tt> but the given string does not denote an
+ * existing, writable regular file and a new regular file of
+ * that name cannot be created, or if some other error occurs
+ * while opening or creating the file
+ * @exception IOException if an I/O error occurs
+ */
+ public XAFile(String filename, String mode, boolean transactionsEnabled)
+ throws IOException {
+ raf = new RandomAccessFile(filename, mode);
+ loggingFolder = new File(Globals.LOG_FOLDER_PATH);
+ locksFolder = new File(Globals.LOCKS_FOLDER_PATH);
+ this.filename = filename;
+ this.mode = mode;
+ this.transactionsEnabled = transactionsEnabled;
+ xares = new Hashtable<Long, XAResourceManager>();
+ initLocksHeld();
+
+ prepareFolders();
+ }
+
+ /**
+ * Method to create folders that are needed
+ * by the library
+ */
+ private void prepareFolders()
+ {
+ if (!loggingFolder.exists())
+ loggingFolder.mkdir();
+ if (!locksFolder.exists())
+ locksFolder.mkdir();
+ }
+
+ /**
+ * Method to create a new Transaction and enlist XAResources.
+ * <p>
+ * The transaction is enlisted in a new {@link XAResourceManager}
+ * and appropriate log files are created each time the method is invoked.
+ * It is important that this method is called after the {@link javax.transaction.TransactionManager}
+ * has began, in order to benefit from the Transactional effects.
+ * <p>
+ * This method can be used in a multi-threaded environemnt but each
+ * thread must call it once, otherwise a <code>DuplicateTransactionsException</code>
+ * will be thrown to prevent this.
+ *
+ * @param txnMngr the <code>TransactionManager</code> used to
+ * commit or rollback any attempted modifications to the
+ * source file
+ *
+ * @exception org.jboss.jbossts.fileio.xalib.txfiles.exceptions.DuplicateTransactionsException
+ * When trying to call this method within the same thread
+ * @exception IOException
+ * May be thrown if there is a problem with either creating
+ * the <code>XAResourceManager</code> or the <code>RecordsLogger</code>
+ * @exception javax.transaction.SystemException
+ * Will be thrown if there is a problem enlisting the XAResource created
+ * within this method or when the <code>getTransaction</code> in the
+ * <code>TransactionManager</code> fails or when the TransactionManager
+ * is not in ready (<code>Status.ACTIVE</code>) mode
+ * @exception javax.transaction.RollbackException
+ * Will be thrown if there is a problem while enlisting the resource to
+ * the transaction
+ * @exception IllegalStateException
+ * if the <code>transactionsEnabled</code> in the constructor of the
+ * XAFile has been chosen to be false
+ */
+ public synchronized void newTransaction(TransactionManager txnMngr)
+ throws DuplicateTransactionsException, IOException, SystemException, RollbackException {
+ if (transactionsEnabled) {
+ if (txnMngr.getStatus() == Status.STATUS_ACTIVE)
+ {
+ long th_id = Thread.currentThread().getId();
+ if (!xares.containsKey(th_id)) {
+ String logName = loggingFolder.getPath() + '/' + th_id + '_' + System.nanoTime();
+
+ RecordsLogger log = new RecordsLogger(logName);
+
+ XAResourceManager xareMngr = new XAResourceManager(this, log, th_id);
+ xares.put(th_id, xareMngr);
+ Transaction txn = txnMngr.getTransaction();
+ txn.enlistResource(xareMngr);
+ } else
+ {
+ throw new DuplicateTransactionsException("Cannot create a new Transaction. " +
+ "There is already a thread with id=<" + th_id + "> associated with that " +
+ "Transaction.");
+ }
+ } else
+ {
+ throw new SystemException("The newTransaction() method must be called only " +
+ "when TransactionManager's status is ACTIVE, after the manager has begun.");
+ }
+ } else
+ {
+ throw new IllegalStateException("Transactional support is set to be disabled. " +
+ "Enable using the setTransactionsEnabled() method.");
+ }
+ }
+
+ /**
+ * Returns the <code>RandomAccessFile</code> object used to read/write
+ * @return the <code>RandomAccessFile</code> object associated with
+ * this class to apply the read/write operations
+ */
+ public RandomAccessFile getRAF() {
+ return raf;
+ }
+
+ /**
+ * Closes this XAFile.
+ * <p>
+ * The method actually closes the random access file stream and releases
+ * any system resources associated with the stream and the Transactions.
+ * If this file has an associated channel then the channel is closed
+ * as well.
+ *
+ * @exception IOException if an I/O error occurs or if there are
+ * incomplete Transactions.
+ */
+ public void close() throws IOException {
+ if (!xares.isEmpty())
+ throw new IOException("Failed to close the file. There are incomplete Transactions."); //todo better rollback??
+ raf.close();
+ if (xaLockManager.obtainHeldLocksWith(null).isEmpty()) // if the file is not empty,
+ xaLockManager.deleteFile(); // possibly another VM has written to it, so
+ } // do not delete it
+
+ /**
+ * Gets the <code>FileDescriptor</code> of the
+ * random access file used and forces synchronization.
+ *
+ * This method is used at the <code>prepare</code> phase of an
+ * <code>{@link XAResourceManager}</code>.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ protected void sync() throws IOException {
+ FileDescriptor fd = raf.getFD();
+ fd.sync();
+ }
+
+ /**
+ * Forces data to be written to disk by instantly closing and re-opening
+ * the random access file. The file pointer returns to the correct
+ * position after re-opening the file.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ public synchronized void flush() throws IOException {
+ long curPos = raf.getFilePointer();
+ raf.close();
+ raf = new RandomAccessFile(filename, mode);
+ raf.seek(curPos);
+ }
+
+ /**
+ * Sets the file-pointer offset, measured from the beginning of this
+ * file, at which the next read or write occurs. The offset may be
+ * set beyond the end of the file. Setting the offset beyond the end
+ * of the file does not change the file length. The file length will
+ * change only by writing after the offset has been set beyond the end
+ * of the file.
+ *
+ * @param position the offset position, measured in bytes from the
+ * beginning of the file, at which to set the file
+ * pointer.
+ * @exception IOException if <code>position</code> is less than
+ * <code>0</code> or if an I/O error occurs.
+ */
+ public void seek(long position) throws IOException {
+ raf.seek(position);
+ }
+
+ /**
+ * Method to enable Transactional support in the file
+ * <p>
+ * If the transactions are enabled and a read/write operation is invoked
+ * outside the scope of begin-commit/rollback an exception will be thrown
+ * as specified in the <code>newTransaction</code> method. To use these
+ * operations in that way set transaction support to false.
+ * @param transactionsEnabled if true, the XAFile will create transactions
+ * and will allow commit/rollback operations in
+ * the <code>TransactionManager.</code>
+ */
+ public void setTransactionsEnabled(boolean transactionsEnabled) {
+ this.transactionsEnabled = transactionsEnabled;
+ }
+
+ /**
+ * Returns a standard error message based on the given <code>th_id</code>
+ * @param th_id the thread id participating in the generated message
+ * @return a standard error message based on the given <code>th_id</code>
+ */
+ private String getErrMsg(long th_id)
+ {
+ return "Failed to update source file. " +
+ "The thread with id=<" + th_id + "> is not associated with any Transaction";
+ }
+
+ /**
+ * Method to update a series of bytes in the file starting from a
+ * given <code>position</code>.
+ * <p>
+ * The method is called after an {@link XAResourceManager} is
+ * ready to commit. It copies <code>data</code> of exact <code>recordLength</code>
+ * and starting from position <code>position</code> in the source file.
+ *
+ * @param position the position in the file to start copying <code>data</code>
+ * @param recordLength the length of bytes to be copied
+ * @param data the actual data/updates
+ * @param th_id the thread associated with the Transaction trying to commit
+ * @exception IOException if an I/O error occurs
+ * @exception IllegalStateException
+ * if the given <code>th_id</code> is not associated with a
+ * Transaction
+ */
+ protected synchronized void commitUpdates(long position, int recordLength, byte[] data,
+ long th_id) throws IOException {
+ if (th_id != Globals.RECOVERY_ID && th_id != getCurrentThreadId())
+ throw new IllegalStateException(getErrMsg(th_id));
+ long curPos = raf.getFilePointer();
+ commitUpdates(position, recordLength, data);
+ raf.seek(curPos);
+ }
+
+ /**
+ * Method to update a series of bytes in the file starting from a
+ * given <code>position</code>.
+ * <p>
+ * The method is called after an {@link XAResourceManager} is
+ * ready to commit. It copies <code>data</code> of exact <code>recordLength</code>
+ * and starting from position <code>position</code> in the source file.
+ *
+ * @param position the position in the file to start copying <code>data</code>
+ * @param recordLength the length of bytes to be copied
+ * @param data the actual data/updates
+ * @exception IOException if an I/O error occurs
+ */
+ private void commitUpdates(long position, int recordLength, byte[] data) throws IOException
+ {
+ raf.seek(position);
+ raf.write(data, 0, recordLength);
+ }
+
+ /**
+ * Disassociates a current thread from an existing Transaction.
+ * <p>
+ * After a <code>TransactionManager</code>'s commit/rollback operations
+ * this method is invoked to remove the thread registered by the
+ * <code>newTransaction</code> method.
+ *
+ * @param xid the thread as a key to remove the corresponding
+ * <code>XAResourceManager</code> from the list.
+ * @param recovers true if the system has crashed and tries to recover;
+ * otherwise false
+ * @exception IOException
+ * if an I/O error occurs or there is no registered thread
+ * with the given <code>th_id</code>
+ */
+ protected synchronized void removeTransaction(Xid xid, boolean recovers)
+ throws IOException {
+ if (!recovers) {
+ long th_id = getCurrentThreadId();
+ xares.remove(th_id);
+ }
+ xaLockManager.releaseLocks(xid);
+ }
+
+ /**
+ * Fill the list of locks held while trying to read/write to
+ * the file.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ protected void initLocksHeld() throws IOException {
+ xaLockManager = new XALockManager(filename);
+ }
+
+ /**
+ * Internal method to ask from {@link org.jboss.jbossts.fileio.xalib.txfiles.locking.XALockManager} to apply
+ * a read/write lock on a given <code>DataRecord</code>.
+ * <p>
+ * The method is invoked by read/write operations within the
+ * XAFile to lock on a specific range of bytes in the file.
+ * @param dr the record to lock on, either in read or write mode
+ * @param xid the {@link javax.transaction.xa.Xid} of the current
+ * {@link XAResourceManager}
+ * @param mode read/write modes as specified in the
+ * {@link com.arjuna.ats.txoj.LockMode} class
+ * @return the {@link com.arjuna.ats.txoj.LockResult} depending on
+ * whether the lock can be <em>GRANTED or REFUSED</em>
+ * @exception IOException if an I/O error occurs
+ * @exception org.jboss.jbossts.fileio.xalib.txfiles.exceptions.LockRefusedException if lock cannot be <em>GRANTED</em>
+ */
+ private int acquireLockOn(DataRecord dr, Xid xid, int mode) throws IOException {
+ XALock xaLock = new XALock(xid, mode, dr.getStartPosition(), dr.getRecordLength());
+ int res = xaLockManager.tryLock(xaLock);//LockManager.waitTotalTimeout);
+
+ if (res == LockResult.REFUSED) {
+ String msg = "REFUSED:WRITE_LOCK on byte(s): " + dr.getRecordStr();
+ if (mode == LockMode.READ) {
+ msg = "REFUSED:READ_LOCK on byte(s): " + dr.getRecordStr();
+ }
+ throw new LockRefusedException(msg);
+ } else if (res == LockResult.GRANTED) {
+ String msg = "GRANTED:WRITE_LOCK on byte(s): " + dr.getRecordStr();
+ if (mode == LockMode.READ) {
+ msg = "GRANTED:READ_LOCK on byte(s): " + dr.getRecordStr();
+ }
+// System.out.println(msg);
+ }
+ return res;
+ }
+
+ /**
+ * Returns the name of the file
+ * @return the name of the file
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Returns the access mode in which the <code>XAFile</code> was
+ * created
+ * @return the access mode which was set when created the
+ * <code>XAFile</code> object
+ */
+ public String getMode() {
+ return mode;
+ }
+
+ /**
+ * Internal method used by read operations in this <code>XAFile</code>.
+ * If the class is used in the Transactional mode (Transactions are enabled)
+ * the method tries to read bytes from memory. If that fails it means no
+ * previous write operations applied on the same range of bytes so the
+ * method will try to read and return the bytes as read directly from the
+ * source file. If Transactions are disabled the method does not make any
+ * attempts to find if there are any modified bytes by previous write
+ * operations and so it reads directly from the file.
+ * <p>
+ * The method starts reading bytes at the current file pointer. If data
+ * are retrieved from the memory instead of the actual file, the file
+ * pointer progresses by <code>len</code> steps, if successful.
+ * @param len the length of data to read
+ * @return the bytes read into an array of Integers
+ * @exception IOException if an I/O error occurs
+ * @exception org.jboss.jbossts.fileio.xalib.txfiles.exceptions.LockRefusedException if a lock cannot be <code>GRANTED</code>
+ */
+ private synchronized int[] readRecord(int len) throws IOException {
+ if (transactionsEnabled) {
+ long curThread = getCurrentThreadId();
+ XAResourceManager xare = xares.get(curThread);
+
+ Hashtable<Long, Integer> updatedBytes = xare.getUpdatedBytes();
+ int[] upds = new int[len];
+ long startPos = raf.getFilePointer();
+ int i;
+ for (i=0;i<len;i++) {
+// if (startPos >= raf.length())
+// return null;
+ if (updatedBytes.containsKey(startPos)) {
+ upds[i] = updatedBytes.get(startPos++);
+ raf.seek(raf.getFilePointer()+1);
+ } else {
+ upds[i] = raf.read();
+ }
+ }
+ DataRecord dr = new DataRecord(raf.getFilePointer()-len, len, upds);
+ acquireLockOn(dr, xare.getXid(), LockMode.READ);
+ return upds;
+ }
+ return readDirectlyFromFile(len);
+ }
+
+ /**
+ * Method to read exactly <code>len</code> bytes
+ * directly from the file and starting at the current file
+ * pointer.
+ *
+ * @param len the length of bytes to read from the file
+ * @return the bytes read into an Integer array
+ * @exception IOException if an I/O error occurs
+ */
+ public int[] readDirectlyFromFile(int len) throws IOException {
+ int[] buffer = new int[len];
+ for (int b=0; b<len; b++) {
+ buffer[b] = raf.read();
+ }
+ return buffer;
+ }
+
+ /**
+ * Reads a <code>boolean</code> from this file. This method reads a
+ * single byte from the <code>readByte</code> method, starting at
+ * the current file pointer. A value of <code>0</code> represents
+ * <code>false</code>. Any other value represents <code>true</code>.
+ * This method blocks until the byte is read, the end of the stream
+ * is detected, or an exception is thrown.
+ *
+ * @return the <code>boolean</code> value read.
+ * @exception EOFException if this file has reached the end.
+ * @exception IOException if an I/O error occurs.
+ */
+ public boolean readBoolean() throws IOException {
+ int ch = readByte();
+ if (ch < 0) {
+ throw new EOFException();
+ }
+ return (ch != 0);
+ }
+
+ /**
+ * Reads a <code>double</code> from this file. This method reads a
+ * <code>long</code> value, starting at the current file pointer,
+ * as if by the <code>readLong</code> method
+ * and then converts that <code>long</code> to a <code>double</code>
+ * using the <code>longBitsToDouble</code> method in
+ * class <code>Double</code>.
+ * <p>
+ * This method blocks until the eight bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next eight bytes of this file, interpreted as a
+ * <code>double</code>.
+ * @exception EOFException if this file reaches the end before reading
+ * eight bytes.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.RandomAccessFile#readLong()
+ * @see java.lang.Double#longBitsToDouble(long)
+ */
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ /**
+ * Reads an unsigned 16-bit number from this file. This method gets
+ * the two bytes return from the <code>readRecord</code> method,
+ * starting at the current file pointer. If the bytes read, in order, are
+ * <code>b1</code> and <code>b2</code>, where
+ * <code>0 <= b1, b2 <= 255</code>,
+ * then the result is equal to:
+ * <blockquote><pre>
+ * (b1 << 8) | b2
+ * </pre></blockquote>
+ * <p>
+ * This method blocks until the two bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this file (or memory if there are
+ * uncommitted changes), interpreted as an unsigned
+ * 16-bit integer.
+ * @exception EOFException if this file reaches the end before reading
+ * two bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public int readUnsignedShort() throws IOException {
+ int[] ints = readRecord(Short.SIZE/Byte.SIZE);
+
+ int ch1 = ints[0];
+ int ch2 = ints[1];
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+
+ return (ch1 << 8) + (ch2);
+ }
+
+ /**
+ * Reads <code>bytes.length</code> bytes from this file(or memory if there
+ * are uncommitted updated bytes) into the byte
+ * array, starting at the current file pointer. This method reads
+ * repeatedly until the requested number of bytes are read. This
+ * method blocks until the requested number of bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @param bytes the buffer into which the data is read.
+ * @exception EOFException if this file reaches the end before reading
+ * all the bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void readFully(byte[] bytes) throws IOException {
+ readFully(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Reads exactly <code>len</code> bytes from this file(or memory if there
+ * are uncommitted updated bytes) into the byte
+ * array, starting at the current file pointer. This method reads
+ * repeatedly until the requested number of bytes are read. This
+ * method blocks until the requested number of bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @param bytes the buffer into which the data is read.
+ * @param off the start offset of the data.
+ * @param len the number of bytes to read.
+ * @exception EOFException if this file reaches the end before reading
+ * all the bytes.
+ * @exception IOException if an I/O error occurs.
+ * @exception IndexOutOfBoundsException
+ * if offset is negative, or len is negative, or offset+len is
+ * greater than the size of the <code>bytes</code> array
+ */
+ public void readFully(byte[] bytes, int off, int len) throws IOException {
+ if (off < 0 || len < 0 || off + len > bytes.length)
+ throw new IndexOutOfBoundsException("bytes.length=" + bytes.length +
+ ", off=" + off + ", len=" + len);
+
+ byte[] bs = getBytesFromInts(readRecord(len));
+ System.arraycopy(bs, 0, bytes, off, len);
+// for (int b = off; b < off + len; b++) {
+// bytes[b] = readByte();
+// }
+ }
+
+ /**
+ * Reads a signed 16-bit number from this file. The method reads two
+ * bytes from this file (or memory if there are uncommitted byte updates),
+ * starting at the current file pointer. If the two bytes read, in order,
+ * are <code>b1</code> and <code>b2</code>, where each of the two values is
+ * between <code>0</code> and <code>255</code>, inclusive, then the
+ * result is equal to:
+ * <blockquote><pre>
+ * (short)((b1 << 8) | b2)
+ * </pre></blockquote>
+ * <p>
+ * This method blocks until the two bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this file(memory), interpreted as a signed
+ * 16-bit number.
+ * @exception EOFException if this file reaches the end before reading
+ * two bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public short readShort() throws IOException {
+ int[] ints = readRecord(Short.SIZE/Byte.SIZE);
+
+ int ch1 = ints[0];
+ int ch2 = ints[1];
+ if ((ch1 | ch2) < 0) {
+ throw new EOFException();
+ }
+ return (short) ((ch1 << 8) + (ch2));
+ }
+
+ /**
+ * Reads in a string from this file (or memory if there are uncommitted
+ * byte updates). The string has been encoded using a
+ * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
+ * format.
+ * <p>
+ * The first two bytes are read, starting from the current file
+ * pointer, as if by
+ * <code>readUnsignedShort</code>. This value gives the number of
+ * following bytes that are in the encoded string, not
+ * the length of the resulting string. The following bytes are then
+ * interpreted as bytes encoding characters in the modified UTF-8 format
+ * and are converted into characters.
+ * <p>
+ * This method blocks until all the bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return a Unicode string.
+ * @exception EOFException
+ * if this file reaches the end before reading all the bytes.
+ * @exception IOException if an I/O error occurs.
+ * @exception UTFDataFormatException
+ * if the bytes do not represent
+ * valid modified UTF-8 encoding of a Unicode string.
+ * @see java.io.RandomAccessFile#readUnsignedShort()
+ */
+ public String readUTF() throws IOException {
+ return DataInputStream.readUTF(this);
+ }
+
+ /**
+ * Reads a <code>float</code> from this file (or memory if there are
+ * uncommitted byte updates). This method reads an
+ * <code>int</code> value, starting at the current file pointer,
+ * as if by the <code>readInt</code> method
+ * and then converts that <code>int</code> to a <code>float</code>
+ * using the <code>intBitsToFloat</code> method in class
+ * <code>Float</code>.
+ * <p>
+ * This method blocks until the four bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next four bytes of this file, interpreted as a
+ * <code>float</code>.
+ * @exception EOFException if this file reaches the end before reading
+ * four bytes.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.RandomAccessFile#readInt()
+ * @see java.lang.Float#intBitsToFloat(int)
+ */
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ /**
+ * Reads a signed 32-bit integer from this file (or memory if there
+ * are uncommitted byte updates). This method reads 4
+ * bytes from the file, starting at the current file pointer.
+ * If the bytes read, in order, are <code>b1</code>,
+ * <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
+ * <code>0 <= b1, b2, b3, b4 <= 255</code>,
+ * then the result is equal to:
+ * <blockquote><pre>
+ * (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
+ * </pre></blockquote>
+ * <p>
+ * This method blocks until the four bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next four bytes of this file, interpreted as an
+ * <code>int</code>.
+ * @exception EOFException if this file reaches the end before reading
+ * four bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public int readInt() throws IOException {
+ int[] ints = readRecord(Integer.SIZE/Byte.SIZE);
+
+ int ch1 = ints[0];
+ int ch2 = ints[1];
+ int ch3 = ints[2];
+ int ch4 = ints[3];
+ if ((ch1 | ch2 | ch3 | ch4) < 0) {
+ throw new EOFException();
+ }
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4));
+ }
+
+ /**
+ * Reads an unsigned eight-bit number from this file (or memory if there
+ * are uncommitted byte updates). This method reads
+ * a byte, starting at the current file pointer, and returns that byte.
+ * <p>
+ * This method blocks until the byte is read, the end of the stream
+ * is detected, or an exception is thrown.
+ *
+ * @return the next byte of this file, interpreted as an unsigned
+ * eight-bit number.
+ * @exception EOFException if this file has reached the end.
+ * @exception IOException if an I/O error occurs.
+ */
+ public int readUnsignedByte() throws IOException {
+ int ch = readByte();
+ if (ch < 0) {
+ throw new EOFException();
+ }
+ return ch;
+ }
+
+ /**
+ * Reads a signed eight-bit value from this file (or memory if there
+ * are uncommitted byte updates). This method reads a
+ * byte, starting from the current file pointer. If the byte read is
+ * <code>b</code>, where <code>0 <= b <= 255</code>,
+ * then the result is:
+ * <blockquote><pre>
+ * (byte)(b)
+ * </pre></blockquote>
+ * <p>
+ * This method blocks until the byte is read, the end of the stream
+ * is detected, or an exception is thrown.
+ *
+ * @return the next byte of this file as a signed eight-bit
+ * <code>byte</code>.
+ * @exception EOFException if this file has reached the end.
+ * @exception IOException if an I/O error occurs.
+ */
+ public byte readByte() throws IOException {
+ int[] ints = readRecord(Byte.SIZE/Byte.SIZE);
+ return (ints != null) ? (byte) ints[0] : -1;
+ }
+
+ /**
+ * Reads the next line of text from this file. This method successively
+ * reads bytes from the file (or memory if there are uncommitted byte updates),
+ * starting at the current file pointer, until it reaches a line terminator
+ * or the end of the file. Each byte is converted into a character by taking
+ * the byte's value for the lower eight bits of the character and setting the
+ * high eight bits of the character to zero. This method does not,
+ * therefore, support the full Unicode character set.
+ *
+ * <p> A line of text is terminated by a carriage-return character
+ * (<code>'\r'</code>), a newline character (<code>'\n'</code>), a
+ * carriage-return character immediately followed by a newline character,
+ * or the end of the file. Line-terminating characters are discarded and
+ * are not included as part of the string returned.
+ *
+ * <p> This method blocks until a newline character is read, a carriage
+ * return and the byte following it are read (to see if it is a newline),
+ * the end of the file is reached, or an exception is thrown.
+ *
+ * @return the next line of text from this file, or null if end
+ * of file is encountered before even one byte is read.
+ * @exception IOException if an I/O error occurs.
+ */
+ public String readLine() throws IOException {
+ StringBuffer input = new StringBuffer();
+ int c = -1;
+ boolean eol = false;
+
+ while (!eol) {
+ switch (c = readByte()) {
+ case -1:
+ case '\n':
+ eol = true;
+ break;
+ case '\r':
+ eol = true;
+ long cur = raf.getFilePointer();
+ if ((readByte()) != '\n') {
+ raf.seek(cur);
+ }
+ break;
+ default:
+ input.append((char) c);
+ break;
+ }
+ }
+ if ((c == -1) && (input.length() == 0)) {
+ return null;
+ }
+ return input.toString();
+ }
+
+ /**
+ * Reads up to <code>bytes.length</code> bytes of data from this file
+ * (or memory if there are uncommitted byte updates) into an array of
+ * bytes. This method blocks until at least one byte of input is available.
+ * <p>
+ * Although <code>XAFile</code> is not a subclass of
+ * <code>InputStream</code>, this method behaves in exactly the
+ * same way as the {@link InputStream#read(byte[])} method of
+ * <code>InputStream</code>.
+ *
+ * @param bytes the buffer into which the data is read.
+ * @return the total number of bytes read into the buffer, or
+ * <code>-1</code> if there is no more data because the end of
+ * this file has been reached.
+ * @exception IOException
+ * If the first byte cannot be read for any reason
+ * other than end of file, or if the random access file has been
+ * closed, or if some other I/O error occurs.
+ * @exception NullPointerException If <code>bytes</code> is <code>null</code>.
+ */
+ public int read(byte[] bytes) throws IOException {
+ return read(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Reads exactly <code>len</code> bytes of data from this file
+ * (or memory if there are uncommitted byte updates) into an array of
+ * bytes. This method blocks until at least one byte of input is available.
+ * <p>
+ * Although <code>XAFile</code> is not a subclass of
+ * <code>InputStream</code>, this method behaves in exactly the
+ * same way as the {@link InputStream#read(byte[])} method of
+ * <code>InputStream</code>.
+ *
+ * @param bytes the buffer into which the data is read.
+ * @param off the start offset in array <code>bytes</code>
+ * at which the data is written.
+ * @param len the maximum number of bytes read.
+ * @return the total number of bytes read into the buffer, or
+ * <code>-1</code> if there is no more data because the end of
+ * this file has been reached.
+ * @exception IOException
+ * If the first byte cannot be read for any reason
+ * other than end of file, or if the random access file has been
+ * closed, or if some other I/O error occurs.
+ * @exception NullPointerException If <code>bytes</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException
+ * if offset is negative, or len is negative, or offset+len is
+ * greater than the size of the <code>bytes</code> array
+ */
+ public int read(byte[] bytes, int off, int len) throws IOException {
+ if (off < 0 || len < 0 || off + len > bytes.length)
+ throw new IndexOutOfBoundsException("bytes.length=" + bytes.length +
+ ", off=" + off + ", len=" + len);
+ if (bytes.length == 0)
+ return 0;
+ byte[] bs = getBytesFromInts(readRecord(len)); //todo readRecord may return null
+ System.arraycopy(bs, 0, bytes, off, len);
+// int b;
+// for (b = off; b < off + len; b++) {
+// if (b >= raf.length())
+// return -1;
+// bytes[b] = readByte();
+// }
+ return bs.length;
+ }
+
+ /**
+ * Reads up to <code>chars.length</code> characters from this file
+ * (or memory if there are uncommitted byte updates) into an array
+ * of characters.
+ * <p>
+ * This method blocks until the char is read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @param chars the array into which the characters are read
+ * @return a <code>String</code> containing the read characters
+ * @exception EOFException if this file reaches the end before reading
+ * <code>chars.length</code> characters.
+ * @exception IOException if an I/O error occurs.
+ */
+ public String readChars(char[] chars) throws IOException {
+ for (int c = 0; c < chars.length; c++) {
+ chars[c] = readChar();
+ }
+ return new String(chars);
+ }
+
+ /**
+ * Reads a character from this file. This method reads two
+ * bytes from the file (or memory if there are uncommitted byte updates),
+ * starting at the current file pointer.
+ * If the bytes read, in order, are
+ * <code>b1</code> and <code>b2</code>, where
+ * <code>0 <= b1, b2 <= 255</code>,
+ * then the result is equal to:
+ * <blockquote><pre>
+ * (char)((b1 << 8) | b2)
+ * </pre></blockquote>
+ * <p>
+ * This method blocks until the two bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this file, interpreted as a
+ * <code>char</code>.
+ * @exception EOFException if this file reaches the end before reading
+ * two bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public char readChar() throws IOException {
+ int[] ints = readRecord(Character.SIZE/Byte.SIZE);
+
+ int ch1 = ints[0];
+ int ch2 = ints[1];
+ if ((ch1 | ch2) < 0) {
+ throw new EOFException();
+ }
+ return (char) ((ch1 << 8) + (ch2));
+ }
+
+ /**
+ * Reads a signed 64-bit integer from this file. This method reads eight
+ * bytes from the file (or memory if there are uncommitted byte updates),
+ * starting at the current file pointer.
+ * If the bytes read, in order, are
+ * <code>b1</code>, <code>b2</code>, <code>b3</code>,
+ * <code>b4</code>, <code>b5</code>, <code>b6</code>,
+ * <code>b7</code>, and <code>b8,</code> where:
+ * <blockquote><pre>
+ * 0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
+ * </pre></blockquote>
+ * <p>
+ * then the result is equal to:
+ * <p><blockquote><pre>
+ * ((long)b1 << 56) + ((long)b2 << 48)
+ * + ((long)b3 << 40) + ((long)b4 << 32)
+ * + ((long)b5 << 24) + ((long)b6 << 16)
+ * + ((long)b7 << 8) + b8
+ * </pre></blockquote>
+ * <p>
+ * This method blocks until the eight bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next eight bytes of this file, interpreted as a
+ * <code>long</code>.
+ * @exception EOFException if this file reaches the end before reading
+ * eight bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public long readLong() throws IOException {
+ return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
+ }
+
+ /**
+ * Attempts to write a <code>long</code> to the file as eight bytes,
+ * high byte first. The write starts at the current position of the
+ * file pointer.
+ * <p>
+ * Updates will be written to the file after a commit operation call.
+ *
+ * @param l a <code>long</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void writeLong(long l) throws IOException {
+ int[] ls = new int[]{((int) (l >>> 56) & 0xFF), ((int) (l >>> 48) & 0xFF),
+ ((int) (l >>> 40) & 0xFF), ((int) (l >>> 32) & 0xFF),
+ ((int) (l >>> 24) & 0xFF), ((int) (l >>> 16) & 0xFF),
+ ((int) (l >>> 8) & 0xFF), ((int) (l) & 0xFF)};
+ writeRecord(ls);
+ }
+
+ /**
+ * Attempts to write the specified byte to this file. The write starts at
+ * the current file pointer.
+ * <p>
+ * The byte will be written to the file only after the commit operation
+ * @param b the <code>byte</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void write(int b) throws IOException {
+ writeRecord(new int[]{b});
+ }
+
+ /**
+ * Attempts to write an <code>int</code> to the file as four bytes, high
+ * byte first. The write starts at the current position of the file pointer.
+ * <p>
+ * The <code>int</code> will be written to the file only after a commit operation call
+ * @param n an <code>int</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void writeInt(int n) throws IOException {
+ int[] ls = new int[]{((n >>> 24) & 0xFF), ((n >>> 16) & 0xFF),
+ ((n >>> 8) & 0xFF), ((n) & 0xFF)};
+ writeRecord(ls);
+ }
+
+ /**
+ * Converts the double argument to a <code>long</code> using the
+ * <code>doubleToLongBits</code> method in class <code>Double</code>,
+ * and then attempts to write that <code>long</code> value to the file as an
+ * eight-byte quantity, high byte first. The write starts at the current
+ * position of the file pointer.
+ * <p>
+ * The <code>double</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param d a <code>double</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.lang.Double#doubleToLongBits(double)
+ */
+ public void writeDouble(double d) throws IOException {
+ writeLong(Double.doubleToLongBits(d));
+ }
+
+ /**
+ * Converts the float argument to an <code>int</code> using the
+ * <code>floatToIntBits</code> method in class <code>Float</code>,
+ * and then attempts to write that <code>int</code> value to the file as a
+ * four-byte quantity, high byte first. The write starts at the
+ * current position of the file pointer.
+ * <p>
+ * The <code>float</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param f a <code>float</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.lang.Float#floatToIntBits(float)
+ * @see XAFile#readFloat()
+ */
+ public void writeFloat(float f) throws IOException {
+ writeInt(Float.floatToIntBits(f));
+ }
+
+ /**
+ * Attempts to write a <code>boolean</code> to the file as a one-byte
+ * value. The value <code>true</code> is written out as the value
+ * <code>(byte)1</code>; the value <code>false</code> is written out
+ * as the value <code>(byte)0</code>. The write starts at
+ * the current position of the file pointer.
+ * <p>
+ * The <code>boolean</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param b a <code>boolean</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see XAFile#readBoolean()
+ */
+ public void writeBoolean(boolean b) throws IOException {
+ write(b ? 1 : 0);
+ }
+
+ /**
+ * Attempts to write a <code>byte</code> to the file as a one-byte value.
+ * The write starts at the current position of the file pointer.
+ * <p>
+ * The <code>byte</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param b a <code>byte</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized void writeByte(int b) throws IOException {
+ write(b);
+ }
+
+ /**
+ * Internal method used by write oprations in the <code>XAFile</code>.
+ * If Transactions are disabled, bytes are written directly to the file.
+ * If Transactions are enabled the given <code>bytes</code> are written
+ * to the memory (added in a hashtable).
+ * <p>
+ * The method may be called by different threads. To prevent different
+ * Transactions to modify an already modified record by another
+ * Transaction a lock in <code>WRITE</code> mode is acquired. If the lock
+ * is finally <code>GRANTED</code> the <code>bytes<code> are added
+ * to the list of modified records in the correct <code>XAResourceManager</code>.
+ *
+ * @param bytes the array of bytes to write to the file
+ * @exception IOException if an I/O error occurs
+ * @exception org.jboss.jbossts.fileio.xalib.txfiles.exceptions.LockRefusedException if lock is <code>REFUSED</code>
+ */
+ private synchronized void writeRecord(int[] bytes) throws IOException {
+ if (transactionsEnabled) { // write bytes to the memory first
+ long curthr = getCurrentThreadId();
+ int lockRes;
+
+ XAResourceManager xareMngr = xares.get(curthr);
+
+ DataRecord dr = new DataRecord(raf.getFilePointer(), bytes.length, bytes);
+ lockRes = acquireLockOn(dr, xareMngr.getXid(), LockMode.WRITE);
+
+ if (lockRes == LockResult.GRANTED) {
+ xareMngr.addUpdatedBytes(raf.getFilePointer(), bytes);
+ xareMngr.add2Log(dr);
+ raf.skipBytes(bytes.length);
+ }
+ } else { // write bytes directly to the file
+ // XAFile will now behave like a RandomAccessFile
+ commitUpdates(raf.getFilePointer(), bytes.length, getBytesFromInts(bytes));
+ }
+ }
+
+ /**
+ * Attempts to write a <code>short</code> to the file as two bytes, high
+ * byte first. The write starts at the current position of the file pointer.
+ * <p>
+ * The <code>short</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param s a <code>short</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see XAFile#readShort()
+ */
+ public void writeShort(int s) throws IOException {
+ int[] ss = new int[]{(s >>> 8 & 0xFF), ((s) & 0xFF)};
+ writeRecord(ss);
+ }
+
+ /**
+ * Attempts to write <code>len</code> bytes from the specified
+ * byte array starting at offset <code>off</code> to this file.
+ * <p>
+ * The bytes will be written to the file only after a
+ * commit operation call
+ *
+ * @param bytes the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @exception IOException if an I/O error occurs.
+ * @exception IndexOutOfBoundsException
+ * if offset is negative, or len is negative, or offset+len is
+ * greater than the size of the <code>bytes</code> array
+ * @see XAFile#read(byte[], int, int)
+ */
+ public void write(byte[] bytes, int off, int len) throws IOException {
+ if (off < 0 || len < 0 || off + len > bytes.length)
+ throw new IndexOutOfBoundsException("bytes.length=" + bytes.length +
+ ", off=" + off + ", len=" + len);
+
+ byte[] newByteArray = new byte[len];
+ System.arraycopy(bytes, off, newByteArray, 0, len);
+ writeRecord(getIntsFromBytes(newByteArray));
+ }
+
+ /**
+ * Attempts to write a string to the file as a sequence of characters.
+ * Each character is written to the data output stream as if by the
+ * <code>writeChar</code> method. The write starts at the current
+ * position of the file pointer.
+ * <p>
+ * The <code>String</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param s a <code>String</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.RandomAccessFile#writeChar(int)
+ * @see XAFile#readChar()
+ */
+ public void writeChars(String s) throws IOException {
+ int clen = s.length();
+ int blen = 2 * clen;
+ byte[] b = new byte[blen];
+ char[] c = new char[clen];
+ s.getChars(0, clen, c, 0);
+ for (int i = 0, j = 0; i < clen; i++) {
+ b[j++] = (byte) (c[i] >>> 8);
+ b[j++] = (byte) (c[i]);
+ }
+ write(b, 0, blen);
+ }
+
+ /**
+ * Attempts to write a <code>char</code> to the file as a two-byte
+ * value, high byte first. The write starts at the current position
+ * of the file pointer.
+ * <p>
+ * The <code>char</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param ch a <code>char</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see XAFile#writeChars(String)
+ * @see XAFile#readChar()
+ */
+ public void writeChar(int ch) throws IOException {
+ int[] chs = new int[]{((ch >>> 8) & 0xFF), ((ch) & 0xFF)};
+ writeRecord(chs);
+ }
+
+ /**
+ * Attempts to write the string to the file as a sequence of bytes. Each
+ * character in the string is written out, in sequence, by discarding
+ * its high eight bits. The write starts at the current position of
+ * the file pointer.
+ * <p>
+ * The <code>String</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param bytes a string of bytes to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void writeBytes(String bytes) throws IOException {
+ int len = bytes.length();
+ byte[] b = bytes.getBytes();
+ write(b, 0, len);
+ }
+
+ /**
+ * Attempts to write <code>bytes.length</code> bytes from the specified
+ * byte array to this file, starting at current file pointer.
+ * <p>
+ * The bytes will be written to the file only after a
+ * commit operation call
+ *
+ * @param bytes the data.
+ * @exception IOException if an I/O error occurs.
+ * @see XAFile#read(byte[])
+ */
+ public void write(byte[] bytes) throws IOException {
+ write(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Attempts to write a string to the file using
+ * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
+ * encoding in a machine-independent manner.
+ * <p>
+ * First, two bytes are written to the memory, starting at the
+ * current file pointer, as if by the
+ * <code>writeShort</code> method giving the number of bytes to
+ * follow. This value is the number of bytes actually written out,
+ * not the length of the string. Following the length, each character
+ * of the string is output, in sequence, using the modified UTF-8 encoding
+ * for each character.
+ * <p>
+ * The <code>String</code> will be written to the file only after a
+ * commit operation call
+ *
+ * @param str a string to be written.
+ * @exception IOException if an I/O error occurs
+ * @see XAFile#readUTF()
+ */
+ public void writeUTF(String str) throws IOException {
+ DataOutputStream.writeUTF(str, this);
+ }
+
+ /**
+ * Returns the current offset in this file.
+ *
+ * @return the offset from the beginning of the file, in bytes,
+ * at which the next attempt of read or write occurs.
+ * @exception IOException if an I/O error occurs.
+ */
+ public long getFilePointer() throws IOException {
+ return raf.getFilePointer();
+ }
+
+ /**
+ * Attempts to skip over <code>n</code> bytes of input discarding the
+ * skipped bytes.
+ * <p>
+ * This method may skip over some smaller number of bytes, possibly zero.
+ * This may result from any of a number of conditions; reaching end of
+ * file before <code>n</code> bytes have been skipped is only one
+ * possibility. This method never throws an <code>EOFException</code>.
+ * The actual number of bytes skipped is returned. If <code>n</code>
+ * is negative, no bytes are skipped.
+ *
+ * @param n the number of bytes to be skipped.
+ * @return the actual number of bytes skipped.
+ * @exception IOException if an I/O error occurs.
+ */
+ public int skipBytes(int n) throws IOException {
+ return raf.skipBytes(n);
+ }
+
+ /**
+ * Returns the length of this file.
+ *
+ * @return the length of this file, measured in bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public long length() throws IOException {
+ return raf.length();
+ }
+
+ /**
+ * Returns the current thread id. If this id was not used to
+ * register a Transaction before an <code>IOException</code> will
+ * be thrown.
+ * @return the current thread's id registered with a Transaction
+ * @exception IOException if the current thread is not associated
+ * with any Transaction
+ */
+ private long getCurrentThreadId() throws IOException {
+ Thread th = Thread.currentThread();
+ long th_id = th.getId();
+
+ if (xares.isEmpty() || !xares.containsKey(th_id)) {
+ throw new IOException("There is no thread-transaction association. " +
+ "\nCurrent thread with id=<" + th_id + "> has not been registered " +
+ "with any Transaction. Possibly a read/write operation happens outside " +
+ "the scope of TransactionManager (begin - commit/rollback.) or the " +
+ "\nnewTransaction() method has not been called after the TransactionManager " +
+ "has begun.");
+ }
+ return th_id;
+ }
+
+ /**
+ * Used by <code>XAResourceManager</code> after starting-up
+ * recovery procedure
+ * @exception FileNotFoundException if the File does not exist
+ */
+ protected void initRAF() throws FileNotFoundException {
+ raf = new RandomAccessFile(filename, mode);
+ }
+
+ /**
+ * Converts an array of type <code>int</code> to an array of
+ * type <code>byte</code> and returns that.
+ *
+ * @param ints the array to convert into a <code>byte</code> array
+ * @return an array of bytes containing the values from the
+ * <code>ints</code> array
+ */
+ protected static byte[] getBytesFromInts(int[] ints) {
+ byte[] bytes = new byte[ints.length];
+ for (int b = 0; b < ints.length; b++) {
+ bytes[b] = (byte) ints[b];
+ }
+ return bytes;
+ }
+
+ /**
+ * Converts an array of type <code>byte</code> to an array of
+ * type <code>int</code> and returns that.
+ *
+ * @param bytes the array to convert into an <code>int</code> array
+ * @return an array of <code>int</code> containing the values from the
+ * <code>bytes</code> array.
+ */
+ protected static int[] getIntsFromBytes(byte[] bytes) {
+ int[] ints = new int[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
+ ints[i] = bytes[i];
+ }
+ return ints;
+ }
+
+ protected void finalize() throws Throwable { //todo comments
+ try {
+ if (raf != null)
+ raf.close(); // close if file is open
+ } finally {
+ super.finalize();
+ }
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAResourceManager.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAResourceManager.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/file/XAResourceManager.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,470 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.file;
+
+import org.jboss.jbossts.fileio.xalib.txfiles.logging.LogEntry;
+import org.jboss.jbossts.fileio.xalib.txfiles.logging.RecordsLogger;
+import org.jboss.jbossts.fileio.xalib.Globals;
+import org.jboss.jbossts.fileio.xalib.txfiles.file.DataRecord;
+
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import javax.transaction.xa.XAException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.util.*;
+
+/**
+ * This class implements methods of the standard {@link javax.transaction.xa.XAResource} interface
+ * which acts like a contract between a Resource Manager and a Transaction
+ * Manager.
+ * <p>
+ * Each instance of an <code>XAResourceManager</code> has its own {@link javax.transaction.xa.Xid}
+ * which distinguishes by the other objects of this class and is associated
+ * with a Transaction. Each <code>XAResourceManager</code> instance has also
+ * a <code>log</code> ({@link RecordsLogger} object) to write information
+ * about the bytes that have been updated by <code>XAFile</code>. The updates
+ * are also added into a hashtable (depending on the starting position of the bytes
+ * that make up a {@link DataRecord} each time. If no crash has occured the
+ * Resource manager will retrieve information about the requested records using
+ * its hashtable in memory. In a situation where a system failure occured and
+ * the system tries to recover, the logs will be used to obtain the requested
+ * information.
+ * <p>
+ * The requested information will be written back to the original file only if
+ * Transaction Manager decides to commit and asks from the Resource Manager to
+ * commit and delete the log file. If Transaction Manager decides a rollback
+ * operation the Resource Manager invokes its own rollback operation which will
+ * cause all updates made so far to be removed and the log file to be deleted.
+ * When the log is deleted (either in commit or rollback invocations) the thread
+ * is disassociated from the corresponding transaction.
+ *
+ * @author Ioannis Ganotis
+ * @version Jun 13, 2008
+ *
+ * @see XAFile
+ * @see RecordsLogger
+ * @see DataRecord
+ */
+public class XAResourceManager implements XAResource, Serializable
+{
+ private Xid currentXid;
+ private int timeout;
+ private XAFile xaFile;
+ private RecordsLogger log;
+ private long th_id;
+ transient private final int DEFAULT_TIMEOUT = 60;
+ private boolean recovers;
+ transient private Hashtable<Long, Integer> updatedBytes;
+
+ /**
+ * Constructor to create Resource Manager objects. Each of these
+ * objects, at transaction time, are informed by the Transaction
+ * Manager to prepare, commit or rollback (depending on the outcome
+ * of the 2PC protocol). Each of these Resource Manager objects are
+ * kept in a hashtable in the XAFile and are associated with the
+ * thread id that created the corresponding Transaction.
+ * @param xaFile the file instance on which updates take place
+ * @param log the logger object which keeps update-relative information
+ * @param th_id the thread id that created the transaction in which
+ * this <code>XAResourceManager</code> object has been
+ * enlisted.
+ * @throws IOException if an I/O error occurs
+ */
+ protected XAResourceManager(XAFile xaFile, RecordsLogger log,
+ long th_id) throws IOException {
+ this.xaFile = xaFile;
+ this.log = log;
+ this.th_id = th_id;
+ timeout = DEFAULT_TIMEOUT;
+ recovers = false;
+ updatedBytes = new Hashtable<Long, Integer>(89);
+ }
+
+ /**
+ * Method to prepare a transaction with the given <code>xid</code> to
+ * commit.
+ * <p>
+ * The method forces system-memory buffers to write their data to the
+ * log file to ensure all the updates that are to be applied to the
+ * file are included in the log. The log file is then closed.
+ *
+ * @param xid a global Transaction id
+ * @return <code>XA_OK</code> after synchronizing the log
+ * @exception XAException if an error occured while synchronizing the log
+ */
+ public int prepare(Xid xid) throws XAException {
+ // flush data on disk here
+ System.out.println("XAResourceManager.prepare(Xid=" + xid + "), th_id=" + th_id);
+ try {
+ log.flush();
+ log.close();
+ } catch (IOException e) {
+ throw new XAException("Unable to flush data to the log file <" +
+ log.getFilename() + ">.");
+ }
+ return XAResource.XA_OK;
+ }
+
+ /**
+ * Method to commit the global transaction with the given <code>xid</code>.
+ * <p>
+ * If this method is not invoked by a Recovery Manager, then the updated
+ * bytes will be read from the hashtable that contains them and will be
+ * written back to the original source file. In case there was a system
+ * failure, the same procedure will be followed but the updated bytes
+ * will be read from the log file instead of the memory, as memory will
+ * contain no relative information during recovery.
+ *
+ * @param xid a global Transaction id
+ * @param onePhase If true, the resource manager should use a one-phase
+ * commit protocol to commit the work done on behalf of xid
+ * @exception XAException if an I/O error occurs when calling the
+ * <code>commitChanges</code> method
+ */
+ public void commit(Xid xid, boolean onePhase) throws XAException {
+ System.out.println("XAResourceManager.commit(Xid=" + xid + ", onePhase=" + onePhase + "), th_id=" + th_id);
+ if (!xid.equals(currentXid)) {
+ System.out.println("XAResourceManager.commit - wrong Xid!");
+ }
+//System.exit(1); // todo testcode ----------------------#################################---------------------
+
+// try {
+// if (th_id == 10l) {
+// Thread.sleep(3000);
+// }
+// if (th_id == 11l) {
+// Thread.sleep(3000);
+// }
+//// if (th_id == 12l) {
+//// System.exit(1);
+//// }
+// } catch (InterruptedException e) {
+// e.printStackTrace();
+// }
+ try {
+ commitChanges();
+// if (th_id == 12l)
+// throw new IOException("stupid exception!");
+ } catch (IOException ioe)
+ {
+ ioe.printStackTrace();
+ throw new XAException("Commit failed! Possibly an I/O error occurd while " +
+ "using the log file <" + log.getFilename() + ">" );
+ }
+
+ log.delete();
+ System.out.println("Original File Updated Successfully.");
+
+ currentXid = null;
+
+ }
+
+ /**
+ * Ends the work performed on behalf of a transaction branch.
+ *
+ * @param xid a global transaction identifier that is the same as what was used
+ * previously in the start method
+ * @param flags (can be anything)
+ */
+ public void end(Xid xid, int flags) {
+ System.out.println("XAResourceManager.end(Xid=" + xid + ", flags=" + flags + "), th_id=" + th_id);
+ }
+
+ /**
+ * Forget about a heuristically completed transaction branch.
+ * The <code>currentXid</code> is set to null
+ * @param xid a global Transaction id
+ */
+ public void forget(Xid xid) {
+ System.out.println("XAResourceManager.forget(Xid=" + xid + ")");
+ if (!xid.equals(currentXid)) {
+ System.out.println("XAResourceManager.forget - wrong Xid!");
+ }
+ currentXid = null;
+ }
+
+ /**
+ * Obtain the current transaction timeout value set for this
+ * <code>XAResourceManager</code> instance. If
+ * <code>XAResourceManager.setTransactionTimeout</code> was not used prior to
+ * invoking this method, the return value is the default timeout set for
+ * the resource manager; otherwise, the value used in the previous
+ * <code>setTransactionTimeout</code> call is returned.
+ *
+ * @return the transaction timeout value in seconds
+ */
+ public int getTransactionTimeout() {
+ System.out.println("XAResourceManager.getTransactionTimeout() [returning " + timeout + "]");
+ return timeout;
+ }
+
+ /*
+ * No implementation.
+ * @param xares an XAResource object whose resource manager instance is to
+ * be compared with the resource manager instance of the target
+ * object
+ * @return always false
+ */
+ public boolean isSameRM(XAResource xares) {
+ System.out.println("XAResourceManager.isSameRM(xares=" + xares + ")");
+ return false;
+ }
+
+ /*
+ * No implementation.
+ * @param flag
+ * @return always zero xids
+ */
+ public Xid[] recover(int flag) {
+ System.out.println("XAResourceManager.recover(flag=" + flag + ")");
+ return new Xid[0];
+ }
+
+ /**
+ * Rollback any updates attempted to be written to the file.
+ * <p>
+ * The method closes the log file and deletes it as it is not
+ * useful anymore. It also deletes the bytes from the hashtable
+ * and disassociates the transaction with the given <code>xid</code>
+ * from the thread that initiated it, if not called by the Recovery
+ * Manager.
+ *
+ * @param xid a global Transaction id
+ * @exception XAException
+ * if an error occured while trying to disassociate the
+ * the given <code>xid</code> from its initiator thread
+ */
+ public void rollback(Xid xid) throws XAException {
+ System.out.println("XAResourceManager.rollback(Xid=" + xid + "), th_id=" + th_id);
+ long th_id = Globals.RECOVERY_ID;
+ if (!xid.equals(currentXid)) {
+ System.out.println("XAResourceManager.rollback - wrong Xid!");
+ }
+
+ if (!recovers) { // normal operation (memory)
+ th_id = this.th_id;
+ updatedBytes.clear();
+ }
+ try {
+ xaFile.removeTransaction(currentXid, recovers);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new XAException("Rollback failed. Could not disassociate " +
+ "the current thread " + th_id + "from the transaction with xid=<" + xid);
+ }
+ log.close();
+ log.delete();
+ currentXid = null;
+ }
+
+ /**
+ * Set the current transaction <code>timeout</code> value for this
+ * <code>XAResourceManager</code> instance. Once set, this timeout value is
+ * effective until <code>setTransactionTimeout</code> is invoked again with
+ * a different value. To reset the timeout value to the default value used by
+ * the resource manager, set the value to zero. If the timeout operation is
+ * performed successfully, the method returns true; otherwise false. If a
+ * resource manager does not support transaction timeout value to be set
+ * explicitly, this method returns false
+ * @param seconds transaction timeout value in seconds
+ * @return true if transaction timeout value is set successfully;
+ * otherwise false
+ */
+ public boolean setTransactionTimeout(int seconds) {
+ System.out.println("XAResourceManager.setTransactionTimeout(timeout=" + seconds + ")");
+ if (seconds >= 0) {
+ timeout = seconds;
+ if (timeout == 0)
+ timeout = DEFAULT_TIMEOUT;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Start work on behalf of a transaction branch specified in <code>xid</code>.
+ * @param xid a global Transaction id to be associated with this
+ * Resource Manager instance
+ * @param flags (can be anything)
+ * @throws XAException if there is already a Transaction
+ */
+ public void start(Xid xid, int flags) throws XAException {
+ System.out.println("XAResourceManager.start(Xid=" + xid + ", flags=" +
+ flags + "), th_id=" + th_id);
+ if (currentXid != null) {
+ System.out.println("XAResourceManager.start - wrong Xid!");
+ throw new XAException("Current Transaction is: <" + currentXid +
+ ">\nCannot start the new Transaction.");
+ }
+ currentXid = xid;
+ }
+
+ /**
+ * The method is invoked by the <code>commit</code> method
+ * of the <code>XAResourceManager</code> and performs the
+ * work as specified in that method.
+ *
+ * @exception IOException If an I/O error occurs while reading the log
+ */
+ private void commitChanges() throws IOException {
+ long th_id = this.th_id;
+ if (recovers) { // after a crash occured
+ th_id = Globals.RECOVERY_ID;
+ }
+ LinkedList<DataRecord> records = retrieveRecords();
+
+ for (DataRecord dr : records) {
+ xaFile.commitUpdates(dr.getStartPosition(),
+ dr.getRecordLength(), dr.getRecordBytes(), th_id);
+ }
+ xaFile.sync(); // Force updates to be written to the file
+ if (!recovers) { // normal operation(memory)
+ updatedBytes.clear();
+ }
+ xaFile.removeTransaction(currentXid, recovers);
+ }
+
+ /**
+ * Returns a list with the records (updates) written by some write
+ * operation in the <code>XAFile</code>. It either reads the records
+ * from the hashtable in memory (if not in recover phase), or from
+ * the <code>log</code> file which contains all the updates (if the
+ * system tries to recover).
+ *
+ * @return a list with <code>DataRecord</code> objects that contain
+ * the updated data
+ * @exception IOException if an I/O error occurs
+ */
+ private LinkedList<DataRecord> retrieveRecords() throws IOException {
+ LinkedList<DataRecord> records = new LinkedList<DataRecord>();
+ if (recovers) {
+ LinkedList<LogEntry> logRecords = log.readAllRecords();
+ for (LogEntry entry : logRecords) {
+ try {
+ DataRecord dr = new DataRecord(entry.getPosition(), entry.getRecordLength(),
+ XAFile.getIntsFromBytes(entry.getData()));
+ records.add(dr);
+ } catch (Exception ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ } else {
+ Set<Map.Entry<Long, Integer>> set = updatedBytes.entrySet();
+ Iterator<Map.Entry<Long, Integer>> it = set.iterator();
+
+ while (it.hasNext()) {
+ int[] bs = new int[set.size()];
+ Map.Entry<Long, Integer> kvPair = it.next();
+ long position = kvPair.getKey();
+ int b = kvPair.getValue();
+ int index = bs.length-1;
+ bs[index] = b;
+ while (it.hasNext()) {
+ kvPair = it.next();
+ if (position - kvPair.getKey() == 1) {
+ index--;
+ bs[index] = kvPair.getValue();
+ position = kvPair.getKey();
+ } else
+ break;
+ }
+ int[] bytes = new int[bs.length - index];
+ System.arraycopy(bs, 0, bytes, 0, bytes.length);
+ records.add(new DataRecord(position-index, bytes.length, bytes));
+ }
+ }
+ return records;
+ }
+
+ /**
+ * Adds adequate information to the <code>log</code>. Each entry
+ * consists of the start position of the record in the file, its
+ * length and the data (updates)
+ * @param dr the record by which information will be written to the log
+ * @exception IOException if an I/O error occurs
+ */
+ protected void add2Log(DataRecord dr) throws IOException {
+ LogEntry le = new LogEntry(dr.getStartPosition(), dr.getRecordLength(), dr.getRecordBytes());
+ log.addInfo(le);
+ }
+
+ /**
+ * Returns the global Transaction id associated with this
+ * <code>XAResourceManager</code>
+ * @return the global Transaction id associated with this
+ * XAResourceManager
+ */
+ protected Xid getXid() {
+ return currentXid;
+ }
+
+ /**
+ * Returns all the updated bytes that have been modified by the
+ * transaction with <code>currentXid</code>.
+ * @return all the updated bytes that have been stored in the
+ * table by previous <code>write</code> operations.
+ */
+ protected Hashtable<Long, Integer> getUpdatedBytes() {
+ return updatedBytes;
+ }
+
+ /**
+ * Adds the given <code>bytes</code> to the table that keeps all
+ * the <code>updatedBytes</code>. The array is treated as a group
+ * of modified bytes (record) with its first byte located at
+ * <code>startPosition</code> in the Transactional File.
+ *
+ * @param startPosition the position in the file where the record starts
+ * @param bytes an array that includes all the updated bytes to be stored
+ */
+ protected void addUpdatedBytes(long startPosition, int[] bytes) {
+ for (int b : bytes)
+ updatedBytes.put(startPosition++, b);
+ }
+
+ /**
+ * This method is invoked when trying to deserialize an <code>XAResourceManager</code>
+ * object.
+ * <p>
+ * The method will read the non-static and non-transient fields of the current class and
+ * will set the <code>recovers</code> value to <code>true</code>. This will allow the
+ * rest of the methods in this class to be aware that the system is in its recovery
+ * phase and aact properly.
+ * <p>
+ * The method also initializes the <code>RandomAccessFile</code> object used in the
+ * <Code>XAFile</code> as well as the <code>XALockManager</code> objects to obtain the
+ * list of held locks.
+ *
+ * @param in input stream to read from
+ * @exception IOException if an I/O error occurs
+ * @exception ClassNotFoundException if the class of the Serialized object cannot be
+ * found
+ */
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+ recovers = true;
+ xaFile.initRAF();
+ xaFile.initLocksHeld();
+ }
+}
\ No newline at end of file
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALock.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALock.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALock.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,381 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.locking;
+
+import com.arjuna.ats.txoj.LockMode;
+import javax.transaction.xa.Xid;
+import java.io.Serializable;
+import java.util.Hashtable;
+
+import org.jboss.jbossts.fileio.xalib.Globals;
+
+/**
+ * Instances of this class allow accessing the information held by each lock.
+ * The <em>byte-range</em> to lock, the <code>mode</code> as well as the
+ * <code>xid</code> are the ones that define a lock.
+ * <p>
+ * Each lock object has a separate mode for each byte in its byte-range. When
+ * initialised all of these bytes have the mode passed in the constructor during
+ * initialisation. Computations by the <code>adjustWith</code> method determine
+ * which of the bytes within the byte-range needs to change mode. This will allow
+ * more concurrent operations acting on the same lock.
+ * <p>
+ * The class implements {@link java.io.Serializable} as the objects need to be
+ * stored in a file by the {@link XALockManager}. It also implements the
+ * {@link Comparable} interface to allow comparisons between <code>XALock</code>
+ * objects.
+ *
+ * @author Ioannis Ganotis
+ * @version Jul 23, 2008
+ */
+public class XALock implements Serializable, Comparable<XALock>
+{
+ private Xid xid;
+ private long startPosition;
+ private long lockLength;
+ private Hashtable<Long, Integer> byteModes;
+
+ /**
+ * Constructor to create <code>XALock</code> objects. These objects are
+ * managed by the {@link XALockManager} and are stored in a <em>locks file</em>.
+ *
+ * @param xid the global Transaction id which was used when the lock was acquired
+ * @param mode the mode (<code>LockMode.READ</code or <code>LockMode.WRITE) in which
+ * the bytes in the byte-range will be initialised
+ * @param startPosition the position in the file of the first byte in the byte-range
+ * @param lockLength the number of bytes to lock
+ */
+ public XALock(Xid xid, int mode, long startPosition, long lockLength) {
+ this.xid = xid;
+ this.startPosition = startPosition;
+ this.lockLength = lockLength;
+
+ initModes(mode);
+ }
+
+ /**
+ * The method is used to initialise all the bytes in the byte range to the
+ * given <code>mode</code>.
+ *
+ * @param mode the mode in which the bytes will be initialised
+ */
+ private void initModes(int mode) {
+ byteModes = new Hashtable<Long, Integer>(89);
+ long sp = startPosition;
+ while (sp < getEndPosition()) {
+ byteModes.put(sp++, mode);
+ }
+ }
+
+ /**
+ * Returns the global id used when this lock was acquired.
+ *
+ * @return the global Transaction id used when the lock was
+ * acquired
+ */
+ protected Xid getXid() {
+ return xid;
+ }
+
+ /**
+ * This method sets a <code>newMode</code> at <code>position</code>
+ *
+ * @param position the position at which the new mode will be set
+ * @param newMode the new mode to set to the byte at <code>position</code>
+ */
+ private void setModeAt(long position, int newMode) {
+ byteModes.put(position, newMode);
+ }
+
+ /**
+ * This method sets a <code>newMode</code> to a byte-range.
+ *
+ * @param position the position of the first byte in the byte range
+ * @param len the number of bytes that will change mode
+ * @param newMode the new mode to set to a range of bytes
+ */
+ private void setModeStartingAt(long position, long len, int newMode) {
+ long ps = position;
+ while (ps < len+position) {
+ setModeAt(ps++, newMode);
+ }
+ }
+
+ /**
+ * Returns the position in the file where this lock starts.
+ *
+ * @return the start position of this lock
+ */
+ private long getStartPosition() {
+ return startPosition;
+ }
+
+ /**
+ * This method sets a new start position of this lock. The
+ * method is especially useful when the bounds of the lock
+ * have to be moved.
+ *
+ * @param startPosition the new start position of this lock
+ */
+ private void setStartPosition(long startPosition) {
+ long len_difference = this.startPosition - startPosition;
+ lockLength += len_difference;
+ this.startPosition = startPosition;
+ }
+
+ /**
+ * Returns the length of the locked (by this lock) region in the
+ * Transactional file.
+ *
+ * @return a <code>long</code> which specifies how many bytes have
+ * been locked by this lock
+ */
+ private long getLockLength() {
+ return lockLength;
+ }
+
+ /**
+ * This method modifies the amount of bytes locked by this lock
+ *
+ * @param lockLength how many bytes to lock
+ */
+ private void setLockLength(long lockLength) {
+ this.lockLength = lockLength;
+ }
+
+ /**
+ * Returns the end position in the file of this lock.
+ *
+ * @return a <code>long</code> which represents the end
+ * of this lock within the Transactional file
+ */
+ private long getEndPosition() {
+ return getStartPosition() + getLockLength();
+ }
+
+ /**
+ * This method returns true if the <code>mode</code> is contained
+ * in the byte-range of this lock; otherwise returns false.
+ *
+ * @param mode the mode to check if exists
+ * @return true if the <code>mode</code> exists in the byte-range;
+ * false otherwise
+ */
+ private boolean containsMode(int mode) {
+ return byteModes.containsValue(mode);
+ }
+
+ /**
+ * This method checks if the <code>mode</code> is contained within a
+ * specified range (sub-range) in the byte-range of this lock.
+ *
+ * @param sp the start position of the sub-range to search
+ * @param ep the end position of the sub-range
+ * @param mode the mode to search if exists
+ * @return true if the mode exists within the given sub-range
+ */
+ private boolean containsModeBetween(long sp, long ep, int mode) {
+ for (long p=sp; p<ep; p++) {
+ if (byteModes.get(sp) == mode)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method returns true if the <code>newLock</code> conflicts with this
+ * lock; false otherwise.
+ *
+ * @param newLock the lock trying to add
+ * @return true if there is a conflict between <code>this</code> lock and the
+ * <code>newLock</code>; false otherwise
+ */
+ private boolean conflictsWith(XALock newLock) {
+ return newLock == null || this.wraps(newLock) ||
+ this.isWrappedBy(newLock) || this.equals(newLock) ||
+ this.intersectedFromLeft(newLock) || this.intersectedFromRight(newLock);
+ }
+
+ /**
+ * This method performs some calculations to return a result whether
+ * a conflict exists or not. If the conflict exists then it tries to
+ * check the byte-range to see if the lock can be granted or not. If
+ * needed the method will modify the bounds of existing locks so the
+ * new one can fit next to it.
+ *
+ * @param neighLock the lock located next to this lock
+ * @return <code>Globals.UPDATE_OLD_LOCK</code>, <code>Globals.ADD_NEW_LOCK</code>
+ * <code>Globals.MOVE_LOCK_BOUNDS</code>, <code>Globals.REFUSE_LOCK</code>,
+ * <code>Globals.NO_MOD_LOCK</code>, depending on the outcome of computations
+ */
+ protected int adjustWith(XALock neighLock) {
+ final int R_M = LockMode.READ;
+ final int W_M = LockMode.WRITE;
+ int result = Globals.REFUSE_LOCK;
+
+ if (neighLock != null && neighLock.conflictsWith(this)) {
+ if (neighLock.getXid() == this.getXid()) {
+ if (this.equals(neighLock) || neighLock.wraps(this)) {
+ if (!this.containsMode(W_M))
+ result = Globals.NO_MOD_LOCK;
+ if (!this.containsMode(R_M)) {
+ neighLock.setModeStartingAt(this.getStartPosition(), this.getLockLength(), W_M);
+ result = Globals.UPDATE_OLD_LOCK;
+ }
+ } else if (neighLock.intersectedFromLeft(this)) {
+ long neighSP = neighLock.getStartPosition();
+ long conflict_length = this.getEndPosition() - neighSP;
+ if (!this.containsMode(R_M)) // the new Lock is in W_M
+ neighLock.setModeStartingAt(neighSP, conflict_length, W_M);
+ this.setLockLength(this.getLockLength() - conflict_length);
+ result = Globals.MOVE_LOCK_BOUNDS;
+ } else if (neighLock.intersectedFromRight(this)) {
+ long neighEP = neighLock.getEndPosition();
+ long conflict_length = neighEP - this.getStartPosition();
+ if (!this.containsMode(R_M)) // the new lock is in W_M
+ neighLock.setModeStartingAt(this.getStartPosition(), conflict_length, W_M);
+ this.setStartPosition(neighEP); // lock length will be calculated by this method
+ result = Globals.MOVE_LOCK_BOUNDS;
+ } else if (neighLock.isWrappedBy(this)) {
+ if (!this.containsMode(R_M)) {
+ neighLock.setStartPosition(this.getStartPosition());
+ neighLock.setLockLength(this.getLockLength());
+ neighLock.initModes(W_M);
+ result = Globals.UPDATE_OLD_LOCK;
+ }
+ }
+ } else { //different Transaction
+ if (this.containsMode(W_M) || neighLock.containsMode(W_M)) {
+ result = Globals.REFUSE_LOCK;
+ } else if (neighLock.intersectedFromLeft(this)) {
+ if (!this.containsMode(W_M)) {
+ if (neighLock.containsModeBetween(neighLock.getStartPosition(), this.getEndPosition(), R_M))
+ result = Globals.NO_MOD_LOCK;
+ else
+ result = Globals.REFUSE_LOCK;
+ }
+ } else if (neighLock.intersectedFromRight(this)) {
+ if (!this.containsMode(W_M)) {
+ if (neighLock.containsModeBetween(this.getStartPosition(), neighLock.getEndPosition(), R_M))
+ result = Globals.NO_MOD_LOCK;
+ else
+ result = Globals.REFUSE_LOCK;
+ }
+ }
+ }
+ } else {
+ return Globals.ADD_NEW_LOCK;
+ }
+ return result;
+ }
+
+ /**
+ * The method returns true if an existing lock surrounds the new lock;
+ * false otherwise.
+ *
+ * @param newLock the lock trying to add
+ * @return true if the existing lock surrounds the new lock; false othewise
+ */
+ private boolean wraps(XALock newLock) {
+ return (this.getStartPosition() <= newLock.getStartPosition() &&
+ this.getEndPosition() > newLock.getEndPosition()) ||
+ (this.getStartPosition() < newLock.getStartPosition() &&
+ this.getEndPosition() >= newLock.getEndPosition());
+ }
+
+ /**
+ * The method returns true if the existing lock is surrounded by the
+ * new lock; false otherwise.
+ *
+ * @param newLock the lock trying to add
+ * @return true if the existing lock is surrounded by the new lock;
+ * false otherwise
+ */
+ private boolean isWrappedBy(XALock newLock) {
+ return (this.getStartPosition() >= newLock.getStartPosition() &&
+ this.getEndPosition() < newLock.getEndPosition()) ||
+ (this.getStartPosition() > newLock.getStartPosition() &&
+ this.getEndPosition() <= newLock.getEndPosition());
+ }
+
+ /**
+ * Returns true if the existing lock conflicts from the left hand side
+ * with the new lock; false otherwise.
+ *
+ * @param newLock the lock trying to add
+ * @return true if the existing lock conflicts from the left hand side
+ * with the new lock; false otherwise
+ */
+ private boolean intersectedFromLeft(XALock newLock) {
+ return newLock.getStartPosition() < this.getStartPosition() &&
+ newLock.getEndPosition() <= this.getEndPosition();
+ }
+
+ /**
+ * Returns true if the existing lock conflicts from the right hand side
+ * with the new lock; false otherwise.
+ *
+ * @param newLock the lock trying to add
+ * @return true if the existing lock conflicts from the right hand side
+ * with the new lock; false otherwise
+ */
+ private boolean intersectedFromRight(XALock newLock) {
+ return newLock.getStartPosition() >= this.getStartPosition() &&
+ newLock.getEndPosition() > this.getEndPosition();
+ }
+
+ /**
+ * This method compares two locks.
+ * <p>It will return:
+ * <code>-1</code> if the new lock is on the right side of the existing lock
+ * <p>
+ * <code>0</code> if both of the locks are starting at the same position
+ * <p>
+ * <code>1</code> if the new lock is on the left side of the existing lock
+ *
+ * @param newXALock the lock trying to add
+ * @return <code>-1</code> if the new lock is on the right side of the existing lock
+ * <code>0</code> if both of the locks are starting at the same position
+ * <code>1</code> if the new lock is on the left side of the existing lock
+ */
+ public int compareTo(XALock newXALock) {
+ if (getStartPosition() < newXALock.getStartPosition()) {
+ return 1;
+ } else if (getStartPosition() > newXALock.getStartPosition()) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * This method checks the equality of two locks. It will return
+ * true if both of the locks start at the same point in the
+ * Transactional file and they have the same length; false
+ * otherwise.
+ *
+ * @param newLock the lock trying to add
+ * @return true if the locks are equal; false otherwise
+ */
+ private boolean equals(XALock newLock) {
+ return (this.compareTo(newLock) == 0 &&
+ getLockLength() == newLock.getLockLength());
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALockManager.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALockManager.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/locking/XALockManager.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,334 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.locking;
+
+import com.arjuna.ats.txoj.LockResult;
+import javax.transaction.xa.Xid;
+import java.io.*;
+import java.util.*;
+
+import org.jboss.jbossts.fileio.ObjectOutputStreamAppend;
+import org.jboss.jbossts.fileio.xalib.Globals;
+
+/**
+ * This class is used to manage Locking on a {@link org.jboss.jbossts.fileio.xalib.txfiles.file.XAFile}. When methods
+ * like read/write are invoked from within the <code>XAFile</code> class
+ * locks are acquired automatically in read or write modes, respectively.
+ * <p>
+ * The class uses a list to maintain all the locks held by the VM. If
+ * multiple VMs act on the same Transactional file simultaneously the
+ * locks are kept in a file, so every VM is aware of which regions of the
+ * file are locked. Processing a different <code>XAFile</code> will
+ * produce another <em>lock file</em> containing the locked regions of
+ * that Transactional file.
+ * <p>
+ * When a lock is acquired through the <code>tryLock</code> method, depending
+ * on the outcome of the <code>manageLocks</code> a lock will either be
+ * <em>GRANTED</em> or <em>REFUSED</em>. Internally, computations happen to
+ * either add a new lock, update an existing one (by moving its bounds or
+ * changing its access mode), or do nothing at all.
+ *
+ * @author Ioannis Ganotis
+ * @version Jul 23, 2008
+ */
+public class XALockManager implements Serializable
+{
+ private String filename = Globals.LOCKS_FOLDER_PATH;
+ transient private LinkedList<XALock> heldLocks;
+
+ /**
+ * Constructor to create <code>XALockManager</code> objects. Using such
+ * an object <code>XAFile</code> can acquire read/write locks on specific
+ * regions of any Transactional file.
+ * <p>
+ * The constructor also invokes a method to get all the locks that have
+ * been held so far, before going any further.
+ *
+ * @param xaFilename the Transactional file on which to apply locks
+ */
+ public XALockManager(String xaFilename) {
+ filename += getProcessedName(xaFilename);
+ heldLocks = new LinkedList<XALock>();
+ try {
+ File f = new File(filename);
+ if (f.exists()) {
+ obtainHeldLocksWith(null);
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
+ private String getProcessedName(String xaFilename) {
+ xaFilename = xaFilename.replace('/', '_');
+ xaFilename = xaFilename.replace('\\', '_');
+ return xaFilename.concat("_locks.log");
+ }
+
+ /**
+ * This method opens an existing <em>locks file</em> and retrieves its existing
+ * {@link XALock} objects. These objects now are kept in memory and can be used
+ * for further processing.
+ *
+ * @param xid the global Transaction id that was used when a lock was acquired.
+ * if <code>xid</code> is <code>null</code> then all the locks that
+ * are in a file will be returned.
+ * @return a list of {@link XALock} objects acquired by a Transaction with the
+ * given <code>xid</code>. If <code>xid</code> is <code>null</code> then
+ * all the locks that are stored in that file will be returned.
+ * @exception IOException if an I/O error occurs
+ */
+ public synchronized LinkedList<XALock> obtainHeldLocksWith(Xid xid) throws IOException {
+ File file = new File (filename);
+ if (file.exists()) {
+ FileInputStream fIn = new FileInputStream(filename);
+ ObjectInputStream in = new ObjectInputStream(fIn);
+
+ boolean eof = false;
+ do {
+ try {
+ Object obj = in.readObject();
+ XALock xaLock = (XALock) obj;
+ if (xaLock.getXid() == xid || xid == null) {
+ insertLockAt(binarySearch(xaLock), xaLock);
+ }
+ } catch (EOFException eofe) {
+ eof = true;
+ } catch (ClassNotFoundException cnfe) {
+ cnfe.printStackTrace();
+ }
+ } while (!eof);
+ in.close();
+ fIn.close();
+ }
+ return heldLocks;
+ }
+
+ /**
+ * The method is used to keep the memory locks synchronized
+ * with the ones that already exist in the file. Non existing
+ * ones will be just added to the <em>locks file</em>.
+ */
+ private synchronized void syncLocks() {
+ try {
+ FileOutputStream fOut = new FileOutputStream(filename);
+ ObjectOutputStream out = new ObjectOutputStream(fOut);
+
+ for (XALock lock : heldLocks) {
+ out.writeObject(lock);
+ }
+ out.close();
+ fOut.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
+ /**
+ * This method tries to set a given <code>lock</code>. If the lock to
+ * be set is <code>null</code> the outcome is to <em>REFUSE</em> that
+ * lock. In other cases the lock is added, the <em>locks file</code>
+ * is synchronized, or both apply.
+ * @param lock the lock trying to set
+ * @return <code>LockResult.GRANTED</code> if the lock can be granted
+ * or <code>LockResult.REFUSED</code> fail to grant the lock
+ *
+ * @exception IOException if an I/O error occurs while adding new locks
+ */
+ public synchronized int tryLock(XALock lock) throws IOException {
+ if (lock != null) {
+ int index = binarySearch(lock);
+ int vResult = manageLocks(index, lock);
+ if (vResult != Globals.REFUSE_LOCK) { // allowed to add the new lock
+ if (vResult == Globals.ADD_NEW_LOCK || vResult == Globals.MOVE_LOCK_BOUNDS) {
+ if (vResult == Globals.MOVE_LOCK_BOUNDS)
+ syncLocks();
+ insertLockAt(index, lock);
+ FileOutputStream fOut;
+ ObjectOutputStream out;
+ File lockFile = new File(filename);
+
+ if (lockFile.exists()) { // Append the file, write new locks at the end
+ fOut = new FileOutputStream(filename, true);
+ out = new ObjectOutputStreamAppend(fOut);
+ } else {
+ fOut = new FileOutputStream(filename);
+ out = new ObjectOutputStream(fOut);
+ }
+ out.writeObject(lock);
+ out.close();
+ fOut.close();
+ } else if (vResult == Globals.UPDATE_OLD_LOCK) {
+ syncLocks();
+ }
+ return LockResult.GRANTED;
+ }
+ }
+ return LockResult.REFUSED;
+ }
+
+ /**
+ * Searches the list of locks in memory to find at which index the
+ * <code>newLock</code> should be added. The list is sorted, so
+ * when finds the appropriate index, returns it.
+ *
+ * @param newLock the lock to add in the locks list
+ * @return an index at which the <code>newLock</code> is about to be added
+ */
+ private int binarySearch(XALock newLock) { //todo fix comment
+ int mid;
+ int res = 0;
+ int low = 0;
+ int high = heldLocks.size() - 1;
+
+ while( low <= high )
+ {
+ mid = ( low + high ) / 2;
+ XALock curLock = heldLocks.get(mid);
+ int compRes = curLock.compareTo(newLock);
+
+ if( compRes > 0 ) {
+ low = mid + 1;
+ res = mid+1;
+ }
+ else if( compRes < 0 ) {
+ high = mid - 1;
+ res = mid;
+ }
+ else
+ return mid;
+ }
+ return res; // NOT_FOUND = -1
+ }
+
+ /**
+ * Inserts the given <code>newLock</code> at the specified <code>index</code>.
+ *
+ * @param index the index to add the new lock
+ * @param newLock the lock to be added
+ */
+ private void insertLockAt(int index, XALock newLock) {
+ if (index >= 0) {
+ heldLocks.add(index, newLock);
+ }
+ }
+
+ /**
+ * The method takes the given <code>xaLock</code> and checks any of its
+ * existing neighbours. If any of the neighbours deny to add the new lock,
+ * it will be <em>REFUSED</em> from being added. Other possible cases is
+ * to return <code>Globals.UPDATE_OLD_LOCK</code>, <code>Globals.MOVE_LOCK_BOUNDS</code>,
+ * or <code>Globals.ADD_NEW_LOCK</code>.
+ * <p>
+ * If one of the neighbours is <code>null</code> the result of the other neighbour is
+ * returned instead. if both of them are <code>null</code> the method returns
+ * <code>Globals.ADD_NEW_LOCK</code>, as this means there are no neighbours and as
+ * a result there are no conflicts, so the new lock can be added safely.
+ *
+ * @param it the index at which the lock will be added. Before adding the lock this index
+ * points to the right neighbour, or to a <code>null</code> cell, if the
+ * neighbour does not exist.
+ * @param xaLock the lock trying to add
+ * @return returns <code>Globals.ADD_NEW_LOCK</code>, <code>Globals.UPDATE_OLD_LOCK</code>,
+ * <code>Globals.MOVE_LOCK_BOUNDS</code>, or <code>Globals.REFUSE_LOCK</code>
+ * depending on the result of computations.
+ */
+ private int manageLocks(int it, XALock xaLock) {
+ if (xaLock.getXid() != null) {
+ XALock leftLock = null;
+ XALock rightLock = null;
+
+ if (it > 0)
+ leftLock = heldLocks.get(it-1);
+ if (it < heldLocks.size())
+ rightLock = heldLocks.get(it);
+
+ if (leftLock != null && rightLock != null) {
+ int leftResult = xaLock.adjustWith(leftLock);
+ int rightResult = xaLock.adjustWith(rightLock);
+
+ if (leftResult == Globals.REFUSE_LOCK || rightResult == Globals.REFUSE_LOCK)
+ return Globals.REFUSE_LOCK;
+ else {
+ if (leftResult == Globals.MOVE_LOCK_BOUNDS || rightResult == Globals.MOVE_LOCK_BOUNDS) {
+ return Globals.MOVE_LOCK_BOUNDS;
+ } else if (leftResult == Globals.UPDATE_OLD_LOCK || rightResult == Globals.UPDATE_OLD_LOCK) {
+ return Globals.UPDATE_OLD_LOCK;
+ } else if (leftResult == Globals.ADD_NEW_LOCK || rightResult == Globals.ADD_NEW_LOCK) {
+ return Globals.ADD_NEW_LOCK;
+ }
+ }
+ } else
+ {
+ if (leftLock == null && rightLock == null) {
+ return Globals.ADD_NEW_LOCK;
+ } else if (leftLock == null) {
+ return xaLock.adjustWith(rightLock);
+ } else {
+ return xaLock.adjustWith(leftLock);
+ }
+ }
+ }
+ return Globals.REFUSE_LOCK;
+ }
+
+ /**
+ * This method releases all the locks that have been held by
+ * a Transaction with the given <code>xid</code>.
+ * <p>
+ * The method removes existing locks from the memory and then
+ * synchronizes the <em>locks file</em>.
+ *
+ * @param xid the global Transaction id which was used for
+ * the locks trying to release
+ */
+ public void releaseLocks(Xid xid) {
+ XALock[] locks = new XALock[heldLocks.size()];
+ heldLocks.toArray(locks);
+ for (XALock lock : locks) {
+ if (lock.getXid() == xid) {
+ heldLocks.remove(lock);
+ }
+ }
+ syncLocks();
+// System.out.println("--- Locks participating in transaction with xid=" + xid +
+// " have been released!");
+ }
+
+ /**
+ * Returns a list with all the held locks.
+ * @return a list with all the held locks
+ */
+ public LinkedList<XALock> getHeldLocks() {
+ return heldLocks;
+ }
+
+ /**
+ * This method deletes the <em>locks file</em> and
+ * is called by the <code>XAFile#close</code>, as it
+ * is no more needed.
+ */
+ public void deleteFile() {
+ File f = new File(filename);
+ if (f.exists())
+ f.delete();
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/LogEntry.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/LogEntry.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/LogEntry.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.logging;
+
+/**
+ * This class represents an entry in a log file the information of which is
+ * handled by a {@link RecordsLogger} object.
+ * <p>
+ * The class is only a single representation of such entries and is not written
+ * in any of the log files. It is the information it includes that is written
+ * and there are appropriate <em>get</em> methods to support this.
+ *
+ * @author Ioannis Ganotis
+ * @version Jul 25, 2008
+ */
+public class LogEntry //implements Serializable
+{
+ private long position;
+ private int recordLength;
+ private byte[] data;
+
+ /**
+ * Constructor to create <code<LogEntry</code> objects to represent an
+ * entry in the log file.
+ *
+ * @param position the position in the Transactional file of the first byte
+ * in the <code>data</code> array of bytes
+ * @param recordLength the length of the <code>data</code> to read/write
+ * @param data an array of the updated bytes
+ */
+ public LogEntry(long position, int recordLength, byte[] data) {
+ this.position = position;
+ this.recordLength = recordLength;
+ this.data = data;
+ }
+
+ /**
+ * Returns the position of the first byte in the file.
+ * @return the position of the first byte in the file
+ */
+ public long getPosition() {
+ return position;
+ }
+
+ /**
+ * Returns the length of the <code>data</code> of this record.
+ * @return the length of the <code>data</code> of this record
+ */
+ public int getRecordLength() {
+ return recordLength;
+ }
+
+ /**
+ * Returns the actual updated bytes of this entry.
+ * @return the actual updated bytes of this entry
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
Added: labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/RecordsLogger.java
===================================================================
--- labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/RecordsLogger.java (rev 0)
+++ labs/jbosstm/workspace/transactionalFileIO/trunk/src/org/jboss/jbossts/fileio/xalib/txfiles/logging/RecordsLogger.java 2008-08-28 15:21:53 UTC (rev 21957)
@@ -0,0 +1,151 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a full listing
+ * of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author Red Hat Middleware LLC.
+ */
+package org.jboss.jbossts.fileio.xalib.txfiles.logging;
+
+import java.io.*;
+import java.util.LinkedList;
+
+/**
+ * This class is used to handle important information with log files.
+ * <p>
+ * The class provides both read and write operations to retrieve or
+ * add new information to a log file. To manage this an object of
+ * <code>RandomAccessFile</code> class is used and data are written
+ * in binary format (<code>writeLong, writeInt</code>). The option
+ * given by a <code>RandomAccessFile</code> object to define whether
+ * to open the file in <code>r</code> or <code>rw</code> mode is useful
+ * to disallow any writes when reading the <code>log</code>.
+ *
+ * @author Ioannis Ganotis
+ * @version Jun 17, 2008
+ */
+public class RecordsLogger implements Serializable
+{
+ private String filename;
+ transient private RandomAccessFile raf;
+
+ /**
+ * Constructor to create <code>RecordsLogger</code> objects that will
+ * allow to handle <code>log</code> related information.
+ * <p>
+ * The constructor creates file with the given <code>filename</code>.
+ * The access mode is defined to be in <code>rw</code> mode as when
+ * the <code>XAResourceManager</code> opens the log to write the
+ * <code>updatedBytes</code>. When it finishes, it closes the
+ * <code>log</code> and if in recovery phase, it re-opens the log
+ * in <code>r</code> mode and retrieves all the log entries. *
+ *
+ * @param filename the name of the log file to store to or retrieve from,
+ * necessary information
+ * @throws IOException if an I/O error occurs
+ */
+ public RecordsLogger(String filename) throws IOException {
+ this.filename = filename;
+ raf = new RandomAccessFile(filename, "rw");
+ }
+
+ /**
+ * Returns the name used to create this log file.
+ * @return a <code>String</code> which represents the name of
+ * the log file.
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Opens the log file in <code>r</code> mode and reads all the
+ * log entries. After that, the file is closed.
+ *
+ * @return a list with all the log entries contained in the log file
+ * @throws IOException if an I/O error occurs
+ */
+ public synchronized LinkedList<LogEntry> readAllRecords() throws IOException {
+ close();
+ raf = new RandomAccessFile(filename, "r");
+ LinkedList<LogEntry> records = new LinkedList<LogEntry>();
+ boolean eof = false;
+ do {
+ try {
+ long pos = raf.readLong();
+ int len = raf.readInt();
+ byte[] data = new byte[len];
+ raf.read(data);
+ LogEntry le = new LogEntry(pos, len, data);
+ records.add(le);
+ } catch (EOFException eofe) {
+ eof = true;
+ }
+ } while (!eof);
+ raf.close();
+ return records;
+ }
+
+ /**
+ * Appends the log file by adding a new <code>LogEntry</code> at
+ * the end. It does not actually write the <code>LogEntry</code>
+ * as an object but only the necessary information it contains.
+ *
+ * @param le the <code>LogEntry</code> to write to the log file
+ * @throws IOException if an I/O error occurs
+ */
+ public synchronized void addInfo(LogEntry le) throws IOException {
+ raf.writeLong(le.getPosition());
+ raf.writeInt(le.getRecordLength());
+ raf.write(le.getData());
+ }
+
+ /**
+ * The method gets the <code>FileDescriptor</code> of the
+ * <code>raf</code> object and calls the <code>sync</code> method
+ * to force log data to be written to disk.
+ * <p>
+ * The method is used when preparing to commit, to ensure all the
+ * updates have been written to the log.
+ *
+ * @exception IOException if an I/O error occurs
+ */
+ public void flush() throws IOException {
+ FileDescriptor fd = raf.getFD();
+ fd.sync();
+ }
+
+ /**
+ * if the <code>raf</code> object exists close it.
+ */
+ public synchronized void close() {
+ try {
+ if (raf != null)
+ raf.close();
+ } catch (IOException ioe) {
+ System.out.println("XXXX Error while processing file: " + filename + " XXXX");
+ }
+ }
+
+ /**
+ * Method to delete the log file with name <code>filename</code>.
+ * Is used by the <code>XAResourceManager</code> when it has
+ * committed or rolledback and the log is not more needed.
+ */
+ public synchronized void delete() {
+ new File(filename).delete();
+ }
+}
More information about the jboss-svn-commits
mailing list