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