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, final FileObject parentLayer,
56              final FileSystemOptions fileSystemOptions) throws FileSystemException {
57          super(rootName, parentLayer, fileSystemOptions);
58  
59          // Make a local copy of the file
60          file = parentLayer.getFileSystem().replicateFile(parentLayer, Selectors.SELECT_SELF);
61  
62          // Open the Tar file
63          if (!file.exists()) {
64              // Don't need to do anything
65              tarFile = null;
66              return;
67          }
68  
69          // tarFile = createTarFile(this.file);
70      }
71  
72      @Override
73      public void init() throws FileSystemException {
74          super.init();
75  
76          // Build the index
77          try {
78              final List<TarFileObject> strongRef = new ArrayList<>(DEFAULT_INDEX_SIZE);
79              TarArchiveEntry entry;
80              while ((entry = getTarFile().getNextTarEntry()) != null) {
81                  final AbstractFileName/../../../org/apache/commons/vfs2/provider/AbstractFileName.html#AbstractFileName">AbstractFileName name = (AbstractFileName) getFileSystemManager().resolveName(getRootName(),
82                          UriParser.encode(entry.getName()));
83  
84                  // Create the file
85                  TarFileObject fileObj;
86                  if (entry.isDirectory() && getFileFromCache(name) != null) {
87                      fileObj = (TarFileObject) getFileFromCache(name);
88                      fileObj.setTarEntry(entry);
89                      continue;
90                  }
91  
92                  fileObj = createTarFileObject(name, entry);
93                  putFileToCache(fileObj);
94                  strongRef.add(fileObj);
95                  fileObj.holdObject(strongRef);
96  
97                  // Make sure all ancestors exist
98                  // TODO - create these on demand
99                  TarFileObject parent = null;
100                 for (AbstractFileName/../org/apache/commons/vfs2/provider/AbstractFileName.html#AbstractFileName">AbstractFileName parentName = (AbstractFileName) name
101                         .getParent(); parentName != null; fileObj = parent, parentName = (AbstractFileName) parentName
102                                 .getParent()) {
103                     // Locate the parent
104                     parent = (TarFileObject) getFileFromCache(parentName);
105                     if (parent == null) {
106                         parent = createTarFileObject(parentName, null);
107                         putFileToCache(parent);
108                         strongRef.add(parent);
109                         parent.holdObject(strongRef);
110                     }
111 
112                     // Attach child to parent
113                     parent.attachChild(fileObj.getName());
114                 }
115             }
116         } catch (final IOException e) {
117             throw new FileSystemException(e);
118         } finally {
119             closeCommunicationLink();
120         }
121     }
122 
123     public InputStream getInputStream(final TarArchiveEntry entry) throws FileSystemException {
124         resetTarFile();
125         try {
126             while (!tarFile.getNextEntry().equals(entry)) {
127             }
128             return tarFile;
129         } catch (final IOException e) {
130             throw new FileSystemException(e);
131         }
132     }
133 
134     protected void resetTarFile() throws FileSystemException {
135         // Reading specific entries requires skipping through the tar file from the beginning
136         // Not especially elegant, but we don't have the ability to seek to specific positions
137         // with an input stream.
138         if (this.file.exists()) {
139             recreateTarFile();
140         }
141     }
142 
143     private void recreateTarFile() throws FileSystemException {
144         if (this.tarFile != null) {
145             try {
146                 this.tarFile.close();
147             } catch (final IOException e) {
148                 throw new FileSystemException("vfs.provider.tar/close-tar-file.error", file, e);
149             }
150             tarFile = null;
151         }
152         final TarArchiveInputStream tarFile = createTarFile(this.file);
153         this.tarFile = tarFile;
154     }
155 
156     protected TarArchiveInputStream getTarFile() throws FileSystemException {
157         if (tarFile == null && this.file.exists()) {
158             recreateTarFile();
159         }
160 
161         return tarFile;
162     }
163 
164     protected TarFileObject createTarFileObject(final AbstractFileName name, final TarArchiveEntry entry)
165             throws FileSystemException {
166         return new TarFileObject(name, entry, this, true);
167     }
168 
169     protected TarArchiveInputStream createTarFile(final File file) throws FileSystemException {
170         try {
171             if ("tgz".equalsIgnoreCase(getRootName().getScheme())) {
172                 return new TarArchiveInputStream(new GZIPInputStream(new FileInputStream(file)));
173             } else if ("tbz2".equalsIgnoreCase(getRootName().getScheme())) {
174                 return new TarArchiveInputStream(
175                         Bzip2FileObject.wrapInputStream(file.getAbsolutePath(), new FileInputStream(file)));
176             }
177             return new TarArchiveInputStream(new FileInputStream(file));
178         } catch (final IOException ioe) {
179             throw new FileSystemException("vfs.provider.tar/open-tar-file.error", file, ioe);
180         }
181     }
182 
183     @Override
184     protected void doCloseCommunicationLink() {
185         // Release the tar file
186         try {
187             if (tarFile != null) {
188                 tarFile.close();
189                 tarFile = null;
190             }
191         } catch (final IOException e) {
192             // getLogger().warn("vfs.provider.tar/close-tar-file.error :" + file, e);
193             VfsLog.warn(getLogger(), LOG, "vfs.provider.tar/close-tar-file.error :" + file, e);
194         }
195     }
196 
197     /**
198      * Returns the capabilities of this file system.
199      */
200     @Override
201     protected void addCapabilities(final Collection<Capability> caps) {
202         caps.addAll(TarFileProvider.capabilities);
203     }
204 
205     /**
206      * Creates a file object.
207      */
208     @Override
209     protected FileObject createFile(final AbstractFileName name) throws FileSystemException {
210         // This is only called for files which do not exist in the Tar file
211         return new TarFileObject(name, null, this, false);
212     }
213 
214     /**
215      * will be called after all file-objects closed their streams. protected void notifyAllStreamsClosed() {
216      * closeCommunicationLink(); }
217      */
218 }