History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/misc/URLClassPath.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/misc/URLClassPath.java+1074−0
@@ -0,0 +1,1074 @@
1+/*
2+ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.misc;
27+
28+import java.util.*;
29+import java.util.jar.JarFile;
30+import sun.misc.JarIndex;
31+import sun.misc.InvalidJarIndexException;
32+import sun.net.www.ParseUtil;
33+import java.util.zip.ZipEntry;
34+import java.util.jar.JarEntry;
35+import java.util.jar.Manifest;
36+import java.util.jar.Attributes;
37+import java.util.jar.Attributes.Name;
38+import java.net.JarURLConnection;
39+import java.net.MalformedURLException;
40+import java.net.URL;
41+import java.net.URLConnection;
42+import java.net.HttpURLConnection;
43+import java.net.URLStreamHandler;
44+import java.net.URLStreamHandlerFactory;
45+import java.io.*;
46+import java.security.AccessController;
47+import java.security.AccessControlException;
48+import java.security.CodeSigner;
49+import java.security.Permission;
50+import java.security.PrivilegedAction;
51+import java.security.PrivilegedExceptionAction;
52+import java.security.cert.Certificate;
53+import sun.misc.FileURLMapper;
54+import sun.net.util.URLUtil;
55+
56+/**
57+ * This class is used to maintain a search path of URLs for loading classes
58+ * and resources from both JAR files and directories.
59+ *
60+ * @author David Connelly
61+ */
62+public class URLClassPath {
63+ final static String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
64+ final static String JAVA_VERSION;
65+ private static final boolean DEBUG;
66+
67+ static {
68+ JAVA_VERSION = java.security.AccessController.doPrivileged(
69+ new sun.security.action.GetPropertyAction("java.version"));
70+ DEBUG = (java.security.AccessController.doPrivileged(
71+ new sun.security.action.GetPropertyAction("sun.misc.URLClassPath.debug")) != null);
72+ }
73+
74+ /* The original search path of URLs. */
75+ private ArrayList<URL> path = new ArrayList<URL>();
76+
77+ /* The stack of unopened URLs */
78+ Stack<URL> urls = new Stack<URL>();
79+
80+ /* The resulting search path of Loaders */
81+ ArrayList<Loader> loaders = new ArrayList<Loader>();
82+
83+ /* Map of each URL opened to its corresponding Loader */
84+ HashMap<String, Loader> lmap = new HashMap<String, Loader>();
85+
86+ /* The jar protocol handler to use when creating new URLs */
87+ private URLStreamHandler jarHandler;
88+
89+ /* Whether this URLClassLoader has been closed yet */
90+ private boolean closed = false;
91+
92+ /**
93+ * Creates a new URLClassPath for the given URLs. The URLs will be
94+ * searched in the order specified for classes and resources. A URL
95+ * ending with a '/' is assumed to refer to a directory. Otherwise,
96+ * the URL is assumed to refer to a JAR file.
97+ *
98+ * @param urls the directory and JAR file URLs to search for classes
99+ * and resources
100+ * @param factory the URLStreamHandlerFactory to use when creating new URLs
101+ */
102+ public URLClassPath(URL[] urls, URLStreamHandlerFactory factory) {
103+ for (int i = 0; i < urls.length; i++) {
104+ path.add(urls[i]);
105+ }
106+ push(urls);
107+ if (factory != null) {
108+ jarHandler = factory.createURLStreamHandler("jar");
109+ }
110+ }
111+
112+ public URLClassPath(URL[] urls) {
113+ this(urls, null);
114+ }
115+
116+ public synchronized List<IOException> closeLoaders() {
117+ if (closed) {
118+ return Collections.emptyList();
119+ }
120+ List<IOException> result = new LinkedList<IOException>();
121+ for (Loader loader : loaders) {
122+ try {
123+ loader.close();
124+ } catch (IOException e) {
125+ result.add (e);
126+ }
127+ }
128+ closed = true;
129+ return result;
130+ }
131+
132+ /**
133+ * Appends the specified URL to the search path of directory and JAR
134+ * file URLs from which to load classes and resources.
135+ * <p>
136+ * If the URL specified is null or is already in the list of
137+ * URLs, then invoking this method has no effect.
138+ */
139+ public synchronized void addURL(URL url) {
140+ if (closed)
141+ return;
142+ synchronized (urls) {
143+ if (url == null || path.contains(url))
144+ return;
145+
146+ urls.add(0, url);
147+ path.add(url);
148+ }
149+ }
150+
151+ /**
152+ * Returns the original search path of URLs.
153+ */
154+ public URL[] getURLs() {
155+ synchronized (urls) {
156+ return path.toArray(new URL[path.size()]);
157+ }
158+ }
159+
160+ /**
161+ * Finds the resource with the specified name on the URL search path
162+ * or null if not found or security check fails.
163+ *
164+ * @param name the name of the resource
165+ * @param check whether to perform a security check
166+ * @return a <code>URL</code> for the resource, or <code>null</code>
167+ * if the resource could not be found.
168+ */
169+ public URL findResource(String name, boolean check) {
170+ Loader loader;
171+ for (int i = 0; (loader = getLoader(i)) != null; i++) {
172+ URL url = loader.findResource(name, check);
173+ if (url != null) {
174+ return url;
175+ }
176+ }
177+ return null;
178+ }
179+
180+ /**
181+ * Finds the first Resource on the URL search path which has the specified
182+ * name. Returns null if no Resource could be found.
183+ *
184+ * @param name the name of the Resource
185+ * @param check whether to perform a security check
186+ * @return the Resource, or null if not found
187+ */
188+ public Resource getResource(String name, boolean check) {
189+ if (DEBUG) {
190+ System.err.println("URLClassPath.getResource(\"" + name + "\")");
191+ }
192+
193+ Loader loader;
194+ for (int i = 0; (loader = getLoader(i)) != null; i++) {
195+ Resource res = loader.getResource(name, check);
196+ if (res != null) {
197+ return res;
198+ }
199+ }
200+ return null;
201+ }
202+
203+ /**
204+ * Finds all resources on the URL search path with the given name.
205+ * Returns an enumeration of the URL objects.
206+ *
207+ * @param name the resource name
208+ * @return an Enumeration of all the urls having the specified name
209+ */
210+ public Enumeration<URL> findResources(final String name,
211+ final boolean check) {
212+ return new Enumeration<URL>() {
213+ private int index = 0;
214+ private URL url = null;
215+
216+ private boolean next() {
217+ if (url != null) {
218+ return true;
219+ } else {
220+ Loader loader;
221+ while ((loader = getLoader(index++)) != null) {
222+ url = loader.findResource(name, check);
223+ if (url != null) {
224+ return true;
225+ }
226+ }
227+ return false;
228+ }
229+ }
230+
231+ public boolean hasMoreElements() {
232+ return next();
233+ }
234+
235+ public URL nextElement() {
236+ if (!next()) {
237+ throw new NoSuchElementException();
238+ }
239+ URL u = url;
240+ url = null;
241+ return u;
242+ }
243+ };
244+ }
245+
246+ public Resource getResource(String name) {
247+ return getResource(name, true);
248+ }
249+
250+ /**
251+ * Finds all resources on the URL search path with the given name.
252+ * Returns an enumeration of the Resource objects.
253+ *
254+ * @param name the resource name
255+ * @return an Enumeration of all the resources having the specified name
256+ */
257+ public Enumeration<Resource> getResources(final String name,
258+ final boolean check) {
259+ return new Enumeration<Resource>() {
260+ private int index = 0;
261+ private Resource res = null;
262+
263+ private boolean next() {
264+ if (res != null) {
265+ return true;
266+ } else {
267+ Loader loader;
268+ while ((loader = getLoader(index++)) != null) {
269+ res = loader.getResource(name, check);
270+ if (res != null) {
271+ return true;
272+ }
273+ }
274+ return false;
275+ }
276+ }
277+
278+ public boolean hasMoreElements() {
279+ return next();
280+ }
281+
282+ public Resource nextElement() {
283+ if (!next()) {
284+ throw new NoSuchElementException();
285+ }
286+ Resource r = res;
287+ res = null;
288+ return r;
289+ }
290+ };
291+ }
292+
293+ public Enumeration<Resource> getResources(final String name) {
294+ return getResources(name, true);
295+ }
296+
297+ /*
298+ * Returns the Loader at the specified position in the URL search
299+ * path. The URLs are opened and expanded as needed. Returns null
300+ * if the specified index is out of range.
301+ */
302+ private synchronized Loader getLoader(int index) {
303+ if (closed) {
304+ return null;
305+ }
306+ // Expand URL search path until the request can be satisfied
307+ // or the URL stack is empty.
308+ while (loaders.size() < index + 1) {
309+ // Pop the next URL from the URL stack
310+ URL url;
311+ synchronized (urls) {
312+ if (urls.empty()) {
313+ return null;
314+ } else {
315+ url = urls.pop();
316+ }
317+ }
318+ // Skip this URL if it already has a Loader. (Loader
319+ // may be null in the case where URL has not been opened
320+ // but is referenced by a JAR index.)
321+ String urlNoFragString = URLUtil.urlNoFragString(url);
322+ if (lmap.containsKey(urlNoFragString)) {
323+ continue;
324+ }
325+ // Otherwise, create a new Loader for the URL.
326+ Loader loader;
327+ try {
328+ loader = getLoader(url);
329+ // If the loader defines a local class path then add the
330+ // URLs to the list of URLs to be opened.
331+ URL[] urls = loader.getClassPath();
332+ if (urls != null) {
333+ push(urls);
334+ }
335+ } catch (IOException e) {
336+ // Silently ignore for now...
337+ continue;
338+ }
339+ // Finally, add the Loader to the search path.
340+ loaders.add(loader);
341+ lmap.put(urlNoFragString, loader);
342+ }
343+ return loaders.get(index);
344+ }
345+
346+ /*
347+ * Returns the Loader for the specified base URL.
348+ */
349+ private Loader getLoader(final URL url) throws IOException {
350+ try {
351+ return java.security.AccessController.doPrivileged(
352+ new java.security.PrivilegedExceptionAction<Loader>() {
353+ public Loader run() throws IOException {
354+ String file = url.getFile();
355+ if (file != null && file.endsWith("/")) {
356+ if ("file".equals(url.getProtocol())) {
357+ return new FileLoader(url);
358+ } else {
359+ return new Loader(url);
360+ }
361+ } else {
362+ return new JarLoader(url, jarHandler, lmap);
363+ }
364+ }
365+ });
366+ } catch (java.security.PrivilegedActionException pae) {
367+ throw (IOException)pae.getException();
368+ }
369+ }
370+
371+ /*
372+ * Pushes the specified URLs onto the list of unopened URLs.
373+ */
374+ private void push(URL[] us) {
375+ synchronized (urls) {
376+ for (int i = us.length - 1; i >= 0; --i) {
377+ urls.push(us[i]);
378+ }
379+ }
380+ }
381+
382+ /**
383+ * Convert class path specification into an array of file URLs.
384+ *
385+ * The path of the file is encoded before conversion into URL
386+ * form so that reserved characters can safely appear in the path.
387+ */
388+ public static URL[] pathToURLs(String path) {
389+ StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
390+ URL[] urls = new URL[st.countTokens()];
391+ int count = 0;
392+ while (st.hasMoreTokens()) {
393+ File f = new File(st.nextToken());
394+ try {
395+ f = new File(f.getCanonicalPath());
396+ } catch (IOException x) {
397+ // use the non-canonicalized filename
398+ }
399+ try {
400+ urls[count++] = ParseUtil.fileToEncodedURL(f);
401+ } catch (IOException x) { }
402+ }
403+
404+ if (urls.length != count) {
405+ URL[] tmp = new URL[count];
406+ System.arraycopy(urls, 0, tmp, 0, count);
407+ urls = tmp;
408+ }
409+ return urls;
410+ }
411+
412+ /*
413+ * Check whether the resource URL should be returned.
414+ * Return null on security check failure.
415+ * Called by java.net.URLClassLoader.
416+ */
417+ public URL checkURL(URL url) {
418+ try {
419+ check(url);
420+ } catch (Exception e) {
421+ return null;
422+ }
423+
424+ return url;
425+ }
426+
427+ /*
428+ * Check whether the resource URL should be returned.
429+ * Throw exception on failure.
430+ * Called internally within this file.
431+ */
432+ static void check(URL url) throws IOException {
433+ SecurityManager security = System.getSecurityManager();
434+ if (security != null) {
435+ URLConnection urlConnection = url.openConnection();
436+ Permission perm = urlConnection.getPermission();
437+ if (perm != null) {
438+ try {
439+ security.checkPermission(perm);
440+ } catch (SecurityException se) {
441+ // fallback to checkRead/checkConnect for pre 1.2
442+ // security managers
443+ if ((perm instanceof java.io.FilePermission) &&
444+ perm.getActions().indexOf("read") != -1) {
445+ security.checkRead(perm.getName());
446+ } else if ((perm instanceof
447+ java.net.SocketPermission) &&
448+ perm.getActions().indexOf("connect") != -1) {
449+ URL locUrl = url;
450+ if (urlConnection instanceof JarURLConnection) {
451+ locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
452+ }
453+ security.checkConnect(locUrl.getHost(),
454+ locUrl.getPort());
455+ } else {
456+ throw se;
457+ }
458+ }
459+ }
460+ }
461+ }
462+
463+ /**
464+ * Inner class used to represent a loader of resources and classes
465+ * from a base URL.
466+ */
467+ private static class Loader implements Closeable {
468+ private final URL base;
469+ private JarFile jarfile; // if this points to a jar file
470+
471+ /*
472+ * Creates a new Loader for the specified URL.
473+ */
474+ Loader(URL url) {
475+ base = url;
476+ }
477+
478+ /*
479+ * Returns the base URL for this Loader.
480+ */
481+ URL getBaseURL() {
482+ return base;
483+ }
484+
485+ URL findResource(final String name, boolean check) {
486+ URL url;
487+ try {
488+ url = new URL(base, ParseUtil.encodePath(name, false));
489+ } catch (MalformedURLException e) {
490+ throw new IllegalArgumentException("name");
491+ }
492+
493+ try {
494+ if (check) {
495+ URLClassPath.check(url);
496+ }
497+
498+ /*
499+ * For a HTTP connection we use the HEAD method to
500+ * check if the resource exists.
501+ */
502+ URLConnection uc = url.openConnection();
503+ if (uc instanceof HttpURLConnection) {
504+ HttpURLConnection hconn = (HttpURLConnection)uc;
505+ hconn.setRequestMethod("HEAD");
506+ if (hconn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
507+ return null;
508+ }
509+ } else {
510+ // our best guess for the other cases
511+ InputStream is = url.openStream();
512+ is.close();
513+ }
514+ return url;
515+ } catch (Exception e) {
516+ return null;
517+ }
518+ }
519+
520+ Resource getResource(final String name, boolean check) {
521+ final URL url;
522+ try {
523+ url = new URL(base, ParseUtil.encodePath(name, false));
524+ } catch (MalformedURLException e) {
525+ throw new IllegalArgumentException("name");
526+ }
527+ final URLConnection uc;
528+ try {
529+ if (check) {
530+ URLClassPath.check(url);
531+ }
532+ uc = url.openConnection();
533+ InputStream in = uc.getInputStream();
534+ if (uc instanceof JarURLConnection) {
535+ /* Need to remember the jar file so it can be closed
536+ * in a hurry.
537+ */
538+ JarURLConnection juc = (JarURLConnection)uc;
539+ jarfile = juc.getJarFile();
540+ }
541+ } catch (Exception e) {
542+ return null;
543+ }
544+ return new Resource() {
545+ public String getName() { return name; }
546+ public URL getURL() { return url; }
547+ public URL getCodeSourceURL() { return base; }
548+ public InputStream getInputStream() throws IOException {
549+ return uc.getInputStream();
550+ }
551+ public int getContentLength() throws IOException {
552+ return uc.getContentLength();
553+ }
554+ };
555+ }
556+
557+ /*
558+ * Returns the Resource for the specified name, or null if not
559+ * found or the caller does not have the permission to get the
560+ * resource.
561+ */
562+ Resource getResource(final String name) {
563+ return getResource(name, true);
564+ }
565+
566+ /*
567+ * close this loader and release all resources
568+ * method overridden in sub-classes
569+ */
570+ public void close () throws IOException {
571+ if (jarfile != null) {
572+ jarfile.close();
573+ }
574+ }
575+
576+ /*
577+ * Returns the local class path for this loader, or null if none.
578+ */
579+ URL[] getClassPath() throws IOException {
580+ return null;
581+ }
582+ }
583+
584+ /*
585+ * Inner class used to represent a Loader of resources from a JAR URL.
586+ */
587+ static class JarLoader extends Loader {
588+ private JarFile jar;
589+ private URL csu;
590+ private JarIndex index;
591+ private MetaIndex metaIndex;
592+ private URLStreamHandler handler;
593+ private HashMap<String, Loader> lmap;
594+ private boolean closed = false;
595+
596+ /*
597+ * Creates a new JarLoader for the specified URL referring to
598+ * a JAR file.
599+ */
600+ JarLoader(URL url, URLStreamHandler jarHandler,
601+ HashMap<String, Loader> loaderMap)
602+ throws IOException
603+ {
604+ super(new URL("jar", "", -1, url + "!/", jarHandler));
605+ csu = url;
606+ handler = jarHandler;
607+ lmap = loaderMap;
608+
609+ if (!isOptimizable(url)) {
610+ ensureOpen();
611+ } else {
612+ String fileName = url.getFile();
613+ if (fileName != null) {
614+ fileName = ParseUtil.decode(fileName);
615+ File f = new File(fileName);
616+ metaIndex = MetaIndex.forJar(f);
617+ // If the meta index is found but the file is not
618+ // installed, set metaIndex to null. A typical
619+ // senario is charsets.jar which won't be installed
620+ // when the user is running in certain locale environment.
621+ // The side effect of null metaIndex will cause
622+ // ensureOpen get called so that IOException is thrown.
623+ if (metaIndex != null && !f.exists()) {
624+ metaIndex = null;
625+ }
626+ }
627+
628+ // metaIndex is null when either there is no such jar file
629+ // entry recorded in meta-index file or such jar file is
630+ // missing in JRE. See bug 6340399.
631+ if (metaIndex == null) {
632+ ensureOpen();
633+ }
634+ }
635+ }
636+
637+ @Override
638+ public void close () throws IOException {
639+ // closing is synchronized at higher level
640+ if (!closed) {
641+ closed = true;
642+ // in case not already open.
643+ ensureOpen();
644+ jar.close();
645+ }
646+ }
647+
648+ JarFile getJarFile () {
649+ return jar;
650+ }
651+
652+ private boolean isOptimizable(URL url) {
653+ return "file".equals(url.getProtocol());
654+ }
655+
656+ private void ensureOpen() throws IOException {
657+ if (jar == null) {
658+ try {
659+ java.security.AccessController.doPrivileged(
660+ new java.security.PrivilegedExceptionAction<Void>() {
661+ public Void run() throws IOException {
662+ if (DEBUG) {
663+ System.err.println("Opening " + csu);
664+ Thread.dumpStack();
665+ }
666+
667+ jar = getJarFile(csu);
668+ index = JarIndex.getJarIndex(jar, metaIndex);
669+ if (index != null) {
670+ String[] jarfiles = index.getJarFiles();
671+ // Add all the dependent URLs to the lmap so that loaders
672+ // will not be created for them by URLClassPath.getLoader(int)
673+ // if the same URL occurs later on the main class path. We set
674+ // Loader to null here to avoid creating a Loader for each
675+ // URL until we actually need to try to load something from them.
676+ for(int i = 0; i < jarfiles.length; i++) {
677+ try {
678+ URL jarURL = new URL(csu, jarfiles[i]);
679+ // If a non-null loader already exists, leave it alone.
680+ String urlNoFragString = URLUtil.urlNoFragString(jarURL);
681+ if (!lmap.containsKey(urlNoFragString)) {
682+ lmap.put(urlNoFragString, null);
683+ }
684+ } catch (MalformedURLException e) {
685+ continue;
686+ }
687+ }
688+ }
689+ return null;
690+ }
691+ }
692+ );
693+ } catch (java.security.PrivilegedActionException pae) {
694+ throw (IOException)pae.getException();
695+ }
696+ }
697+ }
698+
699+ private JarFile getJarFile(URL url) throws IOException {
700+ // Optimize case where url refers to a local jar file
701+ if (isOptimizable(url)) {
702+ FileURLMapper p = new FileURLMapper (url);
703+ if (!p.exists()) {
704+ throw new FileNotFoundException(p.getPath());
705+ }
706+ return new JarFile (p.getPath());
707+ }
708+ URLConnection uc = getBaseURL().openConnection();
709+ uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION);
710+ return ((JarURLConnection)uc).getJarFile();
711+ }
712+
713+ /*
714+ * Returns the index of this JarLoader if it exists.
715+ */
716+ JarIndex getIndex() {
717+ try {
718+ ensureOpen();
719+ } catch (IOException e) {
720+ throw (InternalError) new InternalError().initCause(e);
721+ }
722+ return index;
723+ }
724+
725+ /*
726+ * Creates the resource and if the check flag is set to true, checks if
727+ * is its okay to return the resource.
728+ */
729+ Resource checkResource(final String name, boolean check,
730+ final JarEntry entry) {
731+
732+ final URL url;
733+ try {
734+ url = new URL(getBaseURL(), ParseUtil.encodePath(name, false));
735+ if (check) {
736+ URLClassPath.check(url);
737+ }
738+ } catch (MalformedURLException e) {
739+ return null;
740+ // throw new IllegalArgumentException("name");
741+ } catch (IOException e) {
742+ return null;
743+ } catch (AccessControlException e) {
744+ return null;
745+ }
746+
747+ return new Resource() {
748+ public String getName() { return name; }
749+ public URL getURL() { return url; }
750+ public URL getCodeSourceURL() { return csu; }
751+ public InputStream getInputStream() throws IOException
752+ { return jar.getInputStream(entry); }
753+ public int getContentLength()
754+ { return (int)entry.getSize(); }
755+ public Manifest getManifest() throws IOException
756+ { return jar.getManifest(); };
757+ public Certificate[] getCertificates()
758+ { return entry.getCertificates(); };
759+ public CodeSigner[] getCodeSigners()
760+ { return entry.getCodeSigners(); };
761+ };
762+ }
763+
764+
765+ /*
766+ * Returns true iff atleast one resource in the jar file has the same
767+ * package name as that of the specified resource name.
768+ */
769+ boolean validIndex(final String name) {
770+ String packageName = name;
771+ int pos;
772+ if((pos = name.lastIndexOf("/")) != -1) {
773+ packageName = name.substring(0, pos);
774+ }
775+
776+ String entryName;
777+ ZipEntry entry;
778+ Enumeration<JarEntry> enum_ = jar.entries();
779+ while (enum_.hasMoreElements()) {
780+ entry = enum_.nextElement();
781+ entryName = entry.getName();
782+ if((pos = entryName.lastIndexOf("/")) != -1)
783+ entryName = entryName.substring(0, pos);
784+ if (entryName.equals(packageName)) {
785+ return true;
786+ }
787+ }
788+ return false;
789+ }
790+
791+ /*
792+ * Returns the URL for a resource with the specified name
793+ */
794+ URL findResource(final String name, boolean check) {
795+ Resource rsc = getResource(name, check);
796+ if (rsc != null) {
797+ return rsc.getURL();
798+ }
799+ return null;
800+ }
801+
802+ /*
803+ * Returns the JAR Resource for the specified name.
804+ */
805+ Resource getResource(final String name, boolean check) {
806+ if (metaIndex != null) {
807+ if (!metaIndex.mayContain(name)) {
808+ return null;
809+ }
810+ }
811+
812+ try {
813+ ensureOpen();
814+ } catch (IOException e) {
815+ throw (InternalError) new InternalError().initCause(e);
816+ }
817+ final JarEntry entry = jar.getJarEntry(name);
818+ if (entry != null)
819+ return checkResource(name, check, entry);
820+
821+ if (index == null)
822+ return null;
823+
824+ HashSet<String> visited = new HashSet<String>();
825+ return getResource(name, check, visited);
826+ }
827+
828+ /*
829+ * Version of getResource() that tracks the jar files that have been
830+ * visited by linking through the index files. This helper method uses
831+ * a HashSet to store the URLs of jar files that have been searched and
832+ * uses it to avoid going into an infinite loop, looking for a
833+ * non-existent resource
834+ */
835+ Resource getResource(final String name, boolean check,
836+ Set<String> visited) {
837+
838+ Resource res;
839+ Object[] jarFiles;
840+ boolean done = false;
841+ int count = 0;
842+ LinkedList jarFilesList = null;
843+
844+ /* If there no jar files in the index that can potential contain
845+ * this resource then return immediately.
846+ */
847+ if((jarFilesList = index.get(name)) == null)
848+ return null;
849+
850+ do {
851+ jarFiles = jarFilesList.toArray();
852+ int size = jarFilesList.size();
853+ /* loop through the mapped jar file list */
854+ while(count < size) {
855+ String jarName = (String)jarFiles[count++];
856+ JarLoader newLoader;
857+ final URL url;
858+
859+ try{
860+ url = new URL(csu, jarName);
861+ String urlNoFragString = URLUtil.urlNoFragString(url);
862+ if ((newLoader = (JarLoader)lmap.get(urlNoFragString)) == null) {
863+ /* no loader has been set up for this jar file
864+ * before
865+ */
866+ newLoader = AccessController.doPrivileged(
867+ new PrivilegedExceptionAction<JarLoader>() {
868+ public JarLoader run() throws IOException {
869+ return new JarLoader(url, handler,
870+ lmap);
871+ }
872+ });
873+
874+ /* this newly opened jar file has its own index,
875+ * merge it into the parent's index, taking into
876+ * account the relative path.
877+ */
878+ JarIndex newIndex = newLoader.getIndex();
879+ if(newIndex != null) {
880+ int pos = jarName.lastIndexOf("/");
881+ newIndex.merge(this.index, (pos == -1 ?
882+ null : jarName.substring(0, pos + 1)));
883+ }
884+
885+ /* put it in the global hashtable */
886+ lmap.put(urlNoFragString, newLoader);
887+ }
888+ } catch (java.security.PrivilegedActionException pae) {
889+ continue;
890+ } catch (MalformedURLException e) {
891+ continue;
892+ }
893+
894+
895+ /* Note that the addition of the url to the list of visited
896+ * jars incorporates a check for presence in the hashmap
897+ */
898+ boolean visitedURL = !visited.add(URLUtil.urlNoFragString(url));
899+ if (!visitedURL) {
900+ try {
901+ newLoader.ensureOpen();
902+ } catch (IOException e) {
903+ throw (InternalError) new InternalError().initCause(e);
904+ }
905+ final JarEntry entry = newLoader.jar.getJarEntry(name);
906+ if (entry != null) {
907+ return newLoader.checkResource(name, check, entry);
908+ }
909+
910+ /* Verify that at least one other resource with the
911+ * same package name as the lookedup resource is
912+ * present in the new jar
913+ */
914+ if (!newLoader.validIndex(name)) {
915+ /* the mapping is wrong */
916+ throw new InvalidJarIndexException("Invalid index");
917+ }
918+ }
919+
920+ /* If newLoader is the current loader or if it is a
921+ * loader that has already been searched or if the new
922+ * loader does not have an index then skip it
923+ * and move on to the next loader.
924+ */
925+ if (visitedURL || newLoader == this ||
926+ newLoader.getIndex() == null) {
927+ continue;
928+ }
929+
930+ /* Process the index of the new loader
931+ */
932+ if((res = newLoader.getResource(name, check, visited))
933+ != null) {
934+ return res;
935+ }
936+ }
937+ // Get the list of jar files again as the list could have grown
938+ // due to merging of index files.
939+ jarFilesList = index.get(name);
940+
941+ // If the count is unchanged, we are done.
942+ } while(count < jarFilesList.size());
943+ return null;
944+ }
945+
946+
947+ /*
948+ * Returns the JAR file local class path, or null if none.
949+ */
950+ URL[] getClassPath() throws IOException {
951+ if (index != null) {
952+ return null;
953+ }
954+
955+ if (metaIndex != null) {
956+ return null;
957+ }
958+
959+ ensureOpen();
960+ parseExtensionsDependencies();
961+ if (SharedSecrets.javaUtilJarAccess().jarFileHasClassPathAttribute(jar)) { // Only get manifest when necessary
962+ Manifest man = jar.getManifest();
963+ if (man != null) {
964+ Attributes attr = man.getMainAttributes();
965+ if (attr != null) {
966+ String value = attr.getValue(Name.CLASS_PATH);
967+ if (value != null) {
968+ return parseClassPath(csu, value);
969+ }
970+ }
971+ }
972+ }
973+ return null;
974+ }
975+
976+ /*
977+ * parse the standard extension dependencies
978+ */
979+ private void parseExtensionsDependencies() throws IOException {
980+ ExtensionDependency.checkExtensionsDependencies(jar);
981+ }
982+
983+ /*
984+ * Parses value of the Class-Path manifest attribute and returns
985+ * an array of URLs relative to the specified base URL.
986+ */
987+ private URL[] parseClassPath(URL base, String value)
988+ throws MalformedURLException
989+ {
990+ StringTokenizer st = new StringTokenizer(value);
991+ URL[] urls = new URL[st.countTokens()];
992+ int i = 0;
993+ while (st.hasMoreTokens()) {
994+ String path = st.nextToken();
995+ urls[i] = new URL(base, path);
996+ i++;
997+ }
998+ return urls;
999+ }
1000+ }
1001+
1002+ /*
1003+ * Inner class used to represent a loader of classes and resources
1004+ * from a file URL that refers to a directory.
1005+ */
1006+ private static class FileLoader extends Loader {
1007+ /* Canonicalized File */
1008+ private File dir;
1009+
1010+ FileLoader(URL url) throws IOException {
1011+ super(url);
1012+ if (!"file".equals(url.getProtocol())) {
1013+ throw new IllegalArgumentException("url");
1014+ }
1015+ String path = url.getFile().replace('/', File.separatorChar);
1016+ path = ParseUtil.decode(path);
1017+ dir = (new File(path)).getCanonicalFile();
1018+ }
1019+
1020+ /*
1021+ * Returns the URL for a resource with the specified name
1022+ */
1023+ URL findResource(final String name, boolean check) {
1024+ Resource rsc = getResource(name, check);
1025+ if (rsc != null) {
1026+ return rsc.getURL();
1027+ }
1028+ return null;
1029+ }
1030+
1031+ Resource getResource(final String name, boolean check) {
1032+ final URL url;
1033+ try {
1034+ URL normalizedBase = new URL(getBaseURL(), ".");
1035+ url = new URL(getBaseURL(), ParseUtil.encodePath(name, false));
1036+
1037+ if (url.getFile().startsWith(normalizedBase.getFile()) == false) {
1038+ // requested resource had ../..'s in path
1039+ return null;
1040+ }
1041+
1042+ if (check)
1043+ URLClassPath.check(url);
1044+
1045+ final File file;
1046+ if (name.indexOf("..") != -1) {
1047+ file = (new File(dir, name.replace('/', File.separatorChar)))
1048+ .getCanonicalFile();
1049+ if ( !((file.getPath()).startsWith(dir.getPath())) ) {
1050+ /* outside of base dir */
1051+ return null;
1052+ }
1053+ } else {
1054+ file = new File(dir, name.replace('/', File.separatorChar));
1055+ }
1056+
1057+ if (file.exists()) {
1058+ return new Resource() {
1059+ public String getName() { return name; };
1060+ public URL getURL() { return url; };
1061+ public URL getCodeSourceURL() { return getBaseURL(); };
1062+ public InputStream getInputStream() throws IOException
1063+ { return new FileInputStream(file); };
1064+ public int getContentLength() throws IOException
1065+ { return (int)file.length(); };
1066+ };
1067+ }
1068+ } catch (Exception e) {
1069+ return null;
1070+ }
1071+ return null;
1072+ }
1073+ }
1074+}