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 }