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.vfs2.provider.tar;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.List;
26  import java.util.zip.GZIPInputStream;
27  
28  import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
29  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.commons.vfs2.Capability;
33  import org.apache.commons.vfs2.FileObject;
34  import org.apache.commons.vfs2.FileSystemException;
35  import org.apache.commons.vfs2.FileSystemOptions;
36  import org.apache.commons.vfs2.Selectors;
37  import org.apache.commons.vfs2.VfsLog;
38  import org.apache.commons.vfs2.provider.AbstractFileName;
39  import org.apache.commons.vfs2.provider.AbstractFileSystem;
40  import org.apache.commons.vfs2.provider.UriParser;
41  import org.apache.commons.vfs2.provider.bzip2.Bzip2FileObject;
42  
43  /**
44   * A read-only file system for Tar files.
45   */
46  public class TarFileSystem extends AbstractFileSystem
47  {
48      private static final int DEFAULT_INDEX_SIZE = 100;
49  
50      private static final Log LOG = LogFactory.getLog(TarFileSystem.class);
51  
52      private final File file;
53      private TarArchiveInputStream tarFile;
54  
55      protected TarFileSystem(final AbstractFileName rootName,
56                              final FileObject parentLayer,
57                              final FileSystemOptions fileSystemOptions)
58          throws FileSystemException
59      {
60          super(rootName, parentLayer, fileSystemOptions);
61  
62          // Make a local copy of the file
63          file = parentLayer.getFileSystem().replicateFile(parentLayer, Selectors.SELECT_SELF);
64  
65          // Open the Tar file
66          if (!file.exists())
67          {
68              // Don't need to do anything
69              tarFile = null;
70              return;
71          }
72  
73          // tarFile = createTarFile(this.file);
74      }
75  
76      @Override
77      public void init() throws FileSystemException
78      {
79          super.init();
80  
81          // Build the index
82          try
83          {
84              final List<TarFileObject> strongRef = new ArrayList<TarFileObject>(DEFAULT_INDEX_SIZE);
85              TarArchiveEntry entry;
86              while ((entry = getTarFile().getNextTarEntry()) != null)
87              {
88                  final AbstractFileName name = (AbstractFileName) getFileSystemManager().resolveName(getRootName(),
89                      UriParser.encode(entry.getName()));
90  
91                  // Create the file
92                  TarFileObject fileObj;
93                  if (entry.isDirectory() && getFileFromCache(name) != null)
94                  {
95                      fileObj = (TarFileObject) getFileFromCache(name);
96                      fileObj.setTarEntry(entry);
97                      continue;
98                  }
99  
100                 fileObj = createTarFileObject(name, entry);
101                 putFileToCache(fileObj);
102                 strongRef.add(fileObj);
103                 fileObj.holdObject(strongRef);
104 
105                 // Make sure all ancestors exist
106                 // TODO - create these on demand
107                 TarFileObject parent = null;
108                 for (AbstractFileName parentName = (AbstractFileName) name.getParent();
109                      parentName != null;
110                      fileObj = parent, parentName = (AbstractFileName) parentName.getParent())
111                 {
112                     // Locate the parent
113                     parent = (TarFileObject) getFileFromCache(parentName);
114                     if (parent == null)
115                     {
116                         parent = createTarFileObject(parentName, null);
117                         putFileToCache(parent);
118                         strongRef.add(parent);
119                         parent.holdObject(strongRef);
120                     }
121 
122                     // Attach child to parent
123                     parent.attachChild(fileObj.getName());
124                 }
125             }
126         }
127         catch (final IOException e)
128         {
129             throw new FileSystemException(e);
130         }
131         finally
132         {
133             closeCommunicationLink();
134         }
135     }
136 
137     public InputStream getInputStream(final TarArchiveEntry entry) throws FileSystemException
138     {
139         resetTarFile();
140         try
141         {
142             while (!tarFile.getNextEntry().equals(entry))
143             {
144             }
145             return tarFile;
146         }
147         catch (final IOException e)
148         {
149             throw new FileSystemException(e);
150         }
151     }
152 
153     protected void resetTarFile() throws FileSystemException
154     {
155         // Reading specific entries requires skipping through the tar file from the beginning
156         // Not especially elegant, but we don't have the ability to seek to specific positions
157         // with an input stream.
158         if (this.file.exists())
159         {
160             recreateTarFile();
161         }
162     }
163 
164     private void recreateTarFile() throws FileSystemException
165     {
166         if (this.tarFile != null)
167         {
168             try
169             {
170                 this.tarFile.close();
171             }
172             catch (final IOException e)
173             {
174                 throw new FileSystemException("vfs.provider.tar/close-tar-file.error", file, e);
175             }
176             tarFile = null;
177         }
178         final TarArchiveInputStream tarFile = createTarFile(this.file);
179         this.tarFile = tarFile;
180     }
181 
182     protected TarArchiveInputStream getTarFile() throws FileSystemException
183     {
184         if (tarFile == null && this.file.exists())
185         {
186             recreateTarFile();
187         }
188 
189         return tarFile;
190     }
191 
192     protected TarFileObject createTarFileObject(final AbstractFileName name,
193                                                 final TarArchiveEntry entry) throws FileSystemException
194     {
195         return new TarFileObject(name, entry, this, true);
196     }
197 
198     protected TarArchiveInputStream createTarFile(final File file) throws FileSystemException
199     {
200         try
201         {
202             if ("tgz".equalsIgnoreCase(getRootName().getScheme()))
203             {
204                 return new TarArchiveInputStream(new GZIPInputStream(new FileInputStream(file)));
205             }
206             else if ("tbz2".equalsIgnoreCase(getRootName().getScheme()))
207             {
208                 return new TarArchiveInputStream(Bzip2FileObject.wrapInputStream(file.getAbsolutePath(),
209                     new FileInputStream(file)));
210             }
211             return new TarArchiveInputStream(new FileInputStream(file));
212         }
213         catch (final IOException ioe)
214         {
215             throw new FileSystemException("vfs.provider.tar/open-tar-file.error", file, ioe);
216         }
217     }
218 
219     @Override
220     protected void doCloseCommunicationLink()
221     {
222         // Release the tar file
223         try
224         {
225             if (tarFile != null)
226             {
227                 tarFile.close();
228                 tarFile = null;
229             }
230         }
231         catch (final IOException e)
232         {
233             // getLogger().warn("vfs.provider.tar/close-tar-file.error :" + file, e);
234             VfsLog.warn(getLogger(), LOG, "vfs.provider.tar/close-tar-file.error :" + file, e);
235         }
236     }
237 
238     /**
239      * Returns the capabilities of this file system.
240      */
241     @Override
242     protected void addCapabilities(final Collection<Capability> caps)
243     {
244         caps.addAll(TarFileProvider.capabilities);
245     }
246 
247     /**
248      * Creates a file object.
249      */
250     @Override
251     protected FileObject createFile(final AbstractFileName name) throws FileSystemException
252     {
253         // This is only called for files which do not exist in the Tar file
254         return new TarFileObject(name, null, this, false);
255     }
256 
257     /**
258      * will be called after all file-objects closed their streams.
259     protected void notifyAllStreamsClosed()
260     {
261         closeCommunicationLink();
262     }
263      */
264 }