1 // ========================================================================
2 // Copyright 1996-2005 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 // ========================================================================
14 package org.mortbay.resource;
15 
16 import java.io.File;
17 import java.io.IOException;
18 import java.net.JarURLConnection;
19 import java.net.URL;
20 import java.util.ArrayList;
21 import java.util.Enumeration;
22 import java.util.jar.JarEntry;
23 import java.util.jar.JarFile;
24 
25 import org.mortbay.log.Log;
26 
27 /* ------------------------------------------------------------ */
28 class JarFileResource extends JarResource
29 {
30 
31     transient JarFile _jarFile;
32     transient File _file;
33     transient String[] _list;
34     transient JarEntry _entry;
35     transient boolean _directory;
36     transient String _jarUrl;
37     transient String _path;
38     transient boolean _exists;
39 
40 
41     /* -------------------------------------------------------- */
JarFileResource(URL url)42     JarFileResource(URL url)
43     {
44         super(url);
45     }
46 
JarFileResource(URL url, boolean useCaches)47     JarFileResource(URL url, boolean useCaches)
48     {
49         super(url, useCaches);
50     }
51 
52 
53     /* ------------------------------------------------------------ */
release()54     public synchronized void release()
55     {
56         _list=null;
57         _entry=null;
58         _file=null;
59         _jarFile=null;
60         super.release();
61     }
62 
63     /* ------------------------------------------------------------ */
checkConnection()64     protected boolean checkConnection()
65     {
66         try{
67             super.checkConnection();
68         }
69         finally
70         {
71             if (_jarConnection==null)
72             {
73                 _entry=null;
74                 _file=null;
75                 _jarFile=null;
76                 _list=null;
77             }
78         }
79         return _jarFile!=null;
80     }
81 
82 
83     /* ------------------------------------------------------------ */
newConnection()84     protected void newConnection()
85         throws IOException
86     {
87         super.newConnection();
88 
89         _entry=null;
90         _file=null;
91         _jarFile=null;
92         _list=null;
93 
94         int sep = _urlString.indexOf("!/");
95         _jarUrl=_urlString.substring(0,sep+2);
96         _path=_urlString.substring(sep+2);
97         if (_path.length()==0)
98             _path=null;
99         _jarFile=_jarConnection.getJarFile();
100         _file=new File(_jarFile.getName());
101     }
102 
103 
104     /* ------------------------------------------------------------ */
105     /**
106      * Returns true if the respresenetd resource exists.
107      */
exists()108     public boolean exists()
109     {
110         if (_exists)
111             return true;
112 
113         if (_urlString.endsWith("!/"))
114         {
115 
116             String file_url=_urlString.substring(4,_urlString.length()-2);
117             try{return newResource(file_url).exists();}
118             catch(Exception e) {Log.ignore(e); return false;}
119         }
120 
121         boolean check=checkConnection();
122 
123         // Is this a root URL?
124         if (_jarUrl!=null && _path==null)
125         {
126             // Then if it exists it is a directory
127             _directory=check;
128             return true;
129         }
130         else
131         {
132             // Can we find a file for it?
133             JarFile jarFile=null;
134             if (check)
135                 // Yes
136                 jarFile=_jarFile;
137             else
138             {
139                 // No - so lets look if the root entry exists.
140                 try
141                 {
142                     JarURLConnection c=(JarURLConnection)((new URL(_jarUrl)).openConnection());
143                     c.setUseCaches(getUseCaches());
144                     jarFile=c.getJarFile();
145                 }
146                 catch(Exception e)
147                 {
148                        Log.ignore(e);
149                 }
150             }
151 
152             // Do we need to look more closely?
153             if (jarFile!=null && _entry==null && !_directory)
154             {
155                 // OK - we have a JarFile, lets look at the entries for our path
156                 Enumeration e=jarFile.entries();
157                 while(e.hasMoreElements())
158                 {
159                     JarEntry entry = (JarEntry) e.nextElement();
160                     String name=entry.getName().replace('\\','/');
161 
162                     // Do we have a match
163                     if (name.equals(_path))
164                     {
165                         _entry=entry;
166                         // Is the match a directory
167                         _directory=_path.endsWith("/");
168                         break;
169                     }
170                     else if (_path.endsWith("/"))
171                     {
172                         if (name.startsWith(_path))
173                         {
174                             _directory=true;
175                             break;
176                         }
177                     }
178                     else if (name.startsWith(_path) && name.length()>_path.length() && name.charAt(_path.length())=='/')
179                     {
180                         _directory=true;
181                         break;
182                     }
183                 }
184             }
185         }
186 
187         _exists= ( _directory || _entry!=null);
188         return _exists;
189     }
190 
191 
192     /* ------------------------------------------------------------ */
193     /**
194      * Returns true if the represented resource is a container/directory.
195      * If the resource is not a file, resources ending with "/" are
196      * considered directories.
197      */
isDirectory()198     public boolean isDirectory()
199     {
200         return _urlString.endsWith("/") || exists() && _directory;
201     }
202 
203     /* ------------------------------------------------------------ */
204     /**
205      * Returns the last modified time
206      */
lastModified()207     public long lastModified()
208     {
209         if (checkConnection() && _file!=null)
210             return _file.lastModified();
211         return -1;
212     }
213 
214     /* ------------------------------------------------------------ */
list()215     public synchronized String[] list()
216     {
217 
218         if(isDirectory() && _list==null)
219         {
220             ArrayList list = new ArrayList(32);
221 
222             checkConnection();
223 
224             JarFile jarFile=_jarFile;
225             if(jarFile==null)
226             {
227                 try
228                 {
229                     JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
230                     jc.setUseCaches(getUseCaches());
231                     jarFile=jc.getJarFile();
232                 }
233                 catch(Exception e)
234                 {
235                      Log.ignore(e);
236                 }
237             }
238 
239             Enumeration e=jarFile.entries();
240             String dir=_urlString.substring(_urlString.indexOf("!/")+2);
241             while(e.hasMoreElements())
242             {
243 
244                 JarEntry entry = (JarEntry) e.nextElement();
245                 String name=entry.getName().replace('\\','/');
246                 if(!name.startsWith(dir) || name.length()==dir.length())
247                 {
248                     continue;
249                 }
250                 String listName=name.substring(dir.length());
251                 int dash=listName.indexOf('/');
252                 if (dash>=0)
253                 {
254                     //when listing jar:file urls, you get back one
255                     //entry for the dir itself, which we ignore
256                     if (dash==0 && listName.length()==1)
257                         continue;
258                     //when listing jar:file urls, all files and
259                     //subdirs have a leading /, which we remove
260                     if (dash==0)
261                         listName=listName.substring(dash+1, listName.length());
262                     else
263                         listName=listName.substring(0,dash+1);
264 
265                     if (list.contains(listName))
266                         continue;
267                 }
268 
269                 list.add(listName);
270             }
271 
272             _list=new String[list.size()];
273             list.toArray(_list);
274         }
275         return _list;
276     }
277 
278     /* ------------------------------------------------------------ */
279     /**
280      * Return the length of the resource
281      */
length()282     public long length()
283     {
284         if (isDirectory())
285             return -1;
286 
287         if (_entry!=null)
288             return _entry.getSize();
289 
290         return -1;
291     }
292 
293     /* ------------------------------------------------------------ */
294     /** Encode according to this resource type.
295      * File URIs are not encoded.
296      * @param uri URI to encode.
297      * @return The uri unchanged.
298      */
encode(String uri)299     public String encode(String uri)
300     {
301         return uri;
302     }
303 
304 
305     /**
306      * Take a Resource that possibly might use URLConnection caching
307      * and turn it into one that doesn't.
308      * @param resource
309      * @return
310      */
getNonCachingResource(Resource resource)311     public static Resource getNonCachingResource (Resource resource)
312     {
313         if (!(resource instanceof JarFileResource))
314             return resource;
315 
316         JarFileResource oldResource = (JarFileResource)resource;
317 
318         JarFileResource newResource = new JarFileResource(oldResource.getURL(), false);
319         return newResource;
320 
321     }
322 }
323 
324 
325 
326 
327 
328 
329 
330 
331