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. The JAR file is also returned if the connection to 219 * a JAR file entry fails, but the JAR file itself is accessible. 220 * 221 * @throws IOException if an IOException occurs while trying to 222 * connect to the JAR file for this connection. 223 * 224 * @see #connect 225 */ 226 public abstract JarFile getJarFile() throws IOException; 227 228 /** 229 * Returns the Manifest for this connection, or null if none. 230 * 231 * @return the manifest object corresponding to the JAR file object 232 * for this connection. If the connection is a connection to an 233 * entry of a JAR file, the manifest object is also returned if the 234 * connection to a JAR file entry fails, but the JAR file itself is 235 * accessible. 236 * 237 * @throws IOException if getting the JAR file for this 238 * connection causes an IOException to be thrown. 239 * 240 * @see #getJarFile 241 */ 242 public Manifest getManifest() throws IOException { 243 return getJarFile().getManifest(); 244 } 245 246 /** 247 * Return the JAR entry object for this connection, if any. This 248 * method returns null if the JAR file URL corresponding to this 249 * connection points to a JAR file and not a JAR file entry. 250 * 251 * @return the JAR entry object for this connection, or null if 252 * the JAR URL for this connection points to a JAR file. 253 * 254 * @throws IOException if getting the JAR file for this 255 * connection causes an IOException to be thrown, or if the 256 * connection to a JAR file entry fails. 257 * 258 * @see #getJarFile 259 * @see #getJarEntry 260 */ 261 public JarEntry getJarEntry() throws IOException { 262 return entryName == null ? null : getJarFile().getJarEntry(entryName); 263 } 264 265 /** 266 * Return the Attributes object for this connection if the URL 267 * for it points to a JAR file entry, null otherwise. 268 * 269 * @return the Attributes object for this connection if the URL 270 * for it points to a JAR file entry, null otherwise. 271 * 272 * @throws IOException if getting the JAR entry causes an 273 * IOException to be thrown. 274 * 275 * @see #getJarEntry 276 */ 277 public Attributes getAttributes() throws IOException { 278 JarEntry e = getJarEntry(); 279 return e != null ? e.getAttributes() : null; 280 } 281 282 /** 283 * Returns the main Attributes for the JAR file for this 284 * connection. 285 * 286 * @return the main Attributes for the JAR file for this 287 * connection. 288 * 289 * @throws IOException if getting the manifest causes an 290 * IOException to be thrown. 291 * 292 * @see #getJarFile 293 * @see #getManifest 294 */ 295 public Attributes getMainAttributes() throws IOException { 296 Manifest man = getManifest(); 297 return man != null ? man.getMainAttributes() : null; 298 } 299 300 /** 301 * Returns the Certificate objects for this connection if the URL 302 * for it points to a JAR file entry, null otherwise. This method 303 * can only be called once 304 * the connection has been completely verified by reading 305 * from the input stream until the end of the stream has been 306 * reached. Otherwise, this method will return {@code null} 307 * 308 * @return the Certificate object for this connection if the URL 309 * for it points to a JAR file entry, null otherwise. 310 * 311 * @throws IOException if getting the JAR entry causes an 312 * IOException to be thrown. 313 * 314 * @see #getJarEntry 315 */ 316 public java.security.cert.Certificate[] getCertificates() 317 throws IOException 318 { 319 JarEntry e = getJarEntry(); 320 return e != null ? e.getCertificates() : null; 321 } 322 }