001: /**********************************************************************************
002: * $URL: $
003: * $Id: $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2006, 2007 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.content.api;
021:
022: import java.io.InputStream;
023: import java.io.OutputStream;
024:
025: import javax.servlet.http.HttpServletResponse;
026:
027: import org.sakaiproject.entity.api.ResourceProperties;
028: import org.sakaiproject.exception.IdInvalidException;
029: import org.sakaiproject.exception.IdLengthException;
030: import org.sakaiproject.exception.IdUnusedException;
031: import org.sakaiproject.exception.IdUsedException;
032: import org.sakaiproject.exception.InUseException;
033: import org.sakaiproject.exception.InconsistentException;
034: import org.sakaiproject.exception.OverQuotaException;
035: import org.sakaiproject.exception.PermissionException;
036: import org.sakaiproject.exception.ServerOverloadException;
037: import org.sakaiproject.exception.TypeException;
038:
039: /**
040: *
041: *
042: *
043: */
044: public interface DavManager {
045: /** Status code (201) indicating the request succeeded and created a new resource on the server. */
046: public final static Integer STATUS_CREATED = new Integer(
047: HttpServletResponse.SC_CREATED);
048:
049: /** Status reporting an operation succeeded without creation of a new resource */
050: public final static Integer STATUS_NO_CONTENT = new Integer(
051: HttpServletResponse.SC_NO_CONTENT);
052:
053: /** Status code (401) indicating that the request requires HTTP authentication. */
054: public final static Integer STATUS_UNAUTHORIZED = new Integer(
055: HttpServletResponse.SC_UNAUTHORIZED);
056:
057: /** Status code (403) indicating the server understood the request but refused to fulfill it. */
058: public final static Integer STATUS_FORBIDDEN = new Integer(
059: HttpServletResponse.SC_FORBIDDEN);
060:
061: /** Status code (409) indicating requested action cannot be executed on an existing resource. */
062: public final static Integer STATUS_METHOD_NOT_ALLOWED = new Integer(
063: HttpServletResponse.SC_METHOD_NOT_ALLOWED);
064:
065: /** Status code (409) indicating a resource cannot be created at the destination until one or more intermediate collections have been created. */
066: public final static Integer STATUS_CONFLICT = new Integer(
067: HttpServletResponse.SC_CONFLICT);
068:
069: /*
070: *
071: 412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element or the Overwrite header is "F" and the state of the destination resource is non-null.
072: 412 (Precondition Failed) - The server was unable to maintain the liveness of the properties listed in the propertybehavior XML element or the Overwrite header is "F" and the state of the destination resource is non-null.
073:
074: 415 (Unsupported Media Type)- The server does not support the request type of the body. (MKCOL)
075:
076: 423 (Locked) - The destination resource was locked.
077: 423 (Locked) - The source or the destination resource was locked.
078:
079: 502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
080: 502 (Bad Gateway) - This may occur when the destination is on another server and the destination server refuses to accept the resource.
081:
082: 507 (Insufficient Storage) - The destination resource does not have sufficient space to record the state of the resource after the execution of this method.
083: 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method.
084:
085: */
086:
087: /*
088: * TODO: Locks
089: * General question on locks:
090: * DAV has a locking scheme. Currently it is implemented entirely within DavServlet.
091: * Since Sakai also has locking, we end up with two locking systems that
092: * don't talk. If we do things like copying collections in Content, it also
093: * makes it nearly impossible to do what the protocol suggests and copy
094: * all but the locked items, returning a list of what was and was not
095: * copied. At the moment we simply give an error for the whole operation.
096: * I believe that is legal. I am also skeptical about the ability of clients
097: * to cope with partial success. However to produce a maximally compliant DAV,
098: * we would probably need to move DAV locking into Content.
099: * (hedrick at rutgers dot edu)
100: */
101:
102: /*
103: * General comment on error handling:
104: * DAV has the possibility to return an XML structure listing items and error codes.
105: * If an operation on a collection fails for some items in the collection, the spec requires
106: * us to return a list of all items indicating where it succeeded and where it failed.
107: * Operations here must either succeed in their entirety, fail and back out of all
108: * changes, or return a list of items and status. It would clearly be easier not to
109: * return such a list. If that is the intent, care must be taken to avoid partial
110: * failure. E.g. in copying collections where an existing one will be deleted, it
111: * would be prudent to copy to a new name, verify that the copy worked, and then
112: * delete the old one and rename the new one.
113: * (hedrick at rutgers dot edu)
114: */
115:
116: /*
117: * General comment on return values:
118: * These operations all return boolean. The reason is that DAV has two different
119: * success codes: 201 Created and 204 No content. We must distinguish between
120: * cases where new content was created and where it was not. E.g. the spec for
121: * copy says that 201 is returned if a new resource is created, while
122: * 204 is returned if the resource existed. Operations are expected to
123: * return true for Created and false for No Content. Some operations will
124: * always return the same value. This is noted in the descriptions.
125: * At the HTTP level, 201 returns a URI for the new resources. This allows for
126: * the possibility that we could use a different name than the requested one.
127: * If we wanted to do that, then these functions would return null or a
128: * String. However because clients will typically use these functions to
129: * model a Unix file system, I strongly recommend against doing this.
130: * (hedrick at rutgers dot edu)
131: *
132: * Maybe the return value should be a List of id's indicating entities for which
133: * the operation succeeded/failed? Or a Map (hashtable) with keys for each
134: * item involved in the operation and values indicating whether the operation
135: * succeeded or failed?
136: * (jimeng at umich dot edu)
137: *
138: * Sure, if you want to do that. it would map nicely into what DAV is supposed to
139: * return. Note that the DAV protocol requires minimization of exceptions. So if
140: * a whole tree fails in the same way, you mention only the top of the tree. While
141: * it isn't clear in the text, it appears that if there is partial success, you
142: * return a multistatus XML structure that lists only the things that failed. And
143: * for them you list only the top of any tree for which all elements failed with
144: * the same error code.
145: *
146: * As I read it, most operations return either 201 or 204. I believe the only ones
147: * where partial success is possible are ones where a whole tree is manipulated at
148: * once.
149: *
150: * Copy certainly has this issue, since one could run into a lock or a quota problem
151: * during the recursive copy.
152: *
153: * Delete can succeed partially if elements are locked. Whether other types of
154: * failure are possible depends upon the implementation. I'm guessing that a failure
155: * to delete an item other than because of a lock would indicate a serious problem
156: * with the db or file system.
157: *
158: * Move can have partial success due to system failures. Is anything else possible?
159: * I'm not so sure. If overwrite is on, the implied delete could partially fail,
160: * but in that case the move can't occur at all, so optimally you'd not do the delete
161: * and fail the whole operation. In theory if you're copying to a different file
162: * system you could get a quota problem, but in Sakai I suspect quotas are on a
163: * sakai-wide basis so that wouldn't happen.
164: *
165: * Lock can have partial success, but I'm not currently dealing with the details of
166: * locking unless you tell me you're sure you can implement DAV compatibility.
167: * Otherwise I'd rather maintain the locks myself, and checking for locks before
168: * calling you.
169: * (hedrick at rutgers dot edu)
170: */
171:
172: /**
173: * Copies a resource or collection along with all attributes and properties.
174: * If the parameter overwrite is true, delete existing entity (or entities).
175: * Otherwise update them. For collections, preexisting items from newId that
176: * do not have corresponding items in oldId will remain.
177: * If the parameter oldId identifies a collection and the parameter recursive
178: * is true, copy the entire hierarchy of collections and resources rooted at
179: * oldId. If the parameter oldId identifies a collection and the parameter
180: * recursive is false, copy only the entity oldId. The copy succeeds unless
181: * one of these errors occurs:
182: * <ul>
183: * <li>no entity exists with the identifier oldId</li>
184: * <li>the user does not have permission to access the entity oldId</li>
185: * <li>the user does not have permission to create the entity newId</li>
186: * <li>the entity newId exists, and the user does not have permission to modify/delete oldId</li>
187: * <li>the entity newId exists, and the entity newId (or one of its members) is in use by another user</li>
188: * <li>overwrite is false and one of newId, oldId is a resource and the other a collection</li>
189: * <li>the containing collection for newId does not exist</li>
190: * <li>newId is too long to be used as a resource-id or collection-id</li>
191: * <li>the server is configured to write the resource body to the filesystem and an attempt to save the resource body fails</li>
192: * <li>the source and destination objects are the same
193: * <li>writing the new resource puts the user over quota
194: * </ul>
195: * If overwrite is set, the nature of any existing destination does not matter. It will be deleted.
196: * Return: true if a new collection or resource was created, i.e. newId did not exist previously
197: *
198: * @param oldId
199: * @param newId
200: * @param overwrite
201: * @param recursive
202: *
203: * @return true if a new collection or resource was created, i.e. newId did not exist previously
204: *
205: * @throws PermissionException
206: * if the user does not have permission to access the old entity, delete
207: * the existing entity or create the new entity.
208: * @throws IdUnusedException
209: * if oldIdd does not exist.
210: * @throws InconsistentException
211: * if the containing collection (for newId) does not exist.
212: * @throws TypeException
213: * if overwrite is false, and
214: * newId and oldId do not identify the same kind of entities
215: * (must both be identifiers for collections OR must both
216: * be identifiers for collections)
217: * @throws IdLengthException
218: * if the newId is too long.
219: * @throws InUseException
220: * if the entity specified by oldId is currently in use
221: * by another user or, if overwrite is true, the entity
222: * identified by newId exists and is in use by another user.
223: * @throws ServerOverloadException
224: * if the server is configured to write the resource body to the
225: * filesystem and an attempt to save the resource body fails.
226: * @throws OverQuotaException
227: * if writing the new resource puts the user over quota
228: */
229: public boolean copy(String oldId, String newId, boolean overwrite,
230: boolean recursive) throws PermissionException,
231: InconsistentException, IdLengthException, InUseException,
232: ServerOverloadException;
233:
234: /**
235: *
236: * Make a new collection.
237: * The new collection is empty.
238: * The operation succeeds unless one of the following occurs:
239: * <ul>
240: * <li>the user does not have permission to create the entity newId</li>
241: * <li>an entity newId exists
242: * <li>newId is too long to be used as a resource-id or collection-id</li>
243: * <li>the server is configured to write the resource body to the filesystem and an attempt to save the resource body fails</li>
244: * <li>writing the new collection puts the user over quota
245: * </ul>
246: * If successful, always returns true, because a new collection is always created
247: *
248: * @param newId
249: *
250: * @return true if a new collection is created (which is always true unless an exception is thrown).
251: *
252: * @throws PermissionException
253: * if the user does not have permission to add this entity.
254: * @throws IdUsedException
255: * if the id is already in use.
256: * @throws InconsistentException
257: * if the containing collection does not exist.
258: * @throws IdLengthException
259: * if the newId is too long.
260: * @throws IdInvalidException
261: * if the resource id is invalid.
262: * @throws ServerOverloadException
263: * if the server is configured to write the collection to the
264: * filesystem and it fails
265: * @throws OverQuotaException
266: * if writing the new collection puts the user over quota
267: */
268: public boolean createCollection(String newId)
269: throws PermissionException, IdUsedException,
270: InconsistentException, IdLengthException,
271: IdInvalidException;
272:
273: /**
274: * Create the resource or update it if it exists.
275: * if newid exists and is resource, update its contents, retaining properities;
276: * however delete followed by create is OK as long as it preserves the old
277: * properties.
278: * If newid did not exist, use specified content type, if any.
279: * If contenttype is null, look at the extension. If no extension or unknown, use text/plain
280: * The operation succeeds unless one of the following is true:
281: * <ul>
282: * <li>newid exists and is a collection
283: * <li>user does not have permission to create or write the new object
284: * <li>the containing collection does not exist
285: * <li>newId is too long to be used as a resource-id or collection-id</li>
286: * <li>the server is configured to write the resource body to the filesystem and an attempt to save the resource body fails</li>
287: * <li>writing the new resource puts the user over quota
288: * </ul>
289: * Returns true if a new resource is created, i.e. if newId did not exist
290: *
291: * @param newId
292: * @param content
293: * @param contenttype
294: *
295: * @throws PermissionException
296: * if the user does not have permission to create or write to this entity.
297: * @throws TypeException
298: * if the id is already in use and identifies a collection.
299: * TODO: Is this the right exception in that case?
300: * [I don't care what exception you use, as long as it is documented.]
301: * @throws InconsistentException
302: * if the containing collection does not exist.
303: * @throws IdLengthException
304: * if the newId is too long.
305: * @throws InUseException
306: * if the entity exists and is currently in use by another user.
307: * @throws OverQuotaException
308: * if the entity cannot be added without exceeding the quota.
309: * @throws ServerOverloadException
310: * if the server is configured to write the resource body to the
311: * filesystem and an attempt to save the resource body fails.
312: * @throws OverQuotaException
313: * if writing the new collection puts the user over quota
314: */
315: public boolean createResource(String newId, InputStream content,
316: String contentType) throws PermissionException,
317: TypeException, InconsistentException, IdLengthException,
318: InUseException, OverQuotaException, ServerOverloadException;
319:
320: /**
321: * Delete a collection or resource. If entity is a non-empty collection,
322: * remove it and everything it contains.
323: * Fails if
324: * <ul>
325: * <li>no entity exists with the identifier entityId</li>
326: * <li>the user does not have permission to delete the entity</li>
327: * <li>the server is configured to write the resource body to the filesystem and an attempt to delete it fails</li>
328: * </ul>
329: * Always returns false.
330: *
331: * @param entityId
332: * @throws PermissionException
333: * if the user does not have permission to delete this entity.
334: * @throws InUseException
335: * if the entity (or one of its members) is currently in use
336: * by another user.
337: * @throws IdUnusedException
338: * if entityId does not exist.
339: * @throws ServerOverloadException
340: * if the server is configured to write the resource body to the
341: * filesystem and an attempt to delete it fails.
342: */
343: public boolean delete(String entityId) throws PermissionException,
344: IdUnusedException, InUseException;
345:
346: /**
347: *
348: * @param entityId
349: *
350: * @return
351: *
352: * @throws PermissionException
353: * if the user does not have permission to access this entity.
354: * @throws IdUnusedException
355: * if the entity specified by the id does not exist.
356: */
357: public ResourceProperties getProperties(String entityId)
358: throws PermissionException, IdUnusedException;
359:
360: /**
361: *
362: * @param entityId
363: *
364: * @return
365: *
366: * @throws PermissionException
367: * if the user does not have permission to access this entity.
368: * @throws IdUnusedException
369: * if the entity specified by the id does not exist.
370: * @throws TypeException
371: * if the entity specified by the id exists and is a collection.
372: */
373: public String getContentType(String entityId)
374: throws PermissionException, IdUnusedException,
375: TypeException;
376:
377: /**
378: *
379: * @param entityId
380: * @param propertyName
381: *
382: * @return
383: *
384: * @throws PermissionException
385: * if the user does not have permission to access this entity.
386: * @throws IdUnusedException
387: * if the entity specified by the id does not exist.
388: */
389: public String getProperty(String entityId, String propertyName)
390: throws PermissionException, IdUnusedException;
391:
392: /**
393: * Moves a resource or collection along with all attributes and properties.
394: * No properties are taken from any existing entity.
395: * If the parameter overwrite is true, delete existing entities before
396: * the move; otherwise fail if the new entity exists.
397: * If the parameter oldId identifies a collection, move the entire hierarchy
398: * of collections and resources rooted at oldId. The copy succeeds unless
399: * one of these errors occurs:
400: * <ul>
401: * <li>no entity exists with the identifier oldId</li>
402: * <li>the user does not have permission to access the entity oldId</li>
403: * <li>the user does not have permission to create the entity newId</li>
404: * <li>the user does not have permission to delete oldId</li>
405: * <li>overwrite is true, the entity newId exists, and the user does not have permission to delete newId</li>
406: * <li>overwrite is true, the entity newId exists, and the entity newId (or one of its members) is in use by another user</li>
407: * <li>overwrite is false and the entity newId already exists</li>
408: * <li>the containing collection for newId does not exist</li>
409: * <li>newId is too long to be used as a resource-id or collection-id</li>
410: * <li>the server is configured to write the resource body to the filesystem and an attempt to save the resource body fails</li>
411: * <li>the source and destination objects are the same
412: * <li>writing the new resource puts the user over quota
413: * </ul>
414: * returns true if a new entity is created, i.e. newId did not exist
415: *
416: * @param oldId
417: * @param newId
418: * @param overwrite
419: *
420: * @return true if a new entity is created, i.e. newId did not exist
421: *
422: * @throws PermissionException
423: * if the user does not have permission to delete
424: * the existing entity, or delete/create the new entity.
425: * @throws IdUsedException
426: * if overwrite is false and the newId is already in use.
427: * @throws IdUnusedException
428: * if oldIdd does not exist.
429: * @throws InconsistentException
430: * if the containing collection (for newId) does not exist.
431: * @throws IdLengthException
432: * if the newId is too long.
433: * @throws InUseException
434: * if the entity specified by oldId is currently in use
435: * by another user or, if overwrite is true, the entity
436: * identified by newId exists and is in use by another user.
437: * @throws ServerOverloadException
438: * if the server is configured to write the resource body to the
439: * filesystem and an attempt to save the resource body fails.
440: * @throws OverQuotaException
441: * if writing the new resource puts the user over quota
442: */
443: public boolean rename(String oldId, String newId, boolean overwrite)
444: throws PermissionException, IdUsedException,
445: InconsistentException, IdLengthException, InUseException,
446: TypeException, ServerOverloadException;
447:
448: /**
449: *
450: * @param entityId
451: * @param contentType
452: *
453: * @return
454: *
455: * @throws PermissionException
456: * if the user does not have permission to access this entity.
457: * @throws IdUnusedException
458: * if the entity specified by the id does not exist.
459: * @throws TypeException
460: * if the entity specified by the id exists and is a collection.
461: */
462: public String setContentType(String entityId, String contentType)
463: throws PermissionException, IdUnusedException,
464: TypeException;
465:
466: /**
467: *
468: * @param entityId
469: * @param properties
470: *
471: * @throws PermissionException
472: * if the user does not have permission to modify this entity.
473: * @throws IdUnusedException
474: * if the entity specified by the id does not exist.
475: */
476: public void setProperties(String entityId,
477: ResourceProperties properties) throws PermissionException,
478: IdUnusedException;
479:
480: /**
481: *
482: * @param entityId
483: * @param propertyName
484: * @param propertyValue
485: *
486: * @throws PermissionException
487: * if the user does not have permission to modify this entity.
488: * @throws IdUnusedException
489: * if the entity specified by the id does not exist.
490: */
491: public void setProperty(String entityId, String propertyName,
492: String propertyValue) throws PermissionException,
493: IdUnusedException;
494:
495: /**
496: *
497: * @param entityId
498: *
499: * @return
500: *
501: * @throws PermissionException
502: * if the user does not have permission to access this entity.
503: * @throws IdUnusedException
504: * if the entity specified by the id does not exist.
505: */
506: public OutputStream streamContent(String entityId)
507: throws PermissionException, IdUnusedException;
508:
509: }
|