001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.osgi.application; 023 024import java.io.File; 025import java.io.IOException; 026import java.util.Collection; 027import java.util.List; 028import java.util.jar.JarFile; 029 030import org.nuxeo.osgi.BundleFile; 031import org.nuxeo.osgi.DirectoryBundleFile; 032import org.nuxeo.osgi.JarBundleFile; 033 034/** 035 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 036 */ 037public class ClassPathScanner { 038 039 protected boolean scanForNestedJARs = true; 040 041 protected final Callback callback; 042 043 /** 044 * If set points to a set of path prefixes to be excluded form bundle processing 045 */ 046 protected String[] blackList; 047 048 public ClassPathScanner(Callback callback) { 049 this.callback = callback; 050 } 051 052 public ClassPathScanner(Callback callback, boolean scanForNestedJars, String[] blackList) { 053 this.callback = callback; 054 scanForNestedJARs = scanForNestedJars; 055 this.blackList = blackList; 056 } 057 058 public void setScanForNestedJARs(boolean scanForNestedJars) { 059 scanForNestedJARs = scanForNestedJars; 060 } 061 062 /** 063 * FIXME: this javadoc is not correct. 064 * <p> 065 * Scans the given class path and put found OSGi bundles in bundles, regular JARs in jars and append any nested jar 066 * or bundle into the given class loader. 067 * 068 * @param classPath 069 */ 070 public void scan(List<File> classPath) { 071 for (File file : classPath) { 072 scan(file); 073 } 074 } 075 076 public void scan(File file) { 077 String path = file.getAbsolutePath(); 078 if (!(path.endsWith(".jar") || path.endsWith(".rar") || path.endsWith(".sar") || path.endsWith("_jar") 079 || path.endsWith("_rar") || path.endsWith("_sar"))) { 080 return; 081 } 082 if (blackList != null) { 083 for (String prefix : blackList) { 084 if (path.startsWith(prefix)) { 085 return; 086 } 087 } 088 } 089 try { 090 BundleFile bf; 091 if (file.isFile()) { 092 JarFile jar = new JarFile(file); 093 bf = new JarBundleFile(jar); 094 } else if (file.isDirectory()) { 095 bf = new DirectoryBundleFile(file); 096 } else { 097 return; 098 } 099 File nestedJARsDir; 100 if (bf.getSymbolicName() == null) { // a regular jar 101 nestedJARsDir = callback.handleJar(bf); 102 } else { // an osgi bundle 103 nestedJARsDir = callback.handleBundle(bf); 104 } 105 if (nestedJARsDir != null) { 106 Collection<BundleFile> nested = extractNestedJars(bf, nestedJARsDir); 107 if (nested != null) { 108 for (BundleFile nestedJar : nested) { 109 callback.handleNestedJar(nestedJar); 110 } 111 } 112 } 113 } catch (IOException e) { 114 // ignore exception since some manifest may be invalid (invalid ClassPath entries) etc. 115 } 116 } 117 118 public Collection<BundleFile> extractNestedJars(BundleFile bf, File nestedBundlesDir) throws IOException { 119 Collection<BundleFile> bundles = null; 120 if (scanForNestedJARs) { 121 bundles = bf.findNestedBundles(nestedBundlesDir); 122 } else { // use manifest to find nested jars 123 bundles = bf.getNestedBundles(nestedBundlesDir); 124 } 125 if (bundles != null && bundles.isEmpty()) { 126 bundles = null; 127 } 128 return bundles; 129 } 130 131 public interface Callback { 132 133 /** 134 * A nested JAR was found on the class path. Usually a callback should handle this by adding the JAR to a class 135 * loader 136 * <p> 137 * The callback should return a directory to be used to extract nested JARs from this JAR. 138 * <p> 139 * The callback may return null to skip nested JAR extraction 140 * 141 * @param bf the JAR found 142 */ 143 void handleNestedJar(BundleFile bf); 144 145 /** 146 * A JAR was found on the class path. Usually a callback should handle this by adding the JAR to a class loader. 147 * <p> 148 * The callback should return a directory to be used to extract nested JARs from this JAR. 149 * <p> 150 * The callback may return null to skip nested JAR extraction. 151 * 152 * @param bf the JAR found 153 * @return the folder to be used to extract JARs or null to skip extraction 154 */ 155 File handleJar(BundleFile bf); 156 157 /** 158 * A Bundle was found on the class path. Usually a callback should handle this by adding the Bundle to a class 159 * loader and installing it in an OSGi framework 160 * <p> 161 * The callback should return a directory to be used to extract nested JARs from this JAR. 162 * <p> 163 * The callback may return null to skip nested JAR extraction. 164 * 165 * @param bf the JAR found 166 * @return the folder to be used to extract JARs or null to skip extraction 167 */ 168 File handleBundle(BundleFile bf); 169 170 } 171 172}