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