2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package org.apache.harmony.luni.internal.net.www.protocol.jar;
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.FilterInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.net.ContentHandler;
27 import java.net.ContentHandlerFactory;
28 import java.net.JarURLConnection;
29 import java.net.MalformedURLException;
31 import java.security.AccessController;
32 import java.security.Permission;
33 import java.security.PrivilegedAction;
34 import java.util.HashMap;
35 import java.util.Iterator;
38 import java.util.jar.JarEntry;
39 import java.util.jar.JarFile;
40 import java.util.zip.ZipFile;
41 import org.apache.harmony.luni.util.Util;
44 * This subclass extends {@code URLConnection}.
47 * This class is responsible for connecting and retrieving resources from a Jar
48 * file which can be anywhere that can be referred to by an URL.
50 public class JarURLConnectionImpl extends JarURLConnection {
52 static HashMap<URL, JarFile> jarCache = new HashMap<URL, JarFile>();
54 private URL jarFileURL;
56 private InputStream jarInput;
58 private JarFile jarFile;
60 private JarEntry jarEntry;
62 private boolean closed;
67 * @throws MalformedURLException
68 * if the URL is malformed
70 * if there is a problem opening the connection.
72 public JarURLConnectionImpl(URL url) throws MalformedURLException,
75 jarFileURL = getJarFileURL();
76 jarFileURLConnection = jarFileURL.openConnection();
80 * @see java.net.URLConnection#connect()
83 public void connect() throws IOException {
85 findJarFile(); // ensure the file can be found
86 findJarEntry(); // ensure the entry, if any, can be found
92 * Returns the Jar file referred by this {@code URLConnection}.
94 * @return the JAR file referenced by this connection
97 * thrown if an IO error occurs while connecting to the
101 public JarFile getJarFile() throws IOException {
107 * Returns the Jar file referred by this {@code URLConnection}
109 * @throws IOException
110 * if an IO error occurs while connecting to the resource.
112 private void findJarFile() throws IOException {
114 if (getUseCaches()) {
115 synchronized (jarCache) {
116 jarFile = jarCache.get(jarFileURL);
118 if (jarFile == null) {
120 synchronized (jarCache) {
121 jarFile = jarCache.get(jarFileURL);
122 if (jarFile == null) {
123 jarCache.put(jarFileURL, jar);
131 jarFile = openJarFile();
134 if (jarFile == null) {
135 throw new IOException();
139 @SuppressWarnings("nls")
140 JarFile openJarFile() throws IOException {
142 if (jarFileURL.getProtocol().equals("file")) {
143 jar = new JarFile(new File(Util.decode(jarFileURL.getFile(), false,
144 "UTF-8")), true, ZipFile.OPEN_READ);
146 final InputStream is = jarFileURL.openConnection().getInputStream();
148 jar = AccessController
149 .doPrivileged(new PrivilegedAction<JarFile>() {
150 public JarFile run() {
151 FileOutputStream fos = null;
152 JarFile result = null;
154 File tempJar = File.createTempFile("hyjar_", ".tmp", null);
155 tempJar.deleteOnExit();
156 fos = new FileOutputStream(tempJar);
157 byte[] buf = new byte[4096];
159 while ((nbytes = is.read(buf)) > -1) {
160 fos.write(buf, 0, nbytes);
163 result = new JarFile(tempJar, true,
164 ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
165 } catch (IOException e) {
171 } catch (IOException ex) {
190 * Returns the JarEntry of the entry referenced by this {@code
193 * @return the JarEntry referenced
195 * @throws IOException
196 * if an IO error occurs while getting the entry
199 public JarEntry getJarEntry() throws IOException {
206 * Look up the JarEntry of the entry referenced by this {@code
209 private void findJarEntry() throws IOException {
210 if (getEntryName() == null) {
213 jarEntry = jarFile.getJarEntry(getEntryName());
214 if (jarEntry == null) {
215 throw new FileNotFoundException(getEntryName());
220 * Creates an input stream for reading from this URL Connection.
222 * @return the input stream
224 * @throws IOException
225 * if an IO error occurs while connecting to the resource.
228 public InputStream getInputStream() throws IOException {
230 throw new IllegalStateException("JarURLConnection InputStream has been closed");
233 if (jarInput != null) {
236 if (jarEntry == null) {
237 throw new IOException("Jar entry not specified");
239 return jarInput = new JarURLConnectionInputStream(jarFile
240 .getInputStream(jarEntry), jarFile);
244 * Returns the content type of the resource. For jar file itself
245 * "x-java/jar" should be returned, for jar entries the content type of the
246 * entry should be returned. Returns non-null results ("content/unknown" for
249 * @return the content type
252 public String getContentType() {
253 if (url.getFile().endsWith("!/")) {
254 // the type for jar file itself is always "x-java/jar"
258 String entryName = getEntryName();
260 if (entryName != null) {
261 // if there is an Jar Entry, get the content type from the name
262 cType = guessContentTypeFromName(entryName);
266 cType = jarFileURLConnection.getContentType();
267 } catch (IOException ioe) {
272 cType = "content/unknown";
278 * Returns the content length of the resource. Test cases reveal that if the
279 * URL is referring to a Jar file, this method answers a content-length
280 * returned by URLConnection. For jar entry it should return it's size.
281 * Otherwise, it will return -1.
283 * @return the content length
286 public int getContentLength() {
289 if (jarEntry == null) {
290 return jarFileURLConnection.getContentLength();
292 return (int) getJarEntry().getSize();
293 } catch (IOException e) {
300 * Returns the object pointed by this {@code URL}. If this URLConnection is
301 * pointing to a Jar File (no Jar Entry), this method will return a {@code
302 * JarFile} If there is a Jar Entry, it will return the object corresponding
303 * to the Jar entry content type.
305 * @return a non-null object
307 * @throws IOException
308 * if an IO error occurred
310 * @see ContentHandler
311 * @see ContentHandlerFactory
312 * @see java.io.IOException
313 * @see #setContentHandlerFactory(ContentHandlerFactory)
316 public Object getContent() throws IOException {
318 // if there is no Jar Entry, return a JarFile
319 if (jarEntry == null) {
322 return super.getContent();
326 * Returns the permission, in this case the subclass, FilePermission object
327 * which represents the permission necessary for this URLConnection to
328 * establish the connection.
330 * @return the permission required for this URLConnection.
332 * @throws IOException
333 * thrown when an IO exception occurs while creating the
338 public Permission getPermission() throws IOException {
339 return jarFileURLConnection.getPermission();
343 public boolean getUseCaches() {
344 return jarFileURLConnection.getUseCaches();
348 public void setUseCaches(boolean usecaches) {
349 jarFileURLConnection.setUseCaches(usecaches);
353 public boolean getDefaultUseCaches() {
354 return jarFileURLConnection.getDefaultUseCaches();
358 public void setDefaultUseCaches(boolean defaultusecaches) {
359 jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
363 * Closes the cached files.
365 public static void closeCachedFiles() {
366 Set<Map.Entry<URL, JarFile>> s = jarCache.entrySet();
367 synchronized (jarCache) {
368 Iterator<Map.Entry<URL, JarFile>> i = s.iterator();
369 while (i.hasNext()) {
371 ZipFile zip = i.next().getValue();
375 } catch (IOException e) {
382 private class JarURLConnectionInputStream extends FilterInputStream {
383 InputStream inputStream;
387 protected JarURLConnectionInputStream(InputStream in, JarFile file) {
394 public void close() throws IOException {
396 if (!getUseCaches()) {
403 public int read() throws IOException {
404 return inputStream.read();
408 public int read(byte[] buf, int off, int nbytes) throws IOException {
409 return inputStream.read(buf, off, nbytes);
413 public long skip(long nbytes) throws IOException {
414 return inputStream.skip(nbytes);