From 2f5a87fa9e8c89ebf4c130944d93d7881d16ceb3 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Wed, 15 Feb 2006 13:05:25 +0000 Subject: [PATCH] Added trace debugging categories to catch unclosed IO Channels and unclosed UserTransactions git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2382 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../repo/content/AbstractContentAccessor.java | 71 ++++++++++++++++--- .../repo/content/AbstractContentReader.java | 17 ++++- .../repo/content/AbstractContentWriter.java | 15 ++++ 3 files changed, 93 insertions(+), 10 deletions(-) diff --git a/source/java/org/alfresco/repo/content/AbstractContentAccessor.java b/source/java/org/alfresco/repo/content/AbstractContentAccessor.java index a22b3a3a75..16aff08007 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentAccessor.java +++ b/source/java/org/alfresco/repo/content/AbstractContentAccessor.java @@ -26,6 +26,7 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.List; +import org.alfresco.error.StackTraceUtil; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.service.cmr.repository.ContentAccessor; import org.alfresco.service.cmr.repository.ContentData; @@ -44,6 +45,16 @@ import org.springframework.aop.AfterReturningAdvice; public abstract class AbstractContentAccessor implements ContentAccessor { private static Log logger = LogFactory.getLog(AbstractContentAccessor.class); + private static final Log loggerTrace = LogFactory.getLog(AbstractContentAccessor.class.getName() + ".trace"); + static + { + if (loggerTrace.isDebugEnabled()) + { + loggerTrace.warn("Trace channel assignment logging is on and will affect performance"); + } + } + + private StackTraceElement[] traceLoggerChannelAssignTrace; /** when set, ensures that listeners are executed within a transaction */ private TransactionService transactionService; @@ -67,6 +78,37 @@ public abstract class AbstractContentAccessor implements ContentAccessor encoding = "UTF-8"; } + @Override + protected void finalize() throws Throwable + { + if (loggerTrace.isDebugEnabled() && traceLoggerChannelAssignTrace != null) + { + // check that the channel is closed if it was used + if (isChannelOpen()) + { + StringBuilder sb = new StringBuilder(1024); + StackTraceUtil.buildStackTrace( + "UserTransaction being garbage collected without a commit() or rollback().", + traceLoggerChannelAssignTrace, + sb, + -1); + loggerTrace.error(sb); + } + } + } + + public String toString() + { + StringBuilder sb = new StringBuilder(100); + sb.append("ContentAccessor") + .append("[ contentUrl=").append(getContentUrl()) + .append(", mimetype=").append(getMimetype()) + .append(", size=").append(getSize()) + .append(", encoding=").append(getEncoding()) + .append("]"); + return sb.toString(); + } + public ContentData getContentData() { ContentData property = new ContentData(contentUrl, mimetype, getSize(), encoding); @@ -93,18 +135,29 @@ public abstract class AbstractContentAccessor implements ContentAccessor this.transactionService = transactionService; } - public String toString() + /** + * Derived classes can call this method to ensure that necessary trace logging is performed + * when the IO Channel is opened. + */ + protected final void channelOpened() { - StringBuilder sb = new StringBuilder(100); - sb.append("ContentAccessor") - .append("[ contentUrl=").append(getContentUrl()) - .append(", mimetype=").append(getMimetype()) - .append(", size=").append(getSize()) - .append(", encoding=").append(getEncoding()) - .append("]"); - return sb.toString(); + // trace debug + if (loggerTrace.isDebugEnabled()) + { + Exception e = new Exception(); + e.fillInStackTrace(); + traceLoggerChannelAssignTrace = e.getStackTrace(); + } } + /** + * Derived classes must implement this to help determine if the underlying + * IO Channel is still open. + * + * @return Returns true if the underlying IO Channel is open + */ + protected abstract boolean isChannelOpen(); + public String getContentUrl() { return contentUrl; diff --git a/source/java/org/alfresco/repo/content/AbstractContentReader.java b/source/java/org/alfresco/repo/content/AbstractContentReader.java index 372a9d2daa..a908b500d6 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentReader.java +++ b/source/java/org/alfresco/repo/content/AbstractContentReader.java @@ -69,7 +69,7 @@ public abstract class AbstractContentReader extends AbstractContentAccessor impl listeners = new ArrayList(2); } - + /** * Adds the listener after checking that the output stream isn't already in * use. @@ -141,6 +141,19 @@ public abstract class AbstractContentReader extends AbstractContentAccessor impl } } + /** helper implementation for base class */ + protected boolean isChannelOpen() + { + if (channel != null) + { + return channel.isOpen(); + } + else + { + return false; + } + } + /** * Provides low-level access to read content from the repository. *

@@ -204,6 +217,8 @@ public abstract class AbstractContentReader extends AbstractContentAccessor impl ReadableByteChannel directChannel = getDirectReadableChannel(); channel = getCallbackReadableChannel(directChannel, listeners); + // notify that the channel was opened + super.channelOpened(); // done if (logger.isDebugEnabled()) { diff --git a/source/java/org/alfresco/repo/content/AbstractContentWriter.java b/source/java/org/alfresco/repo/content/AbstractContentWriter.java index 06701d9c00..234da69207 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentWriter.java +++ b/source/java/org/alfresco/repo/content/AbstractContentWriter.java @@ -155,6 +155,19 @@ public abstract class AbstractContentWriter extends AbstractContentAccessor impl return false; } } + + /** helper implementation for base class */ + protected boolean isChannelOpen() + { + if (channel != null) + { + return channel.isOpen(); + } + else + { + return false; + } + } /** * Provides low-level access to write content to the repository. @@ -218,6 +231,8 @@ public abstract class AbstractContentWriter extends AbstractContentAccessor impl WritableByteChannel directChannel = getDirectWritableChannel(); channel = getCallbackWritableChannel(directChannel, listeners); + // notify that the channel was opened + super.channelOpened(); // done if (logger.isDebugEnabled()) {