KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > source > impl > WebDAVSource


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.source.impl;
17
18 import java.io.BufferedInputStream JavaDoc;
19 import java.io.ByteArrayInputStream JavaDoc;
20 import java.io.ByteArrayOutputStream JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.Properties JavaDoc;
28 import java.util.Vector JavaDoc;
29
30 import javax.xml.transform.OutputKeys JavaDoc;
31 import javax.xml.transform.TransformerFactory JavaDoc;
32 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
33 import javax.xml.transform.sax.TransformerHandler JavaDoc;
34 import javax.xml.transform.stream.StreamResult JavaDoc;
35
36 import org.apache.avalon.framework.logger.AbstractLogEnabled;
37 import org.apache.avalon.framework.logger.Logger;
38 import org.apache.cocoon.components.source.InspectableSource;
39 import org.apache.cocoon.components.source.helpers.SourceProperty;
40 import org.apache.cocoon.xml.XMLUtils;
41 import org.apache.commons.httpclient.HttpException;
42 import org.apache.commons.httpclient.HttpStatus;
43 import org.apache.commons.httpclient.HttpURL;
44 import org.apache.commons.httpclient.HttpsURL;
45 import org.apache.commons.httpclient.URIException;
46 import org.apache.excalibur.source.ModifiableSource;
47 import org.apache.excalibur.source.ModifiableTraversableSource;
48 import org.apache.excalibur.source.MoveableSource;
49 import org.apache.excalibur.source.Source;
50 import org.apache.excalibur.source.SourceException;
51 import org.apache.excalibur.source.SourceNotFoundException;
52 import org.apache.excalibur.source.SourceParameters;
53 import org.apache.excalibur.source.SourceUtil;
54 import org.apache.excalibur.source.SourceValidity;
55 import org.apache.excalibur.source.TraversableSource;
56 import org.apache.excalibur.source.impl.validity.TimeStampValidity;
57 import org.apache.webdav.lib.Property;
58 import org.apache.webdav.lib.PropertyName;
59 import org.apache.webdav.lib.ResponseEntity;
60 import org.apache.webdav.lib.WebdavResource;
61 import org.apache.webdav.lib.methods.DepthSupport;
62 import org.w3c.dom.Element JavaDoc;
63 import org.w3c.dom.Node JavaDoc;
64 import org.w3c.dom.NodeList JavaDoc;
65 import org.w3c.dom.Text JavaDoc;
66 import org.xml.sax.ContentHandler JavaDoc;
67 import org.xml.sax.SAXException JavaDoc;
68 import org.xml.sax.helpers.AttributesImpl JavaDoc;
69
70 /**
71  * A source implementation to get access to WebDAV repositories.
72  *
73  * <h2>Protocol syntax</h2>
74  * <p><code>webdav://[usr[:password]@]host[:port][/path][?cocoon:webdav-depth][&cocoon:webdav-action]</code></p>
75  * <p>
76  * <ul>
77  * <li>
78  * <code>cocoon:webdav-depth</code> allows to specify the default depth
79  * to use during initialization of the webdav resource.
80  * </li>
81  * <li>
82  * <code>cocoon:webdav-action</code> allows to specify a default action
83  * to take upon initialization of the webdav resource.
84  * </li>
85  * </ul>
86  * <p>
87  *
88  * @version $Id: WebDAVSource.java 327579 2005-10-21 21:00:52Z joerg $
89 */

90 public class WebDAVSource extends AbstractLogEnabled
91                           implements Source, TraversableSource, ModifiableSource,
92                                      ModifiableTraversableSource, InspectableSource, MoveableSource {
93
94     private static final String JavaDoc NAMESPACE = "http://apache.org/cocoon/webdav/1.0";
95
96     private static final String JavaDoc PREFIX = "webdav";
97     private static final String JavaDoc RESOURCE_NAME = "resource";
98     private static final String JavaDoc COLLECTION_NAME = "collection";
99
100     // the http url
101
private final HttpURL url;
102
103     // the scheme name
104
private final String JavaDoc protocol;
105
106     // cached uri and secureUri values
107
private String JavaDoc uri;
108     private String JavaDoc secureUri;
109
110     // the SWCL resource
111
private WebdavResource resource = null;
112
113     // current resource initialization values
114
private int depth = -1;
115     private int action = -1;
116
117     /**
118      * Default constructor.
119      */

120     private WebDAVSource(HttpURL url, String JavaDoc protocol) throws URIException {
121         this.protocol = protocol;
122         this.url = url;
123
124         String JavaDoc qs = url.getQuery();
125         if (qs != null) {
126             final SourceParameters sp = new SourceParameters(qs);
127
128             // parse optional start depth and start action qs parameters
129
this.depth = sp.getParameterAsInteger("cocoon:webdav-depth", DepthSupport.DEPTH_1);
130             this.action = sp.getParameterAsInteger("cocoon:webdav-action", WebdavResource.NOACTION);
131
132             // [UH] FIXME: Why this alternative way of passing in credentials?
133
String JavaDoc principal = url.getUser();
134             String JavaDoc password = url.getPassword();
135             if (principal == null || password == null) {
136                 principal = sp.getParameter("cocoon:webdav-principal", principal);
137                 password = sp.getParameter("cocoon:webdav-password", password);
138                 if (principal != null) {
139                     url.setUser(principal);
140                     url.setPassword(password);
141                 }
142             }
143
144             sp.removeParameter("cocoon:webdav-depth");
145             sp.removeParameter("cocoon:webdav-action");
146             sp.removeParameter("cocoon:webdav-principal");
147             sp.removeParameter("cocoon:webdav-password");
148
149             // set the qs without WebdavSource specific parameters
150
url.setQuery(sp.getQueryString());
151         }
152     }
153
154     /**
155      * Constructor used by getChildren() method.
156      */

157     private WebDAVSource (WebdavResource resource, HttpURL url, String JavaDoc protocol)
158     throws URIException {
159         this(url, protocol);
160         this.resource = resource;
161     }
162
163     /**
164      * Initialize the SWCL WebdavResource.
165      * <p>
166      * The action argument specifies a set of properties to load during initialization.
167      * Its value is one of WebdavResource.NOACTION, WebdavResource.NAME,
168      * WebdavResource.BASIC, WebdavResource.DEFAULT, WebdavResource.ALL.
169      * Similarly the depth argument specifies the depth header of the PROPFIND
170      * method that is executed upon initialization.
171      * </p>
172      * <p>
173      * The different methods of this Source implementation call this method to
174      * initialize the resource using their minimal action and depth requirements.
175      * For instance the WebDAVSource.getMimeType() method requires WebdavResource.BASIC
176      * properties and a search depth of 0 is sufficient.
177      * </p>
178      * <p>
179      * However it may be that a later call (eg. WebDAVSource.getChildren()) requires more
180      * information. In that case the WebdavResource would have to make another call to the Server.
181      * It would be more efficient if previous initialization had been done using depth 1 instead.
182      * In order give the user more control over this the WebDAVSource can be passed a minimal
183      * action and depth using cocoon:webdav-depth and cocoon:webdav-action query string parameters.
184      * By default the mimimum action is WebdavResource.BASIC (which loads all the following basic
185      * webdav properties: DAV:displayname, DAV:getcontentlength, DAV:getcontenttype DAV:resourcetype,
186      * DAV:getlastmodified and DAV:lockdiscovery). The default minimum depth is 1.
187      * </p>
188      *
189      * @param action the set of propterties the WebdavResource should load.
190      * @param depth the webdav depth.
191      * @throws SourceException
192      * @throws SourceNotFoundException
193      */

194     private void initResource(int action, int depth) throws SourceException, SourceNotFoundException {
195         try {
196             boolean update = false;
197             if (action != WebdavResource.NOACTION) {
198                 if (action > this.action) {
199                     this.action = action;
200                     update = true;
201                 } else {
202                     action = this.action;
203                 }
204             }
205             if (depth > this.depth) {
206                 this.depth = depth;
207                 update = true;
208             } else {
209                 depth = this.depth;
210             }
211             if (this.resource == null) {
212                 this.resource = new WebdavResource(this.url, action, depth);
213             } else if (update) {
214                 this.resource.setProperties(action, depth);
215             }
216             if (this.action > WebdavResource.NOACTION) {
217                 if (this.resource.isCollection()) {
218                     String JavaDoc path = this.url.getPath();
219                     if (path.charAt(path.length()-1) != '/') {
220                         this.url.setPath(path + "/");
221                     }
222                 }
223             }
224        } catch (HttpException e) {
225             if (e.getReasonCode() == HttpStatus.SC_NOT_FOUND) {
226                 throw new SourceNotFoundException("Not found: " + getSecureURI(), e);
227             }
228             final String JavaDoc msg = "Could not initialize webdav resource at " + getSecureURI()
229                 + ". Server responded " + e.getReasonCode() + " (" + e.getReason() + ") - " + e.getMessage();
230             throw new SourceException(msg, e);
231        } catch (IOException JavaDoc e) {
232             throw new SourceException("Could not initialize webdav resource", e);
233        }
234     }
235
236     /**
237      * Static factory method to obtain a Source.
238      */

239     public static WebDAVSource newWebDAVSource(HttpURL url,
240                                                String JavaDoc protocol,
241                                                Logger logger)
242     throws URIException {
243         final WebDAVSource source = new WebDAVSource(url, protocol);
244         source.enableLogging(logger);
245         return source;
246     }
247
248     /**
249      * Static factory method to obtain a Source.
250      */

251     private static WebDAVSource newWebDAVSource(WebdavResource resource,
252                                                 HttpURL url,
253                                                 String JavaDoc protocol,
254                                                 Logger logger)
255     throws URIException {
256         final WebDAVSource source = new WebDAVSource(resource, url, protocol);
257         source.enableLogging(logger);
258         return source;
259     }
260
261     // ---------------------------------------------------- Source implementation
262

263     /**
264      * Get the scheme for this Source.
265      */

266     public String JavaDoc getScheme() {
267         return this.protocol;
268     }
269
270     /**
271      * Return the unique identifer for this source
272      */

273     public String JavaDoc getURI() {
274         if (this.uri == null) {
275             String JavaDoc uri = this.url.toString();
276             final int index = uri.indexOf("://");
277             if (index != -1) {
278                 uri = uri.substring(index+3);
279             }
280             final String JavaDoc userinfo = this.url.getEscapedUserinfo();
281             if (userinfo != null) {
282                 uri = this.protocol + "://" + userinfo + "@" + uri;
283             } else {
284                 uri = this.protocol + "://" + uri;
285             }
286             this.uri = uri;
287         }
288         return this.uri;
289     }
290
291     /**
292      * Return the URI securely, without username and password
293      */

294     protected String JavaDoc getSecureURI() {
295         if (this.secureUri == null) {
296             String JavaDoc uri = this.url.toString();
297             int index = uri.indexOf("://");
298             if (index != -1) {
299                 uri = uri.substring(index+3);
300             }
301             uri = this.protocol + "://" + uri;
302             this.secureUri = uri;
303         }
304         return this.secureUri;
305     }
306
307     /**
308      * Get the Validity object. This can either wrap the last modification
309      * date or the expires information or...
310      * If it is currently not possible to calculate such an information
311      * <code>null</code> is returned.
312      */

313     public SourceValidity getValidity() {
314         final long lm = getLastModified();
315         if (lm > 0) {
316             return new TimeStampValidity(lm);
317         }
318         return null;
319     }
320
321     /**
322      * Refresh the content of this object after the underlying data
323      * content has changed.
324      */

325     public void refresh() {
326         this.resource = null;
327     }
328
329     /**
330      * Return an <code>InputStream</code> object to read from the source.
331      * This is the data at the point of invocation of this method,
332      * so if this is Modifiable, you might get different content
333      * from two different invocations.
334      */

335     public InputStream JavaDoc getInputStream() throws IOException JavaDoc, SourceException {
336         initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
337         try {
338             if (this.resource.isCollection()) {
339                 // [UH] FIXME: why list collection as XML here?
340
// I think its a concern for the TraversableGenerator.
341
WebdavResource[] resources = this.resource.listWebdavResources();
342                 return resourcesToXml(resources);
343             } else {
344                 BufferedInputStream JavaDoc bi = null;
345                 bi = new BufferedInputStream JavaDoc(this.resource.getMethodData());
346                 if (!this.resource.exists()) {
347                     throw new HttpException(getSecureURI() + " does not exist");
348                 }
349                 return bi;
350             }
351         } catch (HttpException he) {
352             throw new SourceException("Could not get WebDAV resource " + getSecureURI(), he);
353         } catch (Exception JavaDoc e) {
354             throw new SourceException("Could not get WebDAV resource" + getSecureURI(), e);
355         }
356     }
357
358     /**
359      * The mime-type of the content described by this object.
360      * If the source is not able to determine the mime-type by itself
361      * this can be <code>null</code>.
362      */

363     public String JavaDoc getMimeType() {
364         try {
365             initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
366         } catch (IOException JavaDoc e) {
367             return null;
368         }
369         return this.resource.getGetContentType();
370     }
371
372     /**
373      * Return the content length of the content or -1 if the length is
374      * unknown
375      */

376     public long getContentLength() {
377         try {
378             initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
379         }
380         catch(IOException JavaDoc e) {
381             return -1;
382         }
383         if (this.resource.isCollection()) {
384             return -1;
385         }
386         return this.resource.getGetContentLength();
387     }
388
389     /**
390      * Get the last modification date.
391      * @return The last modification in milliseconds since January 1, 1970 GMT
392      * or 0 if it is unknown
393      */

394     public long getLastModified() {
395         try {
396             initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
397         } catch(IOException JavaDoc e) {
398             return 0;
399         }
400         return this.resource.getGetLastModified();
401     }
402
403     /**
404      * Does this source actually exist ?
405      *
406      * @return true if the resource exists.
407      */

408     public boolean exists() {
409         try {
410             initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
411         } catch (SourceNotFoundException e) {
412             return false;
413         } catch(IOException JavaDoc e) {
414             return true;
415         }
416         return this.resource.getExistence();
417     }
418
419     private InputStream JavaDoc resourcesToXml(WebdavResource[] resources)
420     throws Exception JavaDoc {
421         TransformerFactory JavaDoc tf = TransformerFactory.newInstance();
422         TransformerHandler JavaDoc th = ((SAXTransformerFactory JavaDoc) tf).newTransformerHandler();
423         ByteArrayOutputStream JavaDoc bOut = new ByteArrayOutputStream JavaDoc();
424         StreamResult JavaDoc result = new StreamResult JavaDoc(bOut);
425         th.setResult(result);
426         th.startDocument();
427         th.startPrefixMapping(PREFIX, NAMESPACE);
428         th.startElement(NAMESPACE, COLLECTION_NAME, PREFIX + ":" + COLLECTION_NAME, XMLUtils.EMPTY_ATTRIBUTES);
429         resourcesToSax(resources, th);
430         th.endElement(NAMESPACE, COLLECTION_NAME, PREFIX + ":" + COLLECTION_NAME);
431         th.endPrefixMapping(PREFIX);
432         th.endDocument();
433
434         return new ByteArrayInputStream JavaDoc(bOut.toByteArray());
435     }
436
437     private void resourcesToSax(
438         WebdavResource[] resources,
439         ContentHandler JavaDoc handler)
440         throws SAXException JavaDoc {
441         for (int i = 0; i < resources.length; i++) {
442             if (getLogger().isDebugEnabled()) {
443                 final String JavaDoc message =
444                     "RESOURCE: " + resources[i].getDisplayName();
445                 getLogger().debug(message);
446             }
447             if (resources[i].isCollection()) {
448                 try {
449                     WebdavResource[] childs =
450                         resources[i].listWebdavResources();
451                     AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
452                     attrs.addAttribute(
453                         NAMESPACE,
454                         COLLECTION_NAME,
455                         PREFIX + ":name",
456                         "CDATA",
457                         resources[i].getDisplayName());
458                     handler.startElement(
459                         NAMESPACE,
460                         COLLECTION_NAME,
461                         PREFIX + ":" + COLLECTION_NAME,
462                         attrs);
463                     this.resourcesToSax(childs, handler);
464                     handler.endElement(
465                         NAMESPACE,
466                         COLLECTION_NAME,
467                         PREFIX + ":" + COLLECTION_NAME);
468                 } catch (HttpException e) {
469                     if (getLogger().isDebugEnabled()) {
470                         final String JavaDoc message =
471                             "Unable to get WebDAV children. Server responded " +
472                             e.getReasonCode() + " (" + e.getReason() + ") - "
473                             + e.getMessage();
474                         getLogger().debug(message);
475                     }
476                 } catch (SAXException JavaDoc e) {
477                     if (getLogger().isDebugEnabled()) {
478                         final String JavaDoc message =
479                             "Unable to get WebDAV children: "
480                             + e.getMessage();
481                         getLogger().debug(message,e);
482                     }
483                 } catch (IOException JavaDoc e) {
484                     if (getLogger().isDebugEnabled()) {
485                         final String JavaDoc message =
486                             "Unable to get WebDAV children: "
487                             + e.getMessage();
488                         getLogger().debug(message,e);
489                     }
490                 } catch (Exception JavaDoc e) {
491                     if (getLogger().isDebugEnabled()) {
492                         final String JavaDoc message =
493                             "Unable to get WebDAV children: "
494                             + e.getMessage();
495                         getLogger().debug(message,e);
496                     }
497                 }
498             } else {
499                 AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
500                 attrs.addAttribute(
501                     NAMESPACE,
502                     "name",
503                     PREFIX + ":name",
504                     "CDATA",
505                     resources[i].getDisplayName());
506                 handler.startElement(
507                     NAMESPACE,
508                     RESOURCE_NAME,
509                     PREFIX + ":" + RESOURCE_NAME,
510                     attrs);
511                 handler.endElement(
512                     NAMESPACE,
513                     RESOURCE_NAME,
514                     PREFIX + ":" + RESOURCE_NAME);
515             }
516         }
517     }
518
519     // ---------------------------------------------------- TraversableSource implementation
520

521     /**
522      * Get a collection child.
523      *
524      * @see org.apache.excalibur.source.TraversableSource#getChild(java.lang.String)
525      */

526     public Source getChild(String JavaDoc childName) throws SourceException {
527         if (!isCollection()) {
528             throw new SourceException(getSecureURI() + " is not a collection.");
529         }
530         try {
531             HttpURL childURL;
532             if (this.url instanceof HttpsURL) {
533                 childURL = new HttpsURL((HttpsURL) this.url, childName);
534             } else {
535                 childURL = new HttpURL(this.url, childName);
536             }
537             return WebDAVSource.newWebDAVSource(childURL, this.protocol, getLogger());
538         } catch (URIException e) {
539             throw new SourceException("Failed to create child", e);
540         }
541     }
542
543     /**
544      * Get the collection children.
545      *
546      * @see org.apache.excalibur.source.TraversableSource#getChildren()
547      */

548     public Collection JavaDoc getChildren() throws SourceException {
549         initResource(WebdavResource.BASIC, DepthSupport.DEPTH_1);
550         ArrayList JavaDoc children = new ArrayList JavaDoc();
551         try {
552             WebdavResource[] resources = this.resource.listWebdavResources();
553             for (int i = 0; i < resources.length; i++) {
554                 HttpURL childURL;
555                 if (this.url instanceof HttpsURL) {
556                     childURL = new HttpsURL((HttpsURL) this.url,resources[i].getName());
557                 } else {
558                     childURL = new HttpURL(this.url,resources[i].getName());
559                 }
560                 WebDAVSource src = WebDAVSource.newWebDAVSource(resources[i],
561                                                                 childURL,
562                                                                 this.protocol,
563                                                                 getLogger());
564                 src.enableLogging(getLogger());
565                 children.add(src);
566             }
567         } catch (HttpException e) {
568             if (getLogger().isDebugEnabled()) {
569                 final String JavaDoc message =
570                     "Unable to get WebDAV children. Server responded " +
571                     e.getReasonCode() + " (" + e.getReason() + ") - "
572                     + e.getMessage();
573                 getLogger().debug(message);
574             }
575             throw new SourceException("Failed to get WebDAV collection children.", e);
576         } catch (SourceException e) {
577             throw e;
578         } catch (IOException JavaDoc e) {
579             throw new SourceException("Failed to get WebDAV collection children.", e);
580         }
581         return children;
582     }
583
584     /**
585      * Get the name of this resource.
586      * @see org.apache.excalibur.source.TraversableSource#getName()
587      */

588     public String JavaDoc getName() {
589         try {
590             initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
591         }
592         catch (IOException JavaDoc e) {
593             return "";
594         }
595         return this.resource.getName();
596     }
597
598     /**
599      * Get the parent.
600      *
601      * @see org.apache.excalibur.source.TraversableSource#getParent()
602      */

603     public Source getParent() throws SourceException {
604         String JavaDoc path;
605         if (this.url.getEscapedPath().endsWith("/")) {
606             path = "..";
607         }
608         else {
609             path = ".";
610         }
611         try {
612             HttpURL parentURL;
613             if (url instanceof HttpsURL) {
614                 parentURL = new HttpsURL((HttpsURL) this.url, path);
615             } else {
616                 parentURL = new HttpURL(this.url, path);
617             }
618             return WebDAVSource.newWebDAVSource(parentURL, this.protocol, getLogger());
619         } catch (URIException e) {
620             throw new SourceException("Failed to create parent", e);
621         }
622     }
623
624     /**
625      * Check if this source is a collection.
626      * @see org.apache.excalibur.source.TraversableSource#isCollection()
627      */

628     public boolean isCollection() {
629         try {
630             initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
631         }
632         catch (IOException JavaDoc e) {
633             return false;
634         }
635         return this.resource.isCollection();
636     }
637
638     // ---------------------------------------------------- ModifiableSource implementation
639

640     /**
641      * Get an <code>OutputStream</code> where raw bytes can be written to.
642      * The signification of these bytes is implementation-dependent and
643      * is not restricted to a serialized XML document.
644      *
645      * @return a stream to write to
646      */

647     public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
648         return new WebDAVSourceOutputStream(this);
649     }
650
651     /**
652      * Can the data sent to an <code>OutputStream</code> returned by
653      * {@link #getOutputStream()} be cancelled ?
654      *
655      * @return true if the stream can be cancelled
656      */

657     public boolean canCancel(OutputStream JavaDoc stream) {
658         if (stream instanceof WebDAVSourceOutputStream) {
659             WebDAVSourceOutputStream wsos = (WebDAVSourceOutputStream) stream;
660             if (wsos.source == this) {
661                 return wsos.canCancel();
662             }
663         }
664         throw new IllegalArgumentException JavaDoc("The stream is not associated to this source");
665     }
666
667     /**
668      * Cancel the data sent to an <code>OutputStream</code> returned by
669      * {@link #getOutputStream()}.
670      * <p>
671      * After cancel, the stream should no more be used.
672      */

673     public void cancel(OutputStream JavaDoc stream) throws SourceException {
674         if (stream instanceof WebDAVSourceOutputStream) {
675             WebDAVSourceOutputStream wsos = (WebDAVSourceOutputStream) stream;
676             if (wsos.source == this) {
677                 try {
678                     wsos.cancel();
679                 }
680                 catch (Exception JavaDoc e) {
681                     throw new SourceException("Failure cancelling Source", e);
682                 }
683             }
684         }
685         throw new IllegalArgumentException JavaDoc("The stream is not associated to this source");
686     }
687
688     /**
689      * Delete this source (unimplemented).
690      * @see org.apache.excalibur.source.ModifiableSource#delete()
691      */

692     public void delete() throws SourceException {
693       initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
694         try {
695             this.resource.deleteMethod();
696         } catch (HttpException e) {
697             throw new SourceException("Unable to delete source: " + getSecureURI(), e);
698         } catch (IOException JavaDoc e) {
699             throw new SourceException("Unable to delete source: " + getSecureURI(), e);
700         }
701     }
702
703     private static class WebDAVSourceOutputStream extends ByteArrayOutputStream JavaDoc {
704
705         private WebDAVSource source = null;
706         private boolean isClosed = false;
707
708         private WebDAVSourceOutputStream(WebDAVSource source) {
709             this.source = source;
710         }
711
712         public void close() throws IOException JavaDoc {
713             if (!isClosed) {
714                 try {
715                     super.close();
716                     this.source.initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
717                     this.source.resource.putMethod(toByteArray());
718                 } catch (HttpException he) {
719                     final String JavaDoc message =
720                         "Unable to close output stream. Server responded " +
721                         he.getReasonCode() + " (" + he.getReason() + ") - "
722                         + he.getMessage();
723                     this.source.getLogger().debug(message);
724                     throw new IOException JavaDoc(he.getMessage());
725                 }
726                 finally {
727                     this.isClosed = true;
728                 }
729             }
730         }
731
732         private boolean canCancel() {
733             return !isClosed;
734         }
735
736         private void cancel() {
737             if (isClosed) {
738                 throw new IllegalStateException JavaDoc("Cannot cancel: outputstream is already closed");
739             }
740             this.isClosed = true;
741         }
742     }
743
744     // ---------------------------------------------------- ModifiableTraversableSource implementation
745

746     /**
747      * Create the collection, if it doesn't exist.
748      * @see org.apache.excalibur.source.ModifiableTraversableSource#makeCollection()
749      */

750     public void makeCollection() throws SourceException {
751         initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
752         if (this.resource.exists()) return;
753         try {
754             if (!this.resource.mkcolMethod()) {
755                 int status = this.resource.getStatusCode();
756                 if (status == 409) {
757                     // parent does not exist, create it and try again
758
((ModifiableTraversableSource) getParent()).makeCollection();
759                     makeCollection();
760                 }
761                 else if (status == 404) {
762                     // apparently mod_dav_svn wrongly returns 404
763
// on MKCOL when parent does not exist
764
((ModifiableTraversableSource) getParent()).makeCollection();
765                     makeCollection();
766                 }
767                 // Ignore status 405 - Not allowed: collection already exists
768
else if (status != 405) {
769                     final String JavaDoc msg =
770                         "Unable to create collection " + getSecureURI()
771                         + ". Server responded " + this.resource.getStatusCode()
772                         + " (" + this.resource.getStatusMessage() + ")";
773                     throw new SourceException(msg);
774                 }
775             }
776         } catch (HttpException e) {
777             throw new SourceException("Unable to create collection(s) " + getSecureURI(), e);
778         } catch (SourceException e) {
779             throw e;
780         } catch (IOException JavaDoc e) {
781             throw new SourceException("Unable to create collection(s)" + getSecureURI(), e);
782         }
783     }
784
785     // ---------------------------------------------------- InspectableSource implementation
786

787     /**
788      * Returns a enumeration of the properties
789      *
790      * @return Enumeration of SourceProperty
791      *
792      * @throws SourceException If an exception occurs.
793      */

794      public SourceProperty[] getSourceProperties() throws SourceException {
795
796          initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
797
798          Vector JavaDoc sourceproperties = new Vector JavaDoc();
799          Enumeration JavaDoc props= null;
800          org.apache.webdav.lib.Property prop = null;
801
802          try {
803              Enumeration JavaDoc responses = this.resource.propfindMethod(0);
804              while (responses.hasMoreElements()) {
805
806                  ResponseEntity response = (ResponseEntity)responses.nextElement();
807                  props = response.getProperties();
808                  while (props.hasMoreElements()) {
809                      prop = (Property) props.nextElement();
810                      SourceProperty srcProperty = new SourceProperty(prop.getElement());
811                      sourceproperties.addElement(srcProperty);
812                  }
813              }
814
815          } catch (Exception JavaDoc e) {
816              throw new SourceException("Error getting properties", e);
817          }
818          SourceProperty[] sourcepropertiesArray = new SourceProperty[sourceproperties.size()];
819          for (int i = 0; i<sourceproperties.size(); i++) {
820              sourcepropertiesArray[i] = (SourceProperty) sourceproperties.elementAt(i);
821          }
822          return sourcepropertiesArray;
823     }
824
825     /**
826      * Returns a property from a source.
827      *
828      * @param namespace Namespace of the property
829      * @param name Name of the property
830      *
831      * @return Property of the source.
832      *
833      * @throws SourceException If an exception occurs.
834      */

835     public SourceProperty getSourceProperty (String JavaDoc namespace, String JavaDoc name) throws SourceException {
836
837         initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
838
839         Vector JavaDoc propNames = new Vector JavaDoc(1);
840         propNames.add(new PropertyName(namespace,name));
841         Enumeration JavaDoc props= null;
842         org.apache.webdav.lib.Property prop = null;
843         try {
844             Enumeration JavaDoc responses = this.resource.propfindMethod(0, propNames);
845             while (responses.hasMoreElements()) {
846                 ResponseEntity response = (ResponseEntity) responses.nextElement();
847                 props = response.getProperties();
848                 if (props.hasMoreElements()) {
849                     prop = (Property) props.nextElement();
850                     return new SourceProperty(prop.getElement());
851                 }
852             }
853         } catch (Exception JavaDoc e) {
854             throw new SourceException("Error getting property: "+name, e);
855         }
856         return null;
857     }
858
859     /**
860      * Remove a specified source property.
861      *
862      * @param namespace Namespace of the property.
863      * @param name Name of the property.
864      *
865      * @throws SourceException If an exception occurs.
866      */

867     public void removeSourceProperty(String JavaDoc namespace, String JavaDoc name)
868     throws SourceException {
869
870         initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
871
872         try {
873             this.resource.proppatchMethod(new PropertyName(namespace, name), "", false);
874         } catch (Exception JavaDoc e) {
875             throw new SourceException("Could not remove property ", e);
876         }
877     }
878
879     /**
880      * Sets a property for a source.
881      *
882      * @param sourceproperty Property of the source
883      *
884      * @throws SourceException If an exception occurs during this operation
885      */

886     public void setSourceProperty(SourceProperty sourceproperty) throws SourceException {
887
888         initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
889
890         try {
891             Node JavaDoc node = null;
892             NodeList JavaDoc list = sourceproperty.getValue().getChildNodes();
893             for (int i=0; i<list.getLength(); i++) {
894                 if ((list.item(i) instanceof Text JavaDoc && !"".equals(list.item(i).getNodeValue()))
895                     || list.item(i) instanceof Element JavaDoc) {
896
897                     node = list.item(i);
898                     break;
899                 }
900             }
901
902             Properties JavaDoc format = new Properties JavaDoc();
903             format.put(OutputKeys.METHOD, "xml");
904             format.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
905             String JavaDoc prop = XMLUtils.serializeNode(node, format);
906
907             this.resource.proppatchMethod(
908                    new PropertyName(sourceproperty.getNamespace(),sourceproperty.getName()),
909                    prop, true);
910
911         } catch(HttpException e) {
912             final String JavaDoc message =
913                 "Unable to set property. Server responded " +
914                 e.getReasonCode() + " (" + e.getReason() + ") - "
915                 + e.getMessage();
916             getLogger().debug(message);
917             throw new SourceException("Could not set property ", e);
918         } catch (Exception JavaDoc e) {
919             throw new SourceException("Could not set property ", e);
920         }
921     }
922
923     /**
924      * Get the current credential for the source
925      */

926 // public SourceCredential getSourceCredential() throws SourceException {
927
// if (this.principal != null) {
928
// return new SourceCredential(this.principal, this.password);
929
// }
930
// return null;
931
// }
932

933     /**
934      * Set the credential for the source
935      */

936 // public void setSourceCredential(SourceCredential sourcecredential)
937
// throws SourceException {
938
// if (sourcecredential != null) {
939
// this.password = sourcecredential.getPassword();
940
// this.principal = sourcecredential.getPrincipal();
941
// refresh();
942
// }
943
// }
944

945     // ---------------------------------------------------- MoveableSource
946

947     /**
948      * Move the current source to a specified destination.
949      *
950      * @param source
951      *
952      * @throws SourceException If an exception occurs during the move.
953      */

954     public void moveTo(Source source) throws SourceException {
955         if (source instanceof WebDAVSource) {
956             initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
957             WebDAVSource destination = (WebDAVSource)source;
958             destination.initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
959             try {
960                  this.resource.moveMethod(destination.resource.getHttpURL().getPath());
961             } catch (HttpException e) {
962                 throw new SourceException("Cannot move source '"+getSecureURI()+"'", e);
963             } catch (IOException JavaDoc e) {
964                 throw new SourceException("Cannot move source '"+getSecureURI()+"'", e);
965             }
966         } else {
967             SourceUtil.move(this,source);
968         }
969     }
970
971     /**
972      * Copy the current source to a specified destination.
973      *
974      * @param source
975      *
976      * @throws SourceException If an exception occurs during the copy.
977      */

978     public void copyTo(Source source) throws SourceException {
979         if (source instanceof WebDAVSource) {
980             initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
981             WebDAVSource destination = (WebDAVSource)source;
982             destination.initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
983             try {
984                 this.resource.copyMethod(destination.resource.getHttpURL().getPath());
985             } catch (HttpException e) {
986                 throw new SourceException("Cannot copy source '"+getSecureURI()+"'", e);
987             } catch (IOException JavaDoc e) {
988                 throw new SourceException("Cannot copy source '"+getSecureURI()+"'", e);
989             }
990         } else {
991             SourceUtil.copy(this,source);
992         }
993     }
994 }
995
Popular Tags