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 mw...@apache.org on 2008/12/12 17:58:51 UTC
svn commit: r726071 - 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/storage/
Author: mwiederkehr
Date: Fri Dec 12 08:58:50 2008
New Revision: 726071
URL: http://svn.apache.org/viewvc?rev=726071&view=rev
Log:
OutputStream support for StorageProvider (MIME4J-91)
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/AbstractStorageProvider.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageOutputStream.java
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyFactory.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/CipherStorageProvider.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/MemoryStorageProvider.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageProvider.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/TempFileStorageProvider.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/ThresholdStorageProvider.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/StorageProviderTest.java
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyFactory.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyFactory.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyFactory.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyFactory.java Fri Dec 12 08:58:50 2008
@@ -43,10 +43,22 @@
private StorageProvider storageProvider;
+ /**
+ * Creates a new <code>BodyFactory</code> instance that uses the default
+ * storage provider for creating message bodies from input streams.
+ */
public BodyFactory() {
this.storageProvider = DefaultStorageProvider.getInstance();
}
+ /**
+ * Creates a new <code>BodyFactory</code> instance that uses the given
+ * storage provider for creating message bodies from input streams.
+ *
+ * @param storageProvider
+ * a storage provider or <code>null</code> to use the default
+ * one.
+ */
public BodyFactory(StorageProvider storageProvider) {
if (storageProvider == null)
storageProvider = DefaultStorageProvider.getInstance();
@@ -54,6 +66,17 @@
this.storageProvider = storageProvider;
}
+ /**
+ * Creates a {@link BinaryBody} that holds the content of the given input
+ * stream.
+ *
+ * @param is
+ * input stream to create a message body from.
+ * @return a {@link SingleBody} that implements the {@link BinaryBody}
+ * interface.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
public BinaryBody binaryBody(InputStream is) throws IOException {
if (is == null)
throw new IllegalArgumentException();
@@ -62,6 +85,45 @@
return new StorageBinaryBody(new MultiReferenceStorage(storage));
}
+ /**
+ * Creates a {@link BinaryBody} that holds the content of the given
+ * {@link Storage}.
+ * <p>
+ * Note that the caller must not invoke {@link Storage#delete() delete()} on
+ * the given <code>Storage</code> object after it has been passed to this
+ * method. Instead the message body created by this method takes care of
+ * deleting the storage when it gets disposed of (see
+ * {@link Disposable#dispose()}).
+ *
+ * @param storage
+ * storage to create a message body from.
+ * @return a {@link SingleBody} that implements the {@link BinaryBody}
+ * interface.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ public BinaryBody binaryBody(Storage storage) throws IOException {
+ if (storage == null)
+ throw new IllegalArgumentException();
+
+ return new StorageBinaryBody(new MultiReferenceStorage(storage));
+ }
+
+ /**
+ * Creates a {@link TextBody} that holds the content of the given input
+ * stream.
+ * <p>
+ * "us-ascii" is used to decode the byte content of the
+ * <code>Storage</code> into a character stream when calling
+ * {@link TextBody#getReader() getReader()} on the returned object.
+ *
+ * @param is
+ * input stream to create a message body from.
+ * @return a {@link SingleBody} that implements the {@link TextBody}
+ * interface.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
public TextBody textBody(InputStream is) throws IOException {
if (is == null)
throw new IllegalArgumentException();
@@ -71,6 +133,25 @@
MessageUtils.DEFAULT_CHARSET);
}
+ /**
+ * Creates a {@link TextBody} that holds the content of the given input
+ * stream.
+ * <p>
+ * The charset corresponding to the given MIME charset name is used to
+ * decode the byte content of the input stream into a character stream when
+ * calling {@link TextBody#getReader() getReader()} on the returned object.
+ * If the MIME charset has no corresponding Java charset or the Java charset
+ * cannot be used for decoding then "us-ascii" is used instead.
+ *
+ * @param is
+ * input stream to create a message body from.
+ * @param mimeCharset
+ * name of a MIME charset.
+ * @return a {@link SingleBody} that implements the {@link TextBody}
+ * interface.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
public TextBody textBody(InputStream is, String mimeCharset)
throws IOException {
if (is == null)
@@ -83,6 +164,85 @@
return new StorageTextBody(new MultiReferenceStorage(storage), charset);
}
+ /**
+ * Creates a {@link TextBody} that holds the content of the given
+ * {@link Storage}.
+ * <p>
+ * "us-ascii" is used to decode the byte content of the
+ * <code>Storage</code> into a character stream when calling
+ * {@link TextBody#getReader() getReader()} on the returned object.
+ * <p>
+ * Note that the caller must not invoke {@link Storage#delete() delete()} on
+ * the given <code>Storage</code> object after it has been passed to this
+ * method. Instead the message body created by this method takes care of
+ * deleting the storage when it gets disposed of (see
+ * {@link Disposable#dispose()}).
+ *
+ * @param storage
+ * storage to create a message body from.
+ * @return a {@link SingleBody} that implements the {@link TextBody}
+ * interface.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ public TextBody textBody(Storage storage) throws IOException {
+ if (storage == null)
+ throw new IllegalArgumentException();
+
+ return new StorageTextBody(new MultiReferenceStorage(storage),
+ MessageUtils.DEFAULT_CHARSET);
+ }
+
+ /**
+ * Creates a {@link TextBody} that holds the content of the given
+ * {@link Storage}.
+ * <p>
+ * The charset corresponding to the given MIME charset name is used to
+ * decode the byte content of the <code>Storage</code> into a character
+ * stream when calling {@link TextBody#getReader() getReader()} on the
+ * returned object. If the MIME charset has no corresponding Java charset or
+ * the Java charset cannot be used for decoding then "us-ascii" is
+ * used instead.
+ * <p>
+ * Note that the caller must not invoke {@link Storage#delete() delete()} on
+ * the given <code>Storage</code> object after it has been passed to this
+ * method. Instead the message body created by this method takes care of
+ * deleting the storage when it gets disposed of (see
+ * {@link Disposable#dispose()}).
+ *
+ * @param storage
+ * storage to create a message body from.
+ * @param mimeCharset
+ * name of a MIME charset.
+ * @return a {@link SingleBody} that implements the {@link TextBody}
+ * interface.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ public TextBody textBody(Storage storage, String mimeCharset)
+ throws IOException {
+ if (storage == null)
+ throw new IllegalArgumentException();
+ if (mimeCharset == null)
+ throw new IllegalArgumentException();
+
+ Charset charset = toJavaCharset(mimeCharset, false);
+ return new StorageTextBody(new MultiReferenceStorage(storage), charset);
+ }
+
+ /**
+ * Creates a {@link TextBody} that holds the content of the given string.
+ * <p>
+ * "us-ascii" is used to encode the characters of the string into
+ * a byte stream when calling
+ * {@link Body#writeTo(java.io.OutputStream, Mode) writeTo(OutputStream, Mode)}
+ * on the returned object.
+ *
+ * @param text
+ * text to create a message body from.
+ * @return a {@link SingleBody} that implements the {@link TextBody}
+ * interface.
+ */
public TextBody textBody(String text) {
if (text == null)
throw new IllegalArgumentException();
@@ -90,6 +250,23 @@
return new StringTextBody(text, MessageUtils.DEFAULT_CHARSET);
}
+ /**
+ * Creates a {@link TextBody} that holds the content of the given string.
+ * <p>
+ * The charset corresponding to the given MIME charset name is used to
+ * encode the characters of the string into a byte stream when calling
+ * {@link Body#writeTo(java.io.OutputStream, Mode) writeTo(OutputStream, Mode)}
+ * on the returned object. If the MIME charset has no corresponding Java
+ * charset or the Java charset cannot be used for encoding then
+ * "us-ascii" is used instead.
+ *
+ * @param text
+ * text to create a message body from.
+ * @param mimeCharset
+ * name of a MIME charset.
+ * @return a {@link SingleBody} that implements the {@link TextBody}
+ * interface.
+ */
public TextBody textBody(String text, String mimeCharset) {
if (text == null)
throw new IllegalArgumentException();
Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/AbstractStorageProvider.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/AbstractStorageProvider.java?rev=726071&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/AbstractStorageProvider.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/AbstractStorageProvider.java Fri Dec 12 08:58:50 2008
@@ -0,0 +1,61 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mime4j.message.storage;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.james.mime4j.decoder.CodecUtil;
+
+/**
+ * Abstract implementation of {@link StorageProvider} that implements
+ * {@link StorageProvider#store(InputStream) store(InputStream)} by copying the
+ * input stream to a {@link StorageOutputStream} obtained from
+ * {@link StorageProvider#createStorageOutputStream() createStorageOutputStream()}.
+ */
+public abstract class AbstractStorageProvider implements StorageProvider {
+
+ /**
+ * Sole constructor.
+ */
+ protected AbstractStorageProvider() {
+ }
+
+ /**
+ * This implementation creates a {@link StorageOutputStream} by calling
+ * {@link StorageProvider#createStorageOutputStream() createStorageOutputStream()}
+ * and copies the content of the given input stream to that output stream.
+ * It then calls {@link StorageOutputStream#toStorage()} on the output
+ * stream and returns this object.
+ *
+ * @param in
+ * stream containing the data to store.
+ * @return a {@link Storage} instance that can be used to retrieve the
+ * stored content.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ public final Storage store(InputStream in) throws IOException {
+ StorageOutputStream out = createStorageOutputStream();
+ CodecUtil.copy(in, out);
+ return out.toStorage();
+ }
+
+}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/CipherStorageProvider.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/CipherStorageProvider.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/CipherStorageProvider.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/CipherStorageProvider.java Fri Dec 12 08:58:50 2008
@@ -26,6 +26,7 @@
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
@@ -43,7 +44,7 @@
* DefaultStorageProvider.setInstance(provider);
* </pre>
*/
-public class CipherStorageProvider implements StorageProvider {
+public class CipherStorageProvider extends AbstractStorageProvider {
private final StorageProvider backend;
private final String algorithm;
@@ -83,19 +84,50 @@
}
}
- public Storage store(InputStream in) throws IOException {
- try {
- byte[] raw = keygen.generateKey().getEncoded();
- SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
+ public StorageOutputStream createStorageOutputStream() throws IOException {
+ return new CipherStorageOutputStream(backend
+ .createStorageOutputStream());
+ }
+
+ private final class CipherStorageOutputStream extends StorageOutputStream {
+ private final StorageOutputStream storageOut;
+ private final SecretKeySpec skeySpec;
+ private final CipherOutputStream cipherOut;
+
+ public CipherStorageOutputStream(StorageOutputStream out)
+ throws IOException {
+ try {
+ this.storageOut = out;
+
+ byte[] raw = keygen.generateKey().getEncoded();
+ skeySpec = new SecretKeySpec(raw, algorithm);
+
+ Cipher cipher = Cipher.getInstance(algorithm);
+ cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
- Cipher cipher = Cipher.getInstance(algorithm);
- cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+ this.cipherOut = new CipherOutputStream(out, cipher);
+ } catch (GeneralSecurityException e) {
+ throw (IOException) new IOException().initCause(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ cipherOut.close();
+ }
+
+ @Override
+ protected void write0(byte[] buffer, int offset, int length)
+ throws IOException {
+ cipherOut.write(buffer, offset, length);
+ }
- InputStream encrypted = new CipherInputStream(in, cipher);
- Storage storage = backend.store(encrypted);
- return new CipherStorage(storage, skeySpec);
- } catch (GeneralSecurityException e) {
- throw (IOException) new IOException().initCause(e);
+ @Override
+ protected Storage toStorage0() throws IOException {
+ // cipherOut has already been closed because toStorage calls close
+ Storage encrypted = storageOut.toStorage();
+ return new CipherStorage(encrypted, skeySpec);
}
}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/MemoryStorageProvider.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/MemoryStorageProvider.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/MemoryStorageProvider.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/MemoryStorageProvider.java Fri Dec 12 08:58:50 2008
@@ -35,7 +35,7 @@
* DefaultStorageProvider.setInstance(provider);
* </pre>
*/
-public class MemoryStorageProvider implements StorageProvider {
+public class MemoryStorageProvider extends AbstractStorageProvider {
/**
* Creates a new <code>MemoryStorageProvider</code>.
@@ -43,16 +43,24 @@
public MemoryStorageProvider() {
}
- public Storage store(InputStream in) throws IOException {
+ public StorageOutputStream createStorageOutputStream() {
+ return new MemoryStorageOutputStream();
+ }
+
+ private static final class MemoryStorageOutputStream extends
+ StorageOutputStream {
ByteArrayBuffer bab = new ByteArrayBuffer(1024);
- final byte[] buffer = new byte[512];
- int inputLength;
- while ((inputLength = in.read(buffer)) != -1) {
- bab.append(buffer, 0, inputLength);
+ @Override
+ protected void write0(byte[] buffer, int offset, int length)
+ throws IOException {
+ bab.append(buffer, offset, length);
}
- return new MemoryStorage(bab.buffer(), bab.length());
+ @Override
+ protected Storage toStorage0() throws IOException {
+ return new MemoryStorage(bab.buffer(), bab.length());
+ }
}
static final class MemoryStorage implements Storage {
Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageOutputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageOutputStream.java?rev=726071&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageOutputStream.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageOutputStream.java Fri Dec 12 08:58:50 2008
@@ -0,0 +1,170 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mime4j.message.storage;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class implements an output stream that can be used to create a
+ * {@link Storage} object. An instance of this class is obtained by calling
+ * {@link StorageProvider#createStorageOutputStream()}. The user can then write
+ * data to this instance and invoke {@link #toStorage()} to retrieve a
+ * {@link Storage} object that contains the data that has been written.
+ * <p>
+ * Note that the <code>StorageOutputStream</code> does not have to be closed
+ * explicitly because {@link #toStorage()} invokes {@link #close()} if
+ * necessary. Also note that {@link #toStorage()} may be invoked only once. One
+ * <code>StorageOutputStream</code> can create only one <code>Storage</code>
+ * instance.
+ */
+public abstract class StorageOutputStream extends OutputStream {
+
+ private byte[] singleByte;
+ private boolean closed;
+ private boolean usedUp;
+
+ /**
+ * Sole constructor.
+ */
+ protected StorageOutputStream() {
+ }
+
+ /**
+ * Closes this output stream if it has not already been closed and returns a
+ * {@link Storage} object contains the bytes that have been written to this
+ * output stream.
+ * <p>
+ * Note that this method may not be invoked a second time. This is because
+ * for some implementations it is not possible to create another
+ * <code>Storage</code> object that can be read from and deleted
+ * independently (e.g. if the implementation writes to a file).
+ *
+ * @return a <code>Storage</code> object as described above.
+ * @throws IOException
+ * if an I/O error occurs.
+ * @throws IllegalStateException
+ * if this method has already been called.
+ */
+ public final Storage toStorage() throws IOException {
+ if (usedUp)
+ throw new IllegalStateException(
+ "toStorage may be invoked only once");
+
+ if (!closed)
+ close();
+
+ usedUp = true;
+ return toStorage0();
+ }
+
+ @Override
+ public final void write(int b) throws IOException {
+ if (closed)
+ throw new IOException("StorageOutputStream has been closed");
+
+ if (singleByte == null)
+ singleByte = new byte[1];
+
+ singleByte[0] = (byte) b;
+ write0(singleByte, 0, 1);
+ }
+
+ @Override
+ public final void write(byte[] buffer) throws IOException {
+ if (closed)
+ throw new IOException("StorageOutputStream has been closed");
+
+ if (buffer == null)
+ throw new NullPointerException();
+
+ if (buffer.length == 0)
+ return;
+
+ write0(buffer, 0, buffer.length);
+ }
+
+ @Override
+ public final void write(byte[] buffer, int offset, int length)
+ throws IOException {
+ if (closed)
+ throw new IOException("StorageOutputStream has been closed");
+
+ if (buffer == null)
+ throw new NullPointerException();
+
+ if (offset < 0 || length < 0 || offset + length > buffer.length)
+ throw new IndexOutOfBoundsException();
+
+ if (length == 0)
+ return;
+
+ write0(buffer, offset, length);
+ }
+
+ /**
+ * Closes this output stream. Subclasses that override this method have to
+ * invoke <code>super.close()</code>.
+ * <p>
+ * This implementation never throws an {@link IOException} but a subclass
+ * might.
+ *
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ }
+
+ /**
+ * Has to implemented by a concrete subclass to write bytes from the given
+ * byte array to this <code>StorageOutputStream</code>. This method gets
+ * called by {@link #write(int)}, {@link #write(byte[])} and
+ * {@link #write(byte[], int, int)}. All the required preconditions have
+ * already been checked by these methods, including the check if the output
+ * stream has already been closed.
+ *
+ * @param buffer
+ * buffer containing bytes to write.
+ * @param offset
+ * start offset in the buffer.
+ * @param length
+ * number of bytes to write.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ protected abstract void write0(byte[] buffer, int offset, int length)
+ throws IOException;
+
+ /**
+ * Has to be implemented by a concrete subclass to create a {@link Storage}
+ * object from the bytes that have been written to this
+ * <code>StorageOutputStream</code>. This method gets called by
+ * {@link #toStorage()} after the preconditions have been checked. The
+ * implementation can also be sure that this methods gets invoked only once.
+ *
+ * @return a <code>Storage</code> object as described above.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ protected abstract Storage toStorage0() throws IOException;
+
+}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageProvider.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageProvider.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageProvider.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/StorageProvider.java Fri Dec 12 08:58:50 2008
@@ -23,7 +23,8 @@
import java.io.InputStream;
/**
- * Provides a strategy for storing the contents of an <code>InputStream</code>.
+ * Provides a strategy for storing the contents of an <code>InputStream</code>
+ * or retrieving the content written to an <code>OutputStream</code>.
*/
public interface StorageProvider {
/**
@@ -36,4 +37,15 @@
*/
Storage store(InputStream in) throws IOException;
+ /**
+ * Creates a {@link StorageOutputStream} where data to be stored can be
+ * written to. Subsequently the user can call
+ * {@link StorageOutputStream#toStorage() toStorage()} on that object to get
+ * a {@link Storage} instance that holds the data that has been written.
+ *
+ * @return a {@link StorageOutputStream} where data can be written to.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ StorageOutputStream createStorageOutputStream() throws IOException;
}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/TempFileStorageProvider.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/TempFileStorageProvider.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/TempFileStorageProvider.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/TempFileStorageProvider.java Fri Dec 12 08:58:50 2008
@@ -30,8 +30,6 @@
import java.util.Iterator;
import java.util.Set;
-import org.apache.james.mime4j.decoder.CodecUtil;
-
/**
* A {@link StorageProvider} that stores the data in temporary files. The files
* are stored either in a user-specified directory or the default temporary-file
@@ -45,7 +43,7 @@
* DefaultStorageProvider.setInstance(provider);
* </pre>
*/
-public class TempFileStorageProvider implements StorageProvider {
+public class TempFileStorageProvider extends AbstractStorageProvider {
private static final String DEFAULT_PREFIX = "m4j";
@@ -102,15 +100,40 @@
this.directory = directory;
}
- public Storage store(InputStream in) throws IOException {
+ public StorageOutputStream createStorageOutputStream() throws IOException {
File file = File.createTempFile(prefix, suffix, directory);
file.deleteOnExit();
- OutputStream out = new FileOutputStream(file);
- CodecUtil.copy(in, out);
- out.close();
+ return new TempFileStorageOutputStream(file);
+ }
+
+ private static final class TempFileStorageOutputStream extends
+ StorageOutputStream {
+ private File file;
+ private OutputStream out;
+
+ public TempFileStorageOutputStream(File file) throws IOException {
+ this.file = file;
+ this.out = new FileOutputStream(file);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ out.close();
+ }
+
+ @Override
+ protected void write0(byte[] buffer, int offset, int length)
+ throws IOException {
+ out.write(buffer, offset, length);
+ }
- return new TempFileStorage(file);
+ @Override
+ protected Storage toStorage0() throws IOException {
+ // out has already been closed because toStorage calls close
+ return new TempFileStorage(file);
+ }
}
private static final class TempFileStorage implements Storage {
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/ThresholdStorageProvider.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/ThresholdStorageProvider.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/ThresholdStorageProvider.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/storage/ThresholdStorageProvider.java Fri Dec 12 08:58:50 2008
@@ -39,7 +39,7 @@
* DefaultStorageProvider.setInstance(provider);
* </pre>
*/
-public class ThresholdStorageProvider implements StorageProvider {
+public class ThresholdStorageProvider extends AbstractStorageProvider {
private final StorageProvider backend;
private final int thresholdSize;
@@ -74,30 +74,58 @@
this.thresholdSize = thresholdSize;
}
- public Storage store(InputStream in) throws IOException {
- final int bufferSize = Math.min(thresholdSize, 1024);
- byte[] buffer = new byte[bufferSize];
- ByteArrayBuffer bab = new ByteArrayBuffer(bufferSize);
-
- for (int remainingHeadSize = thresholdSize; remainingHeadSize > 0;) {
- int bytesToRead = Math.min(remainingHeadSize, bufferSize);
- int nBytes = in.read(buffer, 0, bytesToRead);
- if (nBytes == -1) {
- byte[] head = bab.buffer();
- int headLen = bab.length();
+ public StorageOutputStream createStorageOutputStream() {
+ return new ThresholdStorageOutputStream();
+ }
+
+ private final class ThresholdStorageOutputStream extends
+ StorageOutputStream {
+
+ private final ByteArrayBuffer head;
+ private StorageOutputStream tail;
+
+ public ThresholdStorageOutputStream() {
+ final int bufferSize = Math.min(thresholdSize, 1024);
+ head = new ByteArrayBuffer(bufferSize);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
- return new MemoryStorageProvider.MemoryStorage(head, headLen);
+ if (tail != null)
+ tail.close();
+ }
+
+ @Override
+ protected void write0(byte[] buffer, int offset, int length)
+ throws IOException {
+ int remainingHeadSize = thresholdSize - head.length();
+ if (remainingHeadSize > 0) {
+ int n = Math.min(remainingHeadSize, length);
+ head.append(buffer, offset, n);
+ offset += n;
+ length -= n;
}
- bab.append(buffer, 0, nBytes);
- remainingHeadSize -= nBytes;
+ if (length > 0) {
+ if (tail == null)
+ tail = backend.createStorageOutputStream();
+
+ tail.write(buffer, offset, length);
+ }
}
- byte[] head = bab.buffer();
- int headLen = bab.length();
- Storage tail = backend.store(in);
+ @Override
+ protected Storage toStorage0() throws IOException {
+ if (tail == null)
+ return new MemoryStorageProvider.MemoryStorage(head.buffer(),
+ head.length());
+
+ return new ThresholdStorage(head.buffer(), head.length(), tail
+ .toStorage());
+ }
- return new ThresholdStorage(head, headLen, tail);
}
private static final class ThresholdStorage implements Storage {
Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/StorageProviderTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/StorageProviderTest.java?rev=726071&r1=726070&r2=726071&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/StorageProviderTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/message/storage/StorageProviderTest.java Fri Dec 12 08:58:50 2008
@@ -35,6 +35,7 @@
testReadWrite(provider, 0);
testReadWrite(provider, 1);
testReadWrite(provider, 1024);
+ testReadWrite(provider, 20000);
testDelete(provider);
}
@@ -45,12 +46,13 @@
testReadWrite(provider, 0);
testReadWrite(provider, 1);
testReadWrite(provider, 1024);
+ testReadWrite(provider, 20000);
testDelete(provider);
}
public void testThresholdStorageProvider() throws Exception {
- final int threshold = 500;
+ final int threshold = 5000;
StorageProvider backend = new TempFileStorageProvider();
StorageProvider provider = new ThresholdStorageProvider(backend,
threshold);
@@ -61,6 +63,7 @@
testReadWrite(provider, threshold);
testReadWrite(provider, threshold + 1);
testReadWrite(provider, 2 * threshold);
+ testReadWrite(provider, 10 * threshold);
testDelete(provider);
}
@@ -72,24 +75,47 @@
testReadWrite(provider, 0);
testReadWrite(provider, 1);
testReadWrite(provider, 1024);
+ testReadWrite(provider, 20000);
testDelete(provider);
}
private void testReadWrite(StorageProvider provider, int size)
throws IOException {
+ testStore(provider, size);
+ testCreateStorageOutputStream(provider, size);
+ }
+
+ private void testStore(StorageProvider provider, int size)
+ throws IOException {
byte[] data = createData(size);
assertEquals(size, data.length);
Storage storage = provider.store(new ByteArrayInputStream(data));
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- CodecUtil.copy(storage.getInputStream(), out);
- byte[] result = out.toByteArray();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ CodecUtil.copy(storage.getInputStream(), baos);
+ verifyData(data, baos.toByteArray());
+ }
- assertEquals(size, result.length);
- for (int i = 0; i < size; i++) {
- assertEquals(data[i], result[i]);
+ private void testCreateStorageOutputStream(StorageProvider provider,
+ int size) throws IOException {
+ byte[] data = createData(size);
+ assertEquals(size, data.length);
+
+ StorageOutputStream out = provider.createStorageOutputStream();
+ CodecUtil.copy(new ByteArrayInputStream(data), out);
+ Storage storage = out.toStorage();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ CodecUtil.copy(storage.getInputStream(), baos);
+ verifyData(data, baos.toByteArray());
+ }
+
+ private void verifyData(byte[] expected, byte[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], actual[i]);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org