View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.client.util;
20  
21  import java.io.BufferedInputStream;
22  import java.io.BufferedOutputStream;
23  import java.io.ByteArrayInputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileNotFoundException;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.math.BigInteger;
32  
33  import org.apache.chemistry.opencmis.commons.data.ContentStream;
34  import org.apache.chemistry.opencmis.commons.data.MutableContentStream;
35  import org.apache.chemistry.opencmis.commons.impl.IOUtils;
36  import org.apache.chemistry.opencmis.commons.impl.MimeTypes;
37  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
38  
39  /**
40   * Methods to create {@link ContentStream} objects.
41   */
42  public final class ContentStreamUtils {
43  
44      private static final String OCTETSTREAM = "application/octet-stream";
45  
46      private ContentStreamUtils() {
47      }
48  
49      // --- generic ---
50      /**
51       * Creates a content stream object for an InputStream.
52       *
53       * @param filename
54       *            name of the content stream
55       * @param length
56       *            length of the stream in bytes
57       * @param mimetype
58       *            content MIME type
59       * @param stream
60       *            the InputStream
61       *
62       * @return a {@link MutableContentStream} object
63       */
64      public static MutableContentStream createContentStream(String filename, long length, String mimetype,
65              InputStream stream) {
66          return createContentStream(filename, length < 0 ? null : BigInteger.valueOf(length), mimetype, stream);
67      }
68  
69      /**
70       * Creates a content stream object for an InputStream.
71       *
72       * @param filename
73       *            name of the content stream
74       * @param length
75       *            length of the stream in bytes
76       * @param mimetype
77       *            content MIME type
78       * @param stream
79       *            the InputStream
80       *
81       * @return a {@link MutableContentStream} object
82       */
83      public static MutableContentStream createContentStream(String filename, BigInteger length, String mimetype,
84              InputStream stream) {
85          return new ContentStreamImpl(checkFilename(filename), length, checkMIMEType(mimetype), stream);
86      }
87  
88      // --- byte arrays ---
89      /**
90       * Creates a content stream object from a byte array.
91       *
92       * The MIME type is set to "application/octet-stream".
93       *
94       * @param filename
95       *            name of the content stream
96       * @param contentBytes
97       *            content bytes
98       *
99       * @return a {@link MutableContentStream} object
100      */
101     public static MutableContentStream createByteArrayContentStream(String filename, byte[] contentBytes) {
102         return createByteArrayContentStream(filename, contentBytes, OCTETSTREAM);
103     }
104 
105     /**
106      * Creates a content stream object from a byte array.
107      *
108      * @param filename
109      *            name of the content stream
110      * @param contentBytes
111      *            the content bytes
112      * @param mimetype
113      *            content MIME type
114      *
115      * @return a {@link MutableContentStream} object
116      *
117      */
118     public static MutableContentStream createByteArrayContentStream(String filename, byte[] contentBytes,
119             String mimetype) {
120         if (contentBytes == null) {
121             return createContentStream(filename, null, mimetype, null);
122         }
123 
124         return createByteArrayContentStream(filename, contentBytes, 0, contentBytes.length, mimetype);
125     }
126 
127     /**
128      * Creates a content stream object from a byte array.
129      *
130      * @param filename
131      *            name of the content stream
132      * @param contentBytes
133      *            the content bytes
134      * @param offset
135      *            the offset in the content bytes
136      * @param length
137      *            the maximum number of bytes to read from the content bytes
138      * @param mimetype
139      *            content MIME type
140      *
141      * @return a {@link MutableContentStream} object
142      *
143      */
144     public static MutableContentStream createByteArrayContentStream(String filename, byte[] contentBytes, int offset,
145             int length, String mimetype) {
146         if (contentBytes == null) {
147             return createContentStream(filename, null, mimetype, null);
148         }
149 
150         if (offset < 0 || offset > contentBytes.length) {
151             throw new IndexOutOfBoundsException("Invalid offset!");
152         } else if (length < 0 || (offset + length) > contentBytes.length || (offset + length) < 0) {
153             throw new IndexOutOfBoundsException("Invalid length!");
154         }
155 
156         return createContentStream(filename, length, mimetype, new AutoCloseInputStream(new ByteArrayInputStream(
157                 contentBytes, offset, length)));
158     }
159 
160     // --- strings ---
161     /**
162      * Creates a content stream object from a string.
163      *
164      * The MIME type is set to "text/plain".
165      *
166      * @param filename
167      *            name of the content stream
168      * @param content
169      *            the content string
170      *
171      * @return a {@link MutableContentStream} object
172      */
173     public static MutableContentStream createTextContentStream(String filename, String content) {
174         return createTextContentStream(filename, content, "text/plain; charset=UTF-8");
175     }
176 
177     /**
178      * Creates a content stream object from a string.
179      *
180      * @param filename
181      *            name of the content stream
182      * @param content
183      *            the content string
184      * @param mimetype
185      *            content MIME type
186      *
187      * @return a {@link MutableContentStream} object
188      */
189     public static MutableContentStream createTextContentStream(String filename, String content, String mimetype) {
190         byte[] contentBytes = IOUtils.toUTF8Bytes(content);
191         return createByteArrayContentStream(filename, contentBytes, checkMIMEType(mimetype));
192     }
193 
194     // --- files ---
195     /**
196      * Creates a content stream object from file.
197      *
198      * The MIME type is guessed from the file name and the content.
199      *
200      * @param file
201      *            the file
202      *
203      * @return a {@link MutableContentStream} object
204      */
205     public static MutableContentStream createFileContentStream(File file) throws FileNotFoundException {
206         return createFileContentStream(file.getName(), file, MimeTypes.getMIMEType(file));
207     }
208 
209     /**
210      * Creates a content stream object from file.
211      *
212      * The MIME type is guessed from the file name and the content.
213      *
214      * @param filename
215      *            name of the content stream
216      * @param file
217      *            the file
218      *
219      * @return a {@link MutableContentStream} object
220      */
221     public static MutableContentStream createFileContentStream(String filename, File file) throws FileNotFoundException {
222         return createFileContentStream(filename, file, MimeTypes.getMIMEType(file));
223     }
224 
225     /**
226      * Creates a content stream object from file.
227      *
228      * @param file
229      *            the file
230      * @param mimetype
231      *            content MIME type
232      *
233      * @return a {@link MutableContentStream} object
234      */
235     public static MutableContentStream createFileContentStream(File file, String mimetype) throws FileNotFoundException {
236         return createFileContentStream(file.getName(), file, mimetype);
237     }
238 
239     /**
240      * Creates a content stream object from file.
241      *
242      * @param filename
243      *            name of the content stream
244      * @param file
245      *            the file
246      * @param mimetype
247      *            content MIME type
248      *
249      * @return a {@link MutableContentStream} object
250      */
251     public static MutableContentStream createFileContentStream(String filename, File file, String mimetype)
252             throws FileNotFoundException {
253         return createContentStream(filename, file.length(), mimetype, new AutoCloseInputStream(new BufferedInputStream(
254                 new FileInputStream(file))));
255     }
256 
257     // --- write ---
258 
259     /**
260      * Writes a content stream to an output stream.
261      * 
262      * If the content stream is {@code null} or the stream itself is
263      * {@code null}, nothing is written to the output stream. The content stream
264      * is closed at the end. The output stream remains open.
265      * 
266      * @param contentStream
267      *            the content stream, may be {@code null}
268      * @param outputStream
269      *            the output stream, not {@code null}
270      */
271     public static void writeContentStreamToOutputStream(ContentStream contentStream, OutputStream outputStream)
272             throws IOException {
273         if (outputStream == null) {
274             throw new IllegalArgumentException("Output stream is null!");
275         }
276 
277         if (contentStream == null || contentStream.getStream() == null) {
278             return;
279         }
280 
281         try {
282             IOUtils.copy(contentStream.getStream(), outputStream);
283         } finally {
284             IOUtils.closeQuietly(contentStream);
285         }
286     }
287 
288     /**
289      * Writes a content stream to a file.
290      * 
291      * If the content stream is {@code null} or the stream itself is
292      * {@code null}, the file is empty. The content stream and the file are
293      * closed at the end.
294      * 
295      * @param contentStream
296      *            the content stream, may be {@code null}
297      * @param file
298      *            the file, not {@code null}
299      */
300     public static void writeContentStreamToFile(ContentStream contentStream, File file) throws IOException {
301         if (file == null) {
302             throw new IllegalArgumentException("File is null!");
303         }
304 
305         OutputStream fileStream = new BufferedOutputStream(new FileOutputStream(file));
306         try {
307             writeContentStreamToOutputStream(contentStream, fileStream);
308             fileStream.flush();
309         } finally {
310             IOUtils.closeQuietly(fileStream);
311         }
312     }
313 
314     // --- helpers ---
315     private static String checkFilename(String filename) {
316         if (filename == null || filename.length() == 0) {
317             return "content";
318         }
319 
320         return filename;
321     }
322 
323     private static String checkMIMEType(String mimetype) {
324         if (mimetype == null) {
325             return OCTETSTREAM;
326         }
327 
328         String result = mimetype.trim();
329         if (result.length() < 3) {
330             return OCTETSTREAM;
331         }
332 
333         return result;
334     }
335 
336     // --- classes ---
337     /**
338      * InputStream that gets closed when the end of the stream is reached or the
339      * underlying stream throws an exception.
340      */
341     public static class AutoCloseInputStream extends InputStream {
342 
343         protected InputStream stream;
344 
345         public AutoCloseInputStream(InputStream in) {
346             stream = in;
347         }
348 
349         @Override
350         public int read() throws IOException {
351             if (stream != null) {
352                 int b = -1;
353 
354                 try {
355                     b = stream.read();
356                 } catch (IOException ioe) {
357                     closeQuietly();
358                     throw ioe;
359                 }
360 
361                 if (b == -1) {
362                     close();
363                 }
364 
365                 return b;
366             } else {
367                 throw new IOException("Stream is already closed!");
368             }
369         }
370 
371         @Override
372         public int read(byte[] b) throws IOException {
373             if (stream != null) {
374                 int l = -1;
375 
376                 try {
377                     l = stream.read(b);
378                 } catch (IOException ioe) {
379                     closeQuietly();
380                     throw ioe;
381                 }
382 
383                 if (l == -1) {
384                     close();
385                 }
386 
387                 return l;
388             } else {
389                 throw new IOException("Stream is already closed!");
390             }
391         }
392 
393         @Override
394         public int read(byte[] b, int off, int len) throws IOException {
395             if (stream != null) {
396                 int l = -1;
397 
398                 try {
399                     l = stream.read(b, off, len);
400                 } catch (IOException ioe) {
401                     closeQuietly();
402                     throw ioe;
403                 }
404 
405                 if (l == -1) {
406                     close();
407                 }
408 
409                 return l;
410             } else {
411                 throw new IOException("Stream is already closed!");
412             }
413         }
414 
415         @Override
416         public long skip(long n) throws IOException {
417             if (stream != null) {
418                 try {
419                     return stream.skip(n);
420                 } catch (IOException ioe) {
421                     closeQuietly();
422                     throw ioe;
423                 }
424             } else {
425                 throw new IOException("Stream is already closed!");
426             }
427         }
428 
429         @Override
430         public int available() throws IOException {
431             if (stream != null) {
432                 try {
433                     return stream.available();
434                 } catch (IOException ioe) {
435                     closeQuietly();
436                     throw ioe;
437                 }
438             } else {
439                 throw new IOException("Stream is already closed!");
440             }
441         }
442 
443         @Override
444         public void close() throws IOException {
445             if (stream != null) {
446                 try {
447                     stream.close();
448                 } catch (final IOException ioe) {
449                     throw ioe;
450                 } finally {
451                     stream = null;
452                 }
453             }
454         }
455 
456         public void closeQuietly() {
457             if (stream != null) {
458                 try {
459                     stream.close();
460                 } catch (final IOException ioe) {
461                     // ignore
462                 } finally {
463                     stream = null;
464                 }
465             }
466         }
467 
468         @Override
469         public synchronized void mark(int readlimit) {
470             if (stream != null) {
471                 stream.mark(readlimit);
472             }
473         }
474 
475         @Override
476         public synchronized void reset() throws IOException {
477             if (stream != null) {
478                 try {
479                     stream.reset();
480                 } catch (IOException ioe) {
481                     closeQuietly();
482                     throw ioe;
483                 }
484             } else {
485                 throw new IOException("Stream is already closed!");
486             }
487         }
488 
489         @Override
490         public boolean markSupported() {
491             if (stream != null) {
492                 return stream.markSupported();
493             }
494 
495             return false;
496         }
497 
498         @Override
499         protected void finalize() throws Throwable {
500             closeQuietly();
501             super.finalize();
502         }
503     }
504 }