Commit 2d499712 authored by Andrea Aime's avatar Andrea Aime
Browse files

[GEOS-9109] Styles referring to relative icons in subdirectories fail to change workspace

parent 365a3b42
......@@ -16,7 +16,23 @@ import java.util.List;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import org.apache.commons.io.FileUtils;
import org.geoserver.catalog.*;
import org.apache.commons.lang3.SystemUtils;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.Styles;
import org.geoserver.catalog.WMSLayerInfo;
import org.geoserver.catalog.WMSStoreInfo;
import org.geoserver.catalog.WMTSLayerInfo;
import org.geoserver.catalog.WMTSStoreInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.Paths;
......@@ -26,8 +42,18 @@ import org.geoserver.platform.resource.ResourceStore;
import org.geoserver.platform.resource.Resources;
import org.geoserver.util.EntityResolverProvider;
import org.geotools.data.DataUtilities;
import org.geotools.styling.*;
import org.geotools.styling.AbstractStyleVisitor;
import org.geotools.styling.ChannelSelection;
import org.geotools.styling.DefaultResourceLocator;
import org.geotools.styling.ExternalGraphic;
import org.geotools.styling.Mark;
import org.geotools.styling.ResourceLocator;
import org.geotools.styling.SelectedChannelType;
import org.geotools.styling.Style;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.util.URLs;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.xml.sax.EntityResolver;
/**
......@@ -1251,7 +1277,7 @@ public class GeoServerDataDirectory {
if (styleResource.getType() == Type.UNDEFINED) {
throw new FileNotFoundException("No such resource: " + s.getFilename());
}
final DefaultResourceLocator locator = new DefaultResourceLocator();
final DefaultResourceLocator locator = new ResourceAwareResourceLocator();
locator.setSourceUrl(Resources.toURL(styleResource));
StyledLayerDescriptor sld =
Styles.handler(s.getFormat())
......@@ -1392,12 +1418,13 @@ public class GeoServerDataDirectory {
return;
}
try {
Resource r = resourceLoader.fromURL(exgr.getLocation());
final String location = exgr.getURI();
Resource r = resourceLoader.fromURL(location);
if (r != null && r.getType() != Type.UNDEFINED) {
resources.add(r);
}
} catch (IllegalArgumentException | MalformedURLException e) {
} catch (IllegalArgumentException e) {
GeoServerConfigPersister.LOGGER.log(
Level.WARNING,
"Error attemping to process SLD resource",
......@@ -1405,6 +1432,28 @@ public class GeoServerDataDirectory {
}
}
@Override
public void visit(Mark mark) {
final Expression wellKnownName = mark.getWellKnownName();
if (wellKnownName instanceof Literal) {
final String name = wellKnownName.evaluate(null, String.class);
if (name.startsWith("resource:/")) {
try {
Resource r = resourceLoader.fromURL(name);
if (r != null && r.getType() != Type.UNDEFINED) {
resources.add(r);
}
} catch (IllegalArgumentException e) {
GeoServerConfigPersister.LOGGER.log(
Level.WARNING,
"Error attemping to process SLD resource",
e);
}
}
}
}
// TODO: Workaround for GEOT-4803, Remove when it is fixed, KS
@Override
public void visit(ChannelSelection cs) {
......@@ -1453,7 +1502,31 @@ public class GeoServerDataDirectory {
return locator;
}
private class GeoServerResourceLocator extends DefaultResourceLocator {
private class ResourceAwareResourceLocator extends DefaultResourceLocator {
@Override
protected URL validateRelativeURL(URL relativeUrl) {
if (relativeUrl.getProtocol().equalsIgnoreCase("resource")) {
String path = relativeUrl.getPath();
if (resourceLoader.get(path).getType() != Type.UNDEFINED) {
return relativeUrl;
} else {
return null;
}
} else {
return super.validateRelativeURL(relativeUrl);
}
}
@Override
protected URL makeRelativeURL(String uri, String query) {
if (SystemUtils.IS_OS_WINDOWS && uri.contains("\\")) {
uri = uri.replace('\\', '/');
}
return super.makeRelativeURL(uri, query);
}
}
private class GeoServerResourceLocator extends ResourceAwareResourceLocator {
@Override
public URL locateResource(String uri) {
......@@ -1500,15 +1573,5 @@ public class GeoServerDataDirectory {
return url;
}
}
@Override
protected URL validateRelativeURL(URL relativeUrl) {
// the resource:/ thing does not make for a valid url, so don't validate it
if (relativeUrl.getProtocol().equalsIgnoreCase("resource")) {
return relativeUrl;
} else {
return super.validateRelativeURL(relativeUrl);
}
}
}
}
......@@ -7,6 +7,8 @@ package org.geoserver.config;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
......@@ -75,14 +77,20 @@ public class GeoServerResourcePersister implements CatalogListener {
if (source instanceof StyleInfo) {
i = event.getPropertyNames().indexOf("workspace");
if (i > -1) {
WorkspaceInfo oldWorkspace = (WorkspaceInfo) event.getOldValues().get(i);
WorkspaceInfo newWorkspace = (WorkspaceInfo) event.getNewValues().get(i);
Resource oldDir = dd.getStyles(oldWorkspace);
Resource newDir = dd.getStyles(newWorkspace);
URI oldDirURI = new URI(oldDir.path());
// look for any resource files (image, etc...) and copy them over, don't move
// since they could be shared among other styles
for (Resource old : dd.additionalStyleResources((StyleInfo) source)) {
if (old.getType() != Type.UNDEFINED) {
copyResToDir(old, newDir);
URI oldURI = new URI(old.path());
final URI relative = oldDirURI.relativize(oldURI);
final Resource target = newDir.get(relative.getPath()).parent();
copyResToDir(old, target);
}
}
......@@ -94,7 +102,7 @@ public class GeoServerResourcePersister implements CatalogListener {
}
}
}
} catch (IOException e) {
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
}
......
......@@ -20,6 +20,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.geoserver.catalog.CascadeRemovalReporter.ModificationType;
import org.geoserver.catalog.event.CatalogEvent;
import org.geoserver.catalog.event.CatalogListener;
......@@ -40,6 +41,7 @@ import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.PointSymbolizer;
import org.geotools.util.Version;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
......@@ -72,21 +74,40 @@ public class CatalogIntegrationTest extends GeoServerSystemTestSupport {
@Override
protected void onSetUp(SystemTestData testData) throws IOException {
final Catalog catalog = getCatalog();
testData.addStyle(
"singleStyleGroup",
"singleStyleGroup.sld",
CatalogIntegrationTest.class,
getCatalog());
"singleStyleGroup", "singleStyleGroup.sld", CatalogIntegrationTest.class, catalog);
testData.addStyle(
"multiStyleGroup",
"multiStyleGroup.sld",
CatalogIntegrationTest.class,
getCatalog());
"multiStyleGroup", "multiStyleGroup.sld", CatalogIntegrationTest.class, catalog);
testData.addStyle(
"recursiveStyleGroup",
"recursiveStyleGroup.sld",
CatalogIntegrationTest.class,
getCatalog());
catalog);
// add a style with relative resource references, and resources in sub-directories
testData.addStyle("relative", "se_relativepath.sld", ResourcePoolTest.class, catalog);
StyleInfo style = catalog.getStyleByName("relative");
style.setFormatVersion(new Version("1.1.0"));
catalog.save(style);
File images = new File(testData.getDataDirectoryRoot(), "styles/images");
assertTrue(images.mkdir());
File image = new File("./src/test/resources/org/geoserver/catalog/rockFillSymbol.png");
assertTrue(image.exists());
FileUtils.copyFileToDirectory(image, images);
File svg = new File("./src/test/resources/org/geoserver/catalog/square16.svg");
assertTrue(svg.exists());
FileUtils.copyFileToDirectory(svg, images);
// add a workspace for style move testing
final CatalogFactory factory = catalog.getFactory();
final WorkspaceInfo secondaryWs = factory.createWorkspace();
secondaryWs.setName("secondary");
final NamespaceInfo secondaryNs = factory.createNamespace();
secondaryNs.setPrefix("secondary");
secondaryNs.setURI("http://www.geoserver.org/secondary");
catalog.add(secondaryWs);
catalog.add(secondaryNs);
}
@Test
......@@ -598,4 +619,22 @@ public class CatalogIntegrationTest extends GeoServerSystemTestSupport {
final StyleInfo point = getCatalog().getStyleByName("point");
assertNotNull(point);
}
@Test
public void testChangeStyleWorkspaceRelativeResources() throws Exception {
// move style to a different workspace
final Catalog catalog = getCatalog();
final StyleInfo style = catalog.getStyleByName("relative");
final WorkspaceInfo secondaryWs = catalog.getWorkspaceByName("secondary");
style.setWorkspace(secondaryWs);
catalog.save(style);
// check the referenced image and svg has been moved keeping the relative position
final Resource relativeImage =
getDataDirectory().getStyles(secondaryWs, "images", "rockFillSymbol.png");
assertEquals(Resource.Type.RESOURCE, relativeImage.getType());
final Resource relativeSvg =
getDataDirectory().getStyles(secondaryWs, "images", "square16.svg");
assertEquals(Resource.Type.RESOURCE, relativeSvg.getType());
}
}
......@@ -19,6 +19,20 @@
</se:GraphicFill>
</se:Fill>
</se:PolygonSymbolizer>
<se:PolygonSymbolizer>
<se:Fill>
<se:GraphicFill>
<se:Graphic>
<se:Mark>
<se:WellKnownName>file://images/square16.svg</se:WellKnownName>
<se:Fill>
<se:SvgParameter name="fill">0xFF0000</se:SvgParameter>
</se:Fill>
</se:Mark>
</se:Graphic>
</se:GraphicFill>
</se:Fill>
</se:PolygonSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="Nuovo documento 1">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="375"
inkscape:cy="1091.4286"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1027"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Livello 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,0)">
<rect
style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect2993"
width="16"
height="16"
x="0"
y="0"
transform="translate(0,0)" />
</g>
</svg>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment