KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > webdav > lib > methods > XMLResponseMethodBase


1 /*
2  * $Header: /home/cvs/jakarta-slide/webdavclient/clientlib/src/java/org/apache/webdav/lib/methods/XMLResponseMethodBase.java,v 1.14.2.2 2004/11/17 14:00:44 luetzkendorf Exp $
3  * $Revision: 1.14.2.2 $
4  * $Date: 2004/11/17 14:00:44 $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2002 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24
25 package org.apache.webdav.lib.methods;
26
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.io.StringWriter JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import java.util.Hashtable JavaDoc;
32 import java.util.Vector JavaDoc;
33
34 import javax.xml.parsers.DocumentBuilder JavaDoc;
35 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
36 import javax.xml.parsers.ParserConfigurationException JavaDoc;
37
38 import org.apache.commons.httpclient.Header;
39 import org.apache.commons.httpclient.HttpConnection;
40 import org.apache.commons.httpclient.HttpException;
41 import org.apache.commons.httpclient.HttpState;
42 import org.apache.commons.httpclient.HttpStatus;
43 import org.apache.commons.httpclient.URIException;
44 import org.apache.commons.httpclient.util.URIUtil;
45
46 import org.apache.webdav.lib.Property;
47 import org.apache.webdav.lib.ResponseEntity;
48 import org.apache.webdav.lib.properties.PropertyFactory;
49 import org.apache.webdav.lib.util.DOMUtils;
50 import org.apache.webdav.lib.util.DOMWriter;
51 import org.apache.webdav.lib.util.WebdavStatus;
52 import org.apache.webdav.lib.util.XMLDebugOutputer;
53 import org.w3c.dom.Document JavaDoc;
54 import org.w3c.dom.Element JavaDoc;
55 import org.w3c.dom.Node JavaDoc;
56 import org.w3c.dom.NodeList JavaDoc;
57 import org.xml.sax.ErrorHandler JavaDoc;
58 import org.xml.sax.InputSource JavaDoc;
59 import org.xml.sax.SAXException JavaDoc;
60 import org.xml.sax.SAXParseException JavaDoc;
61
62 /**
63  * Utility class for XML response parsing.
64  *
65  */

66 public abstract class XMLResponseMethodBase
67     extends HttpRequestBodyMethodBase {
68
69     //static private final Log log = LogSource.getInstance(XMLResponseMethodBase.class.getName());
70

71     
72     // debug level
73
private int debug = 0;
74
75
76     // ----------------------------------------------------------- Constructors
77

78
79     /**
80      * Method constructor.
81      */

82     public XMLResponseMethodBase() {
83         super();
84     }
85
86
87     /**
88      * Method constructor.
89      *
90      * @param uri the URI to request
91      */

92     public XMLResponseMethodBase(String JavaDoc uri) {
93         super(uri);
94         
95     }
96
97
98     // ----------------------------------------------------- Instance Variables
99

100     /**
101      * XML Debug Outputer
102      */

103     private XMLDebugOutputer xo = new XMLDebugOutputer();
104
105
106     /**
107      * Response document.
108      */

109     private Document JavaDoc responseDocument = null;
110
111
112     /**
113      * Document builder.
114      */

115     protected DocumentBuilder JavaDoc builder = null;
116
117
118     /**
119      * Hashtable of response nodes
120      */

121     private Hashtable JavaDoc responseHashtable = null;
122
123     /**
124      * Vector of response nodes, to keep track of insertion order
125      * FIXME: the above Hashtable and this Vector should be ported
126      * to plain Collections
127      */

128     protected Vector JavaDoc responseURLs = null;
129
130     protected String JavaDoc decodeResponseHrefs = null;
131
132     // ------------------------------------------------------------- Properties
133

134
135     /**
136      * Response document getter.
137      *
138      * @return Document response document
139      */

140     public Document JavaDoc getResponseDocument() {
141         return this.responseDocument;
142     }
143
144
145     /**
146      * Return an enumeration containing the responses.
147      *
148      * @return An enumeration containing objects implementing the
149      * ResponseEntity interface
150      */

151     public Enumeration JavaDoc getResponses() {
152         return getResponseHashtable().elements();
153     }
154
155
156     // --------------------------------------------------- WebdavMethod Methods
157

158     /**
159      * Debug property setter.
160      *
161      * @param int Debug
162      */

163     public void setDebug(int debug) {
164         this.debug = debug;
165         
166         xo.setDebug((debug > 0));
167     }
168     
169     /**
170      * Debug property getter.
171      *
172      */

173     public int getDebug() {
174         return this.debug;
175     }
176
177     /**
178      * Sets whether the href in responses are decoded, as early as possible.
179      * The <code>href</code> data in responses is often url-encoded, but not
180      * alwyas in a comparable way. Set this to a non-null value to decode the
181      * hrefs as early as possible.
182      * @param encoding The encoding used in while decoding (UTF-8 is recommended)
183      */

184     public void setDecodeResponseHrefs(String JavaDoc encoding) {
185         this.decodeResponseHrefs = encoding;
186     }
187
188     /**
189      * Reset the State of the class to its initial state, so that it can be
190      * used again.
191      */

192     public void recycle() {
193         super.recycle();
194         responseHashtable = null;
195         responseURLs = null;
196     }
197
198     protected void readResponseBody(HttpState state, HttpConnection conn)
199     throws IOException JavaDoc, HttpException {
200
201         super.readResponseBody(state, conn);
202         InputStream JavaDoc inStream = getResponseBodyAsStream();
203         if (inStream != null) {
204             parseResponse(inStream, state, conn);
205             inStream.close();
206         }
207     }
208
209     /**
210      * Return the length (in bytes) of my request body, suitable for use in a
211      * <tt>Content-Length</tt> header.
212      *
213      * <p>
214      * Return <tt>-1</tt> when the content-length is unknown.
215      * </p>
216      *
217      * <p>
218      * This implementation returns <tt>0</tt>, indicating that the request has
219      * no body.
220      * </p>
221      *
222      * @return <tt>0</tt>, indicating that the request has no body.
223      */

224     protected int getRequestContentLength() {
225         if (!isRequestContentAlreadySet()) {
226             String JavaDoc contents = generateRequestBody();
227             // be nice - allow overriding functions to return null or empty
228
// strings for no content.
229
if (contents == null)
230                 contents = "";
231
232             setRequestBody(contents);
233             
234
235             if (debug > 0) {
236                 System.out.println("\n>>>>>>> to server ---------------------------------------------------");
237                 System.out.println(getName() + " " +
238                    getPath() + (getQueryString() != null ? "?" + getQueryString() : "") + " " + "HTTP/1.1");
239         
240                    Header[] headers = getRequestHeaders();
241                    for (int i = 0; i < headers.length; i++) {
242                        Header header = headers[i];
243                        System.out.print(header.toString());
244                    }
245                 System.out.println("Content-Length: "+super.getRequestContentLength());
246                    
247                 if (this instanceof DepthSupport) {
248                     System.out.println("Depth: "+((DepthSupport)this).getDepth());
249                 }
250
251                 System.out.println();
252                 xo.print(contents);
253                 System.out.println("------------------------------------------------------------------------");
254             }
255
256         }
257
258         return super.getRequestContentLength();
259     }
260
261
262
263
264     /**
265      * DAV requests that contain a body must override this function to
266      * generate that body.
267      *
268      * <p>The default behavior simply returns an empty body.</p>
269      */

270     protected String JavaDoc generateRequestBody() {
271         return "";
272     }
273
274     /**
275      * Write the request body to the given {@link HttpConnection}.
276      *
277      * <p>
278      * This implementation writes any computed body and returns <tt>true</tt>.
279      * </p>
280      *
281      * @param state the client state
282      * @param conn the connection to write to
283      *
284      * @return <tt>true</tt>
285      * @throws IOException when i/o errors occur reading the response
286      * @throws HttpException when a protocol error occurs or state is invalid
287      */

288     protected boolean writeRequestBody(HttpState state, HttpConnection conn)
289             throws IOException JavaDoc, HttpException {
290
291         if (getRequestContentLength() > 0) {
292             return super.writeRequestBody(state, conn);
293         }
294         return true;
295     }
296
297     /**
298      * Parse response.
299      *
300      * @param input Input stream
301      */

302     public void parseResponse(InputStream JavaDoc input, HttpState state, HttpConnection conn)
303         throws IOException JavaDoc, HttpException {
304         // Also accept OK sent by buggy servers in reply to a PROPFIND or
305
// REPORT (Xythos, Catacomb, ...?).
306
if (getStatusCode() == WebdavStatus.SC_MULTI_STATUS
307             || (this instanceof PropFindMethod || this instanceof ReportMethod)
308                 && getStatusCode() == HttpStatus.SC_OK) {
309             try {
310                 parseXMLResponse(input);
311             } catch (IOException JavaDoc e) {
312                 // FIX ME: provide a way to deliver non xml data
313
}
314         }
315     }
316
317
318     protected void parseXMLResponse(InputStream JavaDoc input)
319         throws IOException JavaDoc, HttpException {
320
321         if (builder == null) {
322             try {
323                 // TODO: avoid the newInstance call for each method instance for performance reasons.
324
DocumentBuilderFactory JavaDoc factory =
325                     DocumentBuilderFactory.newInstance();
326                 factory.setNamespaceAware(true);
327                 builder = factory.newDocumentBuilder();
328             } catch (ParserConfigurationException JavaDoc e) {
329                 throw new HttpException
330                     ("XML Parser Configuration error: " + e.getMessage());
331             }
332         }
333
334         try {
335
336             // avoid ugly printlns from the default error handler.
337
builder.setErrorHandler(new DummyErrorHandler());
338             responseDocument = builder.parse(new InputSource JavaDoc(input));
339             
340             if (debug > 0) {
341                System.out.println("\n<<<<<<< from server ---------------------------------------------------");
342                System.out.println(getStatusLine());
343                
344                Header[] headers = getResponseHeaders();
345                for (int i = 0; i < headers.length; i++) {
346                   Header header = headers[i];
347                   System.out.print(header.toString());
348                }
349                
350                System.out.println();
351                
352                xo.print(responseDocument);
353                System.out.println("------------------------------------------------------------------------");
354             }
355
356
357         } catch (Exception JavaDoc e) {
358             throw new IOException JavaDoc
359                 ("XML parsing error; response stream is not valid XML: "
360                  + e.getMessage());
361         }
362
363
364         // init the response table to display the responses during debugging
365
/*if (debug > 10) {
366             //if (log.isDebugEnabled()) {
367             initResponseHashtable();
368         }*/

369
370     }
371
372
373     protected Hashtable JavaDoc getResponseHashtable() {
374         checkUsed();
375         if (responseHashtable == null) {
376             initResponseHashtable();
377         }
378         return responseHashtable;
379     }
380
381     protected Vector JavaDoc getResponseURLs() {
382         checkUsed();
383         if (responseHashtable == null) {
384             initResponseHashtable();
385         }
386         return responseURLs;
387     }
388
389     private synchronized void initResponseHashtable() {
390         if (responseHashtable == null) {
391
392             responseHashtable = new Hashtable JavaDoc();
393             responseURLs = new Vector JavaDoc();
394             int status = getStatusLine().getStatusCode();
395            
396             // Also accept OK sent by buggy servers in reply to a PROPFIND
397
// or REPORT (Xythos, Catacomb, ...?).
398
if (status == WebdavStatus.SC_MULTI_STATUS
399                 || (this instanceof PropFindMethod
400                     || this instanceof ReportMethod)
401                     && status == HttpStatus.SC_OK) {
402
403
404                 Document JavaDoc rdoc = getResponseDocument();
405
406                 NodeList JavaDoc list = null;
407                 if (rdoc != null) {
408                     Element JavaDoc multistatus = getResponseDocument().getDocumentElement();
409                     list = multistatus.getChildNodes();
410                 }
411
412                 if (list != null) {
413                     for (int i = 0; i < list.getLength(); i++) {
414                         try {
415                             Element JavaDoc child = (Element JavaDoc) list.item(i);
416                             String JavaDoc name = DOMUtils.getElementLocalName(child);
417                             String JavaDoc namespace = DOMUtils.getElementNamespaceURI
418                                 (child);
419                             if (Response.TAG_NAME.equals(name) &&
420                                 "DAV:".equals(namespace)) {
421                                 Response response =
422                                     new ResponseWithinMultistatus(child);
423                                 String JavaDoc href = getHref(response);
424                                 responseHashtable.put(href,response);
425                                 responseURLs.add(href);
426                             }
427                         } catch (ClassCastException JavaDoc e) {
428                         }
429                     }
430                 }
431             } else if (responseDocument != null) {
432                 Response response = new SingleResponse(responseDocument,
433                     getPath(), status);
434                 String JavaDoc href = getHref(response);
435                 responseHashtable.put(href, response);
436                 responseURLs.add(href);
437             }
438         }
439     }
440
441
442     private String JavaDoc getHref(Response response) {
443         String JavaDoc href = response.getHref();
444         if (this.decodeResponseHrefs != null) {
445             try {
446                 href = URIUtil.decode(href, this.decodeResponseHrefs);
447             }
448             catch (URIException e1) {
449                 // TODO Auto-generated catch block
450
e1.printStackTrace();
451             }
452         }
453         return href;
454     }
455
456
457     /**
458      * This method creates a property implementation from an element.
459      * It treats known properties (i.e., the DAV properties) specially.
460      * These properties are instantiated as an implementation from the
461      * <code>org.apache.webdav.lib.properties</code> package.
462      */

463     protected static Property convertElementToProperty(
464         Response response, Element JavaDoc element) {
465
466         return PropertyFactory.create(response, element);
467         
468     }
469
470
471     // ---------------------------------------------------------- Inner Classes
472

473
474     /**
475      * An abstract class that models a DAV:response.
476      */

477     public abstract class Response implements ResponseEntity {
478
479         protected Node JavaDoc node = null;
480
481         Response(Node JavaDoc node) {
482             this.node = node;
483         }
484
485         public static final String JavaDoc TAG_NAME = "response";
486         public abstract int getStatusCode();
487         public abstract String JavaDoc getHref();
488
489         public Enumeration JavaDoc getHistories(){
490             Vector JavaDoc result = new Vector JavaDoc();
491             return result.elements();
492         }
493         public Enumeration JavaDoc getWorkspaces(){
494             Vector JavaDoc result = new Vector JavaDoc();
495             return result.elements();
496         }
497         public Enumeration JavaDoc getProperties() {
498             NodeList JavaDoc list =
499                 DOMUtils.getElementsByTagNameNS(node, "prop", "DAV:");
500             Vector JavaDoc vector = new Vector JavaDoc();
501             for (int i = 0; list != null && i < list.getLength(); i++ ) {
502                 Element JavaDoc element = (Element JavaDoc) list.item(i);
503                 NodeList JavaDoc children = element.getChildNodes();
504                 for (int j = 0; children != null && j < children.getLength();
505                      j++) {
506                     try {
507                         Element JavaDoc child = (Element JavaDoc) children.item(j);
508                         vector.addElement(XMLResponseMethodBase.
509                             convertElementToProperty(this, child));
510                     } catch (ClassCastException JavaDoc e) {
511                     }
512                 }
513             }
514             return vector.elements();
515         }
516
517         public String JavaDoc toString () {
518             StringWriter JavaDoc tmp = new StringWriter JavaDoc();
519             DOMWriter domWriter = new DOMWriter(tmp, true);
520             domWriter.print(node);
521             return tmp.getBuffer().toString();
522         }
523     }
524
525
526     /**
527      * A class that models the DAV:response element within a multistatus.
528      */

529     class ResponseWithinMultistatus extends Response {
530
531         public ResponseWithinMultistatus(Element JavaDoc element) {
532             super(element);
533         }
534
535         public int getStatusCode() {
536             // The status element for the response can be inside the propstat element
537
// or directly inside the response element.
538

539             // <multistatus xmlns=\DAV:\>
540
// <response>
541
// <href>/slide/files/</href>
542
// <propstat>
543
// <prop><displayname>files</displayname></prop>
544
// <status>HTTP/1.1 200 OK</status>
545
// </propstat>
546
// </response>
547
// </multistatus>
548
Element JavaDoc propstat = getFirstElement("DAV:", "propstat");
549             if (propstat != null ) {
550                 Element JavaDoc status = DOMUtils.getFirstElement(propstat,"DAV:", "status");
551                 if (status != null) {
552                     return DOMUtils.parseStatus(DOMUtils.getTextValue(status));
553                 }
554             }
555
556             // <multistatus xmlns=\DAV:\>
557
// <response>
558
// <href>/slide/files/</href>
559
// <href>/slide/files/a</href>
560
// <status>HTTP/1.1 200 OK</status>
561
// </response>
562
// </multistatus>
563
Element JavaDoc status = getFirstElement("DAV:", "status");
564             if (status != null) {
565                 return DOMUtils.parseStatus(DOMUtils.getTextValue(status));
566             }
567
568             return -1;
569         }
570
571         public String JavaDoc getHref() {
572             Element JavaDoc href = getFirstElement("DAV:", "href");
573             if (href != null) {
574                 return DOMUtils.getTextValue(href);
575             } else {
576                 return "";
577             }
578
579         }
580
581         protected Element JavaDoc getFirstElement(String JavaDoc namespace, String JavaDoc name) {
582             return DOMUtils.getFirstElement(this.node, namespace, name);
583         }
584     }
585
586     class SingleResponse extends Response {
587
588         private int statusCode = -1;
589         private String JavaDoc href = null;
590
591         SingleResponse(Document JavaDoc document, String JavaDoc href, int statusCode) {
592             super(document);
593             this.statusCode = statusCode;
594             this.href = href;
595         }
596
597         public int getStatusCode() {
598             return this.statusCode;
599         }
600
601         public String JavaDoc getHref() {
602             return this.href;
603         }
604     }
605
606         class OptionsResponse extends SingleResponse{
607
608             OptionsResponse(Document JavaDoc document, String JavaDoc href, int statusCode) {
609                 super(document, href, statusCode);
610
611             }
612
613
614             public Enumeration JavaDoc getWorkspaces(){
615
616
617                 Node JavaDoc root = responseDocument.cloneNode(true).getFirstChild();
618                 //System.out.println("Rootnode ws: "+ root.getNodeName());
619

620                 String JavaDoc sPrefix = root.getPrefix();
621                 Vector JavaDoc result = new Vector JavaDoc();
622
623                 Node JavaDoc child = root.getFirstChild();
624                 while (child!=null && !child.getNodeName().equalsIgnoreCase(sPrefix+":workspace-collection-set")){
625                     child = child.getNextSibling();
626                 }
627
628                 if (child!=null && child.getNodeName().equalsIgnoreCase(sPrefix+":workspace-collection-set")){
629                     child = child.getFirstChild();
630                     while (child!=null){
631                         result.add(child.getFirstChild().getNodeValue());
632                         child = child.getNextSibling();
633                     }
634                 }
635
636                 return result.elements();
637             }
638
639             public Enumeration JavaDoc getHistories(){
640                 Node JavaDoc root = responseDocument.cloneNode(true).getFirstChild();
641                 //System.out.println("Rootnode vh : " + root.getNodeName());
642

643                 String JavaDoc sPrefix = root.getPrefix();
644                 Vector JavaDoc result = new Vector JavaDoc();
645
646                 //System.out.println("Prefix : " + sPrefix);
647

648                 Node JavaDoc child = root.getFirstChild();
649                 while (child!=null && !child.getNodeName().equalsIgnoreCase(sPrefix+":version-history-collection-set")){
650                     child = child.getNextSibling();
651                 }
652
653                 if (child!=null && child.getNodeName().equalsIgnoreCase(sPrefix+":version-history-collection-set")){
654                     child = child.getFirstChild();
655                     while (child!=null){
656                         result.add(child.getFirstChild().getNodeValue());
657                         child = child.getNextSibling();
658                     }
659                 }
660
661                 return result.elements();
662             }
663
664         }
665         protected void setDocument(Document JavaDoc doc){
666             responseDocument = doc;
667         }
668         protected void setResponseHashtable(Hashtable JavaDoc h){
669             responseHashtable = h;
670         }
671
672
673
674
675 private static class DummyErrorHandler implements ErrorHandler JavaDoc {
676
677
678     /**
679      * Receive notification of a warning.
680      *
681      * <p>SAX parsers will use this method to report conditions that
682      * are not errors or fatal errors as defined by the XML 1.0
683      * recommendation. The default behaviour is to take no action.</p>
684      *
685      * <p>The SAX parser must continue to provide normal parsing events
686      * after invoking this method: it should still be possible for the
687      * application to process the document through to the end.</p>
688      *
689      * <p>Filters may use this method to report other, non-XML warnings
690      * as well.</p>
691      *
692      * @param exception The warning information encapsulated in a
693      * SAX parse exception.
694      * @exception org.xml.sax.SAXException Any SAX exception, possibly
695      * wrapping another exception.
696      * @see org.xml.sax.SAXParseException
697      */

698     public void warning(SAXParseException JavaDoc exception) throws SAXException JavaDoc
699     {
700         // System.out.println("warning: " + exception.getMessage());
701
}
702
703     /**
704      * Receive notification of a recoverable error.
705      *
706      * <p>This corresponds to the definition of "error" in section 1.2
707      * of the W3C XML 1.0 Recommendation. For example, a validating
708      * parser would use this callback to report the violation of a
709      * validity constraint. The default behaviour is to take no
710      * action.</p>
711      *
712      * <p>The SAX parser must continue to provide normal parsing events
713      * after invoking this method: it should still be possible for the
714      * application to process the document through to the end. If the
715      * application cannot do so, then the parser should report a fatal
716      * error even if the XML 1.0 recommendation does not require it to
717      * do so.</p>
718      *
719      * <p>Filters may use this method to report other, non-XML errors
720      * as well.</p>
721      *
722      * @param exception The error information encapsulated in a
723      * SAX parse exception.
724      * @exception org.xml.sax.SAXException Any SAX exception, possibly
725      * wrapping another exception.
726      * @see org.xml.sax.SAXParseException
727      */

728     public void error(SAXParseException JavaDoc exception) throws SAXException JavaDoc
729     {
730         // System.out.println("error: " + exception.getMessage());
731
}
732
733     /**
734      * Receive notification of a non-recoverable error.
735      *
736      * <p>This corresponds to the definition of "fatal error" in
737      * section 1.2 of the W3C XML 1.0 Recommendation. For example, a
738      * parser would use this callback to report the violation of a
739      * well-formedness constraint.</p>
740      *
741      * <p>The application must assume that the document is unusable
742      * after the parser has invoked this method, and should continue
743      * (if at all) only for the sake of collecting addition error
744      * messages: in fact, SAX parsers are free to stop reporting any
745      * other events once this method has been invoked.</p>
746      *
747      * @param exception The error information encapsulated in a
748      * SAX parse exception.
749      * @exception org.xml.sax.SAXException Any SAX exception, possibly
750      * wrapping another exception.
751      * @see org.xml.sax.SAXParseException
752      */

753     public void fatalError(SAXParseException JavaDoc exception) throws SAXException JavaDoc
754     {
755          // System.out.println("fatal: " + exception.getMessage());
756
}
757
758 }
759
760
761
762
763 }
764
765
Popular Tags