1 /* 2 * Copyright (c) 2014, 2025, 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.jrt; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.UncheckedIOException; 32 import java.net.MalformedURLException; 33 import java.net.URL; 34 35 import jdk.internal.jimage.ImageReader; 36 import jdk.internal.jimage.ImageReader.Node; 37 import jdk.internal.jimage.ImageReaderFactory; 38 39 import sun.net.www.ParseUtil; 40 import sun.net.www.URLConnection; 41 42 /** 43 * URLConnection implementation that can be used to connect to resources 44 * contained in the runtime image. See section "New URI scheme for naming stored 45 * modules, classes, and resources" in <a href="https://openjdk.org/jeps/220"> 46 * JEP 220</a>. 47 */ 48 public class JavaRuntimeURLConnection extends URLConnection { 49 50 // ImageReader to access resources in jimage. 51 private static final ImageReader READER = ImageReaderFactory.getImageReader(); 52 53 // The module and resource name in the URL (i.e. "jrt:/[$MODULE[/$PATH]]"). 54 // 55 // The module name is not percent-decoded, and can be empty. 56 private final String module; 57 // The resource name permits UTF-8 percent encoding of non-ASCII characters. 58 private final String path; 59 60 // The resource node (non-null when connected). 61 private Node resourceNode; 62 63 JavaRuntimeURLConnection(URL url) throws IOException { 64 super(url); 65 String urlPath = url.getPath(); 66 if (urlPath.isEmpty() || urlPath.charAt(0) != '/') { 67 throw new MalformedURLException(url + " missing path or /"); 68 } 69 int pathSep = urlPath.indexOf('/', 1); 70 if (pathSep == -1) { 71 // No trailing resource path. This can never "connect" or return a 72 // resource (see JEP 220 for details). 73 this.module = urlPath.substring(1); 74 this.path = null; 75 } else { 76 this.module = urlPath.substring(1, pathSep); 77 this.path = percentDecode(urlPath.substring(pathSep + 1)); 78 } 79 } 80 81 /** 82 * Finds and caches the resource node associated with this URL and marks the 83 * connection as "connected". 84 */ 85 private synchronized Node connectResourceNode() throws IOException { 86 if (resourceNode == null) { 87 if (module.isEmpty() || path == null) { 88 throw new IOException("cannot connect to jrt:/" + module); 89 } 90 Node node = READER.findResourceNode(module, path); 91 if (node == null) { 92 throw new IOException(module + "/" + path + " not found"); 93 } 94 this.resourceNode = node; 95 super.connected = true; 96 } 97 return resourceNode; 98 } 99 100 @Override 101 public void connect() throws IOException { 102 connectResourceNode(); 103 } 104 105 @Override 106 public InputStream getInputStream() throws IOException { 107 return new ByteArrayInputStream(READER.getResource(connectResourceNode())); 108 } 109 110 @Override 111 public long getContentLengthLong() { 112 try { 113 return connectResourceNode().size(); 114 } catch (IOException ioe) { 115 return -1L; 116 } 117 } 118 119 @Override 120 public int getContentLength() { 121 long len = getContentLengthLong(); 122 return len > Integer.MAX_VALUE ? -1 : (int)len; 123 } 124 125 // Perform percent decoding of the resource name/path from the URL. 126 private static String percentDecode(String path) throws MalformedURLException { 127 // Any additional special case decoding logic should go here. 128 try { 129 return ParseUtil.decode(path); 130 } catch (IllegalArgumentException e) { 131 throw new MalformedURLException(e.getMessage()); 132 } 133 } 134 }