Author: bago Date: Sat Oct 4 08:49:32 2008 New Revision: 701657 URL: http://svn.apache.org/viewvc?rev=701657&view=rev Log: Provide a means to dispose a Message (MIME4J-72) Patch kindly provided by Markus Wiederkehr. Temporary commented out finalizers until more people will comment on the subject.
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/AbstractBody.java james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/SimpleTempStorage.java james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/MessageTest.java james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/SimpleTempStorageTest.java Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/AbstractBody.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/AbstractBody.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/AbstractBody.java (original) +++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/AbstractBody.java Sat Oct 4 08:49:32 2008 @@ -29,6 +29,7 @@ */ public abstract class AbstractBody implements Body { private Entity parent = null; + protected boolean disposed = false; /** * @see org.apache.james.mime4j.message.Body#getParent() @@ -41,17 +42,39 @@ * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity) */ public void setParent(Entity parent) { + if (disposed) + throw new IllegalStateException("AbstractBody has been disposed"); + this.parent = parent; } /** - * Subclasses should override this method if they have allocated resources that need to be - * freed explicitly (e.g. cannot be simply reclaimed by the garbage collector). The default - * implementation of this method does nothing. + * Subclasses should override this method if they have allocated resources + * that need to be freed explicitly (e.g. cannot be simply reclaimed by the + * garbage collector). Subclasses that override this method should invoke + * super.dispose(). + * + * The default implementation marks this AbstractBody as disposed. * * @see org.apache.james.mime4j.message.Disposable#dispose() */ public void dispose() { + if (disposed) + return; + + disposed = true; + + parent = null; } + /** + * Ensures that the <code>dispose</code> method of this abstract body is + * called when there are no more references to it. + * + * Leave them out ATM (https://issues.apache.org/jira/browse/MIME4J-72?focusedCommentId=12636007#action_12636007) + protected void finalize() throws Throwable { + dispose(); + } + */ + } Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java (original) +++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java Sat Oct 4 08:49:32 2008 @@ -40,6 +40,7 @@ private Header header = null; private Body body = null; private Entity parent = null; + private boolean disposed = false; /** * Gets the parent entity of this entity. @@ -58,6 +59,9 @@ * this will be the root entity. */ public void setParent(Entity parent) { + if (disposed) + throw new IllegalStateException("Entity has been disposed"); + this.parent = parent; } @@ -76,6 +80,9 @@ * @param header the header. */ public void setHeader(Header header) { + if (disposed) + throw new IllegalStateException("Entity has been disposed"); + this.header = header; } @@ -94,6 +101,9 @@ * @param body the body. */ public void setBody(Body body) { + if (disposed) + throw new IllegalStateException("Entity has been disposed"); + this.body = body; body.setParent(this); } @@ -173,6 +183,9 @@ * @throws IOException */ public void writeTo(OutputStream out, int mode) throws IOException, MimeException { + if (disposed) + throw new IllegalStateException("Entity has been disposed"); + getHeader().writeTo(out, mode); out.flush(); @@ -199,10 +212,35 @@ * Disposes the body of this entity. Note that the dispose call does not get * forwarded to the parent entity of this Entity. * + * Subclasses that need to free resources should override this method and + * invoke super.dispose(). + * * @see org.apache.james.mime4j.message.Disposable#dispose() */ public void dispose() { - if (body != null) - body.dispose(); + if (disposed) + return; + + try { + if (body != null) + body.dispose(); + } finally { + disposed = true; + + header = null; + body = null; + parent = null; + } + } + + /** + * Ensures that the <code>dispose</code> method of this entity is called + * when there are no more references to it. + * + * Leave them out ATM (https://issues.apache.org/jira/browse/MIME4J-72?focusedCommentId=12636007#action_12636007) + protected void finalize() throws Throwable { + dispose(); } + */ + } Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java (original) +++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Multipart.java Sat Oct 4 08:49:32 2008 @@ -51,6 +51,7 @@ private List bodyParts = new LinkedList(); private Entity parent = null; private String subType; + private boolean disposed = false; /** * Creates a new empty <code>Multipart</code> instance. @@ -78,6 +79,9 @@ * @param subType the sub-type. */ public void setSubType(String subType) { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + this.subType = subType; } @@ -92,6 +96,9 @@ * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity) */ public void setParent(Entity parent) { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + this.parent = parent; for (Iterator it = bodyParts.iterator(); it.hasNext();) { ((BodyPart) it.next()).setParent(parent); @@ -113,6 +120,9 @@ * @param epilogue the epilogue. */ public void setEpilogue(String epilogue) { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + this.epilogue = epilogue; } @@ -131,6 +141,9 @@ * @param bodyParts the new list of <code>BodyPart</code> objects. */ public void setBodyParts(List bodyParts) { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + this.bodyParts = bodyParts; for (Iterator it = bodyParts.iterator(); it.hasNext();) { ((BodyPart) it.next()).setParent(parent); @@ -143,6 +156,9 @@ * @param bodyPart the body part. */ public void addBodyPart(BodyPart bodyPart) { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + bodyParts.add(bodyPart); bodyPart.setParent(parent); } @@ -162,6 +178,9 @@ * @param preamble the preamble. */ public void setPreamble(String preamble) { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + this.preamble = preamble; } @@ -175,6 +194,9 @@ * @throws MimeException if case of a MIME protocol violation */ public void writeTo(final OutputStream out, int mode) throws IOException, MimeException { + if (disposed) + throw new IllegalStateException("Multipart has been disposed"); + Entity e = getParent(); ContentTypeField cField = (ContentTypeField) e.getHeader().getField( @@ -229,8 +251,31 @@ * @see org.apache.james.mime4j.message.Disposable#dispose() */ public void dispose() { - for (Iterator it = bodyParts.iterator(); it.hasNext();) { - ((BodyPart) it.next()).dispose(); + if (disposed) + return; + + try { + for (Iterator it = bodyParts.iterator(); it.hasNext();) { + ((BodyPart) it.next()).dispose(); + } + } finally { + disposed = true; + + preamble = null; + epilogue = null; + bodyParts = null; + parent = null; + subType = null; } } + + /** + * Ensures that the <code>dispose</code> method of this multipart is + * called when there are no more references to it. + * + * Leave them out ATM (https://issues.apache.org/jira/browse/MIME4J-72?focusedCommentId=12636007#action_12636007) + protected void finalize() throws Throwable { + dispose(); + } + */ } Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java (original) +++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java Sat Oct 4 08:49:32 2008 @@ -37,7 +37,6 @@ */ class TempFileBinaryBody extends AbstractBody implements BinaryBody { - private Entity parent = null; private TempFile tempFile = null; /** @@ -57,20 +56,6 @@ } /** - * @see org.apache.james.mime4j.message.AbstractBody#getParent() - */ - public Entity getParent() { - return parent; - } - - /** - * @see org.apache.james.mime4j.message.AbstractBody#setParent(org.apache.james.mime4j.message.Entity) - */ - public void setParent(Entity parent) { - this.parent = parent; - } - - /** * @see org.apache.james.mime4j.message.BinaryBody#getInputStream() */ public InputStream getInputStream() throws IOException { @@ -81,6 +66,9 @@ * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream, int) */ public void writeTo(OutputStream out, int mode) throws IOException { + if (disposed) + throw new IllegalStateException("TempFileBinaryBody has been disposed"); + final InputStream inputStream = getInputStream(); CodecUtil.copy(inputStream,out); } @@ -91,6 +79,12 @@ * @see org.apache.james.mime4j.message.Disposable#dispose() */ public void dispose() { - tempFile.delete(); + try { + tempFile.delete(); + } finally { + tempFile = null; + + super.dispose(); + } } } Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java (original) +++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java Sat Oct 4 08:49:32 2008 @@ -104,8 +104,11 @@ * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream, int) */ public void writeTo(OutputStream out, int mode) throws IOException { + if (disposed) + throw new IllegalStateException("TempFileTextBody has been disposed"); + final InputStream inputStream = tempFile.getInputStream(); - CodecUtil.copy(inputStream,out); + CodecUtil.copy(inputStream, out); } /** @@ -114,6 +117,13 @@ * @see org.apache.james.mime4j.message.Disposable#dispose() */ public void dispose() { - tempFile.delete(); + try { + tempFile.delete(); + } finally { + mimeCharset = null; + tempFile = null; + + super.dispose(); + } } } Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/SimpleTempStorage.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/SimpleTempStorage.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/SimpleTempStorage.java (original) +++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/SimpleTempStorage.java Sat Oct 4 08:49:32 2008 @@ -27,7 +27,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; import java.util.Random; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -182,9 +185,11 @@ } - private class SimpleTempFile implements TempFile { + private static class SimpleTempFile implements TempFile { private File file = null; + private static final Set filesToDelete = new HashSet(); + private SimpleTempFile(File file) { this.file = file; this.file.deleteOnExit(); @@ -212,10 +217,30 @@ } /** - * Do nothing + * @see org.apache.james.mime4j.message.storage.TempFile#delete() */ public void delete() { - // Not implementated + // deleting a file might not immediately succeed if there are still + // streams left open (especially under Windows). so we keep track of + // the files that have to be deleted and try to delete all these + // files each time this method gets invoked. + + // a better but more complicated solution would be to start a + // separate thread that tries to delete the files periodically. + + synchronized (filesToDelete) { + if (file != null) { + filesToDelete.add(file); + file = null; + } + + for (Iterator iterator = filesToDelete.iterator(); iterator + .hasNext();) { + File file = (File) iterator.next(); + if (file.delete()) + iterator.remove(); + } + } } /** Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/MessageTest.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/MessageTest.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/MessageTest.java (original) +++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/MessageTest.java Sat Oct 4 08:49:32 2008 @@ -22,13 +22,16 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.util.List; import junit.framework.TestCase; import org.apache.commons.io.IOUtils; +import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.field.Field; import org.apache.james.mime4j.util.MessageUtils; @@ -163,6 +166,47 @@ assertTrue("header added", lines.contains(testheader)); } + public void testDisposeGetsPropagatedToBody() throws Exception { + DummyBody body1 = new DummyBody(); + BodyPart part1 = new BodyPart(); + part1.setHeader(headerEmpty); + part1.setBody(body1); + + DummyBody body2 = new DummyBody(); + BodyPart part2 = new BodyPart(); + part2.setHeader(headerEmpty); + part2.setBody(body2); + + Multipart mp = new Multipart("mixed"); + mp.addBodyPart(part1); + mp.addBodyPart(part2); + + Message m = new Message(); + m.setHeader(headerMultipartMixed); + m.setBody(mp); + + assertFalse(body1.disposed); + assertFalse(body2.disposed); + + m.dispose(); + + assertTrue(body1.disposed); + assertTrue(body2.disposed); + } + + public void testDisposedMessageThrowsException() + throws Exception { + byte[] inputByte = getRawMessageAsByteArray(); + Message m = new Message(new ByteArrayInputStream(inputByte)); + m.dispose(); + + try { + m.writeTo(new ByteArrayOutputStream(), MessageUtils.LENIENT); + fail(); + } catch (IllegalStateException expected) { + } + } + private byte[] getRawMessageAsByteArray() { StringBuffer header = new StringBuffer(); StringBuffer body = new StringBuffer(); @@ -182,4 +226,19 @@ return complete.toString().getBytes(); } + private static final class DummyBody extends AbstractBody { + + public boolean disposed = false; + + public void writeTo(OutputStream out, int mode) throws IOException, + MimeException { + out.write("dummy".getBytes("US-ASCII")); + } + + public void dispose() { + disposed = true; + } + + } + } Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/SimpleTempStorageTest.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/SimpleTempStorageTest.java?rev=701657&r1=701656&r2=701657&view=diff ============================================================================== --- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/SimpleTempStorageTest.java (original) +++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/SimpleTempStorageTest.java Sat Oct 4 08:49:32 2008 @@ -115,5 +115,18 @@ fileName.matches("^test_prefix[0-9]+\\.suffix$")); assertTrue("Temp file doesn't exist", new File(file.getAbsolutePath()).exists()); - } + } + + public void testDeleteTempFile() throws IOException { + SimpleTempStorage man = new SimpleTempStorage(); + TempPath path = man.getRootTempPath().createTempPath(); + TempFile tempFile = path.createTempFile("test_prefix", ".suffix"); + + File backingFile = new File(tempFile.getAbsolutePath()); + assertTrue(backingFile.exists()); + + tempFile.delete(); + assertFalse(backingFile.exists()); + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]