From e59d6fa2c02f823041f44a0df1aab3e9e43a92db Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 3 May 2018 09:56:08 +1000 Subject: [PATCH] Support root base or home for quickstart #2446 (#2447) * Support root base or home for quickstart #2446 Signed-off-by: Greg Wilkins * Issue #2446 - Root as base for quickstart + Adding more tests for differences we have to resolve with windows vs linux root path differences. and URI's that have an authority vs those without an authority. without authority examples: file:/code/ file:/C:/code/ with authority examples: file:///code/ file:///C:/code/ Signed-off-by: Joakim Erdfelt * Using the canonical URI passes all the tests on linux, but I still have some concerns with the whole approach Signed-off-by: Greg Wilkins * minor cleanups Signed-off-by: Greg Wilkins * Handle windows URIs Because a windows like `file:///F:/` has a path of `/F:/`, then it is OK to strip the trailing `/`, so the expected normalized value can be `file:///F:`. Signed-off-by: Greg Wilkins --- .../jetty/quickstart/AttributeNormalizer.java | 100 ++++++++++-------- .../quickstart/AttributeNormalizerTest.java | 82 +++++++++++--- ...ttributeNormalizer_ToCanonicalUriTest.java | 78 ++++++++++++++ 3 files changed, 202 insertions(+), 58 deletions(-) create mode 100644 tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizer_ToCanonicalUriTest.java diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java index 529fa4fd135..a6a3735eae9 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java @@ -63,7 +63,7 @@ public class AttributeNormalizer { private static final Logger LOG = Log.getLogger(AttributeNormalizer.class); private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}"); - + private static class Attribute { final String key; @@ -78,14 +78,15 @@ public class AttributeNormalizer } } - private static URI toCanonicalURI(URI uri) + public static URI toCanonicalURI(URI uri) { uri = uri.normalize(); - String ascii = uri.toASCIIString(); - if (ascii.endsWith("/")) + String path = uri.getPath(); + if (path!=null && path.length()>1 && path.endsWith("/")) { try { + String ascii = uri.toASCIIString(); uri = new URI(ascii.substring(0,ascii.length()-1)); } catch(URISyntaxException e) @@ -96,7 +97,16 @@ public class AttributeNormalizer return uri; } - private static Path toCanonicalPath(String path) + public static String toCanonicalURI(String uri) + { + if (uri!=null && uri.length()>1 && uri.endsWith("/")) + { + return uri.substring(0,uri.length()-1); + } + return uri; + } + + public static Path toCanonicalPath(String path) { if (path == null) return null; @@ -148,8 +158,8 @@ public class AttributeNormalizer public URIAttribute(String key, URI uri, int weight) { - super(key,uri.toASCIIString(),weight); - this.uri = uri; + super(key,toCanonicalURI(uri.toASCIIString()),weight); + this.uri = toCanonicalURI(uri); } @Override @@ -198,17 +208,6 @@ public class AttributeNormalizer } }; - private static void add(Listpaths,List uris,String key,int weight) - { - String value = System.getProperty(key); - if (value!=null) - { - Path path = toCanonicalPath(value); - paths.add(new PathAttribute(key,path,weight)); - uris.add(new URIAttribute(key+".uri",toCanonicalURI(path.toUri()),weight)); - } - } - private URI warURI; private Map attributes = new HashMap<>(); private List paths = new ArrayList<>(); @@ -223,10 +222,10 @@ public class AttributeNormalizer if (!warURI.isAbsolute()) throw new IllegalArgumentException("WAR URI is not absolute: " + warURI); - add(paths,uris,"jetty.base",9); - add(paths,uris,"jetty.home",8); - add(paths,uris,"user.home",7); - add(paths,uris,"user.dir",6); + addSystemProperty("jetty.base", 9); + addSystemProperty("jetty.home", 8); + addSystemProperty("user.home", 7); + addSystemProperty("user.dir", 6); if (warURI.getScheme().equalsIgnoreCase("file")) paths.add(new PathAttribute("WAR.path",toCanonicalPath(new File(warURI).toString()),10)); @@ -244,6 +243,17 @@ public class AttributeNormalizer { LOG.debug(attr.toString()); } + } + } + + private void addSystemProperty(String key, int weight) + { + String value = System.getProperty(key); + if (value!=null) + { + Path path = toCanonicalPath(value); + paths.add(new PathAttribute(key,path,weight)); + uris.add(new URIAttribute(key+".uri",path.toUri(),weight)); } } @@ -324,35 +334,33 @@ public class AttributeNormalizer { for (URIAttribute a : uris) { - try - { - if (uri.compareTo(a.uri)==0) - return String.format("${%s}",a.key); + if (uri.compareTo(a.uri)==0) + return String.format("${%s}",a.key); - if (!a.uri.getScheme().equalsIgnoreCase(uri.getScheme())) - continue; - if (a.uri.getHost()==null && uri.getHost()!=null) - continue; - if (a.uri.getHost()!=null && !a.uri.getHost().equals(uri.getHost())) - continue; + if (!a.uri.getScheme().equalsIgnoreCase(uri.getScheme())) + continue; + if (a.uri.getHost()==null && uri.getHost()!=null) + continue; + if (a.uri.getHost()!=null && !a.uri.getHost().equals(uri.getHost())) + continue; - if (a.uri.getPath().equals(uri.getPath())) - return a.value; + String aPath = a.uri.getPath(); + String uPath = uri.getPath(); + if (aPath.equals(uPath)) + return a.value; - if (!uri.getPath().startsWith(a.uri.getPath())) - continue; + if (!uPath.startsWith(aPath)) + continue; - String s = uri.getPath().substring(a.uri.getPath().length()); + if (uPath.length()==aPath.length()) + return String.format("${%s}",a.key); + + String s = uPath.substring(aPath.length()); + if (s.length()>0 && s.charAt(0)!='/') + continue; + + return String.format("${%s}%s",a.key,s); - if (s.charAt(0)!='/') - continue; - - return String.format("${%s}%s",a.key,new URI(s).toASCIIString()); - } - catch(URISyntaxException e) - { - LOG.ignore(e); - } } return uri.toASCIIString(); } diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java index add19bb2890..7dabb3744f3 100644 --- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java +++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizerTest.java @@ -62,6 +62,16 @@ public class AttributeNormalizerTest data.add(new Object[]{arch, title, env}); + // ------ + title = "Old Setup"; + + env = new HashMap<>(); + env.put("jetty.home", asTargetPath(title,"jetty-distro")); + env.put("jetty.base", asTargetPath(title,"jetty-distro")); + env.put("WAR", asTargetPath(title,"jetty-distro/webapps/FOO")); + + data.add(new Object[]{arch, title, env}); + // ------ // This puts the jetty.home inside of the jetty.base title = "Overlap Setup"; @@ -82,6 +92,17 @@ public class AttributeNormalizerTest env.put("WAR", asTargetPath(title,"app%2Fnasty/base/webapps/FOO")); data.add(new Object[]{arch, title, env}); + + // ------ + title = "Root Path Setup"; + env = new HashMap<>(); + Path rootPath = MavenTestingUtils.getTargetPath().getRoot(); + env.put("jetty.home", rootPath.toString()); + env.put("jetty.base", rootPath.toString()); + env.put("WAR", rootPath.resolve("webapps/root").toString()); + + data.add(new Object[]{arch, title, env}); + return data; } @@ -96,8 +117,8 @@ public class AttributeNormalizerTest } private Map oldValues = new HashMap<>(); - private final String jettyHome; - private final String jettyBase; + private final Path jettyHome; + private final Path jettyBase; private final String war; private final String arch; private final String title; @@ -118,8 +139,8 @@ public class AttributeNormalizerTest }); // Grab specific values of interest in general - jettyHome = env.get("jetty.home"); - jettyBase = env.get("jetty.base"); + jettyHome = new File(env.get("jetty.home")).toPath().toAbsolutePath(); + jettyBase = new File(env.get("jetty.base")).toPath().toAbsolutePath(); war = env.get("WAR"); // Set environment (skipping null and WAR) @@ -176,42 +197,79 @@ public class AttributeNormalizerTest public void testNormalizeJettyBaseAsFile() { // Normalize jetty.base as File path - assertNormalize(new File(jettyBase), "${jetty.base}"); + assertNormalize(jettyBase.toFile(), "${jetty.base}"); } @Test public void testNormalizeJettyHomeAsFile() { // Normalize jetty.home as File path - assertNormalize(new File(jettyHome), "${jetty.home}"); + String expected = jettyBase.equals(jettyHome)?"${jetty.base}":"${jetty.home}"; + assertNormalize(jettyHome.toFile(), expected); + } + + @Test + public void testNormalizeJettyBaseAsPath() + { + // Normalize jetty.base as File path + assertNormalize(jettyBase, "${jetty.base}"); + } + + @Test + public void testNormalizeJettyHomeAsPath() + { + // Normalize jetty.home as File path + String expected = jettyBase.equals(jettyHome)?"${jetty.base}":"${jetty.home}"; + assertNormalize(jettyHome, expected); } @Test - public void testNormalizeJettyBaseAsURI() + public void testNormalizeJettyBaseAsURI_WithAuthority() { // Normalize jetty.base as URI path - assertNormalize(new File(jettyBase).toURI(), "${jetty.base.uri}"); + // Path.toUri() typically includes an URI authority + assertNormalize(jettyBase.toUri(), "${jetty.base.uri}"); + } + + @Test + public void testNormalizeJettyBaseAsURI_WithoutAuthority() + { + // Normalize jetty.base as URI path + // File.toURI() typically DOES NOT include an URI authority + assertNormalize(jettyBase.toFile().toURI(), "${jetty.base.uri}"); } @Test - public void testNormalizeJettyHomeAsURI() + public void testNormalizeJettyHomeAsURI_WithAuthority() + { + // Normalize jetty.home as URI path + String expected = jettyBase.equals(jettyHome)?"${jetty.base.uri}":"${jetty.home.uri}"; + + // Path.toUri() typically includes an URI authority + assertNormalize(jettyHome.toUri(), expected); + } + + @Test + public void testNormalizeJettyHomeAsURI_WithoutAuthority() { // Normalize jetty.home as URI path - assertNormalize(new File(jettyHome).toURI(), "${jetty.home.uri}"); + String expected = jettyBase.equals(jettyHome)?"${jetty.base.uri}":"${jetty.home.uri}"; + // File.toURI() typically DOES NOT include an URI authority + assertNormalize(jettyHome.toFile().toURI(), expected); } @Test public void testExpandJettyBase() { // Expand jetty.base - assertExpandPath("${jetty.base}", jettyBase); + assertExpandPath("${jetty.base}", jettyBase.toString()); } @Test public void testExpandJettyHome() { // Expand jetty.home - assertExpandPath("${jetty.home}", jettyHome); + assertExpandPath("${jetty.home}", jettyHome.toString()); } @Test diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizer_ToCanonicalUriTest.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizer_ToCanonicalUriTest.java new file mode 100644 index 00000000000..dfb4cb3ead7 --- /dev/null +++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/AttributeNormalizer_ToCanonicalUriTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.quickstart; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.net.URI; +import java.nio.file.FileSystems; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AttributeNormalizer_ToCanonicalUriTest +{ + @Parameterized.Parameters(name = "[{index}] {0} - {1}") + public static List data() + { + List data = new ArrayList<>(); + + + // root without authority + data.add(new String[]{ "file:/", "file:/" }); + data.add(new String[]{ "file:/F:/", "file:/F:" }); + + // root with empty authority + data.add(new String[]{ "file:///", "file:///" }); + data.add(new String[]{ "file:///F:/", "file:///F:" }); + + // deep directory - no authority + data.add(new String[]{ "file:/home/user/code/", "file:/home/user/code" }); + data.add(new String[]{ "file:/C:/code/", "file:/C:/code" }); + + // deep directory - with authority + data.add(new String[]{ "file:///home/user/code/", "file:///home/user/code" }); + data.add(new String[]{ "file:///C:/code/", "file:///C:/code" }); + + // Some non-file tests + data.add(new String[]{ "http://webtide.com/", "http://webtide.com/" }); + data.add(new String[]{ "http://webtide.com/cometd/", "http://webtide.com/cometd" }); + + return data; + } + + @Parameterized.Parameter + public String input; + + @Parameterized.Parameter(1) + public String expected; + + @Test + public void testCanonicalURI() + { + URI inputURI = URI.create(input); + URI actual = AttributeNormalizer.toCanonicalURI(inputURI); + assertThat(input, actual.toASCIIString(), is(expected)); + } +}