1 /* 2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.net.www.protocol.jar; 27 28 import java.io.InputStream; 29 import java.io.IOException; 30 import java.io.FileNotFoundException; 31 import java.io.BufferedInputStream; 32 import java.net.URL; 33 import java.net.URLConnection; 34 import java.net.MalformedURLException; 35 import java.net.UnknownServiceException; 36 import java.util.Enumeration; 37 import java.util.Map; 38 import java.util.List; 39 import java.util.jar.JarEntry; 40 import java.util.jar.JarFile; 41 import java.util.jar.Manifest; 42 import java.security.Permission; 43 44 /** 45 * @author Benjamin Renaud 46 * @since 1.2 47 */ 48 public class JarURLConnection extends java.net.JarURLConnection { 49 50 private static final boolean debug = false; 51 52 /* the Jar file factory. It handles both retrieval and caching. 53 */ 54 private static final JarFileFactory factory = JarFileFactory.getInstance(); 55 56 /* the url for the Jar file */ 57 private URL jarFileURL; 58 59 /* the permission to get this JAR file. This is the actual, ultimate, 60 * permission, returned by the jar file factory. 61 */ 62 private Permission permission; 63 64 /* the url connection for the JAR file */ 65 private URLConnection jarFileURLConnection; 66 67 /* the entry name, if any */ 68 private String entryName; 69 70 /* the JarEntry */ 71 private JarEntry jarEntry; 72 73 /* the jar file corresponding to this connection */ 74 private JarFile jarFile; 75 76 /* the content type for this connection */ 77 private String contentType; 78 JarURLConnection(URL url, Handler handler)79 public JarURLConnection(URL url, Handler handler) 80 throws MalformedURLException, IOException { 81 super(url); 82 83 jarFileURL = getJarFileURL(); 84 jarFileURLConnection = jarFileURL.openConnection(); 85 entryName = getEntryName(); 86 } 87 getJarFile()88 public JarFile getJarFile() throws IOException { 89 connect(); 90 return jarFile; 91 } 92 getJarEntry()93 public JarEntry getJarEntry() throws IOException { 94 connect(); 95 return jarEntry; 96 } 97 getPermission()98 public Permission getPermission() throws IOException { 99 return jarFileURLConnection.getPermission(); 100 } 101 102 class JarURLInputStream extends java.io.FilterInputStream { JarURLInputStream(InputStream src)103 JarURLInputStream (InputStream src) { 104 super (src); 105 } close()106 public void close () throws IOException { 107 try { 108 super.close(); 109 } finally { 110 if (!getUseCaches()) { 111 jarFile.close(); 112 } 113 } 114 } 115 } 116 117 118 connect()119 public void connect() throws IOException { 120 if (!connected) { 121 /* the factory call will do the security checks */ 122 jarFile = factory.get(getJarFileURL(), getUseCaches()); 123 124 /* we also ask the factory the permission that was required 125 * to get the jarFile, and set it as our permission. 126 */ 127 if (getUseCaches()) { 128 jarFileURLConnection = factory.getConnection(jarFile); 129 } 130 131 if ((entryName != null)) { 132 jarEntry = (JarEntry)jarFile.getEntry(entryName); 133 if (jarEntry == null) { 134 try { 135 if (!getUseCaches()) { 136 jarFile.close(); 137 } 138 } catch (Exception e) { 139 } 140 throw new FileNotFoundException("JAR entry " + entryName + 141 " not found in " + 142 jarFile.getName()); 143 } 144 } 145 connected = true; 146 } 147 } 148 getInputStream()149 public InputStream getInputStream() throws IOException { 150 connect(); 151 152 InputStream result = null; 153 154 if (entryName == null) { 155 throw new IOException("no entry name specified"); 156 } else { 157 if (jarEntry == null) { 158 throw new FileNotFoundException("JAR entry " + entryName + 159 " not found in " + 160 jarFile.getName()); 161 } 162 result = new JarURLInputStream (jarFile.getInputStream(jarEntry)); 163 } 164 return result; 165 } 166 getContentLength()167 public int getContentLength() { 168 long result = getContentLengthLong(); 169 if (result > Integer.MAX_VALUE) 170 return -1; 171 return (int) result; 172 } 173 getContentLengthLong()174 public long getContentLengthLong() { 175 long result = -1; 176 try { 177 connect(); 178 if (jarEntry == null) { 179 /* if the URL referes to an archive */ 180 result = jarFileURLConnection.getContentLengthLong(); 181 } else { 182 /* if the URL referes to an archive entry */ 183 result = getJarEntry().getSize(); 184 } 185 } catch (IOException e) { 186 } 187 return result; 188 } 189 getContent()190 public Object getContent() throws IOException { 191 Object result = null; 192 193 connect(); 194 if (entryName == null) { 195 result = jarFile; 196 } else { 197 result = super.getContent(); 198 } 199 return result; 200 } 201 getContentType()202 public String getContentType() { 203 if (contentType == null) { 204 if (entryName == null) { 205 contentType = "x-java/jar"; 206 } else { 207 try { 208 connect(); 209 InputStream in = jarFile.getInputStream(jarEntry); 210 contentType = guessContentTypeFromStream( 211 new BufferedInputStream(in)); 212 in.close(); 213 } catch (IOException e) { 214 // don't do anything 215 } 216 } 217 if (contentType == null) { 218 contentType = guessContentTypeFromName(entryName); 219 } 220 if (contentType == null) { 221 contentType = "content/unknown"; 222 } 223 } 224 return contentType; 225 } 226 getHeaderField(String name)227 public String getHeaderField(String name) { 228 return jarFileURLConnection.getHeaderField(name); 229 } 230 231 /** 232 * Sets the general request property. 233 * 234 * @param key the keyword by which the request is known 235 * (e.g., "<code>accept</code>"). 236 * @param value the value associated with it. 237 */ setRequestProperty(String key, String value)238 public void setRequestProperty(String key, String value) { 239 jarFileURLConnection.setRequestProperty(key, value); 240 } 241 242 /** 243 * Returns the value of the named general request property for this 244 * connection. 245 * 246 * @return the value of the named general request property for this 247 * connection. 248 */ getRequestProperty(String key)249 public String getRequestProperty(String key) { 250 return jarFileURLConnection.getRequestProperty(key); 251 } 252 253 /** 254 * Adds a general request property specified by a 255 * key-value pair. This method will not overwrite 256 * existing values associated with the same key. 257 * 258 * @param key the keyword by which the request is known 259 * (e.g., "<code>accept</code>"). 260 * @param value the value associated with it. 261 */ addRequestProperty(String key, String value)262 public void addRequestProperty(String key, String value) { 263 jarFileURLConnection.addRequestProperty(key, value); 264 } 265 266 /** 267 * Returns an unmodifiable Map of general request 268 * properties for this connection. The Map keys 269 * are Strings that represent the request-header 270 * field names. Each Map value is a unmodifiable List 271 * of Strings that represents the corresponding 272 * field values. 273 * 274 * @return a Map of the general request properties for this connection. 275 */ getRequestProperties()276 public Map<String,List<String>> getRequestProperties() { 277 return jarFileURLConnection.getRequestProperties(); 278 } 279 280 /** 281 * Set the value of the <code>allowUserInteraction</code> field of 282 * this <code>URLConnection</code>. 283 * 284 * @param allowuserinteraction the new value. 285 * @see java.net.URLConnection#allowUserInteraction 286 */ setAllowUserInteraction(boolean allowuserinteraction)287 public void setAllowUserInteraction(boolean allowuserinteraction) { 288 jarFileURLConnection.setAllowUserInteraction(allowuserinteraction); 289 } 290 291 /** 292 * Returns the value of the <code>allowUserInteraction</code> field for 293 * this object. 294 * 295 * @return the value of the <code>allowUserInteraction</code> field for 296 * this object. 297 * @see java.net.URLConnection#allowUserInteraction 298 */ getAllowUserInteraction()299 public boolean getAllowUserInteraction() { 300 return jarFileURLConnection.getAllowUserInteraction(); 301 } 302 303 /* 304 * cache control 305 */ 306 307 /** 308 * Sets the value of the <code>useCaches</code> field of this 309 * <code>URLConnection</code> to the specified value. 310 * <p> 311 * Some protocols do caching of documents. Occasionally, it is important 312 * to be able to "tunnel through" and ignore the caches (e.g., the 313 * "reload" button in a browser). If the UseCaches flag on a connection 314 * is true, the connection is allowed to use whatever caches it can. 315 * If false, caches are to be ignored. 316 * The default value comes from DefaultUseCaches, which defaults to 317 * true. 318 * 319 * @see java.net.URLConnection#useCaches 320 */ setUseCaches(boolean usecaches)321 public void setUseCaches(boolean usecaches) { 322 jarFileURLConnection.setUseCaches(usecaches); 323 } 324 325 /** 326 * Returns the value of this <code>URLConnection</code>'s 327 * <code>useCaches</code> field. 328 * 329 * @return the value of this <code>URLConnection</code>'s 330 * <code>useCaches</code> field. 331 * @see java.net.URLConnection#useCaches 332 */ getUseCaches()333 public boolean getUseCaches() { 334 return jarFileURLConnection.getUseCaches(); 335 } 336 337 /** 338 * Sets the value of the <code>ifModifiedSince</code> field of 339 * this <code>URLConnection</code> to the specified value. 340 * 341 * @param value the new value. 342 * @see java.net.URLConnection#ifModifiedSince 343 */ setIfModifiedSince(long ifmodifiedsince)344 public void setIfModifiedSince(long ifmodifiedsince) { 345 jarFileURLConnection.setIfModifiedSince(ifmodifiedsince); 346 } 347 348 /** 349 * Sets the default value of the <code>useCaches</code> field to the 350 * specified value. 351 * 352 * @param defaultusecaches the new value. 353 * @see java.net.URLConnection#useCaches 354 */ setDefaultUseCaches(boolean defaultusecaches)355 public void setDefaultUseCaches(boolean defaultusecaches) { 356 jarFileURLConnection.setDefaultUseCaches(defaultusecaches); 357 } 358 359 /** 360 * Returns the default value of a <code>URLConnection</code>'s 361 * <code>useCaches</code> flag. 362 * <p> 363 * Ths default is "sticky", being a part of the static state of all 364 * URLConnections. This flag applies to the next, and all following 365 * URLConnections that are created. 366 * 367 * @return the default value of a <code>URLConnection</code>'s 368 * <code>useCaches</code> flag. 369 * @see java.net.URLConnection#useCaches 370 */ getDefaultUseCaches()371 public boolean getDefaultUseCaches() { 372 return jarFileURLConnection.getDefaultUseCaches(); 373 } 374 } 375