1 /* 2 * Copyright (c) 1997, 2013, 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 // Android-added: Upstream fix to avoid affecting useCaches setting. 129 // This line and the one further down were integrated from an 130 // upstream commit beyond OpenJDK 8u121-b13. See http://b/62368386 131 boolean oldUseCaches = jarFileURLConnection.getUseCaches(); 132 jarFileURLConnection = factory.getConnection(jarFile); 133 // Android-added: Upstream fix to avoid affecting useCaches setting. 134 jarFileURLConnection.setUseCaches(oldUseCaches); 135 } 136 137 if ((entryName != null)) { 138 jarEntry = (JarEntry)jarFile.getEntry(entryName); 139 if (jarEntry == null) { 140 try { 141 if (!getUseCaches()) { 142 jarFile.close(); 143 } 144 } catch (Exception e) { 145 } 146 throw new FileNotFoundException("JAR entry " + entryName + 147 " not found in " + 148 jarFile.getName()); 149 } 150 } 151 connected = true; 152 } 153 } 154 getInputStream()155 public InputStream getInputStream() throws IOException { 156 connect(); 157 158 InputStream result = null; 159 160 if (entryName == null) { 161 throw new IOException("no entry name specified"); 162 } else { 163 if (jarEntry == null) { 164 throw new FileNotFoundException("JAR entry " + entryName + 165 " not found in " + 166 jarFile.getName()); 167 } 168 result = new JarURLInputStream (jarFile.getInputStream(jarEntry)); 169 } 170 return result; 171 } 172 getContentLength()173 public int getContentLength() { 174 long result = getContentLengthLong(); 175 if (result > Integer.MAX_VALUE) 176 return -1; 177 return (int) result; 178 } 179 getContentLengthLong()180 public long getContentLengthLong() { 181 long result = -1; 182 try { 183 connect(); 184 if (jarEntry == null) { 185 /* if the URL referes to an archive */ 186 result = jarFileURLConnection.getContentLengthLong(); 187 } else { 188 /* if the URL referes to an archive entry */ 189 result = getJarEntry().getSize(); 190 } 191 } catch (IOException e) { 192 } 193 return result; 194 } 195 getContent()196 public Object getContent() throws IOException { 197 Object result = null; 198 199 connect(); 200 if (entryName == null) { 201 result = jarFile; 202 } else { 203 result = super.getContent(); 204 } 205 return result; 206 } 207 getContentType()208 public String getContentType() { 209 if (contentType == null) { 210 if (entryName == null) { 211 contentType = "x-java/jar"; 212 } else { 213 try { 214 connect(); 215 InputStream in = jarFile.getInputStream(jarEntry); 216 contentType = guessContentTypeFromStream( 217 new BufferedInputStream(in)); 218 in.close(); 219 } catch (IOException e) { 220 // don't do anything 221 } 222 } 223 if (contentType == null) { 224 contentType = guessContentTypeFromName(entryName); 225 } 226 if (contentType == null) { 227 contentType = "content/unknown"; 228 } 229 } 230 return contentType; 231 } 232 getHeaderField(String name)233 public String getHeaderField(String name) { 234 return jarFileURLConnection.getHeaderField(name); 235 } 236 237 /** 238 * Sets the general request property. 239 * 240 * @param key the keyword by which the request is known 241 * (e.g., "<code>accept</code>"). 242 * @param value the value associated with it. 243 */ setRequestProperty(String key, String value)244 public void setRequestProperty(String key, String value) { 245 jarFileURLConnection.setRequestProperty(key, value); 246 } 247 248 /** 249 * Returns the value of the named general request property for this 250 * connection. 251 * 252 * @return the value of the named general request property for this 253 * connection. 254 */ getRequestProperty(String key)255 public String getRequestProperty(String key) { 256 return jarFileURLConnection.getRequestProperty(key); 257 } 258 259 /** 260 * Adds a general request property specified by a 261 * key-value pair. This method will not overwrite 262 * existing values associated with the same key. 263 * 264 * @param key the keyword by which the request is known 265 * (e.g., "<code>accept</code>"). 266 * @param value the value associated with it. 267 */ addRequestProperty(String key, String value)268 public void addRequestProperty(String key, String value) { 269 jarFileURLConnection.addRequestProperty(key, value); 270 } 271 272 /** 273 * Returns an unmodifiable Map of general request 274 * properties for this connection. The Map keys 275 * are Strings that represent the request-header 276 * field names. Each Map value is a unmodifiable List 277 * of Strings that represents the corresponding 278 * field values. 279 * 280 * @return a Map of the general request properties for this connection. 281 */ getRequestProperties()282 public Map<String,List<String>> getRequestProperties() { 283 return jarFileURLConnection.getRequestProperties(); 284 } 285 286 /** 287 * Set the value of the <code>allowUserInteraction</code> field of 288 * this <code>URLConnection</code>. 289 * 290 * @param allowuserinteraction the new value. 291 * @see java.net.URLConnection#allowUserInteraction 292 */ setAllowUserInteraction(boolean allowuserinteraction)293 public void setAllowUserInteraction(boolean allowuserinteraction) { 294 jarFileURLConnection.setAllowUserInteraction(allowuserinteraction); 295 } 296 297 /** 298 * Returns the value of the <code>allowUserInteraction</code> field for 299 * this object. 300 * 301 * @return the value of the <code>allowUserInteraction</code> field for 302 * this object. 303 * @see java.net.URLConnection#allowUserInteraction 304 */ getAllowUserInteraction()305 public boolean getAllowUserInteraction() { 306 return jarFileURLConnection.getAllowUserInteraction(); 307 } 308 309 /* 310 * cache control 311 */ 312 313 /** 314 * Sets the value of the <code>useCaches</code> field of this 315 * <code>URLConnection</code> to the specified value. 316 * <p> 317 * Some protocols do caching of documents. Occasionally, it is important 318 * to be able to "tunnel through" and ignore the caches (e.g., the 319 * "reload" button in a browser). If the UseCaches flag on a connection 320 * is true, the connection is allowed to use whatever caches it can. 321 * If false, caches are to be ignored. 322 * The default value comes from DefaultUseCaches, which defaults to 323 * true. 324 * 325 * @see java.net.URLConnection#useCaches 326 */ setUseCaches(boolean usecaches)327 public void setUseCaches(boolean usecaches) { 328 jarFileURLConnection.setUseCaches(usecaches); 329 } 330 331 /** 332 * Returns the value of this <code>URLConnection</code>'s 333 * <code>useCaches</code> field. 334 * 335 * @return the value of this <code>URLConnection</code>'s 336 * <code>useCaches</code> field. 337 * @see java.net.URLConnection#useCaches 338 */ getUseCaches()339 public boolean getUseCaches() { 340 return jarFileURLConnection.getUseCaches(); 341 } 342 343 /** 344 * Sets the value of the <code>ifModifiedSince</code> field of 345 * this <code>URLConnection</code> to the specified value. 346 * 347 * @param value the new value. 348 * @see java.net.URLConnection#ifModifiedSince 349 */ setIfModifiedSince(long ifmodifiedsince)350 public void setIfModifiedSince(long ifmodifiedsince) { 351 jarFileURLConnection.setIfModifiedSince(ifmodifiedsince); 352 } 353 354 /** 355 * Sets the default value of the <code>useCaches</code> field to the 356 * specified value. 357 * 358 * @param defaultusecaches the new value. 359 * @see java.net.URLConnection#useCaches 360 */ setDefaultUseCaches(boolean defaultusecaches)361 public void setDefaultUseCaches(boolean defaultusecaches) { 362 jarFileURLConnection.setDefaultUseCaches(defaultusecaches); 363 } 364 365 /** 366 * Returns the default value of a <code>URLConnection</code>'s 367 * <code>useCaches</code> flag. 368 * <p> 369 * Ths default is "sticky", being a part of the static state of all 370 * URLConnections. This flag applies to the next, and all following 371 * URLConnections that are created. 372 * 373 * @return the default value of a <code>URLConnection</code>'s 374 * <code>useCaches</code> flag. 375 * @see java.net.URLConnection#useCaches 376 */ getDefaultUseCaches()377 public boolean getDefaultUseCaches() { 378 return jarFileURLConnection.getDefaultUseCaches(); 379 } 380 } 381