View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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      static final int STATE_NONE = 0;
48      static final int STATE_READING = 1;
49      static final int STATE_WRITING = 2;
50      static final int STATE_RANDOM_ACCESS = 3;
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         // Do some checking
125         if (!file.getType().hasContent())
126         {
127             throw new FileSystemException("vfs.provider/get-size-not-file.error", file);
128         }
129         /*
130         if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS)
131         {
132             throw new FileSystemException("vfs.provider/get-size-write.error", file);
133         }
134         */
135 
136         try
137         {
138             // Get the size
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         if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS)
154         {
155             throw new FileSystemException("vfs.provider/get-last-modified-writing.error", file);
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         if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS)
179         {
180             throw new FileSystemException("vfs.provider/set-last-modified-writing.error", file);
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         if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS)
281         {
282             throw new FileSystemException("vfs.provider/get-certificates-writing.error", file);
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         if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS)
311         {
312             throw new FileSystemException("vfs.provider/read-in-use.error", file);
313         }
314         */
315 
316         // Get the raw input stream
317         final InputStream instr = file.getInputStream();
318         final InputStream wrappedInstr = new FileContentInputStream(file, instr);
319 
320         this.getThreadData().addInstr(wrappedInstr);
321         streamOpened();
322 
323         // setState(STATE_OPENED);
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         if (getThreadData().getState() != STATE_NONE)
335         {
336             throw new FileSystemException("vfs.provider/read-in-use.error", file);
337         }
338         */
339 
340         // Get the content
341         final RandomAccessContent rastr = file.getRandomAccessContent(mode);
342 
343 		FileRandomAccessContent rac = new FileRandomAccessContent(file, rastr);
344 		this.getThreadData().addRastr(rac);
345         streamOpened();
346 
347         // setState(STATE_OPENED);
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         if (getThreadData().getState() != STATE_NONE)
366         */
367         if (this.getThreadData().getOutstr() != null)
368         {
369             throw new FileSystemException("vfs.provider/write-in-use.error", file);
370         }
371 
372         // Get the raw output stream
373         final OutputStream outstr = file.getOutputStream(bAppend);
374 
375         // Create wrapper
376         this.getThreadData().setOutstr(new FileContentOutputStream(file, outstr));
377         streamOpened();
378 
379         // setState(STATE_OPENED);
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             // Close the input stream
392             while (getThreadData().getInstrsSize() > 0)
393             {
394                 final FileContentInputStream instr = (FileContentInputStream) getThreadData().removeInstr(0);
395                 instr.close();
396             }
397 
398 			// Close the randomAccess stream
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 			// Close the output stream
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         if (!getThreadData().hasStreams())
433         {
434             setState(STATE_CLOSED);
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         // setState(STATE_CLOSED);
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         // setState(STATE_CLOSED);
458 
459         file.endOutput();
460     }
461 
462     /*
463     private void setState(int state)
464     {
465         getThreadData().setState(state);
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         // return getThreadData().getState() == STATE_OPENED;
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         // avoid gc
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         // avoid gc
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         // avoid gc
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 }