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