1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.provider;
18
19 import org.apache.commons.vfs.FileContent;
20 import org.apache.commons.vfs.FileContentInfo;
21 import org.apache.commons.vfs.FileContentInfoFactory;
22 import org.apache.commons.vfs.FileObject;
23 import org.apache.commons.vfs.FileSystemException;
24 import org.apache.commons.vfs.RandomAccessContent;
25 import org.apache.commons.vfs.util.MonitorInputStream;
26 import org.apache.commons.vfs.util.MonitorOutputStream;
27 import org.apache.commons.vfs.util.MonitorRandomAccessContent;
28 import org.apache.commons.vfs.util.RandomAccessMode;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.security.cert.Certificate;
34 import java.util.Collections;
35 import java.util.Map;
36 import java.util.Set;
37
38 /***
39 * The content of a file.
40 *
41 * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
42 * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
43 */
44 public final class DefaultFileContent implements FileContent
45 {
46
47
48
49
50
51
52
53 static final int STATE_CLOSED = 0;
54 static final int STATE_OPENED = 1;
55
56 private final AbstractFileObject file;
57 private Map attrs;
58 private Map roAttrs;
59 private FileContentInfo fileContentInfo;
60 private final FileContentInfoFactory fileContentInfoFactory;
61
62 private final ThreadLocal threadData = new ThreadLocal();
63
64 /***
65 * open streams counter for this file
66 */
67 private int openStreams = 0;
68
69 public DefaultFileContent(final AbstractFileObject file, final FileContentInfoFactory fileContentInfoFactory)
70 {
71 this.file = file;
72 this.fileContentInfoFactory = fileContentInfoFactory;
73 }
74
75 private FileContentThreadData getThreadData()
76 {
77 FileContentThreadData data = (FileContentThreadData) this.threadData.get();
78 if (data == null)
79 {
80 data = new FileContentThreadData();
81 this.threadData.set(data);
82 }
83 return data;
84 }
85
86 void streamOpened()
87 {
88 synchronized (this)
89 {
90 openStreams++;
91 }
92 ((AbstractFileSystem) file.getFileSystem()).streamOpened();
93 }
94
95 void streamClosed()
96 {
97 synchronized (this)
98 {
99 if (openStreams > 0)
100 {
101 openStreams--;
102 if (openStreams < 1)
103 {
104 file.notifyAllStreamsClosed();
105 }
106 }
107 }
108 ((AbstractFileSystem) file.getFileSystem()).streamClosed();
109 }
110
111 /***
112 * Returns the file that this is the content of.
113 */
114 public FileObject getFile()
115 {
116 return file;
117 }
118
119 /***
120 * Returns the size of the content (in bytes).
121 */
122 public long getSize() throws FileSystemException
123 {
124
125 if (!file.getType().hasContent())
126 {
127 throw new FileSystemException("vfs.provider/get-size-not-file.error", file);
128 }
129
130
131
132
133
134
135
136 try
137 {
138
139 return file.doGetContentSize();
140 }
141 catch (final Exception exc)
142 {
143 throw new FileSystemException("vfs.provider/get-size.error", new Object[]{file}, exc);
144 }
145 }
146
147 /***
148 * Returns the last-modified timestamp.
149 */
150 public long getLastModifiedTime() throws FileSystemException
151 {
152
153
154
155
156
157
158 if (!file.getType().hasAttributes())
159 {
160 throw new FileSystemException("vfs.provider/get-last-modified-no-exist.error", file);
161 }
162 try
163 {
164 return file.doGetLastModifiedTime();
165 }
166 catch (final Exception e)
167 {
168 throw new FileSystemException("vfs.provider/get-last-modified.error", file, e);
169 }
170 }
171
172 /***
173 * Sets the last-modified timestamp.
174 */
175 public void setLastModifiedTime(final long modTime) throws FileSystemException
176 {
177
178
179
180
181
182
183 if (!file.getType().hasAttributes())
184 {
185 throw new FileSystemException("vfs.provider/set-last-modified-no-exist.error", file);
186 }
187 try
188 {
189 file.doSetLastModifiedTime(modTime);
190 }
191 catch (final Exception e)
192 {
193 throw new FileSystemException("vfs.provider/set-last-modified.error", file, e);
194 }
195 }
196
197 /***
198 * Returns a read-only map of this file's attributes.
199 */
200 public Map getAttributes() throws FileSystemException
201 {
202 if (!file.getType().hasAttributes())
203 {
204 throw new FileSystemException("vfs.provider/get-attributes-no-exist.error", file);
205 }
206 if (roAttrs == null)
207 {
208 try
209 {
210 attrs = file.doGetAttributes();
211 roAttrs = Collections.unmodifiableMap(attrs);
212 }
213 catch (final Exception e)
214 {
215 throw new FileSystemException("vfs.provider/get-attributes.error", file, e);
216 }
217 }
218 return roAttrs;
219 }
220
221 /***
222 * Lists the attributes of this file.
223 */
224 public String[] getAttributeNames() throws FileSystemException
225 {
226 getAttributes();
227 final Set names = attrs.keySet();
228 return (String[]) names.toArray(new String[names.size()]);
229 }
230
231 /***
232 * Gets the value of an attribute.
233 */
234 public Object getAttribute(final String attrName)
235 throws FileSystemException
236 {
237 getAttributes();
238 if (attrs.containsKey(attrName))
239 {
240 return attrs.get(attrName);
241 }
242 return attrs.get(attrName.toLowerCase());
243 }
244
245 /***
246 * Sets the value of an attribute.
247 */
248 public void setAttribute(final String attrName, final Object value)
249 throws FileSystemException
250 {
251 if (!file.getType().hasAttributes())
252 {
253 throw new FileSystemException("vfs.provider/set-attribute-no-exist.error", new Object[]{attrName, file});
254 }
255 try
256 {
257 file.doSetAttribute(attrName, value);
258 }
259 catch (final Exception e)
260 {
261 throw new FileSystemException("vfs.provider/set-attribute.error", new Object[]{attrName, file}, e);
262 }
263
264 if (attrs != null)
265 {
266 attrs.put(attrName, value);
267 }
268 }
269
270 /***
271 * Returns the certificates used to sign this file.
272 */
273 public Certificate[] getCertificates() throws FileSystemException
274 {
275 if (!file.exists())
276 {
277 throw new FileSystemException("vfs.provider/get-certificates-no-exist.error", file);
278 }
279
280
281
282
283
284
285
286 try
287 {
288 final Certificate[] certs = file.doGetCertificates();
289 if (certs != null)
290 {
291 return certs;
292 }
293 else
294 {
295 return new Certificate[0];
296 }
297 }
298 catch (final Exception e)
299 {
300 throw new FileSystemException("vfs.provider/get-certificates.error", file, e);
301 }
302 }
303
304 /***
305 * Returns an input stream for reading the content.
306 */
307 public InputStream getInputStream() throws FileSystemException
308 {
309
310
311
312
313
314
315
316
317 final InputStream instr = file.getInputStream();
318 final InputStream wrappedInstr = new FileContentInputStream(file, instr);
319
320 this.getThreadData().addInstr(wrappedInstr);
321 streamOpened();
322
323
324 return wrappedInstr;
325 }
326
327 /***
328 * Returns an input/output stream to use to read and write the content of the file in an
329 * random manner.
330 */
331 public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException
332 {
333
334
335
336
337
338
339
340
341 final RandomAccessContent rastr = file.getRandomAccessContent(mode);
342
343 FileRandomAccessContent rac = new FileRandomAccessContent(file, rastr);
344 this.getThreadData().addRastr(rac);
345 streamOpened();
346
347
348 return rac;
349 }
350
351 /***
352 * Returns an output stream for writing the content.
353 */
354 public OutputStream getOutputStream() throws FileSystemException
355 {
356 return getOutputStream(false);
357 }
358
359 /***
360 * Returns an output stream for writing the content in append mode.
361 */
362 public OutputStream getOutputStream(boolean bAppend) throws FileSystemException
363 {
364
365
366
367 if (this.getThreadData().getOutstr() != null)
368 {
369 throw new FileSystemException("vfs.provider/write-in-use.error", file);
370 }
371
372
373 final OutputStream outstr = file.getOutputStream(bAppend);
374
375
376 this.getThreadData().setOutstr(new FileContentOutputStream(file, outstr));
377 streamOpened();
378
379
380 return this.getThreadData().getOutstr();
381 }
382
383 /***
384 * Closes all resources used by the content, including all streams, readers
385 * and writers.
386 */
387 public void close() throws FileSystemException
388 {
389 try
390 {
391
392 while (getThreadData().getInstrsSize() > 0)
393 {
394 final FileContentInputStream instr = (FileContentInputStream) getThreadData().removeInstr(0);
395 instr.close();
396 }
397
398
399 while (getThreadData().getRastrsSize() > 0)
400 {
401 final RandomAccessContent ra = (RandomAccessContent) getThreadData().removeRastr(0);
402 try
403 {
404 ra.close();
405 }
406 catch (IOException e)
407 {
408 throw new FileSystemException(e);
409 }
410 }
411
412
413 if (this.getThreadData().getOutstr() != null)
414 {
415 this.getThreadData().closeOutstr();
416 }
417 }
418 finally
419 {
420 threadData.set(null);
421 }
422 }
423
424 /***
425 * Handles the end of input stream.
426 */
427 private void endInput(final FileContentInputStream instr)
428 {
429 getThreadData().removeInstr(instr);
430 streamClosed();
431
432
433
434
435
436
437 }
438
439 /***
440 * Handles the end of random access.
441 */
442 private void endRandomAccess(RandomAccessContent rac)
443 {
444 getThreadData().removeRastr(rac);
445 streamClosed();
446
447 }
448
449 /***
450 * Handles the end of output stream.
451 */
452 private void endOutput() throws Exception
453 {
454 streamClosed();
455
456 this.getThreadData().setOutstr(null);
457
458
459 file.endOutput();
460 }
461
462
463
464
465
466
467
468
469 /***
470 * check if a input and/or output stream is open.<br />
471 * This checks only the scope of the current thread.
472 *
473 * @return true if this is the case
474 */
475 public boolean isOpen()
476 {
477
478 return getThreadData().hasStreams();
479 }
480
481 /***
482 * check if a input and/or output stream is open.<br />
483 * This checks all threads.
484 *
485 * @return true if this is the case
486 */
487 public boolean isOpenGlobal()
488 {
489 synchronized (this)
490 {
491 return openStreams > 0;
492 }
493 }
494
495 /***
496 * An input stream for reading content. Provides buffering, and
497 * end-of-stream monitoring.
498 */
499 private final class FileContentInputStream
500 extends MonitorInputStream
501 {
502
503 private final FileObject file;
504
505 FileContentInputStream(final FileObject file, final InputStream instr)
506 {
507 super(instr);
508 this.file = file;
509 }
510
511 /***
512 * Closes this input stream.
513 */
514 public void close() throws FileSystemException
515 {
516 try
517 {
518 super.close();
519 }
520 catch (final IOException e)
521 {
522 throw new FileSystemException("vfs.provider/close-instr.error", file, e);
523 }
524 }
525
526 /***
527 * Called after the stream has been closed.
528 */
529 protected void onClose() throws IOException
530 {
531 try
532 {
533 super.onClose();
534 }
535 finally
536 {
537 endInput(this);
538 }
539 }
540 }
541
542 /***
543 * An input/output stream for reading/writing content on random positions
544 */
545 private final class FileRandomAccessContent extends MonitorRandomAccessContent
546 {
547
548 private final FileObject file;
549 private final RandomAccessContent content;
550
551 FileRandomAccessContent(final FileObject file, final RandomAccessContent content)
552 {
553 super(content);
554 this.file = file;
555 this.content = content;
556 }
557
558 /***
559 * Called after the stream has been closed.
560 */
561 protected void onClose() throws IOException
562 {
563 try
564 {
565 super.onClose();
566 }
567 finally
568 {
569 endRandomAccess(content);
570 }
571 }
572 }
573
574 /***
575 * An output stream for writing content.
576 */
577 final class FileContentOutputStream extends MonitorOutputStream
578 {
579
580 private final FileObject file;
581
582 FileContentOutputStream(final FileObject file, final OutputStream outstr)
583 {
584 super(outstr);
585 this.file = file;
586 }
587
588 /***
589 * Closes this output stream.
590 */
591 public void close() throws FileSystemException
592 {
593 try
594 {
595 super.close();
596 }
597 catch (final IOException e)
598 {
599 throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
600 }
601 }
602
603 /***
604 * Called after this stream is closed.
605 */
606 protected void onClose() throws IOException
607 {
608 try
609 {
610 super.onClose();
611 }
612 finally
613 {
614 try
615 {
616 endOutput();
617 }
618 catch (Exception e)
619 {
620 throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
621 }
622 }
623 }
624 }
625
626 /***
627 * get the content info. e.g. content-type, content-encoding
628 */
629 public FileContentInfo getContentInfo() throws FileSystemException
630 {
631 if (fileContentInfo == null)
632 {
633 fileContentInfo = fileContentInfoFactory.create(this);
634 }
635
636 return fileContentInfo;
637 }
638 }