| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.openejb.config; |
| |
| import org.apache.openejb.loader.FileUtils; |
| import org.apache.openejb.loader.IO; |
| import org.apache.openejb.loader.Options; |
| import org.apache.openejb.loader.SystemInstance; |
| import org.apache.openejb.util.Join; |
| import org.apache.openejb.util.Logger; |
| import org.apache.openejb.util.PerformanceTimer; |
| import org.apache.openejb.util.URLs; |
| import org.apache.xbean.finder.UrlSet; |
| import org.apache.xbean.finder.archive.Archive; |
| import org.apache.xbean.finder.archive.ClasspathArchive; |
| import org.apache.xbean.finder.archive.FilteredArchive; |
| import org.apache.xbean.finder.filter.ExcludeIncludeFilter; |
| import org.apache.xbean.finder.filter.Filter; |
| import org.apache.xbean.finder.filter.Filters; |
| import org.apache.xbean.finder.filter.IncludeExcludeFilter; |
| import org.apache.xbean.finder.filter.PatternFilter; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * @version $Rev$ $Date$ |
| */ |
| public class NewLoaderLogic { |
| private static final Logger logger = DeploymentLoader.logger; |
| public static final String DEFAULT_EXCLUSIONS_ALIAS = "default-list"; |
| public static final String ADDITIONAL_EXCLUDES = SystemInstance.get().getOptions().get("openejb.additional.exclude", (String) null); |
| public static final String ADDITIONAL_INCLUDE = SystemInstance.get().getOptions().get("openejb.additional.include", (String) null); |
| public static final String EXCLUSION_FILE = "exclusions.list"; |
| private static String[] exclusions = null; |
| |
| public static UrlSet filterArchives(final Filter filter, final ClassLoader classLoader, UrlSet urlSet) { |
| |
| for (final URL url : urlSet.getUrls()) { |
| for (final Archive archive : ClasspathArchive.archives(classLoader, url)) { |
| |
| final FilteredArchive filtered = new FilteredArchive(archive, filter); |
| |
| if (!filtered.iterator().hasNext()) { |
| urlSet = urlSet.exclude(url); |
| } |
| |
| } |
| } |
| |
| return urlSet; |
| } |
| |
| public static Set<String> callers() { |
| |
| final Set<String> callers = new LinkedHashSet<String>(); |
| |
| final List<StackTraceElement> elements = new ArrayList<StackTraceElement>(Arrays.asList(new Exception().fillInStackTrace().getStackTrace())); |
| |
| // Yank out everything until we find a known ENTRY point |
| // if we don't find one, so be it, this is only a convenience |
| { |
| // Entry points are the following: |
| final Filter start = Filters.classes("javax.ejb.embeddable.EJBContainer", "javax.naming.InitialContext"); |
| |
| final Iterator<StackTraceElement> iterator = elements.iterator(); |
| while (iterator.hasNext()) { |
| final StackTraceElement element = iterator.next(); |
| iterator.remove(); |
| |
| // If we haven't yet reached an entry point, just keep going |
| if (!start.accept(element.getClassName())) continue; |
| |
| // We found an entry point. |
| // Fast-forward past this class |
| while (iterator.hasNext() && element.getClassName().equals(iterator.next().getClassName())) iterator.remove(); |
| |
| // Ok, we have iterated up to the calling user class, so stop now |
| break; |
| } |
| } |
| |
| |
| // Now iterate till we find an END point |
| // We don't want any of the classes after that |
| { |
| final Filter end = Filters.packages( |
| "junit.", |
| "org.junit.", |
| "org.testng.", |
| "org.apache.maven.", |
| "org.eclipse.", |
| "com.intellij.", |
| "org.scalatest." |
| ); |
| |
| // Everything between here and the end is part |
| // of the call chain in which we are interested |
| for (final StackTraceElement element : elements) { |
| if (end.accept(element.getClassName())) break; |
| |
| callers.add(element.getClassName()); |
| } |
| } |
| |
| // We don't need this anymore |
| elements.clear(); |
| |
| // Finally filter out everything that we definitely don't want |
| { |
| final Filter unwanted = Filters.packages( |
| "java.", |
| "javax.", |
| "sun.reflect." |
| ); |
| |
| final Iterator<String> classes = callers.iterator(); |
| while (classes.hasNext()) { |
| if (unwanted.accept(classes.next())) classes.remove(); |
| } |
| } |
| |
| |
| return callers; |
| } |
| |
| public static UrlSet applyBuiltinExcludes(final UrlSet urlSet) throws MalformedURLException { |
| return applyBuiltinExcludes(urlSet, null); |
| } |
| |
| public static UrlSet applyBuiltinExcludes(final UrlSet urlSet, final Filter includeFilter) throws MalformedURLException { |
| return applyBuiltinExcludes(urlSet, includeFilter, null); |
| } |
| |
| public static UrlSet applyBuiltinExcludes(final UrlSet urlSet, final Filter includeFilter, final Filter excludeFilter) throws MalformedURLException { |
| final Filter filter = Filters.prefixes(getExclusions()); |
| |
| //filter = Filters.optimize(filter, new PatternFilter(".*/openejb-.*")); |
| final List<URL> urls = urlSet.getUrls(); |
| final Iterator<URL> iterator = urls.iterator(); |
| while (iterator.hasNext()) { |
| final URL url = iterator.next(); |
| final File file = URLs.toFile(url); |
| |
| final String name = filter(file).getName(); |
| if (includeFilter == null || !includeFilter.accept(name)) { |
| if (filter != null && filter.accept(name)) { |
| iterator.remove(); |
| } else if (excludeFilter != null && excludeFilter.accept(name)) { |
| iterator.remove(); |
| } |
| } |
| } |
| |
| return new UrlSet(urls); |
| } |
| |
| public static void setExclusions(final String[] exclusionArray) { |
| exclusions = exclusionArray; |
| } |
| |
| public static String[] getExclusions() { |
| |
| if (exclusions != null) { |
| return exclusions; |
| } |
| |
| FileInputStream fis = null; |
| |
| try { |
| final File exclusionsFile = SystemInstance.get().getConf(EXCLUSION_FILE); |
| if (exclusionsFile != null && exclusionsFile.exists()) { |
| fis = new FileInputStream(exclusionsFile); |
| exclusions = readInputStreamList(fis); |
| |
| logger.info("Loaded classpath exclusions from: " + exclusionsFile.getAbsolutePath()); |
| } |
| } catch (Throwable e) { |
| // ignored |
| } finally { |
| IO.close(fis); |
| } |
| |
| if (exclusions == null) { |
| |
| exclusions = readDefaultExclusions(); |
| } |
| |
| final List<String> excludes = null != exclusions ? Arrays.asList(exclusions) : new ArrayList<String>(); |
| |
| if (ADDITIONAL_EXCLUDES != null) { |
| for (final String exclude : ADDITIONAL_EXCLUDES.split(",")) { |
| excludes.add(exclude.trim()); |
| } |
| } |
| if (ADDITIONAL_INCLUDE != null) { // include = not excluded |
| for (final String rawInclude : ADDITIONAL_INCLUDE.split(",")) { |
| final String include = rawInclude.trim(); |
| final Iterator<String> excluded = excludes.iterator(); |
| while (excluded.hasNext()) { |
| if (excluded.next().startsWith(include)) { |
| excluded.remove(); |
| } |
| } |
| } |
| } |
| |
| return excludes.toArray(new String[excludes.size()]); |
| } |
| |
| private static String[] readDefaultExclusions() { |
| InputStream is = null; |
| String[] read = null; |
| try { |
| is = NewLoaderLogic.class.getResourceAsStream("/default.exclusions"); |
| read = readInputStreamList(is); |
| |
| logger.debug("Loaded default.exclusions"); |
| |
| } catch (Throwable e) { |
| // ignored |
| } finally { |
| IO.close(is); |
| } |
| |
| return read; |
| } |
| |
| public static String sanitize(final String value) { |
| if (value.endsWith("*.jar")) { |
| return value.substring(0, value.length() - 5); |
| } else if (value.endsWith("*")) { |
| return value.substring(0, value.length() - 1); |
| } |
| return value; |
| } |
| |
| public static String[] readInputStreamList(final InputStream is) { |
| |
| final List<String> list = new ArrayList<String>(); |
| BufferedReader reader = null; |
| String line; |
| |
| try { |
| |
| reader = new BufferedReader(new InputStreamReader(is)); |
| |
| while ((line = reader.readLine()) != null) { |
| final String value = line.trim(); |
| if (line.startsWith("#") || value.isEmpty()) { |
| continue; |
| } |
| |
| if (DEFAULT_EXCLUSIONS_ALIAS.equals(value)) { |
| for (String v : readDefaultExclusions()) { |
| list.add(v); |
| } |
| } else { |
| list.add(sanitize(value)); |
| } |
| } |
| } catch (Throwable e) { |
| logger.warning("readInputStreamList: Failed to read provided stream"); |
| } finally { |
| if (null != reader) { |
| try { |
| reader.close(); |
| } catch (Throwable e) { |
| //Ignore |
| } |
| } |
| } |
| |
| return list.toArray(new String[list.size()]); |
| } |
| |
| private static File filter(File location) { |
| final List<String> invalid = new ArrayList<String>(); |
| invalid.add("classes"); |
| invalid.add("test-classes"); |
| invalid.add("target"); |
| invalid.add("build"); |
| invalid.add("dist"); |
| invalid.add("bin"); |
| |
| while (invalid.contains(location.getName())) { |
| location = location.getParentFile(); |
| } |
| return location; |
| } |
| |
| |
| public static void _loadFromClasspath(final FileUtils base, final List<URL> jarList, final ClassLoader classLoader) { |
| |
| final PerformanceTimer timer = new PerformanceTimer(); |
| |
| timer.event("create filters"); |
| final Options options = SystemInstance.get().getOptions(); |
| final String include = ""; |
| final String exclude = ""; |
| final PatternFilter classpathInclude = new PatternFilter(options.get(DeploymentFilterable.CLASSPATH_INCLUDE, ".*")); |
| final PatternFilter classpathExclude = new PatternFilter(options.get(DeploymentFilterable.CLASSPATH_EXCLUDE, "")); |
| final Filter classpathFilter = new ExcludeIncludeFilter(classpathInclude, classpathExclude); |
| |
| |
| final PatternFilter packageInclude = new PatternFilter(options.get(DeploymentFilterable.PACKAGE_INCLUDE, ".*")); |
| final PatternFilter packageExclude = new PatternFilter(options.get(DeploymentFilterable.PACKAGE_EXCLUDE, "")); |
| |
| final IncludeExcludeFilter packageFilter; |
| if (classpathInclude.getPattern().pattern().equals(".*") && packageInclude.getPattern().pattern().equals(".*")) { |
| |
| timer.event("callers"); |
| |
| final Set<String> callers = callers(); |
| |
| timer.event("parse packages"); |
| |
| callers.size(); |
| |
| final Set<String> packages = new HashSet<String>(); |
| for (final String caller : callers) { |
| String[] parts = caller.split("\\."); |
| if (parts.length > 2) { |
| parts = new String[]{parts[0], parts[1]}; |
| } |
| packages.add(Join.join(".", parts)); |
| } |
| |
| final Filter includes = Filters.packages(packages.toArray(new String[packages.size()])); |
| |
| packageFilter = new IncludeExcludeFilter(includes, packageExclude); |
| |
| } else { |
| |
| packageFilter = new IncludeExcludeFilter(packageInclude, packageExclude); |
| |
| } |
| |
| timer.event("urlset"); |
| |
| final Set<RequireDescriptors> requireDescriptors = options.getAll(DeploymentFilterable.CLASSPATH_REQUIRE_DESCRIPTOR, RequireDescriptors.CLIENT); |
| final boolean filterDescriptors = options.get(DeploymentFilterable.CLASSPATH_FILTER_DESCRIPTORS, false); |
| final boolean filterSystemApps = options.get(DeploymentFilterable.CLASSPATH_FILTER_SYSTEMAPPS, true); |
| |
| try { |
| UrlSet urlSet = new UrlSet(classLoader); |
| |
| timer.event("exclude system urls"); |
| urlSet = URLs.cullSystemJars(urlSet); |
| |
| |
| timer.event("classpath filter"); |
| |
| final UrlSet beforeFiltering = urlSet; |
| |
| urlSet = urlSet.filter(classpathFilter); |
| |
| |
| // If the user filtered out too much, that's a problem |
| if (urlSet.size() == 0) { |
| final String message = String.format("Classpath Include/Exclude resulted in zero URLs. There were %s possible URLs before filtering and 0 after: include=\"%s\", exclude=\"%s\"", beforeFiltering.size(), include, exclude); |
| logger.error(message); |
| logger.info("Eligible Classpath before filtering:"); |
| |
| for (final URL url : beforeFiltering) { |
| logger.info(String.format(" %s", url.toExternalForm())); |
| } |
| // throw new IllegalStateException(message); |
| |
| } |
| |
| // If they are the same size, than nothing was filtered |
| // and we know the user did not take action to change the default |
| final boolean userSuppliedClasspathFilter = beforeFiltering.size() != urlSet.size(); |
| |
| if (!userSuppliedClasspathFilter) { |
| |
| logger.info("Applying buildin classpath excludes"); |
| timer.event("buildin excludes"); |
| urlSet = applyBuiltinExcludes(urlSet); |
| |
| } |
| |
| DeploymentsResolver.processUrls(urlSet.getUrls(), classLoader, EnumSet.allOf(RequireDescriptors.class), base, jarList); |
| |
| |
| timer.event("package filter"); |
| |
| urlSet = filterArchives(packageFilter, classLoader, urlSet); |
| |
| timer.event("process urls"); |
| |
| // we should exclude system apps before and apply user properties after |
| // if (filterSystemApps){ |
| // urlSet = urlSet.exclude(".*/openejb-[^/]+(.(jar|ear|war)(!/)?|/target/(test-)?classes/?)"); |
| // } |
| |
| final List<URL> urls = urlSet.getUrls(); |
| final int size = urls.size(); |
| // if (size == 0) { |
| // logger.warning("No classpath URLs matched. Current settings: " + CLASSPATH_EXCLUDE + "='" + exclude + "', " + CLASSPATH_INCLUDE + "='" + include + "'"); |
| // return; |
| // } else if (size == 0 && (!filterDescriptors && prefiltered.getUrls().size() == 0)) { |
| // return; |
| // } else if (size < 20) { |
| // logger.debug("Inspecting classpath for applications: " + urls.size() + " urls."); |
| // } else { |
| // // Has the user allowed some module types to be discoverable via scraping? |
| // boolean willScrape = requireDescriptors.size() < RequireDescriptors.values().length; |
| // |
| // if (size < 50 && willScrape) { |
| // logger.info("Inspecting classpath for applications: " + urls.size() + " urls. Consider adjusting your exclude/include. Current settings: " + CLASSPATH_EXCLUDE + "='" + exclude + "', " + CLASSPATH_INCLUDE + "='" + include + "'"); |
| // } else if (willScrape) { |
| // logger.warning("Inspecting classpath for applications: " + urls.size() + " urls."); |
| // logger.warning("ADJUST THE EXCLUDE/INCLUDE!!!. Current settings: " + CLASSPATH_EXCLUDE + "='" + exclude + "', " + CLASSPATH_INCLUDE + "='" + include + "'"); |
| // } |
| // } |
| |
| final long begin = System.currentTimeMillis(); |
| DeploymentsResolver.processUrls(urls, classLoader, requireDescriptors, base, jarList); |
| final long end = System.currentTimeMillis(); |
| final long time = end - begin; |
| |
| timer.stop(System.out); |
| |
| final UrlSet unchecked = new UrlSet(); |
| // if (!filterDescriptors){ |
| // unchecked = prefiltered.exclude(urlSet); |
| // if (filterSystemApps){ |
| // unchecked = unchecked.exclude(".*/openejb-[^/]+(.(jar|ear|war)(./)?|/target/classes/?)"); |
| // } |
| DeploymentsResolver.processUrls(unchecked.getUrls(), classLoader, EnumSet.allOf(RequireDescriptors.class), base, jarList); |
| // } |
| |
| if (logger.isDebugEnabled()) { |
| final int urlCount = urlSet.getUrls().size() + unchecked.getUrls().size(); |
| logger.debug("URLs after filtering: " + urlCount); |
| for (final URL url : urlSet.getUrls()) { |
| logger.debug("Annotations path: " + url); |
| } |
| for (final URL url : unchecked.getUrls()) { |
| logger.debug("Descriptors path: " + url); |
| } |
| } |
| |
| if (urls.size() == 0) return; |
| |
| if (time < 1000) { |
| logger.debug("Searched " + urls.size() + " classpath urls in " + time + " milliseconds. Average " + (time / urls.size()) + " milliseconds per url."); |
| } else if (time < 4000 || urls.size() < 3) { |
| logger.info("Searched " + urls.size() + " classpath urls in " + time + " milliseconds. Average " + (time / urls.size()) + " milliseconds per url."); |
| } else if (time < 10000) { |
| logger.warning("Searched " + urls.size() + " classpath urls in " + time + " milliseconds. Average " + (time / urls.size()) + " milliseconds per url."); |
| logger.warning("Consider adjusting your " + DeploymentFilterable.CLASSPATH_EXCLUDE + " and " + DeploymentFilterable.CLASSPATH_INCLUDE + " settings. Current settings: exclude='" + exclude + "', include='" + include + "'"); |
| } else { |
| logger.fatal("Searched " + urls.size() + " classpath urls in " + time + " milliseconds. Average " + (time / urls.size()) + " milliseconds per url. TOO LONG!"); |
| logger.fatal("ADJUST THE EXCLUDE/INCLUDE!!!. Current settings: " + DeploymentFilterable.CLASSPATH_EXCLUDE + "='" + exclude + "', " + DeploymentFilterable.CLASSPATH_INCLUDE + "='" + include + "'"); |
| final List<String> list = new ArrayList<String>(); |
| for (final URL url : urls) { |
| list.add(url.toExternalForm()); |
| } |
| Collections.sort(list); |
| for (final String url : list) { |
| logger.info("Matched: " + url); |
| } |
| } |
| } catch (IOException e1) { |
| e1.printStackTrace(); |
| logger.warning("Unable to search classpath for modules: Received Exception: " + e1.getClass().getName() + " " + e1.getMessage(), e1); |
| } |
| |
| } |
| } |