blob: 0177d364dc83b8182513e80981b8a085a9aca880 [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation.
*
* 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.
*/
package org.apache.jackrabbit.webdav.simple;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.server.io.AbstractExportContext;
import org.apache.jackrabbit.server.io.ExportContext;
import org.apache.jackrabbit.server.io.ExportContextImpl;
import org.apache.jackrabbit.server.io.IOManager;
import org.apache.jackrabbit.server.io.IOUtil;
import org.apache.jackrabbit.server.io.ImportContext;
import org.apache.jackrabbit.server.io.ImportContextImpl;
import org.apache.jackrabbit.util.ISO9075;
import org.apache.jackrabbit.util.Text;
import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceFactory;
import org.apache.jackrabbit.webdav.DavResourceIterator;
import org.apache.jackrabbit.webdav.DavResourceIteratorImpl;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
import org.apache.jackrabbit.webdav.observation.ObservationConstants;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.jcr.JcrDavException;
import org.apache.jackrabbit.webdav.jcr.lock.JcrActiveLock;
import org.apache.jackrabbit.webdav.lock.ActiveLock;
import org.apache.jackrabbit.webdav.lock.LockDiscovery;
import org.apache.jackrabbit.webdav.lock.LockInfo;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.lock.Scope;
import org.apache.jackrabbit.webdav.lock.SupportedLock;
import org.apache.jackrabbit.webdav.lock.Type;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyIterator;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.apache.jackrabbit.webdav.property.ResourceType;
import org.apache.jackrabbit.webdav.xml.Namespace;
import org.apache.log4j.Logger;
import javax.jcr.Item;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.Lock;
import javax.jcr.nodetype.PropertyDefinition;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* DavResourceImpl implements a DavResource.
*/
public class DavResourceImpl implements DavResource, JcrConstants {
/**
* the default logger
*/
private static final Logger log = Logger.getLogger(DavResourceImpl.class);
private static final HashMap reservedNamespaces = new HashMap();
static {
reservedNamespaces.put(DavConstants.NAMESPACE.getPrefix(), DavConstants.NAMESPACE.getURI());
reservedNamespaces.put(ObservationConstants.NAMESPACE.getPrefix(), ObservationConstants.NAMESPACE.getURI());
}
private DavResourceFactory factory;
private LockManager lockManager;
private DavSession session;
private Node node;
private DavResourceLocator locator;
private DavPropertySet properties = new DavPropertySet();
private boolean inited = false;
private boolean isCollection = true;
private ItemFilter filter;
private IOManager ioManager;
private long modificationTime = IOUtil.UNDEFINED_TIME;
/**
* Create a new {@link DavResource}.
*
* @param locator
* @param factory
* @param session
*/
public DavResourceImpl(DavResourceLocator locator, DavResourceFactory factory,
DavSession session, ResourceConfig config) throws RepositoryException, DavException {
this.session = session;
this.factory = factory;
this.locator = locator;
this.filter = config.getItemFilter();
this.ioManager = config.getIOManager();
if (locator != null && locator.getResourcePath() != null) {
try {
Item item = session.getRepositorySession().getItem(locator.getJcrPath());
if (item != null && item.isNode()) {
node = (Node) item;
// define what is a collection in webdav
isCollection = config.isCollectionResource(node);
}
} catch (PathNotFoundException e) {
// ignore: exists field evaluates to false
}
} else {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
}
/**
* @return DavResource#COMPLIANCE_CLASS
* @see org.apache.jackrabbit.webdav.DavResource#getComplianceClass()
*/
public String getComplianceClass() {
return DavResource.COMPLIANCE_CLASS;
}
/**
* @return DavResource#METHODS
* @see org.apache.jackrabbit.webdav.DavResource#getSupportedMethods()
*/
public String getSupportedMethods() {
return DavResource.METHODS;
}
/**
* @see DavResource#exists() )
*/
public boolean exists() {
return node != null;
}
/**
* @see DavResource#isCollection()
*/
public boolean isCollection() {
return isCollection;
}
/**
* Package protected method that allows to define whether this resource
* represents a collection or not.
*
* @param isCollection
*/
void setIsCollection(boolean isCollection) {
this.isCollection = isCollection;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getLocator()
*/
public DavResourceLocator getLocator() {
return locator;
}
/**
* @see DavResource#getResourcePath()
*/
public String getResourcePath() {
return locator.getResourcePath();
}
/**
* @see DavResource#getHref()
*/
public String getHref() {
return locator.getHref(isCollection());
}
/**
* Returns the the last segment of the resource path.<p>
* Note that this must not correspond to the name of the underlying
* repository item for two reasons:<ul>
* <li>SameNameSiblings have an index appended to their item name.</li>
* <li>the resource path may differ from the item path.</li>
* </ul>
* Using the item name as DAV:displayname caused problems with XP built-in
* client in case of resources representing SameNameSibling nodes.
*
* @see DavResource#getDisplayName()
*/
public String getDisplayName() {
String resPath = getResourcePath();
return (resPath != null) ? Text.getName(resPath) : resPath;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getModificationTime()
*/
public long getModificationTime() {
initProperties();
return modificationTime;
}
/**
* If this resource exists and the specified context is not <code>null</code>
* this implementation build a new {@link ExportContext} based on the specified
* context and forwards the export to its <code>IOManager</code>. If the
* {@link IOManager#exportContent(ExportContext, DavResource)} fails,
* an <code>IOException</code> is thrown.
*
* @see DavResource#spool(OutputContext)
* @see ResourceConfig#getIOManager()
* @throws IOException if the export fails.
*/
public void spool(OutputContext outputContext) throws IOException {
if (exists() && outputContext != null) {
ExportContext exportCtx = getExportContext(outputContext);
if (!ioManager.exportContent(exportCtx, this)) {
throw new IOException("Unexpected Error while spooling resource.");
}
}
}
/**
* @see DavResource#getProperty(org.apache.jackrabbit.webdav.property.DavPropertyName)
*/
public DavProperty getProperty(DavPropertyName name) {
initProperties();
return properties.get(name);
}
/**
* @see DavResource#getProperties()
*/
public DavPropertySet getProperties() {
initProperties();
return properties;
}
/**
* @see DavResource#getPropertyNames()
*/
public DavPropertyName[] getPropertyNames() {
return getProperties().getPropertyNames();
}
/**
* Fill the set of properties
*/
protected void initProperties() {
if (!exists() || inited) {
return;
}
try {
ioManager.exportContent(new PropertyExportCtx(), this);
} catch (IOException e) {
// should not occure....
}
if (getDisplayName() != null) {
properties.add(new DefaultDavProperty(DavPropertyName.DISPLAYNAME, getDisplayName()));
}
if (isCollection()) {
properties.add(new ResourceType(ResourceType.COLLECTION));
// Windows XP support
properties.add(new DefaultDavProperty(DavPropertyName.ISCOLLECTION, "1"));
} else {
properties.add(new ResourceType(ResourceType.DEFAULT_RESOURCE));
// Windows XP support
properties.add(new DefaultDavProperty(DavPropertyName.ISCOLLECTION, "0"));
}
/* set current lock information. If no lock is set to this resource,
an empty lockdiscovery will be returned in the response. */
properties.add(new LockDiscovery(getLock(Type.WRITE, Scope.EXCLUSIVE)));
/* lock support information: all locks are lockable. */
SupportedLock supportedLock = new SupportedLock();
supportedLock.addEntry(Type.WRITE, Scope.EXCLUSIVE);
properties.add(supportedLock);
// non-protected JCR properties defined on the underlying jcr node
try {
PropertyIterator it = node.getProperties();
while (it.hasNext()) {
Property p = it.nextProperty();
String pName = p.getName();
PropertyDefinition def = p.getDefinition();
if (def.isMultiple() || isFilteredItem(p)) {
log.debug("Property '" + pName + "' not added to webdav property set (either multivalue or filtered).");
continue;
}
DavPropertyName name = getDavName(pName, node.getSession());
String value = p.getValue().getString();
properties.add(new DefaultDavProperty(name, value, def.isProtected()));
}
} catch (RepositoryException e) {
log.error("Unexpected error while retrieving properties: " + e.getMessage());
}
inited = true;
}
/**
* @param property
* @throws DavException
* @see DavResource#setProperty(org.apache.jackrabbit.webdav.property.DavProperty)
*/
public void setProperty(DavProperty property) throws DavException {
if (isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
if (!exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
try {
setJcrProperty(property);
node.save();
} catch (RepositoryException e) {
// revert any changes made so far an throw exception
JcrDavException je = new JcrDavException(e);
try {
node.refresh(false);
} catch (RepositoryException re) {
// should not happen...
}
throw je;
}
}
/**
* @param propertyName
* @throws DavException
* @see DavResource#removeProperty(org.apache.jackrabbit.webdav.property.DavPropertyName)
*/
public void removeProperty(DavPropertyName propertyName) throws DavException {
if (isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
if (!exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
try {
removeJcrProperty(propertyName);
node.save();
} catch (RepositoryException e) {
JcrDavException je = new JcrDavException(e);
try {
node.refresh(false);
} catch (RepositoryException re) {
// should not happen...
}
throw je;
}
}
/**
* @see DavResource#alterProperties(org.apache.jackrabbit.webdav.property.DavPropertySet, org.apache.jackrabbit.webdav.property.DavPropertyNameSet)
*/
public MultiStatusResponse alterProperties(DavPropertySet setProperties,
DavPropertyNameSet removePropertyNames)
throws DavException {
if (isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
if (!exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
MultiStatusResponse msr = new MultiStatusResponse(getHref(), null);
boolean success = true;
// loop over set and remove Sets and remember all properties and propertyNames
// that have successfully been altered
List successList = new ArrayList();
DavPropertyIterator setIter = setProperties.iterator();
while (setIter.hasNext()) {
DavProperty prop = setIter.nextProperty();
try {
setJcrProperty(prop);
successList.add(prop);
} catch (RepositoryException e) {
msr.add(prop.getName(), new JcrDavException(e).getErrorCode());
success = false;
}
}
Iterator remNameIter = removePropertyNames.iterator();
while (remNameIter.hasNext()) {
DavPropertyName propName = (DavPropertyName) remNameIter.next();
try {
removeJcrProperty(propName);
successList.add(propName);
} catch (RepositoryException e) {
msr.add(propName, new JcrDavException(e).getErrorCode());
success = false;
}
}
try {
if (success) {
// save all changes together (reverted in case this fails)
node.save();
} else {
// set/remove of at least a single prop failed: undo modifications.
node.refresh(false);
}
/* loop over list of properties/names that were successfully altered
and them to the multistatus response respecting the resulte of the
complete action. in case of failure set the status to 'failed-dependency'
in order to indicate, that altering those names/properties would
have succeeded, if no other error occured.*/
Iterator it = successList.iterator();
while (it.hasNext()) {
Object o = it.next();
int status = (success) ? DavServletResponse.SC_OK : DavServletResponse.SC_FAILED_DEPENDENCY;
if (o instanceof DavProperty) {
msr.add(((DavProperty) o).getName(), status);
} else {
msr.add((DavPropertyName) o, status);
}
}
return msr;
} catch (RepositoryException e) {
// revert any changes made so far an throw exception
try {
node.refresh(false);
} catch (RepositoryException re) {
// should not happen
}
throw new JcrDavException(e);
}
}
/**
* @see DavResource#getCollection()
*/
public DavResource getCollection() {
DavResource parent = null;
if (getResourcePath() != null && !getResourcePath().equals("/")) {
String parentPath = Text.getRelativeParent(getResourcePath(), 1);
if (parentPath.equals("")) {
parentPath = "/";
}
DavResourceLocator parentloc = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), parentPath);
try {
parent = factory.createResource(parentloc, session);
} catch (DavException e) {
// should not occur
}
}
return parent;
}
/**
* @see DavResource#getMembers()
*/
public DavResourceIterator getMembers() {
ArrayList list = new ArrayList();
if (exists() && isCollection()) {
try {
NodeIterator it = node.getNodes();
while (it.hasNext()) {
Node n = it.nextNode();
if (!isFilteredItem(n)) {
DavResourceLocator resourceLocator = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), n.getPath(), false);
DavResource childRes = factory.createResource(resourceLocator, session);
list.add(childRes);
} else {
log.debug("Filtered resource '" + n.getName() + "'.");
}
}
} catch (RepositoryException e) {
// should not occure
} catch (DavException e) {
// should not occure
}
}
return new DavResourceIteratorImpl(list);
}
/**
* Adds a new member to this resource.
*
* @see DavResource#addMember(DavResource, org.apache.jackrabbit.webdav.io.InputContext)
*/
public void addMember(DavResource member, InputContext inputContext) throws DavException {
if (!exists()) {
throw new DavException(DavServletResponse.SC_CONFLICT);
}
if (isLocked(this) || isLocked(member)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
// don't allow creation of nodes, that would be filtered out
if (isFilteredResource(member)) {
log.debug("Avoid creation of filtered resource: " + member.getDisplayName());
throw new DavException(DavServletResponse.SC_FORBIDDEN);
}
try {
String memberName = Text.getName(member.getLocator().getJcrPath());
ImportContext ctx = getImportContext(inputContext, memberName);
if (!ioManager.importContent(ctx, member)) {
// any changes should have been reverted in the importer
throw new DavException(DavServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
}
// persist changes after successful import
node.save();
} catch (RepositoryException e) {
log.error("Error while importing resource: " + e.toString());
throw new JcrDavException(e);
} catch (IOException e) {
log.error("Error while importing resource: " + e.toString());
throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
}
/**
* @see DavResource#removeMember(DavResource)
*/
public void removeMember(DavResource member) throws DavException {
if (!exists() || !member.exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
if (isLocked(this) || isLocked(member)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
// don't allow removal of nodes, that would be filtered out
if (isFilteredResource(member)) {
log.debug("Avoid removal of filtered resource: " + member.getDisplayName());
throw new DavException(DavServletResponse.SC_FORBIDDEN);
}
try {
// make sure, non-jcr locks are removed.
if (!isJsrLockable()) {
ActiveLock lock = getLock(Type.WRITE, Scope.EXCLUSIVE);
if (lock != null) {
lockManager.releaseLock(lock.getToken(), member);
}
}
ActiveLock lock = getLock(Type.WRITE, Scope.EXCLUSIVE);
if (lock != null && lockManager.hasLock(lock.getToken(), member)) {
lockManager.releaseLock(lock.getToken(), member);
}
String itemPath = member.getLocator().getJcrPath();
Item memItem = session.getRepositorySession().getItem(itemPath);
memItem.remove();
session.getRepositorySession().save();
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
}
/**
* @see DavResource#move(DavResource)
*/
public void move(DavResource destination) throws DavException {
if (!exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
if (isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
if (isFilteredResource(destination)) {
throw new DavException(DavServletResponse.SC_FORBIDDEN);
}
try {
String destItemPath = destination.getLocator().getJcrPath();
session.getRepositorySession().getWorkspace().move(locator.getJcrPath(), destItemPath);
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
}
/**
* @see DavResource#copy(DavResource, boolean)
*/
public void copy(DavResource destination, boolean shallow) throws DavException {
if (!exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
if (isLocked(destination)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
if (isFilteredResource(destination)) {
throw new DavException(DavServletResponse.SC_FORBIDDEN);
}
// TODO: support shallow and deep copy
if (shallow) {
throw new DavException(DavServletResponse.SC_FORBIDDEN, "Unable to perform shallow copy.");
}
try {
String destItemPath = destination.getLocator().getJcrPath();
session.getRepositorySession().getWorkspace().copy(locator.getJcrPath(), destItemPath);
} catch (PathNotFoundException e) {
// according to rfc 2518: missing parent
throw new DavException(DavServletResponse.SC_CONFLICT, e.getMessage());
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
}
/**
* @param type
* @param scope
* @return true if type is {@link Type#WRITE} and scope is {@link Scope#EXCLUSIVE}
* @see DavResource#isLockable(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope)
*/
public boolean isLockable(Type type, Scope scope) {
return Type.WRITE.equals(type) && Scope.EXCLUSIVE.equals(scope);
}
/**
* @see DavResource#hasLock(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope)
*/
public boolean hasLock(Type type, Scope scope) {
return getLock(type, scope) != null;
}
/**
* @see DavResource#getLock(Type, Scope)
*/
public ActiveLock getLock(Type type, Scope scope) {
ActiveLock lock = null;
if (exists() && Type.WRITE.equals(type) && Scope.EXCLUSIVE.equals(scope)) {
// try to retrieve the repository lock information first
try {
if (node.isLocked()) {
Lock jcrLock = node.getLock();
if (jcrLock != null && jcrLock.isLive()) {
lock = new JcrActiveLock(jcrLock);
}
}
} catch (RepositoryException e) {
// LockException (no lock applies) >> should never occur
// RepositoryException, AccessDeniedException or another error >> ignore
}
// could not retrieve a jcr-lock. test if a simple webdav lock is present.
if (lock == null) {
lock = lockManager.getLock(type, scope, this);
}
}
return lock;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getLocks()
*/
public ActiveLock[] getLocks() {
ActiveLock writeLock = getLock(Type.WRITE, Scope.EXCLUSIVE);
return (writeLock != null) ? new ActiveLock[]{writeLock} : new ActiveLock[0];
}
/**
* @see DavResource#lock(LockInfo)
*/
public ActiveLock lock(LockInfo lockInfo) throws DavException {
ActiveLock lock = null;
if (isLockable(lockInfo.getType(), lockInfo.getScope())) {
// TODO: deal with existing locks, that may have been created, before the node was jcr-lockable...
if (isJsrLockable()) {
try {
// try to execute the lock operation
Lock jcrLock = node.lock(lockInfo.isDeep(), false);
if (jcrLock != null) {
lock = new JcrActiveLock(jcrLock);
}
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
} else {
// create a new webdav lock
lock = lockManager.createLock(lockInfo, this);
}
} else {
throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Unsupported lock type or scope.");
}
return lock;
}
/**
* @see DavResource#refreshLock(LockInfo, String)
*/
public ActiveLock refreshLock(LockInfo lockInfo, String lockToken) throws DavException {
if (!exists()) {
throw new DavException(DavServletResponse.SC_NOT_FOUND);
}
ActiveLock lock = getLock(lockInfo.getType(), lockInfo.getScope());
if (lock == null) {
throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "No lock with the given type/scope present on resource " + getResourcePath());
}
if (lock instanceof JcrActiveLock) {
try {
// refresh JCR lock and return the original lock object.
node.getLock().refresh();
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
} else {
lock = lockManager.refreshLock(lockInfo, lockToken, this);
}
/* since lock has infinite lock (simple) or undefined timeout (jcr)
return the lock as retrieved from getLock. */
return lock;
}
/**
* @see DavResource#unlock(String)
*/
public void unlock(String lockToken) throws DavException {
ActiveLock lock = getLock(Type.WRITE, Scope.EXCLUSIVE);
if (lock == null) {
throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
} else if (lock.isLockedByToken(lockToken)) {
if (lock instanceof JcrActiveLock) {
try {
node.unlock();
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
} else {
lockManager.releaseLock(lockToken, this);
}
} else {
throw new DavException(DavServletResponse.SC_LOCKED);
}
}
/**
* @see DavResource#addLockManager(org.apache.jackrabbit.webdav.lock.LockManager)
*/
public void addLockManager(LockManager lockMgr) {
this.lockManager = lockMgr;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getFactory()
*/
public DavResourceFactory getFactory() {
return factory;
}
/**
* Returns the node that is wrapped by this resource.
*
* @return
*/
protected Node getNode() {
return node;
}
/**
* Returns a new <code>ImportContext</code>
*
* @param inputCtx
* @param systemId
* @return
* @throws IOException
*/
protected ImportContext getImportContext(InputContext inputCtx, String systemId) throws IOException {
return new ImportContextImpl(node, systemId, inputCtx);
}
/**
* Returns a new <code>ExportContext</code>
*
* @param outputCtx
* @return
* @throws IOException
*/
protected ExportContext getExportContext(OutputContext outputCtx) throws IOException {
return new ExportContextImpl(node, outputCtx);
}
/**
* Returns true, if the underlying node is nodetype jcr:lockable,
* without checking its current lock status. If the node is not jcr-lockable
* an attempt is made to add the mix:lockable mixin type.
*
* @return true if this resource is lockable.
*/
private boolean isJsrLockable() {
boolean lockable = false;
if (exists()) {
try {
lockable = node.isNodeType(MIX_LOCKABLE);
// not jcr-lockable: try to make the node jcr-lockable
if (!lockable && node.canAddMixin(MIX_LOCKABLE)) {
node.addMixin(MIX_LOCKABLE);
node.save();
lockable = true;
}
} catch (RepositoryException e) {
// -> node is definitely not jcr-lockable.
}
}
return lockable;
}
/**
* Return true if this resource cannot be modified due to a write lock
* that is not owned by the given session.
*
* @return true if this resource cannot be modified due to a write lock
*/
private boolean isLocked(DavResource res) {
ActiveLock lock = res.getLock(Type.WRITE, Scope.EXCLUSIVE);
if (lock == null) {
return false;
} else {
String[] sLockTokens = session.getLockTokens();
for (int i = 0; i < sLockTokens.length; i++) {
if (sLockTokens[i].equals(lock.getToken())) {
return false;
}
}
return true;
}
}
/**
* Builds a webdav property name from the given jcrName. In case the jcrName
* contains a namespace prefix that would conflict with any of the predefined
* webdav namespaces a new prefix is assigned.<br>
* Please note, that the local part of the jcrName is checked for XML
* compatibility by calling {@link ISO9075#encode(String)}
*
* @param jcrName
* @param session
* @return a <code>DavPropertyName</code> for the given jcr name.
*/
private DavPropertyName getDavName(String jcrName, Session session) throws RepositoryException {
// make sure the local name is xml compliant
String localName = ISO9075.encode(Text.getLocalName(jcrName));
String prefix = Text.getNamespacePrefix(jcrName);
String uri = session.getNamespaceURI(prefix);
// check for conflicts with reserved webdav-namespaces
if (reservedNamespaces.containsKey(prefix) && !reservedNamespaces.get(prefix).equals(uri)) {
prefix = prefix + "0";
}
Namespace namespace = Namespace.getNamespace(prefix, uri);
DavPropertyName name = DavPropertyName.create(localName, namespace);
return name;
}
/**
* Build jcr property name from dav property name. If the property name
* defines a namespace uri, that has not been registered yet, an attempt
* is made to register the uri with the prefix defined. Note, that no
* extra effort is made to generated a unique prefix.
*
* @param propName
* @return jcr name
* @throws RepositoryException
*/
private String getJcrName(DavPropertyName propName) throws RepositoryException {
// remove any encoding necessary for xml compliance
String pName = ISO9075.decode(propName.getName());
String uri = propName.getNamespace().getURI();
if (uri != null && !"".equals(uri)) {
Session s = session.getRepositorySession();
String prefix;
try {
// lookup 'prefix' in the session-ns-mappings / namespace-registry
prefix = s.getNamespacePrefix(uri);
} catch (NamespaceException e) {
// namespace uri has not been registered yet
NamespaceRegistry nsReg = s.getWorkspace().getNamespaceRegistry();
prefix = propName.getNamespace().getPrefix();
// avoid trouble with default namespace
if (prefix == null || "".equals(prefix)) {
prefix = "_pre" + nsReg.getPrefixes().length + 1;
}
// NOTE: will fail if prefix is already in use in the namespace registry
nsReg.registerNamespace(prefix, uri);
}
if (prefix != null && !"".equals(prefix)) {
pName = prefix + ":" + pName;
}
}
return pName;
}
/**
* @param property
* @throws RepositoryException
*/
private void setJcrProperty(DavProperty property) throws RepositoryException {
// retrieve value
String value = property.getValue().toString();
// set value; since multivalued-properties are not listed in the set
// of available properties, this extra validation-check is omitted.
node.setProperty(getJcrName(property.getName()), value);
}
/**
* @param propertyName
* @throws RepositoryException
*/
private void removeJcrProperty(DavPropertyName propertyName) throws RepositoryException {
String jcrName = getJcrName(propertyName);
if (node.hasProperty(jcrName)) {
node.getProperty(jcrName).remove();
}
// removal of non existing property succeeds
}
private boolean isFilteredResource(DavResource resource) {
// TODO: filtered nodetypes should be checked as well in order to prevent problems.
return filter != null && filter.isFilteredItem(resource.getDisplayName(), session.getRepositorySession());
}
private boolean isFilteredItem(Item item) {
return filter != null && filter.isFilteredItem(item);
}
//--------------------------------------------------------< inner class >---
/**
* ExportContext that writes the properties of this <code>DavResource</code>
* and provides not stream.
*/
private class PropertyExportCtx extends AbstractExportContext {
private PropertyExportCtx() {
super(node, false, null);
// set defaults:
setCreationTime(IOUtil.UNDEFINED_TIME);
setModificationTime(IOUtil.UNDEFINED_TIME);
}
public OutputStream getOutputStream() {
return null;
}
public void setContentLanguage(String contentLanguage) {
if (contentLanguage != null) {
properties.add(new DefaultDavProperty(DavPropertyName.GETCONTENTLANGUAGE, contentLanguage));
}
}
public void setContentLength(long contentLength) {
if (contentLength > IOUtil.UNDEFINED_LENGTH) {
properties.add(new DefaultDavProperty(DavPropertyName.GETCONTENTLENGTH, contentLength + ""));
}
}
public void setContentType(String mimeType, String encoding) {
String contentType = IOUtil.buildContentType(mimeType, encoding);
if (contentType != null) {
properties.add(new DefaultDavProperty(DavPropertyName.GETCONTENTTYPE, contentType));
}
}
public void setCreationTime(long creationTime) {
String created = IOUtil.getCreated(creationTime);
properties.add(new DefaultDavProperty(DavPropertyName.CREATIONDATE, created));
}
public void setModificationTime(long modTime) {
if (modTime <= IOUtil.UNDEFINED_TIME) {
modificationTime = new Date().getTime();
} else {
modificationTime = modTime;
}
String lastModified = IOUtil.getLastModified(modificationTime);
properties.add(new DefaultDavProperty(DavPropertyName.GETLASTMODIFIED, lastModified));
}
public void setETag(String etag) {
if (etag != null) {
properties.add(new DefaultDavProperty(DavPropertyName.GETETAG, etag));
}
}
public void setProperty(Object propertyName, Object propertyValue) {
if (propertyName instanceof DavPropertyName) {
DavPropertyName pName = (DavPropertyName)propertyName;
properties.add(new DefaultDavProperty(pName, propertyValue));
}
}
}
}