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