/* * eXist Open Source Native XML Database * Copyright (C) 2010 The eXist Project * http://exist-db.org * * This program 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 * of the License, or (at your option) any later version. * * This program 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. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.webdav; import com.bradmcevoy.http.Auth; import com.bradmcevoy.http.CollectionResource; import com.bradmcevoy.http.CopyableResource; import com.bradmcevoy.http.DeletableResource; import com.bradmcevoy.http.GetableResource; import com.bradmcevoy.http.LockInfo; import com.bradmcevoy.http.LockNullResource; import com.bradmcevoy.http.LockResult; import com.bradmcevoy.http.LockTimeout; import com.bradmcevoy.http.LockToken; import com.bradmcevoy.http.LockingCollectionResource; import com.bradmcevoy.http.MakeCollectionableResource; import com.bradmcevoy.http.MoveableResource; import com.bradmcevoy.http.PropFindableResource; import com.bradmcevoy.http.PutableResource; import com.bradmcevoy.http.Range; import com.bradmcevoy.http.Resource; import com.bradmcevoy.http.exceptions.BadRequestException; import com.bradmcevoy.http.exceptions.ConflictException; import com.bradmcevoy.http.exceptions.LockedException; import com.bradmcevoy.http.exceptions.NotAuthorizedException; import com.bradmcevoy.http.exceptions.PreConditionFailedException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.UUID; import javax.xml.transform.TransformerException; import org.exist.EXistException; import org.exist.security.PermissionDeniedException; import org.exist.security.User; import org.exist.storage.BrokerPool; import org.exist.webdav.ExistResource.Mode; import org.exist.webdav.exceptions.CollectionDoesNotExistException; import org.exist.webdav.exceptions.CollectionExistsException; import org.exist.xmldb.XmldbURI; import org.exist.util.serializer.XMLWriter; /** * Class for representing an eXist-db collection as a Milton WebDAV collection. * See <a href="http://milton.ettrema.com">Milton</a>. * * @author Dannes Wessels (dizzzz_at_exist-db.org) */ public class MiltonCollection extends MiltonResource implements CollectionResource, GetableResource, PropFindableResource, DeletableResource, MakeCollectionableResource, PutableResource, LockingCollectionResource /*, DigestResource */, MoveableResource, CopyableResource, LockNullResource { private ExistCollection existCollection; /** * Constructor of representation of a Collection in the Milton framework, without user information. * To be called by the resource factory. * * @param host FQ host name including port number. * @param uri Path on server indicating path of resource * @param brokerPool Handle to Exist database. */ public MiltonCollection(String host, XmldbURI uri, BrokerPool pool) { this(host, uri, pool, null); } /** * Constructor of representation of a Document in the Milton framework, with user information. * To be called by the resource factory. * * @param host FQ host name including port number. * @param uri Path on server indicating path of resource. * @param user An Exist operation is performed with User. Can be NULL. * @param brokerPool Handle to Exist database. */ public MiltonCollection(String host, XmldbURI uri, BrokerPool pool, User user) { super(); if(LOG.isDebugEnabled()) LOG.debug("COLLECTION:" + uri.toString()); resourceXmldbUri = uri; brokerPool = pool; this.host = host; existCollection = new ExistCollection(uri, brokerPool); // store simpler type existResource = existCollection; // If user is available, additional data can be retrieved. if (user != null) { existCollection.setUser(user); existCollection.initMetadata(); } } /* =================== * Collection Resource * =================== */ //@Override public Resource child(String childName) { if(LOG.isDebugEnabled()) LOG.debug("get child=" + childName); // Safe guard value if (childName == null) { return null; } // Search all resources, return resource upon match List<? extends Resource> allResources = getChildren(); for (Resource resource : allResources) { if (childName.equals(resource.getName())) { return resource; } } // Not found return null; } private List<MiltonCollection> getCollectionResources() { List<MiltonCollection> allResources = new ArrayList<MiltonCollection>(); for (XmldbURI path : existCollection.getCollectionURIs()) { allResources.add(new MiltonCollection(this.host, path, brokerPool, user)); } return allResources; } private List<MiltonDocument> getDocumentResources() { List<MiltonDocument> allResources = new ArrayList<MiltonDocument>(); for (XmldbURI path : existCollection.getDocumentURIs()) { MiltonDocument mdoc = new MiltonDocument(this.host, path, brokerPool, user); // Show (restimated) size for PROPFIND only mdoc.setIsPropFind(true); allResources.add(mdoc); } return allResources; } //@Override public List<? extends Resource> getChildren() { List<Resource> allResources = new ArrayList<Resource>(); allResources.addAll(getCollectionResources()); allResources.addAll(getDocumentResources()); if(LOG.isDebugEnabled()) LOG.debug("Nr of children=" + allResources.size()); return allResources; } /* ==================== * PropFindableResource * ==================== */ //@Override public Date getCreateDate() { Date createDate = null; Long time = existCollection.getCreationTime(); if (time != null) { createDate = new Date(time); } if(LOG.isTraceEnabled()) LOG.trace("Create date=" + createDate); return createDate; } /* ==================== * DeletableResource * ==================== */ //@Override public void delete() throws NotAuthorizedException, ConflictException, BadRequestException { if(LOG.isDebugEnabled()) LOG.debug("Delete collection '" + resourceXmldbUri + "'."); existCollection.delete(); } /* ========================== * MakeCollectionableResource * ========================== */ //@Override public CollectionResource createCollection(String name) throws NotAuthorizedException, ConflictException { if(LOG.isTraceEnabled()) LOG.trace("Create collection '" + name + "' in '" + resourceXmldbUri + "'."); CollectionResource collection = null; try { XmldbURI collectionURI = existCollection.createCollection(name); collection = new MiltonCollection(host, collectionURI, brokerPool, user); } catch (PermissionDeniedException ex) { LOG.debug(ex.getMessage()); throw new NotAuthorizedException(this); } catch (CollectionExistsException ex) { LOG.debug(ex.getMessage()); throw new ConflictException(this); } catch (EXistException ex) { LOG.debug(ex.getMessage()); throw new ConflictException(this); } return collection; } /* =============== * PutableResource * =============== */ //@Override public Resource createNew(String newName, InputStream is, Long length, String contentType) throws IOException, ConflictException { if(LOG.isTraceEnabled()) LOG.trace("Create '" + newName + "' in '" + resourceXmldbUri + "'"); Resource resource = null; try { // submit XmldbURI resourceURI = existCollection.createFile(newName, is, length, contentType); resource = new MiltonDocument(host, resourceURI, brokerPool, user); } catch (PermissionDeniedException e) { LOG.debug(e.getMessage()); throw new ConflictException(this); } catch (CollectionDoesNotExistException e) { LOG.debug(e.getMessage()); throw new ConflictException(this); } catch (IOException e){ LOG.debug(e.getMessage()); throw new ConflictException(this); } return resource; } /* ========================= * LockingCollectionResource * ========================= */ //@Override public LockToken createAndLock(String name, LockTimeout timeout, LockInfo lockInfo) throws NotAuthorizedException { if(LOG.isDebugEnabled()) LOG.debug("'" + resourceXmldbUri + "' name='" + name + "'"); String token = UUID.randomUUID().toString(); return new LockToken(token, lockInfo, timeout); } /* ================ * LockableResource * ================ */ //@Override public LockResult lock(LockTimeout timeout, LockInfo lockInfo) throws NotAuthorizedException, PreConditionFailedException, LockedException { if(LOG.isDebugEnabled()) LOG.debug("'" + resourceXmldbUri + "' -- "+ lockInfo.toString()); return refreshLock(UUID.randomUUID().toString()); } //@Override public LockResult refreshLock(String token) throws NotAuthorizedException, PreConditionFailedException { if(LOG.isDebugEnabled()) LOG.debug("'" + resourceXmldbUri + "' token='" + token + "'"); LockInfo lockInfo = new LockInfo(LockInfo.LockScope.NONE, LockInfo.LockType.READ, token, LockInfo.LockDepth.ZERO); LockTimeout lockTime = new LockTimeout(Long.MAX_VALUE); LockToken lockToken = new LockToken(token, lockInfo, lockTime); return new LockResult(LockResult.FailureReason.PRECONDITION_FAILED, lockToken); } //@Override public void unlock(String tokenId) throws NotAuthorizedException, PreConditionFailedException { // Just do nothing if(LOG.isDebugEnabled()) LOG.debug("'" + resourceXmldbUri + "' token='" + tokenId + "'"); } //@Override public LockToken getCurrentLock() { if(LOG.isDebugEnabled()) LOG.debug("'" + resourceXmldbUri + "'"); return null; // null is allowed } /* =============== * MovableResource * =============== */ //@Override public void moveTo(CollectionResource rDest, String newName) throws ConflictException { if(LOG.isDebugEnabled()) LOG.debug("Move '"+ resourceXmldbUri + "' to '" + newName + "' in '" + rDest.getName() + "'"); XmldbURI destCollection = ((MiltonCollection) rDest).getXmldbUri(); try { existCollection.resourceCopyMove(destCollection, newName, Mode.MOVE); } catch (EXistException ex) { throw new ConflictException(this); } } /* ================ * CopyableResource * ================ */ //@Override public void copyTo(CollectionResource toCollection, String newName) { if(LOG.isDebugEnabled()) LOG.debug("Move '"+ resourceXmldbUri + "' to '" + newName + "' in '" + toCollection.getName() + "'"); XmldbURI destCollection = ((MiltonCollection) toCollection).getXmldbUri(); try { existCollection.resourceCopyMove(destCollection, newName, Mode.COPY); } catch (EXistException ex) { // copyTo does not throw COnflictException LOG.error(ex.getMessage()); } } /* ================ * GettableResource * ================ */ //@Override public void sendContent(OutputStream out, Range range, Map<String, String> params, String contentType) throws IOException, NotAuthorizedException, BadRequestException { try { Writer w = new OutputStreamWriter(out); XMLWriter xw = new XMLWriter(w); xw.setDefaultNamespace("http://exist.sourceforge.net/NS/exist"); // Begin document xw.startDocument(); // Root element xw.startElement("result"); // Root collection xw.startElement("collection"); xw.attribute("name", resourceXmldbUri.lastSegment().toString()); xw.attribute("created", getXmlDateTime(existCollection.getCreationTime())); xw.attribute("owner", existCollection.getOwnerUser()); xw.attribute("group", existCollection.getOwnerGroup()); xw.attribute("permissions", "" + existCollection.getPermissions().toString()); // Iterate over all collections in collection for (MiltonCollection collection : getCollectionResources()) { collection.writeXML(xw); } // Iterate over all documents in collection for (MiltonDocument document : getDocumentResources()) { document.writeXML(xw); } // Finish top collection xw.endElement("collection"); // Finish root element xw.endElement("result"); // Finish document xw.endDocument(); w.flush(); } catch (TransformerException ex) { LOG.error(ex); throw new IOException(ex.getMessage()); } } //@Override public Long getMaxAgeSeconds(Auth auth) { return null; } //@Override public String getContentType(String accepts) { return "application/xml"; } //@Override public Long getContentLength() { return null; } /* ================ * StAX serializer * ================ */ private void writeXML(XMLWriter xw) throws TransformerException { xw.startElement("collection"); xw.attribute("name", resourceXmldbUri.lastSegment().toString()); xw.attribute("created", getXmlDateTime(existCollection.getCreationTime())); xw.attribute("owner", existCollection.getOwnerUser()); xw.attribute("group", existCollection.getOwnerGroup()); xw.attribute("permissions", existCollection.getPermissions().toString()); xw.endElement("collection"); } }