0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.apache.tools.ant.module.bridge;
0043:
0044: import java.beans.PropertyChangeEvent;
0045: import java.beans.PropertyChangeListener;
0046: import java.io.File;
0047: import java.io.FilenameFilter;
0048: import java.io.IOException;
0049: import java.io.InputStream;
0050: import java.io.PrintStream;
0051: import java.lang.ref.Reference;
0052: import java.lang.ref.SoftReference;
0053: import java.lang.reflect.Method;
0054: import java.net.URL;
0055: import java.net.URLClassLoader;
0056: import java.security.AllPermission;
0057: import java.security.CodeSource;
0058: import java.security.PermissionCollection;
0059: import java.security.Permissions;
0060: import java.util.ArrayList;
0061: import java.util.Arrays;
0062: import java.util.Collection;
0063: import java.util.Collections;
0064: import java.util.Enumeration;
0065: import java.util.HashMap;
0066: import java.util.Iterator;
0067: import java.util.LinkedHashSet;
0068: import java.util.List;
0069: import java.util.Locale;
0070: import java.util.Map;
0071: import javax.swing.event.ChangeListener;
0072: import org.apache.tools.ant.module.AntSettings;
0073: import org.openide.ErrorManager;
0074: import org.openide.modules.InstalledFileLocator;
0075: import org.openide.modules.ModuleInfo;
0076: import org.openide.util.ChangeSupport;
0077: import org.openide.util.Lookup;
0078: import org.openide.util.LookupEvent;
0079: import org.openide.util.LookupListener;
0080: import org.openide.util.io.NullOutputStream;
0081: import org.openide.xml.XMLUtil;
0082: import org.w3c.dom.Document;
0083: import org.w3c.dom.Element;
0084: import org.w3c.dom.Node;
0085: import org.w3c.dom.NodeList;
0086: import org.xml.sax.InputSource;
0087: import org.xml.sax.SAXException;
0088:
0089: /**
0090: * Utility class providing entry points to the bridging functionality.
0091: * @author Jesse Glick
0092: */
0093: public final class AntBridge {
0094:
0095: private static final ErrorManager err = ErrorManager.getDefault()
0096: .getInstance(AntBridge.class.getName());
0097:
0098: private AntBridge() {
0099: }
0100:
0101: private static final class AntInstance {
0102: public final String mainClassPath;
0103: public final ClassLoader mainClassLoader;
0104: public final ClassLoader bridgeClassLoader;
0105: public final BridgeInterface bridge;
0106: public final Map<String, Map<String, Class>> customDefs;
0107: public final Map<String, ClassLoader> customDefClassLoaders;
0108:
0109: public AntInstance(String mainClassPath,
0110: ClassLoader mainClassLoader,
0111: ClassLoader bridgeClassLoader, BridgeInterface bridge,
0112: Map<String, Map<String, Class>> customDefs,
0113: Map<String, ClassLoader> customDefClassLoaders) {
0114: this .mainClassPath = mainClassPath;
0115: this .mainClassLoader = mainClassLoader;
0116: this .bridgeClassLoader = bridgeClassLoader;
0117: this .bridge = bridge;
0118: this .customDefs = customDefs;
0119: this .customDefClassLoaders = customDefClassLoaders;
0120: }
0121: }
0122:
0123: private static Reference<AntInstance> antInstance = null;
0124:
0125: private static final ChangeSupport cs = new ChangeSupport(
0126: AntBridge.class);
0127:
0128: private static final class MiscListener implements
0129: PropertyChangeListener, LookupListener {
0130: MiscListener() {
0131: }
0132:
0133: private ModuleInfo[] modules = null;
0134:
0135: public void propertyChange(PropertyChangeEvent ev) {
0136: String prop = ev.getPropertyName();
0137: if (AntSettings.PROP_ANT_HOME.equals(prop)
0138: || AntSettings.PROP_EXTRA_CLASSPATH.equals(prop)
0139: || AntSettings.PROP_AUTOMATIC_EXTRA_CLASSPATH
0140: .equals(prop)) {
0141: err.log("AntBridge got settings change in " + prop);
0142: fireChange();
0143: } else if (ModuleInfo.PROP_ENABLED.equals(prop)) {
0144: err.log("AntBridge got module enablement change on "
0145: + ev.getSource());
0146: fireChange();
0147: }
0148: }
0149:
0150: public void resultChanged(LookupEvent ev) {
0151: err.log("AntModule got ModuleInfo change");
0152: synchronized (this ) {
0153: if (modules != null) {
0154: for (ModuleInfo module : modules) {
0155: module.removePropertyChangeListener(this );
0156: }
0157: modules = null;
0158: }
0159: }
0160: fireChange();
0161: }
0162:
0163: public synchronized ModuleInfo[] getEnabledModules() {
0164: if (modules == null) {
0165: Collection<? extends ModuleInfo> c = modulesResult
0166: .allInstances();
0167: modules = c.toArray(new ModuleInfo[c.size()]);
0168: for (ModuleInfo module : modules) {
0169: module.addPropertyChangeListener(this );
0170: }
0171: }
0172: List<ModuleInfo> enabledModules = new ArrayList<ModuleInfo>(
0173: modules.length);
0174: for (ModuleInfo module : modules) {
0175: if (module.isEnabled()) {
0176: enabledModules.add(module);
0177: }
0178: }
0179: return enabledModules.toArray(new ModuleInfo[enabledModules
0180: .size()]);
0181: }
0182: }
0183:
0184: private static MiscListener miscListener = new MiscListener();
0185: private static Lookup.Result<ModuleInfo> modulesResult = Lookup
0186: .getDefault().lookupResult(ModuleInfo.class);
0187: static {
0188: AntSettings.addPropertyChangeListener(miscListener);
0189: modulesResult.addLookupListener(miscListener);
0190: }
0191:
0192: /**
0193: * Listen for changes in the contents of the bridge, as e.g. after changing the
0194: * location of the installed copy of Ant.
0195: */
0196: public static synchronized void addChangeListener(ChangeListener l) {
0197: cs.addChangeListener(l);
0198: }
0199:
0200: /**
0201: * Stop listening for changes in the contents of the bridge.
0202: */
0203: public static synchronized void removeChangeListener(
0204: ChangeListener l) {
0205: cs.removeChangeListener(l);
0206: }
0207:
0208: private static void fireChange() {
0209: antInstance = null;
0210: cs.fireChange();
0211: }
0212:
0213: /**
0214: * Get the loader responsible for loading Ant together with any
0215: * user-defined classpath.
0216: */
0217: public static ClassLoader getMainClassLoader() {
0218: return getAntInstance().mainClassLoader;
0219: }
0220:
0221: /**
0222: * Get any custom task/type definitions stored in $nbhome/ant/nblib/*.jar.
0223: * Some of the classes might not be fully resolvable, so beware.
0224: * The names will include namespace prefixes.
0225: * <p>
0226: * Only minimal antlib syntax is currently interpreted here:
0227: * only <code><taskdef></code> and <code><typedef></code>,
0228: * and only the <code>name</code> and <code>classname</code> attributes.
0229: */
0230: public static Map<String, Map<String, Class>> getCustomDefsWithNamespace() {
0231: return getAntInstance().customDefs;
0232: }
0233:
0234: /**
0235: * Same as {@link #getCustomDefsWithNamespace} but without any namespace prefixes.
0236: */
0237: public static Map<String, Map<String, Class>> getCustomDefsNoNamespace() {
0238: Map<String, Map<String, Class>> m = new HashMap<String, Map<String, Class>>();
0239: for (Map.Entry<String, Map<String, Class>> entry : getCustomDefsWithNamespace()
0240: .entrySet()) {
0241: String type = entry.getKey();
0242: Map<String, Class> defs = entry.getValue();
0243: Map<String, Class> m2 = new HashMap<String, Class>();
0244: for (Map.Entry<String, Class> entry2 : defs.entrySet()) {
0245: String fqn = entry2.getKey();
0246: Class clazz = entry2.getValue();
0247: String name;
0248: int idx = fqn.lastIndexOf(':');
0249: if (idx != -1) {
0250: name = fqn.substring(idx + 1);
0251: } else {
0252: name = fqn;
0253: }
0254: m2.put(name, clazz);
0255: }
0256: m.put(type, m2);
0257: }
0258: return m;
0259: }
0260:
0261: /**
0262: * Get a map from enabled module code name bases to class loaders containing
0263: * JARs from ant/nblib/*.jar.
0264: */
0265: // XXX does not have to be here, used only by BridgeImpl
0266: public static Map<String, ClassLoader> getCustomDefClassLoaders()
0267: throws IOException {
0268: return getAntInstance().customDefClassLoaders;
0269: }
0270:
0271: /**
0272: * Get the bridge interface.
0273: */
0274: public static BridgeInterface getInterface() {
0275: return getAntInstance().bridge;
0276: }
0277:
0278: private synchronized static AntInstance getAntInstance() {
0279: AntInstance ai;
0280: if (antInstance != null) {
0281: ai = antInstance.get();
0282: } else {
0283: ai = null;
0284: }
0285: if (ai == null) {
0286: ai = createAntInstance();
0287: // XXX would be more accurate to stuff this struct into by BridgeImpl
0288: // so that it all lives or dies iff that class loader is still alive
0289: // (current impl is just workaround for JDK #6389107)
0290: antInstance = new SoftReference<AntInstance>(ai);
0291: }
0292: return ai;
0293: }
0294:
0295: private static AntInstance createAntInstance() {
0296: err
0297: .log("AntBridge.createAntInstance - loading Ant installation...");
0298: try {
0299: List<File> mainClassPath = createMainClassPath();
0300: err.log("mainClassPath=" + mainClassPath);
0301: ClassLoader main = createMainClassLoader(mainClassPath);
0302: ClassLoader bridgeLoader = createBridgeClassLoader(main);
0303: // Ensures that the loader is functional, and that it is at least 1.5.x
0304: // so that our classes can link against it successfully, and that
0305: // we are really loading Ant from the right place:
0306: Class ihClazz = Class.forName(
0307: "org.apache.tools.ant.input.InputHandler", false,
0308: bridgeLoader); // NOI18N
0309: Class<? extends BridgeInterface> impl = bridgeLoader
0310: .loadClass(
0311: "org.apache.tools.ant.module.bridge.impl.BridgeImpl")
0312: .asSubclass(BridgeInterface.class); // NOI18N
0313: if (AntSettings.getAntHome() != null) {
0314: ClassLoader loaderUsedForAnt = ihClazz.getClassLoader();
0315: if (loaderUsedForAnt != main) {
0316: throw new IllegalStateException(
0317: "Wrong class loader is finding Ant: "
0318: + loaderUsedForAnt); // NOI18N
0319: }
0320: Class ihClazz2 = Class.forName(
0321: "org.apache.tools.ant.input.InputHandler",
0322: false, main); // NOI18N
0323: if (ihClazz2 != ihClazz) {
0324: throw new IllegalStateException(
0325: "Main and bridge class loaders do not agree on version of Ant: "
0326: + ihClazz2.getClassLoader()); // NOI18N
0327: }
0328: try {
0329: Class alClazz = Class.forName(
0330: "org.apache.tools.ant.taskdefs.Antlib",
0331: false, bridgeLoader); // NOI18N
0332: if (alClazz.getClassLoader() != main) {
0333: throw new IllegalStateException(
0334: "Bridge loader is loading stuff from elsewhere: "
0335: + alClazz.getClassLoader()); // NOI18N
0336: }
0337: Class alClazz2 = Class.forName(
0338: "org.apache.tools.ant.taskdefs.Antlib",
0339: false, main); // NOI18N
0340: if (alClazz2 != alClazz) {
0341: throw new IllegalStateException(
0342: "Main and bridge class loaders do not agree on version of Ant: "
0343: + alClazz2.getClassLoader()); // NOI18N
0344: }
0345: } catch (ClassNotFoundException cnfe) {
0346: // Fine, it was added in Ant 1.6.
0347: }
0348: if (impl.getClassLoader() != bridgeLoader) {
0349: throw new IllegalStateException(
0350: "Wrong class loader is finding bridge impl: "
0351: + impl.getClassLoader()); // NOI18N
0352: }
0353: } // in classpath mode, these checks do not apply
0354: Map<String, ClassLoader> cDCLs = createCustomDefClassLoaders(main);
0355: return new AntInstance(classPathToString(mainClassPath),
0356: main, bridgeLoader, impl.newInstance(),
0357: createCustomDefs(cDCLs), cDCLs);
0358: } catch (Exception e) {
0359: return fallback(e);
0360: } catch (LinkageError e) {
0361: return fallback(e);
0362: }
0363: }
0364:
0365: private static AntInstance fallback(Throwable e) {
0366: ClassLoader dummy = ClassLoader.getSystemClassLoader();
0367: Map<String, Map<String, Class>> defs = new HashMap<String, Map<String, Class>>();
0368: defs.put("task", new HashMap<String, Class>()); // NOI18N
0369: defs.put("type", new HashMap<String, Class>()); // NOI18N
0370: return new AntInstance("", dummy, dummy,
0371: new DummyBridgeImpl(e), defs, Collections
0372: .<String, ClassLoader> emptyMap());
0373: }
0374:
0375: private static final class JarFilter implements FilenameFilter {
0376: JarFilter() {
0377: }
0378:
0379: public boolean accept(File dir, String name) {
0380: return name.toLowerCase(Locale.US).endsWith(".jar"); // NOI18N
0381: }
0382: }
0383:
0384: private static String classPathToString(List<File> cp) {
0385: StringBuffer b = new StringBuffer();
0386: Iterator<File> it = cp.iterator();
0387: while (it.hasNext()) {
0388: b.append(it.next().getAbsolutePath());
0389: if (it.hasNext()) {
0390: b.append(File.pathSeparator);
0391: }
0392: }
0393: return b.toString();
0394: }
0395:
0396: private static String originalJavaClassPath = System
0397: .getProperty("java.class.path"); // NOI18N
0398:
0399: /**
0400: * Get the equivalent of java.class.path for the main Ant loader.
0401: * Includes everything in the main class loader,
0402: * plus the regular system class path (for tools.jar etc.).
0403: */
0404: public static String getMainClassPath() {
0405: return getAntInstance().mainClassPath + File.pathSeparatorChar
0406: + originalJavaClassPath;
0407: }
0408:
0409: private static List<File> createMainClassPath() throws Exception {
0410: // Use LinkedHashSet to automatically suppress duplicates.
0411: Collection<File> cp = new LinkedHashSet<File>();
0412: File antHome = AntSettings.getAntHome();
0413: if (antHome != null) {
0414: File libdir = new File(antHome, "lib"); // NOI18N
0415: if (!libdir.isDirectory()) {
0416: throw new IOException("No such Ant library dir: "
0417: + libdir); // NOI18N
0418: }
0419: err.log("Creating main class loader from " + libdir);
0420: // First look for ${ant.home}/patches/*.jar, to support e.g. patching #47708:
0421: File[] patches = new File(libdir.getParentFile(), "patches")
0422: .listFiles(new JarFilter()); // NOI18N
0423: if (patches != null) {
0424: cp.addAll(Arrays.asList(patches));
0425: }
0426: // Now continue with regular classpath.
0427: File[] libs = libdir.listFiles(new JarFilter());
0428: if (libs == null) {
0429: throw new IOException("Listing: " + libdir); // NOI18N
0430: }
0431: cp.addAll(Arrays.asList(libs));
0432: } else {
0433: // Classpath mode. Try to add in tools.jar if we can find it somewhere.
0434: File toolsJar = new File(new File(new File(System
0435: .getProperty("java.home")).getParentFile(), "lib"),
0436: "tools.jar");
0437: if (toolsJar.isFile()) {
0438: cp.add(toolsJar);
0439: }
0440: }
0441: // XXX consider adding ${user.home}/.ant/lib/*.jar (org.apache.tools.ant.launch.Launcher.USER_LIBDIR)
0442: cp.addAll(AntSettings.getExtraClasspath());
0443: cp.addAll(AntSettings.getAutomaticExtraClasspath());
0444: // XXX note that systemClassLoader will include boot.jar, and perhaps anything else
0445: // in lib/ext/*.jar, like rmi-ext.jar. Would be nicer to exclude everything NB-specific.
0446: // However the simplest way - to use the parent loader (JRE ext loader) - does not work
0447: // well because Ant assumes that tools.jar is in its classpath (for <javac> etc.).
0448: // Manually readding the JDK JARs would be possible, but then they would not be shared
0449: // with the versions used inside NB, which may cause inefficiencies or more memory usage.
0450: // On the other hand, if ant.jar is in ${java.class.path} (e.g. from a unit test), we
0451: // have to explicitly mask it out. What a mess...
0452: //org.openide.DialogDisplayer.getDefault().notify(new org.openide.NotifyDescriptor.Message("cp=" + cp));
0453: return new ArrayList<File>(cp);
0454: }
0455:
0456: private static ClassLoader createMainClassLoader(
0457: List<File> mainClassPath) throws Exception {
0458: URL[] cp = new URL[mainClassPath.size()];
0459: Iterator<File> it = mainClassPath.iterator();
0460: int i = 0;
0461: while (it.hasNext()) {
0462: cp[i++] = it.next().toURI().toURL();
0463: }
0464: if (AntSettings.getAntHome() != null) {
0465: ClassLoader parent = ClassLoader.getSystemClassLoader();
0466: if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
0467: List<URL> parentURLs;
0468: if (parent instanceof URLClassLoader) {
0469: parentURLs = Arrays
0470: .asList(((URLClassLoader) parent).getURLs());
0471: } else {
0472: parentURLs = null;
0473: }
0474: err.log("AntBridge.createMainClassLoader: cp="
0475: + Arrays.asList(cp) + " parent.urls="
0476: + parentURLs);
0477: }
0478: return new MaskedClassLoader(cp, parent);
0479: } else {
0480: // Run-in-classpath mode.
0481: ClassLoader existing = AntBridge.class.getClassLoader();
0482: if (existing instanceof URLClassLoader) {
0483: try {
0484: // Need to insert resources into it.
0485: // We could also try making a fresh loader which masks the parent
0486: // yet delegates findResource to it, so as to be initiating loader
0487: // for everything Ant. Might be safer.
0488: Method addURL = URLClassLoader.class
0489: .getDeclaredMethod("addURL", URL.class);
0490: addURL.setAccessible(true);
0491: for (URL u : cp) {
0492: addURL.invoke(existing, u);
0493: }
0494: return existing;
0495: } catch (Exception e) {
0496: // Problem. Don't do it, I guess.
0497: err.notify(ErrorManager.WARNING, e);
0498: }
0499: }
0500: // Probably won't work as desired, but just in case:
0501: return new AllPermissionURLClassLoader(cp, existing);
0502: }
0503: }
0504:
0505: private static ClassLoader createBridgeClassLoader(ClassLoader main)
0506: throws Exception {
0507: File bridgeJar = InstalledFileLocator.getDefault().locate(
0508: "ant/nblib/bridge.jar", "org.apache.tools.ant.module",
0509: false); // NOI18N
0510: if (bridgeJar == null) {
0511: // Run-in-classpath mode.
0512: return main;
0513: }
0514: return createAuxClassLoader(bridgeJar, main, AntBridge.class
0515: .getClassLoader());
0516: }
0517:
0518: private static ClassLoader createAuxClassLoader(File lib,
0519: ClassLoader main, ClassLoader moduleLoader)
0520: throws IOException {
0521: return new AuxClassLoader(moduleLoader, main, lib.toURI()
0522: .toURL());
0523: }
0524:
0525: /**
0526: * Get a map from enabled module code name bases to class loaders containing
0527: * JARs from ant/nblib/*.jar.
0528: */
0529: private static Map<String, ClassLoader> createCustomDefClassLoaders(
0530: ClassLoader main) throws IOException {
0531: Map<String, ClassLoader> m = new HashMap<String, ClassLoader>();
0532: ModuleInfo[] modules = miscListener.getEnabledModules();
0533: InstalledFileLocator ifl = InstalledFileLocator.getDefault();
0534: for (ModuleInfo module : modules) {
0535: String cnb = module.getCodeNameBase();
0536: String cnbDashes = cnb.replace('.', '-');
0537: File lib = ifl.locate("ant/nblib/" + cnbDashes + ".jar",
0538: cnb, false); // NOI18N
0539: if (lib == null) {
0540: if (main.getResource(cnb.replace('.', '/')
0541: + "/antlib.xml") != null) { // NOI18N
0542: // Run-in-classpath mode.
0543: m.put(cnb, main);
0544: }
0545: continue;
0546: }
0547: ClassLoader l = createAuxClassLoader(lib, main, module
0548: .getClassLoader());
0549: m.put(cnb, l);
0550: }
0551: return m;
0552: }
0553:
0554: private static Map<String, Map<String, Class>> createCustomDefs(
0555: Map<String, ClassLoader> cDCLs) throws IOException {
0556: Map<String, Map<String, Class>> m = new HashMap<String, Map<String, Class>>();
0557: Map<String, Class> tasks = new HashMap<String, Class>();
0558: Map<String, Class> types = new HashMap<String, Class>();
0559: // XXX #36776: should eventually support <macrodef>s here
0560: m.put("task", tasks); // NOI18N
0561: m.put("type", types); // NOI18N
0562: for (Map.Entry<String, ClassLoader> entry : cDCLs.entrySet()) {
0563: String cnb = entry.getKey();
0564: ClassLoader l = entry.getValue();
0565: String resource = cnb.replace('.', '/') + "/antlib.xml"; // NOI18N
0566: URL antlib = l.getResource(resource);
0567: if (antlib == null) {
0568: throw new IOException("Could not find " + resource
0569: + " in ant/nblib/" + cnb.replace('.', '-')
0570: + ".jar"); // NOI18N
0571: }
0572: Document doc;
0573: try {
0574: doc = XMLUtil.parse(new InputSource(antlib
0575: .toExternalForm()), false, true, /*XXX needed?*/
0576: null, null);
0577: } catch (SAXException e) {
0578: throw (IOException) new IOException(e.toString())
0579: .initCause(e);
0580: }
0581: Element docEl = doc.getDocumentElement();
0582: if (!docEl.getLocalName().equals("antlib")) { // NOI18N
0583: throw new IOException("Bad root element for " + antlib
0584: + ": " + docEl); // NOI18N
0585: }
0586: NodeList nl = docEl.getChildNodes();
0587: Map<String, String> newTaskDefs = new HashMap<String, String>();
0588: Map<String, String> newTypeDefs = new HashMap<String, String>();
0589: for (int i = 0; i < nl.getLength(); i++) {
0590: Node n = nl.item(i);
0591: if (n.getNodeType() != Node.ELEMENT_NODE) {
0592: continue;
0593: }
0594: Element def = (Element) n;
0595: boolean type;
0596: if (def.getNodeName().equals("taskdef")) { // NOI18N
0597: type = false;
0598: } else if (def.getNodeName().equals("typedef")) { // NOI18N
0599: type = true;
0600: } else {
0601: err.log(ErrorManager.WARNING,
0602: "Warning: unrecognized definition " + def
0603: + " in " + antlib);
0604: continue;
0605: }
0606: String name = def.getAttribute("name"); // NOI18N
0607: if (name == null) {
0608: // Not a hard error since there might be e.g. <taskdef resource="..."/> here
0609: // which we do not parse but which is permitted in antlib by Ant.
0610: err.log(ErrorManager.WARNING,
0611: "Warning: skipping definition " + def
0612: + " with no 'name' in " + antlib);
0613: continue;
0614: }
0615: String classname = def.getAttribute("classname"); // NOI18N
0616: if (classname == null) {
0617: // But this is a hard error.
0618: throw new IOException(
0619: "No 'classname' attr on def of " + name
0620: + " in " + antlib); // NOI18N
0621: }
0622: // XXX would be good to handle at least onerror attr too
0623: String nsname = "antlib:" + cnb + ":" + name; // NOI18N
0624: (type ? newTypeDefs : newTaskDefs).put(nsname,
0625: classname);
0626: }
0627: loadDefs(newTaskDefs, tasks, l);
0628: loadDefs(newTypeDefs, types, l);
0629: }
0630: return m;
0631: }
0632:
0633: private static void loadDefs(Map<String, String> p,
0634: Map<String, Class> defs, ClassLoader l) throws IOException {
0635: // Similar to IntrospectedInfo.load, after having parsed the properties.
0636: for (Map.Entry<String, String> entry : p.entrySet()) {
0637: String name = entry.getKey();
0638: String clazzname = entry.getValue();
0639: try {
0640: Class clazz = l.loadClass(clazzname);
0641: defs.put(name, clazz);
0642: } catch (ClassNotFoundException cnfe) {
0643: // This is not normal. If the class is mentioned, it should be there.
0644: throw (IOException) new IOException(
0645: "Could not load class " + clazzname + ": "
0646: + cnfe).initCause(cnfe); // NOI18N
0647: } catch (NoClassDefFoundError ncdfe) {
0648: // Normal for e.g. tasks dumped there by disabled modules.
0649: // Cf. #36702 for possible better solution.
0650: err.log("AntBridge.loadDefs: skipping " + clazzname
0651: + ": " + ncdfe);
0652: } catch (LinkageError e) {
0653: // Not normal; if it is there it ought to be resolvable etc.
0654: throw (IOException) new IOException(
0655: "Could not load class " + clazzname + ": " + e)
0656: .initCause(e); // NOI18N
0657: }
0658: }
0659: }
0660:
0661: static class AllPermissionURLClassLoader extends URLClassLoader {
0662:
0663: private static PermissionCollection allPermission;
0664:
0665: private static synchronized PermissionCollection getAllPermissions() {
0666: if (allPermission == null) {
0667: allPermission = new Permissions();
0668: allPermission.add(new AllPermission());
0669: }
0670: return allPermission;
0671: }
0672:
0673: public AllPermissionURLClassLoader(URL[] urls,
0674: ClassLoader parent) {
0675: super (urls, parent);
0676: }
0677:
0678: @Override
0679: protected final PermissionCollection getPermissions(
0680: CodeSource cs) {
0681: return getAllPermissions();
0682: }
0683:
0684: @Override
0685: public String toString() {
0686: return super .toString() + "[parent=" + getParent()
0687: + ",urls=" + Arrays.asList(getURLs()) + "]";
0688: }
0689:
0690: @Override
0691: public URL getResource(String name) {
0692: URL u = super .getResource(name);
0693: if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
0694: err.log("APURLCL.gR: " + name + " -> " + u + " ["
0695: + this + "]");
0696: }
0697: return u;
0698: }
0699:
0700: @Override
0701: public Enumeration<URL> findResources(String name)
0702: throws IOException {
0703: try {
0704: Enumeration<URL> us = super .findResources(name);
0705: if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
0706: // Make a copy so it can be logged:
0707: List<URL> resources = Collections.list(us);
0708: us = Collections.enumeration(resources);
0709: err.log("APURLCL.fRs: " + name + " -> " + resources
0710: + " [" + this + "]");
0711: }
0712: return us;
0713: } catch (IOException e) {
0714: if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
0715: err.notify(ErrorManager.INFORMATIONAL, e);
0716: }
0717: throw e;
0718: }
0719: }
0720:
0721: /*
0722: public Class loadClass(String name) throws ClassNotFoundException {
0723: try {
0724: Class c = super.loadClass(name);
0725: java.security.CodeSource s = c.getProtectionDomain().getCodeSource();
0726: System.err.println("ACL.lC: " + name + " from " + (s != null ? s.getLocation() : null) + " [" + this + "]");
0727: return c;
0728: } catch (ClassNotFoundException e) {
0729: System.err.println("ACL.lC: CNFE on " + name + " [" + this + "]");
0730: throw e;
0731: }
0732: }
0733: */
0734:
0735: }
0736:
0737: private static boolean masked(String clazz) {
0738: return clazz.startsWith("org.apache.tools.")
0739: || clazz.startsWith("org.netbeans.")
0740: || clazz.startsWith("org.openide.")
0741: || clazz.startsWith("org.openidex."); // NOI18N
0742: }
0743:
0744: /**
0745: * Special class loader that refuses to load Ant or NetBeans classes from its parent.
0746: * Necessary in order to be able to load the intended Ant distro from a unit test.
0747: */
0748: private static final class MaskedClassLoader extends
0749: AllPermissionURLClassLoader {
0750:
0751: public MaskedClassLoader(URL[] urls, ClassLoader parent) {
0752: super (urls, parent);
0753: }
0754:
0755: @Override
0756: protected synchronized Class<?> loadClass(String name,
0757: boolean resolve) throws ClassNotFoundException {
0758: if (masked(name)) {
0759: Class c = findLoadedClass(name);
0760: // Careful with that parent loader Eugene!
0761: if (c == null) {
0762: c = findClass(name);
0763: }
0764: if (resolve) {
0765: resolveClass(c);
0766: }
0767: return c;
0768: } else {
0769: return super .loadClass(name, resolve);
0770: }
0771: }
0772:
0773: }
0774:
0775: // I/O redirection impl. Keyed by thread group (each Ant process has its own TG).
0776: // Various Ant tasks (e.g. <java fork="false" output="..." ...>) need the system
0777: // I/O streams to be redirected to the demux streams of the project so they can
0778: // be handled properly. Ideally nothing would try to read directly from stdin
0779: // or print directly to stdout/stderr but in fact some tasks do.
0780: // Could also pass a custom InputOutput to ExecutionEngine, perhaps, but this
0781: // seems a lot simpler and probably has the same effect.
0782:
0783: private static int delegating = 0;
0784: private static InputStream origIn;
0785: private static PrintStream origOut, origErr;
0786: private static Map<ThreadGroup, InputStream> delegateIns = new HashMap<ThreadGroup, InputStream>();
0787: private static Map<ThreadGroup, PrintStream> delegateOuts = new HashMap<ThreadGroup, PrintStream>();
0788: private static Map<ThreadGroup, PrintStream> delegateErrs = new HashMap<ThreadGroup, PrintStream>();
0789: /** list, not set, so can be reentrant - treated as a multiset */
0790: private static List<Thread> suspendedDelegationTasks = new ArrayList<Thread>();
0791:
0792: /**
0793: * Handle I/O scoping for overlapping project runs.
0794: * You must call {@link #restoreSystemInOutErr} in a finally block.
0795: * @param in new temporary input stream for this thread group
0796: * @param out new temporary output stream for this thread group
0797: * @param err new temporary error stream for this thread group
0798: * @see "#36396"
0799: */
0800: public static synchronized void pushSystemInOutErr(InputStream in,
0801: PrintStream out, PrintStream err) {
0802: if (delegating++ == 0) {
0803: origIn = System.in;
0804: origOut = System.out;
0805: origErr = System.err;
0806: System.setIn(new MultiplexInputStream());
0807: System.setOut(new MultiplexPrintStream(false));
0808: System.setErr(new MultiplexPrintStream(true));
0809: }
0810: ThreadGroup tg = Thread.currentThread().getThreadGroup();
0811: delegateIns.put(tg, in);
0812: delegateOuts.put(tg, out);
0813: delegateErrs.put(tg, err);
0814: }
0815:
0816: /**
0817: * Restore original I/O streams after a call to {@link #pushSystemInOutErr}.
0818: */
0819: public static synchronized void restoreSystemInOutErr() {
0820: assert delegating > 0;
0821: if (--delegating == 0) {
0822: System.setIn(origIn);
0823: System.setOut(origOut);
0824: System.setErr(origErr);
0825: origIn = null;
0826: origOut = null;
0827: origErr = null;
0828: }
0829: ThreadGroup tg = Thread.currentThread().getThreadGroup();
0830: delegateIns.remove(tg);
0831: delegateOuts.remove(tg);
0832: delegateErrs.remove(tg);
0833: }
0834:
0835: /**
0836: * Temporarily suspend delegation of system I/O streams for the current thread.
0837: * Useful when running callbacks to IDE code that might try to print to stderr etc.
0838: * Must be matched in a finally block by {@link #resumeDelegation}.
0839: * Safe to call when not actually delegating; in that case does nothing.
0840: * Safe to call in reentrant but not overlapping fashion.
0841: */
0842: public static synchronized void suspendDelegation() {
0843: Thread t = Thread.currentThread();
0844: //assert delegateOuts.containsKey(t.getThreadGroup()) : "Not currently delegating in " + t;
0845: // #58394: do *not* check that it does not yet contain t. It is OK if it does; need to
0846: // be able to call suspendDelegation reentrantly.
0847: suspendedDelegationTasks.add(t);
0848: }
0849:
0850: /**
0851: * Resume delegation of system I/O streams for the current thread group
0852: * after a call to {@link #suspendDelegation}.
0853: */
0854: public static synchronized void resumeDelegation() {
0855: Thread t = Thread.currentThread();
0856: //assert delegateOuts.containsKey(t.getThreadGroup()) : "Not currently delegating in " + t;
0857: // This is still valid: suspendedDelegationTasks must have *at least one* copy of t.
0858: assert suspendedDelegationTasks.contains(t) : "Have not suspended delegation in "
0859: + t;
0860: suspendedDelegationTasks.remove(t);
0861: }
0862:
0863: public static synchronized InputStream delegateInputStream() {
0864: Thread t = Thread.currentThread();
0865: ThreadGroup tg = t.getThreadGroup();
0866: while (tg != null && !delegateIns.containsKey(tg)) {
0867: tg = tg.getParent();
0868: }
0869: InputStream is = delegateIns.get(tg);
0870: return is != null ? is : origIn;
0871: }
0872:
0873: public static synchronized PrintStream delegateOutputStream(
0874: boolean err) {
0875: Thread t = Thread.currentThread();
0876: ThreadGroup tg = t.getThreadGroup();
0877: while (tg != null && !delegateIns.containsKey(tg)) {
0878: tg = tg.getParent();
0879: }
0880: PrintStream ps = (err ? delegateErrs : delegateOuts).get(tg);
0881: return ps != null ? ps : (err ? origErr : origOut);
0882: }
0883:
0884: private static final class MultiplexInputStream extends InputStream {
0885:
0886: public MultiplexInputStream() {
0887: }
0888:
0889: private InputStream delegate() {
0890: Thread t = Thread.currentThread();
0891: ThreadGroup tg = t.getThreadGroup();
0892: while (tg != null && !delegateIns.containsKey(tg)) {
0893: tg = tg.getParent();
0894: }
0895: InputStream is = delegateIns.get(tg);
0896: if (is != null && !suspendedDelegationTasks.contains(t)) {
0897: return is;
0898: } else if (delegating > 0) {
0899: assert origIn != null;
0900: return origIn;
0901: } else {
0902: // Probably should not happen? But not sure.
0903: return System.in;
0904: }
0905: }
0906:
0907: @Override
0908: public int read() throws IOException {
0909: return delegate().read();
0910: }
0911:
0912: @Override
0913: public int read(byte[] b) throws IOException {
0914: return delegate().read(b);
0915: }
0916:
0917: @Override
0918: public int read(byte[] b, int off, int len) throws IOException {
0919: return delegate().read(b, off, len);
0920: }
0921:
0922: @Override
0923: public int available() throws IOException {
0924: return delegate().available();
0925: }
0926:
0927: @Override
0928: public boolean markSupported() {
0929: return delegate().markSupported();
0930: }
0931:
0932: @Override
0933: public void mark(int readlimit) {
0934: delegate().mark(readlimit);
0935: }
0936:
0937: @Override
0938: public void close() throws IOException {
0939: delegate().close();
0940: }
0941:
0942: @Override
0943: public long skip(long n) throws IOException {
0944: return delegate().skip(n);
0945: }
0946:
0947: @Override
0948: public void reset() throws IOException {
0949: delegate().reset();
0950: }
0951:
0952: }
0953:
0954: private static final class MultiplexPrintStream extends PrintStream {
0955:
0956: private final boolean err;
0957:
0958: public MultiplexPrintStream(boolean err) {
0959: this (new NullOutputStream(), err);
0960: }
0961:
0962: private MultiplexPrintStream(NullOutputStream nos, boolean err) {
0963: super (nos);
0964: nos.throwException = true;
0965: this .err = err;
0966: }
0967:
0968: private PrintStream delegate() {
0969: Thread t = Thread.currentThread();
0970: ThreadGroup tg = t.getThreadGroup();
0971: Map<ThreadGroup, PrintStream> delegates = err ? delegateErrs
0972: : delegateOuts;
0973: while (tg != null && !delegates.containsKey(tg)) {
0974: tg = tg.getParent();
0975: }
0976: PrintStream ps = delegates.get(tg);
0977: if (ps != null && !suspendedDelegationTasks.contains(t)) {
0978: assert !(ps instanceof MultiplexPrintStream);
0979: return ps;
0980: } else if (delegating > 0) {
0981: PrintStream orig = err ? origErr : origOut;
0982: assert orig != null;
0983: assert !(orig instanceof MultiplexPrintStream);
0984: return orig;
0985: } else {
0986: // Probably should not happen? But not sure.
0987: PrintStream stream = err ? System.err : System.out;
0988: assert !(stream instanceof MultiplexPrintStream); // #89020
0989: return stream;
0990: }
0991: }
0992:
0993: @Override
0994: public boolean checkError() {
0995: return delegate().checkError();
0996: }
0997:
0998: @Override
0999: public void close() {
1000: delegate().close();
1001: }
1002:
1003: @Override
1004: public void flush() {
1005: delegate().flush();
1006: }
1007:
1008: @Override
1009: public void print(long l) {
1010: delegate().print(l);
1011: }
1012:
1013: @Override
1014: public void print(char[] s) {
1015: delegate().print(s);
1016: }
1017:
1018: @Override
1019: public void print(int i) {
1020: delegate().print(i);
1021: }
1022:
1023: @Override
1024: public void print(boolean b) {
1025: delegate().print(b);
1026: }
1027:
1028: @Override
1029: public void print(char c) {
1030: delegate().print(c);
1031: }
1032:
1033: @Override
1034: public void print(float f) {
1035: delegate().print(f);
1036: }
1037:
1038: @Override
1039: public void print(double d) {
1040: delegate().print(d);
1041: }
1042:
1043: @Override
1044: public void print(Object obj) {
1045: delegate().print(obj);
1046: }
1047:
1048: @Override
1049: public void print(String s) {
1050: delegate().print(s);
1051: }
1052:
1053: @Override
1054: public void println(double x) {
1055: delegate().println(x);
1056: }
1057:
1058: @Override
1059: public void println(Object x) {
1060: delegate().println(x);
1061: }
1062:
1063: @Override
1064: public void println(float x) {
1065: delegate().println(x);
1066: }
1067:
1068: @Override
1069: public void println(int x) {
1070: delegate().println(x);
1071: }
1072:
1073: @Override
1074: public void println(char x) {
1075: delegate().println(x);
1076: }
1077:
1078: @Override
1079: public void println(boolean x) {
1080: delegate().println(x);
1081: }
1082:
1083: @Override
1084: public void println(String x) {
1085: delegate().println(x);
1086: }
1087:
1088: @Override
1089: public void println(char[] x) {
1090: delegate().println(x);
1091: }
1092:
1093: @Override
1094: public void println() {
1095: delegate().println();
1096: }
1097:
1098: @Override
1099: public void println(long x) {
1100: delegate().println(x);
1101: }
1102:
1103: @Override
1104: public void write(int b) {
1105: delegate().write(b);
1106: }
1107:
1108: @Override
1109: public void write(byte[] b) throws IOException {
1110: delegate().write(b);
1111: }
1112:
1113: @Override
1114: public void write(byte[] b, int off, int len) {
1115: delegate().write(b, off, len);
1116: }
1117:
1118: // XXX printf/format with varargs cannot be overridden here (JDK 1.5 specific)
1119: // nor can append(char,CharSequence)
1120: // probably does not matter however...
1121:
1122: }
1123:
1124: // Faking the system property java.class.path for the benefit of a few tasks
1125: // that expect it to be equal to the Ant class loader path.
1126:
1127: private static int fakingJavaClassPath = 0;
1128:
1129: /**
1130: * Fake the system property java.class.path temporarily.
1131: * Must be followed by {@link unfakeJavaClassPath} in a finally block.
1132: * Reentrant.
1133: */
1134: public static synchronized void fakeJavaClassPath() {
1135: if (fakingJavaClassPath++ == 0) {
1136: String cp = getMainClassPath();
1137: err.log("Faking java.class.path=" + cp);
1138: System.setProperty("java.class.path", cp); // NOI18N
1139: }
1140: }
1141:
1142: /**
1143: * Reverse the effect of {@link fakeJavaClassPath}.
1144: */
1145: public static synchronized void unfakeJavaClassPath() {
1146: if (--fakingJavaClassPath == 0) {
1147: err.log("Restoring java.class.path="
1148: + originalJavaClassPath);
1149: System
1150: .setProperty("java.class.path",
1151: originalJavaClassPath); // NOI18N
1152: }
1153: }
1154:
1155: }
|