/* * Copyright 2002-2003,2009 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.opensymphony.xwork2.util.finder; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Use with ClassFinder to filter the Urls to be scanned, example: * <pre> * UrlSet urlSet = new UrlSet(classLoader); * urlSet = urlSet.exclude(ClassLoader.getSystemClassLoader().getParent()); * urlSet = urlSet.excludeJavaExtDirs(); * urlSet = urlSet.excludeJavaEndorsedDirs(); * urlSet = urlSet.excludeJavaHome(); * urlSet = urlSet.excludePaths(System.getProperty("sun.boot.class.path", "")); * urlSet = urlSet.exclude(".*?/JavaVM.framework/.*"); * urlSet = urlSet.exclude(".*?/activemq-(core|ra)-[\\d.]+.jar(!/)?"); * </pre> * @author David Blevins * @version $Rev$ $Date$ */ public class UrlSet { private static final Logger LOG = LoggerFactory.getLogger(UrlSet.class); private final Map<String,URL> urls; private Set<String> protocols; private UrlSet() { this.urls = new HashMap<String,URL>(); } public UrlSet(ClassLoaderInterface classLoader) throws IOException { this(); load(getUrls(classLoader)); } public UrlSet(ClassLoaderInterface classLoader, Set<String> protocols) throws IOException { this(); this.protocols = protocols; load(getUrls(classLoader, protocols)); } public UrlSet(URL... urls){ this(Arrays.asList(urls)); } /** * Ignores all URLs that are not "jar" or "file" * @param urls */ public UrlSet(Collection<URL> urls){ this(); load(urls); } private UrlSet(Map<String, URL> urls) { this.urls = urls; } private void load(Collection<URL> urls){ for (URL location : urls) { try { this.urls.put(location.toExternalForm(), location); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn("Cannot translate url to external form!", e); } } } } public UrlSet include(UrlSet urlSet){ Map<String, URL> urls = new HashMap<String, URL>(this.urls); urls.putAll(urlSet.urls); return new UrlSet(urls); } public UrlSet exclude(UrlSet urlSet) { Map<String, URL> urls = new HashMap<String, URL>(this.urls); Map<String, URL> parentUrls = urlSet.urls; for (String url : parentUrls.keySet()) { urls.remove(url); } return new UrlSet(urls); } public UrlSet exclude(ClassLoaderInterface parent) throws IOException { return exclude(new UrlSet(parent, this.protocols)); } public UrlSet exclude(File file) throws MalformedURLException { return exclude(relative(file)); } public UrlSet exclude(String pattern) throws MalformedURLException { return exclude(matching(pattern)); } /** * Calls excludePaths(System.getProperty("java.ext.dirs")) * @return * @throws MalformedURLException */ public UrlSet excludeJavaExtDirs() throws MalformedURLException { return excludePaths(System.getProperty("java.ext.dirs", "")); } /** * Calls excludePaths(System.getProperty("java.endorsed.dirs")) * * @return * @throws MalformedURLException */ public UrlSet excludeJavaEndorsedDirs() throws MalformedURLException { return excludePaths(System.getProperty("java.endorsed.dirs", "")); } public UrlSet excludeJavaHome() throws MalformedURLException { String path = System.getProperty("java.home"); if (path != null) { File java = new File(path); if (path.matches("/System/Library/Frameworks/JavaVM.framework/Versions/[^/]+/Home")){ java = java.getParentFile(); } return exclude(java); } else { return this; } } public UrlSet excludePaths(String pathString) throws MalformedURLException { String[] paths = pathString.split(File.pathSeparator); UrlSet urlSet = this; for (String path : paths) { if (StringUtils.isNotEmpty(path)) { File file = new File(path); urlSet = urlSet.exclude(file); } } return urlSet; } public UrlSet matching(String pattern) { Map<String, URL> urls = new HashMap<String, URL>(); for (Map.Entry<String, URL> entry : this.urls.entrySet()) { String url = entry.getKey(); if (url.matches(pattern)){ urls.put(url, entry.getValue()); } } return new UrlSet(urls); } /** * Try to find a classes directory inside a war file add its normalized url to this set */ public UrlSet includeClassesUrl(ClassLoaderInterface classLoaderInterface, FileProtocolNormalizer normalizer) throws IOException { Enumeration<URL> rootUrlEnumeration = classLoaderInterface.getResources(""); while (rootUrlEnumeration.hasMoreElements()) { URL url = rootUrlEnumeration.nextElement(); String externalForm = StringUtils.removeEnd(url.toExternalForm(), "/"); if (externalForm.endsWith(".war/WEB-INF/classes")) { //if it is inside a war file, get the url to the file externalForm = StringUtils.substringBefore(externalForm, "/WEB-INF/classes"); URL warUrl = new URL(externalForm); URL normalizedUrl = normalizer.normalizeToFileProtocol(warUrl); URL finalUrl = ObjectUtils.defaultIfNull(normalizedUrl, warUrl); Map<String, URL> newUrls = new HashMap<String, URL>(this.urls); if ("jar".equals(finalUrl.getProtocol()) || "file".equals(finalUrl.getProtocol())) { newUrls.put(finalUrl.toExternalForm(), finalUrl); } return new UrlSet(newUrls); } } return this; } public UrlSet relative(File file) throws MalformedURLException { String urlPath = file.toURI().toURL().toExternalForm(); Map<String, URL> urls = new HashMap<String, URL>(); for (Map.Entry<String, URL> entry : this.urls.entrySet()) { String url = entry.getKey(); if (url.startsWith(urlPath) || url.startsWith("jar:"+urlPath)){ urls.put(url, entry.getValue()); } } return new UrlSet(urls); } public List<URL> getUrls() { return new ArrayList<URL>(urls.values()); } private List<URL> getUrls(ClassLoaderInterface classLoader) throws IOException { List<URL> list = new ArrayList<URL>(); //find jars ArrayList<URL> urls = Collections.list(classLoader.getResources("META-INF")); for (URL url : urls) { if ("jar".equalsIgnoreCase(url.getProtocol())) { String externalForm = url.toExternalForm(); //build a URL pointing to the jar, instead of the META-INF dir url = new URL(StringUtils.substringBefore(externalForm, "META-INF")); list.add(url); } else if (LOG.isDebugEnabled()) LOG.debug("Ignoring URL [#0] because it is not a jar", url.toExternalForm()); } //usually the "classes" dir list.addAll(Collections.list(classLoader.getResources(""))); return list; } private List<URL> getUrls(ClassLoaderInterface classLoader, Set<String> protocols) throws IOException { if (protocols == null) { return getUrls(classLoader); } List<URL> list = new ArrayList<URL>(); //find jars ArrayList<URL> urls = Collections.list(classLoader.getResources("META-INF")); for (URL url : urls) { if (protocols.contains(url.getProtocol())) { String externalForm = url.toExternalForm(); //build a URL pointing to the jar, instead of the META-INF dir url = new URL(StringUtils.substringBefore(externalForm, "META-INF")); list.add(url); } else if (LOG.isDebugEnabled()) LOG.debug("Ignoring URL [#0] because it is not a valid protocol", url.toExternalForm()); } return list; } public static interface FileProtocolNormalizer { URL normalizeToFileProtocol(URL url); } }