You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2022/02/18 14:32:56 UTC
[jackrabbit-filevault] branch master updated: JCRVLT-600 optionally allow undeclared prefixes in docview filenames (#209)
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
The following commit(s) were added to refs/heads/master by this push:
new 29ab281 JCRVLT-600 optionally allow undeclared prefixes in docview filenames (#209)
29ab281 is described below
commit 29ab281dd22195f2b138f7f771dc0a261d7321bd
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Feb 18 15:32:53 2022 +0100
JCRVLT-600 optionally allow undeclared prefixes in docview filenames (#209)
add more test for namespaced file imports
---
src/site/markdown/validation.md | 2 +-
.../vault/fs/impl/io/AbstractArtifactHandler.java | 2 +-
.../vault/fs/impl/io/DocViewAnalyzer.java | 4 +-
.../vault/fs/impl/io/DocViewSAXHandler.java | 57 ++++++--------
.../vault/fs/impl/io/FileArtifactHandler.java | 3 -
.../jackrabbit/vault/fs/io/DocViewParser.java | 44 ++++++++---
.../jackrabbit/vault/util/DocViewProperty2.java | 15 +++-
...spaceImportTest.java => NamespaceImportIT.java} | 92 ++++++++++++++--------
.../namespace.zip/META-INF/vault/filter.xml | 4 +
.../namespace.zip/META-INF/vault/nodetypes.cnd | 9 +++
.../namespace.zip/META-INF/vault/properties.xml | 18 +++++
.../namespace.zip/jcr_root/.content.xml | 17 ++++
.../namespace.zip/jcr_root/testroot/.content.xml | 4 +
.../jcr_root/testroot/_cq_content.xml | 20 +++++
.../spi/impl/DocumentViewParserValidator.java | 26 +++++-
.../impl/DocumentViewParserValidatorFactory.java | 10 ++-
.../DocumentViewParserValidatorTest.java | 37 +++++++--
.../vault/validation/ValidationExecutorTest.java | 5 ++
.../simple-package/jcr_root/apps/_cq_content.xml | 20 +++++
19 files changed, 290 insertions(+), 99 deletions(-)
diff --git a/src/site/markdown/validation.md b/src/site/markdown/validation.md
index 176e987..ce37577 100644
--- a/src/site/markdown/validation.md
+++ b/src/site/markdown/validation.md
@@ -54,7 +54,7 @@ ID | Description | Options | Incremental Execution Limitations
`jackrabbit-filter` | Checks for validity of the [filter.xml](./filter.html) (according to a predefined XML schema). In addition checks that every [docview xml node](./docview.html) is contained in the filter. It also makes sure that all filter root's ancestors are either known/valid roots or are contained in the package dependencies. For ancestor nodes which are not covered by a filter at least a `warn` is emitted. Also it makes sure that `pattern` values for includes/excludes as well [...]
`jackrabbit-properties ` | Checks for validity of the [properties.xml](./properties.html) | none | none
`jackrabbit-dependencies` | Checks for overlapping filter roots of the referenced package dependencies as well as for valid package dependency references (i.e. references which can be resolved). | *severityForUnresolvedDependencies*: severity of validation messages for unresolved dependencies (default = `warn`) | none
-`jackrabbit-docviewparser` | Checks if all docview files in the package are compliant with the [(extended) Document View Format](docview.html). This involves checking for XML validity as well as checking for correct property types. | none | none
+`jackrabbit-docviewparser` | Checks if all docview files in the package are compliant with the [(extended) Document View Format](docview.html). This involves checking for XML validity as well as checking for correct property types. | *allowUndeclaredPrefixInFileName*: if set to `true` then prefixes only used in the encoded docview xml filename don't need to be declared in the XML itself, otherwise a missing declaration leads to a validation error. | none
`jackrabbit-emptyelements` | Check for empty elements within DocView files (used for ordering purposes, compare with [(extended) Document View Format](docview.html)) which are included in the filter with import=replace as those are actually not replaced! | none | none
`jackrabbit-mergelimitations` | Checks for the limitation of import mode=merge outlined at [JCRVLT-255][jcrvlt-255]. | none | none
`jackrabbit-oakindex` | Checks if the package (potentially) modifies/creates an OakIndexDefinition. This is done by evaluating both the filter.xml for potential matches as well as the actual content for nodes with jcr:primaryType `oak:indexDefinition`. | none | none
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
index f1f02ab..545f712 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
@@ -165,7 +165,7 @@ public abstract class AbstractArtifactHandler implements ArtifactHandler, Dumpab
rootNodePath += "/";
}
rootNodePath += rootNodeName;
- new DocViewParser().parse(rootNodePath, source, handler, parentNode.getSession());
+ new DocViewParser(parentNode.getSession()).parse(rootNodePath, source, handler);
} catch (XmlParseException e) {
// wrap as repositoryException although not semantically correct for backwards compatibility
throw new RepositoryException(e);
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java
index df6f2c5..62ca89f 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java
@@ -62,8 +62,8 @@ public class DocViewAnalyzer implements DocViewParserHandler {
InputSource source)
throws IOException {
try {
- DocViewParser docViewParser = new DocViewParser();
- docViewParser.parse(rootPath, source, new DocViewAnalyzer(listener), session);
+ DocViewParser docViewParser = new DocViewParser(session);
+ docViewParser.parse(rootPath, source, new DocViewAnalyzer(listener));
} catch (XmlParseException e) {
throw new IllegalStateException(e);
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java
index e192a25..07f29c3 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java
@@ -29,12 +29,12 @@ import java.util.Optional;
import javax.jcr.NamespaceException;
import javax.jcr.RepositoryException;
-import javax.jcr.Session;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NameFactory;
-import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
+import org.apache.jackrabbit.spi.commons.conversion.ParsingNameResolver;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
@@ -137,15 +137,15 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
private final NamespaceSupport nsSupport;
/**
- * the default name path resolver
+ * the name path resolver to use for the filename (outside the docview), only relevant to resolve jcr:root element
*/
- private final DefaultNamePathResolver npResolver = new DefaultNamePathResolver(this);
+ private final NameResolver nameResolver;
/**
- * Optional JCR session used for namespace lookup if not declared in XML
+ * Optional additional namespace resolver used for namespace lookup if not declared in XML
*/
- private final @Nullable Session session;
-
+ private final @Nullable NamespaceResolver nsResolver;
+
private final DocViewParserHandler handler;
private final String rootNodePath;
@@ -155,7 +155,7 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
private Locator locator;
- public DocViewSAXHandler(@NotNull DocViewParserHandler handler, @NotNull String rootNodePath, @Nullable Session session) {
+ public DocViewSAXHandler(@NotNull DocViewParserHandler handler, @NotNull String rootNodePath, @Nullable NamespaceResolver nsResolver) {
super();
Objects.requireNonNull(handler, "handler must not be null");
this.handler = handler;
@@ -169,7 +169,8 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
nodeStack = new LinkedList<>();
currentPath = null;
locator = new LocatorImpl();
- this.session = session;
+ this.nsResolver = nsResolver;
+ this.nameResolver = new ParsingNameResolver(NameFactoryImpl.getInstance(), this);
}
/**
@@ -256,6 +257,9 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
return new SimpleEntry<>(name, index);
}
+ /**
+ * This considers both namespaces declared in the XML as well as namespaces from the underlying namespace resolver.
+ */
@Override
public String getURI(String prefix) throws NamespaceException {
if (prefix.equals(Name.NS_EMPTY_PREFIX)) {
@@ -263,32 +267,23 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
}
String uri = nsSupport.getURI(prefix);
if (uri == null) {
- if (session != null) {
- try {
- return session.getNamespaceURI(prefix);
- } catch (NamespaceException e) {
- throw e;
- } catch (RepositoryException e) {
- throw new NamespaceException("Unknown prefix " + prefix, e);
- }
+ if (nsResolver != null) {
+ return nsResolver.getURI(prefix);
}
throw new NamespaceException("Unknown prefix " + prefix);
}
return uri;
}
+ /**
+ * This considers both namespaces declared in the XML as well as namespaces from the underlying namespace resolver.
+ */
@Override
public String getPrefix(String uri) throws NamespaceException {
String prefix = nsSupport.getPrefix(uri);
if (prefix == null) {
- if (session != null) {
- try {
- return session.getNamespacePrefix(uri);
- } catch (NamespaceException e) {
- throw e;
- } catch (RepositoryException e) {
- throw new NamespaceException("Unmapped URL " + prefix, e);
- }
+ if (nsResolver != null) {
+ return nsResolver.getPrefix(uri);
}
throw new NamespaceException("Unmapped URL " + uri);
}
@@ -312,7 +307,7 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
Map.Entry<String, Integer> nameAndIndex = getNameAndIndex(Text.getName(rootNodePath));
index = nameAndIndex.getValue();
try {
- name = npResolver.getQName(nameAndIndex.getKey());
+ name = nameResolver.getQName(nameAndIndex.getKey());
} catch (NamespaceException e) {
throw new SAXException("Unknown namespace prefix used in file name '" + nameAndIndex.getKey() + "'", e);
} catch (IllegalNameException e) {
@@ -329,9 +324,7 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
// root node element name should take precedence of root node name derived from path
currentPath = Text.getRelativeParent(rootNodePath, 1);
}
- currentPath = PathUtil.append(currentPath, npResolver.getJCRName(name));
- } catch (NamespaceException e) {
- throw new SAXException("No prefix defined for namespace uri '" + uri + "' used in node name '" + nameAndIndex.getKey() + "'", e);
+ currentPath = PathUtil.append(currentPath, ISO9075.decode(qName));
} catch (IllegalArgumentException e) {
throw new SAXException("Invalid name format used in node name '" + nameAndIndex.getKey() + "'", e);
}
@@ -347,11 +340,9 @@ public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements
attributes.getURI(i),
ISO9075.decode(attributes.getLocalName(i)));
DocViewProperty2 property = DocViewProperty2.parse(
- pName.toString(),
- attributes.getValue(i),
- npResolver);
+ pName,
+ attributes.getValue(i));
props.add(property);
-
}
DocViewNode2 ni = new DocViewNode2(name, index, props);
handler.startDocViewNode(currentPath, ni, Optional.ofNullable(nodeStack.peek()), locator.getLineNumber(), locator.getColumnNumber());
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
index 9d14429..3e9ce06 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
@@ -28,8 +28,6 @@ import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.nodetype.NodeType;
-import javax.xml.parsers.ParserConfigurationException;
-
import org.apache.jackrabbit.util.Text;
import org.apache.jackrabbit.vault.fs.api.Artifact;
import org.apache.jackrabbit.vault.fs.api.ArtifactType;
@@ -46,7 +44,6 @@ import org.apache.jackrabbit.vault.util.MimeTypes;
import org.apache.jackrabbit.vault.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
/**
* Creates nt:file structures from {@link SerializationType#XML_GENERIC} or
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java
index 2a724c9..d8f1074 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java
@@ -19,7 +19,6 @@ package org.apache.jackrabbit.vault.fs.io;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@@ -29,24 +28,44 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
-import org.apache.commons.io.IOUtils;
-import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.namespace.SessionNamespaceResolver;
import org.apache.jackrabbit.vault.fs.impl.io.DocViewSAXHandler;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
- * This is a thread-safe sax parser which deals with docview files and passes them to a given {@link DocViewParserHandler}.
+ * This is a thread-safe SAX parser which deals with docview files and passes them to a given {@link DocViewParserHandler}.
*
*/
public class DocViewParser {
-
- /**
- * Thrown in case the XML is not <a href="https://www.w3.org/TR/REC-xml/#sec-well-formed">well-formed</a>
- * or no valid docview format.
- */
+
+ private final @Nullable NamespaceResolver resolver;
+
+ public DocViewParser() {
+ this((NamespaceResolver)null);
+ }
+
+ /**
+ *
+ * @param session uses the namespace from the session for resolving otherwise unknown namespace prefixes in docview files
+ */
+ public DocViewParser(@NotNull Session session) {
+ this(new SessionNamespaceResolver(session));
+ }
+
+ public DocViewParser(@Nullable NamespaceResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ /**
+ * Thrown in case the XML is not
+ * <a href="https://www.w3.org/TR/REC-xml/#sec-well-formed">well-formed</a> or
+ * no valid docview format.
+ */
public static final class XmlParseException extends Exception {
/**
@@ -134,7 +153,7 @@ public class DocViewParser {
// check for docview
return str.contains("<jcr:root") && str.contains("\"http://www.jcp.org/jcr/1.0\"");
}
-
+
private SAXParser createSaxParser() throws ParserConfigurationException, SAXException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
@@ -150,18 +169,17 @@ public class DocViewParser {
* @param rootNodePath the path of the root node of the given docview xml
* @param inputSource the source of the docview xml
* @param handler the callback handler which gets the deserialized node information
- * @param session optional session used for namespace resolution
* @throws IOException
* @throws XmlParseException
*/
- public void parse(String rootNodePath, InputSource inputSource, DocViewParserHandler handler, @Nullable Session session) throws IOException, XmlParseException {
+ public void parse(String rootNodePath, InputSource inputSource, DocViewParserHandler handler) throws IOException, XmlParseException {
final SAXParser parser;
try {
parser = createSaxParser();
} catch (ParserConfigurationException|SAXException e) {
throw new IllegalStateException("Could not create SAX parser" + e.getMessage(), e);
}
- DocViewSAXHandler docViewSaxHandler = new DocViewSAXHandler(handler, rootNodePath, session);
+ DocViewSAXHandler docViewSaxHandler = new DocViewSAXHandler(handler, rootNodePath, resolver);
try {
parser.parse(inputSource, docViewSaxHandler);
} catch (SAXException|IllegalArgumentException e) {
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java
index faaceba..8779993 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java
@@ -261,6 +261,19 @@ public class DocViewProperty2 {
* @throws IllegalNameException
*/
public static @NotNull DocViewProperty2 parse(String name, String value, NameResolver nameResolver) throws IllegalNameException, NamespaceException {
+ return parse(nameResolver.getQName(name), value);
+ }
+
+ /**
+ * Parses a enhanced docview property string and returns the property.
+ * @param name name of the property
+ * @param value (attribute) value
+ * @throws IllegalArgumentException in case the given value does not follow the doc view property grammar
+ * @return a property
+ * @throws NamespaceException
+ * @throws IllegalNameException
+ */
+ public static @NotNull DocViewProperty2 parse(Name name, String value) throws IllegalNameException, NamespaceException {
boolean isMulti = false;
boolean isBinaryRef = false;
int type = PropertyType.UNDEFINED;
@@ -371,7 +384,7 @@ public class DocViewProperty2 {
} else {
vals = Collections.singletonList(tmp.toString());
}
- return new DocViewProperty2(nameResolver.getQName(name), vals, isMulti, type, isBinaryRef);
+ return new DocViewProperty2(name, vals, isMulti, type, isBinaryRef);
}
/**
* Formats (serializes) the given JCR property value according to the enhanced docview syntax.
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportTest.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java
similarity index 63%
rename from vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportTest.java
rename to vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java
index c247c66..2225b47 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportTest.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java
@@ -16,16 +16,23 @@
*/
package org.apache.jackrabbit.vault.packaging.integration;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
import java.io.File;
import java.io.IOException;
import java.util.Properties;
+import javax.jcr.NamespaceException;
+import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
+import org.apache.jackrabbit.api.JackrabbitRepository;
import org.apache.jackrabbit.oak.jcr.Jcr;
import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
@@ -36,16 +43,14 @@ import org.apache.jackrabbit.vault.packaging.ExportOptions;
import org.apache.jackrabbit.vault.packaging.PackageException;
import org.apache.jackrabbit.vault.packaging.VaultPackage;
import org.apache.jackrabbit.vault.packaging.impl.JcrPackageManagerImpl;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
/**
- * Is rather an IT but not relying on {@link IntegrationTestBase}
+ * Tests namespace aware node/property imports
*/
-public class NamespaceImportTest {
+public class NamespaceImportIT extends IntegrationTestBase {
private final static String PREFIX = "prefix";
@@ -53,27 +58,41 @@ public class NamespaceImportTest {
private final static String URI2 = "http://two.namespace.io";
- private Instance i1;
-
- private Instance i2;
+ private Instance sourceOakRepository;
@Before
- public void setup() throws RepositoryException {
- i1 = new Instance();
- i2 = new Instance();
+ public void setUp() throws Exception {
+ sourceOakRepository = new Instance(); // source instance
// Register namespaces with same prefix but different URIs
- // on different instances, i1, i2
- i1.registerNamespace(PREFIX, URI1);
- i2.registerNamespace(PREFIX, URI2);
+ // on different instances
+ sourceOakRepository.registerNamespace(PREFIX, URI1);
+ super.setUp();
+ NamespaceRegistry nsRegistry = admin.getWorkspace().getNamespaceRegistry();
+ try {
+ if (URI1.equals(nsRegistry.getURI(PREFIX))) {
+ throw new IllegalStateException("prefix already registered for a different uri");
+ }
+ } catch (NamespaceException e) {
+ nsRegistry.registerNamespace(PREFIX, URI2);
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ sourceOakRepository.admin.logout();
+ if (sourceOakRepository.repository instanceof JackrabbitRepository) {
+ JackrabbitRepository.class.cast(sourceOakRepository.repository).shutdown();
+ }
+ super.tearDown();
}
@Test
public void importClashingNamespace() throws RepositoryException, IOException, PackageException {
// Set a property with the namespace prefix on instance i1
- i1.getRootNode().addNode("tmp").setProperty("{" + URI1 + "}prop1", "value1");
- i1.admin.save();
+ sourceOakRepository.getRootNode().addNode("tmp").setProperty("{" + URI1 + "}prop1", "value1");
+ sourceOakRepository.admin.save();
// Export the property from instance i1 in a content package archive
@@ -89,16 +108,15 @@ public class NamespaceImportTest {
opts.setMetaInf(inf);
File tmpFile = File.createTempFile("vaulttest", "zip");
- try (VaultPackage pkg = i1.packMgr.assemble(i1.admin, opts, tmpFile)) {
+ try (VaultPackage pkg = sourceOakRepository.packMgr.assemble(sourceOakRepository.admin, opts, tmpFile)) {
Archive archive = pkg.getArchive();
- // Import the archive in the instance i2, with strict mode enabled
-
+ // Import the archive in the target repo, with strict mode enabled
ImportOptions io = new ImportOptions();
io.setStrict(true);
- i2.packMgr.extract(archive, io, true);
+ packMgr.extract(archive, io, true);
- assertEquals(i2.getRootNode().getNode("tmp").getProperty("{" + URI1 + "}prop1").getString(), "value1");
+ assertEquals(admin.getRootNode().getNode("tmp").getProperty("{" + URI1 + "}prop1").getString(), "value1");
} finally {
tmpFile.delete();
}
@@ -108,8 +126,8 @@ public class NamespaceImportTest {
public void importClashingNamespaceOnPath() throws RepositoryException, IOException, PackageException {
// Set a property with the namespace prefix on instance i1
- i1.getRootNode().addNode("tmp").addNode("{" + URI1 + "}node1").setProperty("test", "value1");
- i1.admin.save();
+ sourceOakRepository.getRootNode().addNode("tmp").addNode("{" + URI1 + "}node1").setProperty("test", "value1");
+ sourceOakRepository.admin.save();
// Export the property from instance i1 in a content package archive
@@ -125,24 +143,33 @@ public class NamespaceImportTest {
opts.setMetaInf(inf);
File tmpFile = File.createTempFile("vaulttest", "zip");
- try (VaultPackage pkg = i1.packMgr.assemble(i1.admin, opts, tmpFile)) {
+ try (VaultPackage pkg = sourceOakRepository.packMgr.assemble(sourceOakRepository.admin, opts, tmpFile)) {
Archive archive = pkg.getArchive();
- // Import the archive in the instance i2, with strict mode enabled
-
+ // Import the archive in another instance with strict mode enabled
ImportOptions io = new ImportOptions();
io.setStrict(true);
- i2.packMgr.extract(archive, io, true);
+ packMgr.extract(archive, io, true);
- assertEquals(i2.getRootNode().getProperty("tmp/{" + URI1 + "}node1/test").getString(), "value1");
+ assertEquals(admin.getRootNode().getProperty("tmp/{" + URI1 + "}node1/test").getString(), "value1");
- i2.relogin();
- assertNotEquals(PREFIX, i2.admin.getNamespacePrefix(URI1));
+ Session admin2 = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
+ assertNotEquals(PREFIX, admin2.getNamespacePrefix(URI1));
+ admin2.logout();
} finally {
tmpFile.delete();
}
}
+ @Test
+ public void importUndeclaredNamespaceOnDocViewPath() throws IOException, PackageException, RepositoryException {
+ // namespace declared in package but not in affected docview file
+ try (VaultPackage pkg = extractVaultPackageStrict("/test-packages/namespace.zip")) {
+ assertNotNull(pkg);
+ }
+ }
+
+ /** Simple Oak repository wrapper */
private static final class Instance {
final Repository repository;
@@ -168,11 +195,6 @@ public class NamespaceImportTest {
admin.getWorkspace().getNamespaceRegistry().registerNamespace(prefix, uri);
}
- void relogin() throws RepositoryException {
- admin.logout();
- admin = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
-
- }
}
}
diff --git a/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/filter.xml b/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/filter.xml
new file mode 100644
index 0000000..bbbd616
--- /dev/null
+++ b/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/filter.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<workspaceFilter version="1.0">
+ <filter root="/testroot"/>
+</workspaceFilter>
diff --git a/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/nodetypes.cnd b/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/nodetypes.cnd
new file mode 100644
index 0000000..3771ba5
--- /dev/null
+++ b/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/nodetypes.cnd
@@ -0,0 +1,9 @@
+<'sling'='http://sling.apache.org/jcr/sling/1.0'>
+<'nt'='http://www.jcp.org/jcr/nt/1.0'>
+<'cq'='http://www.day.com/jcr/cq/1.0'>
+
+[sling:Folder] > nt:folder
+ - * (undefined)
+ - * (undefined) multiple
+ + * (nt:base) = sling:Folder version
+
diff --git a/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/properties.xml b/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/properties.xml
new file mode 100644
index 0000000..c606e19
--- /dev/null
+++ b/vault-core/src/test/resources/test-packages/namespace.zip/META-INF/vault/properties.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+<comment>FileVault Package Properties</comment>
+<entry key="createdBy">admin</entry>
+<entry key="name">import-modes-test</entry>
+<entry key="lastModified">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="lastModifiedBy">admin</entry>
+<entry key="created">2011-11-15T09:45:14.685+01:00</entry>
+<entry key="buildCount">1</entry>
+<entry key="version"/>
+<entry key="dependencies"/>
+<entry key="packageFormatVersion">2</entry>
+<entry key="description"/>
+<entry key="lastWrapped">2011-11-15T09:45:14.664+01:00</entry>
+<entry key="group"/>
+<entry key="lastWrappedBy">admin</entry>
+</properties>
diff --git a/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/.content.xml b/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/.content.xml
new file mode 100644
index 0000000..e546f29
--- /dev/null
+++ b/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/.content.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+ jcr:mixinTypes="[rep:AccessControllable]"
+ jcr:primaryType="rep:root"
+ sling:resourceType="sling:redirect"
+ sling:target="/index.html">
+ <rep:policy/>
+ <jcr:system/>
+ <var/>
+ <libs/>
+ <etc/>
+ <apps/>
+ <content/>
+ <tmp/>
+ <home/>
+ <testroot/>
+</jcr:root>
\ No newline at end of file
diff --git a/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/testroot/.content.xml b/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/testroot/.content.xml
new file mode 100644
index 0000000..c5cf582
--- /dev/null
+++ b/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/testroot/.content.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:my="http://jackrabbit.apache.org/filevault/testing"
+ jcr:primaryType="sling:Folder">
+</jcr:root>
diff --git a/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/testroot/_cq_content.xml b/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/testroot/_cq_content.xml
new file mode 100644
index 0000000..531bf71
--- /dev/null
+++ b/vault-core/src/test/resources/test-packages/namespace.zip/jcr_root/testroot/_cq_content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ jcr:primaryType="nt:unstructured">
+</jcr:root>
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidator.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidator.java
index 9a0fa92..7b66d33 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidator.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidator.java
@@ -29,7 +29,10 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import javax.jcr.NamespaceException;
+
import org.apache.commons.io.input.CloseShieldInputStream;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
import org.apache.jackrabbit.vault.fs.io.DocViewParser;
import org.apache.jackrabbit.vault.fs.io.DocViewParser.XmlParseException;
import org.apache.jackrabbit.vault.util.Constants;
@@ -51,10 +54,25 @@ public class DocumentViewParserValidator implements GenericJcrDataValidator {
private final DocViewParser docViewParser;
private final @NotNull ValidationMessageSeverity severity;
- public DocumentViewParserValidator(@NotNull ValidationMessageSeverity severity) {
+ public DocumentViewParserValidator(@NotNull ValidationMessageSeverity severity, boolean allowUndeclaredPrefixInFileName) {
super();
this.docViewValidators = new HashMap<>();
- this.docViewParser = new DocViewParser();
+ if (allowUndeclaredPrefixInFileName) {
+ NamespaceResolver nsResolver = new NamespaceResolver() {
+
+ @Override
+ public String getURI(String prefix) throws NamespaceException {
+ return "http://undeclared.uri";
+ }
+ @Override
+ public String getPrefix(String uri) throws NamespaceException {
+ return "undeclared prefix";
+ }
+ };
+ this.docViewParser = new DocViewParser(nsResolver);
+ } else {
+ this.docViewParser = new DocViewParser();
+ }
this.severity = severity;
}
@@ -146,10 +164,10 @@ public class DocumentViewParserValidator implements GenericJcrDataValidator {
enrichedMessages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG, "Detected DocView..."));
ValidatorDocViewParserHandler handler = new ValidatorDocViewParserHandler(docViewValidators, filePath, basePath);
try {
- docViewParser.parse(rootNodePath, new InputSource(new CloseShieldInputStream(input)), handler, null);
+ docViewParser.parse(rootNodePath, new InputSource(new CloseShieldInputStream(input)), handler);
enrichedMessages.addAll(ValidationViolation.wrapMessages(null, handler.getViolations(), filePath, basePath, rootNodePath, 0, 0));
} catch (XmlParseException e) {
- enrichedMessages.add(new ValidationViolation(severity, "Could not parse FileVault Document View XML: " + e.getMessage(), filePath, basePath, e.getNodePath(), e.getLineNumber(), e.getColumnNumber(), e));
+ enrichedMessages.add(new ValidationViolation(DocumentViewParserValidatorFactory.ID, severity, "Could not parse FileVault Document View XML: " + e.getMessage(), filePath, basePath, e.getNodePath(), e.getLineNumber(), e.getColumnNumber(), e));
}
nodePathsAndLineNumbers.putAll(handler.getNodePaths());
return enrichedMessages;
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidatorFactory.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidatorFactory.java
index dbce9b0..6006805 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidatorFactory.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DocumentViewParserValidatorFactory.java
@@ -28,9 +28,17 @@ public class DocumentViewParserValidatorFactory implements ValidatorFactory {
public static final String ID = ValidatorFactory.ID_PREFIX_JACKRABBIT + "docviewparser";
+ public static final String OPTION_ALLOW_UNDECLARED_PREFIX_IN_FILE_NAME = "allowUndeclaredPrefixInFileName";
+
@Override
public Validator createValidator(@NotNull ValidationContext context, @NotNull ValidatorSettings settings) {
- return new DocumentViewParserValidator(settings.getDefaultSeverity());
+ final boolean allowUndeclaredPrefixInFileName;
+ if (settings.getOptions().containsKey(OPTION_ALLOW_UNDECLARED_PREFIX_IN_FILE_NAME)) {
+ allowUndeclaredPrefixInFileName = Boolean.valueOf(settings.getOptions().get(OPTION_ALLOW_UNDECLARED_PREFIX_IN_FILE_NAME));
+ } else {
+ allowUndeclaredPrefixInFileName = false;
+ }
+ return new DocumentViewParserValidator(settings.getDefaultSeverity(), allowUndeclaredPrefixInFileName);
}
@Override
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/DocumentViewParserValidatorTest.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/DocumentViewParserValidatorTest.java
index c8113c0..5fa268a 100644
--- a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/DocumentViewParserValidatorTest.java
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/DocumentViewParserValidatorTest.java
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.vault.validation;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
@@ -34,6 +35,7 @@ import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NameFactory;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.vault.fs.io.DocViewParser;
import org.apache.jackrabbit.vault.fs.io.DocViewParser.XmlParseException;
import org.apache.jackrabbit.vault.util.DocViewNode2;
import org.apache.jackrabbit.vault.util.DocViewProperty2;
@@ -42,6 +44,7 @@ import org.apache.jackrabbit.vault.validation.spi.DocumentViewXmlValidator;
import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
import org.apache.jackrabbit.vault.validation.spi.impl.DocumentViewParserValidator;
+import org.apache.jackrabbit.vault.validation.spi.impl.DocumentViewParserValidatorFactory;
import org.apache.jackrabbit.vault.validation.spi.util.NodeContextImpl;
import org.apache.sling.api.SlingConstants;
import org.apache.sling.jcr.resource.JcrResourceConstants;
@@ -69,7 +72,7 @@ public class DocumentViewParserValidatorTest {
@Before
public void setUp() throws ParserConfigurationException, SAXException, IOException {
- validator = new DocumentViewParserValidator(ValidationMessageSeverity.ERROR);
+ validator = new DocumentViewParserValidator(ValidationMessageSeverity.ERROR, false);
nodePathsAndLineNumbers = new HashMap<>();
validator.setDocumentViewXmlValidators(Collections.singletonMap("docviewid", docViewXmlValidator));
}
@@ -154,7 +157,30 @@ public class DocumentViewParserValidatorTest {
Mockito.verify(docViewXmlValidator).validate(node, new NodeContextImpl("/", Paths.get(".content.xml"), Paths.get("")), true);
}
}
-
+
+ @Test
+ public void testDocViewWithNamespacedFilename()
+ throws ParserConfigurationException, SAXException, URISyntaxException, IOException, NamespaceException {
+ Path filePath = Paths.get("apps", "_cq_content.xml");
+ String nodePath = "/apps/cq:content";
+ String message = "Unknown namespace prefix used in file name 'cq:content'";
+ // fail during parsing due to unknown namespace in filename
+ try (InputStream input = this.getClass().getResourceAsStream("/simple-package/jcr_root/apps/_cq_content.xml")) {
+ Collection<ValidationMessage> messages = validator.validateJcrData(input, filePath, Paths.get(""), nodePathsAndLineNumbers);
+ ValidationExecutorTest.assertViolation(messages,
+ new ValidationViolation(DocumentViewParserValidatorFactory.ID, ValidationMessageSeverity.ERROR,
+ "Could not parse FileVault Document View XML: " + message,
+ filePath, Paths.get(""), nodePath, 19, 36, new DocViewParser.XmlParseException(message, nodePath, 19, 36)
+ ));
+ }
+ validator = new DocumentViewParserValidator(ValidationMessageSeverity.ERROR, true);
+ try (InputStream input = this.getClass().getResourceAsStream("/simple-package/jcr_root/apps/_cq_content.xml")) {
+ Collection<ValidationMessage> messages = validator.validateJcrData(input, filePath, Paths.get(""), nodePathsAndLineNumbers);
+ // filter
+ MatcherAssert.assertThat(messages, AnyValidationViolationMessageMatcher.noValidationViolationMessageInCollection());
+ }
+ }
+
@Test
public void testDocViewWithEmptyElements() throws IOException {
try (InputStream input = this.getClass().getResourceAsStream("/simple-package/jcr_root/apps/emptyelements/.content.xml")) {
@@ -205,7 +231,7 @@ public class DocumentViewParserValidatorTest {
Collection<ValidationMessage> messages = validator.validateJcrData(input, Paths.get("apps", "_cq_child1.xml"), Paths.get(""), nodePathsAndLineNumbers);
ValidationExecutorTest.assertViolation(messages,
- new ValidationViolation(ValidationMessageSeverity.ERROR,
+ new ValidationViolation(DocumentViewParserValidatorFactory.ID, ValidationMessageSeverity.ERROR,
"Could not parse FileVault Document View XML: Unknown namespace prefix used in file name 'cq:child1'",
Paths.get("apps", "_cq_child1.xml"), Paths.get(""), "/apps/cq:child1", 20, 36,
new XmlParseException("Unknown namespace prefix used in file name 'cq:child1'", "/apps/cq:child1", 20, 36)));
@@ -261,8 +287,9 @@ public class DocumentViewParserValidatorTest {
Collection<ValidationMessage> messages = validator.validateJcrData(input, Paths.get("apps", "invalid","wrongtype.xml"), Paths.get(""), nodePathsAndLineNumbers);
ValidationExecutorTest.assertViolation(messages,
- new ValidationViolation(ValidationMessageSeverity.ERROR,
- "Could not parse FileVault Document View XML: unknown type: Invalid", Paths.get("apps/invalid/wrongtype.xml"), Paths.get(""), "/apps/invalid/wrongtype/somepath", 24, 6,
+ new ValidationViolation(DocumentViewParserValidatorFactory.ID,
+ ValidationMessageSeverity.ERROR,
+ "Could not parse FileVault Document View XML: unknown type: Invalid", Paths.get("apps/invalid/wrongtype.xml"), Paths.get(""), "/apps/invalid/wrongtype/somepath", 24, 6,
new XmlParseException(new IllegalArgumentException("unknown type: Invalid"), "/apps/invalid/wrongtype/somepath", 24, 6)));
}
}
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/ValidationExecutorTest.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/ValidationExecutorTest.java
index 02b5296..9e71119 100644
--- a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/ValidationExecutorTest.java
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/ValidationExecutorTest.java
@@ -295,6 +295,11 @@ public class ValidationExecutorTest {
}
}
+ /**
+ * Assert that all messages (after ones with level < WARN are filtered out) are equal to the given violations
+ * @param messages the actual messages (to be filtered)
+ * @param violations the expected violations (in the right order)
+ */
public static void assertViolation(Collection<? extends ValidationMessage> messages, ValidationMessage... violations) {
ValidationExecutorTest.assertViolation(messages, ValidationMessageSeverity.WARN, violations);
}
diff --git a/vault-validation/src/test/resources/simple-package/jcr_root/apps/_cq_content.xml b/vault-validation/src/test/resources/simple-package/jcr_root/apps/_cq_content.xml
new file mode 100644
index 0000000..fff1fdb
--- /dev/null
+++ b/vault-validation/src/test/resources/simple-package/jcr_root/apps/_cq_content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ jcr:primaryType="sling:Folder">
+</jcr:root>