You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ba...@apache.org on 2008/10/04 17:49:33 UTC

svn commit: r701657 - in /james/mime4j/trunk/src: main/java/org/apache/james/mime4j/message/ main/java/org/apache/james/mime4j/message/storage/ test/java/org/apache/james/mime4j/message/ test/java/org/apache/james/mime4j/message/storage/

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: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org