1 /*
2 * Copyright (c) 1997, 2020, 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.jar;
27
28 import java.io.InputStream;
29 import java.io.IOException;
30 import java.io.FileNotFoundException;
31 import java.io.BufferedInputStream;
32 import java.net.URL;
33 import java.net.URLConnection;
34 import java.net.MalformedURLException;
35 import java.net.UnknownServiceException;
36 import java.util.Enumeration;
37 import java.util.Map;
38 import java.util.List;
39 import java.util.jar.JarEntry;
40 import java.util.jar.JarFile;
41 import java.util.jar.Manifest;
42 import java.security.Permission;
43
44 /**
45 * @author Benjamin Renaud
46 * @since 1.2
47 */
48 public class JarURLConnection extends java.net.JarURLConnection {
49
50 private static final boolean debug = false;
51
52 /* the Jar file factory. It handles both retrieval and caching.
53 */
54 private static final JarFileFactory factory = JarFileFactory.getInstance();
55
56 /* the url for the Jar file */
57 private URL jarFileURL;
58
59 /* the permission to get this JAR file. This is the actual, ultimate,
60 * permission, returned by the jar file factory.
61 */
62 private Permission permission;
63
64 /* the url connection for the JAR file */
65 private URLConnection jarFileURLConnection;
66
67 /* the entry name, if any */
68 private String entryName;
69
70 /* the JarEntry */
71 private JarEntry jarEntry;
72
73 /* the jar file corresponding to this connection */
74 private JarFile jarFile;
75
76 /* the content type for this connection */
77 private String contentType;
78
79 public JarURLConnection(URL url, Handler handler)
80 throws MalformedURLException, IOException {
81 super(url);
82
83 jarFileURL = getJarFileURL();
84 jarFileURLConnection = jarFileURL.openConnection();
85 // whether, or not, the embedded URL should use the cache will depend
86 // on this instance's cache value
87 jarFileURLConnection.setUseCaches(useCaches);
88 entryName = getEntryName();
89 }
90
91 public JarFile getJarFile() throws IOException {
92 try {
93 connect();
94 return jarFile;
95 } catch (IOException e) {
96 if (jarFile != null) {
97 return jarFile;
98 } else {
99 throw e;
100 }
101 }
102 }
103
104 public JarEntry getJarEntry() throws IOException {
105 connect();
106 return jarEntry;
107 }
108
109 public Permission getPermission() throws IOException {
110 return jarFileURLConnection.getPermission();
111 }
112
113 class JarURLInputStream extends java.io.FilterInputStream {
114 JarURLInputStream (InputStream src) {
115 super (src);
116 }
117 public void close () throws IOException {
118 try {
119 super.close();
120 } finally {
121 if (!getUseCaches()) {
122 jarFile.close();
123 }
124 }
125 }
126 }
127
128
129
130 public void connect() throws IOException {
131 if (!connected) {
132 /* the factory call will do the security checks */
133 jarFile = factory.get(getJarFileURL(), getUseCaches());
134
135 /* we also ask the factory the permission that was required
136 * to get the jarFile, and set it as our permission.
137 */
138 if (getUseCaches()) {
139 boolean oldUseCaches = jarFileURLConnection.getUseCaches();
140 jarFileURLConnection = factory.getConnection(jarFile);
141 jarFileURLConnection.setUseCaches(oldUseCaches);
142 }
143
144 if ((entryName != null)) {
145 jarEntry = (JarEntry)jarFile.getEntry(entryName);
146 if (jarEntry == null) {
147 try {
148 if (!getUseCaches()) {
149 jarFile.close();
150 }
151 } catch (Exception e) {
152 }
153 throw new FileNotFoundException("JAR entry " + entryName +
154 " not found in " +
155 jarFile.getName());
156 }
157 }
158 connected = true;
159 }
160 }
161
162 public InputStream getInputStream() throws IOException {
163 connect();
164
165 InputStream result = null;
166
167 if (entryName == null) {
168 throw new IOException("no entry name specified");
169 } else {
170 if (jarEntry == null) {
171 throw new FileNotFoundException("JAR entry " + entryName +
172 " not found in " +
173 jarFile.getName());
174 }
175 result = new JarURLInputStream (jarFile.getInputStream(jarEntry));
176 }
177 return result;
178 }
179
180 public int getContentLength() {
181 long result = getContentLengthLong();
182 if (result > Integer.MAX_VALUE)
183 return -1;
184 return (int) result;
185 }
186
187 public long getContentLengthLong() {
188 long result = -1;
189 try {
190 connect();
191 if (jarEntry == null) {
192 /* if the URL referes to an archive */
193 result = jarFileURLConnection.getContentLengthLong();
194 } else {
195 /* if the URL referes to an archive entry */
196 result = getJarEntry().getSize();
197 }
198 } catch (IOException e) {
199 }
200 return result;
201 }
202
203 public Object getContent() throws IOException {
204 Object result = null;
205
206 connect();
207 if (entryName == null) {
208 result = jarFile;
209 } else {
210 result = super.getContent();
211 }
212 return result;
213 }
214
215 public String getContentType() {
216 if (contentType == null) {
217 if (entryName == null) {
218 contentType = "x-java/jar";
219 } else {
220 try {
221 connect();
222 InputStream in = jarFile.getInputStream(jarEntry);
223 contentType = guessContentTypeFromStream(
224 new BufferedInputStream(in));
225 in.close();
226 } catch (IOException e) {
227 // don't do anything
228 }
229 }
230 if (contentType == null) {
231 contentType = guessContentTypeFromName(entryName);
232 }
233 if (contentType == null) {
234 contentType = "content/unknown";
235 }
236 }
237 return contentType;
238 }
239
240 public String getHeaderField(String name) {
241 return jarFileURLConnection.getHeaderField(name);
242 }
243
244 /**
245 * Sets the general request property.
246 *
247 * @param key the keyword by which the request is known
248 * (e.g., "<code>accept</code>").
249 * @param value the value associated with it.
250 */
251 public void setRequestProperty(String key, String value) {
252 jarFileURLConnection.setRequestProperty(key, value);
253 }
254
255 /**
256 * Returns the value of the named general request property for this
257 * connection.
258 *
259 * @return the value of the named general request property for this
260 * connection.
261 */
262 public String getRequestProperty(String key) {
263 return jarFileURLConnection.getRequestProperty(key);
264 }
265
266 /**
267 * Adds a general request property specified by a
268 * key-value pair. This method will not overwrite
269 * existing values associated with the same key.
270 *
271 * @param key the keyword by which the request is known
272 * (e.g., "<code>accept</code>").
273 * @param value the value associated with it.
274 */
275 public void addRequestProperty(String key, String value) {
276 jarFileURLConnection.addRequestProperty(key, value);
277 }
278
279 /**
280 * Returns an unmodifiable Map of general request
281 * properties for this connection. The Map keys
282 * are Strings that represent the request-header
283 * field names. Each Map value is a unmodifiable List
284 * of Strings that represents the corresponding
285 * field values.
286 *
287 * @return a Map of the general request properties for this connection.
288 */
289 public Map<String,List<String>> getRequestProperties() {
290 return jarFileURLConnection.getRequestProperties();
291 }
292
293 /**
294 * Set the value of the <code>allowUserInteraction</code> field of
295 * this <code>URLConnection</code>.
296 *
297 * @param allowuserinteraction the new value.
298 * @see java.net.URLConnection#allowUserInteraction
299 */
300 public void setAllowUserInteraction(boolean allowuserinteraction) {
301 jarFileURLConnection.setAllowUserInteraction(allowuserinteraction);
302 }
303
304 /**
305 * Returns the value of the <code>allowUserInteraction</code> field for
306 * this object.
307 *
308 * @return the value of the <code>allowUserInteraction</code> field for
309 * this object.
310 * @see java.net.URLConnection#allowUserInteraction
311 */
312 public boolean getAllowUserInteraction() {
313 return jarFileURLConnection.getAllowUserInteraction();
314 }
315
316 /*
317 * cache control
318 */
319
320 /**
321 * Sets the value of the <code>useCaches</code> field of this
322 * <code>URLConnection</code> to the specified value.
323 * <p>
324 * Some protocols do caching of documents. Occasionally, it is important
325 * to be able to "tunnel through" and ignore the caches (e.g., the
326 * "reload" button in a browser). If the UseCaches flag on a connection
327 * is true, the connection is allowed to use whatever caches it can.
328 * If false, caches are to be ignored.
329 * The default value comes from DefaultUseCaches, which defaults to
330 * true.
331 *
332 * @see java.net.URLConnection#useCaches
333 */
334 public void setUseCaches(boolean usecaches) {
335 jarFileURLConnection.setUseCaches(usecaches);
336 }
337
338 /**
339 * Returns the value of this <code>URLConnection</code>'s
340 * <code>useCaches</code> field.
341 *
342 * @return the value of this <code>URLConnection</code>'s
343 * <code>useCaches</code> field.
344 * @see java.net.URLConnection#useCaches
345 */
346 public boolean getUseCaches() {
347 return jarFileURLConnection.getUseCaches();
348 }
349
350 /**
351 * Sets the value of the <code>ifModifiedSince</code> field of
352 * this <code>URLConnection</code> to the specified value.
353 *
354 * @param ifmodifiedsince the new value.
355 * @see java.net.URLConnection#ifModifiedSince
356 */
357 public void setIfModifiedSince(long ifmodifiedsince) {
358 jarFileURLConnection.setIfModifiedSince(ifmodifiedsince);
359 }
360
361 /**
362 * Sets the default value of the <code>useCaches</code> field to the
363 * specified value.
364 *
365 * @param defaultusecaches the new value.
366 * @see java.net.URLConnection#useCaches
367 */
368 public void setDefaultUseCaches(boolean defaultusecaches) {
369 jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
370 }
371
372 /**
373 * Returns the default value of a <code>URLConnection</code>'s
374 * <code>useCaches</code> flag.
375 * <p>
376 * Ths default is "sticky", being a part of the static state of all
377 * URLConnections. This flag applies to the next, and all following
378 * URLConnections that are created.
379 *
380 * @return the default value of a <code>URLConnection</code>'s
381 * <code>useCaches</code> flag.
382 * @see java.net.URLConnection#useCaches
383 */
384 public boolean getDefaultUseCaches() {
385 return jarFileURLConnection.getDefaultUseCaches();
386 }
387 }
--- EOF ---