1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.river.api.net;
19
20 import org.apache.river.action.GetPropertyAction;
21 import java.io.BufferedReader;
22 import java.io.ByteArrayOutputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.ObjectStreamException;
30 import java.io.UnsupportedEncodingException;
31 import java.net.HttpURLConnection;
32 import java.net.JarURLConnection;
33 import java.net.MalformedURLException;
34 import java.net.URISyntaxException;
35 import java.net.URL;
36 import java.net.URLClassLoader;
37 import java.net.URLConnection;
38 import java.net.URLDecoder;
39 import java.net.URLStreamHandlerFactory;
40 import java.security.AccessControlContext;
41 import java.security.AccessController;
42 import java.security.CodeSource;
43 import java.security.Permission;
44 import java.security.PrivilegedAction;
45 import java.security.cert.Certificate;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.Enumeration;
51 import java.util.HashMap;
52 import java.util.Iterator;
53 import java.util.LinkedList;
54 import java.util.List;
55 import java.util.Locale;
56 import java.util.Map;
57 import java.util.StringTokenizer;
58 import java.util.concurrent.CopyOnWriteArrayList;
59 import java.util.jar.Attributes;
60 import java.util.jar.JarEntry;
61 import java.util.jar.JarFile;
62 import java.util.jar.Manifest;
63 import java.util.logging.Level;
64 import java.util.logging.Logger;
65
66 import org.apache.river.impl.Messages;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 public class RFC3986URLClassLoader extends java.net.URLClassLoader {
99
100
101
102
103
104 private final static boolean uri;
105
106 private final static Logger logger = Logger.getLogger(RFC3986URLClassLoader.class.getName());
107
108 static {
109 try {
110 registerAsParallelCapable();
111 } catch (NoSuchMethodError e){
112
113 logger.log(Level.FINEST, "Platform doesn't support parallel class loading", e);
114 }
115 String codebaseAnnotationProperty = null;
116 String prop = AccessController.doPrivileged(
117 new GetPropertyAction("net.jini.loader.codebaseAnnotation"));
118 if (prop != null && prop.trim().length() > 0) codebaseAnnotationProperty = prop;
119 uri = codebaseAnnotationProperty == null ||
120 !Uri.asciiStringsUpperCaseEqual(codebaseAnnotationProperty, "URL");
121 }
122
123 private final List<URL> originalUrls;
124
125 private final List<URL> searchList;
126
127
128 private final List<URLHandler> handlerList;
129 private final Map<Uri, URLHandler> handlerMap = new HashMap<Uri, URLHandler>();
130
131 private final URLStreamHandlerFactory factory;
132
133 private final AccessControlContext creationContext;
134
135 private static class SubURLClassLoader extends RFC3986URLClassLoader {
136
137
138 SubURLClassLoader(URL[] urls, AccessControlContext context) {
139 super(urls, ClassLoader.getSystemClassLoader(), null, context);
140 }
141
142 SubURLClassLoader(URL[] urls, ClassLoader parent, AccessControlContext context) {
143 super(urls, parent, null, context);
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 @Override
161 protected Class<?> loadClass(String className, boolean resolveClass)
162 throws ClassNotFoundException
163 {
164
165
166
167
168 SecurityManager sm = System.getSecurityManager();
169 if (sm != null) {
170 int index = className.lastIndexOf('.');
171 if (index != -1) {
172 sm.checkPackageAccess(className.substring(0, index));
173 }
174 }
175 return super.loadClass(className, resolveClass);
176 }
177 }
178
179 private static class IndexFile {
180
181 private final HashMap<String, List<URL>> map;
182
183
184
185 static IndexFile readIndexFile(JarFile jf, JarEntry indexEntry, URL url) {
186 BufferedReader in = null;
187 InputStream is = null;
188 try {
189
190 String parentURLString = getParentURL(url).toExternalForm();
191 String prefix = "jar:"
192 + parentURLString + "/";
193 is = jf.getInputStream(indexEntry);
194 in = new BufferedReader(new InputStreamReader(is, "UTF8"));
195 HashMap<String, List<URL>> pre_map = new HashMap<String, List<URL>>();
196
197 if (in.readLine() == null) return null;
198 if (in.readLine() == null) return null;
199 TOP_CYCLE:
200 while (true) {
201 String line = in.readLine();
202 if (line == null) {
203 break;
204 }
205 URL jar = new URL(prefix + line + "!/");
206 while (true) {
207 line = in.readLine();
208 if (line == null) {
209 break TOP_CYCLE;
210 }
211 if ("".equals(line)) {
212 break;
213 }
214 List<URL> list;
215 if (pre_map.containsKey(line)) {
216 list = pre_map.get(line);
217 } else {
218 list = new LinkedList<URL>();
219 pre_map.put(line, list);
220 }
221 list.add(jar);
222 }
223 }
224 if (!pre_map.isEmpty()) {
225 return new IndexFile(pre_map);
226 }
227 } catch (MalformedURLException e) {
228
229 } catch (IOException e) {
230
231 }
232 finally {
233 if (in != null) {
234 try {
235 in.close();
236 } catch (IOException e) {
237 }
238 }
239 if (is != null) {
240 try {
241 is.close();
242 } catch (IOException e) {
243 }
244 }
245 }
246 return null;
247 }
248
249 private static URL getParentURL(URL url) throws IOException {
250 URL fileURL = ((JarURLConnection) url.openConnection()).getJarFileURL();
251 String file = fileURL.getFile();
252 String parentFile = new File(file).getParent();
253 parentFile = parentFile.replace(File.separatorChar, '/');
254 if (parentFile.charAt(0) != '/') {
255 parentFile = "/" + parentFile;
256 }
257 URL parentURL = new URL(fileURL.getProtocol(), fileURL
258 .getHost(), fileURL.getPort(), parentFile);
259 return parentURL;
260 }
261
262 public IndexFile(HashMap<String,List<URL>> map) {
263
264
265 this.map = map;
266 }
267
268 List<URL> get(String name) {
269 synchronized (map){
270 return map.get(name);
271 }
272 }
273 }
274
275 private static class URLHandler {
276 final URL url;
277 final URL codeSourceUrl;
278 final RFC3986URLClassLoader loader;
279
280 public URLHandler(URL url, RFC3986URLClassLoader loader) {
281 this.url = url;
282 this.codeSourceUrl = url;
283 this.loader = loader;
284 }
285
286 public URLHandler(URL url, URL codeSourceUrl, RFC3986URLClassLoader loader){
287 this.url = url;
288 this.codeSourceUrl = codeSourceUrl;
289 this.loader = loader;
290 }
291
292 void findResources(String name, List<URL> resources) {
293 URL res = findResource(name);
294 if (res != null && !resources.contains(res)) {
295 resources.add(res);
296 }
297 }
298
299 Class<?> findClass(String packageName, String name, String origName) {
300 URL resURL = targetURL(url, name);
301 if (resURL != null) {
302 try {
303 InputStream is = resURL.openStream();
304 return createClass(is, packageName, origName);
305 } catch (IOException e) {
306 }
307 }
308 return null;
309 }
310
311
312 Class<?> createClass(InputStream is, String packageName, String origName) {
313 if (is == null) {
314 return null;
315 }
316 byte[] clBuf = null;
317 try {
318 clBuf = getBytes(is);
319 } catch (IOException e) {
320 return null;
321 } finally {
322 try {
323 is.close();
324 } catch (IOException e) {
325 }
326 }
327 if (packageName != null) {
328 String packageDotName = packageName.replace('/', '.');
329 Package packageObj = loader.getPackage(packageDotName);
330 if (packageObj == null) {
331 try {
332 loader.definePackage(packageDotName, null, null,
333 null, null, null, null, null);
334 } catch (IllegalArgumentException e){
335
336
337 packageObj = loader.getPackage(packageDotName);
338 if (packageObj.isSealed()) {
339 throw new SecurityException(Messages
340 .getString("luni.A1"));
341 }
342 }
343 } else {
344 if (packageObj.isSealed()) {
345 throw new SecurityException(Messages
346 .getString("luni.A1"));
347 }
348 }
349 }
350
351 if (uri) return loader.defineClass(
352 origName,
353 clBuf,
354 0,
355 clBuf != null ? clBuf.length: 0,
356 new UriCodeSource(codeSourceUrl, (Certificate[]) null, null)
357 );
358 return loader.defineClass(
359 origName,
360 clBuf,
361 0,
362 clBuf != null ? clBuf.length: 0,
363 new CodeSource(codeSourceUrl, (Certificate[]) null)
364 );
365 }
366
367 URL findResource(String name) {
368 URL resURL = targetURL(url, name);
369 if (resURL != null) {
370 try {
371 URLConnection uc = resURL.openConnection();
372 uc.getInputStream().close();
373
374
375 if (!resURL.getProtocol().equals("http")) {
376 return resURL;
377 }
378 int code;
379 if ((code = ((HttpURLConnection) uc).getResponseCode()) >= 200
380 && code < 300) {
381 return resURL;
382 }
383 } catch (SecurityException e) {
384 return null;
385 } catch (IOException e) {
386 return null;
387 }
388 }
389 return null;
390 }
391
392 URL targetURL(URL base, String name) {
393 try {
394 String file = base.getFile() + URIEncoderDecoder.quoteIllegal(name,
395 "/@" + Uri.someLegal);
396
397 return new URL(base.getProtocol(), base.getHost(), base.getPort(),
398 file, null);
399 } catch (UnsupportedEncodingException e) {
400 return null;
401 } catch (MalformedURLException e) {
402 return null;
403 }
404 }
405
406 public void close() throws IOException {
407
408 }
409
410 }
411
412 private static class URLJarHandler extends URLHandler {
413 private final JarFile jf;
414 private final String prefixName;
415 private final IndexFile index;
416 private final Map<Uri, URLHandler> subHandlers = new HashMap<Uri, URLHandler>();
417
418 public URLJarHandler(URL url, URL jarURL, JarFile jf, String prefixName, RFC3986URLClassLoader loader) {
419 super(url, jarURL, loader);
420 this.jf = jf;
421 this.prefixName = prefixName;
422 final JarEntry je = jf.getJarEntry("META-INF/INDEX.LIST");
423 this.index = (je == null ? null : IndexFile.readIndexFile(jf, je, url));
424 }
425
426 public URLJarHandler(URL url, URL jarURL, JarFile jf, String prefixName, IndexFile index, RFC3986URLClassLoader loader) {
427 super(url, jarURL, loader);
428 this.jf = jf;
429 this.prefixName = prefixName;
430 this.index = index;
431 }
432
433 IndexFile getIndex() {
434 return index;
435 }
436
437 @Override
438 void findResources(String name, List<URL> resources) {
439 URL res = findResourceInOwn(name);
440 if (res != null && !resources.contains(res)) {
441 resources.add(res);
442 }
443 if (index != null) {
444 int pos = name.lastIndexOf("/");
445
446
447 String indexedName = (pos > 0) ? name.substring(0, pos) : name;
448 List<URL> urls = index.get(indexedName);
449 if (urls != null) {
450 synchronized (urls){
451 urls.remove(url);
452 urls = new ArrayList<URL>(urls);
453 }
454 for (URL u : urls) {
455 URLHandler h = getSubHandler(u);
456 if (h != null) {
457 h.findResources(name, resources);
458 }
459 }
460 }
461 }
462
463 }
464
465 @Override
466 Class<?> findClass(String packageName, String name, String origName) {
467 String entryName = prefixName + name;
468 JarEntry entry = jf.getJarEntry(entryName);
469 if (entry != null) {
470
471
472
473
474
475 try {
476 Manifest manifest = jf.getManifest();
477 return createClass(entry, manifest, packageName, origName);
478 } catch (IOException e) {
479 }
480 }
481 if (index != null) {
482 List<URL> urls;
483 if (packageName == null) {
484 urls = index.get(name);
485 } else {
486 urls = index.get(packageName);
487 }
488 if (urls != null) {
489 synchronized (urls){
490 urls.remove(url);
491 urls = new ArrayList<URL>(urls);
492 }
493 for (URL u : urls) {
494 URLHandler h = getSubHandler(u);
495 if (h != null) {
496 Class<?> res = h.findClass(packageName, name, origName);
497 if (res != null) {
498 return res;
499 }
500 }
501 }
502 }
503 }
504 return null;
505 }
506
507 private Class<?> createClass(JarEntry entry, Manifest manifest, String packageName, String origName) {
508 InputStream is = null;
509 byte[] clBuf;
510 try {
511 is = jf.getInputStream(entry);
512 clBuf = getBytes(is);
513 } catch (IOException e) {
514 return null;
515 } finally {
516 if (is != null) {
517 try {
518 is.close();
519 } catch (IOException e) {
520 }
521 }
522 }
523 if (packageName != null) {
524 String packageDotName = packageName.replace('/', '.');
525 Package packageObj = loader.getPackage(packageDotName);
526 if (packageObj == null) {
527 if (manifest != null) {
528 loader.definePackage(packageDotName, manifest,
529 codeSourceUrl);
530 } else {
531 loader.definePackage(packageDotName, null, null,
532 null, null, null, null, null);
533 }
534 } else {
535 boolean exception = packageObj.isSealed();
536 if (manifest != null) {
537 if (loader.isSealed(manifest, packageName + "/")) {
538 exception = !packageObj
539 .isSealed(codeSourceUrl);
540 }
541 }
542 if (exception) {
543 throw new SecurityException(Messages
544 .getString("luni.A1", packageName));
545 }
546 }
547 }
548 CodeSource codeS = uri ?
549 new UriCodeSource(codeSourceUrl, entry.getCertificates(),null)
550 : new CodeSource(codeSourceUrl, entry.getCertificates());
551 return loader.defineClass(
552 origName,
553 clBuf,
554 0,
555 clBuf.length,
556 codeS
557 );
558 }
559
560 URL findResourceInOwn(String name) {
561 String entryName = prefixName + name;
562 if (jf.getEntry(entryName) != null) {
563 return targetURL(url, name);
564 }
565 return null;
566 }
567
568 @Override
569 URL findResource(String name) {
570 URL res = findResourceInOwn(name);
571 if (res != null) {
572 return res;
573 }
574 if (index != null) {
575 int pos = name.lastIndexOf("/");
576
577
578 String indexedName = (pos > 0) ? name.substring(0, pos) : name;
579 List<URL> urls = index.get(indexedName);
580 if (urls != null) {
581 synchronized (urls){
582 urls.remove(url);
583 urls = new ArrayList<URL>(urls);
584 }
585 for (URL u : urls) {
586 URLHandler h = getSubHandler(u);
587 if (h != null) {
588 res = h.findResource(name);
589 if (res != null) {
590 return res;
591 }
592 }
593 }
594 }
595 }
596 return null;
597 }
598
599 private URLHandler getSubHandler(URL url) {
600 Uri key = null;
601 try {
602 key = Uri.urlToUri(url);
603 } catch (URISyntaxException ex) {
604 logger.log(Level.WARNING, "Unable to create Uri from URL" + url.toString(), ex);
605 }
606 synchronized (subHandlers){
607 URLHandler sub = subHandlers.get(key);
608 if (sub != null) {
609 return sub;
610 }
611 String protocol = url.getProtocol();
612 if (protocol.equals("jar")) {
613 sub = loader.createURLJarHandler(url);
614 } else if (protocol.equals("file")) {
615 sub = createURLSubJarHandler(url);
616 } else {
617 sub = loader.createURLHandler(url);
618 }
619 if (sub != null && key != null) {
620 subHandlers.put(key, sub);
621 }
622 return sub;
623 }
624 }
625
626 private URLHandler createURLSubJarHandler(URL url) {
627 String prfixName;
628 String file = url.getFile();
629 if (url.getFile().endsWith("!/")) {
630 prfixName = "";
631 } else {
632 int sepIdx = file.lastIndexOf("!/");
633 if (sepIdx == -1) {
634
635 return null;
636 }
637 sepIdx += 2;
638 prfixName = file.substring(sepIdx);
639 }
640 try {
641 URL jarURL = ((JarURLConnection) url
642 .openConnection()).getJarFileURL();
643 JarURLConnection juc = (JarURLConnection) new URL(
644 "jar", "",
645 jarURL.toExternalForm() + "!/").openConnection();
646 JarFile jfile = juc.getJarFile();
647 URLJarHandler jarH = new URLJarHandler(url, jarURL, jfile, prfixName, loader);
648
649 return jarH;
650 } catch (IOException e) {
651 }
652 return null;
653 }
654
655 public void close() throws IOException {
656 IOException first = null;
657 try {
658 jf.close();
659 } catch (IOException e){
660 first = e;
661 }
662 synchronized (subHandlers){
663 Iterator<URLHandler> it = subHandlers.values().iterator();
664 while (it.hasNext()){
665 try {
666 it.next().close();
667 } catch (IOException e){
668 if (first == null) first = e;
669 else {
670 logger.log(Level.WARNING, "Unable to close URLHandler during URLClassLoader close()", e);
671 }
672 }
673 }
674 subHandlers.clear();
675 }
676 if (first != null) throw first;
677 }
678
679 }
680
681 private static class URLFileHandler extends URLHandler {
682 private final String prefix;
683
684 public URLFileHandler(URL url, RFC3986URLClassLoader loader) {
685 super(url, loader);
686 String baseFile = url.getFile();
687 String host = url.getHost();
688 int hostLength = 0;
689 if (host != null) {
690 hostLength = host.length();
691 }
692 StringBuilder buf = new StringBuilder(2 + hostLength
693 + baseFile.length());
694 if (hostLength > 0) {
695 buf.append("//").append(host); //$NON-NLS-1$
696 }
697
698 buf.append(baseFile);
699 prefix = buf.toString();
700 }
701
702 @Override
703 Class<?> findClass(String packageName, String name, String origName) {
704 String filename = prefix + name;
705 try {
706 filename = URLDecoder.decode(filename, "UTF-8");
707 } catch (IllegalArgumentException e) {
708 return null;
709 } catch (UnsupportedEncodingException e) {
710 return null;
711 }
712
713 File file = new File(filename);
714 if (file.exists()) {
715 InputStream is = null;
716 try {
717 is = new FileInputStream(file);
718 return createClass(is, packageName, origName);
719 } catch (FileNotFoundException e) {
720 } finally {
721 try {
722 if (is != null) is.close();
723 } catch (IOException ex){}
724 }
725 }
726 return null;
727 }
728
729 @Override
730 URL findResource(String name) {
731 int idx = 0;
732 String filename;
733
734
735 while (idx < name.length() &&
736 ((name.charAt(idx) == '/') || (name.charAt(idx) == '\\'))) {
737 idx++;
738 }
739
740 if (idx > 0) {
741 name = name.substring(idx);
742 }
743
744 try {
745 filename = URLDecoder.decode(prefix, "UTF-8") + name;
746
747 if (new File(filename).exists()) {
748 return targetURL(url, name);
749 }
750 return null;
751 } catch (IllegalArgumentException e) {
752 return null;
753 } catch (UnsupportedEncodingException e) {
754
755 throw new AssertionError(e);
756 }
757 }
758
759 }
760
761
762
763
764
765
766
767
768
769 private static class UriCodeSource extends CodeSource {
770 private static final long serialVersionUID = 1L;
771 private final Uri uri;
772 private final int hashCode;
773
774 UriCodeSource(URL url, Certificate [] certs, Collection<Permission> perms){
775 super(url, certs);
776 Uri uRi = null;
777 try {
778 uRi = Uri.urlToUri(url);
779 } catch (URISyntaxException ex) { }
780 this.uri = uRi;
781 int hash = 7;
782 hash = 23 * hash + (this.uri != null ? this.uri.hashCode() : 0);
783 hash = 23 * hash + (certs != null ? Arrays.hashCode(certs) : 0);
784 hashCode = hash;
785 }
786
787 @Override
788 public int hashCode() {
789 return hashCode;
790 }
791
792 @Override
793 public boolean equals(Object o){
794 if (!(o instanceof UriCodeSource)) return false;
795 if (uri == null) return super.equals(o);
796 UriCodeSource that = (UriCodeSource) o;
797 if ( !uri.equals(that.uri)) return false;
798 Certificate [] mine = getCertificates();
799 Certificate [] theirs = that.getCertificates();
800 if ( mine == null && theirs == null) return true;
801 if ( mine == null && theirs != null) return false;
802 if ( mine != null && theirs == null) return false;
803 return (Arrays.asList(getCertificates()).equals(
804 Arrays.asList(that.getCertificates())));
805 }
806
807 Object writeReplace() throws ObjectStreamException {
808 return new CodeSource(getLocation(), getCertificates());
809 }
810
811 }
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828 public RFC3986URLClassLoader(URL[] urls) {
829 this(urls, ClassLoader.getSystemClassLoader(), null);
830 }
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847 public RFC3986URLClassLoader(URL[] urls, ClassLoader parent) {
848 this(urls, parent, null);
849 }
850
851
852
853
854
855
856
857 @Override
858 protected void addURL(URL url) {
859 try {
860 originalUrls.add(url);
861 searchList.add(createSearchURL(url));
862 } catch (MalformedURLException e) {
863 }
864 }
865
866
867
868
869
870
871
872
873
874
875 @Override
876 public Enumeration<URL> findResources(final String name) throws IOException {
877 List<URL> result = AccessController.doPrivileged(
878 new PrivilegedAction<List<URL>>() {
879 @Override
880 public List<URL> run() {
881 List<URL> results = new LinkedList<URL>();
882 findResourcesImpl(name, results);
883 return results;
884 }
885 }, creationContext);
886 SecurityManager sm;
887 int length = result.size();
888 if (length > 0 && (sm = System.getSecurityManager()) != null) {
889 ArrayList<URL> reduced = new ArrayList<URL>(length);
890 for (int i = 0; i < length; i++) {
891 URL url = result.get(i);
892 try {
893 sm.checkPermission(url.openConnection().getPermission());
894 reduced.add(url);
895 } catch (IOException e) {
896 } catch (SecurityException e) {
897 }
898 }
899 result = reduced;
900 }
901 return Collections.enumeration(result);
902 }
903
904 void findResourcesImpl(String name, List<URL> result) {
905 if (name == null) {
906 return;
907 }
908 int n = 0;
909 while (true) {
910 URLHandler handler = getHandler(n++);
911 if (handler == null) {
912 break;
913 }
914 handler.findResources(name, result);
915 }
916 }
917
918
919
920
921
922
923
924
925
926 private static byte[] getBytes(InputStream is)
927 throws IOException {
928 byte[] buf = new byte[4096];
929 ByteArrayOutputStream bos = new ByteArrayOutputStream(4096);
930 int count;
931 while ((count = is.read(buf)) > 0) {
932 bos.write(buf, 0, count);
933 }
934 return bos.toByteArray();
935 }
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992 @Override
993 public URL[] getURLs() {
994 return originalUrls.toArray(new URL[originalUrls.size()]);
995 }
996
997
998
999
1000 private static boolean isDirectory(URL url) {
1001 String file = url.getFile();
1002 return (file.length() > 0 && file.charAt(file.length() - 1) == File.separatorChar);
1003 }
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016 public static URLClassLoader newInstance(final URL[] urls) {
1017 final AccessControlContext context = AccessController.getContext();
1018 RFC3986URLClassLoader sub = AccessController
1019 .doPrivileged(new PrivilegedAction<RFC3986URLClassLoader>() {
1020 @Override
1021 public RFC3986URLClassLoader run() {
1022 return new SubURLClassLoader(urls, context);
1023 }
1024 });
1025 return sub;
1026 }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041 public static URLClassLoader newInstance(final URL[] urls,
1042 final ClassLoader parentCl) {
1043 final AccessControlContext context = AccessController.getContext();
1044 RFC3986URLClassLoader sub = AccessController
1045 .doPrivileged(new PrivilegedAction<RFC3986URLClassLoader>() {
1046 @Override
1047 public RFC3986URLClassLoader run() {
1048 return new SubURLClassLoader(urls, parentCl, context);
1049 }
1050 });
1051 return sub;
1052 }
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074 public RFC3986URLClassLoader(URL[] searchUrls, ClassLoader parent,
1075 URLStreamHandlerFactory factory) {
1076 this(searchUrls, parent, factory, AccessController.getContext());
1077 }
1078
1079 RFC3986URLClassLoader( URL[] searchUrls,
1080 ClassLoader parent,
1081 URLStreamHandlerFactory factory,
1082 AccessControlContext context)
1083 {
1084 super(searchUrls, parent, factory);
1085 this.factory = factory;
1086
1087 creationContext = context;
1088 int nbUrls = searchUrls.length;
1089 List<URL> origUrls = new ArrayList<URL>(nbUrls);
1090 handlerList = new ArrayList<URLHandler>(nbUrls);
1091 searchList = Collections.synchronizedList(new LinkedList<URL>());
1092 for (int i = 0; i < nbUrls; i++) {
1093 origUrls.add(searchUrls[i]);
1094 try {
1095 searchList.add(createSearchURL(searchUrls[i]));
1096 } catch (MalformedURLException e) {
1097 }
1098 }
1099 this.originalUrls = new CopyOnWriteArrayList<URL>(origUrls);
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 @Override
1117 protected Class<?> findClass(final String clsName)
1118 throws ClassNotFoundException {
1119 Class<?> cls = AccessController.doPrivileged(
1120 new PrivilegedAction<Class<?>>() {
1121 @Override
1122 public Class<?> run() {
1123 return findClassImpl(clsName);
1124 }
1125 }, creationContext);
1126 if (cls != null) {
1127 return cls;
1128 }
1129 throw new ClassNotFoundException(clsName);
1130 }
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144 private URL createSearchURL(URL url) throws MalformedURLException {
1145 if (url == null) {
1146 return url;
1147 }
1148
1149 String protocol = url.getProtocol();
1150
1151 if (isDirectory(url) || protocol.equals("jar")) {
1152 return url;
1153 }
1154 if (factory == null) {
1155 return new URL("jar", "",
1156 -1, url.toString() + "!/");
1157 }
1158
1159 return new URL("jar", "",
1160 -1, url.toString() + "!/",
1161 factory.createURLStreamHandler("jar"));
1162 }
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172 @Override
1173 public URL findResource(final String name) {
1174 if (name == null) {
1175 return null;
1176 }
1177 URL result = AccessController.doPrivileged(new PrivilegedAction<URL>() {
1178 @Override
1179 public URL run() {
1180 return findResourceImpl(name);
1181 }
1182 }, creationContext);
1183 SecurityManager sm;
1184 if (result != null && (sm = System.getSecurityManager()) != null) {
1185 try {
1186 sm.checkPermission(result.openConnection().getPermission());
1187 } catch (IOException e) {
1188 return null;
1189 } catch (SecurityException e) {
1190 return null;
1191 }
1192 }
1193 return result;
1194 }
1195
1196
1197
1198
1199
1200
1201
1202
1203 URL findResourceImpl(String resName) {
1204 int n = 0;
1205
1206 while (true) {
1207 URLHandler handler = getHandler(n++);
1208 if (handler == null) {
1209 break;
1210 }
1211 URL res = handler.findResource(resName);
1212 if (res != null) {
1213 return res;
1214 }
1215 }
1216 return null;
1217 }
1218
1219 URLHandler getHandler(int num) {
1220 synchronized (handlerList){
1221 if (num < handlerList.size()) {
1222 return handlerList.get(num);
1223 }
1224
1225 makeNewHandler();
1226 if (num < handlerList.size()) {
1227 return handlerList.get(num);
1228 }
1229 return null;
1230 }
1231 }
1232
1233
1234 private void makeNewHandler() {
1235 while (!searchList.isEmpty()) {
1236 URL nextCandidate = searchList.remove(0);
1237 if (nextCandidate == null) {
1238 throw new NullPointerException(Messages.getString("luni.94"));
1239 }
1240 Uri candidateKey = null;
1241 try {
1242 candidateKey = Uri.urlToUri(nextCandidate);
1243 } catch (URISyntaxException ex) {
1244 logger.log(Level.WARNING, "Unable to parse URL" + nextCandidate.toString(), ex);
1245 }
1246 if (!handlerMap.containsKey(candidateKey)) {
1247 URLHandler result;
1248 String protocol = nextCandidate.getProtocol();
1249 if (protocol.equals("jar")) {
1250 result = createURLJarHandler(nextCandidate);
1251 } else if (protocol.equals("file")) {
1252 result = createURLFileHandler(nextCandidate);
1253 } else {
1254 result = createURLHandler(nextCandidate);
1255 }
1256 if (result != null) {
1257 handlerMap.put(candidateKey, result);
1258 handlerList.add(result);
1259 return;
1260 }
1261 }
1262 }
1263 }
1264
1265 private URLHandler createURLHandler(URL url) {
1266 return new URLHandler(url, this);
1267 }
1268
1269 private URLHandler createURLFileHandler(URL url) {
1270 return new URLFileHandler(url, this);
1271 }
1272
1273 private URLHandler createURLJarHandler(URL url) {
1274 String prefixName;
1275 String file = url.getFile();
1276 if (url.getFile().endsWith("!/")) {
1277 prefixName = "";
1278 } else {
1279 int sepIdx = file.lastIndexOf("!/");
1280 if (sepIdx == -1) {
1281
1282 return null;
1283 }
1284 sepIdx += 2;
1285 prefixName = file.substring(sepIdx);
1286 }
1287 try {
1288 URL jarURL = ((JarURLConnection) url
1289 .openConnection()).getJarFileURL();
1290 JarURLConnection juc = (JarURLConnection) new URL(
1291 "jar", "",
1292 jarURL.toExternalForm() + "!/").openConnection();
1293 JarFile jf = juc.getJarFile();
1294 URLJarHandler jarH = new URLJarHandler(url, jarURL, jf, prefixName, this);
1295
1296 if (jarH.getIndex() == null) {
1297 try {
1298 Manifest manifest = jf.getManifest();
1299 if (manifest != null) {
1300 String classpath = manifest.getMainAttributes().getValue(
1301 Attributes.Name.CLASS_PATH);
1302 if (classpath != null) {
1303 searchList.addAll(0, getInternalURLs(url, classpath));
1304 }
1305 }
1306 } catch (IOException e) {
1307 }
1308 }
1309 return jarH;
1310 } catch (IOException e) {
1311 }
1312 return null;
1313 }
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330 @Override
1331 protected Package definePackage(String packageName, Manifest manifest,
1332 URL url) throws IllegalArgumentException {
1333 Attributes mainAttributes = manifest.getMainAttributes();
1334 String dirName = packageName.replace('.', '/') + "/";
1335 Attributes packageAttributes = manifest.getAttributes(dirName);
1336 boolean noEntry = false;
1337 if (packageAttributes == null) {
1338 noEntry = true;
1339 packageAttributes = mainAttributes;
1340 }
1341 String specificationTitle = packageAttributes
1342 .getValue(Attributes.Name.SPECIFICATION_TITLE);
1343 if (specificationTitle == null && !noEntry) {
1344 specificationTitle = mainAttributes
1345 .getValue(Attributes.Name.SPECIFICATION_TITLE);
1346 }
1347 String specificationVersion = packageAttributes
1348 .getValue(Attributes.Name.SPECIFICATION_VERSION);
1349 if (specificationVersion == null && !noEntry) {
1350 specificationVersion = mainAttributes
1351 .getValue(Attributes.Name.SPECIFICATION_VERSION);
1352 }
1353 String specificationVendor = packageAttributes
1354 .getValue(Attributes.Name.SPECIFICATION_VENDOR);
1355 if (specificationVendor == null && !noEntry) {
1356 specificationVendor = mainAttributes
1357 .getValue(Attributes.Name.SPECIFICATION_VENDOR);
1358 }
1359 String implementationTitle = packageAttributes
1360 .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
1361 if (implementationTitle == null && !noEntry) {
1362 implementationTitle = mainAttributes
1363 .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
1364 }
1365 String implementationVersion = packageAttributes
1366 .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
1367 if (implementationVersion == null && !noEntry) {
1368 implementationVersion = mainAttributes
1369 .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
1370 }
1371 String implementationVendor = packageAttributes
1372 .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
1373 if (implementationVendor == null && !noEntry) {
1374 implementationVendor = mainAttributes
1375 .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
1376 }
1377
1378 return definePackage(packageName, specificationTitle,
1379 specificationVersion, specificationVendor, implementationTitle,
1380 implementationVersion, implementationVendor, isSealed(manifest,
1381 dirName) ? url : null);
1382 }
1383
1384 private boolean isSealed(Manifest manifest, String dirName) {
1385 Attributes mainAttributes = manifest.getMainAttributes();
1386 String value = mainAttributes.getValue(Attributes.Name.SEALED);
1387 boolean sealed = value != null && value.toLowerCase(Locale.getDefault()).equals("true");
1388 Attributes attributes = manifest.getAttributes(dirName);
1389 if (attributes != null) {
1390 value = attributes.getValue(Attributes.Name.SEALED);
1391 if (value != null) {
1392 sealed = value.toLowerCase(Locale.getDefault()).equals("true");
1393 }
1394 }
1395 return sealed;
1396 }
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407 private ArrayList<URL> getInternalURLs(URL root, String classpath) {
1408
1409 StringTokenizer tokenizer = new StringTokenizer(classpath);
1410 ArrayList<URL> addedURLs = new ArrayList<URL>();
1411 String file = root.getFile();
1412 int jarIndex = file.lastIndexOf("!/") - 1;
1413 int index = file.lastIndexOf("/", jarIndex) + 1;
1414 if (index == 0) {
1415 index = file.lastIndexOf(
1416 System.getProperty("file.separator"), jarIndex) + 1;
1417 }
1418 file = file.substring(0, index);
1419 while (tokenizer.hasMoreElements()) {
1420 String element = tokenizer.nextToken();
1421 if (!element.equals("")) {
1422 try {
1423
1424 URL url = new URL(new URL(file), element);
1425 addedURLs.add(createSearchURL(url));
1426 } catch (MalformedURLException e) {
1427
1428 }
1429 }
1430 }
1431 return addedURLs;
1432 }
1433
1434 Class<?> findClassImpl(String className) {
1435 char dot = '.';
1436 char slash = '/';
1437 int len = className.length();
1438 char[] name = new char [len + 6];
1439
1440 className.getChars(0,len, name, 0);
1441 ".class".getChars(0, 6, name, len);
1442
1443 int lastSlash = -1;
1444 for (int i = 0; i < len; i++){
1445 if (name[i] == dot) {
1446 name[i] = slash;
1447 lastSlash = i;
1448 }
1449 }
1450
1451 String classFileName = new String(name);
1452
1453 String packageName = null;
1454 if (lastSlash != -1) {
1455 packageName = classFileName.substring(0, lastSlash);
1456 }
1457
1458 int n = 0;
1459 while (true) {
1460 URLHandler handler = getHandler(n++);
1461 if (handler == null) {
1462 break;
1463 }
1464 Class<?> res = handler.findClass(packageName, classFileName, className);
1465 if (res != null) {
1466 return res;
1467 }
1468 }
1469 return null;
1470
1471 }
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487 public void close() throws IOException {
1488 synchronized (searchList){
1489 searchList.clear();
1490 }
1491 IOException first = null;
1492 synchronized (handlerList){
1493 Iterator<URLHandler> it = handlerList.iterator();
1494 while (it.hasNext()){
1495 try {
1496 it.next().close();
1497 } catch (IOException e){
1498 if (first == null) first = e;
1499 else {
1500
1501 logger.log(Level.WARNING, "Unable to close URLHandler during URLClassLoader close()", e);
1502 }
1503 }
1504 }
1505 handlerList.clear();
1506 handlerMap.clear();
1507 if (first != null) throw first;
1508 }
1509
1510 }
1511
1512 }