org.opencms.webdav.CmsDavResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opencms-core Show documentation
Show all versions of opencms-core Show documentation
OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.webdav;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.repository.CmsPropertyName;
import org.opencms.repository.I_CmsRepositoryItem;
import org.opencms.repository.I_CmsRepositorySession;
import org.opencms.security.CmsPermissionViolationException;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsStringUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.jackrabbit.webdav.DavCompliance;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavMethods;
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.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.lock.ActiveLock;
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.Type;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.apache.jackrabbit.webdav.property.PropEntry;
import org.apache.jackrabbit.webdav.property.ResourceType;
import org.apache.jackrabbit.webdav.xml.Namespace;
/**
* Represents a resource in the WebDav repository (may not actually correspond to an actual OpenCms resource, since
* DavResource are also created for the target locations for move/copy operations, before any of the moving / copying happens.
*/
public class CmsDavResource implements DavResource {
/** Logger instance for this class. **/
private static final Log LOG = CmsLog.getLog(CmsDavResource.class);
/** The resource factory that produced this resource. */
private CmsDavResourceFactory m_factory;
/** The resource locator for this resource. */
private DavResourceLocator m_locator;
/** The Webdav session object. */
private CmsDavSession m_session;
/** Lazily initialized repository item - null means not initialized, Optional.empty means tried to load resource, but it was not found. */
private Optional m_item;
/** The lock manager. */
private LockManager m_lockManager;
/**
* Creates a new instance.
*
* @param loc the locator for this resource
* @param factory the factory that produced this resource
* @param session the Webdav session
* @param lockManager the lock manager
*/
public CmsDavResource(
DavResourceLocator loc,
CmsDavResourceFactory factory,
CmsDavSession session,
LockManager lockManager) {
m_factory = factory;
m_locator = loc;
m_session = session;
m_lockManager = lockManager;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#addLockManager(org.apache.jackrabbit.webdav.lock.LockManager)
*/
public void addLockManager(LockManager lockmgr) {
m_lockManager = lockmgr;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#addMember(org.apache.jackrabbit.webdav.DavResource, org.apache.jackrabbit.webdav.io.InputContext)
*/
public void addMember(DavResource dres, InputContext inputContext) throws DavException {
I_CmsRepositorySession session = getRepositorySession();
String childPath = ((CmsDavResource)dres).getCmsPath();
String method = ((CmsDavInputContext)inputContext).getMethod();
InputStream stream = inputContext.getInputStream();
if (method.equals(DavMethods.METHOD_MKCOL) && (stream != null)) {
throw new DavException(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
}
if (dres.exists() && isLocked(dres)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
try {
if (stream != null) {
session.save(childPath, stream, true);
} else {
session.create(childPath);
}
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new DavException(CmsDavUtil.getStatusForException(e), e);
} catch (Exception e) {
throw new DavException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#alterProperties(java.util.List)
*/
public MultiStatusResponse alterProperties(List extends PropEntry> changeList) throws DavException {
if (exists() && isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
MultiStatusResponse res = new MultiStatusResponse(getHref(), null);
Map propMap = new HashMap<>();
for (PropEntry entry : changeList) {
if (entry instanceof DefaultDavProperty>) {
DefaultDavProperty prop = (DefaultDavProperty)entry;
CmsPropertyName cmsPropName = new CmsPropertyName(
prop.getName().getNamespace().getURI(),
prop.getName().getName());
propMap.put(cmsPropName, prop.getValue());
} else if (entry instanceof DavPropertyName) {
CmsPropertyName cmsPropName = new CmsPropertyName(
((DavPropertyName)entry).getNamespace().getURI(),
((DavPropertyName)entry).getName());
propMap.put(cmsPropName, "");
}
}
int status = HttpServletResponse.SC_OK;
try {
getRepositorySession().updateProperties(getCmsPath(), propMap);
} catch (CmsException e) {
LOG.warn(e.getLocalizedMessage(), e);
if (e instanceof CmsPermissionViolationException) {
status = HttpServletResponse.SC_FORBIDDEN;
}
}
for (PropEntry entry : changeList) {
if (entry instanceof DavPropertyName) {
res.add((DavPropertyName)entry, status);
} else if (entry instanceof DefaultDavProperty>) {
res.add((DavProperty)entry, status);
} else {
res.add((DavPropertyName)entry, HttpServletResponse.SC_FORBIDDEN);
}
}
return res;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#copy(org.apache.jackrabbit.webdav.DavResource, boolean)
*/
public void copy(DavResource dres, boolean shallow) throws DavException {
CmsDavResource other = (CmsDavResource)dres;
boolean targetParentExists = false;
try {
targetParentExists = dres.getCollection().exists();
} catch (Exception e) {
LOG.warn(e.getLocalizedMessage(), e);
}
if (!targetParentExists) {
throw new DavException(HttpServletResponse.SC_CONFLICT);
}
try {
getRepositorySession().copy(getCmsPath(), other.getCmsPath(), true, shallow);
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new DavException(CmsDavUtil.getStatusForException(e));
}
}
/**
* Deletes the resource.
*
* @throws DavException if an error occurs
*/
public void delete() throws DavException {
if (!exists()) {
throw new DavException(HttpServletResponse.SC_NOT_FOUND);
}
try {
getRepositorySession().delete(getCmsPath());
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new DavException(CmsDavUtil.getStatusForException(e), e);
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#exists()
*/
public boolean exists() {
return getItem() != null;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getCollection()
*/
public DavResource getCollection() {
DavResourceLocator locator = m_locator.getFactory().createResourceLocator(
m_locator.getPrefix(),
m_locator.getWorkspacePath(),
CmsResource.getParentFolder(m_locator.getResourcePath()));
try {
return m_factory.createResource(locator, m_session);
} catch (DavException e) {
return null;
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getComplianceClass()
*/
public String getComplianceClass() {
return DavCompliance.concatComplianceClasses(new String[] {DavCompliance._1_, DavCompliance._2_});
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getDisplayName()
*/
public String getDisplayName() {
String result = CmsResource.getName(getCmsPath());
result = result.replace("/", "");
return result;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getFactory()
*/
public DavResourceFactory getFactory() {
return m_factory;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getHref()
*/
public String getHref() {
String href = m_locator.getHref(true);
String result = CmsFileUtil.removeTrailingSeparator(href);
return result;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getLocator()
*/
public DavResourceLocator getLocator() {
return m_locator;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getLock(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope)
*/
public ActiveLock getLock(Type type, Scope scope) {
return m_lockManager.getLock(type, scope, this);
}
/**
* @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 org.apache.jackrabbit.webdav.DavResource#getMembers()
*/
public DavResourceIterator getMembers() {
I_CmsRepositorySession session = getRepositorySession();
try {
List children = session.list(getCmsPath());
List childDavRes = children.stream().map(child -> {
String childPath = CmsStringUtil.joinPaths(m_locator.getWorkspacePath(), child.getName());
DavResourceLocator childLocator = m_locator.getFactory().createResourceLocator(
m_locator.getPrefix(),
m_locator.getWorkspacePath(),
childPath);
return new CmsDavResource(childLocator, m_factory, m_session, m_lockManager);
}).filter(child -> {
boolean exists = child.exists();
if (!exists) {
// one case where this happens is when the child resource has a name that would be
// modified by the configured file translation rules.
LOG.warn(
"Invalid child resource: "
+ child.getLocator().getPrefix()
+ ":"
+ child.getLocator().getResourcePath());
}
return exists;
}).collect(Collectors.toList());
return new DavResourceIteratorImpl(childDavRes);
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
return null;
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getModificationTime()
*/
public long getModificationTime() {
I_CmsRepositoryItem item = getItem();
return item.getLastModifiedDate();
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getProperties()
*/
public DavPropertySet getProperties() {
DavPropertySet result = new DavPropertySet();
ResourceType typeProp = new ResourceType(
isCollection() ? ResourceType.COLLECTION : ResourceType.DEFAULT_RESOURCE);
result.add(typeProp);
result.add(
new DefaultDavProperty(
DavPropertyName.GETLASTMODIFIED,
CmsDavUtil.DATE_FORMAT.format(new Date(getItem().getLastModifiedDate()))));
result.add(new DefaultDavProperty(DavPropertyName.DISPLAYNAME, "" + getItem().getName()));
if (!isCollection()) {
result.add(
new DefaultDavProperty(DavPropertyName.GETCONTENTLENGTH, "" + getItem().getContentLength()));
result.add(new DefaultDavProperty(DavPropertyName.GETETAG, getETag()));
}
try {
Map cmsProps = getRepositorySession().getProperties(getCmsPath());
for (Map.Entry entry : cmsProps.entrySet()) {
CmsPropertyName propName = entry.getKey();
DavPropertyName name = DavPropertyName.create(
propName.getName(),
Namespace.getNamespace(propName.getNamespace()));
result.add(new DefaultDavProperty(name, entry.getValue()));
}
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
return result;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getProperty(org.apache.jackrabbit.webdav.property.DavPropertyName)
*/
public DavProperty> getProperty(DavPropertyName name) {
return getProperties().get(name);
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getPropertyNames()
*/
public DavPropertyName[] getPropertyNames() {
return getProperties().getPropertyNames();
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getResourcePath()
*/
public String getResourcePath() {
return m_locator.getResourcePath();
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getSession()
*/
public DavSession getSession() {
return m_session;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#getSupportedMethods()
*/
public String getSupportedMethods() {
TreeSet methods = new TreeSet<>();
I_CmsRepositoryItem item = getItem();
if (item == null) {
methods.addAll(Arrays.asList(DavMethods.METHOD_OPTIONS, DavMethods.METHOD_PUT, DavMethods.METHOD_MKCOL));
methods.add(DavMethods.METHOD_LOCK);
} else {
methods.addAll(
Arrays.asList(
DavMethods.METHOD_OPTIONS,
DavMethods.METHOD_HEAD,
DavMethods.METHOD_POST,
DavMethods.METHOD_DELETE));
methods.add(DavMethods.METHOD_PROPFIND);
methods.add(DavMethods.METHOD_PROPPATCH);
Arrays.asList(DavMethods.METHOD_COPY, DavMethods.METHOD_MOVE);
if (!item.isCollection()) {
methods.add(DavMethods.METHOD_PUT);
}
}
return CmsStringUtil.listAsString(new ArrayList<>(methods), ", ");
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#hasLock(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope)
*/
public boolean hasLock(Type type, Scope scope) {
return m_lockManager.getLock(type, scope, this) != null;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#isCollection()
*/
public boolean isCollection() {
I_CmsRepositoryItem item = getItem();
return (item != null) && item.isCollection();
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#isLockable(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope)
*/
public boolean isLockable(Type type, Scope scope) {
// TODO Auto-generated method stub
return false;
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#lock(org.apache.jackrabbit.webdav.lock.LockInfo)
*/
public ActiveLock lock(LockInfo reqLockInfo) throws DavException {
return m_lockManager.createLock(reqLockInfo, this);
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#move(org.apache.jackrabbit.webdav.DavResource)
*/
public void move(DavResource destination) throws DavException {
CmsDavResource other = (CmsDavResource)destination;
if (isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
try {
getRepositorySession().move(getCmsPath(), other.getCmsPath(), true);
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new DavException(CmsDavUtil.getStatusForException(e));
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#refreshLock(org.apache.jackrabbit.webdav.lock.LockInfo, java.lang.String)
*/
public ActiveLock refreshLock(LockInfo reqLockInfo, String lockToken) throws DavException {
return m_lockManager.refreshLock(reqLockInfo, lockToken, this);
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#removeMember(org.apache.jackrabbit.webdav.DavResource)
*/
public void removeMember(DavResource member) throws DavException {
if (isLocked(this) || isLocked(member)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
((CmsDavResource)member).delete();
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#removeProperty(org.apache.jackrabbit.webdav.property.DavPropertyName)
*/
public void removeProperty(DavPropertyName propertyName) throws DavException {
if (exists() && isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
I_CmsRepositorySession session = getRepositorySession();
Map props = new HashMap<>();
CmsPropertyName key = new CmsPropertyName(propertyName.getNamespace().getURI(), propertyName.getName());
props.put(key, "");
try {
session.updateProperties(getCmsPath(), props);
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new DavException(500);
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#setProperty(org.apache.jackrabbit.webdav.property.DavProperty)
*/
public void setProperty(DavProperty> property) throws DavException {
if (exists() && isLocked(this)) {
throw new DavException(DavServletResponse.SC_LOCKED);
}
if (!(property instanceof DefaultDavProperty)) {
throw new DavException(HttpServletResponse.SC_FORBIDDEN);
}
I_CmsRepositorySession session = getRepositorySession();
Map props = new HashMap<>();
DavPropertyName propertyName = property.getName();
String newValue = ((DefaultDavProperty)property).getValue();
if (newValue == null) {
newValue = "";
}
CmsPropertyName key = new CmsPropertyName(propertyName.getNamespace().getURI(), propertyName.getName());
props.put(key, newValue);
try {
session.updateProperties(getCmsPath(), props);
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new DavException(500);
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#spool(org.apache.jackrabbit.webdav.io.OutputContext)
*/
public void spool(OutputContext outputContext) throws IOException {
I_CmsRepositoryItem item = getItem();
outputContext.setContentType(item.getMimeType());
outputContext.setContentLength(item.getContentLength());
outputContext.setModificationTime(item.getLastModifiedDate());
outputContext.setETag(getETag());
OutputStream out = outputContext.getOutputStream();
if (out != null) {
out.write(item.getContent());
}
}
/**
* @see org.apache.jackrabbit.webdav.DavResource#unlock(java.lang.String)
*/
public void unlock(String lockToken) throws DavException {
m_lockManager.releaseLock(lockToken, this);
}
/**
* Gets the OpenCms path corresponding to this resource's locator.
*
* @return the OpenCms path
*/
private String getCmsPath() {
String path = m_locator.getResourcePath();
String workspace = m_locator.getWorkspacePath();
Optional remainingPath = CmsStringUtil.removePrefixPath(workspace, path);
return remainingPath.orElse(null);
}
/**
* Computes the ETag for the item (the item must be not null).
*
* @return the ETag for the repository item
*/
private String getETag() {
return "\"" + getItem().getContentLength() + "-" + getItem().getLastModifiedDate() + "\"";
}
/**
* Tries to load the appropriate repository item for this resource.
*
* @return the repository item, or null if none was found
*/
private I_CmsRepositoryItem getItem() {
if (m_item == null) {
try {
I_CmsRepositoryItem item = getRepositorySession().getItem(getCmsPath());
m_item = Optional.of(item);
} catch (Exception e) {
if (!(e instanceof CmsVfsResourceNotFoundException)) {
LOG.error(e.getLocalizedMessage(), e);
} else {
LOG.info(e.getLocalizedMessage(), e);
}
m_item = Optional.empty();
}
}
return m_item.orElse(null);
}
/**
* Gets the OpenCms repository session for which this resource was created.
*
* @return the OpenCms repository session
*/
private I_CmsRepositorySession getRepositorySession() {
return m_session.getRepositorySession();
}
/**
* Return true if this resource cannot be modified due to a write lock
* that is not owned by the current session.
*
* @param res the resource to check
*
* @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 {
for (String sLockToken : m_session.getLockTokens()) {
if (sLockToken.equals(lock.getToken())) {
return false;
}
}
return true;
}
}
}