KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > slide > webdav > method > UpdateMethod


1 /*
2  * $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/UpdateMethod.java,v 1.32 2004/08/02 16:36:01 unico Exp $
3  * $Revision: 1.32 $
4  * $Date: 2004/08/02 16:36:01 $
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 package org.apache.slide.webdav.method;
25
26 import java.io.IOException JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30
31 import org.apache.slide.common.NamespaceAccessToken;
32 import org.apache.slide.common.NestedSlideException;
33 import org.apache.slide.common.PropertyParseException;
34 import org.apache.slide.common.RequestedProperties;
35 import org.apache.slide.common.RequestedPropertiesImpl;
36 import org.apache.slide.common.ServiceAccessException;
37 import org.apache.slide.common.SlideException;
38 import org.apache.slide.content.NodeRevisionDescriptor;
39 import org.apache.slide.content.NodeRevisionDescriptors;
40 import org.apache.slide.content.RevisionDescriptorNotFoundException;
41 import org.apache.slide.event.EventDispatcher;
42 import org.apache.slide.structure.ObjectNode;
43 import org.apache.slide.webdav.WebdavException;
44 import org.apache.slide.webdav.WebdavServletConfig;
45 import org.apache.slide.webdav.event.WebdavEvent;
46 import org.apache.slide.webdav.util.DeltavConstants;
47 import org.apache.slide.webdav.util.LabeledRevisionNotFoundException;
48 import org.apache.slide.webdav.util.PreconditionViolationException;
49 import org.apache.slide.webdav.util.PropertyRetriever;
50 import org.apache.slide.webdav.util.PropertyRetrieverImpl;
51 import org.apache.slide.webdav.util.UriHandler;
52 import org.apache.slide.webdav.util.VersioningHelper;
53 import org.apache.slide.webdav.util.ViolatedPrecondition;
54 import org.apache.slide.webdav.util.WebdavStatus;
55 import org.apache.slide.webdav.util.WebdavUtils;
56 import org.apache.slide.webdav.util.resourcekind.AbstractResourceKind;
57 import org.apache.slide.webdav.util.resourcekind.CheckedInVersionControlled;
58 import org.apache.slide.webdav.util.resourcekind.ResourceKind;
59 import org.jdom.Document;
60 import org.jdom.Element;
61 import org.jdom.JDOMException;
62 import org.jdom.output.XMLOutputter;
63
64 /**
65  * UPDATE method.
66  *
67  */

68 public class UpdateMethod extends AbstractMultistatusResponseMethod
69     implements DeltavConstants, WriteMethod {
70
71     /** The update target */
72     private String JavaDoc resourcePath;
73
74     /** The update source */
75     private String JavaDoc updateSourcePath;
76
77     /** The label of the update source */
78     private String JavaDoc updateLabelName;
79
80     /** Requested properties */
81     private RequestedProperties requestedProps;
82
83     /**
84      * The VersioningHelper used by this instance.
85      */

86     protected VersioningHelper versioningHelper = null;
87
88     /**
89      * The URI of the server, e.g. <code>localhost:4000</code>.
90      */

91     protected String JavaDoc serverUri = null;
92
93     /**
94      * The PropertyRetriever used to retrieve any requested properties.
95      */

96     protected PropertyRetriever propertyRetriever = null;
97
98
99     // ----------------------------------------------------------- Constructors
100

101
102     /**
103      * Constructor.
104      *
105      * @param token the token for accessing the namespace
106      * @param config configuration of the WebDAV servlet
107      */

108     public UpdateMethod(NamespaceAccessToken token,
109                         WebdavServletConfig config) {
110         super(token, config);
111     }
112
113     /**
114      * Parse WebDAV XML query.
115      *
116      * @exception WebdavException
117      */

118     protected void parseRequest() throws WebdavException {
119
120         versioningHelper = VersioningHelper.getVersioningHelper(
121             slideToken, token, req, resp, getConfig() );
122 // readRequestContent();
123
serverUri = req.getServerName() + ":" + req.getServerPort();
124         propertyRetriever = new PropertyRetrieverImpl(token, slideToken, getConfig());
125
126         resourcePath = requestUri;
127         if (resourcePath == null) {
128             resourcePath = "/";
129         }
130
131         if( req.getContentLength() == 0 ) {
132             int statusCode = WebdavStatus.SC_BAD_REQUEST;
133             sendError( statusCode, getClass().getName()+".missingRequestBody" );
134             throw new WebdavException( statusCode );
135         }
136
137         try{
138             parseUpdateRequestContent();
139         }
140         catch (JDOMException e){
141             int statusCode = WebdavStatus.SC_BAD_REQUEST;
142             sendError( statusCode, e );
143             throw new WebdavException( statusCode );
144         }
145         catch (PropertyParseException e){
146             int statusCode = WebdavStatus.SC_BAD_REQUEST;
147             sendError( statusCode, e );
148             throw new WebdavException( statusCode );
149         }
150         catch( IOException JavaDoc e ){
151             int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR;
152             sendError( statusCode, e );
153             throw new WebdavException( statusCode );
154         }
155     }
156
157     /**
158      * Parses the expected request content specified for the Update method.
159      *
160      * @throws JDOMException if parsing the request failed for any reason.
161      * @throws IOException
162      * @throws PropertyParseException if parsing the property fails for any reason.
163      */

164     private void parseUpdateRequestContent() throws IOException JavaDoc, JDOMException, PropertyParseException {
165
166         Element ve = null;
167         Iterator JavaDoc i = parseRequestContent(E_UPDATE).getChildren().iterator();
168         while( i.hasNext() ) {
169             Element e = (Element)i.next();
170             if( e.getName().equals(E_VERSION) ) {
171                 if( updateSourcePath != null ) {
172                     throw new JDOMException("At most one &lt;"+E_VERSION+"&gt; element allowed" );
173                 }
174                 if (updateLabelName != null) {
175                     throw new JDOMException("Either a &lt;"+E_VERSION+"&gt; OR a &lt;"+E_LABEL_NAME+"&gt; element allowed");
176                 }
177                 // get the href element
178
ve = e;
179                 try {
180                     Element hre = (Element)ve.getChildren().get(0);
181                     if( hre == null || !hre.getName().equals(E_HREF) )
182                         throw new Exception JavaDoc();
183                     updateSourcePath = getSlidePath( hre.getText() );
184                 }
185                 catch( Exception JavaDoc x ) {
186                     throw new JDOMException("&lt;"+E_VERSION+"&gt; element must contain &lt;"+E_HREF+"&gt; element" );
187                 }
188             }
189             if( e.getName().equals(E_PROP) ) {
190                 if( requestedProps != null ) {
191                     throw new JDOMException("At most one "+E_PROP+" element allowed" );
192                 }
193                 requestedProps = new RequestedPropertiesImpl( e );
194             }
195             if( e.getName().equals(E_LABEL_NAME) ) {
196                 if (updateSourcePath != null) {
197                     throw new JDOMException("Either a &lt;"+E_VERSION+"&gt; OR a &lt;"+E_LABEL_NAME+"&gt; element allowed");
198                 }
199                 if( updateLabelName != null ) {
200                     throw new JDOMException("At most one &lt;"+E_LABEL_NAME+"&gt; element allowed" );
201                 }
202                 updateLabelName = e.getText();
203             }
204         }
205     }
206
207     /**
208      * Execute the request.
209      *
210      * @exception WebdavException
211      */

212     protected void executeRequest() throws WebdavException, IOException JavaDoc {
213
214         // Prevent dirty reads
215
slideToken.setForceStoreEnlistment(true);
216         
217         // check lock-null resources
218
try {
219             if (isLockNull(resourcePath)) {
220                 int statusCode = WebdavStatus.SC_NOT_FOUND;
221                 sendError( statusCode, "lock-null resource", new Object JavaDoc[]{resourcePath} );
222                 throw new WebdavException( statusCode );
223             }
224         }
225         catch (ServiceAccessException e) {
226             int statusCode = getErrorCode((Exception JavaDoc)e);
227             sendError( statusCode, e );
228             throw new WebdavException( statusCode );
229         }
230
231         Element multistatusElement = new Element(E_MULTISTATUS, DNSP);
232
233         try {
234             if ( WebdavEvent.UPDATE.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(WebdavEvent.UPDATE, new WebdavEvent(this));
235
236             update(updateSourcePath, updateLabelName, resourcePath, getDepth(), multistatusElement);
237         } catch (NestedSlideException nestedSlideException) {
238
239             if (!requestHeaders.isDefined(H_DEPTH)) {
240                 // do not send a 207 multistatus if the depth header is not set
241
SlideException exception = (SlideException)nestedSlideException.enumerateExceptions().nextElement();
242                 resp.setStatus(getErrorCode(exception)); // special handling needed
243
if (exception instanceof PreconditionViolationException) {
244                     try {
245                         sendPreconditionViolation((PreconditionViolationException)exception);
246                     } catch(IOException JavaDoc e) {
247                         // Critical error ... Servlet container is dead or something
248
int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR;
249                         sendError( statusCode, e );
250                         throw new WebdavException( statusCode );
251                     }
252                 }
253                 throw new WebdavException(getErrorCode(exception), false); // abort the TA
254
}
255         } catch (SlideException e) {
256             int statusCode = getErrorCode( e );
257             sendError( statusCode, e );
258             throw new WebdavException( statusCode );
259         }
260
261
262         try {
263             resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
264             resp.setContentType(TEXT_XML_UTF_8);
265             org.jdom.output.Format format = org.jdom.output.Format.getPrettyFormat();
266             format.setIndent(XML_RESPONSE_INDENT);
267             new XMLOutputter(format).
268                 output(new Document(multistatusElement), resp.getWriter());
269         }
270         catch (Exception JavaDoc e) {
271             int statusCode = getErrorCode( e );
272             sendError( statusCode, e );
273             throw new WebdavException( statusCode );
274         }
275     }
276
277     /**
278      * Updates the resource identified by <code>resourcePath</code>
279      * with the properties and the content either of the resource identified
280      * <code>updateSourcePath</code> or the version with the label
281      * <code>updateLabelName</code> (only one of these parameters is set).
282      * If <code>depth</code> is > 0, the operation is applied recursivly
283      * to all children of the destination resource.
284      *
285      * @param updateSourcePath the URI of update source.
286      * @param updateLabelName the label of the version used for the update.
287      * @param resourcePath the URI of update destination.
288      * @param depth the depth to use. If > 0, the update is
289      * applied recursivly.
290      * @param multistatusElement the <code>&lt;multistatus&gt;</code> element
291      * to append the <code>&lt;response&gt;</code>
292      * elements to.
293      */

294     protected void update(String JavaDoc updateSourcePath, String JavaDoc updateLabelName, String JavaDoc resourcePath, int depth, Element multistatusElement) throws NestedSlideException {
295         NestedSlideException nestedSlideException = new NestedSlideException(null);
296         update(updateSourcePath, updateLabelName, resourcePath, depth, multistatusElement, nestedSlideException);
297         if ( ! nestedSlideException.isEmpty() ) {
298             throw nestedSlideException;
299         }
300     }
301
302
303     /**
304      * Updates the resource identified by <code>resourcePath</code>
305      * with the properties and the content either of the resource identified
306      * <code>updateSourcePath</code> or the version with the label
307      * <code>updateLabelName</code> (only one of these parameters is set).
308      * If <code>depth</code> is > 0, the operation is applied recursivly
309      * to all children of the destination resource.
310      *
311      * @param updateSourcePath the URI of update source.
312      * @param updateLabelName the label of the version used for the update.
313      * @param resourcePath the URI of update destination.
314      * @param depth the depth to use. If > 0, the update is
315      * applied recursivly.
316      * @param multistatusElement the <code>&lt;multistatus&gt;</code> element
317      * to append the <code>&lt;response&gt;</code>
318      * elements to.
319      */

320     protected void update(String JavaDoc updateSourcePath, String JavaDoc updateLabelName, String JavaDoc resourcePath, int depth, Element multistatusElement, NestedSlideException nestedSlideException) {
321
322         Element responseElement = new Element(E_RESPONSE, DNSP);
323         multistatusElement.addContent(responseElement);
324         Element hrefElement = new Element(E_HREF, DNSP);
325         
326         String JavaDoc absUri = WebdavUtils.getAbsolutePath (resourcePath, req, getConfig());
327         hrefElement.setText(absUri);
328         responseElement.addContent(hrefElement);
329         
330         Element statusElement = new Element(E_STATUS, DNSP);
331         responseElement.addContent(statusElement);
332
333         Enumeration JavaDoc childrenEnum = null;
334         try {
335             if ( isCollection(resourcePath) && (depth > 0) ) {
336                 ObjectNode currentNode = structure.retrieve(slideToken, resourcePath);
337                 childrenEnum = structure.getChildren(slideToken, currentNode);
338             }
339
340             checkPreconditions(updateSourcePath, updateLabelName, resourcePath);
341
342             if (updateLabelName != null) {
343                 updateSourcePath = versioningHelper.getLabeledResourceUri(resourcePath,
344                                                                           updateLabelName);
345             }
346
347             versioningHelper.update( resourcePath, updateSourcePath );
348             statusElement.setText(HTTP_VERSION + " " +
349                                       WebdavStatus.SC_OK + " " +
350                                       WebdavStatus.getStatusText(WebdavStatus.SC_OK));
351
352             appendRequestedProps(resourcePath, responseElement);
353         }
354         catch (SlideException e) {
355             handleException(e, statusElement, responseElement, nestedSlideException);
356         }
357         catch (JDOMException e) {
358             handleException(e, statusElement, responseElement, nestedSlideException);
359         }
360         // process children recursivly
361
if (childrenEnum != null) {
362             while (childrenEnum.hasMoreElements()) {
363                 update(updateSourcePath,
364                        updateLabelName,
365                            ((ObjectNode)childrenEnum.nextElement()).getUri(),
366                        depth-1,
367                        multistatusElement,
368                        nestedSlideException);
369             }
370         }
371     }
372
373     /**
374      * Appends the &lt;propstat&gt; elements for the requested properties to
375      * the given <code>responseElement</code>.
376      *
377      * @param resourcePath the path of the resource for which to retrieve
378      * the properties.
379      * @param responseElement the &lt;reponse&gt; element to add the &lt;propstat&gt;
380      * elements to.
381      *
382      * @throws SlideException
383      * @throws JDOMException
384      */

385     private void appendRequestedProps(String JavaDoc resourcePath, Element responseElement) throws SlideException, JDOMException {
386
387         if (requestedProps != null) {
388             // TOCHECK serverURI ???
389
// List propStatList = propertyRetriever.getPropertiesOfObject(requestedProps,
390
// resourcePath,
391
// req.getContextPath(),
392
// serverUri,
393
// true);
394
List JavaDoc propStatList = propertyRetriever.getPropertiesOfObject(requestedProps,
395                     resourcePath,
396                     getSlideContextPath(),
397                     true);
398             Iterator JavaDoc iterator = propStatList.iterator();
399             while (iterator.hasNext()) {
400                 responseElement.addContent((Element)iterator.next());
401             }
402         }
403     }
404
405
406     /**
407      * Checks the preconditions of the Update method.
408      *
409      * @param updateSourcePath the URI of update source.
410      * @param updateLabelName the label of the version used for the update.
411      * @param resourcePath the URI of update destination.
412      *
413      * @throws SlideException
414      * @throws PreconditionViolationException if any precondition has been violated.
415      */

416     private void checkPreconditions(String JavaDoc updateSourcePath, String JavaDoc updateLabelName, String JavaDoc resourcePath) throws SlideException, PreconditionViolationException {
417
418         ViolatedPrecondition violatedPrecondition = getPreconditionViolation(updateSourcePath,
419                                                                              updateLabelName,
420                                                                              resourcePath);
421         if (violatedPrecondition != null) {
422             throw new PreconditionViolationException(violatedPrecondition, resourcePath);
423         }
424     }
425
426
427     /**
428      * Sets the appropriate status text and appends a &lt;responsedescription&gt;
429      * element if a precondition has been violated.
430      *
431      * @param exception the JDOMException that occurred.
432      * @param statusElement the &lt;status&gt; element.
433      * @param responseElement the &lt;response&gt; element.
434      * @param nestedSlideException the NestedSlideException to add the exception to.
435      */

436     private void handleException(JDOMException exception, Element statusElement, Element responseElement, NestedSlideException nestedSlideException) {
437         handleException(new SlideException("Nested exception: " + exception),
438                         statusElement,
439                         responseElement,
440                         nestedSlideException);
441     }
442
443     /**
444      * Sets the appropriate status text and appends a &lt;responsedescription&gt;
445      * element if a precondition has been violated.
446      *
447      * @param exception the SlideException that occurred.
448      * @param statusElement the &lt;status&gt; element.
449      * @param responseElement the &lt;response&gt; element.
450      * @param nestedSlideException the NestedSlideException to add the exception to.
451      */

452     private void handleException(SlideException exception, Element statusElement, Element responseElement, NestedSlideException nestedSlideException) {
453
454         nestedSlideException.addException(exception);
455
456         int errorCode = getErrorCode(exception);
457         statusElement.setText(HTTP_VERSION + " " + errorCode + " " +
458                                   WebdavStatus.getStatusText(errorCode));
459         if (exception instanceof PreconditionViolationException) {
460             Element responseDescriptionElement = new Element(E_RESPONSEDESCRIPTION,
461                                                              DNSP);
462             responseElement.addContent(responseDescriptionElement);
463             Element errorElement = MethodUtil.getPreconditionViolationError(((PreconditionViolationException)exception).getViolatedPrecondition());
464             responseDescriptionElement.addContent(errorElement);
465         }
466     }
467
468
469     /**
470      * Checks the (DeltaV) preconditions
471      * <ul>
472      * <li>&lt;DAV:must-be-checked-in-version-controlled-resource&gt;</li>
473      * <li>&lt;DAV:must-select-version-in-history&gt;</li>
474      * </ul>
475      *
476      * @param updateSourcePath the URI of update source.
477      * @param updateLabelName the label of the version used for the update.
478      * @param resourcePath the URI of update destination.
479      *
480      * @return the precondition that has been violated (if any).
481      *
482      * @throws SlideException
483      */

484     protected ViolatedPrecondition getPreconditionViolation(String JavaDoc updateSourcePath, String JavaDoc updateSourceLabel, String JavaDoc resourcePath) throws SlideException {
485
486         ViolatedPrecondition violatedPrecondition = null;
487
488         NodeRevisionDescriptors revisionDescriptors =
489             content.retrieve(slideToken, resourcePath);
490         NodeRevisionDescriptor revisionDescriptor =
491             content.retrieve(slideToken, revisionDescriptors);
492         ResourceKind resourceKind = AbstractResourceKind.determineResourceKind(token,
493                                                                                revisionDescriptors,
494                                                                                revisionDescriptor);
495         if ( ! (resourceKind instanceof CheckedInVersionControlled) ) {
496             return new ViolatedPrecondition(DeltavConstants.C_MUST_BE_CHECKED_IN_VERSION_CONTROLLED_RESOURCE,
497                                             WebdavStatus.SC_CONFLICT);
498         }
499
500         if (updateSourceLabel != null) {
501             try {
502                 updateSourcePath = versioningHelper.getLabeledResourceUri(resourcePath, updateLabelName);
503             }
504             catch (LabeledRevisionNotFoundException e) {
505                 return new ViolatedPrecondition(DeltavConstants.C_MUST_SELECT_VERSION_IN_HISTORY,
506                                                 WebdavStatus.SC_CONFLICT);
507             }
508         }
509
510         String JavaDoc associatedVrUri = versioningHelper.getUriOfAssociatedVR(resourcePath);
511         String JavaDoc vcrHistoryUri = UriHandler.getUriHandler(associatedVrUri).getAssociatedHistoryUri();
512         UriHandler vrUriHandler = UriHandler.getUriHandler(updateSourcePath);
513         boolean isVersionOfVcrHistory = false;
514         if (vrUriHandler.isVersionUri() &&
515             vcrHistoryUri.equals(vrUriHandler.getAssociatedHistoryUri()) ) {
516
517             NodeRevisionDescriptors vrDescriptors =
518                 content.retrieve(slideToken, updateSourcePath);
519             try {
520                 content.retrieve(slideToken, vrDescriptors);
521                 isVersionOfVcrHistory = true;
522             }
523             catch (RevisionDescriptorNotFoundException e) {
524             }
525         }
526
527         if ( ! isVersionOfVcrHistory ) {
528             return new ViolatedPrecondition(DeltavConstants.C_MUST_SELECT_VERSION_IN_HISTORY,
529                                             WebdavStatus.SC_CONFLICT);
530         }
531
532         return violatedPrecondition;
533     }
534
535
536     /**
537      * Returns the value of the <code>Depth</code> header. If not specified,
538      * <code>0</code> is used as default.
539      *
540      * @return the value of the <code>Depth</code> header.
541      */

542     protected int getDepth() throws WebdavException {
543         return requestHeaders.getDepth(0);
544     }
545
546
547 }
548
549
550
Popular Tags