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.zip;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.nio.charset.Charset;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Enumeration;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.zip.ZipEntry;
29  import java.util.zip.ZipFile;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.commons.vfs2.Capability;
34  import org.apache.commons.vfs2.FileName;
35  import org.apache.commons.vfs2.FileObject;
36  import org.apache.commons.vfs2.FileSystemException;
37  import org.apache.commons.vfs2.FileSystemOptions;
38  import org.apache.commons.vfs2.Selectors;
39  import org.apache.commons.vfs2.VfsLog;
40  import org.apache.commons.vfs2.provider.AbstractFileName;
41  import org.apache.commons.vfs2.provider.AbstractFileSystem;
42  import org.apache.commons.vfs2.provider.UriParser;
43  
44  /**
45   * A read-only file system for ZIP and JAR files.
46   */
47  public class ZipFileSystem extends AbstractFileSystem {
48  
49      private static final Log LOG = LogFactory.getLog(ZipFileSystem.class);
50  
51      private final File file;
52      private final Charset charset;
53      private ZipFile zipFile;
54  
55      /**
56       * Cache doesn't need to be synchronized since it is read-only.
57       */
58      private final Map<FileName, FileObject> cache = new HashMap<>();
59  
60      public ZipFileSystem(final AbstractFileName rootName, final FileObject parentLayer,
61              final FileSystemOptions fileSystemOptions) throws FileSystemException {
62          super(rootName, parentLayer, fileSystemOptions);
63  
64          // Make a local copy of the file
65          file = parentLayer.getFileSystem().replicateFile(parentLayer, Selectors.SELECT_SELF);
66          this.charset = ZipFileSystemConfigBuilder.getInstance().getCharset(fileSystemOptions);
67  
68          // Open the Zip file
69          if (!file.exists()) {
70              // Don't need to do anything
71              zipFile = null;
72              return;
73          }
74      }
75  
76      @Override
77      public void init() throws FileSystemException {
78          super.init();
79  
80          try {
81              // Build the index
82              final List<ZipFileObject> strongRef = new ArrayList<>(getZipFile().size());
83              final Enumeration<? extends ZipEntry> entries = getZipFile().entries();
84              while (entries.hasMoreElements()) {
85                  final ZipEntry entry = entries.nextElement();
86                  final AbstractFileName/../../../org/apache/commons/vfs2/provider/AbstractFileName.html#AbstractFileName">AbstractFileName name = (AbstractFileName) getFileSystemManager().resolveName(getRootName(),
87                          UriParser.encode(entry.getName()));
88  
89                  // Create the file
90                  ZipFileObject fileObj;
91                  if (entry.isDirectory() && getFileFromCache(name) != null) {
92                      fileObj = (ZipFileObject) getFileFromCache(name);
93                      fileObj.setZipEntry(entry);
94                      continue;
95                  }
96  
97                  fileObj = createZipFileObject(name, entry);
98                  putFileToCache(fileObj);
99                  strongRef.add(fileObj);
100                 fileObj.holdObject(strongRef);
101 
102                 // Make sure all ancestors exist
103                 // TODO - create these on demand
104                 ZipFileObject parent;
105                 for (AbstractFileName/../org/apache/commons/vfs2/provider/AbstractFileName.html#AbstractFileName">AbstractFileName parentName = (AbstractFileName) name
106                         .getParent(); parentName != null; fileObj = parent, parentName = (AbstractFileName) parentName
107                                 .getParent()) {
108                     // Locate the parent
109                     parent = (ZipFileObject) getFileFromCache(parentName);
110                     if (parent == null) {
111                         parent = createZipFileObject(parentName, null);
112                         putFileToCache(parent);
113                         strongRef.add(parent);
114                         parent.holdObject(strongRef);
115                     }
116 
117                     // Attach child to parent
118                     parent.attachChild(fileObj.getName());
119                 }
120             }
121         } finally {
122             closeCommunicationLink();
123         }
124     }
125 
126     protected ZipFile getZipFile() throws FileSystemException {
127         if (zipFile == null && this.file.exists()) {
128             this.zipFile = createZipFile(this.file);
129         }
130 
131         return zipFile;
132     }
133 
134     protected ZipFileObject createZipFileObject(final AbstractFileName name, final ZipEntry entry)
135             throws FileSystemException {
136         return new ZipFileObject(name, entry, this, true);
137     }
138 
139     protected ZipFile createZipFile(final File file) throws FileSystemException {
140         try {
141             return charset == null ? new ZipFile(file) : new ZipFile(file, charset);
142         } catch (final IOException ioe) {
143             throw new FileSystemException("vfs.provider.zip/open-zip-file.error", file, ioe);
144         }
145     }
146 
147     @Override
148     protected void doCloseCommunicationLink() {
149         // Release the zip file
150         try {
151             if (zipFile != null) {
152                 zipFile.close();
153                 zipFile = null;
154             }
155         } catch (final IOException e) {
156             // getLogger().warn("vfs.provider.zip/close-zip-file.error :" + file, e);
157             VfsLog.warn(getLogger(), LOG, "vfs.provider.zip/close-zip-file.error :" + file, e);
158         }
159     }
160 
161     /**
162      * Returns the capabilities of this file system.
163      */
164     @Override
165     protected void addCapabilities(final Collection<Capability> caps) {
166         caps.addAll(ZipFileProvider.capabilities);
167     }
168 
169     /**
170      * Creates a file object.
171      */
172     @Override
173     protected FileObject createFile(final AbstractFileName name) throws FileSystemException {
174         // This is only called for files which do not exist in the Zip file
175         return new ZipFileObject(name, null, this, false);
176     }
177 
178     /**
179      * Adds a file object to the cache.
180      */
181     @Override
182     protected void putFileToCache(final FileObject file) {
183         cache.put(file.getName(), file);
184     }
185 
186     protected Charset getCharset() {
187         return charset;
188     }
189 
190     /**
191      * Returns a cached file.
192      */
193     @Override
194     protected FileObject getFileFromCache(final FileName name) {
195         return cache.get(name);
196     }
197 
198     /**
199      * remove a cached file.
200      */
201     @Override
202     protected void removeFileFromCache(final FileName name) {
203         cache.remove(name);
204     }
205 
206     @Override
207     public String toString() {
208         return super.toString() + " for " + file;
209     }
210 
211     /**
212      * will be called after all file-objects closed their streams. protected void notifyAllStreamsClosed() {
213      * closeCommunicationLink(); }
214      */
215 }