001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.util.io;
018:
019: import java.io.File;
020: import java.io.FileOutputStream;
021: import java.io.IOException;
022: import java.io.OutputStream;
023:
024: /**
025: * <p>
026: * An output stream which will retain data in memory until a specified threshold is
027: * reached, and only then commit it to disk. If the stream is closed before the threshold
028: * is reached, the data will not be written to disk at all.
029: * </p>
030: * <p>
031: * This class originated in FileUpload processing. In this use case, you do not know in
032: * advance the size of the file being uploaded. If the file is small you want to store it
033: * in memory (for speed), but if the file is large you want to store it to file (to avoid
034: * memory issues).
035: * </p>
036: * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
037: * @version $Id$
038: */
039: public class DeferredFileOutputStream extends ThresholdingOutputStream {
040:
041: // ----------------------------------------------------------- Data members
042:
043: /**
044: * The output stream to which data will be written at any given time. This will always
045: * be one of <code>memoryOutputStream</code> or <code>diskOutputStream</code>.
046: */
047: private OutputStream currentOutputStream;
048:
049: /**
050: * The output stream to which data will be written prior to the theshold being
051: * reached.
052: */
053: private ByteArrayOutputStream memoryOutputStream;
054:
055: /**
056: * The file to which output will be directed if the threshold is exceeded.
057: */
058: private final File outputFile;
059:
060: // ----------------------------------------------------------- Constructors
061:
062: /**
063: * Constructs an instance of this class which will trigger an event at the specified
064: * threshold, and save data to a file beyond that point.
065: * @param threshold The number of bytes at which to trigger an event.
066: * @param outputFile The file to which data is saved beyond the threshold.
067: */
068: public DeferredFileOutputStream(int threshold, File outputFile) {
069: super (threshold);
070: this .outputFile = outputFile;
071:
072: memoryOutputStream = new ByteArrayOutputStream();
073: currentOutputStream = memoryOutputStream;
074: }
075:
076: // --------------------------------------- ThresholdingOutputStream methods
077:
078: /**
079: * Returns the data for this output stream as an array of bytes, assuming that the
080: * data has been retained in memory. If the data was written to disk, this method
081: * returns <code>null</code>.
082: * @return The data for this output stream, or <code>null</code> if no such data is
083: * available.
084: */
085: public byte[] getData() {
086: if (memoryOutputStream != null) {
087: return memoryOutputStream.toByteArray();
088: }
089: return null;
090: }
091:
092: /**
093: * Returns the data for this output stream as a <code>File</code>, assuming that
094: * the data was written to disk. If the data was retained in memory, this method
095: * returns <code>null</code>.
096: * @return The file for this output stream, or <code>null</code> if no such file
097: * exists.
098: */
099: public File getFile() {
100: return outputFile;
101: }
102:
103: // --------------------------------------------------------- Public methods
104:
105: /**
106: * Determines whether or not the data for this output stream has been retained in
107: * memory.
108: * @return <code>true</code> if the data is available in memory; <code>false</code>
109: * otherwise.
110: */
111: public boolean isInMemory() {
112: return (!isThresholdExceeded());
113: }
114:
115: /**
116: * Returns the current output stream. This may be memory based or disk based,
117: * depending on the current state with respect to the threshold.
118: * @return The underlying output stream.
119: * @exception IOException if an error occurs.
120: */
121: protected OutputStream getStream() throws IOException {
122: return currentOutputStream;
123: }
124:
125: /**
126: * Switches the underlying output stream from a memory based stream to one that is
127: * backed by disk. This is the point at which we realise that too much data is being
128: * written to keep in memory, so we elect to switch to disk-based storage.
129: * @exception IOException if an error occurs.
130: */
131: protected void thresholdReached() throws IOException {
132: byte[] data = memoryOutputStream.toByteArray();
133: FileOutputStream fos = new FileOutputStream(outputFile);
134: fos.write(data);
135: currentOutputStream = fos;
136: memoryOutputStream = null;
137: }
138: }
|