/** * 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 * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * 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 jug.client.util; import org.apache.xbean.finder.archive.Archive; import org.apache.xbean.finder.archive.ClassesArchive; import org.apache.xbean.finder.archive.ClasspathArchive; import org.apache.xbean.finder.archive.CompositeArchive; import org.apache.xbean.finder.archive.FilteredArchive; import org.apache.xbean.finder.filter.Filter; import org.apache.xbean.finder.filter.FilterList; import org.apache.xbean.finder.filter.PackageFilter; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.BufferedInputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; public class ConfigurableClasspathArchive extends CompositeArchive { private static final SAXParserFactory SAX_FACTORY = SAXParserFactory.newInstance(); private static final String SCAN_XML = "META-INF/scan.xml"; public ConfigurableClasspathArchive(final ClassLoader loader, final Iterable<URL> urls) { super(archive(loader, urls, true)); } public static List<Archive> archive(final ClassLoader loader, final Iterable<URL> urls, boolean forceDescriptor) { final List<Archive> archives = new ArrayList<Archive>(); for (URL location : urls) { try { archives.add(archive(loader, location, true)); } catch (Exception e) { // ignored } } return archives; } public static Archive archive(final ClassLoader loader, final URL location, boolean forceDescriptor) { try { URL scanXml = loader.getResource(SCAN_XML); if (scanXml == null && !forceDescriptor) { return ClasspathArchive.archive(loader, location); } else if (scanXml == null) { return new ClassesArchive(); } // read descriptors ScanHandler scan; if (scanXml != null) { scan = read(scanXml); } else { scan = new ScanHandler(); } final Archive packageArchive = packageArchive(scan.getPackages(), loader, location); final Archive classesArchive = classesArchive(scan.getPackages(), scan.getClasses(), loader); if (packageArchive != null && classesArchive != null) { return new CompositeArchive(classesArchive, packageArchive); } else if (packageArchive != null) { return packageArchive; } return classesArchive; } catch (IOException e) { if (forceDescriptor) { return new ClassesArchive(); } return ClasspathArchive.archive(loader, location); } } public static Archive packageArchive(final Set<String> packageNames, final ClassLoader loader, final URL url) { if (!packageNames.isEmpty()) { return new FilteredArchive(ClasspathArchive.archive(loader, url), filters(packageNames)); } return null; } private static Filter filters(final Set<String> packageNames) { final List<Filter> filters = new ArrayList<Filter>(); for (String packageName : packageNames) { filters.add(new PackageFilter(packageName)); } return new FilterList(filters); } public static Archive classesArchive(final Set<String> packages, final Set<String> classnames, final ClassLoader loader) { Class<?>[] classes = new Class<?>[classnames.size()]; int i = 0; for (String clazz : classnames) { // skip classes managed by package filtering if (packages != null && clazzInPackage(packages, clazz)) { continue; } try { classes[i++] = loader.loadClass(clazz); } catch (ClassNotFoundException e) { // ignored } } if (i != classes.length) { // shouldn't occur final Class<?>[] updatedClasses = new Class<?>[i]; System.arraycopy(classes, 0, updatedClasses, 0, i); classes = updatedClasses; } return new ClassesArchive(classes); } private static boolean clazzInPackage(final Collection<String> packagename, final String clazz) { for (String str : packagename) { if (clazz.startsWith(str)) { return true; } } return false; } public static ScanHandler read(final URL scanXml) throws IOException { final SAXParser parser; try { synchronized (SAX_FACTORY) { parser = SAX_FACTORY.newSAXParser(); } final ScanHandler handler = new ScanHandler(); parser.parse(new BufferedInputStream(scanXml.openStream()), handler); return handler; } catch (Exception e) { throw new IOException("can't parse " + scanXml.toExternalForm()); } } public static final class ScanHandler extends DefaultHandler { private final Set<String> classes = new HashSet<String>(); private final Set<String> packages = new HashSet<String>(); private Set<String> current = null; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("class")) { current = classes; } else if (qName.equals("package")) { current = packages; } } @Override public void characters(char ch[], int start, int length) throws SAXException { if (current != null) { current.add(new String(ch, start, length)); } } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { current = null; } public Set<String> getPackages() { return packages; } public Set<String> getClasses() { return classes; } } }