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.io.ByteArrayInputStream; 33 import java.net.URL; 34 import java.net.URLConnection; 35 import java.net.MalformedURLException; 36 import java.net.UnknownServiceException; 37 import java.nio.ByteBuffer; 38 import java.util.Enumeration; 39 import java.util.Map; 40 import java.util.List; 41 import java.util.jar.JarEntry; 42 import java.util.jar.JarFile; 43 import java.util.jar.Manifest; 44 import java.security.Permission; 45 46 /** 47 * @author Benjamin Renaud 48 * @since 1.2 49 */ 50 public class JarURLConnection extends java.net.JarURLConnection { 51 52 private static final boolean debug = false; 53 54 /* the Jar file factory. It handles both retrieval and caching. 55 */ 56 private static final JarFileFactory factory = JarFileFactory.getInstance(); 57 58 private static final sun.misc.JavaUtilZipFileAccess zipAccess 59 = sun.misc.SharedSecrets.getJavaUtilZipFileAccess(); 60 61 /* the url for the Jar file */ 62 private URL jarFileURL; 63 64 /* the permission to get this JAR file. This is the actual, ultimate, 65 * permission, returned by the jar file factory. 66 */ 67 private Permission permission; 68 69 /* the url connection for the JAR file */ 70 private URLConnection jarFileURLConnection; 71 72 /* the entry name, if any */ 73 private String entryName; 74 75 /* the JarEntry */ 76 private JarEntry jarEntry; 77 78 /* the jar file corresponding to this connection */ 79 private JarFile jarFile; 80 81 /* the content type for this connection */ 82 private String contentType; 83 84 public JarURLConnection(URL url, Handler handler) 85 throws MalformedURLException, IOException { 86 super(url); 87 88 jarFileURL = getJarFileURL(); 89 jarFileURLConnection = jarFileURL.openConnection(); 90 entryName = getEntryName(); 91 } 92 93 public JarFile getJarFile() throws IOException { 94 connect(); 95 return jarFile; 96 } 97 98 public JarEntry getJarEntry() throws IOException { 99 connect(); 100 return jarEntry; 101 } 102 103 public Permission getPermission() throws IOException { 104 return jarFileURLConnection.getPermission(); 105 } 106 107 class JarURLInputStream extends java.io.FilterInputStream { 108 JarURLInputStream (InputStream src) { 109 super (src); 110 } 111 public void close () throws IOException { 112 try { 113 super.close(); 114 } finally { 115 if (!getUseCaches()) { 116 jarFile.close(); 117 } 118 } 119 } 120 } 121 122 123 124 public void connect() throws IOException { 125 if (!connected) { 126 /* the factory call will do the security checks */ 127 jarFile = factory.get(getJarFileURL(), getUseCaches()); 128 129 /* we also ask the factory the permission that was required 130 * to get the jarFile, and set it as our permission. 131 */ 132 if (getUseCaches()) { 133 jarFileURLConnection = factory.getConnection(jarFile); 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 long size = jarEntry.getSize(); 168 if (size > 0 && size < 512 * 1024) { 169 byte[] bytes = new byte[(int) size]; 170 ByteBuffer bb = ByteBuffer.wrap(bytes); 171 zipAccess.fillByteBuffer(jarFile, jarEntry, bb); 172 result = new JarURLInputStream(new ByteArrayInputStream(bytes, 0, bb.position())); 173 } else { 174 // Unknown or too large ZipEntry size to use a array 175 result = new JarURLInputStream(jarFile.getInputStream(jarEntry)); 176 } 177 } 178 return result; 179 } 180 181 public int getContentLength() { 182 long result = getContentLengthLong(); 183 if (result > Integer.MAX_VALUE) 184 return -1; 185 return (int) result; 186 } 187 188 public long getContentLengthLong() { 189 long result = -1; 190 try { 191 connect(); 192 if (jarEntry == null) { 193 /* if the URL referes to an archive */ 194 result = jarFileURLConnection.getContentLengthLong(); 195 } else { 196 /* if the URL referes to an archive entry */ 197 result = getJarEntry().getSize(); 198 } 199 } catch (IOException e) { 200 } 201 return result; 202 } 203 204 public Object getContent() throws IOException { 205 Object result = null; 206 207 connect(); 208 if (entryName == null) { 209 result = jarFile; 210 } else { 211 result = super.getContent(); 212 } 213 return result; 214 } 215 216 public String getContentType() { 217 if (contentType == null) { 218 if (entryName == null) { 219 contentType = "x-java/jar"; 220 } else { 221 try { 222 connect(); 223 InputStream in = jarFile.getInputStream(jarEntry); 224 contentType = guessContentTypeFromStream( 225 new BufferedInputStream(in)); 226 in.close(); 227 } catch (IOException e) { 228 // don't do anything 229 } 230 } 231 if (contentType == null) { 232 contentType = guessContentTypeFromName(entryName); 233 } 234 if (contentType == null) { 235 contentType = "content/unknown"; 236 } 237 } 238 return contentType; 239 } 240 241 public String getHeaderField(String name) { 242 return jarFileURLConnection.getHeaderField(name); 243 } 244 245 /** 246 * Sets the general request property. 247 * 248 * @param key the keyword by which the request is known 249 * (e.g., "<code>accept</code>"). 250 * @param value the value associated with it. 251 */ 252 public void setRequestProperty(String key, String value) { 253 jarFileURLConnection.setRequestProperty(key, value); 254 } 255 256 /** 257 * Returns the value of the named general request property for this 258 * connection. 259 * 260 * @return the value of the named general request property for this 261 * connection. 262 */ 263 public String getRequestProperty(String key) { 264 return jarFileURLConnection.getRequestProperty(key); 265 } 266 267 /** 268 * Adds a general request property specified by a 269 * key-value pair. This method will not overwrite 270 * existing values associated with the same key. 271 * 272 * @param key the keyword by which the request is known 273 * (e.g., "<code>accept</code>"). 274 * @param value the value associated with it. 275 */ 276 public void addRequestProperty(String key, String value) { 277 jarFileURLConnection.addRequestProperty(key, value); 278 } 279 280 /** 281 * Returns an unmodifiable Map of general request 282 * properties for this connection. The Map keys 283 * are Strings that represent the request-header 284 * field names. Each Map value is a unmodifiable List 285 * of Strings that represents the corresponding 286 * field values. 287 * 288 * @return a Map of the general request properties for this connection. 289 */ 290 public Map<String,List<String>> getRequestProperties() { 291 return jarFileURLConnection.getRequestProperties(); 292 } 293 294 /** 295 * Set the value of the <code>allowUserInteraction</code> field of 296 * this <code>URLConnection</code>. 297 * 298 * @param allowuserinteraction the new value. 299 * @see java.net.URLConnection#allowUserInteraction 300 */ 301 public void setAllowUserInteraction(boolean allowuserinteraction) { 302 jarFileURLConnection.setAllowUserInteraction(allowuserinteraction); 303 } 304 305 /** 306 * Returns the value of the <code>allowUserInteraction</code> field for 307 * this object. 308 * 309 * @return the value of the <code>allowUserInteraction</code> field for 310 * this object. 311 * @see java.net.URLConnection#allowUserInteraction 312 */ 313 public boolean getAllowUserInteraction() { 314 return jarFileURLConnection.getAllowUserInteraction(); 315 } 316 317 /* 318 * cache control 319 */ 320 321 /** 322 * Sets the value of the <code>useCaches</code> field of this 323 * <code>URLConnection</code> to the specified value. 324 * <p> 325 * Some protocols do caching of documents. Occasionally, it is important 326 * to be able to "tunnel through" and ignore the caches (e.g., the 327 * "reload" button in a browser). If the UseCaches flag on a connection 328 * is true, the connection is allowed to use whatever caches it can. 329 * If false, caches are to be ignored. 330 * The default value comes from DefaultUseCaches, which defaults to 331 * true. 332 * 333 * @see java.net.URLConnection#useCaches 334 */ 335 public void setUseCaches(boolean usecaches) { 336 jarFileURLConnection.setUseCaches(usecaches); 337 } 338 339 /** 340 * Returns the value of this <code>URLConnection</code>'s 341 * <code>useCaches</code> field. 342 * 343 * @return the value of this <code>URLConnection</code>'s 344 * <code>useCaches</code> field. 345 * @see java.net.URLConnection#useCaches 346 */ 347 public boolean getUseCaches() { 348 return jarFileURLConnection.getUseCaches(); 349 } 350 351 /** 352 * Sets the value of the <code>ifModifiedSince</code> field of 353 * this <code>URLConnection</code> to the specified value. 354 * 355 * @param value the new value. 356 * @see java.net.URLConnection#ifModifiedSince 357 */ 358 public void setIfModifiedSince(long ifmodifiedsince) { 359 jarFileURLConnection.setIfModifiedSince(ifmodifiedsince); 360 } 361 362 /** 363 * Sets the default value of the <code>useCaches</code> field to the 364 * specified value. 365 * 366 * @param defaultusecaches the new value. 367 * @see java.net.URLConnection#useCaches 368 */ 369 public void setDefaultUseCaches(boolean defaultusecaches) { 370 jarFileURLConnection.setDefaultUseCaches(defaultusecaches); 371 } 372 373 /** 374 * Returns the default value of a <code>URLConnection</code>'s 375 * <code>useCaches</code> flag. 376 * <p> 377 * Ths default is "sticky", being a part of the static state of all 378 * URLConnections. This flag applies to the next, and all following 379 * URLConnections that are created. 380 * 381 * @return the default value of a <code>URLConnection</code>'s 382 * <code>useCaches</code> flag. 383 * @see java.net.URLConnection#useCaches 384 */ 385 public boolean getDefaultUseCaches() { 386 return jarFileURLConnection.getDefaultUseCaches(); 387 } 388 }