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