View Javadoc

1   /*
2    * Copyright (c) 2006-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.danube.webdav.spring;
14  
15  import org.abstracthorizon.danube.http.Status;
16  import org.abstracthorizon.danube.http.util.IOUtils;
17  import org.abstracthorizon.danube.webdav.ResourceAdapter;
18  import org.abstracthorizon.danube.webdav.lock.LockingMechanism;
19  import org.abstracthorizon.danube.webdav.lock.impl.SimpleInMemoryLockingMechanism;
20  import org.abstracthorizon.danube.webdav.util.NamespacesProvider;
21  import org.abstracthorizon.danube.webdav.util.SimpleNamespacesProvider;
22  import org.abstracthorizon.danube.webdav.xml.dav.DAVNamespace;
23  import org.abstracthorizon.danube.webdav.xml.dav.request.properties.RequestProperty;
24  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.CreationDate;
25  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.DisplayName;
26  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetContentLength;
27  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetContentType;
28  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetETag;
29  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetLastModified;
30  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.LockDiscovery;
31  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.ResourceType;
32  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.ResponseProperty;
33  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.SupportedLock;
34  
35  import java.io.File;
36  import java.io.FileInputStream;
37  import java.io.FileOutputStream;
38  import java.io.IOException;
39  import java.io.InputStream;
40  import java.io.OutputStream;
41  import java.net.URL;
42  
43  import org.springframework.core.io.DescriptiveResource;
44  import org.springframework.core.io.Resource;
45  
46  /**
47   * Simple file system resource adapter
48   *
49   * @author Daniel Sendula
50   */
51  public class SpringResourceWebDAVResourceAdapter implements ResourceAdapter {
52  
53      /** Root path of the files to be served */
54      protected Resource path;
55  
56      /** Default file property names */
57      protected static ResponseProperty[] filePropertyNames = new ResponseProperty[]{
58          new CreationDate(Status.OK),
59          new DisplayName(Status.OK),
60          new GetContentLength(Status.OK),
61          new GetContentType(Status.OK),
62          new GetETag(Status.OK),
63          new GetLastModified(Status.OK),
64          new LockDiscovery(Status.OK),
65          new ResourceType(Status.OK),
66          // new Source(null),
67          new SupportedLock(Status.OK)
68      };
69  
70      /** Default directory property names */
71      protected static ResponseProperty[] dirPropertyNames = new ResponseProperty[]{
72          new CreationDate(Status.OK),
73          new DisplayName(Status.OK),
74          new GetETag(Status.OK),
75          new GetLastModified(Status.OK),
76          new LockDiscovery(Status.OK),
77          new ResourceType(Status.OK),
78          // new Source(null),
79          new SupportedLock(Status.OK)
80      };
81  
82      /** Default file request properties */
83      protected static RequestProperty[] fileRequestProperties = new RequestProperty[]{
84          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.CreationDate(null),
85          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.DisplayName(null),
86          new org.abstracthorizon.danube.webdav.spring.properties.GetContentLength(null),
87          new org.abstracthorizon.danube.webdav.spring.properties.GetContentType(null),
88          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.GetETag(null),
89          new org.abstracthorizon.danube.webdav.spring.properties.GetLastModified(null),
90          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.LockDiscovery(null),
91          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.ResourceType(null),
92          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.SupportedLock(null, null)
93      };
94  
95      /** Default dir request properties */
96      protected static RequestProperty[] dirRequestProperties = new RequestProperty[]{
97          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.CreationDate(null),
98          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.DisplayName(null),
99          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.GetETag(null),
100         new org.abstracthorizon.danube.webdav.spring.properties.GetLastModified(null),
101         new org.abstracthorizon.danube.webdav.xml.dav.request.properties.LockDiscovery(null),
102         new org.abstracthorizon.danube.webdav.xml.dav.request.properties.ResourceType(null),
103         new org.abstracthorizon.danube.webdav.xml.dav.request.properties.SupportedLock(null, null)
104     };
105 
106     /** Namespace provider to be used with this adapter */
107     protected NamespacesProvider namespacesProvider = new SimpleNamespacesProvider();
108 
109     /** Locking mechanism to be used with this adapter */
110     protected LockingMechanism lockingMechanism = new SimpleInMemoryLockingMechanism();
111 
112     /** Default DAV namespace */
113     protected DAVNamespace davNamespace = new DAVNamespace();
114 
115     /**
116      * Constructor
117      */
118     public SpringResourceWebDAVResourceAdapter() {
119         initHandlers();
120     }
121 
122     /**
123      * Constructor
124      * @param path root directory for files to be served from
125      */
126     public SpringResourceWebDAVResourceAdapter(Resource path) {
127         setResourcePath(path);
128         initHandlers();
129     }
130 
131     /**
132      * Initialises handlers
133      */
134     protected void initHandlers() {
135         davNamespace.setDAVFactory(new SpringResourceDAVFactory());
136         namespacesProvider.addNamespace(davNamespace.getURLString(), davNamespace.getPreferredPrefix(), davNamespace);
137     }
138 
139     /**
140      * Returns root file path
141      * @return root file path
142      */
143     public Resource getResourcePath() {
144         return path;
145     }
146 
147     /**
148      * Sets root file path
149      * @param path root file path
150      */
151     public void setResourcePath(Resource path) {
152         this.path = path;
153         try {
154             URL url = path.getURL();
155             if (!url.getPath().endsWith("/")) {
156                 // This is needed since spring resource doesn't recognise resource as a
157                 // path unless it has a trailing '/'
158                 String filename = path.getFilename();
159                 this.path = path.createRelative(filename + "/");
160             }
161         } catch (IOException e) {
162         }
163     }
164 
165     /**
166      * Returns namespace provider
167      * @return namespace provider
168      */
169     public NamespacesProvider getNamespacesProvider() {
170         return namespacesProvider;
171     }
172 
173     /**
174      * Sets namespace provider
175      * @param namespacesProvider namespace provider
176      */
177     public void setNamespacesProvider(NamespacesProvider namespacesProvider) {
178         this.namespacesProvider = namespacesProvider;
179     }
180 
181     /**
182      * Returns locking mechanism
183      * @return locking mechanism
184      */
185     public LockingMechanism getLockingMechanism() {
186         return lockingMechanism;
187     }
188 
189     /**
190      * Sets locking mechanism
191      * @param lockingMechanism locking mechanism
192      */
193     public void setLockingMechanism(LockingMechanism lockingMechanism) {
194         this.lockingMechanism = lockingMechanism;
195     }
196 
197     /**
198      * Returns {@link File} objects of supplied resource directory
199      * @return {@link File} objects of supplied resource directory
200      */
201     public Object[] collectionElements(Object resource) {
202         Resource res = (Resource)resource;
203 
204         try {
205             File dir = res.getFile();
206             File[] list = dir.listFiles();
207             if (list != null) {
208                 Resource[] result = new Resource[list.length];
209                 for (int i = 0; i < list.length; i++) {
210                     result[i] = res.createRelative(list[i].getName());
211                 }
212                 return result;
213             } else {
214                 return null;
215             }
216         } catch (IOException ignore) {
217         }
218         return null;
219     }
220 
221     /**
222      * Copies source file to destination file
223      * @param source source file
224      * @param destination destination file
225      * @param recursive is it deep copy
226      */
227     public void copy(Object source, Object destination, boolean recursive) throws IOException {
228         File from = ((Resource)source).getFile();
229         File to = ((Resource)destination).getFile();
230         // TODO update IOUtils to return IOException
231         if (!IOUtils.copy(from, to, recursive)) {
232             throw new IOException("Cannot copy");
233         }
234     }
235 
236     /**
237      * Deletes a file
238      * @param resource file
239      * @throws IOException thrown if there is a problem with deletion of the resource
240      */
241     public void delete(Object resource) throws IOException {
242         boolean ok;
243         File file = ((Resource)resource).getFile();
244         if (file.isDirectory()) {
245             // TODO update IOUtils to return IOException
246             ok = IOUtils.delete(file);
247         } else {
248             ok = file.delete();
249         }
250         if (!ok) {
251             throw new IOException("Cannot delete");
252         }
253     }
254 
255     /**
256      * Returns <code>true</code> if file exists
257      * @return <code>true</code> if file exists
258      */
259     public boolean exists(Object resource) {
260         Resource file = (Resource)resource;
261         return file.exists();
262     }
263 
264     /**
265      * Returns {@link File} object of given path startin from {@link #path}
266      * @param path path
267      * @return a file
268      */
269     public Object findResource(String path) {
270         try {
271             Resource file = this.path.createRelative(path);
272             return file;
273         } catch (IOException e) {
274             String basePath = "/";
275             try {
276                 URL url = this.path.getURL();
277                 basePath = url.getPath();
278             } catch (IOException ignore) {
279             }
280             DescriptiveResource res = new DescriptiveResource(IOUtils.addPaths(basePath, path));
281             return res;
282         }
283     }
284 
285     /**
286      * Returns file's parent
287      * @param resource file
288      * @return file's parent
289      */
290     public Object findParentResource(Object resource) {
291         // TODO ??? Check this implementation
292         Resource r = (Resource)resource;
293         if (r.equals(this.path)) {
294             return null;
295         }
296         if (r instanceof DescriptiveResource) {
297             DescriptiveResource res = (DescriptiveResource)r;
298             String path = res.getDescription();
299             path = IOUtils.parentPath(path);
300             try {
301                 return this.path.createRelative(path);
302             } catch (IOException e) {
303                 return new DescriptiveResource(path);
304             }
305         }
306         try {
307             URL url = r.getURL();
308             String path = url.getPath();
309             path = IOUtils.parentPath(path);
310 
311             return this.path.createRelative(path);
312         } catch (IOException ignore) {
313             return null;
314         }
315     }
316 
317     /**
318      * Returns default request properties
319      * @param resource a file
320      * @return {@link #dirRequestProperties} or {@link #fileRequestProperties}
321      */
322     public RequestProperty[] getDefaultRequestProperties(Object resource) {
323         if (isCollection(resource)) {
324             return dirRequestProperties;
325         } else {
326             return fileRequestProperties;
327         }
328     }
329 
330     /**
331      * Returns default response properties
332      * @param resource a file
333      * @return {@link #dirPropertyNames} or {@link #filePropertyNames}
334      */
335     public ResponseProperty[] getDefaultResponseProperties(Object resource) {
336         if (isCollection(resource)) {
337             return dirPropertyNames;
338         } else {
339             return filePropertyNames;
340         }
341     }
342 
343     /**
344      * Returns {@link FileInputStream}
345      * @param resource a file
346      * @return {@link FileInputStream}
347      */
348     public InputStream getInputStream(Object resource) {
349         try {
350             return ((Resource)resource).getInputStream();
351         } catch (IOException e) {
352             return null;
353         }
354     }
355 
356     /**
357      * Returns {@link RandomAccessFileRangeInputStream}
358      * @param resource a file
359      * @param from from offset
360      * @param length length in bytes
361      * @return {@link RandomAccessFileRangeInputStream}
362      */
363     public InputStream getInpusStream(Object resource, long from, long length) {
364         return null;
365     }
366 
367     /**
368      * Returns {@link FileOutputStream}
369      * @param resource a file
370      * @return {@link FileOutputStream}
371      */
372     public OutputStream getOutputStream(Object resource) {
373         try {
374             File file = ((Resource)resource).getFile();
375             return new FileOutputStream(file);
376         } catch (IOException e) {
377             return null;
378         }
379     }
380 
381     /**
382      * Returns {@link RandomAccessFileRangeOutputStream}
383      * @param resource a file
384      * @param from from offset
385      * @param length length in bytes
386      * @return {@link RandomAccessFileRangeOutputStream}
387      */
388     public OutputStream getOutputStream(Object resource, long from, long length) {
389         return null;
390     }
391 
392     /**
393      * Returns file's name
394      * @param resource file
395      * @return file's name
396      */
397     public String getResourceName(Object resource) {
398         return ((Resource)resource).getFilename();
399     }
400 
401     /**
402      * Returns file's last modified timestamp as hex plus file's absolute path
403      * @param resource file
404      * @return file's last modified timestamp as hex plus file's absolute path
405      */
406     public String getResourceETag(Object resource) {
407         try {
408             URL url = ((Resource)resource).getURL();
409             String pathString = url.getPath();
410             URL baseURL = ((Resource)resource).getURL();
411             String basePath = baseURL.getPath();
412 
413             String eTag = pathString;
414             eTag = eTag.substring(basePath.length());
415             String lastModified = "";
416             try {
417                 File file = ((Resource)resource).getFile();
418                 lastModified = Long.toHexString(file.lastModified());
419             } catch (IOException ignore2) {
420             }
421             eTag = "W/\"" + lastModified + "-" + eTag + "\"";
422             return eTag;
423         } catch (IOException e) {
424             return "W/\"" + resource.toString() + "\"";
425         }
426     }
427 
428     /**
429      * Returns <code>true</code> if file is a directory
430      * @param resource a file
431      * @return <code>true</code> if file is a directory
432      */
433     public boolean isCollection(Object resource) {
434         try {
435             File file = ((Resource)resource).getFile();
436             return file.isDirectory();
437         } catch (IOException ignore) {
438         }
439         return false;
440     }
441 
442     /**
443      * Makes a directory
444      * @param resource a file
445      * @throws IOException thrown in there is a problem in making the file
446      */
447     public void makeCollection(Object resource) throws IOException {
448         File file = ((Resource)resource).getFile();
449         if (!file.mkdir()) {
450             throw new IOException("Cannot create folder");
451         }
452     }
453 
454     /**
455      * Renames the file
456      * @param source source
457      * @param destination destination
458      * @throws IOException thrown if there is a problem with copying the file
459      */
460     public void move(Object source, Object destination) throws IOException {
461         File from = ((Resource)source).getFile();
462         File to = ((Resource)destination).getFile();
463         if (!from.renameTo(to)) {
464             throw new IOException("Cannot move");
465         }
466     }
467 
468     /**
469      * Returns file's last modified timestamp
470      * @param resource a file
471      * @return file's last modified timestamp
472      */
473     public long resourceCreated(Object resource) {
474         try {
475             File file = ((Resource)resource).getFile();
476             return file.lastModified();
477         } catch (IOException e) {
478             return -1;
479         }
480     }
481 
482     /**
483      * Returns file's last modified timestamp
484      * @param resource a file
485      * @return file's last modified timestamp
486      */
487     public long resourceLastModified(Object resource) {
488         try {
489             File file = ((Resource)resource).getFile();
490             return file.lastModified();
491         } catch (IOException e) {
492             return -1;
493         }
494     }
495 
496     /**
497      * Returns file's length
498      * @param resource a file
499      * @return file's length
500      */
501     public long resourceLength(Object resource) {
502         try {
503             File file = ((Resource)resource).getFile();
504             return file.length();
505         } catch (IOException e) {
506             return -1;
507         }
508     }
509 
510 }