KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > webdav > protocol > http > WebDAVMethod


1 package com.ibm.webdav.protocol.http;
2
3 /*
4  * (C) Copyright IBM Corp. 2000 All rights reserved.
5  *
6  * The program is provided "AS IS" without any warranty express or
7  * implied, including the warranty of non-infringement and the implied
8  * warranties of merchantibility and fitness for a particular purpose.
9  * IBM will not be liable for any damages suffered by you as a result
10  * of using the Program. In no event will IBM be liable for any
11  * special, indirect or consequential damages or lost profits even if
12  * IBM has been advised of the possibility of their occurrence. IBM
13  * will not be liable for any third party claims against you.
14  *
15  * Portions Copyright (C) Simulacra Media Ltd, 2004.
16  */

17 import java.io.*;
18 import java.net.*;
19 import java.rmi.*;
20 import java.util.*;
21 import java.util.logging.*;
22
23 import javax.servlet.*;
24 import javax.servlet.http.*;
25
26 import com.ibm.webdav.*;
27 import com.ibm.webdav.impl.*;
28
29
30 /**
31  * WebDAVMethod is the abstract superclass of classes corresponding to the
32  * WebDAV methods using the command pattern. It maintains the state and logic
33  * that is common to the execution of all webDAV methods. These subclasses
34  * are a good place to see how the WebDAV protocol is mapped back onto the
35  * DAV4J API.
36  * <p>
37  * WebDAVMethods are constructed by the HTTP server skeleton ResourceHTTPSkel
38  * in order to dispatch Resource methods to ResourceImpl methods on the server.
39  * This treats HTTP just like any other remote procedure call mechanism, and
40  * unifies the client/server communication.</p>
41  * <p>
42  * In general, the execution of a WebDAV method consists of:
43  * <ol>
44  * <li>create an instance of a Resource implementation corresponding to the resource being
45  * manipulated by the method</li>
46  * <li>set the ResourceImpl request context from the request headers using getRequestHeaders()</li>
47  * <li>get the request entity (if any), parse it, and marshal any arguments
48  * it contains for the method</li>
49  * <li>call the ResoureImpl method corresponding to the WebDAVMethod subclass (WebDAV or HTTP method)</li>
50  * <li>put any response context into the result headers using setResponseHeaders()</li>
51  * <li>output the response if any</li>
52  * <li>catch exceptions and translate them to set the status code and message
53  * for method</li>
54  * </ol>
55  * <p>
56  * Some of these operations are generic to all methods while others are specific
57  * to each subclass. In particular, how the context values,
58  * request entity, generation of the result entity, and status codes are handled
59  * are subclass specific.
60  * </p>
61  * <p>Note it is critical that the execute method sets status codes and the request
62  * and response headers at the right time. The order is, (1) use setStatusCode() and set
63  * other response headers the method needs to set. (2) Call setResponseHeaders() to
64  * copy the resource response context to the response headers. (3) Output any response entity
65  * body. The headers and response code are written the the HTTP response output
66  * stream just before the first byte of the response entity body. Any
67  * headers or status code set after the first byte of the response entity body has
68  * been written will be lost.</p>
69  */

70 public abstract class WebDAVMethod extends Object JavaDoc {
71     protected ResourceImpl resource; // t10he resource the method is operating on
72
protected HttpServletRequest request; // the request from the client
73
protected HttpServletResponse response; // the response from the server
74

75     // contexts for communicating HTTP and WebDAV headers (method contol couples)
76
protected ResourceContext context = new ResourceContext();
77     protected String JavaDoc methodName = null;
78     private static final Logger logger = Logger.getLogger(WebDAVMethod.class.getName());
79
80     /** Construct a WebDAVMethod.
81     * @param request the servlet request
82     * @param response the servlet response
83     * @exception com.ibm.webdav.WebDAVException
84     */

85     public WebDAVMethod(HttpServletRequest request,
86                         HttpServletResponse response) throws WebDAVException {
87         this.request = request;
88         this.response = response;
89
90         // this isn't available from the HttpServletRequest directly, but should be
91
// the javax.servlet.http.HttpUtils.getRequestURL() doesn't strip off the query string,
92
// and looses the default port if it was specified.
93
URL requestURL = getRequestURL();
94
95         resource = new ResourceImpl(requestURL, request.getPathTranslated());
96
97         if ((resource.exists() && resource.isCollection()) ||
98                 request.getMethod().equals("MKCOL")) {
99             resource = new CollectionImpl(requestURL,
100                                           request.getPathTranslated(), null);
101         }
102     }
103
104     /**
105      * Copies input stream to an HTTP output stream.
106      * @param in the source stream from the NamespaceManager
107      * @param out the destination stream from the servlet response
108      * @param length the number of bytes to read from the steam
109      * @param mime the MIME type of the document to determine if its text or binary
110      */

111     protected void copy(InputStream in, HttpServletResponse response,
112                         int length, String JavaDoc mime) throws WebDAVException {
113         try {
114             int totalRead = 0;
115             int numRead = 0;
116             int numToRead = 0;
117             boolean isText = mime.regionMatches(0, "text", 0, 4);
118
119             if (!isText) { // copy binary
120

121                 ServletOutputStream out = (ServletOutputStream) response.getOutputStream();
122                 byte[] buf = new byte[4096];
123
124                 while (totalRead < length) {
125                     if ((length - totalRead) < buf.length) {
126                         numToRead = length - totalRead;
127                     } else {
128                         numToRead = buf.length;
129                     }
130
131                     numRead = in.read(buf, 0, numToRead);
132
133                     if (numRead == -1) {
134                         break;
135                     }
136
137                     totalRead += numRead;
138                     out.write(buf, 0, numRead);
139                 }
140             } else { // copy text using the client's preferred encoding scheme
141

142                 Reader isr = new InputStreamReader(in,
143                                                    Resource.defaultCharEncoding);
144
145                 ServletOutputStream out = (ServletOutputStream) response.getOutputStream();
146
147
148                 // Can't use response.getWriter() because JWS MIME filtering won't work
149
// It gives an IllegalStateException because it thinks you already got the
150
// output stream. This could be related to the AppServer bug that the response
151
// headers appear to be already written. Probably caused by a clone of the
152
// response for the filter.
153
//Writer out = response.getWriter();
154
/*PrintWriter writer = new PrintWriter(
155                                              new BufferedWriter(
156                                                      new OutputStreamWriter(out,
157                                                                             response.getCharacterEncoding()),
158                                                      1024), false);*/

159                 PrintWriter writer = new PrintWriter(
160                                              new BufferedWriter(
161                                                      new OutputStreamWriter(out,
162                                                                             Resource.defaultCharEncoding),
163                                                      1024), false);
164                 numToRead = 4096;
165
166                 char[] buf = new char[numToRead];
167
168                 while ((numRead = isr.read(buf, 0, numToRead)) != -1) {
169                     writer.write(buf, 0, numRead);
170                 }
171
172                 writer.flush();
173             }
174         } catch (WebDAVException exc) {
175             throw exc;
176         } catch (java.io.IOException JavaDoc exc) {
177             throw new WebDAVException(WebDAVStatus.SC_INTERNAL_SERVER_ERROR,
178                                       "IO Error");
179         }
180     }
181
182     /**
183      * Copies an HTTP input stream to an output stream.
184      * @param in the source stream from the servlet request
185      * @param out the destination stream from the NamespaceManager
186      * @param length the number of bytes to read from the steam
187      * @param mime the MIME type of the document to determine if its text or binary
188      */

189     protected void copy(HttpServletRequest request, OutputStream out,
190                         int length, String JavaDoc mime) throws WebDAVException {
191         try {
192             int totalRead = 0;
193             int numRead = 0;
194             int numToRead = 0;
195             boolean isText = (length > 0)
196                              ? mime.regionMatches(0, "text", 0, 4) : false;
197
198             if (!isText) { // copy binary
199

200                 ServletInputStream in = (ServletInputStream) request.getInputStream();
201                 byte[] buf = new byte[4096];
202
203                 while (totalRead < length) {
204                     if ((length - totalRead) < buf.length) {
205                         numToRead = length - totalRead;
206                     } else {
207                         numToRead = buf.length;
208                     }
209
210                     numRead = in.read(buf, 0, numToRead);
211
212                     if (numRead == -1) {
213                         break;
214                     }
215
216                     totalRead += numRead;
217                     out.write(buf, 0, numRead);
218                 }
219             } else { // copy text server's encoding scheme
220

221                 Reader isr = request.getReader();
222                 PrintWriter osw = new PrintWriter(
223                                           new OutputStreamWriter(out,
224                                                                  Resource.defaultCharEncoding),
225                                           false);
226                 char[] buf = new char[4096];
227
228                 while (totalRead < length) {
229                     if ((length - totalRead) < buf.length) {
230                         numToRead = length - totalRead;
231                     } else {
232                         numToRead = buf.length;
233                     }
234
235                     numRead = isr.read(buf, 0, numToRead);
236
237                     if (numRead == -1) {
238                         break;
239                     }
240
241                     totalRead += numRead;
242                     osw.write(buf, 0, numRead);
243                 }
244
245                 osw.flush();
246             }
247         } catch (WebDAVException exc) {
248             throw exc;
249         } catch (java.io.IOException JavaDoc exc) {
250             throw new WebDAVException(WebDAVStatus.SC_INTERNAL_SERVER_ERROR,
251                                       "IO Error");
252         }
253     }
254
255     /** Create a WebDAVMethod corresponding to the WebDAV or HTTP method in
256     * the request.
257     * @param request the servlet request
258     * @param response the servlet response
259     * @return a subclass of WebDAVMethod corresponding to the request method
260     * @exception com.ibm.webdav.WebDAVException
261     */

262     public static WebDAVMethod create(HttpServletRequest request,
263                                       HttpServletResponse response)
264                                throws WebDAVException {
265         String JavaDoc methodName = request.getMethod();
266         WebDAVMethod method = null;
267         
268         if(logger.isLoggable(Level.FINE)) {
269             logger.logp(Level.FINE, WebDAVMethod.class.getName(), "create", "Processing " + methodName + " request for " + request.getRequestURI());
270         }
271         
272         // create an instance of a ResourceImpl object that will eventually
273
// field the request. To support subclasses of Resource, create a
274
// a subclass of ResourceHTTPSkel and create an instance of the new
275
// impl subclass here.
276
if (methodName.equals("GET")) {
277             method = new GetMethod(request, response);
278         } else if (methodName.equals("HEAD")) {
279             method = new HeadMethod(request, response);
280         } else if (methodName.equals("PUT")) {
281             method = new PutMethod(request, response);
282         } else if (methodName.equals("DELETE")) {
283             method = new DeleteMethod(request, response);
284         } else if (methodName.equals("COPY")) {
285             method = new CopyMethod(request, response);
286         } else if (methodName.equals("MOVE")) {
287             method = new MoveMethod(request, response);
288         } else if (methodName.equals("PROPFIND")) {
289             method = new PropFindMethod(request, response);
290         } else if (methodName.equals("PROPPATCH")) {
291             method = new PropPatchMethod(request, response);
292         } else if (methodName.equals("MKCOL")) {
293             method = new MkcolMethod(request, response);
294         } else if (methodName.equals("LOCK")) {
295             method = new LockMethod(request, response);
296         } else if (methodName.equals("UNLOCK")) {
297             method = new UnlockMethod(request, response);
298         } else if (methodName.equals("OPTIONS")) {
299             method = new OptionsMethod(request, response);
300         } else if (methodName.equals("POST")) {
301             method = new PostMethod(request, response);
302         } else if (methodName.equals("SEARCH")) {
303             method = new SearchMethod(request, response);
304         } else if (methodName.equals("BIND")) {
305             method = new BindMethod(request, response);
306         } else if (methodName.equals(CheckInMethod.METHOD_NAME)) {
307             method = new CheckInMethod(request, response);
308         } else if (methodName.equals(CheckOutMethod.METHOD_NAME)) {
309             method = new CheckOutMethod(request, response);
310         } else if (methodName.equals(ReportMethod.METHOD_NAME)) {
311             method = new ReportMethod(request, response);
312         } else if (methodName.equals(VersionControlMethod.METHOD_NAME)) {
313             method = new VersionControlMethod(request, response);
314         } else if (methodName.equals(UncheckOutMethod.METHOD_NAME)) {
315             method = new UncheckOutMethod(request, response);
316         } else if (methodName.equals(OrderPatchMethod.METHOD_NAME)) {
317             method = new OrderPatchMethod(request, response);
318         }
319
320         method.getRequestHeaders();
321
322         return method;
323     }
324
325     /** Execute this method. Subclasses are expected to override this method to
326     * handle the request entity, do the work, update the context, return
327     * the response entity result if any, and set the status code.
328     *
329     * @return the WebDAV status code.
330     * @exception com.ibm.webdav.WebDAVException
331     */

332     public abstract WebDAVStatus execute() throws WebDAVException;
333
334     /** Get the request method name
335     *
336     */

337     public String JavaDoc getMethodName() {
338         return methodName;
339     }
340
341     /** Initialize the request context from the request headers. This provides
342     * additional parameters for the methods and perhaps other state from the
343     * client for the resource.
344     * @exception com.ibm.webdav.WebDAVException
345     */

346     public void getRequestHeaders() throws WebDAVException {
347         // set the Resource request context from the request headers
348
context = getResource().getContext();
349
350         Enumeration headerNames = request.getHeaderNames();
351
352         while (headerNames.hasMoreElements()) {
353             String JavaDoc name = ((String JavaDoc) headerNames.nextElement()).toLowerCase();
354             String JavaDoc value = request.getHeader(name);
355             context.getRequestContext().put(name, value);
356         }
357     }
358
359     /**
360     * Reconstructs the URL used by the client used to make the
361     * request. This accounts for differences such as addressing
362     * scheme (http, https), but does not attempt to
363     * include query parameters. The port is retained even if the
364     * default port was used.
365     *
366     * <P> This method is useful for creating redirect messages and for
367     * reporting errors.
368     */

369     public URL getRequestURL() {
370         StringBuffer JavaDoc url = new StringBuffer JavaDoc();
371
372         String JavaDoc scheme = request.getScheme();
373         int port = request.getServerPort();
374         String JavaDoc requestURI = request.getRequestURI();
375         int q = requestURI.indexOf('?');
376
377         if (q >= 0) {
378             requestURI = requestURI.substring(0, q); // supposed to be done by getRequestURI()!
379
}
380
381         url.append(scheme); // http, https
382
url.append("://");
383
384
385         // note, this is the IP address, not the server name. We have to be sure we normalize
386
// all host names to the IP address to be sure we can compare them in Preconditions
387
url.append(request.getServerName());
388
389         if (port != -1) {
390             url.append(':');
391             url.append(request.getServerPort());
392         }
393
394         url.append(requestURI);
395
396         URL result = null;
397
398         try {
399             result = new URL(url.toString());
400         } catch (Exception JavaDoc exc) {
401         }
402
403         return result;
404     }
405
406     /** Return the Resource the method operates on.
407     */

408     public ResourceImpl getResource() {
409         return resource;
410     }
411
412     /** Get the client prefered character encoding to be used to encode
413      * text responses. This implementation gets the charset from the Accept
414      * header. TODO: it should probably do something with Accept-Charset too.
415      *
416      * @return the MIME charset
417      */

418     public String JavaDoc getResponseCharset() {
419         String JavaDoc accept = null;
420         String JavaDoc charEncoding = null;
421
422         try {
423             accept = resource.getRequestContext().accept();
424         } catch (Exception JavaDoc exc) {
425         }
426
427         if (accept != null) {
428             charEncoding = ((ServletResponse) response).getCharacterEncoding();
429
430             /*if (charEncoding != null) {
431             charEncoding = MIME2Java.reverse(charEncoding);
432             }*/

433         }
434
435         if (charEncoding == null) {
436             charEncoding = Resource.defaultXMLEncoding;
437         }
438
439         return charEncoding;
440     }
441
442     /** Get the status code for this method.
443     *
444     * @return a status code as defined by HTTP/1.1 and WebDAV
445     */

446     public int getStatusCode() {
447         return context.getStatusCode().getStatusCode();
448     }
449
450     /** Set the response headers from the response context. This must be called
451     * after the remote method has been executed, and BEFORE any response entity
452     * is written.
453     * @exception RemoteException
454     */

455     public void setResponseHeaders() throws WebDAVException {
456         // set the response headers from the response context
457
ResourceContext context = resource.getContext();
458
459         // let the web server calculate the content length header
460
Enumeration propertyNames = context.getResponseContext().keys();
461
462         while (propertyNames.hasMoreElements()) {
463             String JavaDoc name = (String JavaDoc) propertyNames.nextElement();
464             String JavaDoc value = (String JavaDoc) context.getResponseContext().get(name);
465
466             if (!name.equals("content-length")) {
467                 if (name.equals("content-type")) {
468                     // must set Content-Type with this method or
469
// the response state will not be correct
470
response.setContentType(value);
471                 } else {
472                     response.setHeader(name, value);
473                 }
474             }
475         }
476     }
477
478     /** Set the status code for the method. This method must be called before
479     * any of the response entity is written.
480     *
481     * @param statusCode the status code to set as defined by HTTP/1.1
482     * and WebDAV.
483     *
484     */

485     public void setStatusCode(int statusCode) {
486         context.getStatusCode().setStatusCode(statusCode);
487
488
489         // set the returned status code and message
490
// TODO: bug in jsdk2.1, can't set the status message, this method was
491
// deprecated. The javaDoc also ways it sents an HTML response body,
492
// but I think the comment is just wrong.
493
response.setStatus(statusCode,
494                            WebDAVStatus.getStatusMessage(statusCode));
495     }
496 }
Popular Tags