commit 17f2d4cbb747e0c9349d8500acf02d15a620c00f from: leo date: Wed Jan 28 15:17:55 2009 UTC updated property handling (still some minor issues) commit - 557565a3485f3bceaaedfceaed9c90915eb457d4 commit + 17f2d4cbb747e0c9349d8500acf02d15a620c00f blob - 4d645e1afa7c6fd288e8da760d89064436692577 blob + 514a1b9f2955d0a9825b8c0d0ddbb9a1d0a3e359 --- modules/vfs.s3/pom.xml +++ modules/vfs.s3/pom.xml @@ -17,13 +17,8 @@ - - moxo - com.thinkberg.moxo - 1.0-SNAPSHOT - ../../pom.xml - 4.0.0 + com.thinkberg vfs.s3 1.0-SNAPSHOT thinkberg.com S3 VFS provider @@ -69,11 +64,21 @@ + maven-compiler-plugin + + true + true + 1.5 + 1.5 + + + org.apache.maven.plugins maven-surefire-plugin - **/tests/*.java + **/tests/S3FileNameTest.java + **/tests/S3FileProviderTest.java blob - ff8178919c5992604e3c2cd499dda4b4f2804614 blob + 3353936723407d8aaf9b55fb8cc0cec4601cf2e3 --- modules/vfs.s3/src/main/java/com/thinkberg/vfs/s3/jets3t/Jets3tFileObject.java +++ modules/vfs.s3/src/main/java/com/thinkberg/vfs/s3/jets3t/Jets3tFileObject.java @@ -18,9 +18,7 @@ package com.thinkberg.vfs.s3.jets3t; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.commons.vfs.FileName; -import org.apache.commons.vfs.FileObject; -import org.apache.commons.vfs.FileType; +import org.apache.commons.vfs.*; import org.apache.commons.vfs.provider.AbstractFileObject; import org.apache.commons.vfs.util.MonitorOutputStream; import org.jets3t.service.Constants; @@ -34,7 +32,10 @@ import java.io.*; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; +import java.util.Arrays; import java.util.Date; +import java.util.HashMap; +import java.util.Map; /** @@ -121,10 +122,46 @@ public class Jets3tFileObject extends AbstractFileObje attached = false; } - protected void doRename(FileObject newfile) throws Exception { - super.doRename(newfile); + protected void doRename(FileObject targetFileObject) throws Exception { + String bucketId = bucket.getName(); + S3Object targetObject = ((Jets3tFileObject) targetFileObject).object; + + LOG.debug(String.format("move object '%s' to '%s'", getS3Key(), targetObject.getKey())); + + // if this is a folder, then rename all children of the current folder too + if (FileType.FOLDER.equals(getType())) { + String path = object.getKey(); + // make sure we add a '/' slash at the end to find children + if (!"".equals(path)) { + path = path + "/"; + } + + try { + S3Object[] children = service.listObjects(bucket, path, null); + LOG.debug(children); + String targetName = targetObject.getKey(); + for (S3Object child : children) { + String targetChildName = child.getKey(); + targetChildName = targetName + targetChildName.substring(object.getKey().length()); + service.renameObject(bucketId, child.getKey(), new S3Object(bucket, targetChildName)); + } + } catch (S3ServiceException e) { + throw new FileSystemException(String.format("can't move children of '%s' to '%s'", object.getKey(), targetObject.getKey()), e); + } + } + + try { + service.renameObject(bucket.getName(), object.getKey(), ((Jets3tFileObject) targetFileObject).object); + } catch (S3ServiceException e) { + throw new FileSystemException("can't rename object", e); + } } + @Override + public void copyFrom(FileObject file, FileSelector selector) throws FileSystemException { + super.copyFrom(file, selector); + } + protected void doCreateFolder() throws Exception { if (!Mimetypes.MIMETYPE_JETS3T_DIRECTORY.equals(object.getContentType())) { object.setContentType(Mimetypes.MIMETYPE_JETS3T_DIRECTORY); @@ -144,6 +181,7 @@ public class Jets3tFileObject extends AbstractFileObje protected void doSetLastModifiedTime(final long modtime) throws Exception { object.addMetadata(Constants.REST_METADATA_PREFIX + VFS_LAST_MODIFIED_TIME, modtime); + service.updateObjectMetadata(bucket.getName(), object); } protected InputStream doGetInputStream() throws Exception { @@ -172,6 +210,7 @@ public class Jets3tFileObject extends AbstractFileObje protected void onClose() throws IOException { try { LOG.debug(String.format("sending '%s' to storage (cached=%b)", object.getKey(), cacheFile)); + LOG.debug(object); if (cacheFile != null) { FileChannel cacheFc = getCacheFile().getChannel(); object.setContentLength(cacheFc.size()); @@ -198,29 +237,56 @@ public class Jets3tFileObject extends AbstractFileObje return FileType.FILE; } - protected String[] doListChildren() throws Exception { + protected String[] doListChildren() throws FileSystemException { String path = object.getKey(); // make sure we add a '/' slash at the end to find children if (!"".equals(path)) { path = path + "/"; } - S3Object[] children = service.listObjects(bucket, path, "/"); - String[] childrenNames = new String[children.length]; - for (int i = 0; i < children.length; i++) { - if (!children[i].getKey().equals(path)) { - // strip path from name (leave only base name) - childrenNames[i] = children[i].getKey().replaceAll("[^/]*//*", ""); + try { + S3Object[] children = service.listObjects(bucket, path, "/"); + LOG.debug(Arrays.asList(children)); + LOG.debug(Arrays.asList(children)); + String[] childrenNames = new String[children.length]; + for (int i = 0; i < children.length; i++) { + if (!children[i].getKey().equals(path)) { + // strip path from name (leave only base name) + childrenNames[i] = children[i].getKey().replaceAll("[^/]*//*", ""); + } } - } - return childrenNames; + return childrenNames; + } catch (S3ServiceException e) { + throw new FileSystemException(String.format("can't list children of '%s'", path), e); + } } protected long doGetContentSize() throws Exception { return object.getContentLength(); } + @SuppressWarnings("unchecked") + protected Map doGetAttributes() throws Exception { + Map metaData = object.getModifiableMetadata(); + Map attributes = new HashMap(metaData.size()); + for (Object key : metaData.keySet()) { + if (((String) key).startsWith(Constants.REST_METADATA_PREFIX)) { + attributes.put(((String) key).substring(Constants.REST_METADATA_PREFIX.length()), metaData.get(key)); + } else { + attributes.put(key, metaData.get(key)); + } + } + LOG.debug(String.format("%s[%s]", object.getKey(), attributes)); + return attributes; + } + + @SuppressWarnings("unchecked") + protected void doSetAttribute(String attrName, Object value) throws Exception { + object.addMetadata(Constants.REST_METADATA_PREFIX + attrName, value); + service.updateObjectMetadata(bucket.getName(), object); + } + // Utility methods /** * Create an S3 key from a commons-vfs path. This simply blob - 4ab104f040636ab7b267aed201ad98af0049f444 blob + 9e5648fcc4e4e7515289b475da7bb2992a230de9 --- modules/vfs.s3/src/test/java/com/thinkberg/vfs/s3/tests/S3FileProviderTest.java +++ modules/vfs.s3/src/test/java/com/thinkberg/vfs/s3/tests/S3FileProviderTest.java @@ -42,6 +42,9 @@ public class S3FileProviderTest extends S3TestCase { }; private static final String FOLDER = "/directory"; private static final String FILE = FOLDER + "/newfile.txt"; + private static final String FILE_NON_EXISTING = "/nonexisting.txt"; + private static final String ATTR_TESTKEY = "TESTKEY"; + protected static final String ATTR_TESTVALUE = "TESTVALUE"; static { LogFactory.getLog(S3FileProviderTest.class).debug("initializing ..."); @@ -106,12 +109,22 @@ public class S3FileProviderTest extends S3TestCase { public void testGetFolderListing() throws FileSystemException { FileObject object = ROOT.resolveFile(FOLDER); - FileObject[] files = object.findFiles(new DepthFileSelector(1)); - assertEquals(3, files.length); + assertEquals(2, object.getChildren().length); } + public void testGetFolderListingIsShallow() throws FileSystemException { + FileObject object = ROOT.resolveFile(FOLDER); + FileObject subFolder = object.resolveFile("subdir"); + subFolder.createFolder(); + subFolder.resolveFile("subfile.0").createFile(); + subFolder.resolveFile("subfile.1").createFile(); + subFolder.resolveFile("subfile.2").createFile(); + + assertEquals(3, object.getChildren().length); + } + public void testMissingFile() throws FileSystemException { - FileObject object = ROOT.resolveFile("/nonexisting.txt"); + FileObject object = ROOT.resolveFile(FILE_NON_EXISTING); assertFalse(object.exists()); } @@ -137,7 +150,42 @@ public class S3FileProviderTest extends S3TestCase { assertFalse(object.exists()); } - public void testCopyFolder() throws FileSystemException { + public void testCopyFile() throws FileSystemException { + FileObject srcObject = ROOT.resolveFile(FILE); + srcObject.createFile(); + assertTrue("source object should exist", srcObject.exists()); + FileObject dstObject = ROOT.resolveFile(FILE + ".dst"); + assertFalse("destination should not exist", dstObject.exists()); + dstObject.copyFrom(srcObject, ALL_FILE_SELECTOR); + assertTrue("destination should exist after copy", dstObject.exists()); + + srcObject.delete(); + dstObject.delete(); + } + + public void testCopyFileWithAttribute() throws FileSystemException { + if (!BUCKETID.startsWith("s3:")) { + FileObject srcObject = ROOT.resolveFile(FILE); + srcObject.createFile(); + srcObject.getContent().setAttribute(ATTR_TESTKEY, ATTR_TESTVALUE); + assertTrue("source object should exist", srcObject.exists()); + assertEquals("source object attribute missing", + ATTR_TESTVALUE, srcObject.getContent().getAttribute(ATTR_TESTKEY)); + FileObject dstObject = ROOT.resolveFile(FILE + ".dst"); + assertFalse("destination should not exist", dstObject.exists()); + dstObject.copyFrom(srcObject, ALL_FILE_SELECTOR); + assertTrue("destination should exist after copy", dstObject.exists()); + assertEquals("destination object attribute missing", + ATTR_TESTVALUE, dstObject.getContent().getAttribute(ATTR_TESTKEY)); + + srcObject.delete(); + dstObject.delete(); + } else { + LogFactory.getLog(S3FileProviderTest.class).info(String.format("ignoring property test for '%s'", ROOT)); + } + } + + public void testCopyShallowFolder() throws FileSystemException { FileObject origFolder = ROOT.resolveFile(FOLDER); origFolder.createFolder(); @@ -145,17 +193,17 @@ public class S3FileProviderTest extends S3TestCase { origFolder.resolveFile("file.1").createFile(); origFolder.resolveFile("file.2").createFile(); - FileObject[] origFiles = origFolder.findFiles(new DepthFileSelector(1)); - assertEquals(4, origFiles.length); + assertEquals(3, origFolder.getChildren().length); FileObject destFolder = ROOT.resolveFile(FOLDER + "_dest"); assertFalse(destFolder.exists()); destFolder.copyFrom(origFolder, new DepthFileSelector(1)); assertTrue(destFolder.exists()); + assertEquals(3, destFolder.getChildren().length); + + FileObject[] origFiles = origFolder.findFiles(new DepthFileSelector(1)); FileObject[] destFiles = destFolder.findFiles(new DepthFileSelector(1)); - assertEquals(4, destFiles.length); - for (int i = 0; i < origFiles.length; i++) { assertEquals(origFiles[i].getName().getRelativeName(origFolder.getName()), destFiles[i].getName().getRelativeName(destFolder.getName())); @@ -168,10 +216,67 @@ public class S3FileProviderTest extends S3TestCase { assertFalse(destFolder.exists()); } -// public void testMoveFolder() throws FileSystemException { -// -// } + public void testMoveShallowFolder() throws FileSystemException { + FileObject origFolder = ROOT.resolveFile(FOLDER); + origFolder.delete(ALL_FILE_SELECTOR); + origFolder.createFolder(); + origFolder.resolveFile("file.0").createFile(); + origFolder.resolveFile("file.1").createFile(); + origFolder.resolveFile("file.2").createFile(); + + assertEquals(3, origFolder.getChildren().length); + + FileObject destFolder = ROOT.resolveFile(FOLDER + "_dest"); + destFolder.delete(ALL_FILE_SELECTOR); + assertFalse(destFolder.exists()); + + origFolder.moveTo(destFolder); + assertFalse(origFolder.exists()); + assertTrue(destFolder.exists()); + + assertEquals(3, destFolder.getChildren().length); + + destFolder.delete(ALL_FILE_SELECTOR); + + assertFalse(origFolder.exists()); + assertFalse(destFolder.exists()); + } + + public void testMoveDeepFolder() throws FileSystemException { + FileObject origFolder = ROOT.resolveFile(FOLDER); + origFolder.delete(ALL_FILE_SELECTOR); + origFolder.createFolder(); + + origFolder.resolveFile("file.0").createFile(); + origFolder.resolveFile("file.1").createFile(); + origFolder.resolveFile("file.2").createFile(); + origFolder.resolveFile("subfolder").createFolder(); + origFolder.resolveFile("subfolder").resolveFile("subfile.0").createFile(); + origFolder.resolveFile("subfolder").resolveFile("subfile.1").createFile(); + origFolder.resolveFile("subfolder").resolveFile("subfile.2").createFile(); + + + FileObject[] origFiles = origFolder.findFiles(ALL_FILE_SELECTOR); + assertEquals(8, origFiles.length); + + FileObject destFolder = ROOT.resolveFile(FOLDER + "_dest"); + destFolder.delete(ALL_FILE_SELECTOR); + assertFalse(destFolder.exists()); + + origFolder.moveTo(destFolder); + assertFalse(origFolder.exists()); + assertTrue(destFolder.exists()); + + FileObject[] destFiles = destFolder.findFiles(ALL_FILE_SELECTOR); + assertEquals(8, destFiles.length); + + destFolder.delete(ALL_FILE_SELECTOR); + + assertFalse(origFolder.exists()); + assertFalse(destFolder.exists()); + } + public void testCloseFileSystem() throws FileSystemException { VFS.getManager().closeFileSystem(ROOT.getFileSystem()); } blob - c592b4c0538d04698f16be3c7f792e4cf706d0f8 blob + ab1a74fa06b6c50787d3d4de5596e04bba672562 --- modules/webdav/pom.xml +++ modules/webdav/pom.xml @@ -17,13 +17,8 @@ - - moxo - com.thinkberg.moxo - 1.0-SNAPSHOT - ../../pom.xml - 4.0.0 + com.thinkberg webdav 1.0-SNAPSHOT thinkberg.com WebDAV @@ -48,7 +43,21 @@ commons-codec 1.3 - + + dom4j + dom4j + 1.6.1 + + + jaxen + jaxen + 1.1.1 + + + javax.servlet + servlet-api + 2.5 + @@ -58,6 +67,15 @@ + maven-compiler-plugin + + true + true + 1.5 + 1.5 + + + org.apache.maven.plugins maven-surefire-plugin blob - 09b60a7b33e721815fda022a1e7167429e306ca8 blob + bc0c6eff3013f0bb36bc5e4f530ddb7d5d520ba1 --- modules/webdav/src/main/java/com/thinkberg/webdav/DeleteHandler.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/DeleteHandler.java @@ -24,11 +24,12 @@ import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSelectInfo; import org.apache.commons.vfs.FileSelector; -import org.mortbay.jetty.Request; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.text.ParseException; /** @@ -50,12 +51,15 @@ public class DeleteHandler extends WebdavHandler { public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { FileObject object = VFSBackend.resolveFile(request.getPathInfo()); - if (request instanceof Request) { - String fragment = ((Request) request).getUri().getFragment(); + + try { + String fragment = new URI(request.getRequestURI()).getFragment(); if (fragment != null) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } + } catch (URISyntaxException e) { + throw new IOException(e.getMessage()); } try { blob - 204d06d06be8367f5bb86da172606b16b3c525ab blob + c47185b358b6ad5571018d737169ae5e2aa40ad1 --- modules/webdav/src/main/java/com/thinkberg/webdav/LockHandler.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/LockHandler.java @@ -147,7 +147,7 @@ public class LockHandler extends WebdavHandler { logXml(propDoc); } - private void logXml(Node element) { + protected void logXml(Node element) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint()); blob - 21fb7f4fd07568da5160aadb3563b7b18d3ed5f1 blob + 5635c52c1fce56d483cbf7f66f81e52dad5c6c75 --- modules/webdav/src/main/java/com/thinkberg/webdav/MoveHandler.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/MoveHandler.java @@ -16,7 +16,6 @@ package com.thinkberg.webdav; -import com.thinkberg.webdav.vfs.DepthFileSelector; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; @@ -26,7 +25,6 @@ import org.apache.commons.vfs.FileSystemException; */ public class MoveHandler extends CopyMoveBase { protected void copyOrMove(FileObject object, FileObject target, int depth) throws FileSystemException { - target.copyFrom(object, new DepthFileSelector(depth)); - object.delete(new DepthFileSelector()); + object.moveTo(target); } } blob - 00a7383b8d339f22c7242ad22205a99bfaec87cf blob + d7233be643552cc949f8535d2bdc4aade8257493 --- modules/webdav/src/main/java/com/thinkberg/webdav/PropFindHandler.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/PropFindHandler.java @@ -24,17 +24,19 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; -import org.dom4j.*; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -42,26 +44,19 @@ import java.util.List; * @version $Id$ */ public class PropFindHandler extends WebdavHandler { - private static final String TAG_PROP = "prop"; - private static final String TAG_ALLPROP = "allprop"; - private static final String TAG_PROPNAMES = "propnames"; private static final String TAG_MULTISTATUS = "multistatus"; private static final String TAG_HREF = "href"; private static final String TAG_RESPONSE = "response"; + + private static final String TAG_ALLPROP = "allprop"; + private static final String TAG_PROPNAMES = "propnames"; + private static final String TAG_PROP = "prop"; + + private static final List VALID_PROPFIND_TAGS = Arrays.asList( + TAG_ALLPROP, TAG_PROPNAMES, TAG_PROP + ); private static final Log LOG = LogFactory.getLog(PropFindHandler.class); - void logXml(Node element) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - try { - XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint()); - xmlWriter.write(element); - LOG.debug(bos.toString()); - } catch (IOException e) { - LOG.error(e.getMessage()); - } - } - - public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { SAXReader saxReader = new SAXReader(); try { @@ -69,59 +64,46 @@ public class PropFindHandler extends WebdavHandler { logXml(propDoc); Element propFindEl = propDoc.getRootElement(); - Element propEl = (Element) propFindEl.elementIterator().next(); - String propElName = propEl.getName(); + for (Object propElObject : propFindEl.elements()) { + Element propEl = (Element) propElObject; + if (VALID_PROPFIND_TAGS.contains(propEl.getName())) { + FileObject object = VFSBackend.resolveFile(request.getPathInfo()); + if (object.exists()) { + // respond as XML encoded multi status + response.setContentType("text/xml"); + response.setCharacterEncoding("UTF-8"); + response.setStatus(SC_MULTI_STATUS); - List requestedProperties = new ArrayList(); - boolean ignoreValues = false; - if (TAG_PROP.equals(propElName)) { - for (Object id : propEl.elements()) { - requestedProperties.add(((Element) id).getName()); + Document multiStatusResponse = + getMultiStatusResponse(object, + propEl, + getBaseUrl(request), + getDepth(request)); + logXml(multiStatusResponse); + + // write the actual response + XMLWriter writer = new XMLWriter(response.getWriter(), OutputFormat.createCompactFormat()); + writer.write(multiStatusResponse); + writer.flush(); + writer.close(); + + } else { + LOG.error(object.getName().getPath() + " NOT FOUND"); + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + break; } - } else if (TAG_ALLPROP.equals(propElName)) { - requestedProperties = DavResource.ALL_PROPERTIES; - } else if (TAG_PROPNAMES.equals(propElName)) { - requestedProperties = DavResource.ALL_PROPERTIES; - ignoreValues = true; } - - FileObject object = VFSBackend.resolveFile(request.getPathInfo()); - if (object.exists()) { - // respond as XML encoded multi status - response.setContentType("text/xml"); - response.setCharacterEncoding("UTF-8"); - response.setStatus(SC_MULTI_STATUS); - - Document multiStatusResponse = - getMultiStatusRespons(object, - requestedProperties, - getBaseUrl(request), - getDepth(request), - ignoreValues); - logXml(multiStatusResponse); - - // write the actual response - XMLWriter writer = new XMLWriter(response.getWriter(), OutputFormat.createCompactFormat()); - writer.write(multiStatusResponse); - writer.flush(); - writer.close(); - - } else { - LOG.error(object.getName().getPath() + " NOT FOUND"); - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } } catch (DocumentException e) { LOG.error("invalid request: " + e.getMessage()); response.sendError(HttpServletResponse.SC_BAD_REQUEST); } } - @SuppressWarnings({"ConstantConditions"}) - private Document getMultiStatusRespons(FileObject object, - List requestedProperties, - URL baseUrl, - int depth, - boolean ignoreValues) throws FileSystemException { + private Document getMultiStatusResponse(FileObject object, + Element propEl, + URL baseUrl, + int depth) throws FileSystemException { Document propDoc = DocumentHelper.createDocument(); propDoc.setXMLEncoding("UTF-8"); @@ -131,14 +113,12 @@ public class PropFindHandler extends WebdavHandler { Element responseEl = multiStatus.addElement(TAG_RESPONSE); try { URL url = new URL(baseUrl, URLEncoder.encode(child.getName().getPath(), "UTF-8")); - LOG.debug(url); responseEl.addElement(TAG_HREF).addText(url.toExternalForm()); } catch (Exception e) { - e.printStackTrace(); + LOG.error("can't set href in response", e); } DavResource resource = DavResourceFactory.getInstance().getDavResource(child); - resource.setIgnoreValues(ignoreValues); - resource.serializeToXml(responseEl, requestedProperties); + resource.getPropertyValues(responseEl, propEl); } return propDoc; } blob - 07ffae65a4113a2f5526d060bb9abd511fc50a5a blob + 50f915eb7b1c5158cd909b49560628c9b14048a8 --- modules/webdav/src/main/java/com/thinkberg/webdav/PropPatchHandler.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/PropPatchHandler.java @@ -16,13 +16,16 @@ package com.thinkberg.webdav; +import com.thinkberg.webdav.data.AbstractDavResource; import com.thinkberg.webdav.data.DavResource; +import com.thinkberg.webdav.data.DavResourceFactory; import com.thinkberg.webdav.lock.LockException; import com.thinkberg.webdav.lock.LockManager; import com.thinkberg.webdav.vfs.VFSBackend; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileObject; +import org.apache.commons.vfs.FileSystemException; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; @@ -36,6 +39,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URL; import java.text.ParseException; +import java.util.ArrayList; import java.util.List; /** @@ -47,6 +51,10 @@ import java.util.List; public class PropPatchHandler extends WebdavHandler { private static final Log LOG = LogFactory.getLog(PropPatchHandler.class); + private static final String TAG_MULTISTATUS = "multistatus"; + private static final String TAG_HREF = "href"; + private static final String TAG_RESPONSE = "response"; + public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { FileObject object = VFSBackend.resolveFile(request.getPathInfo()); @@ -63,76 +71,66 @@ public class PropPatchHandler extends WebdavHandler { return; } - SAXReader saxReader = new SAXReader(); - try { - Document propDoc = saxReader.read(request.getInputStream()); -// log(propDoc); + if (object.exists()) { + SAXReader saxReader = new SAXReader(); + try { + Document propDoc = saxReader.read(request.getInputStream()); + logXml(propDoc); - response.setContentType("text/xml"); - response.setCharacterEncoding("UTF-8"); - response.setStatus(SC_MULTI_STATUS); - - if (object.exists()) { - Document resultDoc = DocumentHelper.createDocument(); - Element multiStatusResponse = resultDoc.addElement("multistatus", "DAV:"); - Element responseEl = multiStatusResponse.addElement("response"); - try { - URL url = new URL(getBaseUrl(request), URLEncoder.encode(object.getName().getPath(), "UTF-8")); - LOG.debug(url); - responseEl.addElement("href").addText(url.toExternalForm()); - } catch (Exception e) { - e.printStackTrace(); - } - - Element propstatEl = responseEl.addElement("propstat"); - Element propEl = propstatEl.addElement("prop"); - - Element propertyUpdateEl = propDoc.getRootElement(); - for (Object elObject : propertyUpdateEl.elements()) { + Element propUpdateEl = propDoc.getRootElement(); + List requestedProperties = new ArrayList(); + for (Object elObject : propUpdateEl.elements()) { Element el = (Element) elObject; - if ("set".equals(el.getName())) { - for (Object propObject : el.elements()) { - setProperty(propEl, object, (Element) propObject); + String command = el.getName(); + if (AbstractDavResource.TAG_PROP_SET.equals(command) || AbstractDavResource.TAG_PROP_REMOVE.equals(command)) { + for (Object propElObject : el.elements()) { + for (Object propNameElObject : ((Element) propElObject).elements()) { + Element propNameEl = (Element) propNameElObject; + requestedProperties.add(propNameEl); + } } - } else if ("remove".equals(el.getName())) { - for (Object propObject : el.elements()) { - removeProperty(propEl, object, (Element) propObject); - } } } - propstatEl.addElement("status").addText(DavResource.STATUS_403); -// log(resultDoc); + // respond as XML encoded multi status + response.setContentType("text/xml"); + response.setCharacterEncoding("UTF-8"); + response.setStatus(SC_MULTI_STATUS); + Document multiStatusResponse = getMultiStatusResponse(object, requestedProperties, getBaseUrl(request)); + + logXml(multiStatusResponse); + // write the actual response XMLWriter writer = new XMLWriter(response.getWriter(), OutputFormat.createCompactFormat()); - writer.write(resultDoc); + writer.write(multiStatusResponse); writer.flush(); writer.close(); - } else { - LOG.error(object.getName().getPath() + " NOT FOUND"); - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } catch (DocumentException e) { - LOG.error("invalid request: " + e.getMessage()); - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - private void setProperty(Element root, FileObject object, Element el) { - List propList = el.elements(); - for (Object propElObject : propList) { - Element propEl = (Element) propElObject; - for (int i = 0; i < propEl.nodeCount(); i++) { - propEl.node(i).detach(); + } catch (DocumentException e) { + LOG.error("invalid request: " + e.getMessage()); + response.sendError(HttpServletResponse.SC_BAD_REQUEST); } - root.add(propEl.detach()); + } else { + LOG.error(object.getName().getPath() + " NOT FOUND"); + response.sendError(HttpServletResponse.SC_NOT_FOUND); } } - private void removeProperty(Element root, FileObject object, Element el) { - setProperty(root, object, el); - } + private Document getMultiStatusResponse(FileObject object, List requestedProperties, URL baseUrl) throws FileSystemException { + Document propDoc = DocumentHelper.createDocument(); + propDoc.setXMLEncoding("UTF-8"); - + Element multiStatus = propDoc.addElement(TAG_MULTISTATUS, "DAV:"); + Element responseEl = multiStatus.addElement(TAG_RESPONSE); + try { + URL url = new URL(baseUrl, URLEncoder.encode(object.getName().getPath(), "UTF-8")); + responseEl.addElement(TAG_HREF).addText(url.toExternalForm()); + } catch (Exception e) { + LOG.error("can't set HREF tag in response", e); + } + DavResource resource = DavResourceFactory.getInstance().getDavResource(object); + resource.setPropertyValues(responseEl, requestedProperties); + return propDoc; + } } blob - bca6db1498389193b487c1a5c50753dda989f4c5 blob + c4c58177f10fdcc3fa24ad733291fa87a7f918e5 --- modules/webdav/src/main/java/com/thinkberg/webdav/WebdavHandler.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/WebdavHandler.java @@ -21,9 +21,13 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; +import org.dom4j.Node; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -147,4 +151,15 @@ public abstract class WebdavHandler { } return -1; } + + void logXml(Node element) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint()); + xmlWriter.write(element); + LogFactory.getLog(this.getClass()).debug(bos.toString()); + } catch (IOException e) { + LogFactory.getLog(this.getClass()).error(e.getMessage()); + } + } } blob - 953b5f534b2c224ad5eb65cd370f36d6232bca2d blob + 693bd57dff62a318333b2d882a4f4668698b55d2 --- modules/webdav/src/main/java/com/thinkberg/webdav/data/AbstractDavResource.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/data/AbstractDavResource.java @@ -16,50 +16,196 @@ package com.thinkberg.webdav.data; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.vfs.FileContent; +import org.apache.commons.vfs.FileObject; +import org.apache.commons.vfs.FileSystemException; import org.dom4j.Element; +import org.dom4j.Node; -import java.util.HashSet; +import java.util.Arrays; import java.util.List; -import java.util.Set; /** * @author Matthias L. Jugel * @version $Id$ */ public abstract class AbstractDavResource { - private static final String STATUS_200 = "HTTP/1.1 200 OK"; - private static final String STATUS_404 = "HTTP/1.1 404 Not Found"; + public static final String STATUS_200 = "HTTP/1.1 200 OK"; + public static final String STATUS_404 = "HTTP/1.1 404 Not Found"; public static final String STATUS_403 = "HTTP/1.1 403 Forbidden"; + public static final String STATUS_422 = "HTTP/1.1 422 Unprocessable Entity"; - private static final String TAG_PROPSTAT = "propstat"; - private static final String TAG_PROP = "prop"; - private static final String TAG_STATUS = "status"; + public static final String TAG_ALLPROP = "allprop"; + public static final String TAG_PROPNAMES = "propnames"; - public Element serializeToXml(Element root, List requestedProperties) { - Element propStatEl = root.addElement(TAG_PROPSTAT); - Element propEl = propStatEl.addElement(TAG_PROP); + public static final String TAG_PROPSTAT = "propstat"; + public static final String TAG_PROP = "prop"; + public static final String TAG_STATUS = "status"; + public static final String TAG_PROP_SET = "set"; + public static final String TAG_PROP_REMOVE = "remove"; - Set missingProperties = new HashSet(); - for (String propertyName : requestedProperties) { - if (!addPropertyValue(propEl, propertyName)) { - missingProperties.add(propertyName); + // @see http://www.webdav.org/specs/rfc2518.html#dav.properties + public static final String PROP_CREATION_DATE = "creationdate"; + public static final String PROP_DISPLAY_NAME = "displayname"; + public static final String PROP_GET_CONTENT_LANGUAGE = "getcontentlanguage"; + public static final String PROP_GET_CONTENT_LENGTH = "getcontentlength"; + public static final String PROP_GET_CONTENT_TYPE = "getcontenttype"; + public static final String PROP_GET_ETAG = "getetag"; + public static final String PROP_GET_LAST_MODIFIED = "getlastmodified"; + public static final String PROP_LOCK_DISCOVERY = "lockdiscovery"; + public static final String PROP_RESOURCETYPE = "resourcetype"; + public static final String PROP_SOURCE = "source"; + public static final String PROP_SUPPORTED_LOCK = "supportedlock"; + + // non-standard properties + static final String PROP_QUOTA = "quota"; + static final String PROP_QUOTA_USED = "quotaused"; + static final String PROP_QUOTA_AVAILABLE_BYTES = "quota-available-bytes"; + static final String PROP_QUOTA_USED_BYTES = "quota-used-bytes"; + + // list of standard supported properties (for allprop/propname) + public static final List ALL_PROPERTIES = Arrays.asList( + PROP_CREATION_DATE, + PROP_DISPLAY_NAME, + PROP_GET_CONTENT_LANGUAGE, + PROP_GET_CONTENT_LENGTH, + PROP_GET_CONTENT_TYPE, + PROP_GET_ETAG, + PROP_GET_LAST_MODIFIED, + PROP_LOCK_DISCOVERY, + PROP_RESOURCETYPE, + PROP_SOURCE, + PROP_SUPPORTED_LOCK + ); + + protected final FileObject object; + + public AbstractDavResource(FileObject object) { + this.object = object; + } + + public Element setPropertyValues(Element root, List requestedProperties) { + // initialize the element for 200 + Element okPropStatEl = root.addElement(TAG_PROPSTAT); + Element okPropEl = okPropStatEl.addElement(TAG_PROP); + + // initialize the element for 422 + Element failPropStatEl = root.addElement(TAG_PROPSTAT); + Element failPropEl = failPropStatEl.addElement(TAG_PROP); + + // go through the properties and try to set/remove them, + // if it fails, add to the failed list + for (Node propertyEl : requestedProperties) { + if (!setPropertyValue(okPropEl, (Element) propertyEl)) { + failPropEl.addElement(((Element) propertyEl).getQName()); } } - propStatEl.addElement(TAG_STATUS).addText(STATUS_200); - // add missing properties status - if (missingProperties.size() > 0) { - propStatEl = root.addElement(TAG_PROPSTAT); - propEl = propStatEl.addElement(TAG_PROP); - for (String el : missingProperties) { - propEl.addElement(el); + // only add the OK section, if there is content + if (okPropEl.elements().size() > 0) { + okPropStatEl.addElement(TAG_STATUS).addText(STATUS_200); + } else { + okPropStatEl.detach(); + } + + // only add the failed section, if there is content + if (failPropEl.elements().size() > 0) { + failPropStatEl.addElement(TAG_STATUS).addText(STATUS_422); + } else { + failPropStatEl.detach(); + } + + return root; + } + + public Element getPropertyValues(Element root, Element propertyEl) { + // initialize the for 200 + Element okPropStatEl = root.addElement(TAG_PROPSTAT); + Element okPropEl = okPropStatEl.addElement(TAG_PROP); + + // initialize the element for 404 + Element failPropStatEl = root.addElement(TAG_PROPSTAT); + Element failPropEl = failPropStatEl.addElement(TAG_PROP); + + if (TAG_ALLPROP.equalsIgnoreCase(propertyEl.getName()) || + TAG_PROPNAMES.equalsIgnoreCase(propertyEl.getName())) { + boolean ignoreValue = TAG_PROPNAMES.equalsIgnoreCase(propertyEl.getName()); + + // get all known standard properties + for (String propName : ALL_PROPERTIES) { + if (!getPropertyValue(okPropEl, propName, ignoreValue)) { + failPropEl.addElement(propName); + } } - propStatEl.addElement(TAG_STATUS).addText(STATUS_404); + + // additionally try to add all the custom properties + try { + FileContent objectContent = object.getContent(); + for (String attributeName : objectContent.getAttributeNames()) { + if (!getPropertyValue(okPropEl, attributeName, ignoreValue)) { + failPropEl.addElement(attributeName); + } + } + } catch (FileSystemException e) { + LogFactory.getLog(getClass()).error(String.format("can't read attribute properties from '%s'", + object.getName()), e); + } + } else { + List requestedProperties = propertyEl.elements(); + for (Object propertyElObject : requestedProperties) { + Element propEl = (Element) propertyElObject; + final String nameSpace = propEl.getNamespaceURI(); + if (!getPropertyValue(okPropEl, getFQName(nameSpace, propEl.getQualifiedName()), false)) { + failPropEl.addElement(propEl.getQName()); + } + } } + // only add the OK section, if there is content + if (okPropEl.elements().size() > 0) { + okPropStatEl.addElement(TAG_STATUS).addText(STATUS_200); + } else { + okPropStatEl.detach(); + } + + // only add the failed section, if there is content + if (failPropEl.elements().size() > 0) { + failPropStatEl.addElement(TAG_STATUS).addText(STATUS_404); + } else { + failPropStatEl.detach(); + } + + return root; } - @SuppressWarnings({"BooleanMethodIsAlwaysInverted"}) - protected abstract boolean addPropertyValue(Element root, String propertyName); + protected String getFQName(String nameSpace, String name) { + String prefix = ""; + if (!"DAV:".equals(nameSpace) && null != nameSpace && !"".equals(nameSpace)) { + prefix = new String(Base64.encodeBase64(nameSpace.getBytes())); + } + return String.format("%s%s", prefix, name); + } + + /** + * Set the property and its value. Returns false if the property cannot be processed. + * + * @param root the response stat element + * @param propertyEl the property element to set + * @return false if this property cannot be set + */ + protected abstract boolean setPropertyValue(Element root, Element propertyEl); + + /** + * Get the property value and append it to the xml document (root). If this method + * returns false, the property does not exist. + * + * @param root the root element to add the property name (and possible value to) + * @param propertyName the property name to read + * @param ignoreValue ignore the value and just add the name + * @return whether the property exists + */ + protected abstract boolean getPropertyValue(Element root, String propertyName, boolean ignoreValue); } blob - 0e47f326c1df304cd650f3a542497617366517b4 blob + 809b8be1ec0c49b3121f5f5ed7b841cd070a4543 --- modules/webdav/src/main/java/com/thinkberg/webdav/data/DavCollection.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/data/DavCollection.java @@ -30,12 +30,7 @@ public class DavCollection extends DavResource { super(object); } - - public DavCollection(FileObject object, boolean ignoreValues) { - super(object, ignoreValues); - } - - protected boolean addResourceTypeProperty(Element root) { + protected boolean addResourceTypeProperty(Element root, boolean ignoreValue) { root.addElement(PROP_RESOURCETYPE).addElement(COLLECTION); return true; } @@ -46,7 +41,7 @@ public class DavCollection extends DavResource { * @param root the prop element to add to * @return true, even though nothing is added */ - protected boolean addGetContentLanguageProperty(Element root) { + protected boolean addGetContentLanguageProperty(Element root, boolean ignoreValue) { return true; } @@ -56,7 +51,7 @@ public class DavCollection extends DavResource { * @param root the prop element to add to * @return true, even though nothing is added */ - protected boolean addGetContentLengthProperty(Element root) { + protected boolean addGetContentLengthProperty(Element root, boolean ignoreValue) { return true; } @@ -66,27 +61,27 @@ public class DavCollection extends DavResource { * @param root the prop element to add to * @return true, even though nothing is added */ - protected boolean addGetContentTypeProperty(Element root) { + protected boolean addGetContentTypeProperty(Element root, boolean ignoreValue) { return true; } - protected boolean addQuotaProperty(Element root) { + protected boolean addQuotaProperty(Element root, boolean ignoreValue) { root.addElement(PROP_QUOTA).addText("" + Long.MAX_VALUE); return true; } - protected boolean addQuotaUsedProperty(Element root) { + protected boolean addQuotaUsedProperty(Element root, boolean ignoreValue) { // TODO add correct handling of used quota root.addElement(PROP_QUOTA_USED).addText("0"); return true; } - protected boolean addQuotaAvailableBytesProperty(Element root) { + protected boolean addQuotaAvailableBytesProperty(Element root, boolean ignoreValue) { root.addElement(PROP_QUOTA_AVAILABLE_BYTES).addText(Long.toHexString(Long.MAX_VALUE)); return true; } - protected boolean addQuotaUsedBytesProperty(Element root) { + protected boolean addQuotaUsedBytesProperty(Element root, boolean ignoreValue) { // TODO add correct handling of used quota root.addElement(PROP_QUOTA_USED_BYTES).addText("0"); return true; blob - 3d01502a97a50c6eb672345d40fd43c6eee5b1e7 blob + cca7886e245ede67ecb82ca0d3dfb3bde4a06852 --- modules/webdav/src/main/java/com/thinkberg/webdav/data/DavResource.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/data/DavResource.java @@ -19,11 +19,17 @@ package com.thinkberg.webdav.data; import com.thinkberg.webdav.Util; import com.thinkberg.webdav.lock.Lock; import com.thinkberg.webdav.lock.LockManager; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.vfs.FileContent; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; import org.dom4j.Element; -import java.util.Arrays; +import java.io.IOException; +import java.io.StringWriter; import java.util.List; /** @@ -32,131 +38,135 @@ import java.util.List; */ public class DavResource extends AbstractDavResource { - // @see http://www.webdav.org/specs/rfc2518.html#dav.properties - public static final String PROP_CREATION_DATE = "creationdate"; - public static final String PROP_DISPLAY_NAME = "displayname"; - private static final String PROP_GET_CONTENT_LANGUAGE = "getcontentlanguage"; - private static final String PROP_GET_CONTENT_LENGTH = "getcontentlength"; - private static final String PROP_GET_CONTENT_TYPE = "getcontenttype"; - private static final String PROP_GET_ETAG = "getetag"; - private static final String PROP_GET_LAST_MODIFIED = "getlastmodified"; - private static final String PROP_LOCK_DISCOVERY = "lockdiscovery"; - public static final String PROP_RESOURCETYPE = "resourcetype"; - private static final String PROP_SOURCE = "source"; - private static final String PROP_SUPPORTED_LOCK = "supportedlock"; - - // non-standard properties - static final String PROP_QUOTA = "quota"; - static final String PROP_QUOTA_USED = "quotaused"; - static final String PROP_QUOTA_AVAILABLE_BYTES = "quota-available-bytes"; - static final String PROP_QUOTA_USED_BYTES = "quota-used-bytes"; - - // list of standard supported properties (for allprop/propname) - public static final List ALL_PROPERTIES = Arrays.asList( - PROP_CREATION_DATE, - PROP_DISPLAY_NAME, - PROP_GET_CONTENT_LANGUAGE, - PROP_GET_CONTENT_LENGTH, - PROP_GET_CONTENT_TYPE, - PROP_GET_ETAG, - PROP_GET_LAST_MODIFIED, - PROP_LOCK_DISCOVERY, - PROP_RESOURCETYPE, - PROP_SOURCE, - PROP_SUPPORTED_LOCK - ); - - protected final FileObject object; - protected boolean ignoreValues = false; - public DavResource(FileObject object) { - this(object, false); + super(object); } + protected boolean setPropertyValue(Element root, Element propertyEl) { + LogFactory.getLog(getClass()).debug(String.format("[%s].set('%s')", object.getName(), propertyEl.asXML())); - public DavResource(FileObject object, boolean ignoreValues) { - this.object = object; - this.ignoreValues = ignoreValues; - + if (!ALL_PROPERTIES.contains(propertyEl.getName())) { + final String nameSpace = propertyEl.getNamespaceURI(); + final String attributeName = getFQName(nameSpace, propertyEl.getQualifiedName()); + try { + FileContent objectContent = object.getContent(); + final String command = propertyEl.getParent().getParent().getName(); + if (TAG_PROP_SET.equals(command)) { + StringWriter propertyValueWriter = new StringWriter(); + propertyEl.write(propertyValueWriter); + propertyValueWriter.close(); + objectContent.setAttribute(attributeName, propertyValueWriter.getBuffer().toString()); + } else if (TAG_PROP_REMOVE.equals(command)) { + objectContent.setAttribute(attributeName, null); + } + root.addElement(propertyEl.getQName()); + return true; + } catch (IOException e) { + LogFactory.getLog(getClass()).error(String.format("can't store attribute property '%s' = '%s'", + attributeName, + propertyEl.asXML()), e); + } + } + return false; } /** - * Ignore values - * - * @param ignoreValues true if the serialized xml should not contain values - */ - public void setIgnoreValues(boolean ignoreValues) { - this.ignoreValues = ignoreValues; - } - - /** * Add the value for a given property to the result document. If the value * is missing or can not be added for some reason it will return false to * indicate a missing property. * * @param root the root element for the result document fragment - * @param propertyName the property name to add + * @param propertyName the property name to query * @return true for successful addition and false for missing data */ - protected boolean addPropertyValue(Element root, String propertyName) { + protected boolean getPropertyValue(Element root, String propertyName, boolean ignoreValue) { + LogFactory.getLog(getClass()).debug(String.format("[%s].get('%s')", object.getName(), propertyName)); if (PROP_CREATION_DATE.equals(propertyName)) { - return addCreationDateProperty(root); + return addCreationDateProperty(root, ignoreValue); } else if (PROP_DISPLAY_NAME.equals(propertyName)) { - return addGetDisplayNameProperty(root); + return addGetDisplayNameProperty(root, ignoreValue); } else if (PROP_GET_CONTENT_LANGUAGE.equals(propertyName)) { - return addGetContentLanguageProperty(root); + return addGetContentLanguageProperty(root, ignoreValue); } else if (PROP_GET_CONTENT_LENGTH.equals(propertyName)) { - return addGetContentLengthProperty(root); + return addGetContentLengthProperty(root, ignoreValue); } else if (PROP_GET_CONTENT_TYPE.equals(propertyName)) { - return addGetContentTypeProperty(root); + return addGetContentTypeProperty(root, ignoreValue); } else if (PROP_GET_ETAG.equals(propertyName)) { - return addGetETagProperty(root); + return addGetETagProperty(root, ignoreValue); } else if (PROP_GET_LAST_MODIFIED.equals(propertyName)) { - return addGetLastModifiedProperty(root); + return addGetLastModifiedProperty(root, ignoreValue); } else if (PROP_LOCK_DISCOVERY.equals(propertyName)) { - return addLockDiscoveryProperty(root); + return addLockDiscoveryProperty(root, ignoreValue); } else if (PROP_RESOURCETYPE.equals(propertyName)) { - return addResourceTypeProperty(root); + return addResourceTypeProperty(root, ignoreValue); } else if (PROP_SOURCE.equals(propertyName)) { - return addSourceProperty(root); + return addSourceProperty(root, ignoreValue); } else if (PROP_SUPPORTED_LOCK.equals(propertyName)) { - return addSupportedLockProperty(root); + return addSupportedLockProperty(root, ignoreValue); } else { // handle non-standard properties (keep a little separate) if (PROP_QUOTA.equals(propertyName)) { - return addQuotaProperty(root); + return addQuotaProperty(root, ignoreValue); } else if (PROP_QUOTA_USED.equals(propertyName)) { - return addQuotaUsedProperty(root); + return addQuotaUsedProperty(root, ignoreValue); } else if (PROP_QUOTA_AVAILABLE_BYTES.equals(propertyName)) { - return addQuotaAvailableBytesProperty(root); + return addQuotaAvailableBytesProperty(root, ignoreValue); } else if (PROP_QUOTA_USED_BYTES.equals(propertyName)) { - return addQuotaUsedBytesProperty(root); + return addQuotaUsedBytesProperty(root, ignoreValue); + } else { + try { + Object propertyValue = object.getContent().getAttribute(propertyName); + if (null != propertyValue) { + if (((String) propertyValue).startsWith("<")) { + try { + Document property = DocumentHelper.parseText((String) propertyValue); + if (ignoreValue) { + property.clearContent(); + } + root.add(property.getRootElement().detach()); + return true; + } catch (DocumentException e) { + LogFactory.getLog(getClass()).error("property value unparsable", e); + return false; + } + } else { + Element el = root.addElement(propertyName); + if (!ignoreValue) { + el.addText((String) propertyValue); + } + return true; + } + + } + } catch (FileSystemException e) { + LogFactory.getLog(this.getClass()).error(String.format("property '%s' is not supported", propertyName), e); + } } } return false; } - protected boolean addCreationDateProperty(Element root) { + protected boolean addCreationDateProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addGetDisplayNameProperty(Element root) { + protected boolean addGetDisplayNameProperty(Element root, boolean ignoreValue) { Element el = root.addElement(PROP_DISPLAY_NAME); - if (!ignoreValues) { + if (!ignoreValue) { el.addCDATA(object.getName().getBaseName()); } return true; } - protected boolean addGetContentLanguageProperty(Element root) { + protected boolean addGetContentLanguageProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addGetContentLengthProperty(Element root) { + protected boolean addGetContentLengthProperty(Element root, boolean ignoreValue) { try { Element el = root.addElement(PROP_GET_CONTENT_LENGTH); - if (!ignoreValues) { + if (!ignoreValue) { el.addText("" + object.getContent().getSize()); } return true; @@ -166,7 +176,7 @@ public class DavResource extends AbstractDavResource { } } - protected boolean addGetContentTypeProperty(Element root) { + protected boolean addGetContentTypeProperty(Element root, boolean ignoreValue) { try { String contentType = object.getContent().getContentInfo().getContentType(); if (null == contentType || "".equals(contentType)) { @@ -174,7 +184,7 @@ public class DavResource extends AbstractDavResource { } Element el = root.addElement(PROP_GET_CONTENT_TYPE); - if (!ignoreValues) { + if (!ignoreValue) { el.addText(contentType); } return true; @@ -184,14 +194,14 @@ public class DavResource extends AbstractDavResource { } } - protected boolean addGetETagProperty(Element root) { + protected boolean addGetETagProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addGetLastModifiedProperty(Element root) { + protected boolean addGetLastModifiedProperty(Element root, boolean ignoreValue) { try { Element el = root.addElement(PROP_GET_LAST_MODIFIED); - if (!ignoreValues) { + if (!ignoreValue) { el.addText(Util.getDateString(object.getContent().getLastModifiedTime())); } return true; @@ -201,37 +211,37 @@ public class DavResource extends AbstractDavResource { } } - protected boolean addLockDiscoveryProperty(Element root) { + protected boolean addLockDiscoveryProperty(Element root, boolean ignoreValue) { Element lockdiscoveryEl = root.addElement(PROP_LOCK_DISCOVERY); try { List locks = LockManager.getInstance().discoverLock(object); if (locks != null && !locks.isEmpty()) { for (Lock lock : locks) { - if (lock != null && !ignoreValues) { + if (lock != null && !ignoreValue) { lock.serializeToXml(lockdiscoveryEl); } } } return true; } catch (FileSystemException e) { - e.printStackTrace(); root.remove(lockdiscoveryEl); + e.printStackTrace(); return false; } } - protected boolean addResourceTypeProperty(Element root) { + protected boolean addResourceTypeProperty(Element root, boolean ignoreValue) { root.addElement(PROP_RESOURCETYPE); return true; } - protected boolean addSourceProperty(Element root) { + protected boolean addSourceProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addSupportedLockProperty(Element root) { + protected boolean addSupportedLockProperty(Element root, boolean ignoreValue) { Element supportedlockEl = root.addElement(PROP_SUPPORTED_LOCK); - if (!ignoreValues) { + if (!ignoreValue) { Element exclLockentryEl = supportedlockEl.addElement("lockentry"); exclLockentryEl.addElement("lockscope").addElement("exclusive"); exclLockentryEl.addElement("locktype").addElement("write"); @@ -243,19 +253,19 @@ public class DavResource extends AbstractDavResource { return true; } - protected boolean addQuotaProperty(Element root) { + protected boolean addQuotaProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addQuotaUsedProperty(Element root) { + protected boolean addQuotaUsedProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addQuotaAvailableBytesProperty(Element root) { + protected boolean addQuotaAvailableBytesProperty(Element root, boolean ignoreValue) { return false; } - protected boolean addQuotaUsedBytesProperty(Element root) { + protected boolean addQuotaUsedBytesProperty(Element root, boolean ignoreValue) { return false; } } blob - 0bd27718adde8f76ebb9bf18691ac0317fb46631 blob + 93266e8ffdd305cc0bad778b0ea41e45055fa1b7 --- modules/webdav/src/main/java/com/thinkberg/webdav/servlet/MoxoWebDAVServlet.java +++ modules/webdav/src/main/java/com/thinkberg/webdav/servlet/MoxoWebDAVServlet.java @@ -24,7 +24,6 @@ import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.FileSystemOptions; import org.apache.commons.vfs.auth.StaticUserAuthenticator; import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder; -import org.mortbay.jetty.Response; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -113,8 +112,6 @@ public class MoxoWebDAVServlet extends HttpServlet { } else { response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); } - Response jettyResponse = ((Response) response); - String reason = jettyResponse.getReason(); - LOG.debug(String.format("<< %s (%d%s)", request.getMethod(), jettyResponse.getStatus(), reason != null ? ": " + reason : "")); + LOG.debug(String.format("<< %s (%s)", request.getMethod(), response.toString().replaceAll("[\\r\\n]+", ""))); } } blob - 2389b60f777cec922576a63c3d4ad792dd4073da blob + 8f0bdb3b66beb18d36e1200ac5a043a0182ada4f --- modules/webdav/src/test/java/com/thinkberg/webdav/DavTestCase.java +++ modules/webdav/src/test/java/com/thinkberg/webdav/DavTestCase.java @@ -11,8 +11,6 @@ import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.Node; -import java.util.Arrays; - /** * Helper class for DAV tests. * @@ -73,8 +71,12 @@ public class DavTestCase extends TestCase { Element root = DocumentHelper.createElement("root"); DavResourceFactory factory = DavResourceFactory.getInstance(); DavResource davResource = factory.getDavResource(object); - davResource.setIgnoreValues(ignoreValues); - davResource.serializeToXml(root, Arrays.asList(propertyName)); + + Element testPropertyEl = (Element) root.addElement("prop").detach(); + testPropertyEl.addElement(propertyName); + + davResource.getPropertyValues(root, testPropertyEl); + return root; } blob - 06c1c46a60a4facb13fe413c6195580c5b6a3282 blob + 3a0fa1b9ecede2c113b5c0fca9111d6041b9e615 --- pom.xml +++ pom.xml @@ -1,38 +1,32 @@ 4.0.0 - com.thinkberg.moxo + com.thinkberg moxo pom 1.0-SNAPSHOT + Moxo S3 DAV Proxy modules/webdav modules/vfs.s3 - Moxo S3 DAV Proxy http://thinkberg.com - junit - junit - 3.8.1 - test - - org.mortbay.jetty jetty 6.1.1 - dom4j - dom4j - 1.6.1 + com.thinkberg + webdav + 1.0-SNAPSHOT - - - - - + + com.thinkberg + vfs.s3 + 1.0-SNAPSHOT + @@ -96,14 +90,25 @@ org.apache.maven.plugins maven-jar-plugin - - - - com.thinkberg.moxo.Main - true - - - + + + package + + jar + + + + + com.thinkberg.moxo.Main + true + + + + **/moxo/* + + + + blob - 5b43b0aee4de7daf1be61d57c1bd4be286fef0fd blob + 3048290ec50e533b9e6bc29c272b59405f156dad --- src/main/resources/simplelog.properties +++ src/main/resources/simplelog.properties @@ -15,4 +15,5 @@ # org.apache.commons.logging.simplelog.defaultlog=error -org.apache.commons.logging.simplelog.log.com.thinkberg.moxo=debug \ No newline at end of file +org.apache.commons.logging.simplelog.log.com.thinkberg.vfs.s3=debug +org.apache.commons.logging.simplelog.log.com.thinkberg.webdav=debug \ No newline at end of file