View Javadoc
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   */
18  package org.codehaus.plexus.archiver.zip;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.io.SequenceInputStream;
27  import org.apache.commons.io.output.ThresholdingOutputStream;
28  
29  /**
30   * Offloads to disk when a given memory consumption has been reacehd
31   */
32  class OffloadingOutputStream extends ThresholdingOutputStream
33  {
34  
35      // ----------------------------------------------------------- Data members
36  
37      /**
38       * The output stream to which data will be written prior to the theshold
39       * being reached.
40       */
41      private ByteArrayOutputStream memoryOutputStream;
42  
43      /**
44       * The output stream to which data will be written at any given time. This
45       * will always be one of <code>memoryOutputStream</code> or
46       * <code>diskOutputStream</code>.
47       */
48      private OutputStream currentOutputStream;
49  
50      /**
51       * The file to which output will be directed if the threshold is exceeded.
52       */
53      private File outputFile = null;
54  
55      /**
56       * The temporary file prefix.
57       */
58      private final String prefix;
59  
60      /**
61       * The temporary file suffix.
62       */
63      private final String suffix;
64  
65      /**
66       * The directory to use for temporary files.
67       */
68      private final File directory;
69  
70      /**
71       * True when close() has been called successfully.
72       */
73      private boolean closed = false;
74  
75      // ----------------------------------------------------------- Constructors
76  
77      /**
78       * Constructs an instance of this class which will trigger an event at the
79       * specified threshold, and save data to a temporary file beyond that point.
80       *
81       * @param threshold The number of bytes at which to trigger an event.
82       * @param prefix Prefix to use for the temporary file.
83       * @param suffix Suffix to use for the temporary file.
84       * @param directory Temporary file directory.
85       *
86       * @since 1.4
87       */
88      public OffloadingOutputStream( int threshold, String prefix, String suffix, File directory )
89      {
90          this( threshold, null, prefix, suffix, directory );
91          if ( prefix == null )
92          {
93              throw new IllegalArgumentException( "Temporary file prefix is missing" );
94          }
95      }
96  
97      /**
98       * Constructs an instance of this class which will trigger an event at the
99       * specified threshold, and save data either to a file beyond that point.
100      *
101      * @param threshold The number of bytes at which to trigger an event.
102      * @param outputFile The file to which data is saved beyond the threshold.
103      * @param prefix Prefix to use for the temporary file.
104      * @param suffix Suffix to use for the temporary file.
105      * @param directory Temporary file directory.
106      */
107     private OffloadingOutputStream( int threshold, File outputFile, String prefix, String suffix, File directory )
108     {
109         super( threshold );
110         this.outputFile = outputFile;
111 
112         memoryOutputStream = new ByteArrayOutputStream( threshold / 10 );
113         currentOutputStream = memoryOutputStream;
114         this.prefix = prefix;
115         this.suffix = suffix;
116         this.directory = directory;
117     }
118 
119     // --------------------------------------- ThresholdingOutputStream methods
120 
121     /**
122      * Returns the current output stream. This may be memory based or disk
123      * based, depending on the current state with respect to the threshold.
124      *
125      * @return The underlying output stream.
126      *
127      * @exception java.io.IOException if an error occurs.
128      */
129     @Override
130     protected OutputStream getStream() throws IOException
131     {
132         return currentOutputStream;
133     }
134 
135     /**
136      * Switches the underlying output stream from a memory based stream to one
137      * that is backed by disk. This is the point at which we realise that too
138      * much data is being written to keep in memory, so we elect to switch to
139      * disk-based storage.
140      *
141      * @exception java.io.IOException if an error occurs.
142      */
143     @Override
144     protected void thresholdReached() throws IOException
145     {
146         if ( prefix != null )
147         {
148             outputFile = File.createTempFile( prefix, suffix, directory );
149         }
150         currentOutputStream = new FileOutputStream( outputFile );
151     }
152 
153     public InputStream getInputStream() throws IOException
154     {
155 
156         InputStream memoryAsInput = memoryOutputStream.toInputStream();
157         if ( outputFile == null )
158         {
159             return memoryAsInput;
160         }
161         return new SequenceInputStream( memoryAsInput, new FileInputStream( outputFile ) );
162     }
163 
164     // --------------------------------------------------------- Public methods
165 
166     /**
167      * Returns the data for this output stream as an array of bytes, assuming
168      * that the data has been retained in memory. If the data was written to
169      * disk, this method returns <code>null</code>.
170      *
171      * @return The data for this output stream, or <code>null</code> if no such
172      * data is available.
173      */
174     public byte[] getData()
175     {
176         if ( memoryOutputStream != null )
177         {
178             return memoryOutputStream.toByteArray();
179         }
180         return null;
181     }
182 
183     /**
184      * Returns either the output file specified in the constructor or
185      * the temporary file created or null.
186      * <p>
187      * If the constructor specifying the file is used then it returns that
188      * same output file, even when threshold has not been reached.
189      * <p>
190      * If constructor specifying a temporary file prefix/suffix is used
191      * then the temporary file created once the threshold is reached is returned
192      * If the threshold was not reached then <code>null</code> is returned.
193      *
194      * @return The file for this output stream, or <code>null</code> if no such
195      * file exists.
196      */
197     public File getFile()
198     {
199         return outputFile;
200     }
201 
202     /**
203      * Closes underlying output stream, and mark this as closed
204      *
205      * @exception java.io.IOException if an error occurs.
206      */
207     @Override
208     public void close() throws IOException
209     {
210         super.close();
211         closed = true;
212         currentOutputStream.close();
213     }
214 
215 }