1/*
2 * Copyright (c) 2014, 2017, 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
26package sun.net.www.protocol.jrt;
27
28import java.io.ByteArrayInputStream;
29import java.io.IOException;
30import java.io.InputStream;
31import java.net.MalformedURLException;
32import java.net.URL;
33import java.security.AccessController;
34import java.security.Permission;
35import java.security.PrivilegedAction;
36
37import jdk.internal.jimage.ImageLocation;
38import jdk.internal.jimage.ImageReader;
39import jdk.internal.jimage.ImageReaderFactory;
40
41import jdk.internal.loader.URLClassPath;
42import jdk.internal.loader.Resource;
43import sun.net.www.ParseUtil;
44import sun.net.www.URLConnection;
45
46/**
47 * URLConnection implementation that can be used to connect to resources
48 * contained in the runtime image.
49 */
50public class JavaRuntimeURLConnection extends URLConnection {
51
52    // ImageReader to access resources in jimage
53    private static final ImageReader reader;
54    static {
55        PrivilegedAction<ImageReader> pa = ImageReaderFactory::getImageReader;
56        reader = AccessController.doPrivileged(pa);
57    }
58
59    // the module and resource name in the URL
60    private final String module;
61    private final String name;
62
63    // the Resource when connected
64    private volatile Resource resource;
65
66    JavaRuntimeURLConnection(URL url) throws IOException {
67        super(url);
68        String path = url.getPath();
69        if (path.length() == 0 || path.charAt(0) != '/')
70            throw new MalformedURLException(url + " missing path or /");
71        if (path.length() == 1) {
72            this.module = null;
73            this.name = null;
74        } else {
75            int pos = path.indexOf('/', 1);
76            if (pos == -1) {
77                this.module = path.substring(1);
78                this.name = null;
79            } else {
80                this.module = path.substring(1, pos);
81                this.name = ParseUtil.decode(path.substring(pos+1));
82            }
83        }
84    }
85
86    /**
87     * Finds a resource in a module, returning {@code null} if the resource
88     * is not found.
89     */
90    private static Resource findResource(String module, String name) {
91        if (reader != null) {
92            URL url = toJrtURL(module, name);
93            ImageLocation location = reader.findLocation(module, name);
94            if (location != null && URLClassPath.checkURL(url) != null) {
95                return new Resource() {
96                    @Override
97                    public String getName() {
98                        return name;
99                    }
100                    @Override
101                    public URL getURL() {
102                        return url;
103                    }
104                    @Override
105                    public URL getCodeSourceURL() {
106                        return toJrtURL(module);
107                    }
108                    @Override
109                    public InputStream getInputStream() throws IOException {
110                        byte[] resource = reader.getResource(location);
111                        return new ByteArrayInputStream(resource);
112                    }
113                    @Override
114                    public int getContentLength() {
115                        long size = location.getUncompressedSize();
116                        return (size > Integer.MAX_VALUE) ? -1 : (int) size;
117                    }
118                };
119            }
120        }
121        return null;
122    }
123
124    @Override
125    public synchronized void connect() throws IOException {
126        if (!connected) {
127            if (name == null) {
128                String s = (module == null) ? "" : module;
129                throw new IOException("cannot connect to jrt:/" + s);
130            }
131            resource = findResource(module, name);
132            if (resource == null)
133                throw new IOException(module + "/" + name + " not found");
134            connected = true;
135        }
136    }
137
138    @Override
139    public InputStream getInputStream() throws IOException {
140        connect();
141        return resource.getInputStream();
142    }
143
144    @Override
145    public long getContentLengthLong() {
146        try {
147            connect();
148            return resource.getContentLength();
149        } catch (IOException ioe) {
150            return -1L;
151        }
152    }
153
154    @Override
155    public int getContentLength() {
156        long len = getContentLengthLong();
157        return len > Integer.MAX_VALUE ? -1 : (int)len;
158    }
159
160    @Override
161    public Permission getPermission() {
162        return new RuntimePermission("accessSystemModules");
163    }
164
165    /**
166     * Returns a jrt URL for the given module and resource name.
167     */
168    private static URL toJrtURL(String module, String name) {
169        try {
170            return new URL("jrt:/" + module + "/" + name);
171        } catch (MalformedURLException e) {
172            throw new InternalError(e);
173        }
174    }
175
176    /**
177     * Returns a jrt URL for the given module.
178     */
179    private static URL toJrtURL(String module) {
180        try {
181            return new URL("jrt:/" + module);
182        } catch (MalformedURLException e) {
183            throw new InternalError(e);
184        }
185    }
186}
187