Skip to content

Commit b2f2671

Browse files
committed
Review fixes
1 parent ed95026 commit b2f2671

17 files changed

Lines changed: 390 additions & 465 deletions

File tree

geowebcache/core/src/main/java/org/geowebcache/util/NullURLMangler.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,12 @@
1313
*/
1414
package org.geowebcache.util;
1515

16-
import org.apache.commons.lang3.StringUtils;
16+
import java.util.Map;
1717

1818
public class NullURLMangler implements URLMangler {
1919

2020
@Override
21-
public String buildURL(String baseURL, String contextPath, String path) {
22-
final String context = StringUtils.strip(contextPath, "/");
23-
24-
// if context is root ("/") then don't append it to prevent double slashes ("//") in return
25-
// URLs
26-
if (context == null || context.isEmpty()) {
27-
return StringUtils.strip(baseURL, "/") + "/" + StringUtils.strip(path, "/");
28-
} else {
29-
return StringUtils.strip(baseURL, "/") + "/" + context + "/" + StringUtils.strip(path, "/");
30-
}
21+
public void mangleURL(StringBuilder baseURL, StringBuilder path, Map<String, String> kvp, URLType type) {
22+
// Default URL assembly is handled by URLManglerUtils.
3123
}
3224
}
Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,28 @@
1-
/**
1+
/*
22
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
3-
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
4-
* later version.
5-
*
6-
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
7-
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8-
*
9-
* <p>You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
10-
* <http://www.gnu.org/licenses/>.
11-
*
12-
* @author Robert Marianski, OpenGeo, 20012
3+
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
4+
* any later version.
135
*/
146
package org.geowebcache.util;
157

16-
import java.util.Collections;
178
import java.util.Map;
189

19-
/**
20-
* subset copied from org.geoserver.ows.URLMangler
21-
*
22-
* <p>This hook allows others to plug in custom url generation.
23-
*/
10+
/** Hook allowing custom URL generation and mangling. */
2411
public interface URLMangler {
2512

26-
/**
27-
* Allows for a custom url generation strategy
28-
*
29-
* @param baseURL the base url - contains the url up to the domain and port
30-
* @param contextPath the servlet context path, like /geoserver/gwc
31-
* @param path the remaining path after the context path
32-
* @return the full generated url from the pieces
33-
*/
34-
public String buildURL(String baseURL, String contextPath, String path);
35-
36-
/**
37-
* Allows for a custom url generation strategy while also carrying query parameters separately from the path.
38-
*
39-
* <p>The default implementation preserves the legacy behavior and ignores the query parameter map. New callers
40-
* should prefer overriding this method so propagated parameters can remain separated until the final URL is
41-
* emitted.
42-
*
43-
* @param baseURL the base url, contains the url up to the domain and port
44-
* @param contextPath the servlet context path, like /geoserver/gwc
45-
* @param path the remaining path after the context path
46-
* @param queryParameters propagated query parameters to preserve separately from the path
47-
* @return the generated url (without serialized query parameters) together with the resulting query parameter map,
48-
* so implementations return their result instead of mutating the {@code queryParameters} argument
49-
*/
50-
default UrlAndParams buildURL(
51-
String baseURL, String contextPath, String path, Map<String, String> queryParameters) {
52-
return new UrlAndParams(buildURL(baseURL, contextPath, path), queryParameters);
13+
enum URLType {
14+
EXTERNAL,
15+
RESOURCE,
16+
SERVICE
5317
}
5418

5519
/**
56-
* Result of {@link #buildURL(String, String, String, Map)}: the generated URL (without a serialized query string)
57-
* and the query parameters that should eventually be appended to it.
20+
* Callback that can change the base URL, path, or query parameter map before URL serialization.
21+
*
22+
* @param baseURL mutable base URL buffer containing host, port, and application base
23+
* @param path mutable path buffer after the application name
24+
* @param kvp mutable GET request parameters, which may be enriched or modified
25+
* @param type URL type for consideration during mangling
5826
*/
59-
record UrlAndParams(String url, Map<String, String> queryParameters) {
60-
public UrlAndParams {
61-
queryParameters = queryParameters == null ? Collections.emptyMap() : queryParameters;
62-
}
63-
}
27+
void mangleURL(StringBuilder baseURL, StringBuilder path, Map<String, String> kvp, URLType type);
6428
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
3+
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
4+
* any later version.
5+
*/
6+
package org.geowebcache.util;
7+
8+
import java.util.LinkedHashMap;
9+
import java.util.Map;
10+
import org.apache.commons.lang3.StringUtils;
11+
12+
/** Shared URL assembly for GeoWebCache URL manglers. */
13+
public final class URLManglerUtils {
14+
15+
private URLManglerUtils() {}
16+
17+
/**
18+
* Builds a URL after allowing one mangler to mutate its base, path, and query parameters.
19+
*
20+
* <p>Query parameters embedded in {@code path} remain in place; parameters from {@code kvp} are appended after them
21+
* and before any fragment. The input map is copied so callers retain ownership of their map.
22+
*/
23+
public static String buildURL(
24+
String baseURL,
25+
String contextPath,
26+
String path,
27+
Map<String, String> kvp,
28+
URLMangler urlMangler,
29+
URLMangler.URLType type) {
30+
StringBuilder base = new StringBuilder(StringUtils.strip(baseURL, "/"));
31+
String context = StringUtils.strip(contextPath, "/");
32+
String remainingPath = StringUtils.stripStart(path, "/");
33+
boolean trailingSlash = path != null && (path.isEmpty() || path.endsWith("/"));
34+
StringBuilder pathBuffer = new StringBuilder();
35+
if (StringUtils.isNotBlank(context)) pathBuffer.append(context);
36+
if (StringUtils.isNotBlank(remainingPath)) {
37+
if (!pathBuffer.isEmpty()) pathBuffer.append('/');
38+
pathBuffer.append(remainingPath);
39+
}
40+
Map<String, String> parameters = kvp == null ? new LinkedHashMap<>() : new LinkedHashMap<>(kvp);
41+
42+
urlMangler.mangleURL(base, pathBuffer, parameters, type);
43+
44+
String result = base.toString();
45+
if (!pathBuffer.isEmpty()) {
46+
result = appendPath(result, pathBuffer.toString());
47+
}
48+
if (trailingSlash && !pathBuffer.isEmpty() && !result.endsWith("/")) result += "/";
49+
return appendQueryParameters(result, parameters);
50+
}
51+
52+
private static String appendPath(String base, String path) {
53+
if (base.endsWith("/") || path.isEmpty()) return base + path;
54+
return base + "/" + path;
55+
}
56+
57+
private static String appendQueryParameters(String url, Map<String, String> parameters) {
58+
if (parameters.isEmpty()) return url;
59+
String fragment = "";
60+
int fragmentIndex = url.indexOf('#');
61+
if (fragmentIndex >= 0) {
62+
fragment = url.substring(fragmentIndex);
63+
url = url.substring(0, fragmentIndex);
64+
}
65+
StringBuilder query = new StringBuilder();
66+
for (Map.Entry<String, String> entry : parameters.entrySet()) {
67+
if (!query.isEmpty()) query.append('&');
68+
query.append(ServletUtils.URLEncode(entry.getKey())).append('=');
69+
if (entry.getValue() != null) query.append(ServletUtils.URLEncode(entry.getValue()));
70+
}
71+
if (url.endsWith("?") || url.endsWith("&")) return url + query + fragment;
72+
return url + (url.indexOf('?') >= 0 ? '&' : '?') + query + fragment;
73+
}
74+
}

geowebcache/core/src/test/java/org/geowebcache/util/NullURLManglerTest.java

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,56 +11,54 @@ public class NullURLManglerTest {
1111
private URLMangler urlMangler;
1212

1313
@Before
14-
public void setUp() throws Exception {
14+
public void setUp() {
1515
urlMangler = new NullURLMangler();
1616
}
1717

1818
@Test
1919
public void testBuildURL() {
20-
String url = urlMangler.buildURL("http://foo.example.com", "/foo", "/bar");
21-
Assert.assertEquals("http://foo.example.com/foo/bar", url);
20+
Assert.assertEquals(
21+
"http://foo.example.com/foo/bar",
22+
URLManglerUtils.buildURL(
23+
"http://foo.example.com", "/foo", "/bar", null, urlMangler, URLMangler.URLType.SERVICE));
2224
}
2325

2426
@Test
25-
public void testBuildTrailingSlashes() throws Exception {
26-
String url = urlMangler.buildURL("http://foo.example.com/", "/foo/", "/bar");
27-
Assert.assertEquals("http://foo.example.com/foo/bar", url);
27+
public void testBuildURLNormalizesSlashes() {
28+
Assert.assertEquals(
29+
"http://foo.example.com/foo/bar",
30+
URLManglerUtils.buildURL(
31+
"http://foo.example.com/", "/foo/", "/bar", null, urlMangler, URLMangler.URLType.SERVICE));
32+
Assert.assertEquals(
33+
"http://foo.example.com/foo/bar",
34+
URLManglerUtils.buildURL(
35+
"http://foo.example.com/", "foo/", "bar", null, urlMangler, URLMangler.URLType.SERVICE));
2836
}
2937

3038
@Test
31-
public void testBuildNoLeadingSlashes() throws Exception {
32-
String url = urlMangler.buildURL("http://foo.example.com/", "foo/", "bar");
33-
Assert.assertEquals("http://foo.example.com/foo/bar", url);
39+
public void testBuildURLWithEmptyContext() {
40+
Assert.assertEquals(
41+
"http://foo.example.com/bar",
42+
URLManglerUtils.buildURL(
43+
"http://foo.example.com/", "/", "/bar", null, urlMangler, URLMangler.URLType.SERVICE));
44+
Assert.assertEquals(
45+
"http://foo.example.com/bar",
46+
URLManglerUtils.buildURL(
47+
"http://foo.example.com/", null, "/bar", null, urlMangler, URLMangler.URLType.SERVICE));
3448
}
3549

3650
@Test
37-
public void testBuildRootContext() throws Exception {
38-
String url = urlMangler.buildURL("http://foo.example.com/", "/", "/bar");
39-
Assert.assertEquals("http://foo.example.com/bar", url);
40-
}
41-
42-
@Test
43-
public void testBuildNullContext() throws Exception {
44-
String url = urlMangler.buildURL("http://foo.example.com/", null, "/bar");
45-
Assert.assertEquals("http://foo.example.com/bar", url);
46-
}
47-
48-
@Test
49-
public void testBuildEmptyContext() throws Exception {
50-
String url = urlMangler.buildURL("http://foo.example.com/", "", "/bar");
51-
Assert.assertEquals("http://foo.example.com/bar", url);
52-
}
53-
54-
@Test
55-
public void testBuildWithQueryParametersLeavesMapUntouched() throws Exception {
51+
public void testBuildURLWithQueryParameters() {
5652
Map<String, String> queryParameters = new LinkedHashMap<>();
5753
queryParameters.put("projecttoken", "abc123");
58-
59-
URLMangler.UrlAndParams result =
60-
urlMangler.buildURL("http://foo.example.com/", "/foo", "/bar", queryParameters);
61-
62-
Assert.assertEquals("http://foo.example.com/foo/bar", result.url());
63-
Assert.assertEquals(1, result.queryParameters().size());
64-
Assert.assertEquals("abc123", result.queryParameters().get("projecttoken"));
54+
Assert.assertEquals(
55+
"http://foo.example.com/foo/bar?projecttoken=abc123",
56+
URLManglerUtils.buildURL(
57+
"http://foo.example.com/",
58+
"/foo",
59+
"/bar",
60+
queryParameters,
61+
urlMangler,
62+
URLMangler.URLType.SERVICE));
6563
}
6664
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
3+
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
4+
* any later version.
5+
*/
6+
package org.geowebcache.util;
7+
8+
import static org.junit.Assert.assertEquals;
9+
10+
import java.util.LinkedHashMap;
11+
import java.util.Map;
12+
import org.junit.Test;
13+
14+
public class URLManglerUtilsTest {
15+
16+
@Test
17+
public void testBuildURLAppendsMutatedParametersAfterPathQuery() {
18+
URLMangler mangler = (base, path, kvp, type) -> kvp.put("projecttoken", "abc 123");
19+
assertEquals(
20+
"https://host/app/wmts/{TileRow}?format=image/png&projecttoken=abc+123",
21+
URLManglerUtils.buildURL(
22+
"https://host/",
23+
"/app",
24+
"/wmts/{TileRow}?format=image/png",
25+
new LinkedHashMap<>(),
26+
mangler,
27+
URLMangler.URLType.SERVICE));
28+
}
29+
30+
@Test
31+
public void testBuildURLPreservesOrderAndFragment() {
32+
Map<String, String> parameters = new LinkedHashMap<>();
33+
parameters.put("first", "one");
34+
parameters.put("second", "two");
35+
assertEquals(
36+
"https://host/path?existing=yes&first=one&second=two#fragment",
37+
URLManglerUtils.buildURL(
38+
"https://host",
39+
null,
40+
"/path?existing=yes#fragment",
41+
parameters,
42+
new NullURLMangler(),
43+
URLMangler.URLType.RESOURCE));
44+
}
45+
46+
@Test
47+
public void testBuildURLUsesCompleteURLCreatedByMangler() {
48+
URLMangler mangler = (base, path, kvp, type) -> {
49+
base.setLength(0);
50+
base.append("https://proxy.example.com/complete");
51+
path.setLength(0);
52+
kvp.clear();
53+
};
54+
assertEquals(
55+
"https://proxy.example.com/complete",
56+
URLManglerUtils.buildURL(
57+
"https://host", "/context", "/path", null, mangler, URLMangler.URLType.SERVICE));
58+
}
59+
60+
@Test
61+
public void testBuildURLPassesTypeAndAllowsBaseAndPathChanges() {
62+
URLMangler mangler = (base, path, kvp, type) -> {
63+
assertEquals(URLMangler.URLType.RESOURCE, type);
64+
base.append("/proxy");
65+
path.append("/resource");
66+
kvp.put("token", "value");
67+
};
68+
assertEquals(
69+
"https://host/proxy/context/path/resource?token=value",
70+
URLManglerUtils.buildURL(
71+
"https://host", "/context", "/path", null, mangler, URLMangler.URLType.RESOURCE));
72+
}
73+
}

geowebcache/tms/src/main/java/org/geowebcache/service/tms/TMSDocumentFactory.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.geowebcache.layer.meta.LayerMetaInformation;
2828
import org.geowebcache.mime.MimeType;
2929
import org.geowebcache.util.URLMangler;
30+
import org.geowebcache.util.URLManglerUtils;
3031

3132
/**
3233
* Basic implementation of the TMS documents. Not all of GWCs more advanced features can easily be accomodated by this
@@ -95,7 +96,7 @@ protected String getTileMapServiceDoc(String baseUrl, String contextPath) {
9596
xml.header("1.0", encoding);
9697
xml.indentElement("TileMapService")
9798
.attribute("version", "1.0.0")
98-
.attribute("services", urlMangler.buildURL(baseUrl, contextPath, ""));
99+
.attribute("services", buildURL(baseUrl, contextPath, ""));
99100
// TODO can have these set through Spring
100101
xml.simpleElement("Title", "Tile Map Service", true);
101102
xml.simpleElement("Abstract", "A Tile Map Service served by GeoWebCache", true);
@@ -178,7 +179,7 @@ protected String getTileMapDoc(
178179
xml.header("1.0", encoding);
179180
xml.indentElement("TileMap")
180181
.attribute("version", "1.0.0")
181-
.attribute("tilemapservice", urlMangler.buildURL(baseUrl, contextPath, SERVICE_PATH));
182+
.attribute("tilemapservice", buildURL(baseUrl, contextPath, SERVICE_PATH));
182183
xml.simpleElement("Title", tileMapTitle(layer), true);
183184
xml.simpleElement("Abstract", tileMapDescription(layer), true);
184185

@@ -243,12 +244,16 @@ protected String profileForGridSet(GridSet gridSet) {
243244

244245
protected String tileMapUrl(
245246
TileLayer tl, GridSubset gridSub, MimeType mimeType, String baseUrl, String contextPath) {
246-
return urlMangler.buildURL(baseUrl, contextPath, SERVICE_PATH + "/" + tileMapName(tl, gridSub, mimeType));
247+
return buildURL(baseUrl, contextPath, SERVICE_PATH + "/" + tileMapName(tl, gridSub, mimeType));
248+
}
249+
250+
private String buildURL(String baseUrl, String contextPath, String path) {
251+
return URLManglerUtils.buildURL(baseUrl, contextPath, path, null, urlMangler, URLMangler.URLType.SERVICE);
247252
}
248253

249254
protected String tileMapUrl(
250255
TileLayer tl, GridSubset gridSub, MimeType mimeType, int z, String baseUrl, String contextPath) {
251-
return tileMapUrl(tl, gridSub, mimeType, baseUrl, contextPath) + "/" + z;
256+
return buildURL(baseUrl, contextPath, SERVICE_PATH + "/" + tileMapName(tl, gridSub, mimeType) + "/" + z);
252257
}
253258

254259
protected String tileMapName(TileLayer tl, GridSubset gridSub, MimeType mimeType) {

0 commit comments

Comments
 (0)