1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.net;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.io.BufferedReader;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.File;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.FileInputStream;
23f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.io.FileNotFoundException;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.FilePermission;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
27f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonimport java.io.InputStreamReader;
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.UnsupportedEncodingException;
296aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughesimport java.nio.charset.Charsets;
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.CodeSource;
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.PermissionCollection;
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.SecureClassLoader;
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.cert.Certificate;
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.ArrayList;
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Collections;
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Enumeration;
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.HashMap;
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.List;
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Map;
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.StringTokenizer;
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.Attributes;
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.JarEntry;
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.JarFile;
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.Manifest;
45a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilsonimport libcore.io.IoUtils;
46244449b9ccd108197d1c117edda99cd93a891d49Elliott Hughesimport libcore.io.Streams;
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * This class loader is responsible for loading classes and resources from a
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * list of URLs which can refer to either directories or JAR files. Classes
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * loaded by this {@code URLClassLoader} are granted permission to access the
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * URLs contained in the URL search list.
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
540d4daefcf389b6433a0af481ef44a84a2546541aElliott Hughes@FindBugsSuppressWarnings({ "DMI_COLLECTION_OF_URLS", "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED" })
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class URLClassLoader extends SecureClassLoader {
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
57f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    ArrayList<URL> originalUrls;
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
59f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    List<URL> searchList;
60f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    ArrayList<URLHandler> handlerList;
61f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    Map<URL, URLHandler> handlerMap = new HashMap<URL, URLHandler>();
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private URLStreamHandlerFactory factory;
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
65f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    static class IndexFile {
66f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
67f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        private HashMap<String, ArrayList<URL>> map;
68f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        //private URLClassLoader host;
69f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
70f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
71f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        static IndexFile readIndexFile(JarFile jf, JarEntry indexEntry, URL url) {
72f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            BufferedReader in = null;
73f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            InputStream is = null;
74f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
75f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // Add mappings from resource to jar file
76f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                String parentURLString = getParentURL(url).toExternalForm();
776aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughes                String prefix = "jar:" + parentURLString + "/";
78f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                is = jf.getInputStream(indexEntry);
796aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughes                in = new BufferedReader(new InputStreamReader(is, Charsets.UTF_8));
80f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                HashMap<String, ArrayList<URL>> pre_map = new HashMap<String, ArrayList<URL>>();
81f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // Ignore the 2 first lines (index version)
82f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (in.readLine() == null) return null;
83f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (in.readLine() == null) return null;
84f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                TOP_CYCLE:
85f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                while (true) {
86f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    String line = in.readLine();
87f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if (line == null) {
88f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        break;
89f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
90f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    URL jar = new URL(prefix + line + "!/");
91f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    while (true) {
92f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        line = in.readLine();
93f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (line == null) {
94f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            break TOP_CYCLE;
95f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
9680a7fbab52b96c9fd47c72f8987d1babe2cd001dElliott Hughes                        if (line.isEmpty()) {
97f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            break;
98f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
99f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        ArrayList<URL> list;
100f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (pre_map.containsKey(line)) {
101f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            list = pre_map.get(line);
102f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        } else {
103f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            list = new ArrayList<URL>();
104f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            pre_map.put(line, list);
105f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
106f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        list.add(jar);
107f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
108f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
109f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (!pre_map.isEmpty()) {
110f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return new IndexFile(pre_map);
111f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
112f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (MalformedURLException e) {
113f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // Ignore this jar's index
114f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (IOException e) {
115f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // Ignore this jar's index
116a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilson            } finally {
117a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilson                IoUtils.closeQuietly(in);
118a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilson                IoUtils.closeQuietly(is);
119f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
120f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
121f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
122f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
123f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        private static URL getParentURL(URL url) throws IOException {
124f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL fileURL = ((JarURLConnection) url.openConnection()).getJarFileURL();
125f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String file = fileURL.getFile();
126f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String parentFile = new File(file).getParent();
127f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            parentFile = parentFile.replace(File.separatorChar, '/');
128f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (parentFile.charAt(0) != '/') {
129f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                parentFile = "/" + parentFile;
130f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
131f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL parentURL = new URL(fileURL.getProtocol(), fileURL
132f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    .getHost(), fileURL.getPort(), parentFile);
133f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return parentURL;
134f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
135f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
136f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        public IndexFile(HashMap<String, ArrayList<URL>> map) {
137f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.map = map;
138f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
139f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
140f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        ArrayList<URL> get(String name) {
141f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return map.get(name);
142f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
143f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
144f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
145f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    class URLHandler {
146f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL url;
147f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL codeSourceUrl;
148f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
149f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        public URLHandler(URL url) {
150f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.url = url;
151f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.codeSourceUrl = url;
152f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
153f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
154f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        void findResources(String name, ArrayList<URL> resources) {
155f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL res = findResource(name);
156f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (res != null && !resources.contains(res)) {
157f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                resources.add(res);
158f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
159f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
160f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
161f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        Class<?> findClass(String packageName, String name, String origName) {
162f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL resURL = targetURL(url, name);
163f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (resURL != null) {
164f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                try {
165f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    InputStream is = resURL.openStream();
166f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return createClass(is, packageName, origName);
167f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } catch (IOException e) {
168f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
169f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
170f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
171f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
172f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
173f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
174f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        Class<?> createClass(InputStream is, String packageName, String origName) {
175f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (is == null) {
176f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
177f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
178f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson            byte[] clBuf;
179f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
180f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson                clBuf = Streams.readFully(is);
181f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (IOException e) {
182f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
183f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
184f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (packageName != null) {
185f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                String packageDotName = packageName.replace('/', '.');
186f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                Package packageObj = getPackage(packageDotName);
187f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (packageObj == null) {
188f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    definePackage(packageDotName, null, null,
189f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            null, null, null, null, null);
190f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } else {
191f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if (packageObj.isSealed()) {
192b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes                        throw new SecurityException("Package is sealed");
193f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
194f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
195f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
196f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return defineClass(origName, clBuf, 0, clBuf.length, new CodeSource(codeSourceUrl, (Certificate[]) null));
197f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
198f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
199f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL findResource(String name) {
200f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL resURL = targetURL(url, name);
201f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (resURL != null) {
202f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                try {
203f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    URLConnection uc = resURL.openConnection();
204f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    uc.getInputStream().close();
205f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    // HTTP can return a stream on a non-existent file
206f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    // So check for the return code;
207f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    if (!resURL.getProtocol().equals("http")) {
208f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        return resURL;
209f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
210f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    int code;
211f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if ((code = ((HttpURLConnection) uc).getResponseCode()) >= 200
212f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            && code < 300) {
213f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        return resURL;
214f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
215f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } catch (SecurityException e) {
216f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return null;
217f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } catch (IOException e) {
218f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return null;
219f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
220f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
221f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
222f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
223f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
224f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL targetURL(URL base, String name) {
225f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
22656099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson                StringBuilder fileBuilder = new StringBuilder();
22756099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson                fileBuilder.append(base.getFile());
22856099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson                URI.PATH_ENCODER.appendEncoded(fileBuilder, name);
22956099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson                String file = fileBuilder.toString();
230f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
23156099d23fcb002b164bff8fb7f14d6ec0453509eJesse Wilson                return new URL(base.getProtocol(), base.getHost(), base.getPort(), file, null);
232f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (MalformedURLException e) {
233f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
234f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
235f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
236f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
237f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
238f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
239f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    class URLJarHandler extends URLHandler {
240f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        final JarFile jf;
241f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        final String prefixName;
242f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        final IndexFile index;
243f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        final Map<URL, URLHandler> subHandlers = new HashMap<URL, URLHandler>();
244f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
245f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        public URLJarHandler(URL url, URL jarURL, JarFile jf, String prefixName) {
246f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            super(url);
247f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.jf = jf;
248f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.prefixName = prefixName;
249f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.codeSourceUrl = jarURL;
250f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            final JarEntry je = jf.getJarEntry("META-INF/INDEX.LIST");
251f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.index = (je == null ? null : IndexFile.readIndexFile(jf, je, url));
252f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
253f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
254f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        public URLJarHandler(URL url, URL jarURL, JarFile jf, String prefixName, IndexFile index) {
255f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            super(url);
256f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.jf = jf;
257f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.prefixName = prefixName;
258f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.index = index;
259f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            this.codeSourceUrl = jarURL;
260f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
261f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
262f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        IndexFile getIndex() {
263f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return index;
264f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
265f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
266f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        @Override
267f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        void findResources(String name, ArrayList<URL> resources) {
268f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL res = findResourceInOwn(name);
269f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (res != null && !resources.contains(res)) {
270f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                resources.add(res);
271f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
272f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (index != null) {
273f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                int pos = name.lastIndexOf("/");
274f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // only keep the directory part of the resource
275f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // as index.list only keeps track of directories and root files
276f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                String indexedName = (pos > 0) ? name.substring(0, pos) : name;
277f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                ArrayList<URL> urls = index.get(indexedName);
278f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (urls != null) {
279f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    urls.remove(url);
280f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    for (URL url : urls) {
281f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        URLHandler h = getSubHandler(url);
282f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (h != null) {
283f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            h.findResources(name, resources);
284f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
285f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
286f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
287f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
288f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
289f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
290f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
291f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        @Override
292f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        Class<?> findClass(String packageName, String name, String origName) {
293f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String entryName = prefixName + name;
294f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            JarEntry entry = jf.getJarEntry(entryName);
295f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (entry != null) {
296f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                /**
297f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                 * Avoid recursive load class, especially the class
298f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                 * is an implementation class of security provider
299f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                 * and the jar is signed.
300f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                 */
301f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                try {
302f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    Manifest manifest = jf.getManifest();
303f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return createClass(entry, manifest, packageName, origName);
304f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } catch (IOException e) {
305f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
306f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
307f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (index != null) {
308f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                ArrayList<URL> urls;
309f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (packageName == null) {
310f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    urls = index.get(name);
311f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } else {
312f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    urls = index.get(packageName);
313f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
314f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (urls != null) {
315f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    urls.remove(url);
316f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    for (URL url : urls) {
317f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        URLHandler h = getSubHandler(url);
318f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (h != null) {
319f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            Class<?> res = h.findClass(packageName, name, origName);
320f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            if (res != null) {
321f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                return res;
322f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            }
323f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
324f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
325f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
326f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
327f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
328f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
329f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
330f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        private Class<?> createClass(JarEntry entry, Manifest manifest, String packageName, String origName) {
331f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson            byte[] clBuf;
332f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
333f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson                InputStream is = jf.getInputStream(entry);
334f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson                clBuf = Streams.readFully(is);
335f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (IOException e) {
336f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
337f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
338f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (packageName != null) {
339f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                String packageDotName = packageName.replace('/', '.');
340f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                Package packageObj = getPackage(packageDotName);
341f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (packageObj == null) {
342f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if (manifest != null) {
343f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        definePackage(packageDotName, manifest,
344f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                codeSourceUrl);
345f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    } else {
346f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        definePackage(packageDotName, null, null,
347f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                null, null, null, null, null);
348f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
349f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } else {
350f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    boolean exception = packageObj.isSealed();
351f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if (manifest != null) {
352f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (isSealed(manifest, packageName + "/")) {
353f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            exception = !packageObj
354f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                    .isSealed(codeSourceUrl);
355f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
356f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
357f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if (exception) {
358b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes                        throw new SecurityException(String.format("Package %s is sealed",
359b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes                                packageName));
360f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
361f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
362f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
363f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            CodeSource codeS = new CodeSource(codeSourceUrl, entry.getCertificates());
364f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return defineClass(origName, clBuf, 0, clBuf.length, codeS);
365f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
366f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
367f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL findResourceInOwn(String name) {
368f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String entryName = prefixName + name;
369f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (jf.getEntry(entryName) != null) {
370f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return targetURL(url, name);
371f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
372f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
373f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
374f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
375f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        @Override
376f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL findResource(String name) {
377f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL res = findResourceInOwn(name);
378f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (res != null) {
379f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return res;
380f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
381f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (index != null) {
382f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                int pos = name.lastIndexOf("/");
383f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // only keep the directory part of the resource
384f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // as index.list only keeps track of directories and root files
385f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                String indexedName = (pos > 0) ? name.substring(0, pos) : name;
386f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                ArrayList<URL> urls = index.get(indexedName);
387f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (urls != null) {
388f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    urls.remove(url);
389f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    for (URL url : urls) {
390f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        URLHandler h = getSubHandler(url);
391f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (h != null) {
392f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            res = h.findResource(name);
393f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            if (res != null) {
394f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                return res;
395f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            }
396f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        }
397f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    }
398f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
399f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
400f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
401f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
402f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
403f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        private synchronized URLHandler getSubHandler(URL url) {
404f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URLHandler sub = subHandlers.get(url);
405f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (sub != null) {
406f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return sub;
407f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
408f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String protocol = url.getProtocol();
409f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            if (protocol.equals("jar")) {
410f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                sub = createURLJarHandler(url);
411f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            } else if (protocol.equals("file")) {
412f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                sub = createURLSubJarHandler(url);
413f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } else {
414f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                sub = createURLHandler(url);
415f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
416f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (sub != null) {
417f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                subHandlers.put(url, sub);
418f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
419f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return sub;
420f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
421f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
422f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        private URLHandler createURLSubJarHandler(URL url) {
423f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String prefixName;
424f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String file = url.getFile();
425f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            if (url.getFile().endsWith("!/")) {
426f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                prefixName = "";
427f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } else {
428f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                int sepIdx = file.lastIndexOf("!/");
429f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (sepIdx == -1) {
430f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    // Invalid URL, don't look here again
431f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return null;
432f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
433f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                sepIdx += 2;
434f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                prefixName = file.substring(sepIdx);
435f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
436f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
437f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                URL jarURL = ((JarURLConnection) url
438f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        .openConnection()).getJarFileURL();
439f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                JarURLConnection juc = (JarURLConnection) new URL(
440f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                        "jar", "",
441f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                        jarURL.toExternalForm() + "!/").openConnection();
442f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                JarFile jf = juc.getJarFile();
443f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                URLJarHandler jarH = new URLJarHandler(url, jarURL, jf, prefixName, null);
444f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // TODO : to think what we should do with indexes & manifest.class file here
445f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return jarH;
446f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (IOException e) {
447f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
448f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
449f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
450f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
451f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
452f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
453f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    class URLFileHandler extends URLHandler {
454f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        private String prefix;
455f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
456f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        public URLFileHandler(URL url) {
457f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            super(url);
458f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String baseFile = url.getFile();
459f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String host = url.getHost();
460f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            int hostLength = 0;
461f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (host != null) {
462f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                hostLength = host.length();
463f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
464f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            StringBuilder buf = new StringBuilder(2 + hostLength
465f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    + baseFile.length());
466f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (hostLength > 0) {
467f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                buf.append("//").append(host);
468f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
469f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            // baseFile always ends with '/'
470f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            buf.append(baseFile);
471f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            prefix = buf.toString();
472f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
473f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
474f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        @Override
475f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        Class<?> findClass(String packageName, String name, String origName) {
476f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String filename = prefix + name;
477f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
478f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                filename = URLDecoder.decode(filename, "UTF-8");
479f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (IllegalArgumentException e) {
480f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
481f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (UnsupportedEncodingException e) {
482f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
483f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
484f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
485f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            File file = new File(filename);
486f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (file.exists()) {
487f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                try {
488f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    InputStream is = new FileInputStream(file);
489f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return createClass(is, packageName, origName);
490f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } catch (FileNotFoundException e) {
491f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
492f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
493f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return null;
494f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
495f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
496f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        @Override
497f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        URL findResource(String name) {
498f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            int idx = 0;
499f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            String filename;
500f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
501f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            // Do not create a UNC path, i.e. \\host
502f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            while (idx < name.length() &&
503f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                   ((name.charAt(idx) == '/') || (name.charAt(idx) == '\\'))) {
504f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                idx++;
505f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
506f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
507f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (idx > 0) {
508f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                name = name.substring(idx);
509f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
510f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
511f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            try {
512f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                filename = URLDecoder.decode(prefix, "UTF-8") + name;
513f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
514f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (new File(filename).exists()) {
515f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return targetURL(url, name);
516f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
517f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
518f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (IllegalArgumentException e) {
519f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
520f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            } catch (UnsupportedEncodingException e) {
521f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // must not happen
522f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                throw new AssertionError(e);
523f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
524f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
525f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
526f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
527f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
528f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
529adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
530adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new {@code URLClassLoader} instance. The newly created
531adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * instance will have the system ClassLoader as its parent. URLs that end
532adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * with "/" are assumed to be directories, otherwise they are assumed to be
533adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * JAR files.
534f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
535adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param urls
536adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the list of URLs where a specific class or file could be
537adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            found.
538adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
539adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public URLClassLoader(URL[] urls) {
540adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this(urls, ClassLoader.getSystemClassLoader(), null);
541adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
542adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
543adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
544adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new URLClassLoader instance. The newly created instance will
545adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * have the system ClassLoader as its parent. URLs that end with "/" are
546adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * assumed to be directories, otherwise they are assumed to be JAR files.
547f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
548adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param urls
549adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the list of URLs where a specific class or file could be
550adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            found.
551adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param parent
552adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the class loader to assign as this loader's parent.
553adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
554adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public URLClassLoader(URL[] urls, ClassLoader parent) {
555adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this(urls, parent, null);
556adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
557adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
558adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
559adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Adds the specified URL to the search list.
560f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
561adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param url
562adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL which is to add.
563adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
564adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected void addURL(URL url) {
565adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
566f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            originalUrls.add(url);
567f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            searchList.add(createSearchURL(url));
568adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (MalformedURLException e) {
569adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
570adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
571adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
572adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
573adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns all known URLs which point to the specified resource.
574f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
575adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
576adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the requested resource.
577adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the enumeration of URLs which point to the specified resource.
578adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
579adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an I/O error occurs while attempting to connect.
580adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
581adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
582adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Enumeration<URL> findResources(final String name) throws IOException {
583adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (name == null) {
584adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return null;
585adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
586ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        ArrayList<URL> result = new ArrayList<URL>();
587f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        int n = 0;
588f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        while (true) {
589f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URLHandler handler = getHandler(n++);
590f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (handler == null) {
591f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                break;
592adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
593f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            handler.findResources(name, result);
594adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
595ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        return Collections.enumeration(result);
596adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
597adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
598adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
599adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Gets all permissions for the specified {@code codesource}. First, this
600adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * method retrieves the permissions from the system policy. If the protocol
601adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * is "file:/" then a new permission, {@code FilePermission}, granting the
602adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * read permission to the file is added to the permission collection.
603adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Otherwise, connecting to and accepting connections from the URL is
604adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * granted.
605f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
606adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param codesource
607adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the code source object whose permissions have to be known.
608adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the list of permissions according to the code source object.
609adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
610adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
611adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected PermissionCollection getPermissions(final CodeSource codesource) {
612adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        PermissionCollection pc = super.getPermissions(codesource);
613adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        URL u = codesource.getLocation();
614f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (u.getProtocol().equals("jar")) {
615adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            try {
616adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // Create a URL for the resource the jar refers to
617adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                u = ((JarURLConnection) u.openConnection()).getJarFileURL();
618adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } catch (IOException e) {
619adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // This should never occur. If it does continue using the jar
620adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // URL
621adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
622adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
623f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (u.getProtocol().equals("file")) {
624adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String path = u.getFile();
625adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String host = u.getHost();
626adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (host != null && host.length() > 0) {
627f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                path = "//" + host + path;
628adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
629adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
630adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (File.separatorChar != '/') {
631adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                path = path.replace('/', File.separatorChar);
632adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
633adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (isDirectory(u)) {
634f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                pc.add(new FilePermission(path + "-", "read"));
635adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
636f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                pc.add(new FilePermission(path, "read"));
637adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
638adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
639adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String host = u.getHost();
640adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (host.length() == 0) {
641f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                host = "localhost";
642adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
643f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            pc.add(new SocketPermission(host, "connect, accept"));
644adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
645adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return pc;
646adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
647adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
648adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
649adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the search list of this {@code URLClassLoader}.
650f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
651adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the list of all known URLs of this instance.
652adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
653adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public URL[] getURLs() {
654f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return originalUrls.toArray(new URL[originalUrls.size()]);
655adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
656adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
657adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
658adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Determines if the URL is pointing to a directory.
659adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
660adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static boolean isDirectory(URL url) {
661adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String file = url.getFile();
662adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return (file.length() > 0 && file.charAt(file.length() - 1) == '/');
663adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
664adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
665adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
666adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns a new {@code URLClassLoader} instance for the given URLs and the
667ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes     * system {@code ClassLoader} as its parent.
668f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
669adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param urls
670adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the list of URLs that is passed to the new {@code
671ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes     *            URLClassLoader}.
672adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the created {@code URLClassLoader} instance.
673adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
674adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static URLClassLoader newInstance(final URL[] urls) {
675ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        return new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
676adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
677adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
678adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
679adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns a new {@code URLClassLoader} instance for the given URLs and the
680ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes     * specified {@code ClassLoader} as its parent.
681f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
682adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param urls
683ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes     *            the list of URLs that is passed to the new URLClassLoader.
684adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param parentCl
685adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the parent class loader that is passed to the new
686ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes     *            URLClassLoader.
687adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the created {@code URLClassLoader} instance.
688adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
689ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes    public static URLClassLoader newInstance(final URL[] urls, final ClassLoader parentCl) {
690ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        return new URLClassLoader(urls, parentCl);
691adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
692adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
693adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
694adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new {@code URLClassLoader} instance. The newly created
695adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * instance will have the specified {@code ClassLoader} as its parent and
696adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * use the specified factory to create stream handlers. URLs that end with
697adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * "/" are assumed to be directories, otherwise they are assumed to be JAR
698adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * files.
699f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
700adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param searchUrls
701adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the list of URLs where a specific class or file could be
702adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            found.
703adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param parent
704adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the {@code ClassLoader} to assign as this loader's parent.
705adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param factory
706adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the factory that will be used to create protocol-specific
707adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            stream handlers.
708adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
709ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes    public URLClassLoader(URL[] searchUrls, ClassLoader parent, URLStreamHandlerFactory factory) {
710adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        super(parent);
711adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.factory = factory;
712adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int nbUrls = searchUrls.length;
713f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        originalUrls = new ArrayList<URL>(nbUrls);
714f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        handlerList = new ArrayList<URLHandler>(nbUrls);
715f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        searchList = Collections.synchronizedList(new ArrayList<URL>(nbUrls));
716adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int i = 0; i < nbUrls; i++) {
717f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            originalUrls.add(searchUrls[i]);
718adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            try {
719f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                searchList.add(createSearchURL(searchUrls[i]));
720adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } catch (MalformedURLException e) {
721adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
722adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
723adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
724adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
725adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
726adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Tries to locate and load the specified class using the known URLs. If the
727adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * class could be found, a class object representing the loaded class will
728adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * be returned.
729f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
730adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws ClassNotFoundException
731adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if the specified class cannot be loaded.
732adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
733adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
73487548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes    protected Class<?> findClass(final String className) throws ClassNotFoundException {
73587548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        String partialName = className.replace('.', '/');
73687548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        final String classFileName = new StringBuilder(partialName).append(".class").toString();
73787548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        String packageName = null;
73887548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        int position = partialName.lastIndexOf('/');
73987548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        if ((position = partialName.lastIndexOf('/')) != -1) {
74087548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            packageName = partialName.substring(0, position);
741adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
74287548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        int n = 0;
74387548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        while (true) {
74487548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            URLHandler handler = getHandler(n++);
74587548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            if (handler == null) {
74687548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes                break;
74787548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            }
74887548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            Class<?> res = handler.findClass(packageName, classFileName, className);
74987548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            if (res != null) {
75087548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes                return res;
75187548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            }
75287548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        }
75387548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes        throw new ClassNotFoundException(className);
754adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
755adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
756adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
757adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns an URL that will be checked if it contains the class or resource.
758adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * If the file component of the URL is not a directory, a Jar URL will be
759adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * created.
760f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
761adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return java.net.URL a test URL
762adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
763adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private URL createSearchURL(URL url) throws MalformedURLException {
764adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (url == null) {
765adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return url;
766adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
767adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
768adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String protocol = url.getProtocol();
769adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
770f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (isDirectory(url) || protocol.equals("jar")) {
771adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return url;
772adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
773adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (factory == null) {
774f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            return new URL("jar", "",
775f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    -1, url.toString() + "!/");
776adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
777f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        // use jar protocol as the stream handler protocol
778f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        return new URL("jar", "",
779f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                -1, url.toString() + "!/",
780fd6bb3510c2f94d636f3572dcf5f7f4dcd1a2726Elliott Hughes                factory.createURLStreamHandler("jar"));
781adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
782adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
783adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
784adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns an URL referencing the specified resource or {@code null} if the
785adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * resource could not be found.
786f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
787adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
788adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the requested resource.
789adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the URL which points to the given resource.
790adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
791adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
792adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public URL findResource(final String name) {
793adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (name == null) {
794adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return null;
795adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
796f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        int n = 0;
797f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        while (true) {
798f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URLHandler handler = getHandler(n++);
799f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (handler == null) {
800f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                break;
801f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
80287548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes            URL res = handler.findResource(name);
803f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (res != null) {
804f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return res;
805f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
806f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
807f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return null;
808f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
809f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
81087548e8585334658c3ee89050ec917a10ca35e5aElliott Hughes    private URLHandler getHandler(int num) {
811f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (num < handlerList.size()) {
812f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return handlerList.get(num);
813f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
814f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        makeNewHandler();
815f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (num < handlerList.size()) {
816f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return handlerList.get(num);
817f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
818f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return null;
819f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
820f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
821f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private synchronized void makeNewHandler() {
822f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        while (!searchList.isEmpty()) {
823f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL nextCandidate = searchList.remove(0);
824b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            if (nextCandidate == null) {
82586acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("nextCandidate == null");
826f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
827f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (!handlerMap.containsKey(nextCandidate)) {
828f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                URLHandler result;
829f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                String protocol = nextCandidate.getProtocol();
830f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                if (protocol.equals("jar")) {
831f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    result = createURLJarHandler(nextCandidate);
832f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                } else if (protocol.equals("file")) {
833f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    result = createURLFileHandler(nextCandidate);
834f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                } else {
835f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    result = createURLHandler(nextCandidate);
836f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
837f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                if (result != null) {
838f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    handlerMap.put(nextCandidate, result);
839f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    handlerList.add(result);
840f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    return;
841f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                }
842f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
843f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
844f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
845adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
846f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private URLHandler createURLHandler(URL url) {
847f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return new URLHandler(url);
848f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
849f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
850f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private URLHandler createURLFileHandler(URL url) {
851f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return new URLFileHandler(url);
852f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    }
853f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
854f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private URLHandler createURLJarHandler(URL url) {
855f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        String prefixName;
856f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        String file = url.getFile();
857f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (url.getFile().endsWith("!/")) {
858f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            prefixName = "";
859f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        } else {
860f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            int sepIdx = file.lastIndexOf("!/");
861f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (sepIdx == -1) {
862f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                // Invalid URL, don't look here again
863f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                return null;
864f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            }
865f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            sepIdx += 2;
866f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            prefixName = file.substring(sepIdx);
867f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
868f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        try {
869f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URL jarURL = ((JarURLConnection) url
870f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    .openConnection()).getJarFileURL();
871f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            JarURLConnection juc = (JarURLConnection) new URL(
872f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    "jar", "",
873f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    jarURL.toExternalForm() + "!/").openConnection();
874f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            JarFile jf = juc.getJarFile();
875f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            URLJarHandler jarH = new URLJarHandler(url, jarURL, jf, prefixName);
876f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
877f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (jarH.getIndex() == null) {
878f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                try {
879f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    Manifest manifest = jf.getManifest();
880f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    if (manifest != null) {
881f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        String classpath = manifest.getMainAttributes().getValue(
882f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                Attributes.Name.CLASS_PATH);
883f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        if (classpath != null) {
884f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            searchList.addAll(0, getInternalURLs(url, classpath));
885adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        }
886adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
887adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } catch (IOException e) {
888adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
889adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
890f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            return jarH;
891f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        } catch (IOException e) {
892adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
893adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return null;
894adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
895adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
896adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
897adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Defines a new package using the information extracted from the specified
898adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * manifest.
899f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
900adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param packageName
901adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the new package.
902adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param manifest
903adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the manifest containing additional information for the new
904adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            package.
905adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param url
906adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL to the code source for the new package.
907adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the created package.
908adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalArgumentException
909adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if a package with the given name already exists.
910adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
911adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected Package definePackage(String packageName, Manifest manifest,
912f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                    URL url) throws IllegalArgumentException {
913adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Attributes mainAttributes = manifest.getMainAttributes();
914f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        String dirName = packageName.replace('.', '/') + "/";
915adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Attributes packageAttributes = manifest.getAttributes(dirName);
916adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        boolean noEntry = false;
917adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (packageAttributes == null) {
918adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            noEntry = true;
919adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            packageAttributes = mainAttributes;
920adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
921adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String specificationTitle = packageAttributes
922adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getValue(Attributes.Name.SPECIFICATION_TITLE);
923adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (specificationTitle == null && !noEntry) {
924adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            specificationTitle = mainAttributes
925adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .getValue(Attributes.Name.SPECIFICATION_TITLE);
926adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
927adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String specificationVersion = packageAttributes
928adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getValue(Attributes.Name.SPECIFICATION_VERSION);
929adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (specificationVersion == null && !noEntry) {
930adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            specificationVersion = mainAttributes
931adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .getValue(Attributes.Name.SPECIFICATION_VERSION);
932adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
933adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String specificationVendor = packageAttributes
934adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getValue(Attributes.Name.SPECIFICATION_VENDOR);
935adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (specificationVendor == null && !noEntry) {
936adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            specificationVendor = mainAttributes
937adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .getValue(Attributes.Name.SPECIFICATION_VENDOR);
938adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
939adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String implementationTitle = packageAttributes
940adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
941adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (implementationTitle == null && !noEntry) {
942adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            implementationTitle = mainAttributes
943adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
944adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
945adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String implementationVersion = packageAttributes
946adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
947adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (implementationVersion == null && !noEntry) {
948adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            implementationVersion = mainAttributes
949adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
950adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
951adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String implementationVendor = packageAttributes
952adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
953adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (implementationVendor == null && !noEntry) {
954adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            implementationVendor = mainAttributes
955adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
956adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
957adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
958adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return definePackage(packageName, specificationTitle,
959adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                specificationVersion, specificationVendor, implementationTitle,
960adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                implementationVersion, implementationVendor, isSealed(manifest,
961f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                dirName) ? url : null);
962adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
963adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
964adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean isSealed(Manifest manifest, String dirName) {
965adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Attributes attributes = manifest.getAttributes(dirName);
966adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (attributes != null) {
96778e3320540c8bdcbefba5ae1222ee18f6679ab33Elliott Hughes            String value = attributes.getValue(Attributes.Name.SEALED);
968adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (value != null) {
96978e3320540c8bdcbefba5ae1222ee18f6679ab33Elliott Hughes                return value.equalsIgnoreCase("true");
970adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
971adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
97278e3320540c8bdcbefba5ae1222ee18f6679ab33Elliott Hughes        Attributes mainAttributes = manifest.getMainAttributes();
97378e3320540c8bdcbefba5ae1222ee18f6679ab33Elliott Hughes        String value = mainAttributes.getValue(Attributes.Name.SEALED);
97478e3320540c8bdcbefba5ae1222ee18f6679ab33Elliott Hughes        return (value != null && value.equalsIgnoreCase("true"));
975adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
976adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
977adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
978adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * returns URLs referenced in the string classpath.
979f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
980adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param root
981adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the jar URL that classpath is related to
982adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param classpath
983adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the relative URLs separated by spaces
984adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return URL[] the URLs contained in the string classpath.
985adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
986f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private ArrayList<URL> getInternalURLs(URL root, String classpath) {
987adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Class-path attribute is composed of space-separated values.
9885839b909d9528b7726e678a4b696ed37df15d897Jesse Wilson        StringTokenizer tokenizer = new StringTokenizer(classpath);
989f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        ArrayList<URL> addedURLs = new ArrayList<URL>();
990f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        String file = root.getFile();
991f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        int jarIndex = file.lastIndexOf("!/") - 1;
992f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        int index = file.lastIndexOf("/", jarIndex) + 1;
993adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (index == 0) {
994adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            index = file.lastIndexOf(
995f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    System.getProperty("file.separator"), jarIndex) + 1;
996adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
997adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        file = file.substring(0, index);
998adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (tokenizer.hasMoreElements()) {
999adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String element = tokenizer.nextToken();
100080a7fbab52b96c9fd47c72f8987d1babe2cd001dElliott Hughes            if (!element.isEmpty()) {
1001adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                try {
1002a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson                    // Take absolute path case into consideration
1003a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson                    URL url = new URL(new URL(file), element);
1004a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson                    addedURLs.add(createSearchURL(url));
1005adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } catch (MalformedURLException e) {
1006adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    // Nothing is added
1007adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1008adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1009adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1010f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        return addedURLs;
1011adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1012adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
1013