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.java;
14  
15  import org.abstracthorizon.danube.beanconsole.BeanAccessException;
16  import org.abstracthorizon.danube.beanconsole.BeanDef;
17  import org.abstracthorizon.danube.beanconsole.BeanHelper;
18  import org.abstracthorizon.danube.http.Status;
19  import org.abstracthorizon.danube.http.util.IOUtils;
20  import org.abstracthorizon.danube.webdav.ResourceAdapter;
21  import org.abstracthorizon.danube.webdav.lock.LockingMechanism;
22  import org.abstracthorizon.danube.webdav.lock.impl.SimpleInMemoryLockingMechanism;
23  import org.abstracthorizon.danube.webdav.util.NamespacesProvider;
24  import org.abstracthorizon.danube.webdav.util.SimpleNamespacesProvider;
25  import org.abstracthorizon.danube.webdav.xml.dav.DAVNamespace;
26  import org.abstracthorizon.danube.webdav.xml.dav.request.properties.RequestProperty;
27  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.DisplayName;
28  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetContentLength;
29  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetContentType;
30  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.GetETag;
31  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.LockDiscovery;
32  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.ResourceType;
33  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.ResponseProperty;
34  import org.abstracthorizon.danube.webdav.xml.dav.response.properties.SupportedLock;
35  
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.io.OutputStream;
39  import java.util.ArrayList;
40  import java.util.HashMap;
41  import java.util.Map;
42  import java.util.Set;
43  
44  /**
45   * Main class in reflection java resource adapter. This resource adapter
46   * allows WebDAV to be used in accessing internals of java objects. It
47   * exposes collections, maps, array, spring framework application contexts,
48   * java bean properties and methods to the WebDAV handler.
49   *
50   * @author Daniel Sendula
51   */
52  public class JavaWebDAVResourceAdapter implements ResourceAdapter {
53  
54      /** Root object */
55      protected Object rootObject;
56  
57      /** Available property names for files */
58      protected static ResponseProperty[] filePropertyNames = new ResponseProperty[]{
59          new DisplayName(Status.OK),
60          new GetContentLength(Status.OK),
61          new GetContentType(Status.OK),
62          new GetETag(Status.OK),
63          new LockDiscovery(Status.OK),
64          new ResourceType(Status.OK),
65          new SupportedLock(Status.OK)
66      };
67  
68      /** Available property names for 'dirs' */
69      protected static ResponseProperty[] dirPropertyNames = new ResponseProperty[]{
70          new DisplayName(Status.OK),
71          new GetETag(Status.OK),
72          new LockDiscovery(Status.OK),
73          new ResourceType(Status.OK),
74          new SupportedLock(Status.OK)
75      };
76  
77      /** File request propertyes */
78      protected static RequestProperty[] fileRequestProperties = new RequestProperty[]{
79          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.DisplayName(null),
80          new org.abstracthorizon.danube.webdav.java.properties.GetContentLength(null),
81          new org.abstracthorizon.danube.webdav.java.properties.GetContentType(null),
82          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.GetETag(null),
83          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.LockDiscovery(null),
84          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.ResourceType(null),
85          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.SupportedLock(null, null)
86      };
87  
88      /** Dir request properties */
89      protected static RequestProperty[] dirRequestProperties = new RequestProperty[]{
90          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.DisplayName(null),
91          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.GetETag(null),
92          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.LockDiscovery(null),
93          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.ResourceType(null),
94          new org.abstracthorizon.danube.webdav.xml.dav.request.properties.SupportedLock(null, null)
95      };
96  
97      /** Namespace provider. It defaults to  {@link SimpleNamespacesProvider}. */
98      protected NamespacesProvider namespacesProvider = new SimpleNamespacesProvider();
99  
100     /** Locking mechanism. It defaults to {@link SimpleInMemoryLockingMechanism}. */
101     protected LockingMechanism lockingMechanism = new SimpleInMemoryLockingMechanism();
102 
103     /** DAV namespace. It defaults to {@link DAVNamespace} */
104     protected DAVNamespace davNamespace = new DAVNamespace();
105 
106     /**
107      * Constructor
108      */
109     public JavaWebDAVResourceAdapter() {
110         initHandlers();
111     }
112 
113     /**
114      * Constructor
115      * @param rootObject root object
116      */
117     public JavaWebDAVResourceAdapter(Object rootObject) {
118         setRootObject(rootObject);
119         initHandlers();
120     }
121 
122     /**
123      * Initialises java dav facotry and sets namespace
124      */
125     protected void initHandlers() {
126         davNamespace.setDAVFactory(new JavaDAVFactory());
127         namespacesProvider.addNamespace(davNamespace.getURLString(), davNamespace.getPreferredPrefix(), davNamespace);
128     }
129 
130     /**
131      * Returns root object
132      * @return root object
133      */
134     public Object getRootObject() {
135         return rootObject;
136     }
137 
138     /**
139      * Sets root object
140      * @param root root object
141      */
142     public void setRootObject(Object root) {
143         this.rootObject = root;
144     }
145 
146     /**
147      * Returns namespace provider
148      * @return namespace provider
149      */
150     public NamespacesProvider getNamespacesProvider() {
151         return namespacesProvider;
152     }
153 
154     /**
155      * Sets namespace provider
156      * @param namespacesProvider namespace provider
157      */
158     public void setNamespacesProvider(NamespacesProvider namespacesProvider) {
159         this.namespacesProvider = namespacesProvider;
160     }
161 
162     /**
163      * Returns locking mechanism
164      * @return locking mechanism
165      */
166     public LockingMechanism getLockingMechanism() {
167         return lockingMechanism;
168     }
169 
170     /**
171      * Sets locking mechanism
172      * @param lockingMechanism locking mechanism
173      */
174     public void setLockingMechanism(LockingMechanism lockingMechanism) {
175         this.lockingMechanism = lockingMechanism;
176     }
177 
178     /**
179      * Finds a resource (object) from the given path. It returns
180      * {@link Bean} or extension of that object
181      * @return a resource
182      */
183     public Object findResource(String resourcePath) {
184         String name = IOUtils.lastPathComponent(resourcePath);
185         int i = name.lastIndexOf('.');
186         if (!name.endsWith("]") && (i >= 0)) {
187 //            String parent = IOUtils.parentPath(resourcePath);
188             if (name.equals("object.type")) {
189                 return new ObjectType(resourcePath);
190             } else if (name.equals("object.fields")) {
191             } else if (name.equals("object.methods")) {
192             } else if (name.equals("object.properties")) {
193             } else if (name.equals("object.as.collection")) {
194             } else if (name.equals("object.as.map")) {
195             } else if (name.endsWith(".field")) {
196             } else if (name.endsWith(".property")) {
197                 return new Property(resourcePath);
198             } else if (name.endsWith(".invoke")) {
199             } else if (name.equals("index.html")) {
200            }
201         }
202         return new Bean(resourcePath);
203 
204     }
205 
206     /**
207      * This method returns real object from the given bean
208      * @param bean bean (defines path)
209      * @return an object or <code>null</code> if failed
210      */
211     protected Object findObjectImpl(Bean bean) {
212         String resourcePath = bean.getPath();
213 //        String name = IOUtils.lastPathComponent(resourcePath);
214 //        int i = name.indexOf('.');
215 //        if (!name.endsWith("]") && (i == 0)) {
216 //            resourcePath = IOUtils.lastPathComponent(resourcePath);
217 //        }
218         return findObjectImpl(resourcePath);
219     }
220 
221     /**
222      * This method returns real object from the given path
223      * @param path path
224      * @return an object or <code>null</code> if failed
225      */
226     protected Object findObjectImpl(String resourcePath) {
227         Object current = getRootObject();
228         if (resourcePath.length() > 0) {
229             try {
230                 current = BeanHelper.navigate(current, resourcePath);
231             } catch (BeanAccessException exception) {
232                 return null;
233             }
234         }
235         return current;
236     }
237 
238     /**
239      * Returns parent resource of the given resource. It takes
240      * the path and finds parent of the path and then creates new
241      * {@link bean} object (or extension) and returns that. If object
242      * is already root objec then it returns <code>null</code>
243      *
244      * @return parent resource
245      */
246     public Object findParentResource(Object resource) {
247         String path = ((Bean)resource).getPath();
248         if ((path == null) || (path.length() == 0)) {
249             return null;
250         } else {
251             int i = path.lastIndexOf('/');
252             if (i >= 0) {
253                 return findResource(path.substring(0, i));
254             } else {
255                 return rootObject;
256             }
257         }
258     }
259 
260     /**
261      * Returns resource length. It consults {@link Delegate} if resource is of that type.
262      * @return resource length
263      */
264     public long resourceLength(Object resource) {
265         if (resource instanceof Delegate) {
266             return ((Delegate)resource).resourceLength(this);
267         } else {
268             return -1;
269         }
270     }
271 
272     /**
273      * Returns -1
274      * @return -1
275      */
276     public long resourceLastModified(Object resource) {
277         return -1;
278     }
279 
280     /**
281      * Returns -1
282      * @return -1
283      */
284     public long resourceCreated(Object resource) {
285         return -1;
286     }
287 
288     /**
289      * Returns resource name from the last portion of the path
290      * @return resource name
291      */
292     public String getResourceName(Object resource) {
293         return IOUtils.lastPathComponent(((Bean)resource).getPath());
294     }
295 
296     /**
297      * Returns etag. It consults {@link Delegate} if resource is of that type.
298      * @return etag
299      */
300     public String getResourceETag(Object resource) {
301         if (resource instanceof Delegate) {
302             return ((Delegate)resource).getResourceETag(this);
303         } else {
304             Object object = findObjectImpl((Bean)resource);
305             if (object != null) {
306                 String eTag = "W/\"" + Integer.toHexString(System.identityHashCode(object)) + "\"";
307                 return eTag;
308             } else {
309                 String path = ((Bean)resource).getPath();
310                 return "W/\"" + path + "-NULL\"";
311             }
312         }
313     }
314 
315     /**
316      * Returns if resource exists.
317      * @return <code>true</code> if resource exists.
318      */
319     public boolean exists(Object resource) {
320         if (resource instanceof Delegate) {
321             return true;
322         } else {
323             return findObjectImpl((Bean)resource) != null;
324         }
325     }
326 
327     /**
328      * Returns if resource is collection. It consults {@link Delegate} if resource is of that type.
329      * @return <code>true</code> if resource is collection
330      */
331     public boolean isCollection(Object resource) {
332         if (resource instanceof Delegate) {
333             return false;
334         } else {
335             return findObjectImpl((Bean)resource) != null;
336         }
337     }
338 
339     /**
340      * Not implemented
341      * @param resource resource
342      * @throws IOException if there were errors while deleting the resource
343      */
344     public void delete(Object resource) throws IOException {
345     }
346 
347     /**
348      * Not implemented
349      * @param resource resource
350      * @throws IOException if there were errors while collection is created
351      */
352     public void makeCollection(Object resource) throws IOException {
353     }
354 
355     /**
356      * Not implemented
357      * @param source source
358      * @param destination destination
359      * @param recursive deep copy
360      * @throws IOException if there were errors while copying resource(s)
361      */
362     public void copy(Object source, Object destination, boolean recursive) throws IOException {
363     }
364 
365     /**
366      * Not implemented
367      * @param source source
368      * @param destination destination
369      * @throws IOException if there were errors while moving resource(s)
370      */
371     public void move(Object source, Object destination) throws IOException {
372     }
373 
374     /**
375      * Returns <code>null</code> if resource is delegate. Otherwise
376      * returns list of collection, map, arrays, properties, methods, etc..
377      * @param resource resource
378      * @return object
379      */
380     @SuppressWarnings("unchecked")
381     public Object[] collectionElements(Object resource) {
382         if (resource instanceof Delegate) {
383             return null;
384         }
385         Bean bean = (Bean)resource;
386         Object object = findObjectImpl(bean);
387         if (object == null) {
388             return null;
389         } else {
390             Map<String, Object> result = new HashMap<String, Object>();
391             BeanHelper.prepare(object, result);
392             ArrayList<Bean> res = new ArrayList<Bean>();
393             Set<BeanDef> beans = (Set<BeanDef>)result.get("beans");
394             if (beans != null) {
395                 for (BeanDef beanDef : beans) {
396                     if (beanDef.isFollowable()) {
397                         Bean b = new Bean(IOUtils.addPaths(bean.getPath(), "[" + beanDef.getName()) + "]");
398                         res.add(b);
399                     }
400                 }
401             }
402             Set<BeanDef> map = (Set<BeanDef>)result.get("map");
403             if (map != null) {
404                 for (BeanDef beanDef : map) {
405                     if (beanDef.isFollowable()) {
406                         Bean b = new Bean(IOUtils.addPaths(bean.getPath(), "[" + beanDef.getName()) + "]");
407                         res.add(b);
408                     }
409                 }
410             }
411             Set<BeanDef> collection = (Set<BeanDef>)result.get("collection");
412             if (collection != null) {
413                 int i = 0;
414                 for (BeanDef beanDef : collection) {
415                     if (beanDef.isFollowable()) {
416                         Bean b = new Bean(IOUtils.addPaths(bean.getPath(), "[" + i + "]"));
417                         res.add(b);
418                     }
419                     i = i + 1;
420                 }
421             }
422             Set<BeanDef> props = (Set<BeanDef>)result.get("properties");
423             if (props != null) {
424                 for (BeanDef beanDef : props) {
425                     if (beanDef.isFollowable()) {
426                         Bean b = new Bean(IOUtils.addPaths(bean.getPath(), beanDef.getName()));
427                         res.add(b);
428                     }
429                 }
430             }
431             res.add(new ObjectType(IOUtils.addPaths(bean.getPath(), "/object.type")));
432             if (props != null) {
433                 for (BeanDef beanDef : props) {
434                     Property b = new Property(IOUtils.addPaths(bean.getPath(), beanDef.getName() + ".property"));
435                     res.add(b);
436                 }
437             }
438             res.add(new Bean(IOUtils.addPaths(bean.getPath(), "index.html")));
439 
440             Bean[] r = new Bean[res.size()];
441             r = res.toArray(r);
442             return r;
443         }
444     }
445 
446     public RequestProperty[] getDefaultRequestProperties(Object resource) {
447         if (isCollection(resource)) {
448             return dirRequestProperties;
449         } else {
450             return fileRequestProperties;
451         }
452     }
453 
454     public ResponseProperty[] getDefaultResponseProperties(Object resource) {
455         if (isCollection(resource)) {
456             return dirPropertyNames;
457         } else {
458             return filePropertyNames;
459         }
460     }
461 
462     /**
463      * Returns input stream. It consults {@link Delegate} if resource is of that type.
464      * @return input stream or <code>null</code>
465      */
466     public InputStream getInputStream(Object resource) {
467         if (resource instanceof Delegate) {
468             return ((Delegate)resource).getInputStream(this);
469         } else {
470             return null;
471         }
472     }
473 
474     /**
475      * Returns input stream. It consults {@link Delegate} if resource is of that type.
476      * @param from offset
477      * @param length length
478      * @return input stream or <code>null</code>
479      */
480     public InputStream getInpusStream(Object resource, long from, long length) {
481         if (resource instanceof Delegate) {
482             return ((Delegate)resource).getInputStream(this, from, length);
483         } else {
484             return null;
485         }
486     }
487 
488     /**
489      * Returns output stream. It consults {@link Delegate} if resource is of that type.
490      * @return input stream or <code>null</code>
491      */
492     public OutputStream getOutputStream(Object resource) {
493         if (resource instanceof Delegate) {
494             return ((Delegate)resource).getOutputStream(this);
495         } else {
496             return null;
497         }
498     }
499 
500     /**
501      * Returns output stream. It consults {@link Delegate} if resource is of that type.
502      * @param from offset
503      * @param length length
504      * @return output stream or <code>null</code>
505      */
506     public OutputStream getOutputStream(Object resource, long from, long length) {
507         if (resource instanceof Delegate) {
508             return ((Delegate)resource).getOutputStream(this, from, length);
509         } else {
510             return null;
511         }
512     }
513 
514     /**
515      * Returns content type. It consults {@link Delegate} if resource is of that type.
516      * @return content type or &quot;unknown/unknown&quot;
517      */
518     public String getContentType(Object resource) {
519         if (resource instanceof Delegate) {
520             return ((Delegate)resource).getContentType(this);
521         } else {
522             return "unknown/unknown";
523         }
524     }
525 
526 }