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