0001: /*****************************************************************************
0002: * Java Plug-in Framework (JPF)
0003: * Copyright (C) 2004-2007 Dmitry Olshansky
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public
0007: * License as published by the Free Software Foundation; either
0008: * version 2.1 of the License, or (at your option) any later version.
0009: *
0010: * This library is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * Lesser General Public License for more details.
0014: *
0015: * You should have received a copy of the GNU Lesser General Public
0016: * License along with this library; if not, write to the Free Software
0017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0018: *****************************************************************************/package org.java.plugin.standard;
0019:
0020: import java.io.BufferedOutputStream;
0021: import java.io.File;
0022: import java.io.FileOutputStream;
0023: import java.io.IOException;
0024: import java.io.InputStream;
0025: import java.io.OutputStream;
0026: import java.net.MalformedURLException;
0027: import java.net.URL;
0028: import java.net.URLClassLoader;
0029: import java.security.AccessController;
0030: import java.security.CodeSource;
0031: import java.security.PrivilegedAction;
0032: import java.security.ProtectionDomain;
0033: import java.util.Arrays;
0034: import java.util.Collections;
0035: import java.util.Enumeration;
0036: import java.util.HashMap;
0037: import java.util.HashSet;
0038: import java.util.Iterator;
0039: import java.util.LinkedList;
0040: import java.util.List;
0041: import java.util.Map;
0042: import java.util.Set;
0043: import java.util.Map.Entry;
0044:
0045: import org.apache.commons.logging.Log;
0046: import org.apache.commons.logging.LogFactory;
0047: import org.java.plugin.PathResolver;
0048: import org.java.plugin.Plugin;
0049: import org.java.plugin.PluginClassLoader;
0050: import org.java.plugin.PluginManager;
0051: import org.java.plugin.registry.Library;
0052: import org.java.plugin.registry.PluginDescriptor;
0053: import org.java.plugin.registry.PluginPrerequisite;
0054: import org.java.plugin.registry.PluginRegistry;
0055: import org.java.plugin.util.IoUtil;
0056:
0057: /**
0058: * Standard implementation of plug-in class loader.
0059: *
0060: * @version $Id: StandardPluginClassLoader.java,v 1.8 2007/04/07 12:39:50 ddimon Exp $
0061: */
0062: public class StandardPluginClassLoader extends PluginClassLoader {
0063: static Log log = LogFactory.getLog(StandardPluginClassLoader.class);
0064:
0065: private static File libCacheFolder;
0066: private static boolean libCacheFolderInitialized = false;
0067:
0068: private static URL getClassBaseUrl(final Class<?> cls) {
0069: ProtectionDomain pd = cls.getProtectionDomain();
0070: if (pd != null) {
0071: CodeSource cs = pd.getCodeSource();
0072: if (cs != null) {
0073: return cs.getLocation();
0074: }
0075: }
0076: return null;
0077: }
0078:
0079: private static URL[] getUrls(final PluginManager manager,
0080: final PluginDescriptor descr) {
0081: List<URL> result = new LinkedList<URL>();
0082: for (Library lib : descr.getLibraries()) {
0083: if (!lib.isCodeLibrary()) {
0084: continue;
0085: }
0086: result.add(manager.getPathResolver().resolvePath(lib,
0087: lib.getPath()));
0088: }
0089: if (log.isDebugEnabled()) {
0090: final StringBuilder buf = new StringBuilder();
0091: buf.append("Code URL's populated for plug-in " //$NON-NLS-1$
0092: + descr + ":\r\n"); //$NON-NLS-1$
0093: for (Object element : result) {
0094: buf.append("\t"); //$NON-NLS-1$
0095: buf.append(element);
0096: buf.append("\r\n"); //$NON-NLS-1$
0097: }
0098: log.debug(buf.toString());
0099: }
0100: return result.toArray(new URL[result.size()]);
0101: }
0102:
0103: private static URL[] getUrls(final PluginManager manager,
0104: final PluginDescriptor descr, final URL[] existingUrls) {
0105: final List<URL> urls = Arrays.asList(existingUrls);
0106: final List<URL> result = new LinkedList<URL>();
0107: for (Library lib : descr.getLibraries()) {
0108: if (!lib.isCodeLibrary()) {
0109: continue;
0110: }
0111: URL url = manager.getPathResolver().resolvePath(lib,
0112: lib.getPath());
0113: if (!urls.contains(url)) {
0114: result.add(url);
0115: }
0116: }
0117: return result.toArray(new URL[result.size()]);
0118: }
0119:
0120: private static File getLibCacheFolder() {
0121: if (libCacheFolder != null) {
0122: return libCacheFolderInitialized ? libCacheFolder : null;
0123: }
0124: synchronized (StandardPluginClassLoader.class) {
0125: libCacheFolder = new File(System
0126: .getProperty("java.io.tmpdir"), //$NON-NLS-1$
0127: System.currentTimeMillis() + ".jpf-lib-cache"); //$NON-NLS-1$
0128: log.debug("libraries cache folder is " + libCacheFolder); //$NON-NLS-1$
0129: File lockFile = new File(libCacheFolder, "lock"); //$NON-NLS-1$
0130: if (lockFile.exists()) {
0131: log.error("can't initialize libraries cache folder " //$NON-NLS-1$
0132: + libCacheFolder
0133: + " as lock file indicates that it" //$NON-NLS-1$
0134: + " is owned by another JPF instance"); //$NON-NLS-1$
0135: return null;
0136: }
0137: if (libCacheFolder.exists()) {
0138: // clean up folder
0139: IoUtil.emptyFolder(libCacheFolder);
0140: } else {
0141: libCacheFolder.mkdirs();
0142: }
0143: try {
0144: if (!lockFile.createNewFile()) {
0145: log
0146: .error("can\'t create lock file in JPF libraries cache" //$NON-NLS-1$
0147: + " folder " + libCacheFolder); //$NON-NLS-1$
0148: return null;
0149: }
0150: } catch (IOException ioe) {
0151: log.error(
0152: "can\'t create lock file in JPF libraries cache" //$NON-NLS-1$
0153: + " folder " + libCacheFolder, ioe); //$NON-NLS-1$
0154: return null;
0155: }
0156: lockFile.deleteOnExit();
0157: libCacheFolder.deleteOnExit();
0158: libCacheFolderInitialized = true;
0159: }
0160: return libCacheFolder;
0161: }
0162:
0163: private PluginDescriptor[] publicImports;
0164: private PluginDescriptor[] privateImports;
0165: private PluginDescriptor[] reverseLookups;
0166: private PluginResourceLoader resourceLoader;
0167: private Map<String, ResourceFilter> resourceFilters;
0168: private Map<String, File> libraryCache;
0169: private boolean probeParentLoaderLast;
0170: private boolean stickySynchronizing;
0171: private boolean localClassLoadingOptimization = true;
0172: private boolean foreignClassLoadingOptimization = true;
0173: private final Set<String> localPackages = new HashSet<String>();
0174: private final Map<String, PluginDescriptor> pluginPackages = new HashMap<String, PluginDescriptor>();
0175:
0176: /**
0177: * Creates class instance configured to load classes and resources for given
0178: * plug-in.
0179: *
0180: * @param aManager
0181: * plug-in manager instance
0182: * @param descr
0183: * plug-in descriptor
0184: * @param parent
0185: * parent class loader, usually this is JPF "host" application
0186: * class loader
0187: */
0188: public StandardPluginClassLoader(final PluginManager aManager,
0189: final PluginDescriptor descr, final ClassLoader parent) {
0190: super (aManager, descr, getUrls(aManager, descr), parent);
0191: collectImports();
0192: resourceLoader = PluginResourceLoader.get(aManager, descr);
0193: collectFilters();
0194: libraryCache = new HashMap<String, File>();
0195: }
0196:
0197: protected void collectImports() {
0198: // collect imported plug-ins (exclude duplicates)
0199: final Map<String, PluginDescriptor> publicImportsMap = new HashMap<String, PluginDescriptor>();
0200: final Map<String, PluginDescriptor> privateImportsMap = new HashMap<String, PluginDescriptor>();
0201: PluginRegistry registry = getPluginDescriptor().getRegistry();
0202: for (PluginPrerequisite pre : getPluginDescriptor()
0203: .getPrerequisites()) {
0204: if (!pre.matches()) {
0205: continue;
0206: }
0207: PluginDescriptor preDescr = registry
0208: .getPluginDescriptor(pre.getPluginId());
0209: if (pre.isExported()) {
0210: publicImportsMap.put(preDescr.getId(), preDescr);
0211: } else {
0212: privateImportsMap.put(preDescr.getId(), preDescr);
0213: }
0214: }
0215: publicImports = publicImportsMap.values().toArray(
0216: new PluginDescriptor[publicImportsMap.size()]);
0217: privateImports = privateImportsMap.values().toArray(
0218: new PluginDescriptor[privateImportsMap.size()]);
0219: // collect reverse look up plug-ins (exclude duplicates)
0220: final Map<String, PluginDescriptor> reverseLookupsMap = new HashMap<String, PluginDescriptor>();
0221: for (PluginDescriptor descr : registry.getPluginDescriptors()) {
0222: if (descr.equals(getPluginDescriptor())
0223: || publicImportsMap.containsKey(descr.getId())
0224: || privateImportsMap.containsKey(descr.getId())) {
0225: continue;
0226: }
0227: for (PluginPrerequisite pre : descr.getPrerequisites()) {
0228: if (!pre.getPluginId().equals(
0229: getPluginDescriptor().getId())
0230: || !pre.isReverseLookup()) {
0231: continue;
0232: }
0233: if (!pre.matches()) {
0234: continue;
0235: }
0236: reverseLookupsMap.put(descr.getId(), descr);
0237: break;
0238: }
0239: }
0240: reverseLookups = reverseLookupsMap.values().toArray(
0241: new PluginDescriptor[reverseLookupsMap.size()]);
0242: }
0243:
0244: protected void collectFilters() {
0245: if (resourceFilters == null) {
0246: resourceFilters = new HashMap<String, ResourceFilter>();
0247: } else {
0248: resourceFilters.clear();
0249: }
0250: for (Library lib : getPluginDescriptor().getLibraries()) {
0251: resourceFilters.put(getPluginManager().getPathResolver()
0252: .resolvePath(lib, lib.getPath()).toExternalForm(),
0253: new ResourceFilter(lib));
0254: }
0255: }
0256:
0257: /**
0258: * @see org.java.plugin.PluginClassLoader#pluginsSetChanged()
0259: */
0260: @Override
0261: protected void pluginsSetChanged() {
0262: URL[] newUrls = getUrls(getPluginManager(),
0263: getPluginDescriptor(), getURLs());
0264: for (URL element : newUrls) {
0265: addURL(element);
0266: }
0267: if (log.isDebugEnabled()) {
0268: StringBuilder buf = new StringBuilder();
0269: buf.append("New code URL's populated for plug-in " //$NON-NLS-1$
0270: + getPluginDescriptor() + ":\r\n"); //$NON-NLS-1$
0271: for (URL element : newUrls) {
0272: buf.append("\t"); //$NON-NLS-1$
0273: buf.append(element);
0274: buf.append("\r\n"); //$NON-NLS-1$
0275: }
0276: log.debug(buf.toString());
0277: }
0278: collectImports();
0279: // repopulate resource URLs
0280: resourceLoader = PluginResourceLoader.get(getPluginManager(),
0281: getPluginDescriptor());
0282: collectFilters();
0283: Set<Entry<String, File>> entrySet = libraryCache.entrySet();
0284: for (Iterator<Entry<String, File>> it = entrySet.iterator(); it
0285: .hasNext();) {
0286: if (it.next().getValue() == null) {
0287: it.remove();
0288: }
0289: }
0290: synchronized (localPackages) {
0291: localPackages.clear();
0292: }
0293: synchronized (pluginPackages) {
0294: pluginPackages.clear();
0295: }
0296: }
0297:
0298: /**
0299: * @see org.java.plugin.PluginClassLoader#dispose()
0300: */
0301: @Override
0302: protected void dispose() {
0303: for (File file : libraryCache.values()) {
0304: file.delete();
0305: }
0306: libraryCache.clear();
0307: resourceFilters.clear();
0308: privateImports = null;
0309: publicImports = null;
0310: resourceLoader = null;
0311: synchronized (localPackages) {
0312: localPackages.clear();
0313: }
0314: synchronized (pluginPackages) {
0315: pluginPackages.clear();
0316: }
0317: }
0318:
0319: protected void setProbeParentLoaderLast(final boolean value) {
0320: probeParentLoaderLast = value;
0321: }
0322:
0323: protected void setStickySynchronizing(final boolean value) {
0324: stickySynchronizing = value;
0325: }
0326:
0327: protected void setLocalClassLoadingOptimization(final boolean value) {
0328: localClassLoadingOptimization = value;
0329: }
0330:
0331: protected void setForeignClassLoadingOptimization(
0332: final boolean value) {
0333: foreignClassLoadingOptimization = value;
0334: }
0335:
0336: /**
0337: * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
0338: */
0339: @Override
0340: protected Class<?> loadClass(final String name,
0341: final boolean resolve) throws ClassNotFoundException {
0342: Class<?> result;
0343: boolean tryLocal = true;
0344: if (isLocalClass(name)) {
0345: if (log.isDebugEnabled()) {
0346: log.debug("loadClass: trying local class guess, name=" //$NON-NLS-1$
0347: + name + ", this=" + this ); //$NON-NLS-1$
0348: }
0349: result = loadLocalClass(name, resolve, this );
0350: if (result != null) {
0351: if (log.isDebugEnabled()) {
0352: log
0353: .debug("loadClass: local class guess succeeds, name=" //$NON-NLS-1$
0354: + name + ", this=" + this ); //$NON-NLS-1$
0355: }
0356: checkClassVisibility(result, this );
0357: return result;
0358: }
0359: tryLocal = false;
0360: }
0361: if (probeParentLoaderLast) {
0362: try {
0363: result = loadPluginClass(name, resolve, tryLocal, this ,
0364: null);
0365: } catch (ClassNotFoundException cnfe) {
0366: result = getParent().loadClass(name);
0367: }
0368: if (result == null) {
0369: result = getParent().loadClass(name);
0370: }
0371: } else {
0372: try {
0373: result = getParent().loadClass(name);
0374: } catch (ClassNotFoundException cnfe) {
0375: result = loadPluginClass(name, resolve, tryLocal, this ,
0376: null);
0377: }
0378: }
0379: if (result != null) {
0380: return result;
0381: }
0382: throw new ClassNotFoundException(name);
0383: }
0384:
0385: private Class<?> loadLocalClass(final String name,
0386: final boolean resolve,
0387: final StandardPluginClassLoader requestor) {
0388: boolean debugEnabled = log.isDebugEnabled();
0389: synchronized (stickySynchronizing ? requestor : this ) {
0390: Class<?> result = findLoadedClass(name);
0391: if (result != null) {
0392: if (debugEnabled) {
0393: log
0394: .debug("loadLocalClass: found loaded class, class=" //$NON-NLS-1$
0395: + result + ", this=" //$NON-NLS-1$
0396: + this + ", requestor=" + requestor); //$NON-NLS-1$
0397: }
0398: return result; // found already loaded class in this plug-in
0399: }
0400: try {
0401: result = findClass(name);
0402: } catch (LinkageError le) {
0403: if (debugEnabled) {
0404: log.debug("loadLocalClass: class loading failed," //$NON-NLS-1$
0405: + " name=" + name + ", this=" //$NON-NLS-1$ //$NON-NLS-2$
0406: + this + ", requestor=" + requestor, le); //$NON-NLS-1$
0407: }
0408: throw le;
0409: } catch (ClassNotFoundException cnfe) {
0410: // ignore
0411: }
0412: if (result != null) {
0413: if (debugEnabled) {
0414: log.debug("loadLocalClass: found class, class=" //$NON-NLS-1$
0415: + result + ", this=" //$NON-NLS-1$
0416: + this + ", requestor=" + requestor); //$NON-NLS-1$
0417: }
0418: if (resolve) {
0419: resolveClass(result);
0420: }
0421: registerLocalPackage(result);
0422: return result; // found class in this plug-in
0423: }
0424: }
0425: return null;
0426: }
0427:
0428: private Class<?> loadPluginClass(final String name,
0429: final boolean resolve, final boolean tryLocal,
0430: final StandardPluginClassLoader requestor,
0431: final Set<String> seenPlugins)
0432: throws ClassNotFoundException {
0433: Set<String> seen = seenPlugins;
0434: if ((seen != null)
0435: && seen.contains(getPluginDescriptor().getId())) {
0436: return null;
0437: }
0438: if (seen == null) {
0439: seen = new HashSet<String>();
0440: }
0441: seen.add(getPluginDescriptor().getId());
0442: if ((this != requestor)
0443: && !getPluginManager().isPluginActivated(
0444: getPluginDescriptor())
0445: && !getPluginManager().isPluginActivating(
0446: getPluginDescriptor())) {
0447: String msg = "can't load class " + name + ", plug-in " //$NON-NLS-1$ //$NON-NLS-2$
0448: + getPluginDescriptor() + " is not activated yet"; //$NON-NLS-1$
0449: log.warn(msg);
0450: throw new ClassNotFoundException(msg);
0451: }
0452: Class<?> result = null;
0453: boolean debugEnabled = log.isDebugEnabled();
0454: PluginDescriptor descr = guessPlugin(name);
0455: if ((descr != null) && !seen.contains(descr.getId())) {
0456: if (debugEnabled) {
0457: log
0458: .debug("loadPluginClass: trying plug-in guess, name=" //$NON-NLS-1$
0459: + name + ", this=" //$NON-NLS-1$
0460: + this + ", requestor=" + requestor); //$NON-NLS-1$
0461: }
0462: result = ((StandardPluginClassLoader) getPluginManager()
0463: .getPluginClassLoader(descr)).loadPluginClass(name,
0464: resolve, true, requestor, seen);
0465: if (result != null) {
0466: if (debugEnabled) {
0467: log
0468: .debug("loadPluginClass: plug-in guess succeeds, name=" //$NON-NLS-1$
0469: + name + ", this=" //$NON-NLS-1$
0470: + this + ", requestor=" + requestor); //$NON-NLS-1$
0471: }
0472: return result;
0473: }
0474: }
0475: if (tryLocal) {
0476: result = loadLocalClass(name, resolve, requestor);
0477: if (result != null) {
0478: checkClassVisibility(result, requestor);
0479: return result;
0480: }
0481: }
0482: if (debugEnabled) {
0483: log.debug("loadPluginClass: local class not found, name=" //$NON-NLS-1$
0484: + name + ", this=" //$NON-NLS-1$
0485: + this + ", requestor=" + requestor); //$NON-NLS-1$
0486: }
0487: for (PluginDescriptor element : publicImports) {
0488: if (seen.contains(element.getId())) {
0489: continue;
0490: }
0491: result = ((StandardPluginClassLoader) getPluginManager()
0492: .getPluginClassLoader(element)).loadPluginClass(
0493: name, resolve, true, requestor, seen);
0494: if (result != null) {
0495: break; // found class in publicly imported plug-in
0496: }
0497: }
0498: if ((this == requestor) && (result == null)) {
0499: for (PluginDescriptor element : privateImports) {
0500: if (seen.contains(element.getId())) {
0501: continue;
0502: }
0503: result = ((StandardPluginClassLoader) getPluginManager()
0504: .getPluginClassLoader(element))
0505: .loadPluginClass(name, resolve, true,
0506: requestor, seen);
0507: if (result != null) {
0508: break; // found class in privately imported plug-in
0509: }
0510: }
0511: }
0512: if (result == null) {
0513: for (PluginDescriptor element : reverseLookups) {
0514: if (seen.contains(element.getId())) {
0515: continue;
0516: }
0517: if (!getPluginManager().isPluginActivated(element)
0518: && !getPluginManager().isPluginActivating(
0519: element)) {
0520: continue;
0521: }
0522: result = ((StandardPluginClassLoader) getPluginManager()
0523: .getPluginClassLoader(element))
0524: .loadPluginClass(name, resolve, true,
0525: requestor, seen);
0526: if (result != null) {
0527: break; // found class in plug-in that marks itself as
0528: // allowed reverse look up
0529: }
0530: }
0531: }
0532: registerPluginPackage(result);
0533: return result;
0534: }
0535:
0536: private boolean isLocalClass(final String className) {
0537: if (!localClassLoadingOptimization) {
0538: return false;
0539: }
0540: String pkgName = getPackageName(className);
0541: if (pkgName == null) {
0542: return false;
0543: }
0544: return localPackages.contains(pkgName);
0545: }
0546:
0547: private void registerLocalPackage(final Class<?> cls) {
0548: if (!localClassLoadingOptimization) {
0549: return;
0550: }
0551: String pkgName = getPackageName(cls.getName());
0552: if ((pkgName == null) || localPackages.contains(pkgName)) {
0553: return;
0554: }
0555: synchronized (localPackages) {
0556: localPackages.add(pkgName);
0557: }
0558: if (log.isDebugEnabled()) {
0559: log.debug("registered local package: name=" + pkgName); //$NON-NLS-1$
0560: }
0561: }
0562:
0563: private PluginDescriptor guessPlugin(final String className) {
0564: if (!foreignClassLoadingOptimization) {
0565: return null;
0566: }
0567: String pkgName = getPackageName(className);
0568: if (pkgName == null) {
0569: return null;
0570: }
0571: return pluginPackages.get(pkgName);
0572: }
0573:
0574: private void registerPluginPackage(final Class<?> cls) {
0575: if (!foreignClassLoadingOptimization) {
0576: return;
0577: }
0578: Plugin plugin = getPluginManager().getPluginFor(cls);
0579: if (plugin == null) {
0580: return;
0581: }
0582: String pkgName = getPackageName(cls.getName());
0583: if ((pkgName == null) || pluginPackages.containsKey(pkgName)) {
0584: return;
0585: }
0586: synchronized (pluginPackages) {
0587: pluginPackages.put(pkgName, plugin.getDescriptor());
0588: }
0589: if (log.isDebugEnabled()) {
0590: log.debug("registered plug-in package: name=" + pkgName //$NON-NLS-1$
0591: + ", plugin=" + plugin.getDescriptor()); //$NON-NLS-1$
0592: }
0593: }
0594:
0595: private String getPackageName(final String className) {
0596: int p = className.lastIndexOf('.');
0597: if (p == -1) {
0598: return null;
0599: }
0600: return className.substring(0, p);
0601: }
0602:
0603: protected void checkClassVisibility(final Class<?> cls,
0604: final StandardPluginClassLoader requestor)
0605: throws ClassNotFoundException {
0606: if (this == requestor) {
0607: return;
0608: }
0609: URL lib = getClassBaseUrl(cls);
0610: if (lib == null) {
0611: return; // cls is a system class
0612: }
0613: ClassLoader loader = cls.getClassLoader();
0614: if (!(loader instanceof StandardPluginClassLoader)) {
0615: return;
0616: }
0617: if (loader != this ) {
0618: ((StandardPluginClassLoader) loader).checkClassVisibility(
0619: cls, requestor);
0620: } else {
0621: ResourceFilter filter = resourceFilters.get(lib
0622: .toExternalForm());
0623: if (filter == null) {
0624: log
0625: .warn("class not visible, no class filter found, lib=" + lib //$NON-NLS-1$
0626: + ", class=" + cls + ", this=" + this //$NON-NLS-1$ //$NON-NLS-2$
0627: + ", requestor=" + requestor); //$NON-NLS-1$
0628: throw new ClassNotFoundException("class " //$NON-NLS-1$
0629: + cls.getName()
0630: + " is not visible for plug-in " //$NON-NLS-1$
0631: + requestor.getPluginDescriptor().getId()
0632: + ", no filter found for library " + lib); //$NON-NLS-1$
0633: }
0634: if (!filter.isClassVisible(cls.getName())) {
0635: log.warn("class not visible, lib=" + lib //$NON-NLS-1$
0636: + ", class=" + cls + ", this=" + this //$NON-NLS-1$ //$NON-NLS-2$
0637: + ", requestor=" + requestor); //$NON-NLS-1$
0638: throw new ClassNotFoundException("class " //$NON-NLS-1$
0639: + cls.getName()
0640: + " is not visible for plug-in " //$NON-NLS-1$
0641: + requestor.getPluginDescriptor().getId());
0642: }
0643: }
0644: }
0645:
0646: /**
0647: * @see java.lang.ClassLoader#findLibrary(java.lang.String)
0648: */
0649: @Override
0650: protected String findLibrary(final String name) {
0651: if ((name == null) || "".equals(name.trim())) { //$NON-NLS-1$
0652: return null;
0653: }
0654: if (log.isDebugEnabled()) {
0655: log.debug("findLibrary(String): name=" + name //$NON-NLS-1$
0656: + ", this=" + this ); //$NON-NLS-1$
0657: }
0658: String libname = System.mapLibraryName(name);
0659: String result = null;
0660: PathResolver pathResolver = getPluginManager()
0661: .getPathResolver();
0662: for (Library lib : getPluginDescriptor().getLibraries()) {
0663: if (lib.isCodeLibrary())
0664: continue;
0665:
0666: URL libUrl = pathResolver.resolvePath(lib, lib.getPath()
0667: + libname);
0668: if (log.isDebugEnabled()) {
0669: log.debug("findLibrary(String): trying URL " + libUrl); //$NON-NLS-1$
0670: }
0671: File libFile = IoUtil.url2file(libUrl);
0672: if (libFile != null) {
0673: if (log.isDebugEnabled()) {
0674: log.debug("findLibrary(String): URL " + libUrl //$NON-NLS-1$
0675: + " resolved as local file " + libFile); //$NON-NLS-1$
0676: }
0677: if (libFile.isFile()) {
0678: result = libFile.getAbsolutePath();
0679: break;
0680: }
0681: continue;
0682: }
0683: // we have some kind of non-local URL
0684: // try to copy it to local temporary file
0685: String libraryCacheKey = libUrl.toExternalForm();
0686: libFile = libraryCache.get(libraryCacheKey);
0687: if (libFile != null) {
0688: if (libFile.isFile()) {
0689: result = libFile.getAbsolutePath();
0690: break;
0691: }
0692: libraryCache.remove(libraryCacheKey);
0693: }
0694: if (libraryCache.containsKey(libraryCacheKey)) {
0695: // already tried to cache this library
0696: break;
0697: }
0698: libFile = cacheLibrary(libUrl, libname);
0699: if (libFile != null) {
0700: result = libFile.getAbsolutePath();
0701: break;
0702: }
0703: }
0704: if (log.isDebugEnabled()) {
0705: log.debug("findLibrary(String): name=" + name //$NON-NLS-1$
0706: + ", libname=" + libname //$NON-NLS-1$
0707: + ", result=" + result //$NON-NLS-1$
0708: + ", this=" + this ); //$NON-NLS-1$
0709: }
0710: return result;
0711: }
0712:
0713: protected synchronized File cacheLibrary(final URL libUrl,
0714: final String libname) {
0715: String libraryCacheKey = libUrl.toExternalForm();
0716: File result = libraryCache.get(libraryCacheKey);
0717: if (result != null) {
0718: return result;
0719: }
0720: try {
0721: File cacheFolder = getLibCacheFolder();
0722: if (cacheFolder == null) {
0723: throw new IOException(
0724: "can't initialize libraries cache folder"); //$NON-NLS-1$
0725: }
0726: File libCachePluginFolder = new File(cacheFolder,
0727: getPluginDescriptor().getUniqueId());
0728: if (!libCachePluginFolder.exists()
0729: && !libCachePluginFolder.mkdirs()) {
0730: throw new IOException("can't create cache folder " //$NON-NLS-1$
0731: + libCachePluginFolder);
0732: }
0733: result = new File(libCachePluginFolder, libname);
0734: InputStream in = IoUtil.getResourceInputStream(libUrl);
0735: try {
0736: OutputStream out = new BufferedOutputStream(
0737: new FileOutputStream(result));
0738: try {
0739: IoUtil.copyStream(in, out, 512);
0740: } finally {
0741: out.close();
0742: }
0743: } finally {
0744: in.close();
0745: }
0746: if (log.isDebugEnabled()) {
0747: log.debug("library " + libname //$NON-NLS-1$
0748: + " successfully cached from URL " + libUrl //$NON-NLS-1$
0749: + " and saved to local file " + result); //$NON-NLS-1$
0750: }
0751: } catch (IOException ioe) {
0752: log.error("can't cache library " + libname //$NON-NLS-1$
0753: + " from URL " + libUrl, ioe); //$NON-NLS-1$
0754: result = null;
0755: }
0756: libraryCache.put(libraryCacheKey, result);
0757: return result;
0758: }
0759:
0760: /**
0761: * @see java.lang.ClassLoader#findResource(java.lang.String)
0762: */
0763: @Override
0764: public URL findResource(final String name) {
0765: return findResource(name, this , null);
0766: }
0767:
0768: /**
0769: * @see java.lang.ClassLoader#findResources(java.lang.String)
0770: */
0771: @Override
0772: public Enumeration<URL> findResources(final String name)
0773: throws IOException {
0774: final List<URL> result = new LinkedList<URL>();
0775: findResources(result, name, this , null);
0776: return Collections.enumeration(result);
0777: }
0778:
0779: protected URL findResource(final String name,
0780: final StandardPluginClassLoader requestor,
0781: final Set<String> seenPlugins) {
0782: Set<String> seen = seenPlugins;
0783: if ((seen != null)
0784: && seen.contains(getPluginDescriptor().getId())) {
0785: return null;
0786: }
0787: URL result = super .findResource(name);
0788: if (result != null) { // found resource in this plug-in class path
0789: if (log.isDebugEnabled()) {
0790: log
0791: .debug("findResource(...): resource found in classpath, name=" //$NON-NLS-1$
0792: + name + " URL=" + result + ", this=" //$NON-NLS-1$ //$NON-NLS-2$
0793: + this + ", requestor=" + requestor); //$NON-NLS-1$
0794: }
0795: if (isResourceVisible(name, result, requestor)) {
0796: return result;
0797: }
0798: return null;
0799: }
0800: if (resourceLoader != null) {
0801: result = resourceLoader.findResource(name);
0802: if (result != null) { // found resource in this plug-in resource
0803: // libraries
0804: if (log.isDebugEnabled()) {
0805: log
0806: .debug("findResource(...): resource found in libraries, name=" //$NON-NLS-1$
0807: + name
0808: + ", URL=" + result + ", this=" //$NON-NLS-1$ //$NON-NLS-2$
0809: + this + ", requestor=" + requestor); //$NON-NLS-1$
0810: }
0811: if (isResourceVisible(name, result, requestor)) {
0812: return result;
0813: }
0814: return null;
0815: }
0816: }
0817: if (seen == null) {
0818: seen = new HashSet<String>();
0819: }
0820: if (log.isDebugEnabled()) {
0821: log.debug("findResource(...): resource not found, name=" //$NON-NLS-1$
0822: + name + ", this=" //$NON-NLS-1$
0823: + this + ", requestor=" + requestor); //$NON-NLS-1$
0824: }
0825: seen.add(getPluginDescriptor().getId());
0826: for (PluginDescriptor element : publicImports) {
0827: if (seen.contains(element.getId())) {
0828: continue;
0829: }
0830: result = ((StandardPluginClassLoader) getPluginManager()
0831: .getPluginClassLoader(element)).findResource(name,
0832: requestor, seen);
0833: if (result != null) {
0834: break; // found resource in publicly imported plug-in
0835: }
0836: }
0837: if ((this == requestor) && (result == null)) {
0838: for (PluginDescriptor element : privateImports) {
0839: if (seen.contains(element.getId())) {
0840: continue;
0841: }
0842: result = ((StandardPluginClassLoader) getPluginManager()
0843: .getPluginClassLoader(element)).findResource(
0844: name, requestor, seen);
0845: if (result != null) {
0846: break; // found resource in privately imported plug-in
0847: }
0848: }
0849: }
0850: if (result == null) {
0851: for (PluginDescriptor element : reverseLookups) {
0852: if (seen.contains(element.getId())) {
0853: continue;
0854: }
0855: result = ((StandardPluginClassLoader) getPluginManager()
0856: .getPluginClassLoader(element)).findResource(
0857: name, requestor, seen);
0858: if (result != null) {
0859: break; // found resource in plug-in that marks itself as
0860: // allowed reverse look up
0861: }
0862: }
0863: }
0864: return result;
0865: }
0866:
0867: protected void findResources(final List<URL> result,
0868: final String name,
0869: final StandardPluginClassLoader requestor,
0870: final Set<String> seenPlugins) throws IOException {
0871: Set<String> seen = seenPlugins;
0872: if ((seen != null)
0873: && seen.contains(getPluginDescriptor().getId())) {
0874: return;
0875: }
0876: URL url;
0877: for (Enumeration<URL> enm = super .findResources(name); enm
0878: .hasMoreElements();) {
0879: url = enm.nextElement();
0880: if (isResourceVisible(name, url, requestor)) {
0881: result.add(url);
0882: }
0883: }
0884: if (resourceLoader != null) {
0885: for (Enumeration<URL> enm = resourceLoader
0886: .findResources(name); enm.hasMoreElements();) {
0887: url = enm.nextElement();
0888: if (isResourceVisible(name, url, requestor)) {
0889: result.add(url);
0890: }
0891: }
0892: }
0893: if (seen == null) {
0894: seen = new HashSet<String>();
0895: }
0896: seen.add(getPluginDescriptor().getId());
0897: for (PluginDescriptor element : publicImports) {
0898: if (seen.contains(element.getId())) {
0899: continue;
0900: }
0901: ((StandardPluginClassLoader) getPluginManager()
0902: .getPluginClassLoader(element)).findResources(
0903: result, name, requestor, seen);
0904: }
0905: if (this == requestor) {
0906: for (PluginDescriptor element : privateImports) {
0907: if (seen.contains(element.getId())) {
0908: continue;
0909: }
0910: ((StandardPluginClassLoader) getPluginManager()
0911: .getPluginClassLoader(element)).findResources(
0912: result, name, requestor, seen);
0913: }
0914: }
0915: for (PluginDescriptor element : reverseLookups) {
0916: if (seen.contains(element.getId())) {
0917: continue;
0918: }
0919: ((StandardPluginClassLoader) getPluginManager()
0920: .getPluginClassLoader(element)).findResources(
0921: result, name, requestor, seen);
0922: }
0923: }
0924:
0925: protected boolean isResourceVisible(final String name,
0926: final URL url, final StandardPluginClassLoader requestor) {
0927: if (this == requestor) {
0928: return true;
0929: }
0930: URL lib;
0931: try {
0932: String file = url.getFile();
0933: lib = new URL(url.getProtocol(), url.getHost(), file
0934: .substring(0, file.length() - name.length()));
0935: } catch (MalformedURLException mue) {
0936: log.error("can't get resource library URL", mue); //$NON-NLS-1$
0937: return false;
0938: }
0939: ResourceFilter filter = resourceFilters.get(lib
0940: .toExternalForm());
0941: if (filter == null) {
0942: log.warn("no resource filter found for library " //$NON-NLS-1$
0943: + lib + ", name=" + name //$NON-NLS-1$
0944: + ", URL=" + url + ", this=" + this //$NON-NLS-1$ //$NON-NLS-2$
0945: + ", requestor=" + requestor); //$NON-NLS-1$
0946: return false;
0947: }
0948: if (!filter.isResourceVisible(name)) {
0949: log.warn("resource not visible, name=" + name //$NON-NLS-1$
0950: + ", URL=" + url + ", this=" + this //$NON-NLS-1$ //$NON-NLS-2$
0951: + ", requestor=" + requestor); //$NON-NLS-1$
0952: return false;
0953: }
0954: return true;
0955: }
0956:
0957: protected static final class ResourceFilter {
0958: private boolean isPublic;
0959:
0960: private final Set<String> entries;
0961:
0962: protected ResourceFilter(final Library lib) {
0963: entries = new HashSet<String>();
0964: for (String exportPrefix : lib.getExports()) {
0965: if ("*".equals(exportPrefix)) { //$NON-NLS-1$
0966: isPublic = true;
0967: entries.clear();
0968: break;
0969: }
0970: if (!lib.isCodeLibrary()) {
0971: exportPrefix = exportPrefix.replace('\\', '.')
0972: .replace('/', '.');
0973: if (exportPrefix.startsWith(".")) { //$NON-NLS-1$
0974: exportPrefix = exportPrefix.substring(1);
0975: }
0976: }
0977: entries.add(exportPrefix);
0978: }
0979: }
0980:
0981: protected boolean isClassVisible(final String className) {
0982: if (isPublic) {
0983: return true;
0984: }
0985: if (entries.isEmpty()) {
0986: return false;
0987: }
0988: if (entries.contains(className)) {
0989: return true;
0990: }
0991: int p = className.lastIndexOf('.');
0992: if (p == -1) {
0993: return false;
0994: }
0995: return entries.contains(className.substring(0, p) + ".*"); //$NON-NLS-1$
0996: }
0997:
0998: protected boolean isResourceVisible(final String resPath) {
0999: // quick check
1000: if (isPublic) {
1001: return true;
1002: }
1003: if (entries.isEmpty()) {
1004: return false;
1005: }
1006: // translate "path spec" -> "full class name"
1007: String str = resPath.replace('\\', '.').replace('/', '.');
1008: if (str.startsWith(".")) { //$NON-NLS-1$
1009: str = str.substring(1);
1010: }
1011: if (str.endsWith(".")) { //$NON-NLS-1$
1012: str = str.substring(0, str.length() - 1);
1013: }
1014: return isClassVisible(str);
1015: }
1016: }
1017:
1018: static class PluginResourceLoader extends URLClassLoader {
1019: private static Log logger = LogFactory
1020: .getLog(PluginResourceLoader.class);
1021:
1022: static PluginResourceLoader get(final PluginManager manager,
1023: final PluginDescriptor descr) {
1024: final List<URL> urls = new LinkedList<URL>();
1025: for (Library lib : descr.getLibraries()) {
1026: if (lib.isCodeLibrary())
1027: continue;
1028:
1029: urls.add(manager.getPathResolver().resolvePath(lib,
1030: lib.getPath()));
1031: }
1032: if (logger.isDebugEnabled()) {
1033: StringBuilder buf = new StringBuilder();
1034: buf
1035: .append("Resource URL's populated for plug-in " + descr //$NON-NLS-1$
1036: + ":\r\n"); //$NON-NLS-1$
1037: for (URL url : urls) {
1038: buf.append("\t"); //$NON-NLS-1$
1039: buf.append(url);
1040: buf.append("\r\n"); //$NON-NLS-1$
1041: }
1042: logger.trace(buf.toString());
1043: }
1044: if (urls.isEmpty()) {
1045: return null;
1046: }
1047: return AccessController
1048: .<PluginResourceLoader> doPrivileged(new PrivilegedAction<PluginResourceLoader>() {
1049: public PluginResourceLoader run() {
1050: return new PluginResourceLoader(urls
1051: .toArray(new URL[urls.size()]));
1052: }
1053: });
1054: }
1055:
1056: /**
1057: * Creates loader instance configured to load resources only from given
1058: * URLs.
1059: *
1060: * @param urls
1061: * array of resource URLs
1062: */
1063: PluginResourceLoader(final URL[] urls) {
1064: super (urls);
1065: }
1066:
1067: /**
1068: * @see java.lang.ClassLoader#findClass(java.lang.String)
1069: */
1070: @Override
1071: protected Class<?> findClass(final String name)
1072: throws ClassNotFoundException {
1073: throw new ClassNotFoundException(name);
1074: }
1075:
1076: /**
1077: * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
1078: */
1079: @Override
1080: protected Class<?> loadClass(final String name,
1081: final boolean resolve) throws ClassNotFoundException {
1082: throw new ClassNotFoundException(name);
1083: }
1084: }
1085: }
|