pnever 2003/10/28 00:41:42
Modified: src/stores/org/apache/slide/store/txfile
AbstractTxFileStoreService.java StoreLogger.java
TxFileContentStore.java
TxXMLFileDescriptorsStore.java
XMLResourceDescriptor.java
src/stores/org/apache/slide/store/txfile/rm/impl
FileResourceManager.java
Log:
Fixes to the TX store by Oliver Zeigermann:
FIXES:
AbstractTxFileStoreService:
- Transaction will be marked for rollback upon system error or deadlock
- minor flaws
StoreLogger:
- logging of message and throwable will result in two log methods in
Slide logging
TxFileContentStore:
- adaptions to new system error handling in AbstractTxFileStoreService
- streams get closed in finally blocks to assure all locks in
FileResouceManager get freed
- minor flaws
TxXMLFileDescriptorsStore:
- adaptions to new system error handling in AbstractTxFileStoreService
XMLResourceDescriptor:
- adaptions to new system error handling in AbstractTxFileStoreService
- streams get closed in finally blocks to assure all locks in
FileResouceManager get freed
- made it thread safe
- added a tiny hack (which is to be investigated further)
FileResourceManager:
- freeing locks and resource when tx is marked for rollback to avoid
dangling locks when app misses to actually roll back the tx
Revision Changes Path
1.2 +75 -2
jakarta-slide/src/stores/org/apache/slide/store/txfile/AbstractTxFileStoreService.java
Index: AbstractTxFileStoreService.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/AbstractTxFileStoreService.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- AbstractTxFileStoreService.java 9 Oct 2003 09:31:33 -0000 1.1
+++ AbstractTxFileStoreService.java 28 Oct 2003 08:41:42 -0000 1.2
@@ -57,7 +57,11 @@
new File(storeDir).mkdirs();
new File(workDir).mkdirs();
- rm = new FileResourceManager(storeDir, workDir, new
StoreLogger(getLogger(), FileResourceManager.class.getName()));
+ rm =
+ new FileResourceManager(
+ storeDir,
+ workDir,
+ new StoreLogger(getLogger(), FileResourceManager.class.getName()));
getLogger().log(
"File Store configured to " + storeDir + ", working directory " +
workDir,
@@ -134,7 +138,7 @@
LOG_CHANNEL,
Logger.DEBUG);
try {
- int status = rm.prepareTransaction(XidWrapper.wrap(xid));
+ int status = rm.prepareTransaction(txId);
switch (status) {
case ResourceManager.PREPARE_SUCCESS_READONLY :
return XA_RDONLY;
@@ -263,6 +267,75 @@
getLogger().log("Thread of control resume work on known transaction
branch", LOG_CHANNEL, Logger.DEBUG);
// XXX we do not suspend, so we do not resume
break;
+ }
+ }
+
+ public synchronized void throwInternalError(String cause) throws
ServiceAccessException {
+ Object txId = getActiveTxId();
+
+ getLogger().log(
+ "Thread "
+ + Thread.currentThread()
+ + " marked transaction branch "
+ + txId
+ + " for rollback. Cause: "
+ + cause,
+ LOG_CHANNEL,
+ Logger.WARNING);
+
+ try {
+ rm.markTransactionForRollback(txId);
+ } catch (ResourceManagerException re) {
+ throw new ServiceAccessException(this, re);
+ }
+
+ throw new ServiceAccessException(this, cause);
+
+ }
+
+ // TODO if error is caused by lock that could not be acquired
+ // we should try deadlock detection instead of simply rolling back
+ // if no deadlock is detected, retrying for lock would be preferred method
+ public synchronized void throwInternalError(Throwable cause) throws
ServiceAccessException {
+ Object txId = getActiveTxId();
+
+ if ((cause instanceof ResourceManagerException)
+ && ((ResourceManagerException) cause).getStatus() ==
ResourceManagerException.ERR_NO_LOCK) {
+
+ // XXX strictly speaking, this is incorrect, as we actually did not
chck for deadlock,
+ // but silently assume a deadlock must have been the cause for the
failed lock
+ try {
+ rm.markTransactionForRollback(txId);
+ } catch (ResourceManagerException re) {
+ throw new ServiceAccessException(this, re);
+ }
+
+ getLogger().log(
+ "DEADLOCK VICTIM: Thread "
+ + Thread.currentThread()
+ + " marked transaction branch "
+ + txId
+ + " for rollback",
+ LOG_CHANNEL,
+ Logger.INFO);
+
+ throw new ServiceAccessException(this, "deadlock victim");
+
+ } else {
+
+ try {
+ rm.markTransactionForRollback(txId);
+ } catch (ResourceManagerException re) {
+ throw new ServiceAccessException(this, re);
+ }
+
+ getLogger().log(
+ "Thread " + Thread.currentThread() + " marked transaction branch "
+ txId + " for rollback",
+ LOG_CHANNEL,
+ Logger.WARNING);
+
+ throw new ServiceAccessException(this, cause);
+
}
}
1.2 +1 -2
jakarta-slide/src/stores/org/apache/slide/store/txfile/StoreLogger.java
Index: StoreLogger.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/StoreLogger.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- StoreLogger.java 9 Oct 2003 09:31:33 -0000 1.1
+++ StoreLogger.java 28 Oct 2003 08:41:42 -0000 1.2
@@ -37,9 +37,8 @@
public void log(String message, int level, Throwable t) {
if (logger != null) {
- if (t == null) {
logger.log(message, logChannel, level);
- } else {
+ if (t != null) {
logger.log(t, logChannel, level);
}
}
1.2 +26 -11
jakarta-slide/src/stores/org/apache/slide/store/txfile/TxFileContentStore.java
Index: TxFileContentStore.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/TxFileContentStore.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TxFileContentStore.java 9 Oct 2003 09:31:33 -0000 1.1
+++ TxFileContentStore.java 28 Oct 2003 08:41:42 -0000 1.2
@@ -25,7 +25,7 @@
public NodeRevisionContent retrieveRevisionContent(Uri uri,
NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException, RevisionNotFoundException {
- String revisionUri = revisionUri = uri.toString() + "_" +
revisionDescriptor.getRevisionNumber();
+ String revisionUri = uri.toString() + "_" +
revisionDescriptor.getRevisionNumber();
try {
Object txId = getActiveTxId();
@@ -42,7 +42,8 @@
if (e.getStatus() == ResourceManagerException.ERR_NO_SUCH_RESOURCE) {
throw new RevisionNotFoundException(uri.toString(),
revisionDescriptor.getRevisionNumber());
} else {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
+ return null; // XXX fake (is never called)
}
}
}
@@ -59,12 +60,12 @@
storeRevisionContent(uri, revisionDescriptor, revisionContent);
} catch (RevisionNotFoundException e) {
// Can not be, as we just created it. If it unexpectedly is, this is
fatal
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
} catch (ResourceManagerException e) {
if (e.getStatus() == ResourceManagerException.ERR_RESOURCE_EXISTS) {
throw new RevisionAlreadyExistException(uri.toString(),
revisionDescriptor.getRevisionNumber());
} else {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
}
@@ -76,28 +77,42 @@
throws ServiceAccessException, RevisionNotFoundException {
String revisionUri = revisionUri = uri.toString() + "_" +
revisionDescriptor.getRevisionNumber();
+ OutputStream os = null;
+ InputStream is = null;
try {
- OutputStream os = rm.writeResource(getActiveTxId(), revisionUri);
- InputStream is = revisionContent.streamContent();
+ os = rm.writeResource(getActiveTxId(), revisionUri);
+ is = revisionContent.streamContent();
if (is != null) {
long contentBytes = FileHelper.copy(is, os);
- os.close();
long contentLength = revisionDescriptor.getContentLength();
revisionDescriptor.setContentLength(contentBytes);
if (contentLength != -1 && contentBytes != contentLength) {
rm.deleteResource(getActiveTxId(), revisionUri);
- throw new ServiceAccessException(this, "Content length does not
match expected");
+ throwInternalError("Content length does not match expected");
}
}
} catch (IOException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
} catch (ResourceManagerException e) {
if (e.getStatus() == ResourceManagerException.ERR_NO_SUCH_RESOURCE) {
throw new RevisionNotFoundException(uri.toString(),
revisionDescriptor.getRevisionNumber());
} else {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
+ }
+ } finally {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException e) {
+ }
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
}
}
}
@@ -108,7 +123,7 @@
try {
rm.deleteResource(getActiveTxId(), revisionUri);
} catch (ResourceManagerException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
1.2 +16 -15
jakarta-slide/src/stores/org/apache/slide/store/txfile/TxXMLFileDescriptorsStore.java
Index: TxXMLFileDescriptorsStore.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/TxXMLFileDescriptorsStore.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TxXMLFileDescriptorsStore.java 9 Oct 2003 09:31:33 -0000 1.1
+++ TxXMLFileDescriptorsStore.java 28 Oct 2003 08:41:42 -0000 1.2
@@ -64,7 +64,7 @@
xfd.storeObject(uri, object);
} catch (ObjectNotFoundException e) {
// should not happen, if it does, it is an error inside this store:
- throw new ServiceAccessException(this, "Newly created file vanished");
+ throwInternalError("Newly created file vanished");
}
}
@@ -84,7 +84,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.grantPermission(uri, permission);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -93,7 +93,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.revokePermission(uri, permission);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -102,7 +102,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.revokePermissions(uri);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -111,7 +111,8 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
return xfd.enumeratePermissions();
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
+ return null; // XXX fake (is never called)
}
}
@@ -126,7 +127,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.putLock(uri, lock);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -135,7 +136,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.renewLock(uri, lock);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -157,7 +158,8 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
return xfd.enumerateLocks();
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
+ return null; // XXX fake (is never called)
}
}
@@ -183,7 +185,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.createRevisionDescriptors(uri, revisionDescriptors);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -202,7 +204,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.removeRevisionDescriptors(uri);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -228,7 +230,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.createRevisionDescriptor(uri, revisionDescriptor);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -247,7 +249,7 @@
XMLResourceDescriptor xfd = getFileDescriptor(uri);
xfd.removeRevisionDescriptor(uri, number);
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(this, e);
+ throwInternalError(e);
}
}
@@ -260,10 +262,9 @@
/**
* Either returns a cached file descriptor or loads it from DB
*/
- protected XMLResourceDescriptor getFileDescriptor(Uri uri)
- throws ServiceAccessException, ObjectNotFoundException {
+ protected XMLResourceDescriptor getFileDescriptor(Uri uri) throws
ServiceAccessException, ObjectNotFoundException {
Object txId = getActiveTxId();
- XMLResourceDescriptor xfd = new XMLResourceDescriptor(uri, rm, txId,
characterEncoding);
+ XMLResourceDescriptor xfd = new XMLResourceDescriptor(uri, this, rm, txId,
characterEncoding);
return xfd;
}
}
1.2 +58 -34
jakarta-slide/src/stores/org/apache/slide/store/txfile/XMLResourceDescriptor.java
Index: XMLResourceDescriptor.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/XMLResourceDescriptor.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- XMLResourceDescriptor.java 9 Oct 2003 09:31:33 -0000 1.1
+++ XMLResourceDescriptor.java 28 Oct 2003 08:41:42 -0000 1.2
@@ -40,9 +40,10 @@
protected static final String PATH_EXTENSION = ".def.xml";
protected final FileResourceManager rm;
+ protected final TxXMLFileDescriptorsStore store;
protected final String characterEncoding;
- protected final Object txId;
- protected final Uri uri;
+ protected volatile Object txId;
+ protected volatile Uri uri;
protected final String loadPath;
protected StoreLogger logger;
@@ -57,7 +58,12 @@
* @throws ServiceAccessException if anything goes wrong at system level
* @throws ObjectNotFoundException if <code>load</code> is set to
<code>true</code> and the descriptor is not present in resource manager
*/
- public XMLResourceDescriptor(Uri uri, FileResourceManager rm, Object txId,
String characterEncoding)
+ public XMLResourceDescriptor(
+ Uri uri,
+ TxXMLFileDescriptorsStore store,
+ FileResourceManager rm,
+ Object txId,
+ String characterEncoding)
throws ServiceAccessException, ObjectNotFoundException {
// XXX super class tries to create folders, no way to stop it:
// at least let it create folders in a place where it can do no harm
@@ -66,6 +72,7 @@
logger =
rm.getLogger().cloneWithNewLogChannel(XMLResourceDescriptor.class.getName());
this.rm = rm;
+ this.store = store;
this.characterEncoding = characterEncoding;
this.txId = txId;
this.uri = uri;
@@ -79,22 +86,28 @@
* @throws ServiceAccessException if anything goes wrong at system level
* @throws ObjectNotFoundException if the descriptor has not been created
before
*/
- public void save() throws ServiceAccessException, ObjectNotFoundException {
+ public synchronized void save() throws ServiceAccessException,
ObjectNotFoundException {
if (txId == null) {
- throw new ServiceAccessException(null, "Not inside tx");
+ store.throwInternalError("Not inside tx");
}
logger.logFine("Tx " + txId + " saves data for " + loadPath);
+
+ OutputStream os = null;
try {
- OutputStream os = rm.writeResource(txId, loadPath);
+ os = rm.writeResource(txId, loadPath);
save(os);
- os.close();
- } catch (IOException e) {
- throw new ServiceAccessException(null, e);
} catch (ResourceManagerException e) {
if (e.getStatus() == ResourceManagerException.ERR_NO_SUCH_RESOURCE) {
throw new ObjectNotFoundException(uri);
} else {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
+ }
+ } finally {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException e) {
}
}
}
@@ -105,10 +118,10 @@
* @throws ServiceAccessException if anything goes wrong at system level
* @throws ObjectAlreadyExistsException if the descriptor already exists
*/
- public void create() throws ServiceAccessException,
ObjectAlreadyExistsException {
+ public synchronized void create() throws ServiceAccessException,
ObjectAlreadyExistsException {
logger.logFiner("Tx " + txId + " creates " + loadPath);
if (txId == null) {
- throw new ServiceAccessException(null, "Not inside tx");
+ store.throwInternalError("Not inside tx");
}
try {
rm.createResource(txId, loadPath, false);
@@ -117,7 +130,7 @@
if (e.getStatus() == ResourceManagerException.ERR_RESOURCE_EXISTS) {
throw new ObjectAlreadyExistsException(uri.toString());
} else {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
}
}
}
@@ -128,10 +141,10 @@
* @throws ServiceAccessException if anything goes wrong at system level
* @throws ObjectNotFoundException if the descriptor does not exist
*/
- public void delete() throws ServiceAccessException, ObjectNotFoundException {
+ public synchronized void delete() throws ServiceAccessException,
ObjectNotFoundException {
logger.logFiner("Tx " + txId + " deletes " + loadPath);
if (txId == null) {
- throw new ServiceAccessException(null, "Not inside tx");
+ store.throwInternalError("Not inside tx");
}
try {
rm.deleteResource(txId, loadPath, false);
@@ -140,7 +153,7 @@
if (e.getStatus() == ResourceManagerException.ERR_NO_SUCH_RESOURCE) {
throw new ObjectNotFoundException(uri.toString());
} else {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
}
}
}
@@ -151,34 +164,39 @@
* @throws ServiceAccessException if anything goes wrong at system level
* @throws ObjectNotFoundException if the descriptor does not exist
*/
- public void load() throws ServiceAccessException, ObjectNotFoundException {
+ public synchronized void load() throws ServiceAccessException,
ObjectNotFoundException {
logger.logFiner("Tx " + txId + " loads data for " + loadPath);
+
+ InputStream is = null;
try {
logger.logFinest("Faking read access from outside tx for " + loadPath);
if (txId != null) {
if (rm.resourceExists(txId, loadPath)) {
- InputStream is = rm.readResource(txId, loadPath);
+ is = rm.readResource(txId, loadPath);
load(is);
- is.close();
} else {
init();
}
} else {
if (rm.resourceExists(loadPath)) {
- InputStream is = rm.readResource(loadPath);
+ is = rm.readResource(loadPath);
load(is);
- is.close();
} else {
init();
}
}
- } catch (IOException e) {
- throw new ServiceAccessException(null, e);
} catch (ResourceManagerException e) {
if (e.getStatus() == ResourceManagerException.ERR_NO_SUCH_RESOURCE) {
throw new ObjectNotFoundException(uri);
} else {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
+ }
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
}
}
}
@@ -208,7 +226,7 @@
* @param o object to compare this descriptor to
* @return <code>true</code> if object is equal as described above
*/
- public boolean equals(Object o) {
+ public synchronized boolean equals(Object o) {
return (
this == o
|| (o != null
@@ -217,7 +235,7 @@
&& ((XMLResourceDescriptor) o).txId.equals(txId)));
}
- protected void save(OutputStream os) throws ServiceAccessException {
+ protected synchronized void save(OutputStream os) throws ServiceAccessException
{
Element aRoot = encode();
Document aDocument = new Document(aRoot);
@@ -230,23 +248,23 @@
aOutputter.output(aDocument, os);
os.flush();
} catch (IOException e) {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
}
}
- protected void load(InputStream is) throws ServiceAccessException {
+ protected synchronized void load(InputStream is) throws ServiceAccessException {
SAXBuilder aBuilder = new SAXBuilder();
try {
Document aDocument = aBuilder.build(is);
decode(aDocument.getRootElement());
} catch (JDOMException e) {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
} catch (IOException e) {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
}
}
- protected void init() throws ServiceAccessException {
+ protected synchronized void init() throws ServiceAccessException {
object = null;
permissions = new Vector();
locks = new Vector();
@@ -254,14 +272,20 @@
descriptor = new Hashtable();
}
- protected void save(Uri uri) throws ServiceAccessException {
+ protected synchronized void save(Uri uri) throws ServiceAccessException {
if (txId == null) {
- throw new ServiceAccessException(null, "Not inside tx");
+ store.throwInternalError("Not inside tx");
}
+ // XXX this is a HACK, should throw exception, really
+ // if (object == null) {
+ // store.throwInternalError("Resource '"+uri+"' not initialized");
+ // }
+ if (object == null)
+ return;
try {
save();
} catch (ObjectNotFoundException e) {
- throw new ServiceAccessException(null, e);
+ store.throwInternalError(e);
}
}
1.2 +5 -0
jakarta-slide/src/stores/org/apache/slide/store/txfile/rm/impl/FileResourceManager.java
Index: FileResourceManager.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/rm/impl/FileResourceManager.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- FileResourceManager.java 9 Oct 2003 09:31:33 -0000 1.1
+++ FileResourceManager.java 28 Oct 2003 08:41:42 -0000 1.2
@@ -448,8 +448,13 @@
public void markTransactionForRollback(Object txId) throws
ResourceManagerException {
assureRMReady();
TransactionContext context = txInitialSaneCheckForWriting(txId);
+ try {
context.status = STATUS_MARKED_ROLLBACK;
context.saveState();
+ } finally {
+ // be very sure to free locks and resources, as application might crash
or otherwise forget to roll this tx back
+ context.finalCleanUp();
+ }
}
public int prepareTransaction(Object txId) throws ResourceManagerException {
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]