blob: 03650d2a038a80b75f0030f18bd92da71d788104 [file] [log] [blame]
/*
* 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.sling.jcr.contentloader.internal;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
import javax.jcr.Binary;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.version.VersionManager;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.util.ISO8601;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.jcr.contentloader.ContentCreator;
import org.apache.sling.jcr.contentloader.ContentImportListener;
import org.apache.sling.jcr.contentloader.ContentReader;
import org.apache.sling.jcr.contentloader.ImportOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The <code>ContentLoader</code> creates the nodes and properties.
*
* @since 2.0.4
*/
public class DefaultContentCreator implements ContentCreator {
final Logger log = LoggerFactory.getLogger(DefaultContentCreator.class);
private ImportOptions configuration;
private final Stack<Node> parentNodeStack = new Stack<Node>();
/**
* The list of versionables.
*/
private final List<Node> versionables = new ArrayList<Node>();
/**
* Delayed references during content loading for the reference property.
*/
private final Map<String, List<String>> delayedReferences = new HashMap<String, List<String>>();
private final Map<String, String[]> delayedMultipleReferences = new HashMap<String, String[]>();
private String defaultName;
private Node createdRootNode;
private boolean isParentNodeImport;
private boolean ignoreOverwriteFlag = false;
// default content type for createFile()
private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
/**
* Helper class to get the mime type of a file.
*/
private final ContentHelper contentHelper;
/**
* List of active content readers mapped by extension.
*/
private Map<String, ContentReader> contentReaders;
/**
* Optional list of created nodes (for uninstall)
*/
private List<String> createdNodes;
/**
* Optional listener to get notified about changes
*/
private ContentImportListener importListener;
/**
* A one time use seed to randomize the user location.
*/
private static final long INSTANCE_SEED = System.currentTimeMillis();
/**
* The number of levels folder used to store a user, could be a configuration option.
*/
private static final int STORAGE_LEVELS = 3;
/**
* Constructor.
*
* @param contentHelper Helper class to get the mime type of a file
*/
public DefaultContentCreator(ContentHelper contentHelper) {
this.contentHelper = contentHelper;
}
/**
* Initialize this component.
*
* @param pathEntry The configuration for this import.
* @param defaultContentReaders List of all content readers.
* @param createdNodes Optional list to store new nodes (for uninstall)
*/
public void init(final ImportOptions pathEntry, final Map<String, ContentReader> defaultContentReaders, final List<String> createdNodes, final ContentImportListener importListener) {
this.configuration = pathEntry;
// create list of allowed content readers
this.contentReaders = new HashMap<String, ContentReader>();
final Iterator<Map.Entry<String, ContentReader>> entryIter = defaultContentReaders.entrySet().iterator();
while (entryIter.hasNext()) {
final Map.Entry<String, ContentReader> current = entryIter.next();
if (!configuration.isIgnoredImportProvider(current.getKey())) {
contentReaders.put(current.getKey(), current.getValue());
}
}
this.createdNodes = createdNodes;
this.importListener = importListener;
}
/**
* If the defaultName is null, we are in PARENT_NODE import mode.
*
* @param parentNode
* @param defaultName
*/
public void prepareParsing(final Node parentNode, final String defaultName) {
this.parentNodeStack.clear();
this.parentNodeStack.push(parentNode);
this.defaultName = defaultName;
isParentNodeImport = defaultName == null;
this.createdRootNode = null;
}
/**
* Get the list of versionable nodes.
*/
public List<Node> getVersionables() {
return this.versionables;
}
/**
* Clear the content loader.
*/
public void clear() {
this.versionables.clear();
}
/**
* Set the ignore overwrite flag.
*
* @param flag
*/
public void setIgnoreOverwriteFlag(boolean flag) {
this.ignoreOverwriteFlag = flag;
}
/**
* Get the created root node.
*/
public Node getCreatedRootNode() {
return this.createdRootNode;
}
/**
* Get all active content readers.
*
* @return A map of readers
*/
public Map<String, ContentReader> getContentReaders() {
return this.contentReaders;
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#createNode(java.lang.String, java.lang.String, java.lang.String[])
*/
public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException {
final Node parentNode = this.parentNodeStack.peek();
boolean isParentImport = (name == null && isParentNodeImport);
if (name == null) {
if (this.parentNodeStack.size() > 1) {
throw new RepositoryException("Node needs to have a name.");
}
name = this.defaultName;
}
// if we are in parent node import mode, we don't create the root top level node!
if (!isParentImport || this.parentNodeStack.size() > 1) {
// if node already exists but should be overwritten, delete it
if (!this.ignoreOverwriteFlag && this.configuration.isOverwrite() && parentNode.hasNode(name)) {
checkoutIfNecessary(parentNode);
parentNode.getNode(name).remove();
}
// ensure repository node
Node node;
if (parentNode.hasNode(name)) {
// use existing node
node = parentNode.getNode(name);
} else if (primaryNodeType == null) {
// no explicit node type, use repository default
checkoutIfNecessary(parentNode);
node = parentNode.addNode(name);
addNodeToCreatedList(node);
if (this.importListener != null) {
this.importListener.onCreate(node.getPath());
}
} else {
// explicit primary node type
checkoutIfNecessary(parentNode);
node = parentNode.addNode(name, primaryNodeType);
addNodeToCreatedList(node);
if (this.importListener != null) {
this.importListener.onCreate(node.getPath());
}
}
// amend mixin node types
if (mixinNodeTypes != null) {
for (final String mixin : mixinNodeTypes) {
if (!node.isNodeType(mixin)) {
node.addMixin(mixin);
}
}
}
// check if node is versionable
final boolean addToVersionables = this.configuration.isCheckin() && node.isNodeType("mix:versionable");
if (addToVersionables) {
this.versionables.add(node);
}
this.parentNodeStack.push(node);
if (this.createdRootNode == null) {
this.createdRootNode = node;
}
}
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, int, java.lang.String)
*/
public void createProperty(String name, int propertyType, String value) throws RepositoryException {
final Node node = this.parentNodeStack.peek();
// check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case
if (node.hasProperty(name) && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) {
return;
}
if (propertyType == PropertyType.REFERENCE) {
// need to resolve the reference
String propPath = node.getPath() + "/" + name;
String uuid = getUUID(node.getSession(), propPath, getAbsPath(node, value));
if (uuid != null) {
checkoutIfNecessary(node);
node.setProperty(name, uuid, propertyType);
if (this.importListener != null) {
this.importListener.onCreate(node.getProperty(name).getPath());
}
}
} else if ("jcr:isCheckedOut".equals(name)) {
// don't try to write the property but record its state
// for later checkin if set to false
final boolean checkedout = Boolean.valueOf(value);
if (!checkedout) {
if (!this.versionables.contains(node)) {
this.versionables.add(node);
}
}
} else if (propertyType == PropertyType.DATE) {
checkoutIfNecessary(node);
node.setProperty(name, ISO8601.parse(value));
if (this.importListener != null) {
this.importListener.onCreate(node.getProperty(name).getPath());
}
} else {
checkoutIfNecessary(node);
if (propertyType == PropertyType.UNDEFINED) {
node.setProperty(name, value);
} else {
node.setProperty(name, value, propertyType);
}
if (this.importListener != null) {
this.importListener.onCreate(node.getProperty(name).getPath());
}
}
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, int, java.lang.String[])
*/
public void createProperty(String name, int propertyType, String[] values) throws RepositoryException {
final Node node = this.parentNodeStack.peek();
// check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case
if (node.hasProperty(name) && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) {
return;
}
if (propertyType == PropertyType.REFERENCE) {
String propPath = node.getPath() + "/" + name;
boolean hasAll = true;
String[] uuids = new String[values.length];
String[] uuidOrPaths = new String[values.length];
for (int i = 0; i < values.length; i++) {
uuids[i] = getUUID(node.getSession(), propPath, getAbsPath(node, values[i]));
uuidOrPaths[i] = uuids[i] != null ? uuids[i] : getAbsPath(node, values[i]);
if (uuids[i] == null) hasAll = false;
}
checkoutIfNecessary(node);
node.setProperty(name, uuids, propertyType);
if (this.importListener != null) {
this.importListener.onCreate(node.getProperty(name).getPath());
}
if (!hasAll) {
delayedMultipleReferences.put(propPath, uuidOrPaths);
}
} else if (propertyType == PropertyType.DATE) {
checkoutIfNecessary(node);
// This modification is to remove the colon in the JSON Timezone
ValueFactory valueFactory = node.getSession().getValueFactory();
Value[] jcrValues = new Value[values.length];
for (int i = 0; i < values.length; i++) {
jcrValues[i] = valueFactory.createValue(ISO8601.parse(values[i]));
}
node.setProperty(name, jcrValues, propertyType);
if (this.importListener != null) {
this.importListener.onCreate(node.getProperty(name).getPath());
}
} else {
checkoutIfNecessary(node);
if (propertyType == PropertyType.UNDEFINED) {
node.setProperty(name, values);
} else {
node.setProperty(name, values, propertyType);
}
if (this.importListener != null) {
this.importListener.onCreate(node.getProperty(name).getPath());
}
}
}
protected Value createValue(final ValueFactory factory, Object value) throws RepositoryException {
if (value == null) {
return null;
}
if (value instanceof Long) {
return factory.createValue((Long) value);
} else if (value instanceof Date) {
final Calendar c = Calendar.getInstance();
c.setTime((Date) value);
return factory.createValue(c);
} else if (value instanceof Calendar) {
return factory.createValue((Calendar) value);
} else if (value instanceof Double) {
return factory.createValue((Double) value);
} else if (value instanceof Boolean) {
return factory.createValue((Boolean) value);
} else if (value instanceof InputStream) {
Binary binary = factory.createBinary((InputStream)value);
return factory.createValue(binary);
} else {
return factory.createValue(value.toString());
}
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, java.lang.Object)
*/
public void createProperty(String name, Object value) throws RepositoryException {
createProperty(name, value, false);
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, java.lang.Object[])
*/
public void createProperty(String name, Object[] values) throws RepositoryException {
createProperty(name, values, false);
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#finishNode()
*/
public void finishNode() throws RepositoryException {
final Node node = this.parentNodeStack.pop();
// resolve REFERENCE property values pointing to this node
resolveReferences(node);
}
private void addNodeToCreatedList(Node node) throws RepositoryException {
if (this.createdNodes != null) {
this.createdNodes.add(node.getSession().getWorkspace().getName() + ":" + node.getPath());
}
}
private String getAbsPath(Node node, String path) throws RepositoryException {
if (path.startsWith("/")) {
return path;
}
while (path.startsWith("../")) {
path = path.substring(3);
node = node.getParent();
}
while (path.startsWith("./")) {
path = path.substring(2);
}
return node.getPath() + "/" + path;
}
private String getUUID(Session session, String propPath, String referencePath) throws RepositoryException {
if (session.itemExists(referencePath)) {
Item item = session.getItem(referencePath);
if (item.isNode()) {
Node refNode = (Node) item;
if (refNode.isNodeType("mix:referenceable")) {
return refNode.getIdentifier();
}
}
} else {
// not existing yet, keep for delayed setting
List<String> current = delayedReferences.get(referencePath);
if (current == null) {
current = new ArrayList<String>();
delayedReferences.put(referencePath, current);
}
current.add(propPath);
}
// no UUID found
return null;
}
private void resolveReferences(Node node) throws RepositoryException {
List<String> props = delayedReferences.remove(node.getPath());
if (props == null || props.size() == 0) {
return;
}
// check whether we can set at all
if (!node.isNodeType("mix:referenceable")) {
return;
}
Session session = node.getSession();
String uuid = node.getIdentifier();
for (String property : props) {
String name = getName(property);
Node parentNode = getParentNode(session, property);
if (parentNode != null) {
checkoutIfNecessary(parentNode);
if (parentNode.hasProperty(name) && parentNode.getProperty(name).getDefinition().isMultiple()) {
boolean hasAll = true;
String[] uuidOrPaths = delayedMultipleReferences.get(property);
String[] uuids = new String[uuidOrPaths.length];
for (int i = 0; i < uuidOrPaths.length; i++) {
// is the reference still a path
if (uuidOrPaths[i].startsWith("/")) {
if (uuidOrPaths[i].equals(node.getPath())) {
uuidOrPaths[i] = uuid;
uuids[i] = uuid;
} else {
uuids[i] = null;
hasAll = false;
}
} else {
uuids[i] = uuidOrPaths[i];
}
}
parentNode.setProperty(name, uuids, PropertyType.REFERENCE);
if (this.importListener != null) {
this.importListener.onCreate(parentNode.getProperty(name).getPath());
}
if (hasAll) {
delayedMultipleReferences.remove(property);
}
} else {
parentNode.setProperty(name, uuid, PropertyType.REFERENCE);
if (this.importListener != null) {
this.importListener.onCreate(parentNode.getProperty(name).getPath());
}
}
}
}
}
/**
* Gets the name part of the <code>path</code>. The name is
* the part of the path after the last slash (or the complete path if no
* slash is contained).
*
* @param path The path from which to extract the name part.
* @return The name part.
*/
private String getName(String path) {
int lastSlash = path.lastIndexOf('/');
String name = (lastSlash < 0) ? path : path.substring(lastSlash + 1);
return name;
}
private Node getParentNode(Session session, String path) throws RepositoryException {
int lastSlash = path.lastIndexOf('/');
// not an absolute path, cannot find parent
if (lastSlash < 0) {
return null;
}
// node below root
if (lastSlash == 0) {
return session.getRootNode();
}
// item in the hierarchy
path = path.substring(0, lastSlash);
if (!session.itemExists(path)) {
return null;
}
Item item = session.getItem(path);
return (item.isNode()) ? (Node) item : null;
}
private void createProperty(String name, Object value, boolean overwriteExisting) throws RepositoryException {
final Node node = this.parentNodeStack.peek();
// check if the property already exists, don't overwrite it in this case
if (node.hasProperty(name) && !node.getProperty(name).isNew() && !overwriteExisting) {
return;
}
if (value == null) {
if (node.hasProperty(name)) {
checkoutIfNecessary(node);
String propPath = node.getProperty(name).getPath();
node.getProperty(name).remove();
if (this.importListener != null) {
this.importListener.onDelete(propPath);
}
}
} else {
checkoutIfNecessary(node);
final Value jcrValue = this.createValue(node.getSession().getValueFactory(), value);
node.setProperty(name, jcrValue);
if (this.importListener != null) {
this.importListener.onModify(node.getProperty(name).getPath());
}
}
}
private void createProperty(String name, Object[] values, boolean overwriteExisting) throws RepositoryException {
final Node node = this.parentNodeStack.peek();
// check if the property already exists, don't overwrite it in this case
if (node.hasProperty(name) && !node.getProperty(name).isNew() && !overwriteExisting) {
return;
}
if (values == null || values.length == 0) {
if (node.hasProperty(name)) {
checkoutIfNecessary(node);
String propPath = node.getProperty(name).getPath();
node.getProperty(name).remove();
if (this.importListener != null) {
this.importListener.onDelete(propPath);
}
}
} else {
checkoutIfNecessary(node);
final Value[] jcrValues = new Value[values.length];
for (int i = 0; i < values.length; i++) {
jcrValues[i] = this.createValue(node.getSession().getValueFactory(), values[i]);
}
node.setProperty(name, jcrValues);
if (this.importListener != null) {
this.importListener.onModify(node.getProperty(name).getPath());
}
}
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#createFileAndResourceNode(java.lang.String, java.io.InputStream, java.lang.String, long)
*/
public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException {
int lastSlash = name.lastIndexOf('/');
name = (lastSlash < 0) ? name : name.substring(lastSlash + 1);
final Node parentNode = this.parentNodeStack.peek();
// if node already exists but should be overwritten, delete it
if (parentNode.hasNode(name)) {
this.parentNodeStack.push(parentNode.getNode(name));
Node contentNode = parentNode.getNode(name).getNode("jcr:content");
this.parentNodeStack.push(contentNode);
long nodeLastModified = 0L;
if (contentNode.hasProperty("jcr:lastModified")) {
nodeLastModified = contentNode.getProperty("jcr:lastModified").getDate().getTimeInMillis();
}
if (!this.configuration.isOverwrite() && nodeLastModified >= lastModified) {
return;
}
log.info("Updating {} lastModified:{} New Content LastModified:{}", new Object[]{parentNode.getNode(name).getPath(), new Date(nodeLastModified), new Date(lastModified)});
} else {
this.createNode(name, "nt:file", null);
this.createNode("jcr:content", "nt:resource", null);
}
// ensure content type
if (mimeType == null) {
mimeType = contentHelper.getMimeType(name);
if (mimeType == null) {
log.info("createFile: Cannot find content type for {}, using {}", name, DEFAULT_CONTENT_TYPE);
mimeType = DEFAULT_CONTENT_TYPE;
}
}
// ensure sensible last modification date
if (lastModified <= 0) {
lastModified = System.currentTimeMillis();
}
this.createProperty("jcr:mimeType", mimeType, true);
this.createProperty("jcr:lastModified", lastModified, true);
this.createProperty("jcr:data", data, true);
}
/**
* @see org.apache.sling.jcr.contentloader.ContentCreator#switchCurrentNode(java.lang.String, java.lang.String)
*/
public boolean switchCurrentNode(String subPath, String newNodeType) throws RepositoryException {
if (subPath.startsWith("/")) {
subPath = subPath.substring(1);
}
final StringTokenizer st = new StringTokenizer(subPath, "/");
Node node = this.parentNodeStack.peek();
while (st.hasMoreTokens()) {
final String token = st.nextToken();
if (!node.hasNode(token)) {
if (newNodeType == null) {
return false;
}
checkoutIfNecessary(node);
final Node n = node.addNode(token, newNodeType);
addNodeToCreatedList(n);
if (this.importListener != null) {
this.importListener.onCreate(node.getPath());
}
}
node = node.getNode(token);
}
this.parentNodeStack.push(node);
return true;
}
/* (non-Javadoc)
* @see org.apache.sling.jcr.contentloader.ContentCreator#createGroup(java.lang.String, java.lang.String[], java.util.Map)
*/
public void createGroup(final String name, String[] members, Map<String, Object> extraProperties) throws RepositoryException {
final Node parentNode = this.parentNodeStack.peek();
Session session = parentNode.getSession();
UserManager userManager = AccessControlUtil.getUserManager(session);
Authorizable authorizable = userManager.getAuthorizable(name);
if (authorizable == null) {
//principal does not exist yet, so create it
Group group = userManager.createGroup(new Principal() {
public String getName() {
return name;
}
},
hashPath(name)
);
authorizable = group;
} else {
//principal already exists, check to make sure it is the expected type
if (!authorizable.isGroup()) {
throw new RepositoryException("A user already exists with the requested name: " + name);
}
//group already exists so just update it below
}
//update the group members
if (members != null) {
Group group = (Group) authorizable;
for (String member : members) {
Authorizable memberAuthorizable = userManager.getAuthorizable(member);
if (memberAuthorizable != null) {
group.addMember(memberAuthorizable);
}
}
}
if (extraProperties != null) {
ValueFactory valueFactory = session.getValueFactory();
Set<Entry<String, Object>> entrySet = extraProperties.entrySet();
for (Entry<String, Object> entry : entrySet) {
Value value = createValue(valueFactory, entry.getValue());
authorizable.setProperty(entry.getKey(), value);
}
}
}
/* (non-Javadoc)
* @see org.apache.sling.jcr.contentloader.ContentCreator#createUser(java.lang.String, java.lang.String, java.util.Map)
*/
public void createUser(final String name, String password, Map<String, Object> extraProperties) throws RepositoryException {
final Node parentNode = this.parentNodeStack.peek();
Session session = parentNode.getSession();
UserManager userManager = AccessControlUtil.getUserManager(session);
Authorizable authorizable = userManager.getAuthorizable(name);
if (authorizable == null) {
//principal does not exist yet, so create it
User user = userManager.createUser(name,
password,
new Principal() {
public String getName() {
return name;
}
},
hashPath(name)
);
authorizable = user;
} else {
//principal already exists, check to make sure it is the expected type
if (authorizable.isGroup()) {
throw new RepositoryException("A group already exists with the requested name: " + name);
}
//user already exists so just update it below
}
if (extraProperties != null) {
ValueFactory valueFactory = session.getValueFactory();
Set<Entry<String, Object>> entrySet = extraProperties.entrySet();
for (Entry<String, Object> entry : entrySet) {
Value value = createValue(valueFactory, entry.getValue());
authorizable.setProperty(entry.getKey(), value);
}
}
}
/**
* @param item
* @return a parent path fragment for the item.
*/
protected String hashPath(String item) throws RepositoryException {
try {
String hash = digest("sha1", (INSTANCE_SEED + item).getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < STORAGE_LEVELS; i++) {
sb.append(hash, i * 2, (i * 2) + 2).append("/");
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RepositoryException("Unable to hash the path.", e);
} catch (UnsupportedEncodingException e) {
throw new RepositoryException("Unable to hash the path.", e);
}
}
/* (non-Javadoc)
* @see org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String, java.lang.String, java.lang.String[], java.lang.String[])
*/
public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String order) throws RepositoryException {
final Node parentNode = this.parentNodeStack.peek();
Session session = parentNode.getSession();
PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
Principal principal = principalManager.getPrincipal(principalId);
if (principal == null) {
// SLING-7268 - as pointed out in OAK-5496, we cannot successfully use PrincipalManager#getPrincipal in oak
// without the session that created the principal getting saved first (and a subsequent index update).
// Workaround by trying the UserManager#getAuthorizable API to locate the principal.
UserManager userManager = AccessControlUtil.getUserManager(session);
final Authorizable authorizable = userManager.getAuthorizable(principalId);
if (authorizable != null) {
principal = authorizable.getPrincipal();
}
}
if (principal == null) {
throw new RepositoryException("No principal found for id: " + principalId);
}
String resourcePath = parentNode.getPath();
if ((grantedPrivilegeNames != null) || (deniedPrivilegeNames != null)) {
AccessControlUtil.replaceAccessControlEntry(session, resourcePath, principal, grantedPrivilegeNames, deniedPrivilegeNames, null, order);
}
}
/**
* used for the md5
*/
private static final char[] hexTable = "0123456789abcdef".toCharArray();
/**
* Digest the plain string using the given algorithm.
*
* @param algorithm The alogrithm for the digest. This algorithm must be
* supported by the MessageDigest class.
* @param data the data to digest with the given algorithm
* @return The digested plain text String represented as Hex digits.
* @throws java.security.NoSuchAlgorithmException if the desired algorithm is not supported by
* the MessageDigest class.
*/
public static String digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(algorithm);
byte[] digest = md.digest(data);
StringBuffer res = new StringBuffer(digest.length * 2);
for (int i = 0; i < digest.length; i++) {
byte b = digest[i];
res.append(hexTable[(b >> 4) & 15]);
res.append(hexTable[b & 15]);
}
return res.toString();
}
/**
* Find an ancestor that is versionable
*/
protected Node findVersionableAncestor(Node node) throws RepositoryException {
if (node == null) {
return null;
} else if (isVersionable(node)) {
return node;
} else {
try {
node = node.getParent();
return findVersionableAncestor(node);
} catch (ItemNotFoundException e) {
// top-level
return null;
}
}
}
protected boolean isVersionable(Node node) throws RepositoryException {
return node.isNodeType("mix:versionable");
}
/**
* Checkout the node if needed
*/
protected void checkoutIfNecessary(Node node) throws RepositoryException {
if (this.configuration.isAutoCheckout()) {
Node versionableNode = findVersionableAncestor(node);
if (versionableNode != null) {
if (!versionableNode.isCheckedOut()) {
VersionManager versionManager = versionableNode.getSession().getWorkspace().getVersionManager();
versionManager.checkout(versionableNode.getPath());
if (this.importListener != null) {
this.importListener.onCheckout(versionableNode.getPath());
}
}
}
}
}
}