All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openejb.config.TldScanner Maven / Gradle / Ivy

The newest version!
/*
 * 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.OpenEJBException;
import org.apache.openejb.util.DaemonThreadFactory;
import org.apache.openejb.util.URLs;
import org.apache.xbean.finder.UrlSet;
import org.apache.xbean.finder.filter.Filters;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import static java.util.Arrays.asList;
import static org.apache.openejb.config.NewLoaderLogic.applyBuiltinExcludes;
import static org.apache.openejb.util.URLs.toFile;

/**
 * TLD file urls cached on a per classloader basis.  Helps with sharing TLD
 * files between webapps by placing them in a parent classloader.
 * 

* Each webapp will be able to retrieve the cached version of the URLs and * therefore only needs to scan its own libraries, the parent libraries will * already have been scanned. *

* For a tiny bit of performance, we will scan the StandardClassloader at boot * in a separate thread so it should be primed in advance of any deployment. * * @version $Rev$ $Date$ */ public class TldScanner { // first cache, it is the faster one but not relevant between temp and runtime phases private static Map> cache = new WeakHashMap>(); // tld by classloader identified by hash on urls (same hash for temp and runtime classloaders) // a bit longer to compute but let scanning be reused over temp and runtime classloaders private static Map> cacheByhashCode = new WeakHashMap>(); public static Set scan(final ClassLoader classLoader) throws OpenEJBException { if (classLoader == null) { return Collections.emptySet(); } final Set urls = cache.get(classLoader); if (urls != null) { return urls; } final Set result = scanClassLoaderForTagLibs(classLoader); cache.put(classLoader, result); return result; } public static Set scanClassLoaderForTagLibs(final ClassLoader classLoader) throws OpenEJBException { final Set tldUrls = new HashSet(); if (classLoader == null) { return tldUrls; } if (classLoader == Object.class.getClassLoader()) { return tldUrls; } final List urls = urls(classLoader); final int hashCodeForUrls = hash(urls); final Set cachedSet = cacheByhashCode.get(hashCodeForUrls); if (cachedSet != null) { return cachedSet; } tldUrls.addAll(scan(classLoader.getParent())); if (urls.size() > 0) { final ExecutorService es = Executors.newFixedThreadPool(2 * Runtime.getRuntime().availableProcessors() + 1, new DaemonThreadFactory("OpenEJB-tld-server-scanning")); final Collection>> futures = new ArrayList>>(urls.size()); for (URL url : urls) { if (url.getProtocol().equals("jar")) { try { String path = url.getPath(); if (path.endsWith("!/")) { path = path.substring(0, path.length() - 2); } url = new URL(path); } catch (final MalformedURLException e) { DeploymentLoader.logger.warning("JSP tag library location bad: " + url.toExternalForm(), e); continue; } } if (!url.getProtocol().equals("file")) { continue; } final File file; try { file = toFile(url).getCanonicalFile().getAbsoluteFile(); } catch (final IOException e) { DeploymentLoader.logger.warning("JSP tag library location bad: " + url.toExternalForm(), e); continue; } futures.add(es.submit(new Callable>() { @Override public Set call() throws Exception { return scanForTagLibs(file); } })); } es.shutdown(); for (final Future> set : futures) { try { tldUrls.addAll(set.get()); } catch (final Exception e) { // no-op } } } cacheByhashCode.put(hashCodeForUrls, tldUrls); return tldUrls; } static Set scanWarForTagLibs(final File war) { final Set urls = new HashSet(); final File webInfDir = new File(war, "WEB-INF"); if (!webInfDir.isDirectory()) { return urls; } // skip the lib and classes dir in WEB-INF final LinkedList files = new LinkedList(); final File[] list = webInfDir.listFiles(); if (list != null) { for (final File file : list) { if ("lib".equals(file.getName()) || "classes".equals(file.getName())) { continue; } files.add(file); } } final File webInfMetaInf = new File(webInfDir, "classes/META-INF"); if (webInfMetaInf.exists()) { // filter directly to let it be faster in next loop files.addAll(asList(webInfMetaInf.listFiles(new FilenameFilter() { @Override public boolean accept(final File dir, final String name) { return name.endsWith(".tld"); } }))); } if (files.isEmpty()) { return urls; } // recursively scan the directories while (!files.isEmpty()) { File file = files.removeFirst(); if (file.isDirectory()) { final File[] a = file.listFiles(); if (a != null) { files.addAll(asList(a)); } } else if (file.getName().endsWith(".tld")) { try { file = file.getCanonicalFile().getAbsoluteFile(); urls.add(file.toURI().toURL()); } catch (final IOException e) { DeploymentLoader.logger.warning("JSP tag library location bad: " + file.getAbsolutePath(), e); } } } return urls; } static Set scanForTagLibs(final File file) { final Set tldLocations = new HashSet(); try { final String location = file.toURI().toURL().toExternalForm(); if (location.endsWith(".jar")) { final Set urls = scanJarForTagLibs(file); tldLocations.addAll(urls); } else if (file.getName().endsWith(".tld")) { final URL url = file.toURI().toURL(); tldLocations.add(url); } } catch (final IOException e) { DeploymentLoader.logger.warning("Error scanning for JSP tag libraries: " + file.getAbsolutePath(), e); } return tldLocations; } static Set scanJarForTagLibs(final File file) { final Set urls = new HashSet(); if (!file.isFile()) { return urls; } JarFile jarFile = null; try { jarFile = new JarFile(file); final URL jarFileUrl = new URL("jar", "", -1, file.toURI().toURL().toExternalForm() + "!/"); for (final JarEntry entry : Collections.list(jarFile.entries())) { final String name = entry.getName(); if (!name.startsWith("META-INF/") || !name.endsWith(".tld")) { continue; } final URL url = new URL(jarFileUrl, name); urls.add(url); } } catch (final IOException e) { DeploymentLoader.logger.warning("Error scanning jar for JSP tag libraries: " + file.getAbsolutePath(), e); } finally { if (jarFile != null) { try { jarFile.close(); } catch (final IOException e) { // exception ignored } } } return urls; } // mainly used to forget a classloader (temp one generally) but keep scanning info from classloader urls public static void quickClean(final ClassLoader loader) { if (loader == null) { return; } cache.remove(loader); if (loader.getParent() != TldScanner.class.getClassLoader()) { // for ears quickClean(loader.getParent()); } } // this method clean the cacheByhash too public static void forceCompleteClean(final ClassLoader loader) { if (loader == null) { return; } quickClean(loader); cacheByhashCode.remove(hash(urls(loader))); if (loader.getParent() != TldScanner.class.getClassLoader()) { // for ears forceCompleteClean(loader.getParent()); } } private static List urls(final ClassLoader classLoader) { UrlSet urlSet = new UrlSet(); if (classLoader instanceof URLClassLoader) { final URLClassLoader urlClassLoader = (URLClassLoader) classLoader; try { urlSet = new UrlSet(urlClassLoader.getURLs()); } catch (final NullPointerException npe) { // happen for closeable classloaders like WebappClassLoader when already clean up return Collections.emptyList(); } } else { try { urlSet = new UrlSet(classLoader); } catch (final IOException e) { DeploymentLoader.logger.warning("Error scanning class loader for JSP tag libraries", e); } } try { urlSet = URLs.cullSystemJars(urlSet); urlSet = applyBuiltinExcludes(urlSet, Filters.tokens("openejb-jstl", "myfaces-impl", "javax.faces-2.", "spring-security-taglibs", "spring-webmvc")); } catch (final IOException e) { DeploymentLoader.logger.warning("Error scanning class loader for JSP tag libraries", e); } return urlSet.getUrls(); } private static int hash(final List urls) { int hash = 0; for (final URL u : urls) { hash *= 31; if (u != null) { hash += u.toExternalForm().hashCode(); // url.hashCode() can be slow offline } } return hash; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy