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 2024/01/23 11:16:18 UTC
(jackrabbit-filevault) 01/01: JCRVLT-733 Enforce type of uncovered ancestor files
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch feature/enforce-type-of-uncovered-ancestors
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
commit 26f7c0ef31fec6461fe421fc711a4c9aed62adde
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Tue Jan 23 12:16:12 2024 +0100
JCRVLT-733 Enforce type of uncovered ancestor files
WIP
---
.../vault-core-it-support-oak/.factorypath | 40 ++++++++
.../vault/fs/impl/io/AbstractArtifactHandler.java | 13 ++-
.../vault/fs/impl/io/DocViewImporter.java | 49 ++++++----
.../jackrabbit/vault/fs/io/ImportOptions.java | 13 +++
.../apache/jackrabbit/vault/fs/io/Importer.java | 3 +
.../vault/fs/nodetype/RepositoryNodeTypes.java | 61 ++++++++++++
.../nodetype/impl/AbstractRepositoryNodeTypes.java | 91 +++++++++++++++++
.../nodetype/impl/NodeTypesMBean.java} | 11 ++-
.../vault/fs/nodetype/impl/NodeTypesMBeanImpl.java | 54 +++++++++++
.../impl/PropertesFileRepositoryNodeTypesImpl.java | 108 +++++++++++++++++++++
.../impl/SimpleRepositoryNodeTypesImpl.java | 56 +++++++++++
.../vault/packaging/PackageProperties.java | 9 ++
.../vault/packaging/UncoveredAncestorHandling.java | 22 +++++
.../packaging/impl/PackagePropertiesImpl.java | 16 +++
.../jackrabbit/vault/packaging/package-info.java | 2 +-
15 files changed, 526 insertions(+), 22 deletions(-)
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-oak/.factorypath b/vault-core-it/vault-core-it-support/vault-core-it-support-oak/.factorypath
new file mode 100644
index 00000000..94a7a276
--- /dev/null
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-oak/.factorypath
@@ -0,0 +1,40 @@
+<factorypath>
+ <factorypathentry kind="VARJAR" id="M2_REPO/javax/jcr/jcr/2.0/jcr-2.0.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/commons-io/commons-io/2.7/commons-io-2.7.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-core/1.22.4/oak-core-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/osgi/org.osgi.service.component.annotations/1.4.0/org.osgi.service.component.annotations-1.4.0.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/osgi/org.osgi.service.metatype.annotations/1.3.0/org.osgi.service.metatype.annotations-1.3.0.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-jackrabbit-api/1.22.4/oak-jackrabbit-api-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-api/1.22.4/oak-api-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-core-spi/1.22.4/oak-core-spi-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-query-spi/1.22.4/oak-query-spi-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-security-spi/1.22.4/oak-security-spi-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-commons/1.22.4/oak-commons-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-blob-plugins/1.22.4/oak-blob-plugins-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/jackrabbit-data/2.20.1/jackrabbit-data-2.20.1.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jcl-over-slf4j/1.7.30/jcl-over-slf4j-1.7.30.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-blob/1.22.4/oak-blob-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/commons-codec/commons-codec/1.10/commons-codec-1.10.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/osgi/org.osgi.annotation/6.0.0/org.osgi.annotation-6.0.0.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-store-spi/1.22.4/oak-store-spi-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/com/google/guava/guava/15.0/guava-15.0.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/jackrabbit-jcr-commons/2.20.8/jackrabbit-jcr-commons-2.20.8.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-core/1.22.4/oak-core-1.22.4-tests.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-jcr/1.22.4/oak-jcr-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-segment-tar/1.22.4/oak-segment-tar-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-buffer/4.1.14.Final/netty-buffer-4.1.14.Final.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-codec/4.1.14.Final/netty-codec-4.1.14.Final.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-common/4.1.14.Final/netty-common-4.1.14.Final.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-handler/4.1.14.Final/netty-handler-4.1.14.Final.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-resolver/4.1.14.Final/netty-resolver-4.1.14.Final.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/io/netty/netty-transport/4.1.14.Final/netty-transport-4.1.14.Final.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/com/googlecode/concurrentlinkedhashmap/concurrentlinkedhashmap-lru/1.4.2/concurrentlinkedhashmap-lru-1.4.2.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-authorization-principalbased/1.22.4/oak-authorization-principalbased-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/jackrabbit/oak-authorization-cug/1.22.4/oak-authorization-cug-1.22.4.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/felix/org.apache.felix.converter/1.0.14/org.apache.felix.converter-1.0.14.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="VARJAR" id="M2_REPO/org/kohsuke/metainf-services/metainf-services/1.8/metainf-services-1.8.jar" enabled="true" runInBatchMode="false"/>
+ <factorypathentry kind="PLUGIN" id="org.eclipse.jst.ws.annotations.core" enabled="false" runInBatchMode="false"/>
+</factorypath>
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 89d66430..0723eb64 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
@@ -38,6 +38,7 @@ import org.apache.jackrabbit.vault.fs.io.DocViewParser.XmlParseException;
import org.apache.jackrabbit.vault.fs.io.ImportOptions;
import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
+import org.apache.jackrabbit.vault.packaging.UncoveredAncestorHandling;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,6 +68,8 @@ public abstract class AbstractArtifactHandler implements ArtifactHandler, Dumpab
*/
protected AccessControlHandling cugHandling = null;
+ protected UncoveredAncestorHandling uncoveredAncestorHandling = null;
+
/**
* acl management
*/
@@ -106,6 +109,14 @@ public abstract class AbstractArtifactHandler implements ArtifactHandler, Dumpab
this.cugHandling = cugHandling;
}
+ public UncoveredAncestorHandling getUncoveredAncestorHandling() {
+ return uncoveredAncestorHandling;
+ }
+
+ public void setUncoveredAncestorHandling(UncoveredAncestorHandling uncoveredAncestorHandling) {
+ this.uncoveredAncestorHandling = uncoveredAncestorHandling;
+ }
+
/**
* Returns the ACL management
* @return the ACL management
@@ -167,7 +178,7 @@ public abstract class AbstractArtifactHandler implements ArtifactHandler, Dumpab
}
protected ImportInfoImpl importDocView(InputSource source, Node parentNode, String rootNodeName, ArtifactSetImpl artifacts, boolean isStrict, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy) throws IOException, RepositoryException {
- DocViewImporter handler = new DocViewImporter(parentNode, rootNodeName, artifacts, wspFilter, idConflictPolicy, getAcHandling(), getCugHandling());
+ DocViewImporter handler = new DocViewImporter(parentNode, rootNodeName, artifacts, wspFilter, idConflictPolicy, getAcHandling(), getCugHandling(), getUncoveredAncestorHandling());
String rootNodePath = parentNode.getPath();
if (!rootNodePath.equals("/")) {
rootNodePath += "/";
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
index e6af027a..276d988c 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
@@ -82,6 +82,7 @@ import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
import org.apache.jackrabbit.vault.fs.spi.UserManagement;
import org.apache.jackrabbit.vault.fs.spi.impl.jcr20.JackrabbitUserManagement;
import org.apache.jackrabbit.vault.fs.spi.impl.jcr20.JcrNamespaceHelper;
+import org.apache.jackrabbit.vault.packaging.UncoveredAncestorHandling;
import org.apache.jackrabbit.vault.util.DocViewNode2;
import org.apache.jackrabbit.vault.util.DocViewProperty2;
import org.apache.jackrabbit.vault.util.EffectiveNodeType;
@@ -196,6 +197,8 @@ public class DocViewImporter implements DocViewParserHandler {
*/
private final @Nullable AccessControlHandling cugHandling;
+ private final @NotNull UncoveredAncestorHandling uncoveredAncestorHandling;
+
/**
* helper for namespace registration
*/
@@ -233,11 +236,12 @@ public class DocViewImporter implements DocViewParserHandler {
*/
public DocViewImporter(Node parentNode, String rootNodeName,
ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy) throws RepositoryException {
- this(parentNode, rootNodeName, artifacts, wspFilter, idConflictPolicy, AccessControlHandling.IGNORE, null);
+ this(parentNode, rootNodeName, artifacts, wspFilter, idConflictPolicy, AccessControlHandling.IGNORE, null, UncoveredAncestorHandling.CREATE);
}
public DocViewImporter(Node parentNode, String rootNodeName,
- ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy, AccessControlHandling aclHandling, AccessControlHandling cugHandling) throws RepositoryException {
+ ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy, AccessControlHandling aclHandling,
+ AccessControlHandling cugHandling, UncoveredAncestorHandling uncoveredAncestorHandling) throws RepositoryException {
this.filter = artifacts.getCoverage();
this.wspFilter = wspFilter;
this.rootDepth = parentNode.getDepth() + 1;
@@ -248,6 +252,7 @@ public class DocViewImporter implements DocViewParserHandler {
this.idConflictPolicy = idConflictPolicy;
this.aclHandling = aclHandling;
this.cugHandling = cugHandling;
+ this.uncoveredAncestorHandling = uncoveredAncestorHandling;
this.isSnsSupported = session.getRepository().
getDescriptorValue(Repository.NODE_TYPE_MANAGEMENT_SAME_NAME_SIBLINGS_SUPPORTED).getBoolean();
@@ -918,23 +923,33 @@ public class DocViewImporter implements DocViewParserHandler {
// create or update node
boolean isNew = existingNode == null;
if (isNew) {
- // workaround for bug in jcr2spi if mixins are empty
- if (!docViewNode.hasProperty(NameConstants.JCR_MIXINTYPES)) {
- preprocessedProperties.add(new DocViewProperty2(NameConstants.JCR_MIXINTYPES, Collections.emptyList(), PropertyType.NAME));
- }
- stack.ensureCheckedOut();
- existingNode = createNewNode(currentNode, docViewNode.cloneWithDifferentProperties(preprocessedProperties));
- if (existingNode.getDefinition() == null) {
- throw new RepositoryException("Child node not allowed.");
- }
- if (existingNode.isNodeType(JcrConstants.NT_RESOURCE)) {
- if (!existingNode.hasProperty(JcrConstants.JCR_DATA)) {
- importInfo.onMissing(existingNode.getPath() + "/" + JcrConstants.JCR_DATA);
+ // TODO: distinguish between covered and uncovered nodes
+ if (isIncluded(existingNode, existingNode.getDepth() - rootDepth)) {
+ // workaround for bug in jcr2spi if mixins are empty
+ if (!docViewNode.hasProperty(NameConstants.JCR_MIXINTYPES)) {
+ preprocessedProperties.add(new DocViewProperty2(NameConstants.JCR_MIXINTYPES, Collections.emptyList(), PropertyType.NAME));
+ }
+ stack.ensureCheckedOut();
+ existingNode = createNewNode(currentNode, docViewNode.cloneWithDifferentProperties(preprocessedProperties));
+ if (existingNode.getDefinition() == null) {
+ throw new RepositoryException("Child node not allowed.");
+ }
+ if (existingNode.isNodeType(JcrConstants.NT_RESOURCE)) {
+ if (!existingNode.hasProperty(JcrConstants.JCR_DATA)) {
+ importInfo.onMissing(existingNode.getPath() + "/" + JcrConstants.JCR_DATA);
+ }
+ } else if (isCheckedIn) {
+ // don't rely on isVersionable here, since SPI might not have this info yet
+ importInfo.registerToVersion(existingNode.getPath());
+ }
+ } else {
+ switch (uncoveredAncestorHandling) {
+ case VALIDATE:
+ throw new UnsupportedAncestorType("No ancestor node found at path " + path);
+ case CREATE:
+
}
- } else if (isCheckedIn) {
- // don't rely on isVersionable here, since SPI might not have this info yet
- importInfo.registerToVersion(existingNode.getPath());
}
importInfo.onCreated(existingNode.getPath());
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
index 36fde847..baca5abf 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ImportOptions.java
@@ -30,6 +30,7 @@ import org.apache.jackrabbit.vault.fs.api.PathMapping;
import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.packaging.DependencyHandling;
+import org.apache.jackrabbit.vault.packaging.UncoveredAncestorHandling;
import org.jetbrains.annotations.NotNull;
/**
@@ -57,6 +58,8 @@ public class ImportOptions {
private AccessControlHandling cugHandling = null;
+ private UncoveredAncestorHandling uncoveredAncestorHandling = null;
+
private ImportMode importMode;
private Pattern cndPattern = Pattern.compile("^/(apps|libs)/([^/]+/){1,2}nodetypes/.+\\.cnd$");
@@ -97,6 +100,7 @@ public class ImportOptions {
autoSaveThreshold = base.autoSaveThreshold;
acHandling = base.acHandling;
cugHandling = base.cugHandling;
+ uncoveredAncestorHandling = base.uncoveredAncestorHandling;
importMode = base.importMode;
cndPattern = base.cndPattern;
filter = base.filter;
@@ -124,6 +128,7 @@ public class ImportOptions {
ret.autoSaveThreshold = autoSaveThreshold;
ret.acHandling = acHandling;
ret.cugHandling = cugHandling;
+ ret.uncoveredAncestorHandling = uncoveredAncestorHandling;
ret.importMode = importMode;
ret.cndPattern = cndPattern;
ret.filter = filter;
@@ -287,6 +292,14 @@ public class ImportOptions {
this.cugHandling = cugHandling;
}
+ public UncoveredAncestorHandling getUncoveredAncestorHandling() {
+ return uncoveredAncestorHandling;
+ }
+
+ public void setUncoveredAncestorHandling(UncoveredAncestorHandling uncoveredAncestorHandling) {
+ this.uncoveredAncestorHandling = uncoveredAncestorHandling;
+ }
+
/**
* Defines the package installation should recursively install sub packages. Note that if this flag is enabled,
* the {@link org.apache.jackrabbit.vault.packaging.SubPackageHandling} configuration has no effect, as sub packages
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/Importer.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/Importer.java
index 8547e1a3..708bc091 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/Importer.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/Importer.java
@@ -463,10 +463,13 @@ public class Importer {
}
fileHandler.setAcHandling(opts.getAccessControlHandling());
fileHandler.setCugHandling(opts.getCugHandling());
+ fileHandler.setUncoveredAncestorHandling(opts.getUncoveredAncestorHandling());
genericHandler.setAcHandling(opts.getAccessControlHandling());
genericHandler.setCugHandling(opts.getCugHandling());
+ genericHandler.setUncoveredAncestorHandling(opts.getUncoveredAncestorHandling());
folderHandler.setAcHandling(opts.getAccessControlHandling());
folderHandler.setCugHandling(opts.getCugHandling());
+ folderHandler.setUncoveredAncestorHandling(opts.getUncoveredAncestorHandling());
folderHandler.setOverwritePrimaryTypesOfFolders(opts.overwritePrimaryTypesOfFolders(overwritePrimaryTypesOfFoldersByDefault));
filter = opts.getFilter();
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/RepositoryNodeTypes.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/RepositoryNodeTypes.java
new file mode 100644
index 00000000..875fb35f
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/RepositoryNodeTypes.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.vault.fs.nodetype;
+
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.vault.packaging.UncoveredAncestorHandling;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+// TODO: a better name is needed
+public interface RepositoryNodeTypes {
+
+ /**
+ * @param path the repository path
+ * @return {@code true} if node types for the given path are known, {@code false} otherwise
+ */
+ boolean containsNodeTypes(String path);
+
+ /**
+ * Retrieves the primary node type either expected or set at a certain JCR path.
+ * @param path the repository path for which to retrieve the node type
+ *
+ * @return the primary node type for the given path, may be {@code null}
+ */
+ @Nullable String getPrimaryNodeType(String path);
+
+ /**
+ * Retrieves the (non-effective) mixin types either expected or set at a certain JCR path.
+ * This is expected to be one primary type (the first one) and arbitrarily many mixin types.
+ * @param path the repository path for which to retrieve the node types (non-effective)
+ *
+ * @return the (immutable) set of node types for the given path, may be empty if not known or if no mixins are set/expected at the given path
+ */
+ @NotNull Set<String> getMixinNodeTypes(String path);
+
+ void enforce(UncoveredAncestorHandling enforcementType, String path, Session session) throws RepositoryException;
+
+ public static final class IncompatibleNodeTypeException extends RepositoryException {
+
+ private static final long serialVersionUID = -1720934303752421933L;
+
+ }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/AbstractRepositoryNodeTypes.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/AbstractRepositoryNodeTypes.java
new file mode 100644
index 00000000..8d71d0d3
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/AbstractRepositoryNodeTypes.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.vault.fs.nodetype.impl;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.vault.fs.nodetype.RepositoryNodeTypes;
+import org.apache.jackrabbit.vault.packaging.UncoveredAncestorHandling;
+import org.apache.jackrabbit.vault.util.PathUtil;
+
+public abstract class AbstractRepositoryNodeTypes implements RepositoryNodeTypes {
+
+ public AbstractRepositoryNodeTypes() {
+ super();
+ }
+
+ @Override
+ public void enforce(UncoveredAncestorHandling enforcementType, String path, Session session) {
+ switch (enforcementType) {
+ case IGNORE:
+ return;
+ case CREATEORUPDATE:
+
+ case CREATE:
+ break;
+ case VALIDATE:
+ break;
+ default:
+ break;
+ }
+ }
+
+ void validateNodeWithTypes(Node node) throws RepositoryException {
+ final String primaryNodeType = getPrimaryNodeType(node.getPath());
+ if (primaryNodeType.isEmpty()) {
+ // this is fine, as no expectation on node type
+ return;
+ }
+ if (node.getPrimaryNodeType().isNodeType(primaryNodeType)) {
+ throw new RepositoryException("Invalid node type at path " + node.getPath() + ": " + node.getPrimaryNodeType());
+ }
+ for (String mixinType : getMixinNodeTypes(node.getPath())) {
+ if (!node.isNodeType(mixinType)) {
+ throw new RepositoryException("Node at " + node.getPath() + " does not have mixin " + mixinType);
+ }
+ }
+ }
+
+ Node createNodeWithTypes(Node parent, String name) throws RepositoryException {
+ // what happens if this is empty?
+ final Node node;
+ String path = PathUtil.getPath(parent, name);
+ final String primaryNodeType = getPrimaryNodeType(path);
+ if (primaryNodeType.isEmpty()) {
+ node = parent.addNode(name);
+ } else {
+ node = parent.addNode(name, primaryNodeType);
+ for (String mixinType : getMixinNodeTypes(path)) {
+ node.addMixin(mixinType);
+ }
+ }
+ return node;
+ }
+
+ void updateNodeWithTypes(Node node) throws RepositoryException {
+ final String primaryNodeType = getPrimaryNodeType(node.getPath());
+ if (!primaryNodeType.isEmpty()) {
+ node.setPrimaryType(primaryNodeType);
+ for (String mixinType : getMixinNodeTypes(node.getPath())) {
+ node.addMixin(mixinType);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/NodeTypesMBean.java
similarity index 75%
copy from vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
copy to vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/NodeTypesMBean.java
index aa1413be..96650827 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/NodeTypesMBean.java
@@ -14,8 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.apache.jackrabbit.vault.fs.nodetype.impl;
-@Version("2.15.0")
-package org.apache.jackrabbit.vault.packaging;
+import java.io.IOException;
-import org.osgi.annotation.versioning.Version;
+import javax.jcr.RepositoryException;
+
+public interface NodeTypesMBean {
+
+ String getNodeTypePropertiesFileForPaths(String... paths) throws RepositoryException, IOException;
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/NodeTypesMBeanImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/NodeTypesMBeanImpl.java
new file mode 100644
index 00000000..efe88990
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/NodeTypesMBeanImpl.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.vault.fs.nodetype.impl;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.management.DynamicMBean;
+import javax.management.NotCompliantMBeanException;
+import javax.management.StandardMBean;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component(service = DynamicMBean.class, immediate = true, property = {
+ "service.vendor=The Apache Software Foundation",
+ "jmx.objectname=org.apache.jackrabbit.vault.fs.nodetype:type=manager"
+})
+public class NodeTypesMBeanImpl extends StandardMBean implements NodeTypesMBean {
+
+ @Reference
+ private SlingRepository slingRepository;
+
+ public NodeTypesMBeanImpl() throws NotCompliantMBeanException {
+ super(NodeTypesMBean.class);
+ }
+
+ @Override
+ public String getNodeTypePropertiesFileForPaths(String... paths) throws RepositoryException, IOException {
+ final Session session = slingRepository.loginAdministrative(null);
+ try (StringWriter writer = new StringWriter()) {
+ PropertesFileRepositoryNodeTypesImpl.createPropertiesFromSession(writer, session, -1, paths);
+ return writer.toString();
+ }
+ }
+
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/PropertesFileRepositoryNodeTypesImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/PropertesFileRepositoryNodeTypesImpl.java
new file mode 100644
index 00000000..2e3b34bd
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/PropertesFileRepositoryNodeTypesImpl.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.vault.fs.nodetype.impl;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.util.TraversingItemVisitor;
+
+import org.apache.jackrabbit.vault.fs.nodetype.RepositoryNodeTypes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Holds information about primary and mixin types for certain JCR paths.
+ * The data is read from a {@link Properties} file with each entry having the format
+ * <pre><path>=<primary type>{,<mixinType>}</pre>
+ */
+public class PropertesFileRepositoryNodeTypesImpl extends AbstractRepositoryNodeTypes implements RepositoryNodeTypes {
+
+ private final Properties properties;
+
+ protected PropertesFileRepositoryNodeTypesImpl(Reader reader) throws IOException {
+ super();
+ this.properties = new Properties();
+ this.properties.load(reader);
+ }
+
+ public static void createPropertiesFromSession(Writer writer, Session session, int maxDepth, String... relevantRoots) throws RepositoryException, IOException {
+ Properties properties = new Properties();
+ for (String relevantRoot : relevantRoots) {
+ // traverse repository up till maxDepth
+ Node node = session.getNode(relevantRoot);
+ node.accept(new NodeTypeCollectorVisitor(properties));
+ }
+ properties.store(writer, null);
+ }
+
+ private static final class NodeTypeCollectorVisitor extends TraversingItemVisitor.Default {
+
+ private final Properties properties;
+ public NodeTypeCollectorVisitor(Properties properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ protected void entering(Node node, int level) throws RepositoryException {
+ StringBuilder types = new StringBuilder(node.getPrimaryNodeType().getName());
+ Arrays.stream(node.getMixinNodeTypes()).forEach(mt -> types.append(",").append(mt.getName()));
+ properties.setProperty(node.getPath(), types.toString());
+ }
+ }
+
+ @Override
+ public boolean containsNodeTypes(String path) {
+ return getPrimaryNodeType(path) != null;
+ }
+
+ @Override
+ public @Nullable String getPrimaryNodeType(String path) {
+ String allTypes = properties.getProperty(path, "");
+ if (allTypes.isEmpty()) {
+ return null;
+ } else {
+ int pos = allTypes.indexOf(',');
+ if (pos == -1) {
+ pos = allTypes.length();
+ }
+ return allTypes.substring(0, pos);
+ }
+ }
+
+ @Override
+ public @NotNull Set<String> getMixinNodeTypes(String path) {
+ List<String> allTypes = Arrays.asList(properties.getProperty(path, "").split(","));
+ if (allTypes.size() > 1) {
+ return Collections.unmodifiableSet(new HashSet<>(allTypes.subList(1, allTypes.size())));
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/SimpleRepositoryNodeTypesImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/SimpleRepositoryNodeTypesImpl.java
new file mode 100644
index 00000000..a792b7f7
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/nodetype/impl/SimpleRepositoryNodeTypesImpl.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package org.apache.jackrabbit.vault.fs.nodetype.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class SimpleRepositoryNodeTypesImpl extends AbstractRepositoryNodeTypes {
+
+ private final String defaultPrimaryType;
+ private final Collection<String> validPaths;
+
+ public SimpleRepositoryNodeTypesImpl(String defaultPrimaryType, String... validPaths) {
+ super();
+ this.defaultPrimaryType = defaultPrimaryType;
+ this.validPaths = Arrays.asList(validPaths);
+ }
+
+ @Override
+ public boolean containsNodeTypes(String path) {
+ return validPaths.contains(path);
+ }
+
+ @Override
+ public @Nullable String getPrimaryNodeType(String path) {
+ if (validPaths.contains(path)) {
+ return defaultPrimaryType;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public @NotNull Set<String> getMixinNodeTypes(String path) {
+ return Collections.emptySet();
+ }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
index a8f4f31e..cd0b91eb 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
@@ -147,6 +147,8 @@ public interface PackageProperties {
*/
String NAME_SUB_PACKAGE_HANDLING = "subPackageHandling";
+ String NAME_UNCOVERED_ANCESTOR_HANDLING = "uncoveredAncestorHandling";
+
/**
* Name of the flag that defines if the package is supposed to contains/overwrite Oak index definitions.
* @since 3.2.10
@@ -308,6 +310,13 @@ public interface PackageProperties {
*/
SubPackageHandling getSubPackageHandling();
+ /**
+ *
+ * @return the handling of uncovered ancestor nodes during import (never {@code null})
+ * @since 3.8 (package version 2.16)
+ */
+ @NotNull UncoveredAncestorHandling getUncoveredAncestorHandling();
+
/**
* Returns the date property with the given name or {@code null} if it does not exist or if the value cannot be
* converted to a date.
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/UncoveredAncestorHandling.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/UncoveredAncestorHandling.java
new file mode 100644
index 00000000..66ea6026
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/UncoveredAncestorHandling.java
@@ -0,0 +1,22 @@
+package org.apache.jackrabbit.vault.packaging;
+
+public enum UncoveredAncestorHandling {
+ /**
+ * No enforcement at all
+ */
+ IGNORE,
+ /**
+ * Fails with an exception if the node doesn't exist yet otherwise fails only in case the effective node type doesn't contain the given primary and mixin types
+ */
+ VALIDATE,
+ /**
+ * Creates the node with the given types if it doesn't exist yet otherwise fails with fails only with an exception in case the effective node type doesn't contain the given primary and mixin types.
+ * This is the default handling.
+ */
+ CREATE,
+ /**
+ * Creates the node with the given types if it doesn't exist yet, otherwise updates the type information accordingly (addition only for mixin types, i.e. no mixins are removed).
+ * May throw an exception in case existing properties or child nodes are not compliant with the modified effective node types.
+ */
+ CREATEORUPDATE
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java
index 15394a35..dab0a6f5 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagePropertiesImpl.java
@@ -36,6 +36,7 @@ import org.apache.jackrabbit.vault.packaging.PackageId;
import org.apache.jackrabbit.vault.packaging.PackageProperties;
import org.apache.jackrabbit.vault.packaging.PackageType;
import org.apache.jackrabbit.vault.packaging.SubPackageHandling;
+import org.apache.jackrabbit.vault.packaging.UncoveredAncestorHandling;
import org.apache.jackrabbit.vault.packaging.VaultPackage;
import org.apache.jackrabbit.util.Text;
import org.jetbrains.annotations.Nullable;
@@ -169,6 +170,21 @@ public abstract class PackagePropertiesImpl implements PackageProperties {
return SubPackageHandling.fromString(getProperty(NAME_SUB_PACKAGE_HANDLING));
}
+ @Override
+ public UncoveredAncestorHandling getUncoveredAncestorHandling() {
+ String ancestorHandling = getProperty(NAME_UNCOVERED_ANCESTOR_HANDLING);
+ if (ancestorHandling == null) {
+ return UncoveredAncestorHandling.CREATE;
+ } else {
+ try {
+ return UncoveredAncestorHandling.valueOf(ancestorHandling.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ log.warn("invalid access control handling configured: {}", ancestorHandling);
+ return UncoveredAncestorHandling.CREATE;
+ }
+ }
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
index aa1413be..ae88c4a9 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-@Version("2.15.0")
+@Version("2.16.0")
package org.apache.jackrabbit.vault.packaging;
import org.osgi.annotation.versioning.Version;