/*
* (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.nuxeo.osgi.application;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.jar.JarFile;
import org.nuxeo.osgi.BundleFile;
import org.nuxeo.osgi.DirectoryBundleFile;
import org.nuxeo.osgi.JarBundleFile;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class ClassPathScanner {
protected boolean scanForNestedJARs = true;
protected final Callback callback;
/**
* If set points to a set of path prefixes to be excluded form bundle processing
*/
protected String[] blackList;
public ClassPathScanner(Callback callback) {
this.callback = callback;
}
public ClassPathScanner(Callback callback, boolean scanForNestedJars, String[] blackList) {
this.callback = callback;
scanForNestedJARs = scanForNestedJars;
this.blackList = blackList;
}
public void setScanForNestedJARs(boolean scanForNestedJars) {
scanForNestedJARs = scanForNestedJars;
}
/**
* FIXME: this javadoc is not correct.
* <p>
* Scans the given class path and put found OSGi bundles in bundles, regular JARs in jars and append any nested jar
* or bundle into the given class loader.
*
* @param classPath
*/
public void scan(List<File> classPath) {
for (File file : classPath) {
scan(file);
}
}
public void scan(File file) {
String path = file.getAbsolutePath();
if (!(path.endsWith(".jar") || path.endsWith(".rar") || path.endsWith(".sar") || path.endsWith("_jar")
|| path.endsWith("_rar") || path.endsWith("_sar"))) {
return;
}
if (blackList != null) {
for (String prefix : blackList) {
if (path.startsWith(prefix)) {
return;
}
}
}
try {
BundleFile bf;
if (file.isFile()) {
JarFile jar = new JarFile(file);
bf = new JarBundleFile(jar);
} else if (file.isDirectory()) {
bf = new DirectoryBundleFile(file);
} else {
return;
}
File nestedJARsDir;
if (bf.getSymbolicName() == null) { // a regular jar
nestedJARsDir = callback.handleJar(bf);
} else { // an osgi bundle
nestedJARsDir = callback.handleBundle(bf);
}
if (nestedJARsDir != null) {
Collection<BundleFile> nested = extractNestedJars(bf, nestedJARsDir);
if (nested != null) {
for (BundleFile nestedJar : nested) {
callback.handleNestedJar(nestedJar);
}
}
}
} catch (IOException e) {
// ignore exception since some manifest may be invalid (invalid ClassPath entries) etc.
}
}
public Collection<BundleFile> extractNestedJars(BundleFile bf, File nestedBundlesDir) throws IOException {
Collection<BundleFile> bundles = null;
if (scanForNestedJARs) {
bundles = bf.findNestedBundles(nestedBundlesDir);
} else { // use manifest to find nested jars
bundles = bf.getNestedBundles(nestedBundlesDir);
}
if (bundles != null && bundles.isEmpty()) {
bundles = null;
}
return bundles;
}
public interface Callback {
/**
* A nested JAR was found on the class path. Usually a callback should handle this by adding the JAR to a class
* loader
* <p>
* The callback should return a directory to be used to extract nested JARs from this JAR.
* <p>
* The callback may return null to skip nested JAR extraction
*
* @param bf the JAR found
*/
void handleNestedJar(BundleFile bf);
/**
* A JAR was found on the class path. Usually a callback should handle this by adding the JAR to a class loader.
* <p>
* The callback should return a directory to be used to extract nested JARs from this JAR.
* <p>
* The callback may return null to skip nested JAR extraction.
*
* @param bf the JAR found
* @return the folder to be used to extract JARs or null to skip extraction
*/
File handleJar(BundleFile bf);
/**
* A Bundle was found on the class path. Usually a callback should handle this by adding the Bundle to a class
* loader and installing it in an OSGi framework
* <p>
* The callback should return a directory to be used to extract nested JARs from this JAR.
* <p>
* The callback may return null to skip nested JAR extraction.
*
* @param bf the JAR found
* @return the folder to be used to extract JARs or null to skip extraction
*/
File handleBundle(BundleFile bf);
}
}