As in other previous entries the topic of this one is a technology that has been surrounding me several times but never a project finally came out. CalDAV is a standard protocol based on WebDAV (based on HTTP) to access, manage, and share calendaring and scheduling information. It is used by the majority of internet calendar providers like google or yahoo! and
many commercial and open source calendar server applications.
As you see around CalDAV there are some concepts:
- WebDAV (Web-based Distributed Authoring and Versioning) is an extension to HTTP that allows users to edit and manage files collaboratively on remote web servers. RFC-4918 defines the WebDAV extensions.
In a previous entry a simple CGI was used to solve a SUDOKU and there I explained the GET and POST HTTP methods. HyperText Transfer Protocol (HTTP) has another methods like PUT (uploads a specified resource) or DELETE (deletes a web resource). WebDAV extends HTTP with more of them like PROPFIND (retrieves properties of resources), MKCOL (creates a new WebDAV collection), COPY and MOVE (copies and moves one resource from one URI to another) or LOCK and UNLOCK (creates and removes a shared or exclusive lock over a resource). Several RFCs have improved WebDAV with even more extensions like RFC-3253, versioning extensions to the HTTP/1.1 protocol (CHECKOUT, CHECKIN,...) and many others.
WebDAV RFC chooses XML as the language to use over HTTP communication and there are DTDs (Document Type Definition) to define the XML data exchanged in every method. Subsequent extensions (CalDAV too) do exactly the same in their respective RFCs.
Commonly speaking WebDAV protocol lets people manage collections of files stored in the internet. And many internet providers use it to provide virtual disk services to their customers.
- CalDAV (Calendaring Extensions to WebDAV) is just another extension over WebDAV (RFC-4791) to specify a standard way of accessing, managing and sharing calendaring data based on the iCalendar format. In summary it is a protocol to manage typical calendar resources (calendars, events, to-dos,...) as internet WebDAV collections.
- iCalendar (Internet Calendaring and Scheduling Core Object Specification) is a data format (RFC-5545 replaced RFC-2445 and now defines the standard) for representing and exchanging calendaring and scheduling information such as events, to-dos, journal entries, and free/busy information, independent of any particular calendar service or protocol. So, while CalDAV is the protocol to access and manage calendar resources, iCalendar is the format of the data stored in and retrieved from the server. Other way of saying the same, a calendar server manages collections of iCalendar formatted resources accessed by users via CalDAV protocol.
After it is clear what CalDAV means the next question to answer would be what is the current status of the development libraries about this protocol (in java of course). The little investigation I have done (very scattered I admit) gives us some links:
- CalDAV. The only library (I found) written in Java which directly implements CalDAV protocol is caldav4j. caldav4j is a project hosted by google which extends Apache Slide WebDAV protocol library and uses iCal4j for iCalendar data processing. The main problem with caldav4j is that Apache abandoned Slide in 2007 and therefore it is based on a deprecated library (now Apache advises to consider Jackrabbit project instead).
- WebDAV. Apache Jackrabbit is a fully conforming implementation of the Content Repository for Java Technology API or JCR. Not going deep into JCR this API grew out of the needs of content management systems, which require storage of documents and other binary objects with associated metadata. Any JCR needs WebDAV to provide access to the repository, so Jackrabbit gives a WebDAV protocol library which can be downloaded separately from the rest of the project here. But jackrabbit-webdav is not very well documented and any extension needs some study of the library source code (welcome to open source!).
- iCalendar. For iCalendar file format there is a well old known project called iCal4j. iCal4j is a Java API that provides support for the iCalendar specification including a Parser, Object Model and Generator for iCalendar data streams.
Although caldav4j is a very good option (and I am sure this project will slowly move from Slide to Jackrabbit library) I preferred to use jackrabbit-webdav directly. On one hand I wanted to understand CalDAV protocol (and using a high level API that hides all below communication was a bad idea) and, on the other, it is always better to develop with the correct library (and it is the obvious future option even for caldav4j). Finally I decided to code a little Java test class with the following features:
- The class will use iCal4j and jackrabbit-webdav libraries. Take into account that WebDAV part also uses Apache's HttpClient library.
- The resulting Java program will be tested against three different CalDAV servers:
- The Java program will perform the following steps:
- Execute an OPTIONS method. This answers if CalDAV is supported.
- Send a PROPFIND to the root calendar URI of a user. This returns some standard output with calendar information.
- Create a new VEVENT for today using PUT.
- Recover the VEVENT using calendar-multiget REPORT.
- Query for the event using calendar-query REPORT.
- Delete the event.
In this first entry only the first two methods (OPTIONS and PROPFIND) are going to be presented. The rest of them (the ones that really need extensions) will appear in the next post.
OPTIONS
The idea is quite simple, performing a direct HTTP OPTIONS method to the CalDAV URI via Jackrabbit. By default OPTIONS method returns the list of methods supported by the specified URI (in the
Allow header). But any CalDAV server must include
calendar-access as a field in the
DAV response header from an OPTIONS request on any resource that supports any calendar properties, reports, or methods. A value of
calendar-access in the
DAV header indicates that the server supports all MUST level requirements and REQUIRED features specified by the CalDAV RFC.
Here it is the Java code:
HttpClient client = ...
OptionsMethod options = null;
try {
options = new OptionsMethod(uri);
client.executeMethod(options);
System.err.println(options.getStatusLine());
for (int i = 0; i < options.getResponseHeaders().length; i++) {
System.err.println(options.getResponseHeaders()[i].getName() + ": "
+ options.getResponseHeaders()[i].getValue());
}
} finally {
if (options != null) {
options.releaseConnection();
}
}
If you check the responses for every server some providers support more DAV extensions than others. In this entry I am just checking
calendar-access but, of course, there are extensions over CalDAV to support more sophisticated features.
Google Calendar:
OPTIONS /calendar/dav/lrickyepoderi@gmail.com/events/ HTTP/1.1
User-Agent: Jakarta Commons-HttpClient/3.1
Authorization: Basic XXXXXXXXX
Host: www.google.com
HTTP/1.1 200 OK
DAV: 1, calendar-access, calendar-schedule, calendar-proxy
Allow: OPTIONS, PROPFIND, HEAD, GET, REPORT, PROPPATCH, PUT, DELETE, POST
Content-Type: application/xml; charset=UTF-8
Date: Tue, 06 Jul 2010 14:52:17 GMT
Expires: Tue, 06 Jul 2010 14:52:17 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Content-Length: 0
Server: GSE
Yahoo! Calendar:
OPTIONS /dav/rickyepoderi@yahoo.es/Calendar/Ricardo_Martin/ HTTP/1.1
User-Agent: Jakarta Commons-HttpClient/3.1
Authorization: Basic XXXXXXXXX
Host: caldav.calendar.yahoo.com
HTTP/1.1 200 OK
Date: Tue, 06 Jul 2010 15:09:27 GMT
P3P: policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
Allow: PROPPATCH, COPY, DELETE, POST, MKCALENDAR, GET, REPORT, PROPFIND, PUT
Allow: MOVE, UNLOCK, OPTIONS, ACL, HEAD, LOCK, MKCOL
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
DAV: version-control, addressbook, extended-mkcol, calendar-proxy
DAV: calendarserver-principal-property-search
X-Cache: MISS from store138.c104.cal.gq1.yahoo.com
Content-Type: text/plain; charset=utf-8
Age: 1
Transfer-Encoding: chunked
Connection: keep-alive
Server: YTS/1.17.23
Calendar Server:
OPTIONS /calendars/users/admin/calendar/ HTTP/1.1
User-Agent: Jakarta Commons-HttpClient/3.1
Authorization: Digest username="admin", realm="Test Realm", nonce="19089203782138543154880100606", uri="/calendars/users/admin/calendar/", response="26c3007948457e549d7564bdf558558c", algorithm="md5"
Host: localhost:8008
HTTP/1.1 200 OK
Content-Length: 0
Server: CalendarServer/trunk(r5842M) Twisted/10.0.0 TwistedWeb/9.0.0+r5842
Last-Modified: Sun, 04 Jul 2010 16:45:37 GMT
DAV: 1, access-control, calendar-access, calendar-schedule, calendar-auto-schedule, calendar-availability, inbox-availability, calendar-proxy, calendarserver-private-events, calendarserver-private-comments, calendarserver-sharing, addressbook, calendarserver-principal-property-search
ETag: "285DD2-1000-4C30BAB1"
Allow: ACL, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, TRACE, UNLOCK
Date: Tue, 06 Jul 2010 15:19:57 GMT
PROPFIND
The PROPFIND WebDAV method retrieves properties for a specified resource, in case of a CalDAV collection URI it can return an optional property
calendar-description and must return
calendar and
collection as
resourcetype property.
The code for a PROPFIND requesting all properties with DEPTH 0 (only the collection URI) is below. The answer must be a multi-status response (a WebDAV defined XML response with HTTP code 207) with all the properties for every resource found.
HttpClient client = ...
PropFindMethod propFind = null;
try {
propFind = new PropFindMethod(uri,
DavConstants.PROPFIND_ALL_PROP, DavConstants.DEPTH_0);
client.executeMethod(propFind);
MultiStatus multiStatus = propFind.getResponseBodyAsMultiStatus();
} finally {
if (propFind != null) {
propFind.releaseConnection();
}
}
Google Calendar:
PROPFIND /calendar/dav/rickyepoderi@gmail.com/events/ HTTP/1.1
Depth: 0
User-Agent: Jakarta Commons-HttpClient/3.1
Content-Length: 90
Content-Type: text/xml; charset=UTF-8
Authorization: Basic XXXXXXXXX
Host: www.google.com
<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>
HTTP/1.1 207 Multi-Status
DAV: 1, calendar-access, calendar-schedule, calendar-proxy
Content-Type: application/xml; charset=UTF-8
Date: Tue, 06 Jul 2010 16:04:32 GMT
Expires: Tue, 06 Jul 2010 16:04:32 GMT
<?xml version='1.0' encoding='UTF-8'?>
<D:multistatus xmlns:D="DAV:">
<D:response>
<D:href>/calendar/dav/rickyepoderi@gmail.com/events/</D:href>
<D:propstat>
<D:status>HTTP/1.1 200 OK</D:status>
<D:prop>
<C:calendar-description xmlns:C="urn:ietf:params:xml:ns:caldav">rickyepoderi@gmail.com</C:calendar-description>
<A:calendar-color xmlns:A="http://apple.com/ns/ical/">#2952A3</A:calendar-color>
<D:getcontenttype>text/calendar; component=vevent</D:getcontenttype>
<D:displayname>Ricardo Martin</D:displayname>
<D:resourcetype>
<D:collection />
<C:calendar xmlns:C="urn:ietf:params:xml:ns:caldav" />
</D:resourcetype>
<CS:getctag xmlns:CS="http://calendarserver.org/ns/">63414112150</CS:getctag>
</D:prop>
</D:propstat>
</D:response>
</D:multistatus>
Yahoo! Calendar:
PROPFIND /dav/rickyepoderi@yahoo.es/Calendar/Ricardo_Martin/ HTTP/1.1
Depth: 0
User-Agent: Jakarta Commons-HttpClient/3.1
Content-Length: 90
Content-Type: text/xml; charset=UTF-8
Authorization: Basic XXXXXXXXX
Host: caldav.calendar.yahoo.com
<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>
HTTP/1.1 207 Multi Status
Date: Tue, 06 Jul 2010 15:59:20 GMT
P3P: policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
DAV: version-control, addressbook, extended-mkcol, calendar-proxy
DAV: calendarserver-principal-property-search
Content-Type: text/xml; charset=utf-8
X-Cache: MISS from store138.c104.cal.gq1.yahoo.com
Age: 0
Transfer-Encoding: chunked
Connection: keep-alive
Server: YTS/1.17.23
<?xml version='1.0' encoding='UTF-8'?>
<D:multistatus xmlns:D="DAV:">
<D:response>
<D:href>/dav/rickyepoderi@yahoo.es/Calendar/Ricardo_Martin/</D:href>
<D:propstat>
<D:status>HTTP/1.1 200 OK</D:status>
<D:prop>
<D:getetag>"4-1"</D:getetag>
<D:getcontentlength>0</D:getcontentlength>
<D:creationdate>2010-07-04T03:35:33-07:00</D:creationdate>
<D:resourcetype>
<D:collection/>
<C:calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/>
</D:resourcetype>
<D:getlastmodified>Sun, 04 Jul 2010 03:40:42 -0700 (PDT)</D:getlastmodified>
<D:displayname>Ricardo_Martin</D:displayname>
<CS:getctag xmlns:CS="http://calendarserver.org/ns/">4-201</CS:getctag>
</D:prop>
</D:propstat>
</D:response>
</D:multistatus>
Calendar Server:
PROPFIND /calendars/users/admin/calendar/ HTTP/1.1
Depth: 0
User-Agent: Jakarta Commons-HttpClient/3.1
Content-Length: 90
Content-Type: text/xml; charset=UTF-8
Authorization: Digest username="admin", realm="Test Realm", nonce="2563477371875350179601883528", uri="/calendars/users/admin/calendar/", response="5e1623b0f086e2be5dbb3c349f79ffb2", algorithm="md5"
Host: localhost:8008
<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>
HTTP/1.1 207 Multi-Status
Content-Length: 1794
Accept-Ranges: bytes
Server: CalendarServer/trunk(r5842M) Twisted/10.0.0 TwistedWeb/9.0.0+r5842
Last-Modified: Tue, 06 Jul 2010 15:20:08 GMT
DAV: 1, access-control, calendar-access, calendar-schedule, calendar-auto-schedule, calendar-availability, inbox-availability, calendar-proxy, calendarserver-private-events, calendarserver-private-comments, calendarserver-sharing, addressbook, calendarserver-principal-property-search
ETag: "285DD2-1000-4C3349A8"
Date: Tue, 06 Jul 2010 15:49:27 GMT
Content-Type: text/xml
<?xml version='1.0' encoding='UTF-8'?>
<multistatus xmlns='DAV:'>
<response>
<href>/calendars/users/admin/calendar/</href>
<propstat>
<prop>
<getetag>"285DD2-1000-4C3349A8"</getetag>
<current-user-principal>
<href>/principals/_uids_/admin/</href>
</current-user-principal>
<displayname>calendar</displayname>
<getctag xmlns='http://calendarserver.org/ns/'>76184803-9107-4334-a5d7-25c6490d7997#6</getctag>
<getcontenttype>httpd/unix-directory</getcontenttype>
<supportedlock>
<lockentry>
<lockscope>
<exclusive/>
</lockscope>
<locktype>
<write/>
</locktype>
</lockentry>
<lockentry>
<lockscope>
<shared/>
</lockscope>
<locktype>
<write/>
</locktype>
</lockentry>
</supportedlock>
<resourcetype>
<collection/>
<calendar xmlns='urn:ietf:params:xml:ns:caldav'/>
</resourcetype>
<getcontentlength/>
<schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'>
<opaque/>
</schedule-calendar-transp>
<getlastmodified>Tue, 06 Jul 2010 17:20:08 GMT</getlastmodified>
<creationdate>2010-07-06T17:20:08Z</creationdate>
<resource-class xmlns='http://twistedmatrix.com/xml_namespace/dav/'>CalDAVFile</resource-class>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
<propstat>
<prop>
<invite xmlns='http://calendarserver.org/ns/'/>
</prop>
<status>HTTP/1.1 404 Not Found
</propstat>
</response>
</multistatus>
In this entry I just introduced CalDAV access and no real CalDAV method has been executed. The previous two methods (OPTIONS and PROPFIND) are defined in HTTP and WebDAV respectively and, although they return some CalDAV exclusive properties and headers, they are not real CalDAV methods. In the next post I will continue with the previously commented steps and, in some of them, jackrabbit-webdav will need to be extended.
See you soon with the sedond part!
Comments