Coverage Report - org.apache.commons.io.output.DeferredFileOutputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
DeferredFileOutputStream
93%
41/44
100%
12/12
2
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.io.output;
 18  
 
 19  
 import java.io.File;
 20  
 import java.io.FileInputStream;
 21  
 import java.io.FileOutputStream;
 22  
 import java.io.IOException;
 23  
 import java.io.OutputStream;
 24  
 
 25  
 import org.apache.commons.io.IOUtils;
 26  
 
 27  
 
 28  
 /**
 29  
  * An output stream which will retain data in memory until a specified
 30  
  * threshold is reached, and only then commit it to disk. If the stream is
 31  
  * closed before the threshold is reached, the data will not be written to
 32  
  * disk at all.
 33  
  * <p>
 34  
  * This class originated in FileUpload processing. In this use case, you do
 35  
  * not know in advance the size of the file being uploaded. If the file is small
 36  
  * you want to store it in memory (for speed), but if the file is large you want
 37  
  * to store it to file (to avoid memory issues).
 38  
  *
 39  
  * @version $Id: DeferredFileOutputStream.java 1686747 2015-06-21 18:44:49Z krosenvold $
 40  
  */
 41  
 public class DeferredFileOutputStream
 42  
     extends ThresholdingOutputStream
 43  
 {
 44  
 
 45  
     // ----------------------------------------------------------- Data members
 46  
 
 47  
 
 48  
     /**
 49  
      * The output stream to which data will be written prior to the theshold
 50  
      * being reached.
 51  
      */
 52  
     private ByteArrayOutputStream memoryOutputStream;
 53  
 
 54  
 
 55  
     /**
 56  
      * The output stream to which data will be written at any given time. This
 57  
      * will always be one of <code>memoryOutputStream</code> or
 58  
      * <code>diskOutputStream</code>.
 59  
      */
 60  
     private OutputStream currentOutputStream;
 61  
 
 62  
 
 63  
     /**
 64  
      * The file to which output will be directed if the threshold is exceeded.
 65  
      */
 66  
     private File outputFile;
 67  
 
 68  
     /**
 69  
      * The temporary file prefix.
 70  
      */
 71  
     private final String prefix;
 72  
 
 73  
     /**
 74  
      * The temporary file suffix.
 75  
      */
 76  
     private final String suffix;
 77  
 
 78  
     /**
 79  
      * The directory to use for temporary files.
 80  
      */
 81  
     private final File directory;
 82  
 
 83  
 
 84  
     /**
 85  
      * True when close() has been called successfully.
 86  
      */
 87  10
     private boolean closed = false;
 88  
 
 89  
     // ----------------------------------------------------------- Constructors
 90  
 
 91  
 
 92  
     /**
 93  
      * Constructs an instance of this class which will trigger an event at the
 94  
      * specified threshold, and save data to a file beyond that point.
 95  
      *
 96  
      * @param threshold  The number of bytes at which to trigger an event.
 97  
      * @param outputFile The file to which data is saved beyond the threshold.
 98  
      */
 99  
     public DeferredFileOutputStream(final int threshold, final File outputFile)
 100  
     {
 101  6
         this(threshold,  outputFile, null, null, null);
 102  6
     }
 103  
 
 104  
 
 105  
     /**
 106  
      * Constructs an instance of this class which will trigger an event at the
 107  
      * specified threshold, and save data to a temporary file beyond that point.
 108  
      *
 109  
      * @param threshold  The number of bytes at which to trigger an event.
 110  
      * @param prefix Prefix to use for the temporary file.
 111  
      * @param suffix Suffix to use for the temporary file.
 112  
      * @param directory Temporary file directory.
 113  
      *
 114  
      * @since 1.4
 115  
      */
 116  
     public DeferredFileOutputStream(final int threshold, final String prefix, final String suffix, final File directory)
 117  
     {
 118  4
         this(threshold, null, prefix, suffix, directory);
 119  4
         if (prefix == null) {
 120  1
             throw new IllegalArgumentException("Temporary file prefix is missing");
 121  
         }
 122  3
     }
 123  
 
 124  
     /**
 125  
      * Constructs an instance of this class which will trigger an event at the
 126  
      * specified threshold, and save data either to a file beyond that point.
 127  
      *
 128  
      * @param threshold  The number of bytes at which to trigger an event.
 129  
      * @param outputFile The file to which data is saved beyond the threshold.
 130  
      * @param prefix Prefix to use for the temporary file.
 131  
      * @param suffix Suffix to use for the temporary file.
 132  
      * @param directory Temporary file directory.
 133  
      */
 134  
     private DeferredFileOutputStream(final int threshold, final File outputFile, final String prefix,
 135  
                                      final String suffix, final File directory) {
 136  10
         super(threshold);
 137  10
         this.outputFile = outputFile;
 138  
 
 139  10
         memoryOutputStream = new ByteArrayOutputStream();
 140  10
         currentOutputStream = memoryOutputStream;
 141  10
         this.prefix = prefix;
 142  10
         this.suffix = suffix;
 143  10
         this.directory = directory;
 144  10
     }
 145  
 
 146  
 
 147  
     // --------------------------------------- ThresholdingOutputStream methods
 148  
 
 149  
 
 150  
     /**
 151  
      * Returns the current output stream. This may be memory based or disk
 152  
      * based, depending on the current state with respect to the threshold.
 153  
      *
 154  
      * @return The underlying output stream.
 155  
      *
 156  
      * @exception IOException if an error occurs.
 157  
      */
 158  
     @Override
 159  
     protected OutputStream getStream() throws IOException
 160  
     {
 161  29
         return currentOutputStream;
 162  
     }
 163  
 
 164  
 
 165  
     /**
 166  
      * Switches the underlying output stream from a memory based stream to one
 167  
      * that is backed by disk. This is the point at which we realise that too
 168  
      * much data is being written to keep in memory, so we elect to switch to
 169  
      * disk-based storage.
 170  
      *
 171  
      * @exception IOException if an error occurs.
 172  
      */
 173  
     @Override
 174  
     protected void thresholdReached() throws IOException
 175  
     {
 176  5
         if (prefix != null) {
 177  2
             outputFile = File.createTempFile(prefix, suffix, directory);
 178  
         }
 179  5
         final FileOutputStream fos = new FileOutputStream(outputFile);
 180  
         try {
 181  5
             memoryOutputStream.writeTo(fos);
 182  0
         } catch (IOException e){
 183  0
             fos.close();
 184  0
             throw e;
 185  5
         }
 186  5
         currentOutputStream = fos;
 187  5
         memoryOutputStream = null;
 188  5
     }
 189  
 
 190  
 
 191  
     // --------------------------------------------------------- Public methods
 192  
 
 193  
 
 194  
     /**
 195  
      * Determines whether or not the data for this output stream has been
 196  
      * retained in memory.
 197  
      *
 198  
      * @return {@code true} if the data is available in memory;
 199  
      *         {@code false} otherwise.
 200  
      */
 201  
     public boolean isInMemory()
 202  
     {
 203  11
         return !isThresholdExceeded();
 204  
     }
 205  
 
 206  
 
 207  
     /**
 208  
      * Returns the data for this output stream as an array of bytes, assuming
 209  
      * that the data has been retained in memory. If the data was written to
 210  
      * disk, this method returns {@code null}.
 211  
      *
 212  
      * @return The data for this output stream, or {@code null} if no such
 213  
      *         data is available.
 214  
      */
 215  
     public byte[] getData()
 216  
     {
 217  6
         if (memoryOutputStream != null)
 218  
         {
 219  2
             return memoryOutputStream.toByteArray();
 220  
         }
 221  4
         return null;
 222  
     }
 223  
 
 224  
 
 225  
     /**
 226  
      * Returns either the output file specified in the constructor or
 227  
      * the temporary file created or null.
 228  
      * <p>
 229  
      * If the constructor specifying the file is used then it returns that
 230  
      * same output file, even when threshold has not been reached.
 231  
      * <p>
 232  
      * If constructor specifying a temporary file prefix/suffix is used
 233  
      * then the temporary file created once the threshold is reached is returned
 234  
      * If the threshold was not reached then {@code null} is returned.
 235  
      *
 236  
      * @return The file for this output stream, or {@code null} if no such
 237  
      *         file exists.
 238  
      */
 239  
     public File getFile()
 240  
     {
 241  17
         return outputFile;
 242  
     }
 243  
 
 244  
 
 245  
     /**
 246  
      * Closes underlying output stream, and mark this as closed
 247  
      *
 248  
      * @exception IOException if an error occurs.
 249  
      */
 250  
     @Override
 251  
     public void close() throws IOException
 252  
     {
 253  9
         super.close();
 254  9
         closed = true;
 255  9
     }
 256  
 
 257  
 
 258  
     /**
 259  
      * Writes the data from this output stream to the specified output stream,
 260  
      * after it has been closed.
 261  
      *
 262  
      * @param out output stream to write to.
 263  
      * @exception IOException if this stream is not yet closed or an error occurs.
 264  
      */
 265  
     public void writeTo(final OutputStream out) throws IOException
 266  
     {
 267  
         // we may only need to check if this is closed if we are working with a file
 268  
         // but we should force the habit of closing wether we are working with
 269  
         // a file or memory.
 270  4
         if (!closed)
 271  
         {
 272  2
             throw new IOException("Stream not closed");
 273  
         }
 274  
 
 275  2
         if(isInMemory())
 276  
         {
 277  1
             memoryOutputStream.writeTo(out);
 278  
         }
 279  
         else
 280  
         {
 281  1
             final FileInputStream fis = new FileInputStream(outputFile);
 282  
             try {
 283  1
                 IOUtils.copy(fis, out);
 284  
             } finally {
 285  1
                 IOUtils.closeQuietly(fis);
 286  1
             }
 287  
         }
 288  2
     }
 289  
 }