1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider;
18
19 import java.io.BufferedInputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.security.cert.Certificate;
24 import java.util.Collections;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.commons.lang3.ArrayUtils;
29 import org.apache.commons.vfs2.FileContent;
30 import org.apache.commons.vfs2.FileContentInfo;
31 import org.apache.commons.vfs2.FileContentInfoFactory;
32 import org.apache.commons.vfs2.FileObject;
33 import org.apache.commons.vfs2.FileSystemException;
34 import org.apache.commons.vfs2.RandomAccessContent;
35 import org.apache.commons.vfs2.util.MonitorInputStream;
36 import org.apache.commons.vfs2.util.MonitorOutputStream;
37 import org.apache.commons.vfs2.util.MonitorRandomAccessContent;
38 import org.apache.commons.vfs2.util.RandomAccessMode;
39 import org.apache.commons.vfs2.util.RawMonitorInputStream;
40
41
42
43
44 public final class DefaultFileContent implements FileContent {
45
46
47
48
49
50
51
52
53
54 private final class FileContentInputStream extends MonitorInputStream {
55
56 private final FileObject file;
57
58 FileContentInputStream(final FileObject file, final InputStream instr) {
59 super(instr);
60 this.file = file;
61 }
62
63 FileContentInputStream(final FileObject file, final InputStream instr, final int bufferSize) {
64 super(instr, bufferSize);
65 this.file = file;
66 }
67
68
69
70
71 @Override
72 public void close() throws FileSystemException {
73 try {
74 super.close();
75 } catch (final IOException e) {
76 throw new FileSystemException("vfs.provider/close-instr.error", file, e);
77 }
78 }
79
80
81
82
83 @Override
84 protected void onClose() throws IOException {
85 try {
86 super.onClose();
87 } finally {
88 endInput(this);
89 }
90 }
91 }
92
93
94
95 final class FileContentOutputStream extends MonitorOutputStream {
96
97 private final FileObject file;
98
99 FileContentOutputStream(final FileObject file, final OutputStream outstr) {
100 super(outstr);
101 this.file = file;
102 }
103
104 FileContentOutputStream(final FileObject file, final OutputStream outstr, final int bufferSize) {
105 super(outstr, bufferSize);
106 this.file = file;
107 }
108
109
110
111
112 @Override
113 public void close() throws FileSystemException {
114 try {
115 super.close();
116 } catch (final IOException e) {
117 throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
118 }
119 }
120
121
122
123
124 @Override
125 protected void onClose() throws IOException {
126 try {
127 super.onClose();
128 } finally {
129 try {
130 endOutput();
131 } catch (final Exception e) {
132 throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
133 }
134 }
135 }
136 }
137
138
139
140 private final class FileRandomAccessContent extends MonitorRandomAccessContent {
141
142 private final FileObject file;
143
144 FileRandomAccessContent(final FileObject file, final RandomAccessContent content) {
145 super(content);
146 this.file = file;
147 }
148
149 @Override
150 public void close() throws FileSystemException {
151 try {
152 super.close();
153 } catch (final IOException e) {
154 throw new FileSystemException("vfs.provider/close-rac.error", file, e);
155 }
156 }
157
158
159
160
161 @Override
162 protected void onClose() throws IOException {
163 try {
164 super.onClose();
165 } finally {
166 endRandomAccess(this);
167 }
168 }
169 }
170
171
172
173
174
175
176
177 private final class RawFileContentInputStream extends RawMonitorInputStream {
178
179 private final FileObject file;
180
181 RawFileContentInputStream(final FileObject file, final InputStream instr) {
182 super(instr);
183 this.file = file;
184 }
185
186
187
188
189 @Override
190 public void close() throws FileSystemException {
191 try {
192 super.close();
193 } catch (final IOException e) {
194 throw new FileSystemException("vfs.provider/close-instr.error", file, e);
195 }
196 }
197
198
199
200
201 @Override
202 protected void onClose() throws IOException {
203 try {
204 super.onClose();
205 } finally {
206 endInput(this);
207 }
208 }
209 }
210
211 static final int STATE_CLOSED = 0;
212 static final int STATE_OPENED = 1;
213
214 private static final Certificate[] EMPTY_CERTIFICATE_ARRAY = {};
215
216
217
218
219 private static final int WRITE_BUFFER_SIZE = 4096;
220 private final AbstractFileObject<?> fileObject;
221
222 private Map<String, Object> attrs;
223 private Map<String, Object> roAttrs;
224
225 private FileContentInfo fileContentInfo;
226
227 private final FileContentInfoFactory fileContentInfoFactory;
228
229 private final ThreadLocal<FileContentThreadData> threadLocal = ThreadLocal.withInitial(FileContentThreadData::new);
230
231 private boolean resetAttributes;
232
233
234
235
236 private int openStreams;
237
238
239
240
241
242
243
244 public DefaultFileContent(final AbstractFileObject fileObject, final FileContentInfoFactory fileContentInfoFactory) {
245 this.fileObject = fileObject;
246 this.fileContentInfoFactory = fileContentInfoFactory;
247 }
248
249 private InputStream buildInputStream(final int bufferSize) throws FileSystemException {
250
251
252
253
254
255
256 final InputStream inputStream = bufferSize == 0
257 ? fileObject.getInputStream()
258 : fileObject.getInputStream(bufferSize);
259
260
261
262
263
264
265 final InputStream wrappedInputStream;
266 if (inputStream instanceof BufferedInputStream) {
267
268 wrappedInputStream = new RawFileContentInputStream(fileObject, inputStream);
269 } else {
270
271 wrappedInputStream = bufferSize == 0
272 ? new FileContentInputStream(fileObject, inputStream)
273 : new FileContentInputStream(fileObject, inputStream, bufferSize);
274
275 }
276 getFileContentThreadData().add(wrappedInputStream);
277 streamOpened();
278
279 return wrappedInputStream;
280 }
281
282 private OutputStream buildOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
283
284
285
286 final FileContentThreadData threadData = getFileContentThreadData();
287
288 if (threadData.getOutputStream() != null) {
289 throw new FileSystemException("vfs.provider/write-in-use.error", fileObject);
290 }
291
292
293 final OutputStream outstr = fileObject.getOutputStream(bAppend);
294
295
296 final FileContentOutputStream wrapped = bufferSize == 0 ?
297 new FileContentOutputStream(fileObject, outstr) :
298 new FileContentOutputStream(fileObject, outstr, bufferSize);
299 threadData.setOutputStream(wrapped);
300 streamOpened();
301
302 return wrapped;
303 }
304
305
306
307
308
309
310 @Override
311 public void close() throws FileSystemException {
312 FileSystemException caught = null;
313 try {
314 final FileContentThreadData threadData = getFileContentThreadData();
315
316
317 while (threadData.hasInputStream()) {
318 final InputStream inputStream = threadData.removeInputStream(0);
319 try {
320 if (inputStream instanceof FileContentInputStream) {
321 ((FileContentInputStream) inputStream).close();
322 } else if (inputStream instanceof RawFileContentInputStream) {
323 ((RawFileContentInputStream) inputStream).close();
324 } else {
325 caught = new FileSystemException("Unsupported InputStream type: " + inputStream);
326 }
327 } catch (final FileSystemException ex) {
328 caught = ex;
329
330 }
331 }
332
333
334 while (threadData.hasRandomAccessContent()) {
335 final FileRandomAccessContent randomAccessContent = (FileRandomAccessContent) threadData
336 .removeRandomAccessContent(0);
337 try {
338 randomAccessContent.close();
339 } catch (final FileSystemException ex) {
340 caught = ex;
341 }
342 }
343
344
345 final FileContentOutputStream outputStream = threadData.getOutputStream();
346 if (outputStream != null) {
347 threadData.setOutputStream(null);
348 try {
349 outputStream.close();
350 } catch (final FileSystemException ex) {
351 caught = ex;
352 }
353 }
354 } finally {
355 threadLocal.remove();
356 }
357
358
359 if (caught != null) {
360 throw caught;
361 }
362 }
363
364
365
366
367 private void endInput(final InputStream instr) {
368 final FileContentThreadData fileContentThreadData = threadLocal.get();
369 if (fileContentThreadData != null) {
370 fileContentThreadData.remove(instr);
371 }
372 if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
373
374 threadLocal.remove();
375 }
376 streamClosed();
377 }
378
379
380
381
382 private void endOutput() throws Exception {
383 final FileContentThreadData fileContentThreadData = threadLocal.get();
384 if (fileContentThreadData != null) {
385 fileContentThreadData.setOutputStream(null);
386 }
387 if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
388
389 threadLocal.remove();
390 }
391 streamClosed();
392 fileObject.endOutput();
393 }
394
395
396
397
398 private void endRandomAccess(final RandomAccessContent rac) {
399 final FileContentThreadData fileContentThreadData = threadLocal.get();
400 if (fileContentThreadData != null) {
401 fileContentThreadData.remove(rac);
402 }
403 if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
404
405 threadLocal.remove();
406 }
407 streamClosed();
408 }
409
410
411
412
413
414
415
416
417 @Override
418 public Object getAttribute(final String attrName) throws FileSystemException {
419 getAttributes();
420 return attrs.get(attrName);
421 }
422
423
424
425
426
427
428
429 @Override
430 public String[] getAttributeNames() throws FileSystemException {
431 getAttributes();
432 final Set<String> names = attrs.keySet();
433 return names.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
434 }
435
436
437
438
439
440
441
442 @Override
443 public Map<String, Object> getAttributes() throws FileSystemException {
444 if (!fileObject.getType().hasAttributes()) {
445 throw new FileSystemException("vfs.provider/get-attributes-no-exist.error", fileObject);
446 }
447 if (resetAttributes || roAttrs == null) {
448 try {
449 synchronized (this) {
450 attrs = fileObject.doGetAttributes();
451 roAttrs = Collections.unmodifiableMap(attrs);
452 resetAttributes = false;
453 }
454 } catch (final Exception e) {
455 throw new FileSystemException("vfs.provider/get-attributes.error", fileObject, e);
456 }
457 }
458 return roAttrs;
459 }
460
461
462
463
464
465
466
467 @Override
468 public Certificate[] getCertificates() throws FileSystemException {
469 if (!fileObject.exists()) {
470 throw new FileSystemException("vfs.provider/get-certificates-no-exist.error", fileObject);
471 }
472
473
474
475
476
477 try {
478 final Certificate[] certs = fileObject.doGetCertificates();
479 if (certs != null) {
480 return certs;
481 }
482 return EMPTY_CERTIFICATE_ARRAY;
483 } catch (final Exception e) {
484 throw new FileSystemException("vfs.provider/get-certificates.error", fileObject, e);
485 }
486 }
487
488
489
490
491
492
493
494 @Override
495 public FileContentInfo getContentInfo() throws FileSystemException {
496 if (fileContentInfo == null) {
497 fileContentInfo = fileContentInfoFactory.create(this);
498 }
499
500 return fileContentInfo;
501 }
502
503
504
505
506
507
508 @Override
509 public FileObject getFile() {
510 return fileObject;
511 }
512
513 private FileContentThreadData getFileContentThreadData() {
514 return threadLocal.get();
515 }
516
517
518
519
520
521
522
523 @Override
524 public InputStream getInputStream() throws FileSystemException {
525 return buildInputStream(0);
526 }
527
528
529
530
531
532
533
534
535
536 @Override
537 public InputStream getInputStream(final int bufferSize) throws FileSystemException {
538 return buildInputStream(bufferSize);
539 }
540
541
542
543
544
545
546
547 @Override
548 public long getLastModifiedTime() throws FileSystemException {
549
550
551
552
553 if (!fileObject.getType().hasAttributes()) {
554 throw new FileSystemException("vfs.provider/get-last-modified-no-exist.error", fileObject);
555 }
556 try {
557 return fileObject.doGetLastModifiedTime();
558 } catch (final Exception e) {
559 throw new FileSystemException("vfs.provider/get-last-modified.error", fileObject, e);
560 }
561 }
562
563
564
565
566
567
568
569 @Override
570 public OutputStream getOutputStream() throws FileSystemException {
571 return getOutputStream(false);
572 }
573
574
575
576
577
578
579
580
581 @Override
582 public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException {
583 return buildOutputStream(bAppend, 0);
584 }
585
586
587
588
589
590
591
592
593
594
595 @Override
596 public OutputStream getOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
597 return buildOutputStream(bAppend, bufferSize);
598 }
599
600
601
602
603
604
605
606
607
608 @Override
609 public OutputStream getOutputStream(final int bufferSize) throws FileSystemException {
610 return buildOutputStream(false, bufferSize);
611 }
612
613
614
615
616
617
618
619
620 @Override
621 public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException {
622
623
624
625
626
627
628 final RandomAccessContent rastr = fileObject.getRandomAccessContent(mode);
629
630 final FileRandomAccessContent rac = new FileRandomAccessContent(fileObject, rastr);
631
632 getFileContentThreadData().add(rac);
633 streamOpened();
634
635 return rac;
636 }
637
638
639
640
641
642
643
644 @Override
645 public long getSize() throws FileSystemException {
646
647 if (!fileObject.getType().hasContent()) {
648 throw new FileSystemException("vfs.provider/get-size-not-file.error", fileObject);
649 }
650
651
652
653
654
655 try {
656
657 return fileObject.doGetContentSize();
658 } catch (final Exception exc) {
659 throw new FileSystemException("vfs.provider/get-size.error", exc, fileObject);
660 }
661 }
662
663
664
665
666
667
668
669
670
671 @Override
672 public boolean hasAttribute(final String attrName) throws FileSystemException {
673 if (!fileObject.getType().hasAttributes()) {
674 throw new FileSystemException("vfs.provider/exists-attributes-no-exist.error", fileObject);
675 }
676 getAttributes();
677 return attrs.containsKey(attrName);
678 }
679
680
681
682
683
684
685
686
687
688 @Override
689 public boolean isOpen() {
690 final FileContentThreadData fileContentThreadData = threadLocal.get();
691 if (fileContentThreadData != null && fileContentThreadData.hasStreams()) {
692 return true;
693 }
694
695 threadLocal.remove();
696 return false;
697 }
698
699
700
701
702
703
704 public boolean isOpenGlobal() {
705 synchronized (this) {
706 return openStreams > 0;
707 }
708 }
709
710
711
712
713
714
715
716
717 @Override
718 public void removeAttribute(final String attrName) throws FileSystemException {
719 if (!fileObject.getType().hasAttributes()) {
720 throw new FileSystemException("vfs.provider/remove-attribute-no-exist.error", fileObject);
721 }
722
723 try {
724 fileObject.doRemoveAttribute(attrName);
725 } catch (final Exception e) {
726 throw new FileSystemException("vfs.provider/remove-attribute.error", e, attrName, fileObject);
727 }
728
729 if (attrs != null) {
730 attrs.remove(attrName);
731 }
732 }
733
734
735
736
737
738
739 public void resetAttributes() {
740 resetAttributes = true;
741 }
742
743
744
745
746
747
748
749
750 @Override
751 public void setAttribute(final String attrName, final Object value) throws FileSystemException {
752 if (!fileObject.getType().hasAttributes()) {
753 throw new FileSystemException("vfs.provider/set-attribute-no-exist.error", attrName, fileObject);
754 }
755 try {
756 fileObject.doSetAttribute(attrName, value);
757 } catch (final Exception e) {
758 throw new FileSystemException("vfs.provider/set-attribute.error", e, attrName, fileObject);
759 }
760
761 if (attrs != null) {
762 attrs.put(attrName, value);
763 }
764 }
765
766
767
768
769
770
771
772 @Override
773 public void setLastModifiedTime(final long modTime) throws FileSystemException {
774
775
776
777
778 if (!fileObject.getType().hasAttributes()) {
779 throw new FileSystemException("vfs.provider/set-last-modified-no-exist.error", fileObject);
780 }
781 try {
782 if (!fileObject.doSetLastModifiedTime(modTime)) {
783 throw new FileSystemException("vfs.provider/set-last-modified.error", fileObject);
784 }
785 } catch (final Exception e) {
786 throw new FileSystemException("vfs.provider/set-last-modified.error", fileObject, e);
787 }
788 }
789
790 void streamClosed() {
791 synchronized (this) {
792 if (openStreams > 0) {
793 openStreams--;
794 if (openStreams < 1) {
795 fileObject.notifyAllStreamsClosed();
796 }
797 }
798 }
799 ((AbstractFileSystem) fileObject.getFileSystem()).streamClosed();
800 }
801
802 void streamOpened() {
803 synchronized (this) {
804 openStreams++;
805 }
806 ((AbstractFileSystem) fileObject.getFileSystem()).streamOpened();
807 }
808
809
810
811
812
813
814
815
816
817 @Override
818 public long write(final FileContent fileContent) throws IOException {
819 try (OutputStream output = fileContent.getOutputStream()) {
820 return write(output);
821 }
822 }
823
824
825
826
827
828
829
830
831
832 @Override
833 public long write(final FileObject file) throws IOException {
834 return write(file.getContent());
835 }
836
837
838
839
840
841
842
843
844
845 @Override
846 public long write(final OutputStream output) throws IOException {
847 return write(output, WRITE_BUFFER_SIZE);
848 }
849
850
851
852
853
854
855
856
857
858
859 @Override
860 public long write(final OutputStream output, final int bufferSize) throws IOException {
861 final InputStream input = getInputStream();
862 long count = 0;
863 try {
864
865 final byte[] buffer = new byte[bufferSize];
866 int n;
867 while (-1 != (n = input.read(buffer))) {
868 output.write(buffer, 0, n);
869 count += n;
870 }
871 } finally {
872 input.close();
873 }
874 return count;
875 }
876 }