- NEW: supprt IContentDeletable for folders
[polymap3:polymap3-core.git] / plugins / org.polymap.service.fs / src / org / polymap / service / fs / webdav / WebDavResourceFactory.java
1 /* 
2  * polymap.org
3  * Copyright 2011, Polymap GmbH. All rights reserved.
4  *
5  * This is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This software is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  */
15 package org.polymap.service.fs.webdav;
16
17 import java.util.HashMap;
18 import java.util.Map;
19 import java.util.Set;
20
21 import java.lang.reflect.InvocationHandler;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Proxy;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import com.bradmcevoy.http.CollectionResource;
31 import com.bradmcevoy.http.DeletableResource;
32 import com.bradmcevoy.http.GetableResource;
33 import com.bradmcevoy.http.MakeCollectionableResource;
34 import com.bradmcevoy.http.MoveableResource;
35 import com.bradmcevoy.http.PostableResource;
36 import com.bradmcevoy.http.PropFindableResource;
37 import com.bradmcevoy.http.PutableResource;
38 import com.bradmcevoy.http.ReplaceableResource;
39 import com.bradmcevoy.http.Request;
40 import com.bradmcevoy.http.Resource;
41 import com.bradmcevoy.http.ResourceFactory;
42 import com.bradmcevoy.http.SecurityManager;
43 import org.polymap.core.runtime.SessionContext;
44
45 import org.polymap.service.fs.ContentManager;
46 import org.polymap.service.fs.spi.IContentDeletable;
47 import org.polymap.service.fs.spi.IContentFile;
48 import org.polymap.service.fs.spi.IContentFolder;
49 import org.polymap.service.fs.spi.IContentMoveable;
50 import org.polymap.service.fs.spi.IContentNode;
51 import org.polymap.service.fs.spi.IContentPutable;
52 import org.polymap.service.fs.spi.IContentWriteable;
53 import org.polymap.service.fs.spi.IMakeFolder;
54
55 /**
56  * A {@link ResourceFactory} that provides the content of several
57  * {@link ContentManager}, one per user/sessions, to a {@link WebDavServer}.
58  * 
59  * @author <a href="http://www.polymap.de">Falko Bräutigam</a>
60  */
61 class WebDavResourceFactory
62         implements ResourceFactory {
63
64     private static Log log = LogFactory.getLog( WebDavResourceFactory.class );
65
66     private SecurityManager                 securityManager;
67
68     private String                          contextPath;
69
70
71     /**
72      * 
73      * @param securityManager
74      * @param contextPath - this is the leading part of URL's to ignore. For example
75      *        if you're application is deployed to http://localhost:8080/webdav-fs,
76      *        the context path should be webdav-fs
77      */
78     protected WebDavResourceFactory( SecurityManager securityManager, String contextPath ) {
79         this.securityManager = securityManager;
80         this.contextPath = contextPath;
81     }
82
83     
84     public void dispose() {
85     }
86
87     
88     public String getContextPath() {
89         return contextPath;
90     }
91
92
93     public Resource getResource( String host, String path ) {
94         Request request = WebDavServer.request();
95         assert request != null;
96         
97         ContentManager contentManager = (ContentManager)
98                 SessionContext.current().getAttribute( "contentManager" );
99         
100         // get content
101         path = StringUtils.substringAfter( path, contextPath );
102         IContentNode node = contentManager.getNode( contentManager.parsePath( path ) );
103         return node != null ? wrapContentNode( node, contentManager, securityManager ) : null;
104     }
105     
106     
107     static Resource wrapContentNode( IContentNode node, ContentManager contentManager, SecurityManager securityManager ) {
108         if (node == null) {
109             log.warn( "wrapContentNode(): node is null." );
110             return null;
111         }
112         // folder
113         else if (node instanceof IContentFolder) {
114             WebDavFolderHandler folderHandler = new WebDavFolderHandler( contentManager, (IContentFolder)node, securityManager);
115             ClassLoader cl = Thread.currentThread().getContextClassLoader();
116             return (Resource)Proxy.newProxyInstance( cl, folderHandler.getInterfaces(), folderHandler );
117         }
118         // file
119         else if (node instanceof IContentFile) {
120             WebDavFileHandler fileHandler = new WebDavFileHandler( contentManager, (IContentFile)node, securityManager);
121             ClassLoader cl = Thread.currentThread().getContextClassLoader();
122             return (Resource)Proxy.newProxyInstance( cl, fileHandler.getInterfaces(), fileHandler );
123         }
124         // unknown
125         else {
126             throw new IllegalStateException( "Unknown node type: " + node );
127         }        
128     }
129     
130     
131     /*
132      * InvocationHandler of a proxy that exposes the different
133      * interfaces for Getable, Postable, Deletable, etc.
134      */
135     static class WebDavFileHandler
136             implements InvocationHandler {
137
138         private IContentFile        node;
139         
140         private Map<Class,Object>   handlers = new HashMap();
141         
142         
143         public WebDavFileHandler( ContentManager contentManager, IContentFile node, SecurityManager securityManager ) {
144             this.node = node;
145             
146             WebDavFileResource getable = new WebDavFileResource( contentManager, node, securityManager );       
147             handlers.put( ContentNodeResource.class, getable );
148             handlers.put( Resource.class, getable );
149             handlers.put( GetableResource.class, getable );
150             handlers.put( PropFindableResource.class, getable );
151             
152             if (node instanceof IContentWriteable) {
153                 WebDavFilePostable postable = new WebDavFilePostable( contentManager, (IContentWriteable)node, securityManager );
154                 handlers.put( PostableResource.class, postable );
155                 handlers.put( ReplaceableResource.class, postable );
156             }
157             if (node instanceof IContentDeletable) {
158                 WebDavFileDeletable deletable = new WebDavFileDeletable( contentManager, (IContentDeletable)node, securityManager );
159                 handlers.put( DeletableResource.class, deletable );
160             }
161             if (node instanceof IContentMoveable) {
162                 WebDavMoveableResource res = new WebDavMoveableResource( contentManager, node, securityManager );
163                 handlers.put( MoveableResource.class, res );
164             }
165         }
166
167
168         public Class[] getInterfaces() {
169             Set<Class> result = handlers.keySet();
170             return result.toArray( new Class[ result.size()] );
171         }
172         
173         
174         public Object invoke( Object proxy, Method method, Object[] args )
175         throws Throwable {
176             //log.debug( "invoke(): method= " + method.getName() + ", declaringClass= " + method.getDeclaringClass().getSimpleName() );
177
178             Object handler = handlers.get( method.getDeclaringClass() );
179             
180             if (handler == null) {
181                 throw new IllegalStateException( "No handler for declaringClass: " + method.getDeclaringClass().getName() );
182             }
183             
184             try {
185                 return method.invoke( handler, args );
186             }
187             catch (InvocationTargetException e) {
188                 throw e.getTargetException();
189             }
190         }
191         
192     }
193     
194
195     /*
196      * InvocationHandler of a proxy that exposes the different
197      * interfaces for Getable, Postable, Deletable, etc.
198      */
199     static class WebDavFolderHandler
200             implements InvocationHandler {
201
202         private IContentFolder      node;
203         
204         private Map<Class,Object>   handlers = new HashMap();
205         
206         
207         public WebDavFolderHandler( ContentManager contentManager, IContentFolder node, SecurityManager securityManager ) {
208             this.node = node;
209             
210             WebDavFolderResource getable = new WebDavFolderResource( contentManager, node, securityManager );       
211             handlers.put( ContentNodeResource.class, getable );
212             handlers.put( Resource.class, getable );
213             handlers.put( GetableResource.class, getable );
214             handlers.put( CollectionResource.class, getable );
215             handlers.put( PropFindableResource.class, getable );
216             
217             if (node instanceof IContentPutable) {
218                 WebDavFolderPutable putable = new WebDavFolderPutable( contentManager, (IContentPutable)node, securityManager );
219                 handlers.put( PutableResource.class, putable );
220             }
221             if (node instanceof IContentWriteable) {
222                 WebDavFilePostable postable = new WebDavFilePostable( contentManager, (IContentWriteable)node, securityManager );
223                 handlers.put( PostableResource.class, postable );
224                 handlers.put( ReplaceableResource.class, postable );
225             }
226             if (node instanceof IMakeFolder) {
227                 WebDavMakeCollectionResource res = new WebDavMakeCollectionResource( contentManager, node, securityManager );
228                 handlers.put( MakeCollectionableResource.class, res );
229             }
230             if (node instanceof IContentMoveable) {
231                 WebDavMoveableResource res = new WebDavMoveableResource( contentManager, node, securityManager );
232                 handlers.put( MoveableResource.class, res );
233             }
234             if (node instanceof IContentDeletable) {
235                 WebDavFileDeletable deletable = new WebDavFileDeletable( contentManager, (IContentDeletable)node, securityManager );
236                 handlers.put( DeletableResource.class, deletable );
237             }
238         }
239
240
241         public Class[] getInterfaces() {
242             Set<Class> result = handlers.keySet();
243             return result.toArray( new Class[ result.size()] );
244         }
245         
246         
247         public Object invoke( Object proxy, Method method, Object[] args )
248         throws Throwable {
249             //log.debug( "invoke(): method= " + method.getName() + ", declaringClass= " + method.getDeclaringClass().getSimpleName() );
250
251             Object handler = handlers.get( method.getDeclaringClass() );
252             
253             if (handler == null) {
254                 throw new IllegalStateException( "No handler for declaringClass: " + method.getDeclaringClass().getName() );
255             }
256             
257             try {
258                 return method.invoke( handler, args );
259             }
260             catch (InvocationTargetException e) {
261                 throw e.getTargetException();
262             }
263         }
264         
265     }
266     
267 }