From f53776628f15b22899026f05fed00b9610d06c8d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Apr 2017 09:08:44 -0700 Subject: [PATCH] Issue #1448 - Reduce unncessary URL creation --- .../java/org/eclipse/jetty/util/TypeUtil.java | 4 +- .../jetty/webapp/URLStreamHandlerUtil.java | 78 ++++++++++++ .../jetty/webapp/WebAppClassLoaderTest.java | 16 ++- .../WebAppClassLoaderUrlStreamTest.java | 113 ++++++++++++++++++ 4 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 jetty-webapp/src/test/java/org/eclipse/jetty/webapp/URLStreamHandlerUtil.java create mode 100644 jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderUrlStreamTest.java diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index 8897375a763..69f76c79c0c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -34,8 +34,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import javax.servlet.ServletContainerInitializer; - import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -723,7 +721,7 @@ public class TypeUtil { try { - return Resource.newResource(URIUtil.getJarSource(url.toString())); + return Resource.newResource(URIUtil.getJarSource(url.toURI())); } catch(Exception e) { diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/URLStreamHandlerUtil.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/URLStreamHandlerUtil.java new file mode 100644 index 00000000000..1960a494c45 --- /dev/null +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/URLStreamHandlerUtil.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.webapp; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLStreamHandlerFactory; +import java.util.Arrays; +import java.util.Optional; + +public final class URLStreamHandlerUtil +{ + public static void setFactory(URLStreamHandlerFactory factory) + { + try + { + // First, reset the factory field + Field factoryField = getURLStreamHandlerFactoryField(); + factoryField.setAccessible(true); + factoryField.set(null, null); + + if(factory != null) + { + // Next, set the factory + URL.setURLStreamHandlerFactory(factory); + } + } + catch(Throwable ignore) + { + ignore.printStackTrace(System.err); + } + } + + public static URLStreamHandlerFactory getFactory() + { + try + { + // First, reset the factory field + Field factoryField = getURLStreamHandlerFactoryField(); + factoryField.setAccessible(true); + return (URLStreamHandlerFactory) factoryField.get(null); + } + catch(Throwable ignore) + { + return null; + } + } + + private static Field getURLStreamHandlerFactoryField() + { + Optional optFactoryField = Arrays.stream(URL.class.getDeclaredFields()) + .filter((f) -> Modifier.isStatic(f.getModifiers()) && + f.getType().equals(URLStreamHandlerFactory.class)) + .findFirst(); + + if(optFactoryField.isPresent()) + return optFactoryField.get(); + + throw new RuntimeException( "Cannot find URLStreamHandlerFactory field in " + URL.class.getName() ); + } +} diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java index 8a1518a0086..2974ea9f3d0 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java @@ -18,15 +18,18 @@ package org.eclipse.jetty.webapp; -import static org.eclipse.jetty.toolchain.test.ExtraMatchers.*; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeThat; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.net.URI; import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Path; import java.security.ProtectionDomain; import java.util.ArrayList; @@ -48,7 +51,7 @@ public class WebAppClassLoaderTest private Path testWebappDir; private WebAppContext _context; - private WebAppClassLoader _loader; + protected WebAppClassLoader _loader; @Before public void init() throws Exception @@ -241,6 +244,9 @@ public class WebAppClassLoaderTest @Test public void testResources() throws Exception { + // The existence of a URLStreamHandler changes the behavior + assumeThat("No URLStreamHandler in place", URLStreamHandlerUtil.getFactory(), nullValue()); + List expected = new ArrayList<>(); List resources; diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderUrlStreamTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderUrlStreamTest.java new file mode 100644 index 00000000000..296285052e6 --- /dev/null +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderUrlStreamTest.java @@ -0,0 +1,113 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.webapp; + +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; + +public class WebAppClassLoaderUrlStreamTest extends WebAppClassLoaderTest +{ + public static class URLHandlers implements URLStreamHandlerFactory + { + private static final String[] STREAM_HANDLER_PREFIXES; + + static + { + STREAM_HANDLER_PREFIXES = new String[]{ + "sun.net.www.protocol", + "org.apache.harmony.luni.internal.net.www.protocol", + "javax.net.ssl" + }; + } + + private Map handlers = new HashMap<>(); + private ClassLoader loader; + + public URLHandlers(ClassLoader loader) + { + this.loader = loader; + } + + private URLStreamHandler getBuiltInHandler(String protocol, ClassLoader classLoader) + { + URLStreamHandler handler = handlers.get(protocol); + + if (handler == null) + { + for (String prefix : STREAM_HANDLER_PREFIXES) + { + String className = prefix + '.' + protocol + ".Handler"; + try + { + Class clazz = Class.forName(className, false, classLoader); + handler = (URLStreamHandler) clazz.newInstance(); + break; + } + catch (Exception ignore) + { + ignore.printStackTrace(System.err); + } + } + + if (handler != null) + { + handlers.put(protocol, handler); + } + } + + if (handler == null) + { + throw new RuntimeException("Unable to find handler for protocol [" + protocol + "]"); + } + + return handler; + } + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) + { + try + { + return getBuiltInHandler(protocol, loader); + } + catch (Exception e) + { + throw new RuntimeException("Unable to create URLStreamHandler for protocol [" + protocol + "]"); + } + } + } + + @After + public void cleanupURLStreamHandlerFactory() + { + URLStreamHandlerUtil.setFactory(null); + } + + @Before + public void init() throws Exception + { + super.init(); + URLStreamHandlerUtil.setFactory(new URLHandlers(_loader)); + } +}