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