1 /* 2 * Copyright (c) 1997, 2019, 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 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 */ 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 * The url argument may have had a runtime fragment appended, so 178 * we need to add a runtime fragment to the jarFileURL to enable 179 * runtime versioning when the underlying jar file is opened. 180 */ 181 if ("runtime".equals(url.getRef())) { 182 jarFileURL = new URL(jarFileURL, "#runtime"); 183 } 184 entryName = null; 185 186 /* if ! is the last letter of the innerURL, entryName is null */ 187 if (++separator != spec.length()) { 188 entryName = spec.substring(separator, spec.length()); 189 entryName = ParseUtil.decode (entryName); 190 } 191 } 192 193 /** 194 * Returns the URL for the Jar file for this connection. 195 * 196 * @return the URL for the Jar file for this connection. 197 */ 198 public URL getJarFileURL() { 199 return jarFileURL; 200 } 201 202 /** 203 * Return the entry name for this connection. This method 204 * returns null if the JAR file URL corresponding to this 205 * connection points to a JAR file and not a JAR file entry. 206 * 207 * @return the entry name for this connection, if any. 208 */ 209 public String getEntryName() { 210 return entryName; 211 } 212 213 /** 214 * Return the JAR file for this connection. 215 * 216 * @return the JAR file for this connection. If the connection is 217 * a connection to an entry of a JAR file, the JAR file object is 218 * returned 219 * 220 * @throws IOException if an IOException occurs while trying to 221 * connect to the JAR file for this connection. 222 * 223 * @see #connect 224 */ 225 public abstract JarFile getJarFile() throws IOException; 226 227 /** 228 * Returns the Manifest for this connection, or null if none. 229 * 230 * @return the manifest object corresponding to the JAR file object 231 * for this connection. 232 * 233 * @throws IOException if getting the JAR file for this 234 * connection causes an IOException to be thrown. 235 * 236 * @see #getJarFile 237 */ 238 public Manifest getManifest() throws IOException { 239 return getJarFile().getManifest(); 240 } 241 242 /** 243 * Return the JAR entry object for this connection, if any. This 244 * method returns null if the JAR file URL corresponding to this 245 * connection points to a JAR file and not a JAR file entry. 246 * 247 * @return the JAR entry object for this connection, or null if 248 * the JAR URL for this connection points to a JAR file. 249 * 250 * @throws IOException if getting the JAR file for this 251 * connection causes an IOException to be thrown. 252 * 253 * @see #getJarFile 254 * @see #getJarEntry 255 */ 256 public JarEntry getJarEntry() throws IOException { 257 return entryName == null ? null : getJarFile().getJarEntry(entryName); 258 } 259 260 /** 261 * Return the Attributes object for this connection if the URL 262 * for it points to a JAR file entry, null otherwise. 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 * @throws IOException if getting the JAR entry causes an 268 * IOException to be thrown. 269 * 270 * @see #getJarEntry 271 */ 272 public Attributes getAttributes() throws IOException { 273 JarEntry e = getJarEntry(); 274 return e != null ? e.getAttributes() : null; 275 } 276 277 /** 278 * Returns the main Attributes for the JAR file for this 279 * connection. 280 * 281 * @return the main Attributes for the JAR file for this 282 * connection. 283 * 284 * @throws IOException if getting the manifest causes an 285 * IOException to be thrown. 286 * 287 * @see #getJarFile 288 * @see #getManifest 289 */ 290 public Attributes getMainAttributes() throws IOException { 291 Manifest man = getManifest(); 292 return man != null ? man.getMainAttributes() : null; 293 } 294 295 /** 296 * Returns the Certificate objects for this connection if the URL 297 * for it points to a JAR file entry, null otherwise. This method 298 * can only be called once 299 * the connection has been completely verified by reading 300 * from the input stream until the end of the stream has been 301 * reached. Otherwise, this method will return {@code null} 302 * 303 * @return the Certificate object for this connection if the URL 304 * for it points to a JAR file entry, null otherwise. 305 * 306 * @throws IOException if getting the JAR entry causes an 307 * IOException to be thrown. 308 * 309 * @see #getJarEntry 310 */ 311 public java.security.cert.Certificate[] getCertificates() 312 throws IOException 313 { 314 JarEntry e = getJarEntry(); 315 return e != null ? e.getCertificates() : null; 316 } 317 }