1f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/*
2f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  this work for additional information regarding copyright ownership.
5f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  the License.  You may obtain a copy of the License at
8f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
9f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
11f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  See the License for the specific language governing permissions and
15f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *  limitations under the License.
16f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
17f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
18f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectpackage org.apache.harmony.luni.internal.net.www.protocol.jar;
19f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
20f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.File;
21f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.FileNotFoundException;
22f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.FileOutputStream;
23f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.FilterInputStream;
24f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.IOException;
25f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.InputStream;
26f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.net.ContentHandler;
27f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.net.ContentHandlerFactory;
2809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilsonimport java.net.JarURLConnection;
29f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.net.MalformedURLException;
30f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.net.URL;
31f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.security.AccessController;
32f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.security.Permission;
33f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.security.PrivilegedAction;
343819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilsonimport java.util.HashMap;
353819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilsonimport java.util.Iterator;
363819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilsonimport java.util.Map;
373819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilsonimport java.util.Set;
38f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.util.jar.JarEntry;
39f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.util.jar.JarFile;
40f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.util.zip.ZipFile;
41f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
42f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.apache.harmony.luni.util.Msg;
43f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.apache.harmony.luni.util.Util;
44f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
45f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/**
4609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson * This subclass extends {@code URLConnection}.
47f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * <p>
483819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson *
49f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * This class is responsible for connecting and retrieving resources from a Jar
5009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson * file which can be anywhere that can be referred to by an URL.
51f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
5209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilsonpublic class JarURLConnectionImpl extends JarURLConnection {
53f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
543819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    static HashMap<URL, JarFile> jarCache = new HashMap<URL, JarFile>();
553819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
563819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    private URL jarFileURL;
57f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
583819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    private InputStream jarInput;
59f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
60f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private JarFile jarFile;
61f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
62f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private JarEntry jarEntry;
63f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
643819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    private boolean closed;
65f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
66f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
67f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @param url
68f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *            the URL of the JAR
69f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws MalformedURLException
70f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             if the URL is malformed
7109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * @throws IOException
7209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *             if there is a problem opening the connection.
73f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
7409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson    public JarURLConnectionImpl(URL url) throws MalformedURLException,
7509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            IOException {
76f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        super(url);
773819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        jarFileURL = getJarFileURL();
783819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        jarFileURLConnection = jarFileURL.openConnection();
79f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
80f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
81f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
82f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @see java.net.URLConnection#connect()
83f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
84f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
85f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public void connect() throws IOException {
863819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        if (!connected) {
873819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            findJarFile(); // ensure the file can be found
883819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            findJarEntry(); // ensure the entry, if any, can be found
893819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            connected = true;
903819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        }
91f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
92f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
93f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
9409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * Returns the Jar file referred by this {@code URLConnection}.
9509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
96f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return the JAR file referenced by this connection
973819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
98f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws IOException
99f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             thrown if an IO error occurs while connecting to the
100f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             resource.
101f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
102f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
103f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public JarFile getJarFile() throws IOException {
1043819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        connect();
105f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return jarFile;
106f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
107f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
108f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
10909133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * Returns the Jar file referred by this {@code URLConnection}
11009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
111f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws IOException
112f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             if an IO error occurs while connecting to the resource.
113f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
114f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private void findJarFile() throws IOException {
1153819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        JarFile jar = null;
1163819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        if (getUseCaches()) {
11709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            synchronized (jarCache) {
1183819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                jarFile = jarCache.get(jarFileURL);
119f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
1203819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            if (jarFile == null) {
1213819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                jar = openJarFile();
12209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                synchronized (jarCache) {
1233819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                    jarFile = jarCache.get(jarFileURL);
12409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                    if (jarFile == null) {
1253819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                        jarCache.put(jarFileURL, jar);
1263819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                        jarFile = jar;
12709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                    } else {
1283819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                        jar.close();
1293819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                    }
1303819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                }
131f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
13209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        } else {
1333819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            jarFile = openJarFile();
134f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
135f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
1363819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        if (jarFile == null) {
1373819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            throw new IOException();
138f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
1393819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    }
140f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
14109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson    @SuppressWarnings("nls")
1423819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    JarFile openJarFile() throws IOException {
1433819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        JarFile jar = null;
14409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        if (jarFileURL.getProtocol().equals("file")) {
14501021fcb0c9026e81ac2c262caf5e2ec830a7025Jesse Wilson            jar = new JarFile(new File(Util.decode(jarFileURL.getFile(), false,
14601021fcb0c9026e81ac2c262caf5e2ec830a7025Jesse Wilson                    "UTF-8")), true, ZipFile.OPEN_READ);
14701021fcb0c9026e81ac2c262caf5e2ec830a7025Jesse Wilson        } else {
1483819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            final InputStream is = jarFileURL.openConnection().getInputStream();
1493819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            try {
1503819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                jar = AccessController
15109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                        .doPrivileged(new PrivilegedAction<JarFile>() {
15209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                            public JarFile run() {
15309133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                try {
15409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    File tempJar = File.createTempFile(
15509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                            "hyjar_", ".tmp", null);
15609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    tempJar.deleteOnExit();
15709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    FileOutputStream fos = new FileOutputStream(
15809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                            tempJar);
15909133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    byte[] buf = new byte[4096];
16009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    int nbytes = 0;
16109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    while ((nbytes = is.read(buf)) > -1) {
16209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                        fos.write(buf, 0, nbytes);
16309133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    }
16409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    fos.close();
16509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    return new JarFile(tempJar, true,
16609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                            ZipFile.OPEN_READ
16709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                                    | ZipFile.OPEN_DELETE);
16809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                } catch (IOException e) {
16909133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                                    return null;
170f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                                }
171f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                            }
17209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                        });
1733819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            } finally {
17409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                if (is != null) {
17509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                    is.close();
17609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                }
177f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
178f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
179f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
180f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return jar;
181f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
182f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
183f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
18409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * Returns the JarEntry of the entry referenced by this {@code
18509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * URLConnection}.
18609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
18709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * @return the JarEntry referenced
18809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
189f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws IOException
190f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             if an IO error occurs while getting the entry
191f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
192f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
193f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public JarEntry getJarEntry() throws IOException {
1943819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        connect();
195f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return jarEntry;
196f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
197f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
198f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
199f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
20009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * Look up the JarEntry of the entry referenced by this {@code
20109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * URLConnection}.
202f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
203f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private void findJarEntry() throws IOException {
204f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (getEntryName() == null) {
205f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return;
206f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
207f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        jarEntry = jarFile.getJarEntry(getEntryName());
208f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (jarEntry == null) {
209f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            throw new FileNotFoundException(getEntryName());
210f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
211f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
212f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
213f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
214f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Creates an input stream for reading from this URL Connection.
2153819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
216f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return the input stream
2173819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
218f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws IOException
219f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             if an IO error occurs while connecting to the resource.
220f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
221f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
222f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public InputStream getInputStream() throws IOException {
223f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (closed) {
22409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            // KA027=Inputstream of the JarURLConnection has been closed
22509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            throw new IllegalStateException(Msg.getString("KA027")); //$NON-NLS-1$
226f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
2273819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        connect();
228f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (jarInput != null) {
229f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return jarInput;
230f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
231f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (jarEntry == null) {
23209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            // K00fc=Jar entry not specified
233f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            throw new IOException(Msg.getString("K00fc")); //$NON-NLS-1$
234f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
235f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return jarInput = new JarURLConnectionInputStream(jarFile
236f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                .getInputStream(jarEntry), jarFile);
237f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
238f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
239f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
24009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * Returns the content type of the resource. For jar file itself
24109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * "x-java/jar" should be returned, for jar entries the content type of the
24209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * entry should be returned. Returns non-null results ("content/unknown" for
24309133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * unknown types).
24409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
245f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return the content type
246f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
247f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
248f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String getContentType() {
2493819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        if (url.getFile().endsWith("!/")) { //$NON-NLS-1$
2503819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            // the type for jar file itself is always "x-java/jar"
2513819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            return "x-java/jar"; //$NON-NLS-1$
25209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        }
25309133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        String cType = null;
25409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        String entryName = getEntryName();
2553819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
25609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        if (entryName != null) {
25709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            // if there is an Jar Entry, get the content type from the name
25809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            cType = guessContentTypeFromName(entryName);
25909133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        } else {
26009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            try {
26109133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                connect();
26209133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                cType = jarFileURLConnection.getContentType();
26309133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            } catch (IOException ioe) {
26409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson                // Ignore
2653819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            }
266f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
26709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        if (cType == null) {
26809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            cType = "content/unknown"; //$NON-NLS-1$
26909133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        }
27009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        return cType;
271f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
272f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
273f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
274f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Returns the content length of the resource. Test cases reveal that if the
27509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * URL is referring to a Jar file, this method answers a content-length
2763819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     * returned by URLConnection. For jar entry it should return it's size.
2773819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     * Otherwise, it will return -1.
2783819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
279f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return the content length
280f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
281f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
282f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public int getContentLength() {
283f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        try {
2843819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            connect();
2853819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            if (jarEntry == null) {
2863819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                return jarFileURLConnection.getContentLength();
287f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
28809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            return (int) getJarEntry().getSize();
289f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        } catch (IOException e) {
29009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            // Ignored
291f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
292f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return -1;
293f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
294f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
295f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
29609133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * Returns the object pointed by this {@code URL}. If this URLConnection is
29709133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * pointing to a Jar File (no Jar Entry), this method will return a {@code
29809133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * JarFile} If there is a Jar Entry, it will return the object corresponding
29909133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     * to the Jar entry content type.
30009133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
301f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return a non-null object
3023819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
303f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws IOException
30409133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *             if an IO error occurred
30509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson     *
306f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @see ContentHandler
307f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @see ContentHandlerFactory
308f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @see java.io.IOException
309f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @see #setContentHandlerFactory(ContentHandlerFactory)
310f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
311f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
312f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Object getContent() throws IOException {
3133819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        connect();
314f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        // if there is no Jar Entry, return a JarFile
315f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (jarEntry == null) {
316f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return jarFile;
317f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
318f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return super.getContent();
319f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
320f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
321f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
322f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Returns the permission, in this case the subclass, FilePermission object
323f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * which represents the permission necessary for this URLConnection to
324f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * establish the connection.
3253819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
326f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return the permission required for this URLConnection.
3273819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson     *
328f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @throws IOException
329f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             thrown when an IO exception occurs while creating the
330f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *             permission.
331f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
3323819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
333f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
334f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Permission getPermission() throws IOException {
3353819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        return jarFileURLConnection.getPermission();
3363819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    }
3373819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
3383819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    @Override
3393819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    public boolean getUseCaches() {
3403819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        return jarFileURLConnection.getUseCaches();
3413819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    }
3423819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
3433819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    @Override
3443819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    public void setUseCaches(boolean usecaches) {
3453819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        jarFileURLConnection.setUseCaches(usecaches);
3463819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    }
3473819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
3483819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    @Override
3493819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    public boolean getDefaultUseCaches() {
3503819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        return jarFileURLConnection.getDefaultUseCaches();
3513819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    }
3523819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson
3533819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    @Override
3543819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson    public void setDefaultUseCaches(boolean defaultusecaches) {
3553819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
356f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
357f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
358f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
359f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Closes the cached files.
360f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
361f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public static void closeCachedFiles() {
3623819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson        Set<Map.Entry<URL, JarFile>> s = jarCache.entrySet();
36309133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        synchronized (jarCache) {
3643819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            Iterator<Map.Entry<URL, JarFile>> i = s.iterator();
36509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson            while (i.hasNext()) {
3663819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                try {
3673819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                    ZipFile zip = i.next().getValue();
3683819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                    if (zip != null) {
3693819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                        zip.close();
3703819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                    }
3713819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                } catch (IOException e) {
3723819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson                    // Ignored
373f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                }
374f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
37509133811f94298bf72a3bf6ee605f60e7b1b2c81Jesse Wilson        }
376f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
377f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
378f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private class JarURLConnectionInputStream extends FilterInputStream {
379f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        InputStream inputStream;
380f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
381f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        JarFile jarFile;
382f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
383f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        protected JarURLConnectionInputStream(InputStream in, JarFile file) {
384f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            super(in);
385f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            inputStream = in;
386f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            jarFile = file;
387f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
388f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
389f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        @Override
390f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        public void close() throws IOException {
391f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            super.close();
3923819a76e7c1f49253f0e077bd497f149340c02b8Jesse Wilson            if (!getUseCaches()) {
393f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                closed = true;
394f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                jarFile.close();
395f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
396f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
397f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
398f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        @Override
399f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        public int read() throws IOException {
400f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return inputStream.read();
401f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
402f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
403f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        @Override
404f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        public int read(byte[] buf, int off, int nbytes) throws IOException {
405f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return inputStream.read(buf, off, nbytes);
406f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
407f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
408f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        @Override
409f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        public long skip(long nbytes) throws IOException {
410f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return inputStream.skip(nbytes);
411f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
412f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
413f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project}
414