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>