diff --git a/bindings/java/src/org/sleuthkit/datamodel/LogicalFileTransaction.java b/bindings/java/src/org/sleuthkit/datamodel/LogicalFileTransaction.java
new file mode 100755
index 0000000000000000000000000000000000000000..5039492eb1f04f5d9835f79265b17b14399bd4fd
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/LogicalFileTransaction.java
@@ -0,0 +1,148 @@
+/*
+ * Sleuth Kit Data Model
+ * 
+ * Copyright 2013 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import static org.sleuthkit.datamodel.SleuthkitCase.*;
+
+/**
+ * implements the Transaction interface
+ *
+ *
+ *
+ *
+ */
+class LogicalFileTransaction implements Transaction {
+
+	/**
+	 * private commit state
+	 */
+	private Boolean committed = false;
+	/**
+	 * db Connection this transaction is associated with
+	 */
+	private Connection con;
+	private static final Logger logger = Logger.getLogger(LogicalFileTransaction.class.getName());
+	private Boolean closed = false;
+
+	/**
+	 * private constructor
+	 */
+	private LogicalFileTransaction() {
+	}
+
+	/**
+	 * factory creation method
+	 *
+	 * @param con the {@link  ava.sql.Connection}
+	 * @return a LogicalFileTransaction for the given connection
+	 * @throws SQLException
+	 */
+	static public LogicalFileTransaction startTransaction(Connection con) throws SQLException {
+
+		LogicalFileTransaction lft = new LogicalFileTransaction();
+		lft.con = con;
+
+		//get the write lock, released in close()
+		dbWriteLock();
+		try {
+			con.setAutoCommit(false);
+
+		} catch (SQLException ex) {
+			Logger.getLogger(LogicalFileTransaction.class.getName()).log(Level.SEVERE, "failed to set auto-commit to to false", ex);
+			throw ex;
+		}
+
+		return lft;
+	}
+
+	/**
+	 * {@inheritDoc }
+	 *
+	 * NOTE: this implementation of commit also closes the transaction whether
+	 * the commit succeeded or failed
+	 */
+	@Override
+	public void commit() {
+		if (!committed && !closed) {
+			try {
+				con.commit();
+			} catch (SQLException ex) {
+				rollback();
+			} finally {
+				close();
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc }
+	 */
+	@Override
+	public Boolean isCommitted() {
+		return committed;
+
+	}
+
+	/**
+	 * {@inheritDoc }
+	 */
+	@Override
+	public void rollback() {
+		if (!committed && !closed) {
+			try {
+				con.rollback();
+			} catch (SQLException ex1) {
+				Logger.getLogger(LogicalFileTransaction.class.getName()).log(Level.SEVERE, "Exception while attempting to rollback!!", ex1);
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc }
+	 *
+	 */
+	@Override
+	public void close() {
+		if (!closed) {
+			try {
+				con.setAutoCommit(true);
+			} catch (SQLException ex) {
+				logger.log(Level.SEVERE, "Error setting auto-commit to true.", ex);
+			} finally {
+				con = null;
+				committed = true;
+				closed = true;
+				dbWriteUnlock();
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc }
+	 *
+	 */
+	@Override
+	public Boolean isClosed() {
+		return closed;
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 70bba59aee39ae730b3fcf21edfe1751850de200..98f0f7f18028f47ddf26262261f2e2b52f040d0f 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -66,10 +66,8 @@ public class SleuthkitCase {
 	private ResultSetHelper rsHelper = new ResultSetHelper(this);
 	private int artifactIDcounter = 1001;
 	private int attributeIDcounter = 1001;
-	
 	// for use by getCarvedDirectoryId method only
 	private final Map<Long, Long> systemIdMap = new HashMap<Long, Long>();
-
 	//database lock
 	private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy
 	private static final Lock caseDbLock = rwLock.writeLock(); //using exclusing lock for all db ops for now
@@ -111,9 +109,8 @@ public class SleuthkitCase {
 	private PreparedStatement getLastContentIdSt;
 	private PreparedStatement getFsIdForFileIdSt;
 	private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName());
-
 	private ArrayList<ErrorObserver> errorObservers = new ArrayList<ErrorObserver>();
-	
+
 	/**
 	 * constructor (private) - client uses openCase() and newCase() instead
 	 *
@@ -134,7 +131,29 @@ private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle) throw
 		configureDB();
 		initBlackboardTypes();
 		initStatements();
-		
+
+	}
+
+	/**
+	 * create a new transaction: lock the database and set auto-commit false.
+	 * this transaction should be passed to methods who take a transaction and
+	 * then have transaction.commit() invoked on it to commit changes and unlock
+	 * the database
+	 *
+	 * @return
+	 * @throws TskCoreException
+	 */
+	public LogicalFileTransaction createTransaction() throws TskCoreException {
+		if (con != null) {
+			try {
+				return LogicalFileTransaction.startTransaction(con);
+			} catch (SQLException ex) {
+				Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE, "failed to create transaction", ex);
+				throw new TskCoreException("Failed to create transaction", ex);
+			}
+		} else {
+			throw new TskCoreException("could not create transaction with null db connection");
+		}
 	}
 
 	/**
@@ -227,9 +246,9 @@ private void initStatements() throws SQLException {
 		updateMd5St = con.prepareStatement("UPDATE tsk_files SET md5 = ? WHERE obj_id = ?");
 
 		getPathSt = con.prepareStatement("SELECT path FROM tsk_files_path WHERE obj_id = ?");
-		
+
 		getFileParentPathSt = con.prepareStatement("SELECT parent_path FROM tsk_files WHERE obj_id = ?");
-		
+
 		getFileNameSt = con.prepareStatement("SELECT name FROM tsk_files WHERE obj_id = ?");
 
 		getDerivedInfoSt = con.prepareStatement("SELECT derived_id, rederive FROM tsk_files_derived WHERE obj_id = ?");
@@ -245,7 +264,7 @@ private void initStatements() throws SQLException {
 		addFileSt = con.prepareStatement(
 				"INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path) "
 				+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
-		
+
 		addLayoutFileSt = con.prepareStatement(
 				"INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) "
 				+ "VALUES (?, ?, ?, ?)");
@@ -255,7 +274,7 @@ private void initStatements() throws SQLException {
 
 		hasChildrenSt = con.prepareStatement(
 				"SELECT COUNT(obj_id) FROM tsk_objects WHERE par_obj_id = ?");
-		
+
 		getFsIdForFileIdSt = con.prepareStatement(
 				"SELECT fs_obj_id from tsk_files WHERE obj_id=?");
 
@@ -355,7 +374,7 @@ private void closeStatements() {
 				getFileWithParentSt.close();
 				getFileWithParentSt = null;
 			}
-			
+
 			if (getFileNameSt != null) {
 				getFileNameSt.close();
 				getFileNameSt = null;
@@ -365,7 +384,7 @@ private void closeStatements() {
 				updateMd5St.close();
 				updateMd5St = null;
 			}
-			
+
 			if (getLastContentIdSt != null) {
 				getLastContentIdSt.close();
 				getLastContentIdSt = null;
@@ -375,7 +394,7 @@ private void closeStatements() {
 				getPathSt.close();
 				getPathSt = null;
 			}
-			
+
 			if (getFileParentPathSt != null) {
 				getFileParentPathSt.close();
 				getFileParentPathSt = null;
@@ -401,7 +420,7 @@ private void closeStatements() {
 				addFileSt.close();
 				addFileSt = null;
 			}
-			
+
 			if (addLayoutFileSt != null) {
 				addLayoutFileSt.close();
 				addLayoutFileSt = null;
@@ -416,12 +435,12 @@ private void closeStatements() {
 				hasChildrenSt.close();
 				hasChildrenSt = null;
 			}
-			
+
 			if (getFsIdForFileIdSt != null) {
 				getFsIdForFileIdSt.close();
 				getFsIdForFileIdSt = null;
 			}
-			
+
 
 		} catch (SQLException e) {
 			logger.log(Level.WARNING,
@@ -600,7 +619,8 @@ public void clearLookupDatabases() throws TskCoreException {
 	}
 
 	/**
-	 * Get the list of root objects, meaning image files or local files virtual dir container.
+	 * Get the list of root objects, meaning image files or local files virtual
+	 * dir container.
 	 *
 	 * @return list of content objects.
 	 * @throws TskCoreException exception thrown if a critical error occurs
@@ -627,18 +647,15 @@ public List<Content> getRootObjects() throws TskCoreException {
 			for (ObjectInfo i : infos) {
 				if (i.type == ObjectType.IMG) {
 					rootObjs.add(getImageById(i.id));
-				}
-				else if (i.type == ObjectType.ABSTRACTFILE) {
+				} else if (i.type == ObjectType.ABSTRACTFILE) {
 					//check if virtual dir for local files
 					AbstractFile af = getAbstractFileById(i.id);
 					if (af instanceof VirtualDirectory) {
 						rootObjs.add(af);
-					}
-					else {
+					} else {
 						throw new TskCoreException("Parentless object has wrong type to be a root (ABSTRACTFILE, but not VIRTUAL_DIRECTORY: " + i.type);
 					}
-				} 
-				else {
+				} else {
 					throw new TskCoreException("Parentless object has wrong type to be a root: " + i.type);
 				}
 			}
@@ -1023,19 +1040,20 @@ public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypes()
 		}
 
 	}
-	
+
 	/**
-	 * Get all of the blackboard artifact types that are in use in the blackboard. 
-	 * 
+	 * Get all of the blackboard artifact types that are in use in the
+	 * blackboard.
+	 *
 	 * @return List of blackboard artifact types
-	 * @throws TskCoreException 
+	 * @throws TskCoreException
 	 */
 	public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypesInUse() throws TskCoreException {
 		// @@@ TODO: This should be rewritten as a single query. 
-		
+
 		ArrayList<BlackboardArtifact.ARTIFACT_TYPE> allArts = getBlackboardArtifactTypes();
 		ArrayList<BlackboardArtifact.ARTIFACT_TYPE> usedArts = new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>();
-		
+
 		for (BlackboardArtifact.ARTIFACT_TYPE art : allArts) {
 			if (getBlackboardArtifactsTypeCount(art.getTypeID()) > 0) {
 				usedArts.add(art);
@@ -2070,11 +2088,11 @@ boolean getContentHasChildren(Content content) throws TskCoreException {
 		return hasChildren;
 
 	}
-	
+
 	/**
-	 * Counts if the content object  children. Note: this is generally more
-	 * efficient then preloading all children and counting,
-	 * and facilities lazy loading.
+	 * Counts if the content object children. Note: this is generally more
+	 * efficient then preloading all children and counting, and facilities lazy
+	 * loading.
 	 *
 	 * @param content content object to check for children count
 	 * @return children count
@@ -2141,11 +2159,11 @@ List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM typ
 						result = rsHelper.file(rs, null);
 					}
 					children.add(result);
-				} else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR) {	
+				} else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR) {
 					VirtualDirectory virtDir = rsHelper.virtualDirectory(rs);
 					children.add(virtDir);
-				} else if (type == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
-						type == TSK_DB_FILES_TYPE_ENUM.CARVED) {
+				} else if (type == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
+						|| type == TSK_DB_FILES_TYPE_ENUM.CARVED) {
 					String parentPath = rs.getString("parent_path");
 					if (parentPath == null) {
 						parentPath = "";
@@ -2153,9 +2171,9 @@ List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM typ
 					final LayoutFile lf =
 							new LayoutFile(this, rs.getLong("obj_id"), rs.getString("name"),
 							type,
-							TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), 
+							TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")),
 							TSK_FS_META_TYPE_ENUM.ValueOf(rs.getShort("meta_type")),
-							TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), 
+							TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")),
 							rs.getShort("meta_flags"),
 							rs.getLong("size"),
 							rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), parentPath);
@@ -2408,8 +2426,7 @@ public Content getContentById(long id) throws TskCoreException {
 	}
 
 	/**
-	 * Get a path of a file in tsk_files_path table or null if there
-	 * is none
+	 * Get a path of a file in tsk_files_path table or null if there is none
 	 *
 	 * @param id id of the file to get path for
 	 * @return file path or null
@@ -2440,10 +2457,9 @@ String getFilePath(long id) {
 
 		return filePath;
 	}
-	
+
 	/**
-	 * Get a parent_path of a file in tsk_files table or null if there
-	 * is none
+	 * Get a parent_path of a file in tsk_files table or null if there is none
 	 *
 	 * @param id id of the file to get path for
 	 * @return file path or null
@@ -2474,10 +2490,9 @@ String getFileParentPath(long id) {
 
 		return parentPath;
 	}
-	
+
 	/**
-	 * Get a name of a file in tsk_files table or null if there
-	 * is none
+	 * Get a name of a file in tsk_files table or null if there is none
 	 *
 	 * @param id id of the file to get name for
 	 * @return file name or null
@@ -2597,14 +2612,12 @@ public AbstractFile getAbstractFileById(long id) throws TskCoreException {
 		}
 
 	}
-	
-	
-	
-	
+
 	/**
-	 * Get file system id value for file or -1 if there isn't one
-	 * Note: for FsContent files, this is the real fs
-	 * for other non-fs AbstractFile files, this field is used internally for data source id (the root content obj)
+	 * Get file system id value for file or -1 if there isn't one Note: for
+	 * FsContent files, this is the real fs for other non-fs AbstractFile files,
+	 * this field is used internally for data source id (the root content obj)
+	 *
 	 * @param fileId file id to get fs column id for
 	 * @return fs_id or -1 if not present
 	 */
@@ -2624,11 +2637,9 @@ private long getFileSystemByFileId(long fileId) {
 					ret = -1;
 				}
 			}
-		}
-		catch (SQLException e) {
+		} catch (SQLException e) {
 			logger.log(Level.SEVERE, "Error checking file system id of a file", e);
-		}
-		finally {
+		} finally {
 			if (rs != null) {
 				try {
 					rs.close();
@@ -2640,33 +2651,36 @@ private long getFileSystemByFileId(long fileId) {
 		}
 		return ret;
 	}
-	
+
 	/**
-	 * Gets the root-level data source object id
-	 * (such as Image or VirtualDirectory representing filesets) for the file
+	 * Gets the root-level data source object id (such as Image or
+	 * VirtualDirectory representing filesets) for the file
+	 *
 	 * @param file file to get the root-level object id for
-	 * @return the root content object id in the hierarchy, or -1 if not found (such as when invalid file object passed in)
-	 * @throws TskCoreException thrown if check failed due to a critical tsk error
+	 * @return the root content object id in the hierarchy, or -1 if not found
+	 * (such as when invalid file object passed in)
+	 * @throws TskCoreException thrown if check failed due to a critical tsk
+	 * error
 	 */
 	public long getFileDataSource(AbstractFile file) throws TskCoreException {
-		
+
 		final Image image = file.getImage();
 		if (image != null) {
 			//case for image data source
 			return image.getId();
-		}
-		else {
+		} else {
 			//otherwise, get the root non-image data source id
 			//note, we are currently using fs_id internally to store data source id for such files
-		
+
 			return getFileSystemByFileId(file.getId());
 		}
 
 	}
 
 	/**
-	 * Checks if the file is a (sub)child of the data source (parentless Content object
-	 * such as Image or VirtualDirectory representing filesets)
+	 * Checks if the file is a (sub)child of the data source (parentless Content
+	 * object such as Image or VirtualDirectory representing filesets)
+	 *
 	 * @param dataSource dataSource to check
 	 * @param fileId id of file to check
 	 * @return true if the file is in the dataSource hierarchy
@@ -2678,62 +2692,60 @@ public boolean isFileFromSource(Content dataSource, long fileId) throws TskCoreE
 			logger.log(Level.SEVERE, msg);
 			throw new IllegalArgumentException(msg);
 		}
-		
+
 		//get fs_id for file id
 		long fsId = getFileSystemByFileId(fileId);
 		if (fsId == -1) {
 			return false;
 		}
-		
+
 		//if image, check if one of fs in data source
-		
+
 		if (dataSource instanceof Image) {
-			Collection<FileSystem> fss = getFileSystems((Image)dataSource);
+			Collection<FileSystem> fss = getFileSystems((Image) dataSource);
 			for (FileSystem fs : fss) {
 				if (fs.getId() == fsId) {
 					return true;
 				}
 			}
 			return false;
-			
-		}
-		//if VirtualDirectory, check if dataSource id is the fs_id
+
+		} //if VirtualDirectory, check if dataSource id is the fs_id
 		else if (dataSource instanceof VirtualDirectory) {
 			//fs_obj_id is not a real fs in this case
 			//we are currently using this field internally to get to data source of non-fs files quicker
 			//this will be fixed in 2.5 schema
 			return dataSource.getId() == fsId;
-			
-		}
-		else {
+
+		} else {
 			final String msg = "Error, data source should be Image or VirtualDirectory, got: " + dataSource;
 			logger.log(Level.SEVERE, msg);
 			throw new IllegalArgumentException(msg);
 		}
-		
+
 	}
-	
-	
+
 	/**
-	 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to search for the given file name
-	 * @param fileName Pattern of the name of the file or directory to match (case
-	 * insensitive, used in LIKE SQL statement). 
-	 * @return a list of AbstractFile for files/directories whose name matches the
-	 * given fileName
+	 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to
+	 * search for the given file name
+	 * @param fileName Pattern of the name of the file or directory to match
+	 * (case insensitive, used in LIKE SQL statement).
+	 * @return a list of AbstractFile for files/directories whose name matches
+	 * the given fileName
 	 * @throws TskCoreException thrown if check failed
 	 */
 	public List<AbstractFile> findFiles(Content dataSource, String fileName) throws TskCoreException {
-		
+
 		if (dataSource.getParent() != null) {
 			final String msg = "Error, data source should be parent-less (images, file-sets), got: " + dataSource;
 			logger.log(Level.SEVERE, msg);
 			throw new IllegalArgumentException(msg);
 		}
-		
+
 		// set the file name in the prepared statement
 		List<AbstractFile> files = new ArrayList<AbstractFile>();
 		ResultSet rs = null;
-		
+
 		dbReadLock();
 		try {
 			if (dataSource instanceof Image) {
@@ -2779,24 +2791,25 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName) throws
 	}
 
 	/**
-	 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to search for the given file name
-	 * @param fileName Pattern of the name of the file or directory to match (case
-	 * insensitive, used in LIKE SQL statement). 
-	 * @param dirName Pattern of the name of a parent directory of fileName (case
-	 * insensitive, used in LIKE SQL statement)
+	 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to
+	 * search for the given file name
+	 * @param fileName Pattern of the name of the file or directory to match
+	 * (case insensitive, used in LIKE SQL statement).
+	 * @param dirName Pattern of the name of a parent directory of fileName
+	 * (case insensitive, used in LIKE SQL statement)
 	 * @return a list of AbstractFile for files/directories whose name matches
 	 * fileName and whose parent directory contains dirName.
 	 */
 	public List<AbstractFile> findFiles(Content dataSource, String fileName, String dirName) throws TskCoreException {
-			if (dataSource.getParent() != null) {
+		if (dataSource.getParent() != null) {
 			final String msg = "Error, data source should be parent-less (images, file-sets), got: " + dataSource;
 			logger.log(Level.SEVERE, msg);
 			throw new IllegalArgumentException(msg);
 		}
-		
+
 		ResultSet rs = null;
 		List<AbstractFile> files = new ArrayList<AbstractFile>();
-		
+
 		dbReadLock();
 		try {
 			if (dataSource instanceof Image) {
@@ -2852,7 +2865,6 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName, String
 		return files;
 	}
 
-	
 	/**
 	 * Add a path (such as a local path) for a content object to tsk_file_paths
 	 *
@@ -2866,50 +2878,73 @@ private void addFilePath(long objId, String path) throws SQLException {
 			addPathSt.setLong(1, objId);
 			addPathSt.setString(2, path);
 			addPathSt.executeUpdate();
-		}
-		finally {
+		} finally {
 			addPathSt.clearParameters();
 		}
 	}
-	
+
+	/**
+	 * wraps the version of addVirtualDirectory that takes a Transaction in a
+	 * transaction local to this method
+	 *
+	 * @param parentId
+	 * @param directoryName
+	 * @return
+	 * @throws TskCoreException
+	 */
+	public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) throws TskCoreException {
+
+		LogicalFileTransaction localTrans = createTransaction();
+		VirtualDirectory newVD = addVirtualDirectory(parentId, directoryName, localTrans);
+		localTrans.commit();
+		return newVD;
+
+	}
+
 	/**
 	 * Adds a virtual directory to the database and returns a VirtualDirectory
 	 * object representing it.
+	 *
+	 * todo: at the moment we trust the transaction and don't do anything to
+	 * check it is valid or in the correct state. we should.
+	 *
 	 * @param parentId the ID of the parent, or 0 if NULL
 	 * @param directoryName the name of the virtual directory to create
-	 * @return a VirtualDirectory object representing the one added to the database.
-	 * @throws TskCoreException 
+	 * @param trans the transaction that will take care of locking and unlocking
+	 * the database
+	 * @return a VirtualDirectory object representing the one added to the
+	 * database.
+	 * @throws TskCoreException
 	 */
-	public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) throws TskCoreException {	
+	public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, Transaction trans) throws TskCoreException {
 		// get the parent path
 		String parentPath = getFileParentPath(parentId);
 		if (parentPath == null) {
 			parentPath = "";
-		} 
+		}
 		String parentName = getFileName(parentId);
 		if (parentName != null) {
 			parentPath = parentPath + "/" + parentName;
 		}
-		
+
 		//propagate fs id if parent is a file and fs id is set
 		long parentFs = this.getFileSystemByFileId(parentId);
 		if (parentFs == -1) {
 			//use the parentId fs obj id as data source id  internally
 			parentFs = parentId;
 		}
-		
-		dbWriteLock();
-		
+
+
 		VirtualDirectory vd = null;
 
-		//all in one write lock and transaction
+		//don't need to lock database or setAutoCommit(false), since we are
+		//passed Transaction which handles that.
+
 		//get last object id
 		//create tsk_objects object with new id
 		//create tsk_files object with the new id
 		try {
 
-			con.setAutoCommit(false);
-
 			long newObjId = getLastObjectId() + 1;
 			if (newObjId < 1) {
 				throw new TskCoreException("Error creating a virtual directory, cannot get new id of the object.");
@@ -2930,11 +2965,10 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName)
 			//obj_id, fs_obj_id, name
 			addFileSt.clearParameters(); //clear from previous, so we can skip nulls
 			addFileSt.setLong(1, newObjId);
-			
+
 			if (parentFs < 1) {
 				addFileSt.setNull(2, java.sql.Types.BIGINT);
-			}
-			else {
+			} else {
 				addFileSt.setLong(2, parentFs);
 			}
 			addFileSt.setString(3, directoryName);
@@ -2964,7 +2998,7 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName)
 			addFileSt.setString(15, parentPath);
 
 			addFileSt.executeUpdate();
-			
+
 			vd = new VirtualDirectory(this, newObjId, directoryName, dirType,
 					metaType, dirFlag, metaFlags, size, null, FileKnown.UKNOWN,
 					parentPath);
@@ -2977,35 +3011,21 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName)
 			} catch (SQLException ex) {
 				logger.log(Level.SEVERE, "Error clearing parameters after adding virtual directory.", ex);
 			}
-			
-			try {
-				con.commit();
-			} catch (SQLException ex) {
-				logger.log(Level.SEVERE, "Error committing after adding virtual directory.", ex);
-			} finally {
-				try {
-					con.setAutoCommit(true);
-				} catch (SQLException ex) {
-					logger.log(Level.SEVERE, "Error setting auto-commit after adding virtual directory.", ex);
-				} finally {
-					dbWriteUnlock();
-				}
-			}
+
 		}
 
 		return vd;
 	}
-	
-	
+
 	/**
-	 * Get IDs of the virtual folder roots (at the same level as image), used for containers
-	 * such as for local files. 
-	 * 
+	 * Get IDs of the virtual folder roots (at the same level as image), used
+	 * for containers such as for local files.
+	 *
 	 * @return IDs of virtual directory root objects.
 	 */
 	public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException {
 		final List<VirtualDirectory> virtDirRootIds = new ArrayList<VirtualDirectory>();
-		
+
 		//use lock to ensure atomic cache check and db/cache update
 		dbReadLock();
 
@@ -3013,41 +3033,36 @@ public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException
 		ResultSet rs = null;
 		try {
 			statement = con.createStatement();
-			rs = statement.executeQuery("SELECT tsk_files.* FROM tsk_objects, tsk_files WHERE " 
-					+ "tsk_objects.par_obj_id IS NULL AND " 
-					+ "tsk_objects.type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + " AND " 
+			rs = statement.executeQuery("SELECT tsk_files.* FROM tsk_objects, tsk_files WHERE "
+					+ "tsk_objects.par_obj_id IS NULL AND "
+					+ "tsk_objects.type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + " AND "
 					+ "tsk_objects.obj_id = tsk_files.obj_id AND "
 					+ "tsk_files.type = " + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType());
-					
+
 			while (rs.next()) {
 				virtDirRootIds.add(rsHelper.virtualDirectory(rs));
 			}
-		}
-		catch (SQLException ex) {
+		} catch (SQLException ex) {
 			logger.log(Level.SEVERE, "Error getting local files virtual folder id, ", ex);
 			throw new TskCoreException("Error getting local files virtual folder id, ", ex);
-		}
-		
-		finally {
-			try{
+		} finally {
+			try {
 				if (rs != null) {
 					rs.close();
 				}
 				if (statement != null) {
 					statement.close();
 				}
-			}
-			catch (SQLException e) {
+			} catch (SQLException e) {
 				logger.log(Level.WARNING, "Error closing statements after getting local files virt folder id", e);
-			}
-			finally {
+			} finally {
 				dbReadUnlock();
 			}
 		}
 
 		return virtDirRootIds;
 	}
-	
+
 	/**
 	 * @param id an image, volume or file system ID
 	 * @return the ID of the '$CarvedFiles' directory for the given systemId
@@ -3114,34 +3129,35 @@ private long getCarvedDirectoryId(long id) throws TskCoreException {
 
 		return ret;
 	}
-	
+
 	/**
 	 * Adds a carved file to the VirtualDirectory '$CarvedFiles' in the volume
 	 * or file system given by systemId.
-	 * @param carvedFileName the name of the carved file to add 
+	 *
+	 * @param carvedFileName the name of the carved file to add
 	 * @param carvedFileSize the size of the carved file to add
 	 * @param systemId the ID of the parent volume or file system
-	 * @param data the layout information - a list of offsets that make up
-	 * this carved file.
+	 * @param data the layout information - a list of offsets that make up this
+	 * carved file.
 	 */
 	public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize,
 			long systemId, List<TskFileRange> data) throws TskCoreException {
-		
+
 		// get the ID of the appropriate '$CarvedFiles' directory
 		long carvedFilesId = getCarvedDirectoryId(systemId);
-		
+
 		// get the parent path for the $CarvedFiles directory		
 		String parentPath = getFileParentPath(carvedFilesId);
 		if (parentPath == null) {
 			parentPath = "";
-		} 
+		}
 		String parentName = getFileName(carvedFilesId);
 		if (parentName != null) {
 			parentPath = parentPath + "/" + parentName;
 		}
-		
+
 		dbWriteLock();
-		
+
 		LayoutFile lf = null;
 
 		//all in one write lock and transaction
@@ -3175,7 +3191,7 @@ public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize,
 			// type
 			final TSK_DB_FILES_TYPE_ENUM type = TSK_DB_FILES_TYPE_ENUM.CARVED;
 			addFileSt.setShort(4, type.getFileType());
-			
+
 			// has_path
 			addFileSt.setBoolean(5, true);
 
@@ -3202,24 +3218,24 @@ public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize,
 			addFileSt.setString(15, parentPath);
 
 			addFileSt.executeUpdate();
-			
+
 			// tsk_file_layout
-			
+
 			// add an entry in the tsk_layout_file table for each TskFileRange
 			for (TskFileRange tskFileRange : data) {
-				
+
 				// set the object ID
 				addLayoutFileSt.setLong(1, newObjId);
-				
+
 				// set byte_start
 				addLayoutFileSt.setLong(2, tskFileRange.getByteStart());
-				
+
 				// set byte_len
 				addLayoutFileSt.setLong(3, tskFileRange.getByteLen());
-				
+
 				// set the sequence number
 				addLayoutFileSt.setLong(4, tskFileRange.getSequence());
-				
+
 				// execute it
 				addLayoutFileSt.executeUpdate();
 			}
@@ -3238,7 +3254,7 @@ public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize,
 			} catch (SQLException ex) {
 				logger.log(Level.SEVERE, "Error clearing parameters after adding derived file", ex);
 			}
-			
+
 			try {
 				con.commit();
 			} catch (SQLException ex) {
@@ -3260,7 +3276,7 @@ public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize,
 	/**
 	 * Creates a new derived file object, adds it to database and returns it.
 	 *
-	 * TODO add support for adding derived method 
+	 * TODO add support for adding derived method
 	 *
 	 * @param fileName file name the derived file
 	 * @param localPath local path of the derived file, including the file name.
@@ -3282,20 +3298,20 @@ public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize,
 	 * due to a critical system error
 	 */
 	public DerivedFile addDerivedFile(String fileName, String localPath,
-			long size, long ctime, long crtime, long atime, long mtime, 
+			long size, long ctime, long crtime, long atime, long mtime,
 			boolean isFile, AbstractFile parentFile,
 			String rederiveDetails, String toolName, String toolVersion, String otherDetails) throws TskCoreException {
-		
+
 		final long parentId = parentFile.getId();
 		final String parentPath = parentFile.getParentPath() + parentFile.getName() + '/';
-		
+
 		//get fs_obj_id of the parentFile and propagate it to the new derived file
 		//note, fs_obj_id is fs id for FsContent, but for others it is used internally as data source id, and it is not 
 		//part of AbstractFile API, until the next schema change
 		long fsObjId = this.getFileSystemByFileId(parentId);
 
 		DerivedFile ret = null;
-		
+
 		long newObjId = -1;
 
 		dbWriteLock();
@@ -3361,7 +3377,7 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 			addFileSt.setString(15, parentPath);
 
 			addFileSt.executeUpdate();
-			
+
 			//add localPath 
 			addFilePath(newObjId, localPath);
 
@@ -3380,7 +3396,7 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 			} catch (SQLException ex) {
 				logger.log(Level.SEVERE, "Error clearing parameters after adding derived file", ex);
 			}
-			
+
 			try {
 				con.commit();
 			} catch (SQLException ex) {
@@ -3399,61 +3415,97 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 
 		return ret;
 	}
-	
+
+	/**
+	 *
+	 * wraps the version of addLocalFile that takes a Transaction in a
+	 * transaction local to this method.
+	 *
+	 * @param fileName
+	 * @param localPath
+	 * @param size
+	 * @param ctime
+	 * @param crtime
+	 * @param atime
+	 * @param mtime
+	 * @param isFile
+	 * @param parent
+	 * @return
+	 * @throws TskCoreException
+	 */
+	public LocalFile addLocalFile(String fileName, String localPath,
+			long size, long ctime, long crtime, long atime, long mtime,
+			boolean isFile, AbstractFile parent) throws TskCoreException {
+		LogicalFileTransaction localTrans = createTransaction();
+		LocalFile created = addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime, isFile, parent, localTrans);
+		localTrans.commit();
+		return created;
+
+	}
+
 	/**
 	 * Creates a new local file object, adds it to database and returns it.
 	 *
+	 *
+	 * todo: at the moment we trust the transaction and don't do anything to
+	 * check it is valid or in the correct state. we should.
+	 *
+	 *
 	 * @param fileName file name the derived file
-	 * @param localPath local absolute path of the local file, including the file name.
+	 * @param localPath local absolute path of the local file, including the
+	 * file name.
 	 * @param size size of the derived file in bytes
 	 * @param ctime
 	 * @param crtime
 	 * @param atime
 	 * @param mtime
 	 * @param isFile whether a file or directory, true if a file
-	 * @param parent parent file object (such as virtual directory, another local file, or FsContent type of file)
+	 * @param parent parent file object (such as virtual directory, another
+	 * local file, or FsContent type of file)
+	 * @param trans the transaction that will take care of locking and unlocking
+	 * the database
 	 * @return newly created derived file object
 	 * @throws TskCoreException exception thrown if the object creation failed
 	 * due to a critical system error
 	 */
 	public LocalFile addLocalFile(String fileName, String localPath,
-			long size, long ctime, long crtime, long atime, long mtime, 
-			boolean isFile, AbstractFile parent) throws TskCoreException {
-		
+			long size, long ctime, long crtime, long atime, long mtime,
+			boolean isFile, AbstractFile parent, Transaction trans) throws TskCoreException {
+
 		long parentId = -1;
 		String parentPath;
 		if (parent == null) {
 			throw new TskCoreException("Error adding local file: " + fileName + ", parent to add to is null");
-		}
-		else {
+		} else {
 			parentId = parent.getId();
 			parentPath = parent.getParentPath() + "/" + parent.getName();
 		}
-		
+
 		//check parent is a data source (the root obj) and set fs_obj_id that we currently use to track or data sources accordingly
 		long dataSourceId = -1;
 		boolean isParentDataSource = parent.getParent() == null;
 		if (isParentDataSource) {
 			dataSourceId = parentId;
-		}
-		else {
+		} else {
 			//else propagate from parent fs_obj_id
 			dataSourceId = getFileSystemByFileId(parentId);
 		}
 
 		LocalFile ret = null;
-		
+
 		long newObjId = -1;
 
-		dbWriteLock();
 
-		//all in one write lock and transaction
+
+		//don't need to lock database or setAutoCommit(false), since we are
+		//passed Transaction which handles that.
+
+
 		//get last object id
 		//create tsk_objects object with new id
 		//create tsk_files object with the new id
 		try {
 
-			con.setAutoCommit(false);
 
 			newObjId = getLastObjectId() + 1;
 			if (newObjId < 1) {
@@ -3508,7 +3560,7 @@ public LocalFile addLocalFile(String fileName, String localPath,
 			addFileSt.setString(15, parentPath);
 
 			addFileSt.executeUpdate();
-			
+
 			//add localPath 
 			addFilePath(newObjId, localPath);
 
@@ -3525,30 +3577,19 @@ public LocalFile addLocalFile(String fileName, String localPath,
 			} catch (SQLException ex) {
 				logger.log(Level.SEVERE, "Error clearing parameters after adding derived file", ex);
 			}
-			
-			try {
-				con.commit();
-			} catch (SQLException ex) {
-				logger.log(Level.SEVERE, "Error committing after adding derived file", ex);
-			} finally {
-				try {
-					con.setAutoCommit(true);
-				} catch (SQLException ex) {
-					logger.log(Level.SEVERE, "Error setting auto-commit after adding derived file", ex);
-				} finally {
-					dbWriteUnlock();
-				}
-			}
+
+
 		}
 		return ret;
 	}
 
-
 	/**
 	 * Find all files in the data source, by name and parent
-	 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to search for the given file name
-	 * @param fileName Pattern of the name of the file or directory to match (case
-	 * insensitive, used in LIKE SQL statement). 
+	 *
+	 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to
+	 * search for the given file name
+	 * @param fileName Pattern of the name of the file or directory to match
+	 * (case insensitive, used in LIKE SQL statement).
 	 * @param parentFile Object for parent file/directory to find children in
 	 * @return a list of AbstractFile for files/directories whose name matches
 	 * fileName and that were inside a directory described by parentFile.
@@ -3557,10 +3598,9 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName, Abstrac
 		return findFiles(dataSource, fileName, parentFile.getName());
 	}
 
-	
 	/**
 	 * Count files matching the specific Where clause
-	 * 
+	 *
 	 * @param sqlWhereClause a SQL where clause appropriate for the desired
 	 * files (do not begin the WHERE clause with the word WHERE!)
 	 * @return count of files each of which satisfy the given WHERE clause
@@ -3594,14 +3634,15 @@ public long countFilesWhere(String sqlWhereClause) throws TskCoreException {
 			dbReadUnlock();
 		}
 	}
-	
-	
+
 	/**
-	 * Find and return list of all (abstract) files matching the specific Where clause
-	 * 
+	 * Find and return list of all (abstract) files matching the specific Where
+	 * clause
+	 *
 	 * @param sqlWhereClause a SQL where clause appropriate for the desired
 	 * files (do not begin the WHERE clause with the word WHERE!)
-	 * @return a list of AbstractFile each of which satisfy the given WHERE clause
+	 * @return a list of AbstractFile each of which satisfy the given WHERE
+	 * clause
 	 * @throws TskCoreException
 	 */
 	public List<AbstractFile> findAllFilesWhere(String sqlWhereClause) throws TskCoreException {
@@ -3634,8 +3675,9 @@ public List<AbstractFile> findAllFilesWhere(String sqlWhereClause) throws TskCor
 	}
 
 	/**
-	 * Find and return list of all (abstract) ids of files matching the specific Where clause
-	 * 
+	 * Find and return list of all (abstract) ids of files matching the specific
+	 * Where clause
+	 *
 	 * @param sqlWhereClause a SQL where clause appropriate for the desired
 	 * files (do not begin the WHERE clause with the word WHERE!)
 	 * @return a list of file ids each of which satisfy the given WHERE clause
@@ -3649,7 +3691,7 @@ public List<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreExcep
 		try {
 			statement = con.createStatement();
 			rs = statement.executeQuery("SELECT obj_id FROM tsk_files WHERE " + sqlWhereClause);
-			while(rs.next()) {
+			while (rs.next()) {
 				ret.add(rs.getLong(1));
 			}
 		} catch (SQLException e) {
@@ -3674,10 +3716,9 @@ public List<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreExcep
 		return ret;
 	}
 
-
 	/**
 	 * Find and return list of files matching the specific Where clause
-	 * 
+	 *
 	 * @param sqlWhereClause a SQL where clause appropriate for the desired
 	 * files (do not begin the WHERE clause with the word WHERE!)
 	 * @return a list of FsContent each of which satisfy the given WHERE clause
@@ -3711,12 +3752,12 @@ public List<FsContent> findFilesWhere(String sqlWhereClause) throws TskCoreExcep
 			dbReadUnlock();
 		}
 	}
-	
+
 	/**
-	 * Find and return list of file IDs matching the specific Where clause.
-	 * Use this like findFilesWhere() and where file objects are not required upfront
-	 * and so heap usage can be reduced.
-	 * 
+	 * Find and return list of file IDs matching the specific Where clause. Use
+	 * this like findFilesWhere() and where file objects are not required
+	 * upfront and so heap usage can be reduced.
+	 *
 	 * @param sqlWhereClause a SQL where clause appropriate for the desired
 	 * files (do not begin the WHERE clause with the word WHERE!)
 	 * @return a list of file ids each of which satisfy the given WHERE clause
@@ -3733,7 +3774,7 @@ public List<Long> findFileIdsWhere(String sqlWhereClause) throws TskCoreExceptio
 			while (rs.next()) {
 				ret.add(rs.getLong(1));
 			}
-			
+
 		} catch (SQLException e) {
 			throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.findFileIdsWhere() " + sqlWhereClause, e);
 		} finally {
@@ -3757,7 +3798,8 @@ public List<Long> findFileIdsWhere(String sqlWhereClause) throws TskCoreExceptio
 	}
 
 	/**
-	 * @param dataSource the data source (Image, VirtualDirectory for file-sets, etc) to search for the given file name
+	 * @param dataSource the data source (Image, VirtualDirectory for file-sets,
+	 * etc) to search for the given file name
 	 * @param filePath The full path to the file(s) of interest. This can
 	 * optionally include the image and volume names. Treated in a case-
 	 * insensitive manner.
@@ -4071,7 +4113,6 @@ Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException
 			dbReadUnlock();
 		}
 	}
-	
 
 	/**
 	 * Helper to return FileSystems in an Image
@@ -4385,8 +4426,8 @@ List<Long> getDirectoryChildrenIds(Directory dir) throws TskCoreException {
 	 * Returns a list of all direct children for a given virtual directory
 	 *
 	 * @param vDir virtual directory to get the list of direct children for
-	 * @return list of direct children (layout/local files or directories) for
-	 * a given virtual directory
+	 * @return list of direct children (layout/local files or directories) for a
+	 * given virtual directory
 	 * @throws TskCoreException thrown if a critical error occurred within tsk
 	 * core
 	 */
@@ -4484,18 +4525,17 @@ public List<Image> getImages() throws TskCoreException {
 
 		return images;
 	}
-	
-	
+
 	/**
 	 * Get last (max) object id of content object in tsk_objects.
-	 * 
-	 * Note, if you
-	 * are using this id to create a new object, make sure you are getting and
-	 * using it in the same write lock/transaction to avoid potential
-	 * concurrency issues with other writes
+	 *
+	 * Note, if you are using this id to create a new object, make sure you are
+	 * getting and using it in the same write lock/transaction to avoid
+	 * potential concurrency issues with other writes
 	 *
 	 * @return currently max id
-	 * @throws TskCoreException exception thrown when database error occurs and last object id could not be queried
+	 * @throws TskCoreException exception thrown when database error occurs and
+	 * last object id could not be queried
 	 */
 	public long getLastObjectId() throws TskCoreException {
 		long id = -1;
@@ -4524,7 +4564,6 @@ public long getLastObjectId() throws TskCoreException {
 		return id;
 	}
 
-
 	/**
 	 * Set the file paths for the image given by obj_id
 	 *
@@ -4581,8 +4620,8 @@ private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs) throws SQLExce
 				} else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()) {
 					final VirtualDirectory virtDir = rsHelper.virtualDirectory(rs);
 					results.add(virtDir);
-				} else if (type == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() ||
-						type == TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType()) {
+				} else if (type == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType()
+						|| type == TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType()) {
 					TSK_DB_FILES_TYPE_ENUM atype = TSK_DB_FILES_TYPE_ENUM.valueOf(type);
 					String parentPath = rs.getString("parent_path");
 					if (parentPath == null) {
@@ -4883,7 +4922,6 @@ public int countFsContentType(TskData.TSK_FS_META_TYPE_ENUM contentType) throws
 		}
 		return count;
 	}
-	
 
 	/**
 	 * Escape the single quotes in the given string so they can be added to the
@@ -4912,7 +4950,7 @@ public List<AbstractFile> findFilesByMd5(String md5Hash) {
 		try {
 			s = con.createStatement();
 			rs = s.executeQuery("SELECT * FROM tsk_files WHERE "
-				+ " md5 = '" + md5Hash + "' "
+					+ " md5 = '" + md5Hash + "' "
 					+ "AND size > 0");
 			return resultSetToAbstractFiles(rs);
 
@@ -5016,27 +5054,31 @@ public int countFilesMd5Hashed() {
 		}
 		return count;
 	}
-	
+
 	/**
 	 * This is a temporary workaround to avoid an API change.
+	 *
 	 * @deprecated
 	 */
 	@Deprecated
 	public interface ErrorObserver {
+
 		void receiveError(String context, String errorMessage);
 	}
-	
+
 	/**
 	 * This is a temporary workaround to avoid an API change.
+	 *
 	 * @deprecated
 	 */
 	@Deprecated
 	public void addErrorObserver(ErrorObserver observer) {
 		errorObservers.add(observer);
 	}
-	
+
 	/**
 	 * This is a temporary workaround to avoid an API change.
+	 *
 	 * @deprecated
 	 */
 	@Deprecated
@@ -5046,9 +5088,10 @@ public void removerErrorObserver(ErrorObserver observer) {
 			errorObservers.remove(i);
 		}
 	}
-	
+
 	/**
 	 * This is a temporary workaround to avoid an API change.
+	 *
 	 * @deprecated
 	 */
 	@Deprecated
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Transaction.java b/bindings/java/src/org/sleuthkit/datamodel/Transaction.java
new file mode 100755
index 0000000000000000000000000000000000000000..780a619478cea288321d0389e92ac48ef1fa080c
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/Transaction.java
@@ -0,0 +1,59 @@
+/*
+ * Sleuth Kit Data Model
+ * 
+ * Copyright 2013 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel;
+
+/**
+ * interface to encapsulate database transactions
+ *
+ *
+ *
+ */
+public interface Transaction {
+
+	/**
+	 * rollback whatever changes this transaction represents
+	 */
+	public void rollback();
+
+	/**
+	 * check whether this transaction has already been committed
+	 *
+	 * @return whether this transaction has already been committed
+	 */
+	public Boolean isCommitted();
+
+	/**
+	 * commit this transaction to the database
+	 */
+	public void commit();
+
+	/**
+	 *
+	 * close this Transaction so it cannot be committed or rolledback. A closed
+	 * Transaction no longer has a reference to a db Connection and methods
+	 * invoked on a closed Transaction have no effect.
+	 */
+	public void close();
+
+	/**
+	 *
+	 * @return true if this transaction is closed
+	 */
+	public Boolean isClosed();
+}