KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * $Header: /home/cvs/jakarta-slide/webdavclient/clientlib/src/java/org/apache/webdav/lib/methods/LockMethod.java,v 1.6.2.2 2004/10/02 17:33:49 luetzkendorf Exp $
3  * $Revision: 1.6.2.2 $
4  * $Date: 2004/10/02 17:33:49 $
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.webdav.lib.methods;
25
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.StringWriter JavaDoc;
29 import javax.xml.parsers.DocumentBuilder JavaDoc;
30 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
31 import javax.xml.parsers.ParserConfigurationException JavaDoc;
32 import org.apache.commons.httpclient.HttpConnection;
33 import org.apache.commons.httpclient.HttpException;
34 import org.apache.commons.httpclient.HttpState;
35 import org.apache.commons.httpclient.HttpStatus;
36 import org.apache.commons.httpclient.util.URIUtil;
37
38 import org.apache.webdav.lib.WebdavState;
39 import org.apache.webdav.lib.properties.LockEntryProperty;
40 import org.apache.webdav.lib.util.DOMUtils;
41 import org.apache.webdav.lib.util.DOMWriter;
42 import org.w3c.dom.DOMException JavaDoc;
43 import org.w3c.dom.Document JavaDoc;
44 import org.w3c.dom.Element JavaDoc;
45 import org.w3c.dom.NodeList JavaDoc;
46 import org.w3c.dom.Text JavaDoc;
47
48 /**
49  * Web resources can be locked to ensure that only one user is updating
50  * the resource at a time. Locking helps to prevent the "lost update" problem.
51  * There are two types of lock currently defined by the WebDAV specification:
52  * exclusive locks and shared locks.
53  *
54  * <p> Per the specification, a lock indicates that someone is updating the
55  * resource, (hence the lock is a "write lock"), although the specification
56  * notes that the the syntax is extensible, and permits the eventual creation
57  * of locking for other access types.
58  *
59  * <h3>Shared and Exclusive Locks</h3>
60  *
61  * <p> The most basic form of lock is an <em>exclusive lock</em>. This is a
62  * lock where the access right in question is only granted to a single client.
63  * The need for this arbitration results from a desire to avoid having to merge
64  * results. However, there are times when the goal of a lock is not to exclude
65  * others from exercising an access right but rather to provide a mechanism for
66  * principals to indicate that they intend to exercise their access rights.
67  * <em>Shared locks</em> are provided for this case. A shared lock allows
68  * multiple clients to receive a lock. Hence any user with appropriate
69  * access can get the lock.
70  *
71  * <p> With shared locks there are two trust sets that affect a resource.
72  * The first trust set is created by access permissions. Principals who are
73  * trusted, for example, may have permission to write to the resource. Among
74  * those who have access permission to write to the resource, the set of
75  * principals who have taken out a shared lock also must trust each other,
76  * creating a (typically) smaller trust set within the access permission write
77  * set.
78  *
79  * <h3>Lock Compatibility</h3>
80  *
81  * <p> The following table indicates what happens if a new lock request
82  * is sent to a resource that is already locked: </p>
83  *
84  * <table border="1">
85  * <tr><th> </th><th colspan="2"> Lock Request </th></tr>
86  * <tr><th>Current Lock </th><th>Exclusive Lock </th><th>Shared Lock </th></tr>
87  * <tr><td>None </td><td>Success </td><td>Sucess </td></tr>
88  * <tr><td>Shared </td><td>Failure </td><td>Sucess </td></tr>
89  * <tr><td>Exclusive </td><td>Failure </td><td>Failure </td></tr>
90  * </table>
91  *
92  */

93 public class LockMethod
94     extends XMLResponseMethodBase implements DepthSupport {
95
96
97     // -------------------------------------------------------------- Constants
98

99
100     public static final short SCOPE_EXCLUSIVE =
101         LockEntryProperty.SCOPE_EXCLUSIVE;
102     public static final short SCOPE_SHARED = LockEntryProperty.SCOPE_SHARED;
103
104     public static final short TYPE_WRITE = LockEntryProperty.TYPE_WRITE;
105
106     // The timeout value for TimeType "Second" MUST NOT be greater than 2^32-1.
107
public static final int TIMEOUT_INFINITY = Integer.MAX_VALUE;
108
109
110     // ----------------------------------------------------- Instance Variables
111

112
113     /**
114      * The scope of lock we're requesting. The default scope is
115      * SCOPE_EXCLUSIVE.
116      */

117     private short scope = SCOPE_EXCLUSIVE;
118
119     /**
120      * Depth.
121      */

122     private int depth = DEPTH_INFINITY;
123
124
125     /**
126      * Opaque token of the lock we're trying to refresh.
127      */

128     private String JavaDoc refreshOpaqueToken = null;
129
130
131     /**
132      * Lock timeout.
133      */

134     private int timeout = TIMEOUT_INFINITY;
135
136
137     /**
138      * Lock owner.
139      */

140     private String JavaDoc owner = null;
141
142
143     /**
144      * Lock token.
145      */

146     private String JavaDoc lockToken = null;
147     
148     private boolean typeTransaction = false;
149
150
151     // ----------------------------------------------------------- Constructors
152

153
154     /**
155      * Creates a lock method that can <em>start a transaction</em> when server supports
156      * them in a
157      * <a HREF="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wss/wss/_webdav_lock.asp">MS like style</a>.
158      * The transacion handle of the started transaction will be returned as the lock token.
159      * To let subsequent requests participate in the transaction add a
160      * <code>Transaction</code> header with the lock token as value. You will have to enclose it in '&lt;' and '>' just
161      * like ordinary lock tokens.
162      * <br><br>
163      * To either commit or abort the transaction
164      * use {@link UnlockMethod}.
165      *
166      * @param path any path inside Slide's scope
167      * @param owner of this transaction
168      * @param timeout timeout of this transaction
169      * @param isTransaction <code>true</code> when this method is used to starte a transaction
170      *
171      */

172     public LockMethod(String JavaDoc path, String JavaDoc owner, int timeout, boolean isTransaction) {
173         this(path);
174         setOwner(owner);
175         setTimeout(timeout);
176         setTypeTransaction(isTransaction);
177     }
178     
179     /**
180      * Method constructor.
181      */

182     public LockMethod() {
183     }
184
185
186     /**
187      * Method constructor.
188      */

189     public LockMethod(String JavaDoc path) {
190         super(path);
191     }
192
193
194     /**
195      * Method constructor.
196      */

197     public LockMethod(String JavaDoc path, String JavaDoc refreshOpaqueToken, int timeout) {
198         this(path);
199         this.refreshOpaqueToken = refreshOpaqueToken;
200         setTimeout(timeout);
201     }
202
203
204     /**
205      * Method constructor.
206      */

207     public LockMethod(String JavaDoc path, String JavaDoc owner, short scope, int timeout) {
208         this(path);
209         setOwner(owner);
210         setScope(scope);
211         setTimeout(timeout);
212     }
213
214     /**
215      * Method constructor.
216      * @deprecated The timeout value MUST NOT be greater than 2^32-1.
217      */

218     public LockMethod(String JavaDoc path, String JavaDoc refreshOpaqueToken, long timeout) {
219         this(path, refreshOpaqueToken, (int) timeout);
220     }
221
222
223     /**
224      * Method constructor.
225      * @deprecated The timeout value MUST NOT be greater than 2^32-1.
226      */

227     public LockMethod(String JavaDoc path, String JavaDoc owner, short scope, long timeout) {
228         this(path, owner, scope, (int) timeout);
229     }
230
231     // ------------------------------------------------------------- Properties
232

233
234
235
236     /**
237      * Set a header value, redirecting the special cases of Depth and Time headers
238      * to {@link #setDepth} and {@link #setTimeout} as appropriate.
239      *
240      * @param headerName Header name
241      * @param headerValue Header value
242      */

243     public void setRequestHeader(String JavaDoc headerName, String JavaDoc headerValue) {
244         if (headerName.equalsIgnoreCase("Depth")){
245             int depth = -1;
246             if (headerValue.equals("0")){
247                 depth = DEPTH_0;
248             }
249             if (headerValue.equals("1")){
250                 depth = DEPTH_1;
251             }
252             else if (headerValue.equalsIgnoreCase("infinity")){
253                 depth = DEPTH_INFINITY;
254             }
255             setDepth(depth);
256         }
257         else if(headerName.equalsIgnoreCase("Timeout")){
258             if (headerValue.startsWith("Second-"))
259                 headerValue = headerValue.substring("Second-".length());
260             try {
261                 setTimeout(Integer.parseInt(headerValue));
262             } catch (NumberFormatException JavaDoc e) {
263             }
264         }
265         else if(headerName.equalsIgnoreCase("Owner")){
266             setOwner(headerValue);
267         }
268         else{
269             super.setRequestHeader(headerName, headerValue);
270         }
271     }
272
273     public boolean isTypeTransaction() {
274         return typeTransaction;
275     }
276
277     public void setTypeTransaction(boolean typeTransaction) {
278         this.typeTransaction = typeTransaction;
279     }
280
281     /**
282      * Depth setter.
283      *
284      * @param depth New depth value
285      */

286     public void setDepth(int depth) {
287         checkNotUsed();
288         if (depth != DEPTH_0 && depth != DEPTH_INFINITY) {
289             throw new IllegalArgumentException JavaDoc
290             ("invalid depth value for lock method " + depth);
291         }
292         this.depth = depth;
293     }
294
295
296     /**
297      * Depth getter.
298      *
299      * @return int depth value
300      */

301     public int getDepth() {
302         return depth;
303     }
304
305
306     public String JavaDoc getLockToken() {
307         checkUsed();
308         return this.lockToken;
309     }
310
311
312
313     public boolean isRefresh() {
314         return !((this.refreshOpaqueToken == null ) ||
315                  (this.refreshOpaqueToken.equals("")));
316     }
317
318
319     public short getScope() {
320         return this.scope;
321     }
322
323
324     /**
325      * Sets the owner of the lock. This method provides only "basic" owner
326      * information. Thus, <code>setOwner("Jezebel Lipshitz")</code> will
327      * produce an <code>owner</code> element in the request document like this:
328      *
329      * <pre>
330      * &lt;D:owner&gt;Jezebel Lipshitz&lt;/D:owner&gt;
331      * </pre>
332      *
333      * <p> Examples in the Webdav specification suggest that one can use
334      * e-mail addresses, home page URLs, or other information; this
335      * implementation doesn't handle any of that.
336      */

337     public void setOwner(String JavaDoc owner) {
338         checkNotUsed();
339         this.owner = owner;
340     }
341
342     /** Return the owner of the lock as reported by the server. */
343     public String JavaDoc getOwner() {
344         return owner;
345     }
346
347
348     public void setScope(short scope) {
349         checkNotUsed();
350         if (scope != SCOPE_SHARED && scope != SCOPE_EXCLUSIVE) {
351             throw new IllegalArgumentException JavaDoc("invalid scope value");
352         }
353         this.scope = scope;
354     }
355
356
357     /**
358      * get the timeout value.
359      *
360      * @return timeout
361      */

362     public int getTimeout() {
363         return this.timeout;
364     }
365
366
367     /**
368      * Set the timeout value.
369      */

370     public void setTimeout(int timeout) {
371         checkNotUsed();
372         if (timeout < 0) {
373             throw new IllegalArgumentException JavaDoc("invalid timeout value: " +
374                                                    timeout);
375         }
376         this.timeout = timeout;
377     }
378
379     /**
380      * Set the timeout value.
381      * @deprecated The timeout value MUST NOT be greater than 2^32-1.
382      */

383     public void setTimeout(long timeout) {
384         setTimeout((int) timeout);
385     }
386
387
388     // --------------------------------------------------- WebdavMethod Methods
389

390     public String JavaDoc getName() {
391         return "LOCK";
392     }
393
394     public void recycle() {
395         super.recycle();
396         this.refreshOpaqueToken = null;
397         this.depth = DEPTH_INFINITY;
398         this.scope = SCOPE_EXCLUSIVE;
399         this.timeout = TIMEOUT_INFINITY;
400         this.typeTransaction = false;
401     }
402
403
404     /**
405      * Generate additional headers needed by the request.
406      *
407      * @param state State token
408      * @param conn The connection being used for the request.
409      */

410     public void addRequestHeaders(HttpState state, HttpConnection conn)
411     throws IOException JavaDoc, HttpException {
412
413         // set the default utf-8 encoding, if not already present
414
if (getRequestHeader("Content-Type") == null ) super.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
415         super.addRequestHeaders(state, conn);
416
417         switch (depth) {
418             case DEPTH_0:
419                 super.setRequestHeader("Depth", "0");
420                 break;
421             case DEPTH_INFINITY:
422                 super.setRequestHeader("Depth", "infinity");
423                 break;
424             default:
425         }
426
427         if (timeout == TIMEOUT_INFINITY) {
428             super.setRequestHeader("Timeout", "Infinite, Second-" + TIMEOUT_INFINITY);
429         } else {
430             super.setRequestHeader("Timeout", "Second-" + timeout);
431         }
432
433         if (isRefresh()) {
434             super.setRequestHeader("If", "(<" + refreshOpaqueToken + ">)");
435         }
436
437     }
438
439     /**
440      * DAV requests that contain a body must override this function to
441      * generate that body.
442      *
443      * <p>The default behavior simply returns an empty body.</p>
444      */

445     protected String JavaDoc generateRequestBody() {
446
447         String JavaDoc result = "";
448
449         if (!isRefresh()) {
450
451             if (this.owner == null || this.owner.equals("")) {
452                 throw new IllegalStateException JavaDoc
453                     ("The owner property has not been set");
454             }
455
456             try {
457
458                 DocumentBuilderFactory JavaDoc factory =
459                     DocumentBuilderFactory.newInstance();
460                 DocumentBuilder JavaDoc builder = factory.newDocumentBuilder();
461                 Document JavaDoc document = builder.newDocument();
462
463                 Element JavaDoc lockinfo = document.createElement("DAV:lockinfo");
464                 document.appendChild(lockinfo);
465                 lockinfo.setAttribute("xmlns:DAV", "DAV:");
466
467                 Element JavaDoc lockscope = document.createElement("DAV:lockscope");
468                 lockinfo.appendChild(lockscope);
469
470                 if (this.scope == SCOPE_EXCLUSIVE) {
471                     Element JavaDoc exclusive =
472                         document.createElement("DAV:exclusive");
473                     lockscope.appendChild(exclusive);
474                 } else {
475                     Element JavaDoc shared = document.createElement("DAV:shared");
476                     lockscope.appendChild(shared);
477                 }
478
479                 Element JavaDoc locktype = document.createElement("DAV:locktype");
480                 lockinfo.appendChild(locktype);
481
482                 if (typeTransaction) {
483                     Element JavaDoc transaction = document.createElement("DAV:transaction");
484                     locktype.appendChild(transaction);
485                 } else {
486                     Element JavaDoc write = document.createElement("DAV:write");
487                     locktype.appendChild(write);
488                 }
489
490                 Element JavaDoc owner = document.createElement("DAV:owner");
491                 lockinfo.appendChild(owner);
492
493                 Text JavaDoc text = document.createTextNode(this.owner);
494                 owner.appendChild(text);
495
496                 StringWriter JavaDoc stringWriter = new StringWriter JavaDoc();
497                 DOMWriter domWriter = new DOMWriter(stringWriter, false);
498                 domWriter.print(document);
499
500                 result = stringWriter.getBuffer().toString();
501
502             } catch (DOMException JavaDoc e) {
503             } catch (ParserConfigurationException JavaDoc e) {
504             }
505
506         }
507
508         return result;
509     }
510
511     /**
512      * Parse response.
513      *
514      * @param input Input stream
515      */

516     public void parseResponse(InputStream JavaDoc input, HttpState state, HttpConnection conn)
517         throws IOException JavaDoc, HttpException {
518         int status = getStatusLine().getStatusCode();
519         if (status == HttpStatus.SC_OK ||
520             status == HttpStatus.SC_CREATED ||
521             status == HttpStatus.SC_MULTI_STATUS ) {
522
523             parseXMLResponse(input);
524
525             NodeList JavaDoc list = getResponseDocument().getDocumentElement()
526                   .getElementsByTagNameNS("DAV:", "locktoken");
527
528             if (list.getLength() == 1) {
529                 Element JavaDoc locktoken = (Element JavaDoc) list.item(0);
530                 NodeList JavaDoc list2 = locktoken.getElementsByTagNameNS("DAV:", "href");
531                 if (list2.getLength() == 1) {
532                     this.lockToken = DOMUtils.getTextValue(list2.item(0));
533                     if (state instanceof WebdavState) {
534                        /*
535                         * lockMethod/unlockMethod take unescaped URIs but LockMathod.getPath()
536                         * func returns an escaped URI so searching for the lock by path name in
537                         * the state object doesn't work. Convert escaped back to unescaped.
538                         */

539                         ((WebdavState) state).addLock(URIUtil.decode(getPath()),
540                                 this.lockToken);
541                     }
542                 }
543             }
544
545             list = getResponseDocument().getDocumentElement()
546                   .getElementsByTagNameNS("DAV:", "owner");
547
548             if (list.getLength() == 1) {
549                 Element JavaDoc owner = (Element JavaDoc)list.item(0);
550
551                 this.owner = DOMUtils.getTextValue(owner);
552             }
553         }
554     }
555 }
556
557
Popular Tags