• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18 
19 package org.eclipse.jetty.util.resource;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.net.JarURLConnection;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.util.ArrayList;
27 import java.util.Enumeration;
28 import java.util.List;
29 import java.util.jar.JarEntry;
30 import java.util.jar.JarFile;
31 
32 import org.eclipse.jetty.util.log.Log;
33 import org.eclipse.jetty.util.log.Logger;
34 
35 /* ------------------------------------------------------------ */
36 class JarFileResource extends JarResource
37 {
38     private static final Logger LOG = Log.getLogger(JarFileResource.class);
39     private JarFile _jarFile;
40     private File _file;
41     private String[] _list;
42     private JarEntry _entry;
43     private boolean _directory;
44     private String _jarUrl;
45     private String _path;
46     private boolean _exists;
47 
48     /* -------------------------------------------------------- */
JarFileResource(URL url)49     JarFileResource(URL url)
50     {
51         super(url);
52     }
53 
54     /* ------------------------------------------------------------ */
JarFileResource(URL url, boolean useCaches)55     JarFileResource(URL url, boolean useCaches)
56     {
57         super(url, useCaches);
58     }
59 
60 
61     /* ------------------------------------------------------------ */
62     @Override
release()63     public synchronized void release()
64     {
65         _list=null;
66         _entry=null;
67         _file=null;
68         //if the jvm is not doing url caching, then the JarFiles will not be cached either,
69         //and so they are safe to close
70         if (!getUseCaches())
71         {
72             if ( _jarFile != null )
73             {
74                 try
75                 {
76                     LOG.debug("Closing JarFile "+_jarFile.getName());
77                     _jarFile.close();
78                 }
79                 catch ( IOException ioe )
80                 {
81                     LOG.ignore(ioe);
82                 }
83             }
84         }
85         _jarFile=null;
86         super.release();
87     }
88 
89     /* ------------------------------------------------------------ */
90     @Override
checkConnection()91     protected boolean checkConnection()
92     {
93         try
94         {
95             super.checkConnection();
96         }
97         finally
98         {
99             if (_jarConnection==null)
100             {
101                 _entry=null;
102                 _file=null;
103                 _jarFile=null;
104                 _list=null;
105             }
106         }
107         return _jarFile!=null;
108     }
109 
110 
111     /* ------------------------------------------------------------ */
112     @Override
newConnection()113     protected synchronized void newConnection()
114         throws IOException
115     {
116         super.newConnection();
117 
118         _entry=null;
119         _file=null;
120         _jarFile=null;
121         _list=null;
122 
123         int sep = _urlString.indexOf("!/");
124         _jarUrl=_urlString.substring(0,sep+2);
125         _path=_urlString.substring(sep+2);
126         if (_path.length()==0)
127             _path=null;
128         _jarFile=_jarConnection.getJarFile();
129         _file=new File(_jarFile.getName());
130     }
131 
132 
133     /* ------------------------------------------------------------ */
134     /**
135      * Returns true if the represented resource exists.
136      */
137     @Override
exists()138     public boolean exists()
139     {
140         if (_exists)
141             return true;
142 
143         if (_urlString.endsWith("!/"))
144         {
145 
146             String file_url=_urlString.substring(4,_urlString.length()-2);
147             try{return newResource(file_url).exists();}
148             catch(Exception e) {LOG.ignore(e); return false;}
149         }
150 
151         boolean check=checkConnection();
152 
153         // Is this a root URL?
154         if (_jarUrl!=null && _path==null)
155         {
156             // Then if it exists it is a directory
157             _directory=check;
158             return true;
159         }
160         else
161         {
162             // Can we find a file for it?
163             JarFile jarFile=null;
164             if (check)
165                 // Yes
166                 jarFile=_jarFile;
167             else
168             {
169                 // No - so lets look if the root entry exists.
170                 try
171                 {
172                     JarURLConnection c=(JarURLConnection)((new URL(_jarUrl)).openConnection());
173                     c.setUseCaches(getUseCaches());
174                     jarFile=c.getJarFile();
175                 }
176                 catch(Exception e)
177                 {
178                        LOG.ignore(e);
179                 }
180             }
181 
182             // Do we need to look more closely?
183             if (jarFile!=null && _entry==null && !_directory)
184             {
185                 // OK - we have a JarFile, lets look at the entries for our path
186                 Enumeration<JarEntry> e=jarFile.entries();
187                 while(e.hasMoreElements())
188                 {
189                     JarEntry entry = (JarEntry) e.nextElement();
190                     String name=entry.getName().replace('\\','/');
191 
192                     // Do we have a match
193                     if (name.equals(_path))
194                     {
195                         _entry=entry;
196                         // Is the match a directory
197                         _directory=_path.endsWith("/");
198                         break;
199                     }
200                     else if (_path.endsWith("/"))
201                     {
202                         if (name.startsWith(_path))
203                         {
204                             _directory=true;
205                             break;
206                         }
207                     }
208                     else if (name.startsWith(_path) && name.length()>_path.length() && name.charAt(_path.length())=='/')
209                     {
210                         _directory=true;
211                         break;
212                     }
213                 }
214 
215                 if (_directory && !_urlString.endsWith("/"))
216                 {
217                     _urlString+="/";
218                     try
219                     {
220                         _url=new URL(_urlString);
221                     }
222                     catch(MalformedURLException ex)
223                     {
224                         LOG.warn(ex);
225                     }
226                 }
227             }
228         }
229 
230         _exists= ( _directory || _entry!=null);
231         return _exists;
232     }
233 
234 
235     /* ------------------------------------------------------------ */
236     /**
237      * Returns true if the represented resource is a container/directory.
238      * If the resource is not a file, resources ending with "/" are
239      * considered directories.
240      */
241     @Override
isDirectory()242     public boolean isDirectory()
243     {
244         return _urlString.endsWith("/") || exists() && _directory;
245     }
246 
247     /* ------------------------------------------------------------ */
248     /**
249      * Returns the last modified time
250      */
251     @Override
lastModified()252     public long lastModified()
253     {
254         if (checkConnection() && _file!=null)
255         {
256             if (exists() && _entry!=null)
257                 return _entry.getTime();
258             return _file.lastModified();
259         }
260         return -1;
261     }
262 
263     /* ------------------------------------------------------------ */
264     @Override
list()265     public synchronized String[] list()
266     {
267         if (isDirectory() && _list==null)
268         {
269             List<String> list = null;
270             try
271             {
272                 list = listEntries();
273             }
274             catch (Exception e)
275             {
276                 //Sun's JarURLConnection impl for jar: protocol will close a JarFile in its connect() method if
277                 //useCaches == false (eg someone called URLConnection with defaultUseCaches==true).
278                 //As their sun.net.www.protocol.jar package caches JarFiles and/or connections, we can wind up in
279                 //the situation where the JarFile we have remembered in our _jarFile member has actually been closed
280                 //by other code.
281                 //So, do one retry to drop a connection and get a fresh JarFile
282                 LOG.warn("Retrying list:"+e);
283                 LOG.debug(e);
284                 release();
285                 list = listEntries();
286             }
287 
288             if (list != null)
289             {
290                 _list=new String[list.size()];
291                 list.toArray(_list);
292             }
293         }
294         return _list;
295     }
296 
297 
298     /* ------------------------------------------------------------ */
listEntries()299     private List<String> listEntries ()
300     {
301         checkConnection();
302 
303         ArrayList<String> list = new ArrayList<String>(32);
304         JarFile jarFile=_jarFile;
305         if(jarFile==null)
306         {
307             try
308             {
309                 JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
310                 jc.setUseCaches(getUseCaches());
311                 jarFile=jc.getJarFile();
312             }
313             catch(Exception e)
314             {
315 
316                 e.printStackTrace();
317                  LOG.ignore(e);
318             }
319         }
320 
321         Enumeration<JarEntry> e=jarFile.entries();
322         String dir=_urlString.substring(_urlString.indexOf("!/")+2);
323         while(e.hasMoreElements())
324         {
325             JarEntry entry = e.nextElement();
326             String name=entry.getName().replace('\\','/');
327             if(!name.startsWith(dir) || name.length()==dir.length())
328             {
329                 continue;
330             }
331             String listName=name.substring(dir.length());
332             int dash=listName.indexOf('/');
333             if (dash>=0)
334             {
335                 //when listing jar:file urls, you get back one
336                 //entry for the dir itself, which we ignore
337                 if (dash==0 && listName.length()==1)
338                     continue;
339                 //when listing jar:file urls, all files and
340                 //subdirs have a leading /, which we remove
341                 if (dash==0)
342                     listName=listName.substring(dash+1, listName.length());
343                 else
344                     listName=listName.substring(0,dash+1);
345 
346                 if (list.contains(listName))
347                     continue;
348             }
349 
350             list.add(listName);
351         }
352 
353         return list;
354     }
355 
356 
357 
358 
359 
360     /* ------------------------------------------------------------ */
361     /**
362      * Return the length of the resource
363      */
364     @Override
length()365     public long length()
366     {
367         if (isDirectory())
368             return -1;
369 
370         if (_entry!=null)
371             return _entry.getSize();
372 
373         return -1;
374     }
375 
376     /* ------------------------------------------------------------ */
377     /** Encode according to this resource type.
378      * File URIs are not encoded.
379      * @param uri URI to encode.
380      * @return The uri unchanged.
381      */
382     @Override
encode(String uri)383     public String encode(String uri)
384     {
385         return uri;
386     }
387 
388 
389     /**
390      * Take a Resource that possibly might use URLConnection caching
391      * and turn it into one that doesn't.
392      * @param resource
393      * @return the non-caching resource
394      */
getNonCachingResource(Resource resource)395     public static Resource getNonCachingResource (Resource resource)
396     {
397         if (!(resource instanceof JarFileResource))
398             return resource;
399 
400         JarFileResource oldResource = (JarFileResource)resource;
401 
402         JarFileResource newResource = new JarFileResource(oldResource.getURL(), false);
403         return newResource;
404 
405     }
406 
407     /**
408      * Check if this jar:file: resource is contained in the
409      * named resource. Eg <code>jar:file:///a/b/c/foo.jar!/x.html</code> isContainedIn <code>file:///a/b/c/foo.jar</code>
410      * @param resource
411      * @return true if resource is contained in the named resource
412      * @throws MalformedURLException
413      */
414     @Override
isContainedIn(Resource resource)415     public boolean isContainedIn (Resource resource)
416     throws MalformedURLException
417     {
418         String string = _urlString;
419         int index = string.indexOf("!/");
420         if (index > 0)
421             string = string.substring(0,index);
422         if (string.startsWith("jar:"))
423             string = string.substring(4);
424         URL url = new URL(string);
425         return url.sameFile(resource.getURL());
426     }
427 }
428 
429 
430 
431 
432 
433 
434 
435 
436