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 java.net; 27 28 import java.io.IOException; 29 import java.util.jar.JarFile; 30 import java.util.jar.JarEntry; 31 import java.util.jar.Attributes; 32 import java.util.jar.Manifest; 33 import java.security.Permission; 34 import sun.net.www.ParseUtil; 35 36 /** 37 * A URL Connection to a Java ARchive (JAR) file or an entry in a JAR 38 * file. 39 * 40 * <p>The syntax of a JAR URL is: 41 * 42 * <pre> 43 * jar:<url>!/{entry} 44 * </pre> 45 * 46 * <p>for example: 47 * 48 * <p>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class} 49 * 50 * <p>Jar URLs should be used to refer to a JAR file or entries in 51 * a JAR file. The example above is a JAR URL which refers to a JAR 52 * entry. If the entry name is omitted, the URL refers to the whole 53 * JAR file: 54 * 55 * {@code jar:http://www.foo.com/bar/baz.jar!/} 56 * 57 * <p>Users should cast the generic URLConnection to a 58 * JarURLConnection when they know that the URL they created is a JAR 59 * URL, and they need JAR-specific functionality. For example: 60 * 61 * <pre> 62 * URL url = new URL("jar:file:/home/duke/duke.jar!/"); 63 * JarURLConnection jarConnection = (JarURLConnection)url.openConnection(); 64 * Manifest manifest = jarConnection.getManifest(); 65 * </pre> 66 * 67 * <p>JarURLConnection instances can only be used to read from JAR files. 68 * It is not possible to get a {@link java.io.OutputStream} to modify or write 69 * to the underlying JAR file using this class. 70 * <p>Examples: 71 * 72 * <dl> 73 * 74 * <dt>A Jar entry 75 * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class} 76 * 77 * <dt>A Jar file 78 * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/} 79 * 80 * <dt>A Jar directory 81 * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/} 82 * 83 * </dl> 84 * 85 * <p>{@code !/} is referred to as the <em>separator</em>. 86 * 87 * <p>When constructing a JAR url via {@code new URL(context, spec)}, 88 * the following rules apply: 89 * 90 * <ul> 91 * 92 * <li>if there is no context URL and the specification passed to the 93 * URL constructor doesn't contain a separator, the URL is considered 94 * to refer to a JarFile. 95 * 96 * <li>if there is a context URL, the context URL is assumed to refer 97 * to a JAR file or a Jar directory. 98 * 99 * <li>if the specification begins with a '/', the Jar directory is 100 * ignored, and the spec is considered to be at the root of the Jar 101 * file. 102 * 103 * <p>Examples: 104 * 105 * <dl> 106 * 107 * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/</b>, 108 * spec:<b>baz/entry.txt</b> 109 * 110 * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/baz/entry.txt</b> 111 * 112 * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/baz</b>, 113 * spec:<b>entry.txt</b> 114 * 115 * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/baz/entry.txt</b> 116 * 117 * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/baz</b>, 118 * spec:<b>/entry.txt</b> 119 * 120 * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/entry.txt</b> 121 * 122 * </dl> 123 * 124 * </ul> 125 * 126 * @see java.net.URL 127 * @see java.net.URLConnection 128 * 129 * @see java.util.jar.JarFile 130 * @see java.util.jar.JarInputStream 131 * @see java.util.jar.Manifest 132 * @see java.util.zip.ZipEntry 133 * 134 * @author Benjamin Renaud 135 * @since 1.2 136 */ 137 public abstract class JarURLConnection extends URLConnection { 138 139 private URL jarFileURL; 140 private String entryName; 141 142 /** 143 * The connection to the JAR file URL, if the connection has been 144 * initiated. This should be set by connect. 145 */ 146 protected URLConnection jarFileURLConnection; 147 148 /** 149 * Creates the new JarURLConnection to the specified URL. 150 * @param url the URL 151 * @throws MalformedURLException if no legal protocol 152 * could be found in a specification string or the 153 * string could not be parsed. 154 */ 155 JarURLConnection(URL url)156 protected JarURLConnection(URL url) throws MalformedURLException { 157 super(url); 158 parseSpecs(url); 159 } 160 161 /* get the specs for a given url out of the cache, and compute and 162 * cache them if they're not there. 163 */ parseSpecs(URL url)164 private void parseSpecs(URL url) throws MalformedURLException { 165 String spec = url.getFile(); 166 167 int separator = spec.indexOf("!/"); 168 /* 169 * REMIND: we don't handle nested JAR URLs 170 */ 171 if (separator == -1) { 172 throw new MalformedURLException("no !/ found in url spec:" + spec); 173 } 174 175 jarFileURL = new URL(spec.substring(0, separator++)); 176 177 // Android-removed: runtime versioning is unsupported 178 /* 179 * The url argument may have had a runtime fragment appended, so 180 * we need to add a runtime fragment to the jarFileURL to enable 181 * runtime versioning when the underlying jar file is opened. 182 * 183 if ("runtime".equals(url.getRef())) { 184 jarFileURL = new URL(jarFileURL, "#runtime"); 185 } 186 entryName = null; 187 */ 188 189 /* if ! is the last letter of the innerURL, entryName is null */ 190 if (++separator != spec.length()) { 191 entryName = spec.substring(separator, spec.length()); 192 entryName = ParseUtil.decode (entryName); 193 } 194 } 195 196 /** 197 * Returns the URL for the Jar file for this connection. 198 * 199 * @return the URL for the Jar file for this connection. 200 */ getJarFileURL()201 public URL getJarFileURL() { 202 return jarFileURL; 203 } 204 205 /** 206 * Return the entry name for this connection. This method 207 * returns null if the JAR file URL corresponding to this 208 * connection points to a JAR file and not a JAR file entry. 209 * 210 * @return the entry name for this connection, if any. 211 */ getEntryName()212 public String getEntryName() { 213 return entryName; 214 } 215 216 /** 217 * Return the JAR file for this connection. 218 * 219 * @return the JAR file for this connection. If the connection is 220 * a connection to an entry of a JAR file, the JAR file object is 221 * returned 222 * 223 * @exception IOException if an IOException occurs while trying to 224 * connect to the JAR file for this connection. 225 * 226 * @see #connect 227 */ getJarFile()228 public abstract JarFile getJarFile() throws IOException; 229 230 /** 231 * Returns the Manifest for this connection, or null if none. 232 * 233 * @return the manifest object corresponding to the JAR file object 234 * for this connection. 235 * 236 * @exception IOException if getting the JAR file for this 237 * connection causes an IOException to be thrown. 238 * 239 * @see #getJarFile 240 */ getManifest()241 public Manifest getManifest() throws IOException { 242 return getJarFile().getManifest(); 243 } 244 245 /** 246 * Return the JAR entry object for this connection, if any. This 247 * method returns null if the JAR file URL corresponding to this 248 * connection points to a JAR file and not a JAR file entry. 249 * 250 * @return the JAR entry object for this connection, or null if 251 * the JAR URL for this connection points to a JAR file. 252 * 253 * @exception IOException if getting the JAR file for this 254 * connection causes an IOException to be thrown. 255 * 256 * @see #getJarFile 257 * @see #getJarEntry 258 */ getJarEntry()259 public JarEntry getJarEntry() throws IOException { 260 return entryName == null ? null : getJarFile().getJarEntry(entryName); 261 } 262 263 /** 264 * Return the Attributes object for this connection if the URL 265 * for it points to a JAR file entry, null otherwise. 266 * 267 * @return the Attributes object for this connection if the URL 268 * for it points to a JAR file entry, null otherwise. 269 * 270 * @exception IOException if getting the JAR entry causes an 271 * IOException to be thrown. 272 * 273 * @see #getJarEntry 274 */ getAttributes()275 public Attributes getAttributes() throws IOException { 276 JarEntry e = getJarEntry(); 277 return e != null ? e.getAttributes() : null; 278 } 279 280 /** 281 * Returns the main Attributes for the JAR file for this 282 * connection. 283 * 284 * @return the main Attributes for the JAR file for this 285 * connection. 286 * 287 * @exception IOException if getting the manifest causes an 288 * IOException to be thrown. 289 * 290 * @see #getJarFile 291 * @see #getManifest 292 */ getMainAttributes()293 public Attributes getMainAttributes() throws IOException { 294 Manifest man = getManifest(); 295 return man != null ? man.getMainAttributes() : null; 296 } 297 298 /** 299 * Return the Certificate object for this connection if the URL 300 * for it points to a JAR file entry, null otherwise. This method 301 * can only be called once 302 * the connection has been completely verified by reading 303 * from the input stream until the end of the stream has been 304 * reached. Otherwise, this method will return {@code null} 305 * 306 * @return the Certificate object for this connection if the URL 307 * for it points to a JAR file entry, null otherwise. 308 * 309 * @exception IOException if getting the JAR entry causes an 310 * IOException to be thrown. 311 * 312 * @see #getJarEntry 313 */ getCertificates()314 public java.security.cert.Certificate[] getCertificates() 315 throws IOException 316 { 317 JarEntry e = getJarEntry(); 318 return e != null ? e.getCertificates() : null; 319 } 320 } 321