com.adobe.acs.commons.contentsync.ContentReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of acs-aem-commons-bundle Show documentation
Show all versions of acs-aem-commons-bundle Show documentation
Main ACS AEM Commons OSGi Bundle. Includes commons utilities.
/*-
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2013 - 2022 Adobe
* %%
* Licensed 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.
* #L%
*/
package com.adobe.acs.commons.contentsync;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonString;
import javax.json.JsonValue;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
public class ContentReader {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
static final String BINARY_DATA_PLACEHOLDER = "0";
private final NodeTypeManager nodeTypeManager;
private final Collection knownPropertyPrefixes;
public ContentReader(Session session) throws RepositoryException {
Workspace workspace = session.getWorkspace();
knownPropertyPrefixes = new HashSet<>(Arrays.asList(workspace.getNamespaceRegistry().getPrefixes()));
nodeTypeManager = workspace.getNodeTypeManager();
}
/**
* Recursive sanitize the give JCR node and remove protected properties
*
* @param node json node representing a JCR node
* @return sanitized json
* @see #getProtectedProperties(JsonObject)
*/
public JsonObject sanitize(JsonObject node) throws RepositoryException {
JsonObjectBuilder out = Json.createObjectBuilder();
sanitize(node, out);
return out.build();
}
private void sanitize(JsonObject node, JsonObjectBuilder out) throws RepositoryException {
Collection sanitizedProperties = getProtectedProperties(node);
for (Map.Entry field : node.entrySet()) {
String name = field.getKey();
int colonIdx = name.indexOf(':');
if (colonIdx > 0) {
// sanitize unknown namespaces. These can come, for example, from asset metadata
String prefix = name.substring(0, colonIdx);
if (!knownPropertyPrefixes.contains(prefix)) {
log.trace("skipping protected property: {}", name);
continue;
}
}
// sanitize protected properties
if (sanitizedProperties.contains(name)) {
log.trace("skipping unknown namespace: {}", name);
continue;
}
JsonValue value = field.getValue();
switch (value.getValueType()) {
case OBJECT:
JsonObjectBuilder obj = Json.createObjectBuilder();
sanitize((JsonObject) value, obj);
out.add(name, obj);
break;
case ARRAY:
JsonArray array = (JsonArray) value;
out.add(name, array);
break;
default:
if (colonIdx == 0) {
// Leading colon in Sling GET Servlet JSON designates binary data, e.g. :jcr:data
// Put the real property instead (without a leading colon) and set a dummy value
out.add(name.substring(1), BINARY_DATA_PLACEHOLDER);
} else {
out.add(name, value);
}
break;
}
}
}
/**
* Collect protected properties of a given JCR node (non-recursively).
* The list of protected properties consists of:
* - properties protected by node's primary type
* - properties protected by node's mixins
*
* For example, if a cq:Page node does not have any mixins applied this method would return
*
* ["jcr:created", "jcr:createdBy"]
*
*
* If cq:Page is versionable, i.e. has the "mix:versionable" mixin type, then this method would return
* properties protected by the primary type (cq:Page ) and the mixin (mix:versionable) and the list would be
*
* ["jcr:created", "jcr:createdBy", "jcr:versionHistory", "jcr:baseVersion", "jcr:predecessors",
* "jcr:mergeFailed", "jcr:activity", "jcr:configuration", "jcr:isCheckedOut", "jcr:uuid" ]
*
*
* @param node json representing a JCR node
* @return the list of protected properties
*/
public List getProtectedProperties(JsonObject node) throws RepositoryException {
Collection ignored = new HashSet<>(Arrays.asList(JCR_PRIMARYTYPE, JCR_MIXINTYPES));
List props = new ArrayList<>();
props.add("rep:policy"); // ACLs are not importable
List checkTypes = new ArrayList<>();
String primaryType = node.getString(JCR_PRIMARYTYPE);
checkTypes.add(primaryType);
JsonArray mixins = node.getJsonArray(JCR_MIXINTYPES);
if (mixins != null) {
for (JsonValue item : mixins) {
checkTypes.add(((JsonString) item).getString());
}
}
for (String typeName : checkTypes) {
NodeType nodeType = nodeTypeManager.getNodeType(typeName);
for (PropertyDefinition definition : nodeType.getPropertyDefinitions()) {
if (definition.isProtected() && !ignored.contains(definition.getName())) {
props.add(definition.getName());
}
}
}
return props;
}
private void collectBinaryProperties(JsonObject node, String parent, List binaryProperties) {
for (Map.Entry field : node.entrySet()) {
String name = field.getKey();
JsonValue value = field.getValue();
switch (value.getValueType()) {
case OBJECT:
collectBinaryProperties((JsonObject) value, parent + "/" + name, binaryProperties);
break;
case NUMBER:
// leading colon in Sling GET Servlet JSON and a numeric value designate binary data
if (name.startsWith(":")) {
String propPath = parent + "/" + name.substring(1);
binaryProperties.add(propPath);
}
break;
default:
break;
}
}
}
/**
* Recursively collect binary properties from a given json node.
*
* For example, if node
represents a cq:Page object with inline images,
* the output would look like
*
*
* [
* /jcr:content/image/file/jcr:content/jcr:data,
* /jcr:content/image/file/jcr:content/dam:thumbnails/dam:thumbnail_480.png/jcr:content/jcr:data,
* /jcr:content/image/file/jcr:content/dam:thumbnails/dam:thumbnail_60.png/jcr:content/jcr:data,
* /jcr:content/image/file/jcr:content/dam:thumbnails/dam:thumbnail_300.png/jcr:content/jcr:data,
* /jcr:content/image/file/jcr:content/dam:thumbnails/dam:thumbnail_48.png/jcr:content/jcr:data
* ]
*
*
* @param node json representing a JCR node
* @return list of property paths relative to the json root
*/
public List collectBinaryProperties(JsonObject node) {
List binaryProperties = new ArrayList<>();
collectBinaryProperties(node, "", binaryProperties);
return binaryProperties;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy