org.apache.openejb.config.DeploymentsResolver Maven / Gradle / Ivy
/*
* 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.config.sys.Deployments;
import org.apache.openejb.loader.FileUtils;
import org.apache.openejb.loader.Files;
import org.apache.openejb.loader.Options;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.JavaSecurityManagers;
import org.apache.openejb.util.Logger;
import org.apache.openejb.util.URLs;
import org.apache.xbean.finder.UrlSet;
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 java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Locale;
import static org.apache.openejb.util.URLs.toFile;
import static org.apache.openejb.util.URLs.toFileUrl;
/**
* @version $Rev$ $Date$
*/
public class DeploymentsResolver implements DeploymentFilterable {
private static final String EXCLUDE_INCLUDE_ORDER = SystemInstance.get().getOptions().get("openejb.exclude-include.order", "include-exclude");
private static final String[] ignoreDirs = SystemInstance.get().getProperty("openejb.ignore.directories", ".svn,_svn,cvs,.git,.hg").split(",");
private static final Logger logger = DeploymentLoader.LOGGER;
private static File lib;
static {
try {
lib = SystemInstance.get().getHome().getDirectory("lib", false);
} catch (final IOException e) {
//Ignore
}
}
public static boolean isExtractedDir(final File f) {
if (new File(f.getParentFile(), f.getName() + ".war").exists()) {
return true;
}
return new File(f.getParentFile(), f.getName() + ".ear").exists();
}
protected static boolean isValidDirectory(final File file) {
if (file.isDirectory() && !file.isHidden() && !file.equals(lib)) {
final String fn = file.getName();
for (final String dir : ignoreDirs) {
if (fn.equalsIgnoreCase(dir)) {
return false;
}
}
final String[] files = file.list();
return null != files && files.length > 0;
}
return false;
}
public static void loadFrom(final Deployments dep, final FileUtils path, final List jarList) {
if (dep.getDir() != null) {
try {
loadFromDir(dep, path, jarList);
} catch (final Files.FileDoesNotExistException e) {
logger.warning("File error: - " + e.getMessage());
} catch (final RuntimeException e) {
final String message = "Runtime error: - " + e.getMessage();
logger.error(message);
throw new DeploymentsConfigurationException(message);
}
} else if (dep.getFile() != null) {
try {
loadFromFile(dep, path, jarList);
} catch (final RuntimeException e) {
final String message = " - " + e.getMessage();
logger.error(message);
throw new DeploymentsConfigurationException(message);
}
}
}
public static class DeploymentsConfigurationException extends RuntimeException {
public DeploymentsConfigurationException(final String message) {
super(message);
}
}
private static void loadFromFile(final Deployments dep, final FileUtils path, final List jarList) {
final File file = Files.path(path.getDirectory(), dep.getFile());
Files.exists(file);
Files.readable(file);
Files.file(file);
if (!jarList.contains(file)) {
jarList.add(file);
}
}
private static void loadFromDir(final Deployments dep, final FileUtils path, final List jarList) {
final File dir = Files.path(path.getDirectory(), dep.getDir());
Files.exists(dir);
Files.readable(dir);
Files.dir(dir);
Files.notHidden(dir);
final Map files = new LinkedHashMap<>();
final File[] list = dir.listFiles(new FileFilter() {
@Override
public boolean accept(final File f) {
if (f.isDirectory()) {
return DeploymentsResolver.isValidDirectory(f) && !DeploymentsResolver.isExtractedDir(f);
}
return true;
}
});
if (list != null) {
for (final File file : list) {
files.put(file.getAbsolutePath(), file);
}
// Ignore any unpacked versions
for (final File file : list) {
if (!isArchive(file)) {
continue;
}
final String archive = file.getAbsolutePath();
files.remove(archive.substring(0, archive.length() - 4));
}
}
for (final File file : files.values()) {
if (!jarList.contains(file)) {
jarList.add(file);
}
}
}
private static boolean isArchive(final File file) {
if (!file.isFile()) {
return false;
}
if (!file.getName().toLowerCase().endsWith("ar")) {
return false;
}
final String name = file.getName();
final char c = name.charAt(name.length() - 4);
return c == '.';
}
/**
* The algorithm of OpenEJB deployments class-path inclusion and exclusion is implemented as follows:
* 1- If the string value of the resource URL matches the include class-path pattern
* Then load this resource
* 2- If the string value of the resource URL matches the exclude class-path pattern
* Then ignore this resource
* 3- If the include and exclude class-path patterns are not defined
* Then load this resource
*
* The previous steps are based on the following points:
* 1- Include class-path pattern has the highest priority
* This helps in case both patterns are defined using the same values.
* This appears in step 1 and 2 of the above algorithm.
* 2- Loading the resource is the default behaviour in case of not defining a value for any class-path pattern
* This appears in step 3 of the above algorithm.
*/
public static List loadFromClasspath(final ClassLoader classLoader) {
final ClasspathSearcher searchResult = new ClasspathSearcher().loadUrls(classLoader);
if (searchResult.prefiltered == null || searchResult.urlSet == null) { // an error occured
return new ArrayList<>(); // allow iterator to fully work compared to emptyList()
}
try {
final List jarList = new ArrayList<>(searchResult.urls.size());
final int size = searchResult.urls.size();
if (size == 0 && searchResult.include.length() > 0) {
logger.warning("No classpath URLs matched. Current settings: " +
CLASSPATH_EXCLUDE + "='" + searchResult.exclude + "', " +
CLASSPATH_INCLUDE + "='" + searchResult.include + "'");
return jarList;
} else if (size == 0 && !searchResult.filterDescriptors && searchResult.prefiltered.getUrls().size() == 0) {
return jarList;
} else if (size < 20) {
logger.debug("Inspecting classpath for applications: " + size + " urls.");
} else {
// Has the user allowed some module types to be discoverable via scraping?
final boolean willScrape = searchResult.requireDescriptors.size() < RequireDescriptors.values().length;
if (size < 50 && willScrape) {
logger.info("Inspecting classpath for applications: " +
size +
" urls. Consider adjusting your exclude/include. " +
"Current settings: " + CLASSPATH_EXCLUDE + "='" + searchResult.exclude + "', " +
CLASSPATH_INCLUDE + "='" + searchResult.include + "'");
} else if (willScrape) {
logger.warning("Inspecting classpath for applications: " + size + " urls.");
logger.warning("ADJUST THE EXCLUDE/INCLUDE!!!. Current settings: " +
CLASSPATH_EXCLUDE + "='" + searchResult.exclude + "', " +
CLASSPATH_INCLUDE + "='" + searchResult.include + "'");
}
}
final long begin = System.currentTimeMillis();
processUrls("DeploymentsResolver1", searchResult.urls, classLoader, searchResult.requireDescriptors, jarList);
final long end = System.currentTimeMillis();
final long time = end - begin;
UrlSet unchecked = new UrlSet();
if (!searchResult.filterDescriptors) {
unchecked = NewLoaderLogic.applyBuiltinExcludes(searchResult.prefiltered.exclude(searchResult.prefiltered));
if (searchResult.filterSystemApps) {
unchecked = unchecked.exclude(".*/openejb-[^/]+(.(jar|ear|war)(./)?|/target/classes/?)");
}
processUrls("DeploymentsResolver2", unchecked.getUrls(), classLoader, EnumSet.allOf(RequireDescriptors.class), jarList);
}
if (logger.isDebugEnabled()) {
final int urlCount = searchResult.urlSet.getUrls().size() + unchecked.getUrls().size();
logger.debug("DeploymentsResolver: URLs after filtering: " + urlCount);
for (final URL url : searchResult.urlSet.getUrls()) {
logger.debug("Annotations path: " + url);
}
for (final URL url : unchecked.getUrls()) {
logger.debug("Descriptors path: " + url);
}
}
final int urlSize = searchResult.urls.size();
if (urlSize == 0) {
return jarList;
}
if (time < 1000) {
logger.debug("Searched " + urlSize + " classpath urls in " + time + " milliseconds. Average " + time / urlSize + " milliseconds per url.");
} else if (time < 4000 || urlSize < 3) {
logger.info("Searched " + urlSize + " classpath urls in " + time + " milliseconds. Average " + time / urlSize + " milliseconds per url.");
} else if (time < 10000) {
logger.warning("Searched " + urlSize + " classpath urls in " + time + " milliseconds. Average " + time / urlSize + " milliseconds per url.");
logger.warning("Consider adjusting your " +
CLASSPATH_EXCLUDE + " and " + CLASSPATH_INCLUDE + " settings. Current settings: exclude='" +
searchResult.exclude + "', include='" + searchResult.include + "'");
} else {
logger.fatal("Searched " + urlSize + " classpath urls in " + time + " milliseconds. Average " + time / urlSize + " milliseconds per url. TOO LONG!");
logger.fatal("ADJUST THE EXCLUDE/INCLUDE!!!. Current settings: " +
CLASSPATH_EXCLUDE + "='" + searchResult.exclude + "', " +
CLASSPATH_INCLUDE + "='" + searchResult.include + "'");
final List list = new ArrayList<>();
for (final URL url : searchResult.urls) {
list.add(url.toExternalForm());
}
Collections.sort(list);
for (final String url : list) {
logger.info("Matched: " + url);
}
}
return jarList;
} catch (final IOException e1) {
logger.warning("Unable to search classpath for modules: Received Exception: " + e1.getClass().getName() + " " + e1.getMessage(), e1);
}
return new ArrayList<>();
}
/**
* Use {@link #loadFromClasspath(ClassLoader)}
*/
@Deprecated
public static void loadFromClasspath(final FileUtils ignored, final List jarList, final ClassLoader classLoader) {
jarList.addAll(loadFromClasspath(classLoader));
}
/**
* Use {@link #processUrls(String, List, ClassLoader, Set, List)}
*/
@Deprecated
public static void processUrls(final String caller,
final List urls,
final ClassLoader classLoader,
final Set requireDescriptors,
final FileUtils ignored, // don't use it, it will be removed since we already suppose it is null in several places
final List jarList) {
processUrls(caller, urls, classLoader, requireDescriptors, jarList);
}
public static void processUrls(final String caller,
final List urls,
final ClassLoader classLoader,
final Set requireDescriptors,
final List jarList) {
for (final URL url : urls) {
final String urlProtocol = url.getProtocol();
//Currently, we only support jar and file protocol
final boolean isValidURL = urlProtocol.equals("jar") || urlProtocol.equals("file");
if (!isValidURL) {
logger.warning("Unknown protocol " + urlProtocol);
continue;
}
if (logger.isDebugEnabled()) {
logger.debug(caller + ".processing: " + url);
}
try {
final DeploymentLoader deploymentLoader = new DeploymentLoader();
final Class extends DeploymentModule> moduleType = deploymentLoader.discoverModuleType(url, classLoader, requireDescriptors);
if (AppModule.class.isAssignableFrom(moduleType) ||
EjbModule.class.isAssignableFrom(moduleType) ||
PersistenceModule.class.isAssignableFrom(moduleType) ||
ConnectorModule.class.isAssignableFrom(moduleType) ||
ClientModule.class.isAssignableFrom(moduleType)) {
final URL archive = toFileUrl(url);
if (!jarList.contains(archive)) {
jarList.add(archive);
final File file = toFile(archive);
logger.info("Found " + moduleType.getSimpleName() + " in classpath: " + file.getAbsolutePath());
}
}
} catch (final IOException e) {
logger.warning("Unable to determine the module type of " + url.toExternalForm() + ": Exception: " + e.getMessage(), e);
} catch (final UnknownModuleTypeException ignore) {
// no-op
}
}
}
public static class ClasspathSearcher {
// config used for scanning
private final String include;
private final String exclude;
private final boolean filterSystemApps;
private final Set requireDescriptors;
private final boolean filterDescriptors;
// set got
private UrlSet urlSet;
private UrlSet prefiltered;
// final result
private List urls;
public ClasspathSearcher() {
final Options options = SystemInstance.get().getOptions();
include = options.get(CLASSPATH_INCLUDE, ".*");
exclude = options.get(CLASSPATH_EXCLUDE, "");
filterSystemApps = options.get(CLASSPATH_FILTER_SYSTEMAPPS, true);
requireDescriptors = options.getAll(CLASSPATH_REQUIRE_DESCRIPTOR, RequireDescriptors.CLIENT);
filterDescriptors = options.get(CLASSPATH_FILTER_DESCRIPTORS, false);
}
public List getUrls() {
return urls;
}
private UrlSet cleanUpUrlSet(final UrlSet set) {
if (set.size() >= 5) { // if set size == 1 then we use both getURLs() and getresource(META-INF) to find jar, ensure we don't duplicate it, ie size ~ 2
return set;
}
final List copy = set.getUrls();
for (final URL url : set.getUrls()) {
try {
if ("file".equals(url.getProtocol()) && copy.contains(new URL("jar:" + url.toExternalForm() + "!/"))) {
copy.remove(url);
}
} catch (final MalformedURLException e) {
// no-op
}
}
return new UrlSet(copy);
}
public ClasspathSearcher loadUrls(final ClassLoader classLoader) {
try {
final UrlSet original = cleanUpUrlSet(new UrlSet(classLoader));
urlSet = URLs.cullSystemJars(original);
// save the prefiltered list of jars before excluding system apps
// so that we can choose not to filter modules with descriptors on the full list
prefiltered = urlSet;
Filter includeFilter = Filters.patterns(include);
// we should exclude system apps before and apply user properties after
if (!".*".equals(include) || !"".equals(exclude)) { // if we are using default this will not do anything
// the next line should probably replaced by:
// final Filter filter = new ExcludeIncludeFilter(includeFilter, Filters.patterns(exclude));
final Filter filter;
if (EXCLUDE_INCLUDE_ORDER.startsWith("include")) { // this test should be simply enough
filter = new IncludeExcludeFilter(includeFilter, Filters.patterns(exclude));
} else {
filter = new ExcludeIncludeFilter(includeFilter, Filters.patterns(exclude));
}
// filter using user parameters
urlSet = urlSet.filter(filter);
} else {
includeFilter = null;
}
if (prefiltered.size() == urlSet.size()) {
urlSet = NewLoaderLogic.applyBuiltinExcludes(urlSet, includeFilter);
if (filterSystemApps) {
urlSet = urlSet.exclude(".*/openejb-[^/]+(.(jar|ear|war)(!/)?|/target/(test-)?classes/?)");
}
}
final boolean isWindows = JavaSecurityManagers.getSystemProperty("os.name", "unknown").toLowerCase(Locale.ENGLISH).startsWith("windows");
if (!isWindows || !Boolean.parseBoolean(SystemInstance.get().getProperty("openejb.resolver.windows.lowercase-urls", "true"))) {
urls = urlSet.getUrls();
} else {
urls = new ArrayList<>();
for (final URL url : urlSet.getUrls()) {
final String ef = url.toExternalForm().toLowerCase();
final URL u = new URL(ef);
if (!urls.contains(u)) {
urls.add(u);
}
}
}
return this;
} catch (final IOException e1) {
logger.warning("Unable to search classpath", e1);
}
return this;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy