1 /* 2 * Copyright (c) 1997, 2020, 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 79 public JarURLConnection(URL url, Handler handler) 80 throws MalformedURLException, IOException { 81 super(url); 82 83 jarFileURL = getJarFileURL(); 84 jarFileURLConnection = jarFileURL.openConnection(); 85 // whether, or not, the embedded URL should use the cache will depend 86 // on this instance's cache value 87 jarFileURLConnection.setUseCaches(useCaches); 88 entryName = getEntryName(); 89 } 90 91 public JarFile getJarFile() throws IOException { 92 try { 93 connect(); 94 return jarFile; 95 } catch (IOException e) { 96 if (jarFile != null) { 97 return jarFile; 98 } else { 99 throw e; 100 } 101 } 102 } 103 104 public JarEntry getJarEntry() throws IOException { 105 connect(); 106 return jarEntry; 107 } 108 109 public Permission getPermission() throws IOException { 110 return jarFileURLConnection.getPermission(); 111 } 112 113 class JarURLInputStream extends java.io.FilterInputStream { 114 JarURLInputStream (InputStream src) { 115 super (src); 116 } 117 public void close () throws IOException { 118 try { 119 super.close(); 120 } finally { 121 if (!getUseCaches()) { 122 jarFile.close(); 123 } 124 } 125 } 126 } 127 128 129 130 public void connect() throws IOException { 131 if (!connected) { 132 /* the factory call will do the security checks */ 133 jarFile = factory.get(getJarFileURL(), getUseCaches()); 134 135 /* we also ask the factory the permission that was required 136 * to get the jarFile, and set it as our permission. 137 */ 138 if (getUseCaches()) { 139 boolean oldUseCaches = jarFileURLConnection.getUseCaches(); 140 jarFileURLConnection = factory.getConnection(jarFile); 141 jarFileURLConnection.setUseCaches(oldUseCaches); 142 } 143 144 if ((entryName != null)) { 145 jarEntry = (JarEntry)jarFile.getEntry(entryName); 146 if (jarEntry == null) { 147 try { 148 if (!getUseCaches()) { 149 jarFile.close(); 150 } 151 } catch (Exception e) { 152 } 153 throw new FileNotFoundException("JAR entry " + entryName + 154 " not found in " + 155 jarFile.getName()); 156 } 157 } 158 connected = true; 159 } 160 } 161 162 public InputStream getInputStream() throws IOException { 163 connect(); 164 165 InputStream result = null; 166 167 if (entryName == null) { 168 throw new IOException("no entry name specified"); 169 } else { 170 if (jarEntry == null) { 171 throw new FileNotFoundException("JAR entry " + entryName + 172 " not found in " + 173 jarFile.getName()); 174 } 175 result = new JarURLInputStream (jarFile.getInputStream(jarEntry)); 176 } 177 return result; 178 } 179 180 public int getContentLength() { 181 long result = getContentLengthLong(); 182 if (result > Integer.MAX_VALUE) 183 return -1; 184 return (int) result; 185 } 186 187 public long getContentLengthLong() { 188 long result = -1; 189 try { 190 connect(); 191 if (jarEntry == null) { 192 /* if the URL referes to an archive */ 193 result = jarFileURLConnection.getContentLengthLong(); 194 } else { 195 /* if the URL referes to an archive entry */ 196 result = getJarEntry().getSize(); 197 } 198 } catch (IOException e) { 199 } 200 return result; 201 } 202 203 public Object getContent() throws IOException { 204 Object result = null; 205 206 connect(); 207 if (entryName == null) { 208 result = jarFile; 209 } else { 210 result = super.getContent(); 211 } 212 return result; 213 } 214 215 public String getContentType() { 216 if (contentType == null) { 217 if (entryName == null) { 218 contentType = "x-java/jar"; 219 } else { 220 try { 221 connect(); 222 InputStream in = jarFile.getInputStream(jarEntry); 223 contentType = guessContentTypeFromStream( 224 new BufferedInputStream(in)); 225 in.close(); 226 } catch (IOException e) { 227 // don't do anything 228 } 229 } 230 if (contentType == null) { 231 contentType = guessContentTypeFromName(entryName); 232 } 233 if (contentType == null) { 234 contentType = "content/unknown"; 235 } 236 } 237 return contentType; 238 } 239 240 public String getHeaderField(String name) { 241 return jarFileURLConnection.getHeaderField(name); 242 } 243 244 /** 245 * Sets the general request property. 246 * 247 * @param key the keyword by which the request is known 248 * (e.g., "<code>accept</code>"). 249 * @param value the value associated with it. 250 */ 251 public void setRequestProperty(String key, String value) { 252 jarFileURLConnection.setRequestProperty(key, value); 253 } 254 255 /** 256 * Returns the value of the named general request property for this 257 * connection. 258 * 259 * @return the value of the named general request property for this 260 * connection. 261 */ 262 public String getRequestProperty(String key) { 263 return jarFileURLConnection.getRequestProperty(key); 264 } 265 266 /** 267 * Adds a general request property specified by a 268 * key-value pair. This method will not overwrite 269 * existing values associated with the same key. 270 * 271 * @param key the keyword by which the request is known 272 * (e.g., "<code>accept</code>"). 273 * @param value the value associated with it. 274 */ 275 public void addRequestProperty(String key, String value) { 276 jarFileURLConnection.addRequestProperty(key, value); 277 } 278 279 /** 280 * Returns an unmodifiable Map of general request 281 * properties for this connection. The Map keys 282 * are Strings that represent the request-header 283 * field names. Each Map value is a unmodifiable List 284 * of Strings that represents the corresponding 285 * field values. 286 * 287 * @return a Map of the general request properties for this connection. 288 */ 289 public Map<String,List<String>> getRequestProperties() { 290 return jarFileURLConnection.getRequestProperties(); 291 } 292 293 /** 294 * Set the value of the <code>allowUserInteraction</code> field of 295 * this <code>URLConnection</code>. 296 * 297 * @param allowuserinteraction the new value. 298 * @see java.net.URLConnection#allowUserInteraction 299 */ 300 public void setAllowUserInteraction(boolean allowuserinteraction) { 301 jarFileURLConnection.setAllowUserInteraction(allowuserinteraction); 302 } 303 304 /** 305 * Returns the value of the <code>allowUserInteraction</code> field for 306 * this object. 307 * 308 * @return the value of the <code>allowUserInteraction</code> field for 309 * this object. 310 * @see java.net.URLConnection#allowUserInteraction 311 */ 312 public boolean getAllowUserInteraction() { 313 return jarFileURLConnection.getAllowUserInteraction(); 314 } 315 316 /* 317 * cache control 318 */ 319 320 /** 321 * Sets the value of the <code>useCaches</code> field of this 322 * <code>URLConnection</code> to the specified value. 323 * <p> 324 * Some protocols do caching of documents. Occasionally, it is important 325 * to be able to "tunnel through" and ignore the caches (e.g., the 326 * "reload" button in a browser). If the UseCaches flag on a connection 327 * is true, the connection is allowed to use whatever caches it can. 328 * If false, caches are to be ignored. 329 * The default value comes from DefaultUseCaches, which defaults to 330 * true. 331 * 332 * @see java.net.URLConnection#useCaches 333 */ 334 public void setUseCaches(boolean usecaches) { 335 jarFileURLConnection.setUseCaches(usecaches); 336 } 337 338 /** 339 * Returns the value of this <code>URLConnection</code>'s 340 * <code>useCaches</code> field. 341 * 342 * @return the value of this <code>URLConnection</code>'s 343 * <code>useCaches</code> field. 344 * @see java.net.URLConnection#useCaches 345 */ 346 public boolean getUseCaches() { 347 return jarFileURLConnection.getUseCaches(); 348 } 349 350 /** 351 * Sets the value of the <code>ifModifiedSince</code> field of 352 * this <code>URLConnection</code> to the specified value. 353 * 354 * @param ifmodifiedsince the new value. 355 * @see java.net.URLConnection#ifModifiedSince 356 */ 357 public void setIfModifiedSince(long ifmodifiedsince) { 358 jarFileURLConnection.setIfModifiedSince(ifmodifiedsince); 359 } 360 361 /** 362 * Sets the default value of the <code>useCaches</code> field to the 363 * specified value. 364 * 365 * @param defaultusecaches the new value. 366 * @see java.net.URLConnection#useCaches 367 */ 368 public void setDefaultUseCaches(boolean defaultusecaches) { 369 jarFileURLConnection.setDefaultUseCaches(defaultusecaches); 370 } 371 372 /** 373 * Returns the default value of a <code>URLConnection</code>'s 374 * <code>useCaches</code> flag. 375 * <p> 376 * Ths default is "sticky", being a part of the static state of all 377 * URLConnections. This flag applies to the next, and all following 378 * URLConnections that are created. 379 * 380 * @return the default value of a <code>URLConnection</code>'s 381 * <code>useCaches</code> flag. 382 * @see java.net.URLConnection#useCaches 383 */ 384 public boolean getDefaultUseCaches() { 385 return jarFileURLConnection.getDefaultUseCaches(); 386 } 387 }