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 /* -------------------------------------------------------- */
42 JarFileResource(URL url)
43 {
44 super(url);
45 }
46
47 JarFileResource(URL url, boolean useCaches)
48 {
49 super(url, useCaches);
50 }
51
52
53 /* ------------------------------------------------------------ */
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 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 /* ------------------------------------------------------------ */
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 */
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 */
198 public boolean isDirectory()
199 {
200 return _urlString.endsWith("/") || exists() && _directory;
201 }
202
203 /* ------------------------------------------------------------ */
204 /**
205 * Returns the last modified time
206 */
207 public long lastModified()
208 {
209 if (checkConnection() && _file!=null)
210 return _file.lastModified();
211 return -1;
212 }
213
214 /* ------------------------------------------------------------ */
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 */
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 */
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 */
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