1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package org.apache.chemistry.opencmis.server.shared;
20  
21  import java.io.BufferedInputStream;
22  import java.io.BufferedOutputStream;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileNotFoundException;
26  import java.io.FileOutputStream;
27  import java.io.FilterInputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  public class ThresholdOutputStream extends OutputStream
43  {
44      private static final int MAX_GROW = 10 * 1024 * 1024;
45      private static final int DEFAULT_THRESHOLD = 4 * 1024 * 1024;
46      private File tempDir;
47      private int memoryThreshold;
48      private byte[] buf = null;
49      private int bufSize = 0;
50      private long size;
51      private File tempFile;
52      private OutputStream tmpStream;
53  
54      public ThresholdOutputStream( File tempDir, int memoryThreshold )
55      {
56          this( 64 * 1024, tempDir, memoryThreshold );
57      }
58  
59      public ThresholdOutputStream( int initSize, File tempDir, int memoryThreshold )
60      {
61          if ( initSize < 0 )
62          {
63              throw new IllegalArgumentException( "Negative initial size: " + initSize );
64          }
65  
66          this.tempDir = tempDir;
67          this.memoryThreshold = ( ( memoryThreshold < 0 ) ? DEFAULT_THRESHOLD : memoryThreshold );
68  
69          buf = new byte[initSize];
70      }
71  
72      private void expand( int nextBufferSize ) throws IOException
73      {
74          if ( ( bufSize + nextBufferSize ) <= buf.length )
75          {
76              return;
77          }
78  
79          if ( ( bufSize + nextBufferSize ) > memoryThreshold )
80          {
81              if ( tmpStream == null )
82              {
83                  tempFile = File.createTempFile( "opencmis", null, tempDir );
84                  tmpStream = new BufferedOutputStream( new FileOutputStream( tempFile ) );
85              }
86  
87              tmpStream.write( buf, 0, bufSize );
88  
89              if ( buf.length != memoryThreshold )
90              {
91                  buf = new byte[memoryThreshold];
92              }
93  
94              bufSize = 0;
95  
96              return;
97          }
98  
99          int newSize = ( ( ( ( bufSize + nextBufferSize ) * 2 ) < MAX_GROW ) ? ( ( bufSize + nextBufferSize ) * 2 )
100                                                                             : ( buf.length + nextBufferSize + MAX_GROW ) );
101         byte[] newbuf = new byte[newSize];
102         System.arraycopy( buf, 0, newbuf, 0, bufSize );
103         buf = newbuf;
104     }
105 
106     public long getSize(  )
107     {
108         return size;
109     }
110 
111     @Override
112     public void write( byte[] buffer ) throws IOException
113     {
114         write( buffer, 0, buffer.length );
115     }
116 
117     @Override
118     public void write( byte[] buffer, int offset, int len )
119         throws IOException
120     {
121         if ( len == 0 )
122         {
123             return;
124         }
125 
126         expand( len );
127         System.arraycopy( buffer, offset, buf, bufSize, len );
128         bufSize += len;
129         size += len;
130     }
131 
132     @Override
133     public void write( int oneByte ) throws IOException
134     {
135         if ( bufSize == buf.length )
136         {
137             expand( 1 );
138         }
139 
140         buf[bufSize++] = (byte) oneByte;
141         size++;
142     }
143 
144     @Override
145     public void flush(  ) throws IOException
146     {
147         if ( tmpStream != null )
148         {
149             if ( bufSize > 0 )
150             {
151                 tmpStream.write( buf, 0, bufSize );
152                 bufSize = 0;
153             }
154 
155             tmpStream.flush(  );
156         }
157     }
158 
159     @Override
160     public void close(  ) throws IOException
161     {
162         flush(  );
163 
164         if ( tmpStream != null )
165         {
166             tmpStream.close(  );
167         }
168     }
169 
170     
171 
172 
173     public void destroy(  )
174     {
175         try
176         {
177             close(  );
178         }
179         catch ( Exception e )
180         {
181             
182         }
183 
184         if ( tempFile != null )
185         {
186             tempFile.delete(  );
187         }
188 
189         buf = null;
190     }
191 
192     
193 
194 
195     public InputStream getInputStream(  ) throws Exception
196     {
197         if ( tmpStream != null )
198         {
199             close(  );
200             buf = null;
201 
202             return new InternalTempFileInputStream(  );
203         }
204         else
205         {
206             return new InternalBufferInputStream(  );
207         }
208     }
209 
210     
211 
212 
213     public interface ThresholdInputStream
214     {
215         
216 
217 
218 
219         boolean isInMemory(  );
220 
221         
222 
223 
224 
225         File getTemporaryFile(  );
226     }
227 
228     
229 
230 
231     private class InternalBufferInputStream extends InputStream implements ThresholdInputStream
232     {
233         private int pos = 0;
234 
235         public boolean isInMemory(  )
236         {
237             return true;
238         }
239 
240         public File getTemporaryFile(  )
241         {
242             return null;
243         }
244 
245         @Override
246         public boolean markSupported(  )
247         {
248             return false;
249         }
250 
251         @Override
252         public int available(  )
253         {
254             return bufSize - pos;
255         }
256 
257         @Override
258         public int read(  )
259         {
260             return ( ( pos < bufSize ) && ( buf != null ) ) ? ( buf[pos++] & 0xff ) : ( -1 );
261         }
262 
263         @Override
264         public int read( byte[] b ) throws IOException
265         {
266             return read( b, 0, b.length );
267         }
268 
269         @Override
270         public int read( byte[] b, int off, int len )
271         {
272             if ( ( pos >= bufSize ) || ( buf == null ) )
273             {
274                 return -1;
275             }
276 
277             if ( ( pos + len ) > bufSize )
278             {
279                 len = ( bufSize - pos );
280             }
281 
282             System.arraycopy( buf, pos, b, off, len );
283             pos += len;
284 
285             return len;
286         }
287 
288         @Override
289         public long skip( long n )
290         {
291             if ( ( pos + n ) > bufSize )
292             {
293                 n = bufSize - pos;
294             }
295 
296             if ( n < 0 )
297             {
298                 return 0;
299             }
300 
301             pos += n;
302 
303             return n;
304         }
305 
306         @Override
307         public void close(  ) throws IOException
308         {
309             buf = null;
310         }
311     }
312 
313     
314 
315 
316     private class InternalTempFileInputStream extends FilterInputStream implements ThresholdInputStream
317     {
318         private boolean isDeleted = false;
319 
320         public InternalTempFileInputStream(  ) throws FileNotFoundException
321         {
322             super( new BufferedInputStream( new FileInputStream( tempFile ), memoryThreshold ) );
323         }
324 
325         public boolean isInMemory(  )
326         {
327             return false;
328         }
329 
330         public File getTemporaryFile(  )
331         {
332             return tempFile;
333         }
334 
335         @Override
336         public boolean markSupported(  )
337         {
338             return false;
339         }
340 
341         @Override
342         public int read(  ) throws IOException
343         {
344             int b = super.read(  );
345 
346             if ( ( b == -1 ) && !isDeleted )
347             {
348                 super.close(  );
349                 isDeleted = tempFile.delete(  );
350             }
351 
352             return b;
353         }
354 
355         @Override
356         public int read( byte[] b ) throws IOException
357         {
358             return read( b, 0, b.length );
359         }
360 
361         @Override
362         public int read( byte[] b, int off, int len ) throws IOException
363         {
364             int n = super.read( b, off, len );
365 
366             if ( ( n == -1 ) && !isDeleted )
367             {
368                 super.close(  );
369                 isDeleted = tempFile.delete(  );
370             }
371 
372             return n;
373         }
374 
375         @Override
376         public void close(  ) throws IOException
377         {
378             if ( !isDeleted )
379             {
380                 super.close(  );
381                 isDeleted = tempFile.delete(  );
382             }
383         }
384     }
385 }