001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.xbean.finder; 018 019import java.io.File; 020import java.io.IOException; 021import java.net.MalformedURLException; 022import java.net.URL; 023import java.net.URLClassLoader; 024import java.util.Arrays; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.Set; 029import java.util.regex.Pattern; 030 031import org.apache.xbean.finder.util.Files; 032 033public final class ClassLoaders { 034 private static final boolean DONT_USE_GET_URLS = Boolean.getBoolean("xbean.finder.use.get-resources"); 035 private static final ClassLoader SYSTEM = ClassLoader.getSystemClassLoader(); 036 037 private static final boolean UNIX = !System.getProperty("os.name").toLowerCase().contains("win"); 038 private static final Pattern MJAR_PATTERN = Pattern.compile(".*/META-INF/versions/[0-9]+/$"); 039 040 public static Set<URL> findUrls(final ClassLoader classLoader) throws IOException { 041 if (classLoader == null || (SYSTEM.getParent() != null && classLoader == SYSTEM.getParent())) { 042 return Collections.emptySet(); 043 } 044 045 final Set<URL> urls = new HashSet<URL>(); 046 047 if (URLClassLoader.class.isInstance(classLoader) && !DONT_USE_GET_URLS) { 048 if (!isSurefire(classLoader)) { 049 for (final Collection<URL> item : Arrays.asList( 050 Arrays.asList(URLClassLoader.class.cast(classLoader).getURLs()), findUrls(classLoader.getParent()))) { 051 for (final URL url : item) { 052 addIfNotSo(urls, url); 053 } 054 } 055 } else { // http://jira.codehaus.org/browse/SUREFIRE-928 - we could reuse findUrlFromResources but this seems faster 056 urls.addAll(fromClassPath()); 057 } 058 } 059 060 // DONT_USE_GET_URLS || java -jar xxx.jar and use MANIFEST.MF Class-Path? 061 // here perf is not an issue since we would either miss all the classpath or we have a single jar 062 if (urls.size() <= 1) { 063 final Set<URL> urlFromResources = findUrlFromResources(classLoader); 064 if (!urls.isEmpty()) { 065 final URL theUrl = urls.iterator().next(); 066 if ("file".equals(theUrl.getProtocol())) { // theUrl can be file:xxxx but it is the same entry actually 067 urlFromResources.remove(new URL("jar:" + theUrl.toExternalForm() + "!/")); 068 } 069 } 070 urls.addAll(urlFromResources); 071 } 072 073 return urls; 074 } 075 076 private static void addIfNotSo(final Set<URL> urls, final URL url) { 077 if (UNIX && isNative(url)) { 078 return; 079 } 080 081 urls.add(url); 082 } 083 084 public static boolean isNative(final URL url) { 085 final File file = Files.toFile(url); 086 if (file != null) { 087 final String name = file.getName(); 088 if (!name.endsWith(".jar") && !file.isDirectory() 089 && name.contains(".so") && file.getAbsolutePath().startsWith("/usr/lib")) { 090 return true; 091 } 092 } 093 return false; 094 } 095 096 097 private static boolean isSurefire(ClassLoader classLoader) { 098 return System.getProperty("surefire.real.class.path") != null && classLoader == SYSTEM; 099 } 100 101 private static Collection<URL> fromClassPath() { 102 final String[] cp = System.getProperty("java.class.path").split(System.getProperty("path.separator", ":")); 103 final Set<URL> urls = new HashSet<URL>(); 104 for (final String path : cp) { 105 try { 106 urls.add(new File(path).toURI().toURL()); // don't build the url in plain String since it is not portable 107 } catch (final MalformedURLException e) { 108 // ignore 109 } 110 } 111 return urls; 112 } 113 114 public static Set<URL> findUrlFromResources(final ClassLoader classLoader) throws IOException { 115 final Set<URL> set = new HashSet<URL>(); 116 for (final URL url : Collections.list(classLoader.getResources("META-INF"))) { 117 final String externalForm = url.toExternalForm(); 118 set.add(new URL(externalForm.substring(0, externalForm.lastIndexOf("META-INF")))); 119 } 120 for (final URL url : Collections.list(classLoader.getResources(""))) { 121 final String externalForm = url.toExternalForm(); 122 if (MJAR_PATTERN.matcher(externalForm).matches()) { 123 set.add(new URL(externalForm.substring(0, externalForm.lastIndexOf("META-INF")))); 124 } else { 125 set.add(url); 126 } 127 } 128 return set; 129 } 130 131 private ClassLoaders() { 132 // no-op 133 } 134}