KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > kernel > classloader > UrlResourceFinder


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.geronimo.kernel.classloader;
18
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.FileNotFoundException JavaDoc;
22 import java.net.MalformedURLException JavaDoc;
23 import java.net.URL JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.LinkedHashMap JavaDoc;
30 import java.util.LinkedHashSet JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.StringTokenizer JavaDoc;
35 import java.util.jar.Attributes JavaDoc;
36 import java.util.jar.Manifest JavaDoc;
37 import java.util.jar.JarFile JavaDoc;
38
39 /**
40  * @version $Rev: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $
41  */

42 public class UrlResourceFinder implements ResourceFinder {
43     private final Object JavaDoc lock = new Object JavaDoc();
44
45     private final LinkedHashSet JavaDoc urls = new LinkedHashSet JavaDoc();
46     private final LinkedHashMap JavaDoc classPath = new LinkedHashMap JavaDoc();
47     private final LinkedHashSet JavaDoc watchedFiles = new LinkedHashSet JavaDoc();
48
49     private boolean destroyed = false;
50
51     public UrlResourceFinder() {
52     }
53
54     public UrlResourceFinder(URL JavaDoc[] urls) {
55         addUrls(urls);
56     }
57
58     public void destroy() {
59         synchronized (lock) {
60             if (destroyed) {
61                 return;
62             }
63             destroyed = true;
64             urls.clear();
65             for (Iterator JavaDoc iterator = classPath.values().iterator(); iterator.hasNext();) {
66                 ResourceLocation resourceLocation = (ResourceLocation) iterator.next();
67                 resourceLocation.close();
68             }
69             classPath.clear();
70         }
71     }
72
73     public ResourceHandle getResource(String JavaDoc resourceName) {
74         synchronized (lock) {
75             if (destroyed) {
76                 return null;
77             }
78             for (Iterator JavaDoc iterator = getClassPath().entrySet().iterator(); iterator.hasNext();) {
79                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
80                 ResourceLocation resourceLocation = (ResourceLocation) entry.getValue();
81                 ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName);
82                 if (resourceHandle != null && !resourceHandle.isDirectory()) {
83                     return resourceHandle;
84                 }
85             }
86         }
87         return null;
88     }
89
90     public URL JavaDoc findResource(String JavaDoc resourceName) {
91         synchronized (lock) {
92             if (destroyed) {
93                 return null;
94             }
95             for (Iterator JavaDoc iterator = getClassPath().entrySet().iterator(); iterator.hasNext();) {
96                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
97                 ResourceLocation resourceLocation = (ResourceLocation) entry.getValue();
98                 ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName);
99                 if (resourceHandle != null) {
100                     return resourceHandle.getUrl();
101                 }
102             }
103         }
104         return null;
105     }
106
107     public Enumeration JavaDoc findResources(String JavaDoc resourceName) {
108         synchronized (lock) {
109             return new ResourceEnumeration(new ArrayList JavaDoc(getClassPath().values()), resourceName);
110         }
111     }
112
113     public void addUrl(URL JavaDoc url) {
114         addUrls(Collections.singletonList(url));
115     }
116
117     public URL JavaDoc[] getUrls() {
118         synchronized (lock) {
119             return (URL JavaDoc[]) urls.toArray(new URL JavaDoc[urls.size()]);
120         }
121     }
122
123     /**
124      * Adds an array of urls to the end of this class loader.
125      * @param urls the URLs to add
126      */

127     protected void addUrls(URL JavaDoc[] urls) {
128         addUrls(Arrays.asList(urls));
129     }
130
131     /**
132      * Adds a list of urls to the end of this class loader.
133      * @param urls the URLs to add
134      */

135     protected void addUrls(List JavaDoc urls) {
136         synchronized (lock) {
137             if (destroyed) {
138                 throw new IllegalStateException JavaDoc("UrlResourceFinder has been destroyed");
139             }
140
141             boolean shouldRebuild = this.urls.addAll(urls);
142             if (shouldRebuild) {
143                 rebuildClassPath();
144             }
145         }
146     }
147
148     private LinkedHashMap JavaDoc getClassPath() {
149         assert Thread.holdsLock(lock): "This method can only be called while holding the lock";
150
151         for (Iterator JavaDoc iterator = watchedFiles.iterator(); iterator.hasNext();) {
152             File JavaDoc file = (File JavaDoc) iterator.next();
153             if (file.canRead()) {
154                 rebuildClassPath();
155                 break;
156             }
157         }
158
159         return classPath;
160     }
161
162     /**
163      * Rebuilds the entire class path. This class is called when new URLs are added or one of the watched files
164      * becomes readable. This method will not open jar files again, but will add any new entries not alredy open
165      * to the class path. If any file based url is does not exist, we will watch for that file to appear.
166      */

167     private void rebuildClassPath() {
168         assert Thread.holdsLock(lock): "This method can only be called while holding the lock";
169
170         // copy all of the existing locations into a temp map and clear the class path
171
Map JavaDoc existingJarFiles = new LinkedHashMap JavaDoc(classPath);
172         classPath.clear();
173
174         LinkedList JavaDoc locationStack = new LinkedList JavaDoc(urls);
175         try {
176             while (!locationStack.isEmpty()) {
177                 URL JavaDoc url = (URL JavaDoc) locationStack.removeFirst();
178
179                 // Skip any duplicate urls in the claspath
180
if (classPath.containsKey(url)) {
181                     continue;
182                 }
183
184                 // Check is this URL has already been opened
185
ResourceLocation resourceLocation = (ResourceLocation) existingJarFiles.remove(url);
186
187                 // If not opened, cache the url and wrap it with a resource location
188
if (resourceLocation == null) {
189                     try {
190                         File JavaDoc file = cacheUrl(url);
191                         resourceLocation = createResourceLocation(url, file);
192                     } catch (FileNotFoundException JavaDoc e) {
193                         // if this is a file URL, the file doesn't exist yet... watch to see if it appears later
194
if ("file".equals(url.getProtocol())) {
195                             File JavaDoc file = new File JavaDoc(url.getPath());
196                             watchedFiles.add(file);
197                             continue;
198
199                         }
200                     } catch (IOException JavaDoc ignored) {
201                         // can't seem to open the file... this is most likely a bad jar file
202
// so don't keep a watch out for it because that would require lots of checking
203
// Dain: We may want to review this decision later
204
continue;
205                     }
206                 }
207
208                 // add the jar to our class path
209
classPath.put(resourceLocation.getCodeSource(), resourceLocation);
210
211                 // push the manifest classpath on the stack (make sure to maintain the order)
212
List JavaDoc manifestClassPath = getManifestClassPath(resourceLocation);
213                 locationStack.addAll(0, manifestClassPath);
214             }
215         } catch (Error JavaDoc e) {
216             destroy();
217             throw e;
218         }
219
220         for (Iterator JavaDoc iterator = existingJarFiles.values().iterator(); iterator.hasNext();) {
221             ResourceLocation resourceLocation = (ResourceLocation) iterator.next();
222             resourceLocation.close();
223         }
224     }
225
226     protected File JavaDoc cacheUrl(URL JavaDoc url) throws IOException JavaDoc {
227         if (!"file".equals(url.getProtocol())) {
228             // download the jar
229
throw new Error JavaDoc("Only local file jars are supported " + url);
230         }
231
232         File JavaDoc file = new File JavaDoc(url.getPath());
233         if (!file.exists()) {
234             throw new FileNotFoundException JavaDoc(file.getAbsolutePath());
235         }
236         if (!file.canRead()) {
237             throw new IOException JavaDoc("File is not readable: " + file.getAbsolutePath());
238         }
239         return file;
240     }
241
242     protected ResourceLocation createResourceLocation(URL JavaDoc codeSource, File JavaDoc cacheFile) throws IOException JavaDoc {
243         if (!cacheFile.exists()) {
244             throw new FileNotFoundException JavaDoc(cacheFile.getAbsolutePath());
245         }
246         if (!cacheFile.canRead()) {
247             throw new IOException JavaDoc("File is not readable: " + cacheFile.getAbsolutePath());
248         }
249
250         ResourceLocation resourceLocation = null;
251         if (cacheFile.isDirectory()) {
252             // DirectoryResourceLocation will only return "file" URLs within this directory
253
// do not user the DirectoryResourceLocation for non file based urls
254
resourceLocation = new DirectoryResourceLocation(cacheFile);
255         } else {
256             resourceLocation = new JarResourceLocation(codeSource, new JarFile JavaDoc(cacheFile));
257         }
258         return resourceLocation;
259     }
260
261     private List JavaDoc getManifestClassPath(ResourceLocation resourceLocation) {
262         try {
263             // get the manifest, if possible
264
Manifest JavaDoc manifest = resourceLocation.getManifest();
265             if (manifest == null) {
266                 // some locations don't have a manifest
267
return Collections.EMPTY_LIST;
268             }
269
270             // get the class-path attribute, if possible
271
String JavaDoc manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
272             if (manifestClassPath == null) {
273                 return Collections.EMPTY_LIST;
274             }
275
276             // build the urls...
277
// the class-path attribute is space delimited
278
URL JavaDoc codeSource = resourceLocation.getCodeSource();
279             LinkedList JavaDoc classPathUrls = new LinkedList JavaDoc();
280             for (StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
281                 String JavaDoc entry = tokenizer.nextToken();
282                 try {
283                     // the class path entry is relative to the resource location code source
284
URL JavaDoc entryUrl = new URL JavaDoc(codeSource, entry);
285                     classPathUrls.addLast(entryUrl);
286                 } catch (MalformedURLException JavaDoc ignored) {
287                     // most likely a poorly named entry
288
}
289             }
290             return classPathUrls;
291         } catch (IOException JavaDoc ignored) {
292             // error opening the manifest
293
return Collections.EMPTY_LIST;
294         }
295     }
296 }
297
Popular Tags