Using "jar" protocol instead of "nbjcl" protocol, delegating to the JRE's regular handler where necessary. Corresponds to option #1 of issue #130618. diff --git a/core.startup/src/META-INF/services/java.net.URLStreamHandlerFactory b/core.startup/src/META-INF/services/java.net.URLStreamHandlerFactory --- a/core.startup/src/META-INF/services/java.net.URLStreamHandlerFactory +++ b/core.startup/src/META-INF/services/java.net.URLStreamHandlerFactory @@ -1,2 +1,2 @@ +org.netbeans.core.startup.NbURLStreamHandlerFactory org.netbeans.core.startup.layers.NbinstURLStreamHandlerFactory - diff --git a/core.startup/src/org/netbeans/core/startup/Main.java b/core.startup/src/org/netbeans/core/startup/Main.java --- a/core.startup/src/org/netbeans/core/startup/Main.java +++ b/core.startup/src/org/netbeans/core/startup/Main.java @@ -46,10 +46,9 @@ import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; -import java.util.Iterator; import javax.swing.SwingUtilities; import javax.swing.UIManager; -import org.netbeans.JarClassLoader; +import org.netbeans.ProxyURLStreamHandlerFactory; import org.netbeans.Stamps; import org.netbeans.Util; import org.openide.filesystems.FileObject; @@ -61,7 +60,6 @@ import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; -import org.openide.util.RequestProcessor; import org.openide.util.Utilities; /** @@ -93,18 +91,9 @@ } - private static boolean nbFactoryInitialized; /** Initializes default stream factory */ public static void initializeURLFactory () { - if (!nbFactoryInitialized) { - NbURLStreamHandlerFactory fact = new NbURLStreamHandlerFactory(); - try { - java.net.URL.setURLStreamHandlerFactory(fact); - } catch (Error e) { - fact.registerUsingReflection(e); - } - nbFactoryInitialized = true; - } + ProxyURLStreamHandlerFactory.register(); } /** diff --git a/core.startup/src/org/netbeans/core/startup/NbURLStreamHandlerFactory.java b/core.startup/src/org/netbeans/core/startup/NbURLStreamHandlerFactory.java --- a/core.startup/src/org/netbeans/core/startup/NbURLStreamHandlerFactory.java +++ b/core.startup/src/org/netbeans/core/startup/NbURLStreamHandlerFactory.java @@ -44,19 +44,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.reflect.Field; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; -import java.util.Collection; import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; import org.openide.filesystems.FileUtil; import org.openide.util.Lookup; -import org.openide.util.LookupEvent; -import org.openide.util.LookupListener; import org.openide.util.NbBundle; /** @@ -65,26 +59,12 @@ * in which case registering them via Lookup would be deprecated. * @author Jesse Glick */ -final class NbURLStreamHandlerFactory implements URLStreamHandlerFactory, LookupListener { - private static final Logger LOG = Logger.getLogger(NbURLStreamHandlerFactory.class.getName()); +public final class NbURLStreamHandlerFactory implements URLStreamHandlerFactory { - private final Lookup.Result r; - private URLStreamHandlerFactory[] handlers; - private URLStreamHandlerFactory delegate; - - public NbURLStreamHandlerFactory() { - r = Lookup.getDefault().lookupResult(URLStreamHandlerFactory.class); - r.addLookupListener(this); - resultChanged(null); - } + /** public for lookup */ + public NbURLStreamHandlerFactory() {} public URLStreamHandler createURLStreamHandler(String protocol) { - if (protocol.equals("jar") || protocol.equals("file") || // NOI18N - protocol.equals("http") || protocol.equals("https") || protocol.equals("resource")) { // NOI18N - // Well-known handlers in JRE. Do not try to initialize lookup, etc. - return null; - } - if (protocol.equals("nbfs")) { // NOI18N return FileUtil.nbfsURLStreamHandler(); } @@ -94,62 +74,7 @@ return new NbResourceStreamHandler(); } - URLStreamHandlerFactory d = delegate; - if (d != null) { - URLStreamHandler h = d.createURLStreamHandler(protocol); - if (h != null) { - return h; - } - } - - URLStreamHandlerFactory[] _handlers; - synchronized (this) { - _handlers = handlers; - } - if (_handlers == null) { - // Too early during startup (#75422) - return null; - } - for (int i = 0; i < _handlers.length; i++) { - URLStreamHandler h = _handlers[i].createURLStreamHandler(protocol); - if (h != null) { - return h; - } - } return null; - } - - public void resultChanged(LookupEvent ev) { - Collection c = r.allInstances(); - synchronized (this) { - handlers = c.toArray(new URLStreamHandlerFactory[0]); - } - } - - void registerUsingReflection(Error e) { - LOG.log(Level.CONFIG, "Problems registering URLStreamHandlerFactory, trying reflection", e); // NOI18N - try { - URLStreamHandlerFactory prev = null; - for (Field f : URL.class.getDeclaredFields()) { - LOG.log(Level.FINEST, "Found field {0}", f); - if (f.getType() == URLStreamHandlerFactory.class) { - LOG.log(Level.FINEST, "Clearing field {0}"); - f.setAccessible(true); - prev = (URLStreamHandlerFactory)f.get(null); - LOG.log(Level.CONFIG, "Previous value was {0}", prev); - f.set(null, null); - LOG.config("Field is supposed to be empty"); - break; - } - } - URL.setURLStreamHandlerFactory(this); - delegate = prev; - } catch (Throwable t) { - LOG.log(Level.SEVERE, - "No way to register URLStreamHandlerFactory; NetBeans is unlikely to work", - t - ); // NOI18N - } } /** Stream handler for internal resource-based URLs. diff --git a/o.n.bootstrap/src/org/netbeans/JarClassLoader.java b/o.n.bootstrap/src/org/netbeans/JarClassLoader.java --- a/o.n.bootstrap/src/org/netbeans/JarClassLoader.java +++ b/o.n.bootstrap/src/org/netbeans/JarClassLoader.java @@ -49,12 +49,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLConnection; import java.net.URLStreamHandler; -import java.net.URLStreamHandlerFactory; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.Policy; @@ -111,24 +110,7 @@ } static { - ResURLStreamHandlerFactory fact = new ResURLStreamHandlerFactory(); - try { - java.net.URL.setURLStreamHandlerFactory(fact); - } catch (Error e) { - try { - for (Field f : URL.class.getDeclaredFields()) { - if (f.getType() == URLStreamHandlerFactory.class) { - f.setAccessible(true); - fact.del = (URLStreamHandlerFactory)f.get(null); - f.set(null, null); - break; - } - } - URL.setURLStreamHandlerFactory(fact); - } catch (Throwable t) { - throw new InternalError(); // can't really continue - } - } + ProxyURLStreamHandlerFactory.register(); } private static final Logger LOGGER = Logger.getLogger(JarClassLoader.class.getName()); @@ -343,6 +325,12 @@ sources.put(f.toURI().toString(), src); return src; } + + @Override + public String toString() { + return url.toString(); + } + } static class JarSource extends Source { @@ -356,7 +344,7 @@ JarSource(File file) throws IOException { super(file.toURL()); - resPrefix = ResURLStreamHandler.RES_PROTO +":" + file.toURI() + "!/"; // NOI18N; + resPrefix = "jar:" + file.toURI() + "!/"; // NOI18N; this.file = file; } @@ -665,28 +653,16 @@ return known; } - private static class ResURLStreamHandlerFactory implements URLStreamHandlerFactory { - URLStreamHandlerFactory del; - /** - * Creates URLStreamHandler for nbinst protocol - * @param protocol - * @return NbinstURLStreamHandler if the protocol is nbinst otherwise null - */ - public URLStreamHandler createURLStreamHandler(String protocol) { - if (ResURLStreamHandler.RES_PROTO.equals(protocol)) { - return new ResURLStreamHandler (); - } - return del != null ? del.createURLStreamHandler(protocol): null; - } - } - /** * URLStreamHandler for res protocol */ - private static class ResURLStreamHandler extends URLStreamHandler { - public static final String RES_PROTO = "nbjcl"; + static class ResURLStreamHandler extends URLStreamHandler { - ResURLStreamHandler() {} + private final URLStreamHandler originalJarHandler; + + ResURLStreamHandler(URLStreamHandler originalJarHandler) { + this.originalJarHandler = originalJarHandler; + } /** * Creates URLConnection for URL with res protocol. @@ -694,132 +670,30 @@ * @return URLConnection * @throws IOException */ - protected URLConnection openConnection(URL u) throws IOException { + protected JarURLConnection openConnection(URL u) throws IOException { String url = u.getFile();//toExternalForm(); int bang = url.indexOf("!/"); String jar = url.substring(0, bang); String _name = url.substring(bang+2); Source _src = Source.sources.get(jar); if (_src == null) { - String replace = u.toExternalForm().replaceAll("nbjcl:", "jar:"); - - if (archive.isActive()) { - LOGGER.log(Level.WARNING, "Cannot find {0} in current sources", jar); - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.log(Level.FINER, dumpSources(Source.sources, jar)); - } - LOGGER.log(Level.WARNING, "Trying {0} instead", replace); - LOGGER.log(Level.WARNING, "Disabling class cache"); - archive.stopServing(); + try { + Method m = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); + m.setAccessible(true); + return (JarURLConnection) m.invoke(originalJarHandler, u); + } catch (Exception e) { + throw (IOException) new IOException(e.toString()).initCause(e); } - return new URL(replace).openConnection(); } return new ResURLConnection (u, _src, _name); } - protected @Override void parseURL(URL url, String spec, - int start, int limit) { - String file = null; - String ref = null; - // first figure out if there is an anchor - int refPos = spec.indexOf('#', limit); - boolean refOnly = refPos == start; - if (refPos > -1) { - ref = spec.substring(refPos + 1, spec.length()); - if (refOnly) { - file = url.getFile(); - } - } - // then figure out if the spec is - // 1. absolute (res:) - // 2. relative (i.e. url + foo/bar/baz.ext) - // 3. anchor-only (i.e. url + #foo), which we already did (refOnly) - boolean absoluteSpec = false; - if (spec.length() >= RES_PROTO.length()+1) { - absoluteSpec = spec.substring(0, RES_PROTO.length()+1).equalsIgnoreCase(RES_PROTO+":"); - } - spec = spec.substring(start, limit); - - if (absoluteSpec) { - file = parseAbsoluteSpec(spec); - } else if (!refOnly) { - file = parseContextSpec(url, spec); - - // Canonize the result after the bangslash - int bangSlash = file.lastIndexOf("!/") + 1; - String toBangSlash = file.substring(0, bangSlash); - String afterBangSlash = file.substring(bangSlash); - sun.net.www.ParseUtil canonizer = new sun.net.www.ParseUtil(); // XXX - afterBangSlash = canonizer.canonizeString(afterBangSlash); - file = toBangSlash + afterBangSlash; - } - setURLOK(url, file, ref); - } - - private static String dumpSources(Map sources, String jar) { - StringBuilder sb = new StringBuilder(); - sb.append("Searching for ").append(jar).append("\nwhile available:\n"); - for (Map.Entry entry : sources.entrySet()) { - sb.append(entry.getKey()).append('\n'); - } - - return sb.toString(); - } - - @SuppressWarnings("deprecation") - private void setURLOK(URL url, String file, String ref) { - super.setURL(url, RES_PROTO, "", -1, file, ref); - } - - private String parseAbsoluteSpec(String spec) { - URL url = null; - int index = -1; - // check for !/ - if ((index = spec.lastIndexOf("!/") + 1) == -1) { - throw new NullPointerException("no !/ in spec"); - } - // test the inner URL - try { - String innerSpec = spec.substring(0, index - 1); - url = new URL(innerSpec); - } catch (MalformedURLException e) { - throw new NullPointerException("invalid url: " + - spec + " (" + e + ")"); - } - return spec; - } - - private String parseContextSpec(URL url, String spec) { - String ctxFile = url.getFile(); - // if the spec begins with /, chop up the jar back !/ - if (spec.startsWith("/")) { - int bangSlash = ctxFile.lastIndexOf("!/"); - if (bangSlash == -1) { - throw new NullPointerException("malformed " + - "context url:" + - url + - ": no !/"); - } - ctxFile = ctxFile.substring(0, bangSlash+1); - } - if (!ctxFile.endsWith("/") && (!spec.startsWith("/"))){ - // chop up the last component - int lastSlash = ctxFile.lastIndexOf('/'); - if (lastSlash == -1) { - throw new NullPointerException("malformed " + - "context url:" + - url); - } - ctxFile = ctxFile.substring(0, lastSlash + 1); - } - return (ctxFile + spec); - } } /** URLConnection for URL with res protocol. * */ - private static class ResURLConnection extends URLConnection { + private static class ResURLConnection extends JarURLConnection { private JarSource src; private String name; private byte[] data; @@ -830,7 +704,7 @@ * @param url the parameter for which the connection should be * created */ - private ResURLConnection(URL url, Source src, String name) { + private ResURLConnection(URL url, Source src, String name) throws MalformedURLException { super(url); this.src = (JarSource)src; this.name = name; @@ -870,5 +744,10 @@ if (iStream == null) iStream = new ByteArrayInputStream(data); return iStream; } + + @Override + public JarFile getJarFile() throws IOException { + return src.jar; + } } } diff --git a/o.n.bootstrap/src/org/netbeans/ProxyURLStreamHandlerFactory.java b/o.n.bootstrap/src/org/netbeans/ProxyURLStreamHandlerFactory.java new file mode 100644 --- /dev/null +++ b/o.n.bootstrap/src/org/netbeans/ProxyURLStreamHandlerFactory.java @@ -0,0 +1,148 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; + +/** + * A stream handler factory that delegates to others in lookup. + */ +public class ProxyURLStreamHandlerFactory implements URLStreamHandlerFactory, LookupListener { + + private static final Logger LOG = Logger.getLogger(ProxyURLStreamHandlerFactory.class.getName()); + private static boolean proxyFactoryInitialized; + + public static synchronized void register() { + if (!proxyFactoryInitialized) { + URLStreamHandler originalJarHandler = null; + try { + Method m = URL.class.getDeclaredMethod("getURLStreamHandler", String.class); + m.setAccessible(true); + originalJarHandler = (URLStreamHandler) m.invoke(null, "jar"); + } catch (Throwable t) { + LOG.log(Level.SEVERE, "No way to find original stream handler for jar protocol", t); // NOI18N + } + try { + URL.setURLStreamHandlerFactory(new ProxyURLStreamHandlerFactory(null, originalJarHandler)); + } catch (Error e) { + LOG.log(Level.CONFIG, "Problems registering URLStreamHandlerFactory, trying reflection", e); // NOI18N + try { + URLStreamHandlerFactory prev = null; + for (Field f : URL.class.getDeclaredFields()) { + LOG.log(Level.FINEST, "Found field {0}", f); + if (f.getType() == URLStreamHandlerFactory.class) { + LOG.log(Level.FINEST, "Clearing field {0}"); + f.setAccessible(true); + prev = (URLStreamHandlerFactory) f.get(null); + LOG.log(Level.CONFIG, "Previous value was {0}", prev); + f.set(null, null); + LOG.config("Field is supposed to be empty"); + break; + } + } + URL.setURLStreamHandlerFactory(new ProxyURLStreamHandlerFactory(prev, originalJarHandler)); + } catch (Throwable t) { + LOG.log(Level.SEVERE, "No way to register URLStreamHandlerFactory; NetBeans is unlikely to work", t); // NOI18N + } + } + proxyFactoryInitialized = true; + } + } + + private final URLStreamHandlerFactory delegate; + private final URLStreamHandler originalJarHandler; + private Lookup.Result r; + private URLStreamHandlerFactory[] handlers; + + private ProxyURLStreamHandlerFactory(URLStreamHandlerFactory delegate, URLStreamHandler originalJarHandler) { + this.delegate = delegate; + this.originalJarHandler = originalJarHandler; + } + + public URLStreamHandler createURLStreamHandler(String protocol) { + if (protocol.equals("jar")) { + return new JarClassLoader.ResURLStreamHandler(originalJarHandler); + } else if (protocol.equals("file") || protocol.equals("http") || protocol.equals("https") || protocol.equals("resource")) { // NOI18N + // Well-known handlers in JRE. Do not try to initialize lookup, etc. + return null; + } else { + if (delegate != null) { + URLStreamHandler h = delegate.createURLStreamHandler(protocol); + if (h != null) { + return h; + } + } + URLStreamHandlerFactory[] _handlers; + synchronized (this) { + if (handlers == null) { + r = Lookup.getDefault().lookupResult(URLStreamHandlerFactory.class); + r.addLookupListener(this); + resultChanged(null); + } + _handlers = handlers; + } + for (URLStreamHandlerFactory f : _handlers) { + URLStreamHandler h = f.createURLStreamHandler(protocol); + if (h != null) { + return h; + } + } + return null; + } + } + + public void resultChanged(LookupEvent ev) { + Collection c = r.allInstances(); + synchronized (this) { + handlers = c.toArray(new URLStreamHandlerFactory[0]); + } + } + +}