/*
 * JBoss, Home of Professional Open Source
 * Copyright 2009, Red Hat Middleware LLC, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * 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 org.jboss.shrinkwrap.impl.base;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.jboss.shrinkwrap.api.asset.Asset;
import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;

Implementation of scanner which can scan a URLClassLoader
Author:Thomas Heute, Gavin King, Norman Richards, Pete Muir, Aslak Knutsen
/** * Implementation of scanner which can scan a {@link URLClassLoader} * * @author Thomas Heute * @author Gavin King * @author Norman Richards * @author Pete Muir * @author <a href="mailto:aslak@conduct.no">Aslak Knutsen</a> */
public class URLPackageScanner { private static final Logger log = Logger.getLogger(URLPackageScanner.class.getName()); private static final String SUFFIX_CLASS = ".class"; private static final String WEB_INF_CLASSES_DIR = "WEB-INF/classes/"; private final String packageName; private final String packageNamePath; private final boolean addRecursively; private final ClassLoader classLoader; private String prefix; // private final Set<String> classes = new HashSet<String>(); private Callback callback;
Factory method to create an instance of URLPackageScanner.
Params:
  • addRecursively – flag to add child packages
  • classLoader – class loader that will have classes added
  • callback – Callback to invoke when a matching class is found
  • packageName – Package that will be scanned
Returns:new instance of URLPackageScanner
/** * Factory method to create an instance of URLPackageScanner. * * @param addRecursively flag to add child packages * @param classLoader class loader that will have classes added * @param callback Callback to invoke when a matching class is found * @param packageName Package that will be scanned * @return new instance of URLPackageScanner */
public static URLPackageScanner newInstance(boolean addRecursively, final ClassLoader classLoader, final Callback callback, final String packageName) { Validate.notNull(packageName, "Package name must be specified"); Validate.notNull(addRecursively, "AddRecursively must be specified"); Validate.notNull(classLoader, "ClassLoader must be specified"); Validate.notNull(callback, "Callback must be specified"); return new URLPackageScanner(packageName, addRecursively, classLoader, callback); } private URLPackageScanner(String packageName, boolean addRecursively, ClassLoader classLoader, Callback callback) { this.packageName = packageName; this.packageNamePath = packageName.replace(".", "/"); this.addRecursively = addRecursively; this.classLoader = classLoader; this.callback = callback; this.prefix = ""; } public void scanPackage() { try { Set<String> paths = new HashSet<String>(); for (URL url : loadResources(packageNamePath)) { String urlPath = url.getFile(); urlPath = URLDecoder.decode(urlPath, "UTF-8"); if (urlPath.startsWith("file:")) { urlPath = urlPath.substring(5); } if (urlPath.indexOf('!') > 0) { urlPath = urlPath.substring(0, urlPath.indexOf('!')); } paths.add(urlPath); } handle(paths); } catch (IOException ioe) { log.log(Level.WARNING, "could not read: " + packageName, ioe); } catch (ClassNotFoundException ioe) { log.log(Level.WARNING, "Class coud not be loaded in package: " + packageName, ioe); } } private void handleArchiveByFile(File file) throws IOException, ClassNotFoundException { try { log.fine("archive: " + file); ZipFile zip = new ZipFile(file); Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); if (name.startsWith(prefix + packageNamePath) && name.endsWith(SUFFIX_CLASS) && (addRecursively || !name.substring((prefix + packageNamePath).length() + 1).contains("/"))) { String className = name.replace("/", ".").substring(prefix.length(), name.length() - SUFFIX_CLASS.length()); foundClass(className, name ); } } } catch (ZipException e) { throw new RuntimeException("Error handling file " + file, e); } } private void handle(Set<String> paths) throws IOException, ClassNotFoundException { for (String urlPath : paths) { log.fine("scanning: " + urlPath); File file = new File(urlPath); if (file.isDirectory()) { handle(file, packageName); } else { handleArchiveByFile(file); } } } private void handle(File file, String packageName) throws ClassNotFoundException { for (File child : file.listFiles()) { if (!child.isDirectory() && child.getName().endsWith(SUFFIX_CLASS)) { final String packagePrefix = packageName.length() > 0 ? packageName + "." : packageName; String className = packagePrefix + child.getName().substring(0, child.getName().lastIndexOf(SUFFIX_CLASS)); foundClass(className, prefix + className.replace( '.', '/' ) + SUFFIX_CLASS ); } else if (child.isDirectory() && addRecursively) { handle(child, packageName + "." + child.getName()); } } } private void foundClass(String className, String path) { callback.classFound( className, new ClassLoaderAsset( path, classLoader) ); } private List<URL> loadResources(String name) throws IOException { ArrayList<URL> resources = Collections.list(classLoader.getResources(prefix + name)); if (resources.size() == 0) { prefix = WEB_INF_CLASSES_DIR; resources = Collections.list(classLoader.getResources(prefix + name)); } else { for (URL url : resources) { if (url.toString().contains(WEB_INF_CLASSES_DIR)) { prefix = WEB_INF_CLASSES_DIR; break; } } } return resources; }
Callback interface for found classes.
Author:Aslak Knutsen
Version:$Revision: $
/** * Callback interface for found classes. * * @author <a href="mailto:aknutsen@redhat.com">Aslak Knutsen</a> * @version $Revision: $ */
public interface Callback {
Called for each found class.
Params:
  • className – The name of the found class
/** * Called for each found class. * * @param className The name of the found class */
void classFound(String className, Asset asset); } }