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:bs@nuxeo.com">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 public void scan(List<File> classPath) { 063 for (File file : classPath) { 064 scan(file); 065 } 066 } 067 068 public void scan(File file) { 069 String path = file.getAbsolutePath(); 070 if (!(path.endsWith(".jar") || path.endsWith(".rar") || path.endsWith(".sar") || path.endsWith("_jar") 071 || path.endsWith("_rar") || path.endsWith("_sar"))) { 072 return; 073 } 074 if (blackList != null) { 075 for (String prefix : blackList) { 076 if (path.startsWith(prefix)) { 077 return; 078 } 079 } 080 } 081 try { 082 BundleFile bf; 083 if (file.isFile()) { 084 JarFile jar = new JarFile(file); 085 bf = new JarBundleFile(jar); 086 } else if (file.isDirectory()) { 087 bf = new DirectoryBundleFile(file); 088 } else { 089 return; 090 } 091 File nestedJARsDir; 092 if (bf.getSymbolicName() == null) { // a regular jar 093 nestedJARsDir = callback.handleJar(bf); 094 } else { // an osgi bundle 095 nestedJARsDir = callback.handleBundle(bf); 096 } 097 if (nestedJARsDir != null) { 098 Collection<BundleFile> nested = extractNestedJars(bf, nestedJARsDir); 099 if (nested != null) { 100 for (BundleFile nestedJar : nested) { 101 callback.handleNestedJar(nestedJar); 102 } 103 } 104 } 105 } catch (IOException e) { 106 // ignore exception since some manifest may be invalid (invalid ClassPath entries) etc. 107 } 108 } 109 110 public Collection<BundleFile> extractNestedJars(BundleFile bf, File nestedBundlesDir) throws IOException { 111 Collection<BundleFile> bundles = null; 112 if (scanForNestedJARs) { 113 bundles = bf.findNestedBundles(nestedBundlesDir); 114 } else { // use manifest to find nested jars 115 bundles = bf.getNestedBundles(nestedBundlesDir); 116 } 117 if (bundles != null && bundles.isEmpty()) { 118 bundles = null; 119 } 120 return bundles; 121 } 122 123 public interface Callback { 124 125 /** 126 * A nested JAR was found on the class path. Usually a callback should handle this by adding the JAR to a class 127 * loader 128 * <p> 129 * The callback should return a directory to be used to extract nested JARs from this JAR. 130 * <p> 131 * The callback may return null to skip nested JAR extraction 132 * 133 * @param bf the JAR found 134 */ 135 void handleNestedJar(BundleFile bf); 136 137 /** 138 * A JAR was found on the class path. Usually a callback should handle this by adding the JAR to a class loader. 139 * <p> 140 * The callback should return a directory to be used to extract nested JARs from this JAR. 141 * <p> 142 * The callback may return null to skip nested JAR extraction. 143 * 144 * @param bf the JAR found 145 * @return the folder to be used to extract JARs or null to skip extraction 146 */ 147 File handleJar(BundleFile bf); 148 149 /** 150 * A Bundle was found on the class path. Usually a callback should handle this by adding the Bundle to a class 151 * loader and installing it in an OSGi framework 152 * <p> 153 * The callback should return a directory to be used to extract nested JARs from this JAR. 154 * <p> 155 * The callback may return null to skip nested JAR extraction. 156 * 157 * @param bf the JAR found 158 * @return the folder to be used to extract JARs or null to skip extraction 159 */ 160 File handleBundle(BundleFile bf); 161 162 } 163 164}