We know the store is not in stable state, yet. So, please continue to report errors.
Next step to a stable store.
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
Peter, would you...? I hardly dare to ask...
Thanks in advance,
Oliver
Index: AbstractTxFileStoreService.java =================================================================== RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/txfile/AbstractTxFileStoreService.java,v retrieving revision 1.1 diff -u -w -r1.1 AbstractTxFileStoreService.java --- AbstractTxFileStoreService.java 9 Oct 2003 09:31:33 -0000 1.1 +++ AbstractTxFileStoreService.java 24 Oct 2003 13:28:17 -0000 @@ -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; @@ -266,6 +270,75 @@ } } + 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); + + } + } + protected Object getActiveTxId() { Object txId = activeTransactionBranch.get(); return txId; Index: StoreLogger.java =================================================================== RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/txfile/StoreLogger.java,v retrieving revision 1.1 diff -u -w -r1.1 StoreLogger.java --- StoreLogger.java 9 Oct 2003 09:31:33 -0000 1.1 +++ StoreLogger.java 24 Oct 2003 13:28:17 -0000 @@ -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); } } Index: TxFileContentStore.java =================================================================== RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/txfile/TxFileContentStore.java,v retrieving revision 1.1 diff -u -w -r1.1 TxFileContentStore.java --- TxFileContentStore.java 9 Oct 2003 09:31:33 -0000 1.1 +++ TxFileContentStore.java 24 Oct 2003 13:28:18 -0000 @@ -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); } } Index: TxXMLFileDescriptorsStore.java =================================================================== RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/txfile/TxXMLFileDescriptorsStore.java,v retrieving revision 1.1 diff -u -w -r1.1 TxXMLFileDescriptorsStore.java --- TxXMLFileDescriptorsStore.java 9 Oct 2003 09:31:33 -0000 1.1 +++ TxXMLFileDescriptorsStore.java 24 Oct 2003 13:28:18 -0000 @@ -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; } } Index: XMLResourceDescriptor.java =================================================================== RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/txfile/XMLResourceDescriptor.java,v retrieving revision 1.1 diff -u -w -r1.1 XMLResourceDescriptor.java --- XMLResourceDescriptor.java 9 Oct 2003 09:31:33 -0000 1.1 +++ XMLResourceDescriptor.java 24 Oct 2003 13:28:20 -0000 @@ -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); } } Index: rm/impl/FileResourceManager.java =================================================================== RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/txfile/rm/impl/FileResourceManager.java,v retrieving revision 1.1 diff -u -w -r1.1 FileResourceManager.java --- rm/impl/FileResourceManager.java 9 Oct 2003 09:31:33 -0000 1.1 +++ rm/impl/FileResourceManager.java 24 Oct 2003 13:28:22 -0000 @@ -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]