blob: 7d0eb3d912dccaa9423c6f597f326302e6bf9856 [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.servlets.post.impl.helper;
import java.util.List;
import javax.jcr.AccessDeniedException;
import javax.jcr.InvalidItemStateException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.version.VersionException;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.SlingPostConstants;
import org.apache.sling.servlets.post.VersioningConfiguration;
import org.apache.sling.servlets.post.exceptions.PreconditionViolatedPersistenceException;
import org.apache.sling.servlets.post.exceptions.TemporaryPersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JCRSupportImpl {
/** Logger. */
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* Orders the given node according to the specified command. The following
* syntax is supported: <xmp> | first | before all child nodes | before A |
* before child node A | after A | after child node A | last | after all
* nodes | N | at a specific position, N being an integer </xmp>
*
* @param request The http request
* @param resource the resource to order
* @param changes the list of modifications
* @throws PersistenceException in case the operation is not successful
*/
public void orderNode(final SlingHttpServletRequest request,
final Resource resource,
final List<Modification> changes) throws PersistenceException {
final String command = request.getParameter(SlingPostConstants.RP_ORDER);
if (command == null || command.length() == 0) {
// nothing to do
return;
}
final Node node = resource.adaptTo(Node.class);
if (node == null) {
return;
}
try {
final Node parent = node.getParent();
String next = null;
if (command.equals(SlingPostConstants.ORDER_FIRST)) {
next = parent.getNodes().nextNode().getName();
} else if (command.equals(SlingPostConstants.ORDER_LAST)) {
next = "";
} else if (command.startsWith(SlingPostConstants.ORDER_BEFORE)) {
next = command.substring(SlingPostConstants.ORDER_BEFORE.length());
} else if (command.startsWith(SlingPostConstants.ORDER_AFTER)) {
String name = command.substring(SlingPostConstants.ORDER_AFTER.length());
NodeIterator iter = parent.getNodes();
while (iter.hasNext()) {
Node n = iter.nextNode();
if (n.getName().equals(name)) {
if (iter.hasNext()) {
next = iter.nextNode().getName();
} else {
next = "";
}
}
}
} else {
// check for integer
try {
// 01234
// abcde move a -> 2 (above 3)
// bcade move a -> 1 (above 1)
// bacde
int newPos = Integer.parseInt(command);
next = "";
NodeIterator iter = parent.getNodes();
while (iter.hasNext() && newPos >= 0) {
Node n = iter.nextNode();
if (n.getName().equals(node.getName())) {
// if old node is found before index, need to
// inc index
newPos++;
}
if (newPos == 0) {
next = n.getName();
break;
}
newPos--;
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"provided node ordering command is invalid: " + command);
}
}
if (next != null) {
if (next.equals("")) {
next = null;
}
parent.orderBefore(node.getName(), next);
changes.add(Modification.onOrder(node.getPath(), next));
if (logger.isDebugEnabled()) {
logger.debug("Node {} moved '{}'", node.getPath(), command);
}
} else {
throw new IllegalArgumentException(
"provided node ordering command is invalid: " + command);
}
} catch (final VersionException|ConstraintViolationException|ItemNotFoundException e) {
throw new PreconditionViolatedPersistenceException("Unable to order resource", e, resource.getPath(), null);
} catch (final UnsupportedRepositoryOperationException|LockException e) {
throw new TemporaryPersistenceException("Unable to order resource", e, resource.getPath(), null);
} catch ( final RepositoryException re) {
throw new PersistenceException("Unable to order resource", re, resource.getPath(), null);
}
}
private boolean isVersionable(final Node node) throws RepositoryException {
return node.isNodeType(JcrConstants.MIX_VERSIONABLE);
}
public boolean isVersionable(final Resource rsrc) throws PersistenceException {
try {
final Node node = rsrc.adaptTo(Node.class);
return node != null && isVersionable(node);
} catch ( final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re, rsrc.getPath(), null);
}
}
public boolean checkin(final Resource rsrc)
throws PersistenceException {
final Node node = rsrc.adaptTo(Node.class);
if (node != null) {
try {
if (node.isCheckedOut() && isVersionable(node)) {
node.getSession().getWorkspace().getVersionManager().checkin(node.getPath());
return true;
}
} catch (final AccessDeniedException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(), e, rsrc.getPath(), null);
} catch (final UnsupportedRepositoryOperationException|InvalidItemStateException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(), e, rsrc.getPath(), null);
} catch ( final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re, rsrc.getPath(), null);
}
}
return false;
}
private Node findVersionableAncestor(Node node) throws RepositoryException {
if (isVersionable(node)) {
return node;
}
try {
node = node.getParent();
return findVersionableAncestor(node);
} catch (ItemNotFoundException | AccessDeniedException e ) {
// top-level or parent not accessible, stop looking for a versionable ancestor
return null;
}
}
public void checkoutIfNecessary(final Resource resource,
final List<Modification> changes,
final VersioningConfiguration versioningConfiguration)
throws PersistenceException {
if (resource != null && versioningConfiguration.isAutoCheckout()) {
final Node node = resource.adaptTo(Node.class);
if ( node != null ) {
try {
Node versionableNode = findVersionableAncestor(node);
if (versionableNode != null) {
if (!versionableNode.isCheckedOut()) {
versionableNode.getSession().getWorkspace().getVersionManager().checkout(versionableNode.getPath());
changes.add(Modification.onCheckout(versionableNode.getPath()));
}
}
} catch (final AccessDeniedException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final UnsupportedRepositoryOperationException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
}
}
public boolean isNode(final Resource rsrc) {
return rsrc.adaptTo(Node.class) != null;
}
public boolean isNodeType(final Resource rsrc, final String typeHint) {
final Node node = rsrc.adaptTo(Node.class);
if ( node != null ) {
try {
return node.isNodeType(typeHint);
} catch ( final RepositoryException re) {
// ignore
}
}
return false;
}
public Boolean isFileNodeType(final ResourceResolver resolver, final String nodeType) {
final Session session = resolver.adaptTo(Session.class);
if ( session != null ) {
try {
final NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
final NodeType nt = ntMgr.getNodeType(nodeType);
return nt.isNodeType(JcrConstants.NT_FILE);
} catch (RepositoryException e) {
// assuming type not valid.
return null;
}
}
return false;
}
private PropertyDefinition searchPropertyDefinition(final NodeType nodeType, final String name) {
if ( nodeType.getPropertyDefinitions() != null ) {
for(final PropertyDefinition pd : nodeType.getPropertyDefinitions()) {
if ( pd.getName().equals(name) ) {
return pd;
}
}
}
// SLING-2877:
// no need to search property definitions of super types, as nodeType.getPropertyDefinitions()
// already includes those. see javadoc of {@link NodeType#getPropertyDefinitions()}
return null;
}
private PropertyDefinition searchPropertyDefinition(final Node node, final String name)
throws RepositoryException {
PropertyDefinition result = searchPropertyDefinition(node.getPrimaryNodeType(), name);
if ( result == null ) {
if ( node.getMixinNodeTypes() != null ) {
for(final NodeType mt : node.getMixinNodeTypes()) {
result = this.searchPropertyDefinition(mt, name);
if ( result != null ) {
return result;
}
}
}
}
return result;
}
public boolean isPropertyProtectedOrNewAutoCreated(final Object n, final String name)
throws PersistenceException {
final Node node = (Node)n;
try {
final PropertyDefinition pd = this.searchPropertyDefinition(node, name);
if ( pd != null ) {
// SLING-2877 (autocreated check is only required for new nodes)
if ( (node.isNew() && pd.isAutoCreated()) || pd.isProtected() ) {
return true;
}
}
} catch ( final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
return false;
}
public boolean isNewNode(final Object node) {
return ((Node)node).isNew();
}
public boolean isPropertyMandatory(final Object node, final String name)
throws PersistenceException {
try {
final Property prop = ((Node)node).getProperty(name);
return prop.getDefinition().isMandatory();
} catch (final PathNotFoundException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch ( final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
public boolean isPropertyMultiple(final Object node, final String name)
throws PersistenceException {
try {
final Property prop = ((Node)node).getProperty(name);
return prop.getDefinition().isMultiple();
} catch (final PathNotFoundException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch ( final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
public Integer getPropertyType(final Object node, final String name)
throws PersistenceException {
try {
if ( ((Node)node).hasProperty(name) ) {
return ((Node)node).getProperty(name).getType();
}
} catch (final NoSuchNodeTypeException|ConstraintViolationException|PathNotFoundException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final VersionException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
return null;
}
private boolean isWeakReference(int propertyType) {
return propertyType == PropertyType.WEAKREFERENCE;
}
/**
* {@inheritDoc}
*/
public Modification storeAsReference(
final Object n,
final String name,
final String[] values,
final int type,
final boolean multiValued)
throws PersistenceException {
try {
final Node node = (Node)n;
if (multiValued) {
Value[] array = ReferenceParser.parse(node.getSession(), values, isWeakReference(type));
if (array != null) {
return Modification.onModified(
node.setProperty(name, array).getPath());
}
} else {
if (values.length >= 1) {
Value v = ReferenceParser.parse(node.getSession(), values[0], isWeakReference(type));
if (v != null) {
return Modification.onModified(
node.setProperty(name, v).getPath());
}
}
}
return null;
} catch (final NoSuchNodeTypeException|ConstraintViolationException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final VersionException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
public boolean hasSession(final ResourceResolver resolver) {
return resolver.adaptTo(Session.class) != null;
}
public void setTypedProperty(final Object n,
final String name,
final String[] values,
final int type,
final boolean multiValued)
throws PersistenceException {
try {
if (multiValued) {
((Node)n).setProperty(name, values, type);
} else if (values.length >= 1) {
((Node)n).setProperty(name, values[0], type);
}
} catch (final ValueFormatException|ConstraintViolationException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final VersionException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
public Object getNode(final Resource rsrc) {
return rsrc.adaptTo(Node.class);
}
public Object getItem(final Resource rsrc) {
return rsrc.adaptTo(Item.class);
}
public void setPrimaryNodeType(final Object node, final String type)
throws PersistenceException {
try {
((Node)node).setPrimaryType(type);
} catch (final NoSuchNodeTypeException|ConstraintViolationException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final VersionException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
public void move(Object src, Object dstParent, String name)
throws PersistenceException {
try {
final Session session = ((Item)src).getSession();
final Item source = ((Item)src);
final String targetParentPath = ((Node)dstParent).getPath();
final String targetPath = (targetParentPath.equals("/") ? "" : targetParentPath) + '/' + name;
session.move(source.getPath(), targetPath);
} catch (final PathNotFoundException|ConstraintViolationException|ItemExistsException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final VersionException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
/**
* Copy the <code>src</code> item into the <code>dstParent</code> node.
* The name of the newly created item is set to <code>name</code>.
*
* @param src The item to copy to the new location
* @param dstParent The node into which the <code>src</code> node is to be
* copied
* @param name The name of the newly created item. If this is
* <code>null</code> the new item gets the same name as the
* <code>src</code> item.
* @return the name of the newly created object
* @throws PersistenceException May be thrown in case of any problem copying
* the content.
* @throws PersistenceException in case something fails
* @see #copy(Node, Node, String)
* @see #copy(Property, Node, String)
*/
public String copy(Object src, Object dstParent, String name)
throws PersistenceException {
try {
final Item result;
if (((Item)src).isNode()) {
result = copy((Node) src, (Node)dstParent, name);
} else {
result = copy((Property) src, (Node)dstParent, name);
}
return result.getPath();
} catch (final NoSuchNodeTypeException|ConstraintViolationException e) {
throw new PreconditionViolatedPersistenceException(e.getMessage(),e);
} catch (final VersionException|LockException e) {
throw new TemporaryPersistenceException(e.getMessage(),e);
} catch (final RepositoryException re) {
throw new PersistenceException(re.getMessage(), re);
}
}
/**
* Copy the <code>src</code> node into the <code>dstParent</code> node.
* The name of the newly created node is set to <code>name</code>.
* <p>
* This method does a recursive (deep) copy of the subtree rooted at the
* source node to the destination. Any protected child nodes and and
* properties are not copied.
*
* @param src The node to copy to the new location
* @param dstParent The node into which the <code>src</code> node is to be
* copied
* @param name The name of the newly created node. If this is
* <code>null</code> the new node gets the same name as the
* <code>src</code> node.
* @throws RepositoryException May be thrown in case of any problem copying
* the content.
*/
private Item copy(Node src, Node dstParent, String name)
throws RepositoryException {
if(isAncestorOrSameNode(src, dstParent)) {
throw new RepositoryException(
"Cannot copy ancestor " + src.getPath() + " to descendant " + dstParent.getPath());
}
// ensure destination name
if (name == null) {
name = src.getName();
}
// ensure new node creation
if (dstParent.hasNode(name)) {
dstParent.getNode(name).remove();
}
// create new node
Node dst = dstParent.addNode(name, src.getPrimaryNodeType().getName());
for (NodeType mix : src.getMixinNodeTypes()) {
dst.addMixin(mix.getName());
}
// copy the properties
for (PropertyIterator iter = src.getProperties(); iter.hasNext();) {
copy(iter.nextProperty(), dst, null);
}
// copy the child nodes
for (NodeIterator iter = src.getNodes(); iter.hasNext();) {
Node n = iter.nextNode();
if (!n.getDefinition().isProtected()) {
copy(n, dst, null);
}
}
return dst;
}
/**
* determines if the 2 nodes are in ancestor relationship or identical
* @param src one node
* @param dest the other node
* @return true if src is an ancestor node of dest, or if
* both are the same node
* @throws RepositoryException if something goes wrong
**/
public static boolean isAncestorOrSameNode(Node src, Node dest) throws RepositoryException {
if(src.getPath().equals("/")) {
return true;
} else if(src.getPath().equals(dest.getPath())) {
return true;
} else if(dest.getPath().startsWith(src.getPath() + "/")) {
return true;
}
return false;
}
/**
* Copy the <code>src</code> property into the <code>dstParent</code>
* node. The name of the newly created property is set to <code>name</code>.
* <p>
* If the source property is protected, this method does nothing.
*
* @param src The property to copy to the new location
* @param dstParent The node into which the <code>src</code> property is
* to be copied
* @param name The name of the newly created property. If this is
* <code>null</code> the new property gets the same name as the
* <code>src</code> property.
* @throws RepositoryException May be thrown in case of any problem copying
* the content.
*/
private Item copy(Property src, Node dstParent, String name)
throws RepositoryException {
if (!src.getDefinition().isProtected()) {
if (name == null) {
name = src.getName();
}
// ensure new property creation
if (dstParent.hasProperty(name)) {
dstParent.getProperty(name).remove();
}
if (src.getDefinition().isMultiple()) {
return dstParent.setProperty(name, src.getValues());
}
return dstParent.setProperty(name, src.getValue());
}
return null;
}
}