SlideShare a Scribd company logo
Talk CalDAV To Me: Integrating Bedework into OAE Chris Tweney <chris@media.berkeley.edu> Senior Software Developer Educational Technology Services UC Berkeley June 14, 2011
myBerkeley Instance of Sakai OAE with customizations Esp. those that allow advisors to communicate to students
myBerkeley widgets
myBerkeley widgets
myBerkeley Notifications
Notification Dependencies
Calendaring Fundamentals iCalendar specification iCal4j CalDAV protocol WebDAV protocol
iCalendar Simple text-based format for calendar data exchange iCalendar's fundamental objects are: Events (VEVENT) To-dos (VTODO) Journal entries (VJOURNAL) Free-busy info (VFREEBUSY)
iCalendar Usually you encounter iCalendar data in the form of files with a “.ics” extension.  Here’s a typical (abbreviated) example: BEGIN:VCALENDAR PRODID:-//Ben Fortuna//iCal4j 1.0//EN VERSION:2.0 CALSCALE:GREGORIAN BEGIN:VTODO DTSTAMP:20110603T222847Z DTSTART:20110505T151506 DUE:20110505T151506 SUMMARY:Pay your bill UID:f84347ab-575b-4274-9436-a5ac906381f9 DESCRIPTION:Pay your bill by the deadline of May 5.  END:VTODO END:VCALENDAR iCalendar is defined in RFC 5545 http://tools.ietf.org/html/rfc5545
iCalendar Parts
ical4j Open-source Java library to wrangle messy iCalendar data  iCalendar records with time zone data get very complicated; ical4j makes it pretty simple to work with them ical4j home page: http://wiki.modularity.net.au/ical4j/index.php?title=Main_Page
ical4j Java Example protected Calendar buildVTodo(String summary) { CalendarBuilder builder = new CalendarBuilder();     Calendar calendar = new Calendar(); calendar.getProperties().add(newProdId("-//Ben Fortuna//iCal4j 1.0//EN"));     calendar.getProperties().add(Version.VERSION_2_0); calendar.getProperties().add(CalScale.GREGORIAN); TimeZoneRegistry registry = builder.getRegistry(); VTimeZonetz = registry.getTimeZone("America/Los_Angeles").getVTimeZone(); calendar.getComponents().add(tz); DateTime due = new DateTime(DateUtils.addDays(new Date(), new Random().nextInt(28))); VToDovtodo = new VToDo(due, due, summary); vtodo.getProperties().add(newUid(UUID.randomUUID().toString())); vtodo.getProperties().add(CalDavConnector.MYBERKELEY_REQUIRED); vtodo.getProperties().add(newDescription("this is the description, it is long enough to           wrap at the ical specified standard 75th column")); vtodo.getProperties().add(Status.VTODO_NEEDS_ACTION); calendar.getComponents().add(vtodo);     return calendar; }
ical4j pitfall: Line folding Per the iCalendar RFC, long lines of text get newlines inserted at the 75th column Sadly, ical4j does not handle this by default Need to specify “ical4j.unfolding.relaxed=true” in an ical4j.properties file
CalDAV Dialect of WebDAV that provides calendar functionality Much syntax and structure is inherited from its underlying specifications: WebDAV and iCalendar CalDAV is defined in RFC 4791: http://tools.ietf.org/html/rfc4791
Sakai 2 CalDAV GA Tech had a CalDAV project in Sakai 2 that was never released due to gaps in Zimbra's API Zach Thomas left excellent docs that hugely helped our efforts Sakai 2 CalDAV Doc Page: https://confluence.sakaiproject.org/display/CALDAV/Developer's+Guide
CalDAV, WebDAV, HTTP
Learn by snooping
Speaking CalDAV with curl Doing a PROPFIND is a shortcut way to get a user's whole calendar: curl –u user:pass -X PROPFIND http://localhost:8080/ucaldav/user/300846/calendar/ To curl, "-X PROPFIND" means do an HTTP PROPFIND method. HTTP has several funky methods you've never heard of  unless you've worked with WebDAV.
Speaking CalDAV with curl curl –u user:pass -X PROPFIND http://localhost:8080/ucaldav/user/300846/calendar/ HTTP/1.1 207 Multi-Status <?xml version="1.0" encoding="UTF-8" ?> <DAV:multistatus xmlns:DAV="DAV:"               xmlns="urn:ietf:params:xml:ns:caldav"               xmlns:ical="http://www.w3.org/2002/12/cal/ical#">    <DAV:response> <DAV:href>/ucaldav/user/300846/calendar/00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:href>     <DAV:propstat>       <DAV:prop>         <DAV:getcontenttype>text/calendar; charset=UTF-8</DAV:getcontenttype>         <DAV:getcontentlength>339</DAV:getcontentlength>         <DAV:getlastmodified>Tue, 31 May 2011 21:23:32 +0000</DAV:getlastmodified>         <DAV:displayname>00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:displayname> <DAV:getetag>"20110531T212332Z-0"</DAV:getetag>         <DAV:current-user-principal>           <DAV:href>/ucaldav/principals/users/admin/</DAV:href>         </DAV:current-user-principal>         <DAV:resourcetype/>         <DAV:getcontentlanguage>en</DAV:getcontentlanguage>         <DAV:creationdate>20110531T212332Z</DAV:creationdate>       </DAV:prop>       <DAV:status>HTTP/1.1 200 ok</DAV:status>     </DAV:propstat>   </DAV:response>   … more entries snipped … </DAV:multistatus>
CalDAV and WebDAV challenges Not your everyday HTTP request Weird methods (PROPFIND, OPTIONS, PUT, etc) Multi-status responses (HTTP 207)
Fortunately… Jackrabbit, on which OAE is based, ships with a reasonable WebDAV Java client We can use this to make our basic CalDAV connections
CalDavConnector.getCalendarUris() getCalendarUris() is our simplest method Just return URI wrapper objects for a user's whole calendar
Complexity's a problem CalDAV and WebDAV are Complicated Not noob-friendly RFCs are almost the only docs available
Impatience is a virtue Need lots of trial and error Redeploying a server takes 60-90s Running a test takes 2s 2s is too little time to get distracted
Integration-test-driven development JUnit to the rescue! "Unit" tests that actually talk to a running Bedework server are the solution
Write the test first… Before doing anything else I write a test:  public class CalDavConnectorTest() {   @Test()   public void getCalendars() throws CalDavException {           List<CalendarURI> uris =                this.calDavConnector.getCalendarUris();   } }
…Then make it pass Now we'll make it pass in the hackiest way imaginable public class CalDavConnector() {   public void getCalendars() throws CalDavException {           return new ArrayList<CalendarURI>();   } }
…Iterate until done Keep adding implementation code until it does what's needed:        PropFindMethod propFind = executeMethod(new PropFindMethod(this.userHome.toString()));       MultiStatusResponse[] responses = propFind.getResponseBodyAsMultiStatus().getResponses();       for (MultiStatusResponse response : responses) {         if (response.getHref().endsWith(".ics")) {           Status[] status = response.getStatus();           if (status.length == 1 && status[0].getStatusCode() == HttpServletResponse.SC_OK) {             DavPropertySet propSet = response.getProperties(HttpServletResponse.SC_OK); DavProperty etag = propSet.get(DavPropertyName.GETETAG);             try {               CalendarURI calUri = new CalendarURI(                       new URI(this.serverRoot, response.getHref(), false),                       etag.getValue().toString());               uris.add(calUri);             } catch (ParseException pe) {               throw new CalDavException("Invalid etag date", pe);             }           }         }       }
Unit testing heresy These tests talk to and expect a running Bedework server This makes them heretical according to true unit test dogma Tests are not supposed to require an external server
Gracefully failing test This test will succeed even if the Bedework server does not respond (because we catch the IOException): public class CalDavConnectorTest() {   @Test()   public void getCalendars() throws CalDavException {     try {       List<CalendarURI> uris =                this.calDavConnector.getCalendarUris();     } catch (IOException ioe) {       LOGGER.error("Trouble contacting server", ioe);     }   } }
PROPFIND only gives you URIs… curl –u user:pass -X PROPFIND http://localhost:8080/ucaldav/user/300846/calendar/ HTTP/1.1 207 Multi-Status <?xml version="1.0" encoding="UTF-8" ?> <DAV:multistatus xmlns:DAV="DAV:"               xmlns="urn:ietf:params:xml:ns:caldav"               xmlns:ical="http://www.w3.org/2002/12/cal/ical#">    <DAV:response>     <DAV:href>/ucaldav/user/300846/calendar/00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:href>     <DAV:propstat>       <DAV:prop>         <DAV:getcontenttype>text/calendar; charset=UTF-8</DAV:getcontenttype>         <DAV:getcontentlength>339</DAV:getcontentlength>         <DAV:getlastmodified>Tue, 31 May 2011 21:23:32 +0000</DAV:getlastmodified>         <DAV:displayname>00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:displayname>         <DAV:getetag>"20110531T212332Z-0"</DAV:getetag>         <DAV:current-user-principal>           <DAV:href>/ucaldav/principals/users/admin/</DAV:href>         </DAV:current-user-principal>         <DAV:resourcetype/>         <DAV:getcontentlanguage>en</DAV:getcontentlanguage>         <DAV:creationdate>20110531T212332Z</DAV:creationdate>       </DAV:prop>       <DAV:status>HTTP/1.1 200 ok</DAV:status>     </DAV:propstat>   </DAV:response>   … more entries snipped … </DAV:multistatus>
…which you then must get with a REPORT method curl –u user:pass -X REPORT http://localhost:8080/ucaldav/user/mtwain/calendar/ -d "<?xml version="1.0" encoding="UTF-8"?> <C:calendar-multiget xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">     <D:prop>         <D:getetag/>         <C:calendar-data/>     </D:prop>     <D:href>http://localhost:8080/ucaldav/user/mtwain/calendar/d12ad881-5769-4d17-85ac-fa0a0196ec04.ics     </D:href> </C:calendar-multiget>" (Note: Double quotes not escaped for clarity)
REPORT method's response <DAV:response>     <DAV:href>/ucaldav/user/mtwain/calendar/02bfa2cd-6c04-40f4-93da-896d54fc2987.ics</DAV:href>     <DAV:propstat>       <DAV:prop>         <DAV:getetag>"20110602T211046Z-0"</DAV:getetag>         <calendar-data><![CDATA[BEGIN:VCALENDAR PRODID://Bedework.org//BedeWork V3.7//EN VERSION:2.0 BEGIN:VTODO CATEGORIES:MyBerkeley-Required CATEGORIES:MyBerkeley-Archived CREATED:20110601T171120Z DESCRIPTION:test … [ snip ] ]]></calendar-data>       </DAV:prop>       <DAV:status>HTTP/1.1 200 ok</DAV:status>     </DAV:propstat> </DAV:response>
Hairy REPORT Syntax CalDAV's REPORT syntax is hairy XML Have to extend Jackrabbit's ReportInfo classes This blog entry by Ricardo Martin Camarero has very useful starter code: http://blogs.nologin.es/rickyepoderi/index.php?/archives/15-Introducing-CalDAV-Part-II.html
REPORT is foundation for search REPORT methods with parameters let you search by date
Bedework difficulties Bedework's search implementation spotty No support for search on X-props No support for search on CATEGORIES
Filtering on myBerkeley server Due to Bedework bugs we're forced to do some filtering on the myBerkeley server side E.g. Required/Not Required fields
Caching on myBerkeley server CalendarURI wrapper class URI: Locates calendar Etag: Uniquely identifies its contents (sort of like a SHA hash) Caching not implemented yet, but easy enough since all calendars are keyed by CalendarURI
Implementing CalDavProxyServlet Also done with test-driven development Write tests against JSON files that contain servlet's expected inputs When servlet's finished, UI devs use those JSON files as an API reference
Improvements to Nakamura's CalendarService Future work: Store and search calendars using Nakamura's CalendarService Add support for VTODO to CalendarService
Beyond CalendarService Future: Create a full-blown CalDAV Calendar Provider component for Nakamura Store calendars externally, transparently refer to them within Nakamura code

More Related Content

What's hot

Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)
Mark Proctor
 
We Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End DevelopmentWe Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End Development
All Things Open
 
Rails Caching Secrets from the Edge
Rails Caching Secrets from the EdgeRails Caching Secrets from the Edge
Rails Caching Secrets from the Edge
Michael May
 
Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015
Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015
Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015
Fastly
 
Need for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsNeed for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applications
Konrad Malawski
 
Introduction to rest.li
Introduction to rest.liIntroduction to rest.li
Introduction to rest.li
Joe Betz
 
Yaroslav Pernerovsky - You are doing it wrong all the time
Yaroslav Pernerovsky - You are doing it wrong all the timeYaroslav Pernerovsky - You are doing it wrong all the time
Yaroslav Pernerovsky - You are doing it wrong all the time
Ievgenii Katsan
 
Altitude SF 2017: Debugging Fastly VCL 101
Altitude SF 2017: Debugging Fastly VCL 101Altitude SF 2017: Debugging Fastly VCL 101
Altitude SF 2017: Debugging Fastly VCL 101
Fastly
 
Android dev 3
Android dev 3Android dev 3
Android dev 3
Aravindharamanan S
 
MongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-es
MongoDB
 
Implicit and Explicit waits in Selenium WebDriwer, how to.
Implicit and Explicit waits in Selenium WebDriwer, how to.Implicit and Explicit waits in Selenium WebDriwer, how to.
Implicit and Explicit waits in Selenium WebDriwer, how to.
Yaroslav Pernerovsky
 
Automation puzzlers
Automation puzzlersAutomation puzzlers
Automation puzzlers
Yaroslav Pernerovsky
 
servlets
servletsservlets
servlets
Arjun Shanka
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
Peter Friese
 
Presenting CalDAV (draft 1)
Presenting CalDAV (draft 1)Presenting CalDAV (draft 1)
Presenting CalDAV (draft 1)
Roberto Polli
 
WebDriver Waits
WebDriver WaitsWebDriver Waits
WebDriver Waits
Yaroslav Pernerovsky
 
The Need for Async @ ScalaWorld
The Need for Async @ ScalaWorldThe Need for Async @ ScalaWorld
The Need for Async @ ScalaWorld
Konrad Malawski
 
Troubleshooting JIRA & Confluence
Troubleshooting JIRA & ConfluenceTroubleshooting JIRA & Confluence
Troubleshooting JIRA & Confluence
Atlassian
 
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
zshoylev
 
LinkRest at JeeConf 2017
LinkRest at JeeConf 2017LinkRest at JeeConf 2017
LinkRest at JeeConf 2017
Andrus Adamchik
 

What's hot (20)

Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)
 
We Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End DevelopmentWe Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End Development
 
Rails Caching Secrets from the Edge
Rails Caching Secrets from the EdgeRails Caching Secrets from the Edge
Rails Caching Secrets from the Edge
 
Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015
Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015
Mitigating Security Threats with Fastly - Joe Williams at Fastly Altitude 2015
 
Need for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsNeed for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applications
 
Introduction to rest.li
Introduction to rest.liIntroduction to rest.li
Introduction to rest.li
 
Yaroslav Pernerovsky - You are doing it wrong all the time
Yaroslav Pernerovsky - You are doing it wrong all the timeYaroslav Pernerovsky - You are doing it wrong all the time
Yaroslav Pernerovsky - You are doing it wrong all the time
 
Altitude SF 2017: Debugging Fastly VCL 101
Altitude SF 2017: Debugging Fastly VCL 101Altitude SF 2017: Debugging Fastly VCL 101
Altitude SF 2017: Debugging Fastly VCL 101
 
Android dev 3
Android dev 3Android dev 3
Android dev 3
 
MongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-es
 
Implicit and Explicit waits in Selenium WebDriwer, how to.
Implicit and Explicit waits in Selenium WebDriwer, how to.Implicit and Explicit waits in Selenium WebDriwer, how to.
Implicit and Explicit waits in Selenium WebDriwer, how to.
 
Automation puzzlers
Automation puzzlersAutomation puzzlers
Automation puzzlers
 
servlets
servletsservlets
servlets
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
 
Presenting CalDAV (draft 1)
Presenting CalDAV (draft 1)Presenting CalDAV (draft 1)
Presenting CalDAV (draft 1)
 
WebDriver Waits
WebDriver WaitsWebDriver Waits
WebDriver Waits
 
The Need for Async @ ScalaWorld
The Need for Async @ ScalaWorldThe Need for Async @ ScalaWorld
The Need for Async @ ScalaWorld
 
Troubleshooting JIRA & Confluence
Troubleshooting JIRA & ConfluenceTroubleshooting JIRA & Confluence
Troubleshooting JIRA & Confluence
 
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
Taming the Cloud Database with Apache jclouds, ApacheCon Europe 2014
 
LinkRest at JeeConf 2017
LinkRest at JeeConf 2017LinkRest at JeeConf 2017
LinkRest at JeeConf 2017
 

Similar to Integrating Bedework, a CalDAV Calendar Server, into OAE

Play Framework: async I/O with Java and Scala
Play Framework: async I/O with Java and ScalaPlay Framework: async I/O with Java and Scala
Play Framework: async I/O with Java and Scala
Yevgeniy Brikman
 
Azure SQL Database - Connectivity Best Practices
Azure SQL Database - Connectivity Best PracticesAzure SQL Database - Connectivity Best Practices
Azure SQL Database - Connectivity Best Practices
Jose Manuel Jurado Diaz
 
Altitude San Francisco 2018: Testing with Fastly Workshop
Altitude San Francisco 2018: Testing with Fastly WorkshopAltitude San Francisco 2018: Testing with Fastly Workshop
Altitude San Francisco 2018: Testing with Fastly Workshop
Fastly
 
ERGroupware
ERGroupwareERGroupware
ERGroupware
WO Community
 
OGCE Overview for SciDAC 2009
OGCE Overview for SciDAC 2009OGCE Overview for SciDAC 2009
OGCE Overview for SciDAC 2009
marpierc
 
OGCE Project Overview
OGCE Project OverviewOGCE Project Overview
OGCE Project Overview
marpierc
 
Tutorial hadoop hdfs_map_reduce
Tutorial hadoop hdfs_map_reduceTutorial hadoop hdfs_map_reduce
Tutorial hadoop hdfs_map_reduce
mudassar mulla
 
Direct Web Remoting : DWR
Direct Web Remoting : DWRDirect Web Remoting : DWR
Direct Web Remoting : DWR
hussulinux
 
Java Servlets
Java ServletsJava Servlets
Java Servlets
BG Java EE Course
 
Oredev 2009 JAX-RS
Oredev 2009 JAX-RSOredev 2009 JAX-RS
Oredev 2009 JAX-RS
Niklas Gustavsson
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
Atlassian
 
The A to Z of developing for the web
The A to Z of developing for the webThe A to Z of developing for the web
The A to Z of developing for the web
Matt Wood
 
Application Continuity with Oracle DB 12c
Application Continuity with Oracle DB 12c Application Continuity with Oracle DB 12c
Application Continuity with Oracle DB 12c
Léopold Gault
 
Basic testing with selenium
Basic testing with seleniumBasic testing with selenium
Basic testing with selenium
Søren Lund
 
Hackingtomcat
HackingtomcatHackingtomcat
Hackingtomcat
Aung Khant
 
Hacking Tomcat
Hacking TomcatHacking Tomcat
Hacking Tomcat
guestc27cd9
 
Advanced .NET Data Access with Dapper
Advanced .NET Data Access with Dapper Advanced .NET Data Access with Dapper
Advanced .NET Data Access with Dapper
David Paquette
 
REST made simple with Java
REST made simple with JavaREST made simple with Java
REST made simple with Java
elliando dias
 
Select Star: Unified Batch & Streaming with Flink SQL & Pulsar
Select Star: Unified Batch & Streaming with Flink SQL & PulsarSelect Star: Unified Batch & Streaming with Flink SQL & Pulsar
Select Star: Unified Batch & Streaming with Flink SQL & Pulsar
Caito Scherr
 
Quartz.NET - Enterprise Job Scheduler for .NET Platform
Quartz.NET - Enterprise Job Scheduler for .NET PlatformQuartz.NET - Enterprise Job Scheduler for .NET Platform
Quartz.NET - Enterprise Job Scheduler for .NET Platform
Guo Albert
 

Similar to Integrating Bedework, a CalDAV Calendar Server, into OAE (20)

Play Framework: async I/O with Java and Scala
Play Framework: async I/O with Java and ScalaPlay Framework: async I/O with Java and Scala
Play Framework: async I/O with Java and Scala
 
Azure SQL Database - Connectivity Best Practices
Azure SQL Database - Connectivity Best PracticesAzure SQL Database - Connectivity Best Practices
Azure SQL Database - Connectivity Best Practices
 
Altitude San Francisco 2018: Testing with Fastly Workshop
Altitude San Francisco 2018: Testing with Fastly WorkshopAltitude San Francisco 2018: Testing with Fastly Workshop
Altitude San Francisco 2018: Testing with Fastly Workshop
 
ERGroupware
ERGroupwareERGroupware
ERGroupware
 
OGCE Overview for SciDAC 2009
OGCE Overview for SciDAC 2009OGCE Overview for SciDAC 2009
OGCE Overview for SciDAC 2009
 
OGCE Project Overview
OGCE Project OverviewOGCE Project Overview
OGCE Project Overview
 
Tutorial hadoop hdfs_map_reduce
Tutorial hadoop hdfs_map_reduceTutorial hadoop hdfs_map_reduce
Tutorial hadoop hdfs_map_reduce
 
Direct Web Remoting : DWR
Direct Web Remoting : DWRDirect Web Remoting : DWR
Direct Web Remoting : DWR
 
Java Servlets
Java ServletsJava Servlets
Java Servlets
 
Oredev 2009 JAX-RS
Oredev 2009 JAX-RSOredev 2009 JAX-RS
Oredev 2009 JAX-RS
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
 
The A to Z of developing for the web
The A to Z of developing for the webThe A to Z of developing for the web
The A to Z of developing for the web
 
Application Continuity with Oracle DB 12c
Application Continuity with Oracle DB 12c Application Continuity with Oracle DB 12c
Application Continuity with Oracle DB 12c
 
Basic testing with selenium
Basic testing with seleniumBasic testing with selenium
Basic testing with selenium
 
Hackingtomcat
HackingtomcatHackingtomcat
Hackingtomcat
 
Hacking Tomcat
Hacking TomcatHacking Tomcat
Hacking Tomcat
 
Advanced .NET Data Access with Dapper
Advanced .NET Data Access with Dapper Advanced .NET Data Access with Dapper
Advanced .NET Data Access with Dapper
 
REST made simple with Java
REST made simple with JavaREST made simple with Java
REST made simple with Java
 
Select Star: Unified Batch & Streaming with Flink SQL & Pulsar
Select Star: Unified Batch & Streaming with Flink SQL & PulsarSelect Star: Unified Batch & Streaming with Flink SQL & Pulsar
Select Star: Unified Batch & Streaming with Flink SQL & Pulsar
 
Quartz.NET - Enterprise Job Scheduler for .NET Platform
Quartz.NET - Enterprise Job Scheduler for .NET PlatformQuartz.NET - Enterprise Job Scheduler for .NET Platform
Quartz.NET - Enterprise Job Scheduler for .NET Platform
 

Recently uploaded

A Deep Dive into ScyllaDB's Architecture
A Deep Dive into ScyllaDB's ArchitectureA Deep Dive into ScyllaDB's Architecture
A Deep Dive into ScyllaDB's Architecture
ScyllaDB
 
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
"Scaling RAG Applications to serve millions of users",  Kevin Goedecke"Scaling RAG Applications to serve millions of users",  Kevin Goedecke
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
Fwdays
 
"What does it really mean for your system to be available, or how to define w...
"What does it really mean for your system to be available, or how to define w..."What does it really mean for your system to be available, or how to define w...
"What does it really mean for your system to be available, or how to define w...
Fwdays
 
What is an RPA CoE? Session 2 – CoE Roles
What is an RPA CoE?  Session 2 – CoE RolesWhat is an RPA CoE?  Session 2 – CoE Roles
What is an RPA CoE? Session 2 – CoE Roles
DianaGray10
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
Miro Wengner
 
Principle of conventional tomography-Bibash Shahi ppt..pptx
Principle of conventional tomography-Bibash Shahi ppt..pptxPrinciple of conventional tomography-Bibash Shahi ppt..pptx
Principle of conventional tomography-Bibash Shahi ppt..pptx
BibashShahi
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
UiPathCommunity
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
Hiroshi SHIBATA
 
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectorsConnector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
DianaGray10
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
Neo4j
 
High performance Serverless Java on AWS- GoTo Amsterdam 2024
High performance Serverless Java on AWS- GoTo Amsterdam 2024High performance Serverless Java on AWS- GoTo Amsterdam 2024
High performance Serverless Java on AWS- GoTo Amsterdam 2024
Vadym Kazulkin
 
The Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptxThe Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptx
operationspcvita
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
Jakub Marek
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Neo4j
 
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeckPoznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
FilipTomaszewski5
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
Neo4j
 
Demystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through StorytellingDemystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through Storytelling
Enterprise Knowledge
 
Apps Break Data
Apps Break DataApps Break Data
Apps Break Data
Ivo Velitchkov
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
Chart Kalyan
 
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
Edge AI and Vision Alliance
 

Recently uploaded (20)

A Deep Dive into ScyllaDB's Architecture
A Deep Dive into ScyllaDB's ArchitectureA Deep Dive into ScyllaDB's Architecture
A Deep Dive into ScyllaDB's Architecture
 
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
"Scaling RAG Applications to serve millions of users",  Kevin Goedecke"Scaling RAG Applications to serve millions of users",  Kevin Goedecke
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
 
"What does it really mean for your system to be available, or how to define w...
"What does it really mean for your system to be available, or how to define w..."What does it really mean for your system to be available, or how to define w...
"What does it really mean for your system to be available, or how to define w...
 
What is an RPA CoE? Session 2 – CoE Roles
What is an RPA CoE?  Session 2 – CoE RolesWhat is an RPA CoE?  Session 2 – CoE Roles
What is an RPA CoE? Session 2 – CoE Roles
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
 
Principle of conventional tomography-Bibash Shahi ppt..pptx
Principle of conventional tomography-Bibash Shahi ppt..pptxPrinciple of conventional tomography-Bibash Shahi ppt..pptx
Principle of conventional tomography-Bibash Shahi ppt..pptx
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
 
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectorsConnector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
 
High performance Serverless Java on AWS- GoTo Amsterdam 2024
High performance Serverless Java on AWS- GoTo Amsterdam 2024High performance Serverless Java on AWS- GoTo Amsterdam 2024
High performance Serverless Java on AWS- GoTo Amsterdam 2024
 
The Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptxThe Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptx
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
 
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeckPoznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
 
Demystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through StorytellingDemystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through Storytelling
 
Apps Break Data
Apps Break DataApps Break Data
Apps Break Data
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
 
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
 

Integrating Bedework, a CalDAV Calendar Server, into OAE

  • 1. Talk CalDAV To Me: Integrating Bedework into OAE Chris Tweney <chris@media.berkeley.edu> Senior Software Developer Educational Technology Services UC Berkeley June 14, 2011
  • 2. myBerkeley Instance of Sakai OAE with customizations Esp. those that allow advisors to communicate to students
  • 7. Calendaring Fundamentals iCalendar specification iCal4j CalDAV protocol WebDAV protocol
  • 8. iCalendar Simple text-based format for calendar data exchange iCalendar's fundamental objects are: Events (VEVENT) To-dos (VTODO) Journal entries (VJOURNAL) Free-busy info (VFREEBUSY)
  • 9. iCalendar Usually you encounter iCalendar data in the form of files with a “.ics” extension. Here’s a typical (abbreviated) example: BEGIN:VCALENDAR PRODID:-//Ben Fortuna//iCal4j 1.0//EN VERSION:2.0 CALSCALE:GREGORIAN BEGIN:VTODO DTSTAMP:20110603T222847Z DTSTART:20110505T151506 DUE:20110505T151506 SUMMARY:Pay your bill UID:f84347ab-575b-4274-9436-a5ac906381f9 DESCRIPTION:Pay your bill by the deadline of May 5. END:VTODO END:VCALENDAR iCalendar is defined in RFC 5545 http://tools.ietf.org/html/rfc5545
  • 11. ical4j Open-source Java library to wrangle messy iCalendar data iCalendar records with time zone data get very complicated; ical4j makes it pretty simple to work with them ical4j home page: http://wiki.modularity.net.au/ical4j/index.php?title=Main_Page
  • 12. ical4j Java Example protected Calendar buildVTodo(String summary) { CalendarBuilder builder = new CalendarBuilder(); Calendar calendar = new Calendar(); calendar.getProperties().add(newProdId("-//Ben Fortuna//iCal4j 1.0//EN")); calendar.getProperties().add(Version.VERSION_2_0); calendar.getProperties().add(CalScale.GREGORIAN); TimeZoneRegistry registry = builder.getRegistry(); VTimeZonetz = registry.getTimeZone("America/Los_Angeles").getVTimeZone(); calendar.getComponents().add(tz); DateTime due = new DateTime(DateUtils.addDays(new Date(), new Random().nextInt(28))); VToDovtodo = new VToDo(due, due, summary); vtodo.getProperties().add(newUid(UUID.randomUUID().toString())); vtodo.getProperties().add(CalDavConnector.MYBERKELEY_REQUIRED); vtodo.getProperties().add(newDescription("this is the description, it is long enough to wrap at the ical specified standard 75th column")); vtodo.getProperties().add(Status.VTODO_NEEDS_ACTION); calendar.getComponents().add(vtodo); return calendar; }
  • 13. ical4j pitfall: Line folding Per the iCalendar RFC, long lines of text get newlines inserted at the 75th column Sadly, ical4j does not handle this by default Need to specify “ical4j.unfolding.relaxed=true” in an ical4j.properties file
  • 14. CalDAV Dialect of WebDAV that provides calendar functionality Much syntax and structure is inherited from its underlying specifications: WebDAV and iCalendar CalDAV is defined in RFC 4791: http://tools.ietf.org/html/rfc4791
  • 15. Sakai 2 CalDAV GA Tech had a CalDAV project in Sakai 2 that was never released due to gaps in Zimbra's API Zach Thomas left excellent docs that hugely helped our efforts Sakai 2 CalDAV Doc Page: https://confluence.sakaiproject.org/display/CALDAV/Developer's+Guide
  • 18. Speaking CalDAV with curl Doing a PROPFIND is a shortcut way to get a user's whole calendar: curl –u user:pass -X PROPFIND http://localhost:8080/ucaldav/user/300846/calendar/ To curl, "-X PROPFIND" means do an HTTP PROPFIND method. HTTP has several funky methods you've never heard of unless you've worked with WebDAV.
  • 19. Speaking CalDAV with curl curl –u user:pass -X PROPFIND http://localhost:8080/ucaldav/user/300846/calendar/ HTTP/1.1 207 Multi-Status <?xml version="1.0" encoding="UTF-8" ?> <DAV:multistatus xmlns:DAV="DAV:" xmlns="urn:ietf:params:xml:ns:caldav" xmlns:ical="http://www.w3.org/2002/12/cal/ical#"> <DAV:response> <DAV:href>/ucaldav/user/300846/calendar/00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:href> <DAV:propstat> <DAV:prop> <DAV:getcontenttype>text/calendar; charset=UTF-8</DAV:getcontenttype> <DAV:getcontentlength>339</DAV:getcontentlength> <DAV:getlastmodified>Tue, 31 May 2011 21:23:32 +0000</DAV:getlastmodified> <DAV:displayname>00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:displayname> <DAV:getetag>"20110531T212332Z-0"</DAV:getetag> <DAV:current-user-principal> <DAV:href>/ucaldav/principals/users/admin/</DAV:href> </DAV:current-user-principal> <DAV:resourcetype/> <DAV:getcontentlanguage>en</DAV:getcontentlanguage> <DAV:creationdate>20110531T212332Z</DAV:creationdate> </DAV:prop> <DAV:status>HTTP/1.1 200 ok</DAV:status> </DAV:propstat> </DAV:response> … more entries snipped … </DAV:multistatus>
  • 20. CalDAV and WebDAV challenges Not your everyday HTTP request Weird methods (PROPFIND, OPTIONS, PUT, etc) Multi-status responses (HTTP 207)
  • 21. Fortunately… Jackrabbit, on which OAE is based, ships with a reasonable WebDAV Java client We can use this to make our basic CalDAV connections
  • 22. CalDavConnector.getCalendarUris() getCalendarUris() is our simplest method Just return URI wrapper objects for a user's whole calendar
  • 23. Complexity's a problem CalDAV and WebDAV are Complicated Not noob-friendly RFCs are almost the only docs available
  • 24. Impatience is a virtue Need lots of trial and error Redeploying a server takes 60-90s Running a test takes 2s 2s is too little time to get distracted
  • 25. Integration-test-driven development JUnit to the rescue! "Unit" tests that actually talk to a running Bedework server are the solution
  • 26. Write the test first… Before doing anything else I write a test: public class CalDavConnectorTest() { @Test() public void getCalendars() throws CalDavException { List<CalendarURI> uris = this.calDavConnector.getCalendarUris(); } }
  • 27. …Then make it pass Now we'll make it pass in the hackiest way imaginable public class CalDavConnector() { public void getCalendars() throws CalDavException { return new ArrayList<CalendarURI>(); } }
  • 28. …Iterate until done Keep adding implementation code until it does what's needed: PropFindMethod propFind = executeMethod(new PropFindMethod(this.userHome.toString())); MultiStatusResponse[] responses = propFind.getResponseBodyAsMultiStatus().getResponses(); for (MultiStatusResponse response : responses) { if (response.getHref().endsWith(".ics")) { Status[] status = response.getStatus(); if (status.length == 1 && status[0].getStatusCode() == HttpServletResponse.SC_OK) { DavPropertySet propSet = response.getProperties(HttpServletResponse.SC_OK); DavProperty etag = propSet.get(DavPropertyName.GETETAG); try { CalendarURI calUri = new CalendarURI( new URI(this.serverRoot, response.getHref(), false), etag.getValue().toString()); uris.add(calUri); } catch (ParseException pe) { throw new CalDavException("Invalid etag date", pe); } } } }
  • 29. Unit testing heresy These tests talk to and expect a running Bedework server This makes them heretical according to true unit test dogma Tests are not supposed to require an external server
  • 30. Gracefully failing test This test will succeed even if the Bedework server does not respond (because we catch the IOException): public class CalDavConnectorTest() { @Test() public void getCalendars() throws CalDavException { try { List<CalendarURI> uris = this.calDavConnector.getCalendarUris(); } catch (IOException ioe) { LOGGER.error("Trouble contacting server", ioe); } } }
  • 31. PROPFIND only gives you URIs… curl –u user:pass -X PROPFIND http://localhost:8080/ucaldav/user/300846/calendar/ HTTP/1.1 207 Multi-Status <?xml version="1.0" encoding="UTF-8" ?> <DAV:multistatus xmlns:DAV="DAV:" xmlns="urn:ietf:params:xml:ns:caldav" xmlns:ical="http://www.w3.org/2002/12/cal/ical#"> <DAV:response> <DAV:href>/ucaldav/user/300846/calendar/00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:href> <DAV:propstat> <DAV:prop> <DAV:getcontenttype>text/calendar; charset=UTF-8</DAV:getcontenttype> <DAV:getcontentlength>339</DAV:getcontentlength> <DAV:getlastmodified>Tue, 31 May 2011 21:23:32 +0000</DAV:getlastmodified> <DAV:displayname>00137ac8-1638-4193-ac3e-c172dfe3fd35.ics</DAV:displayname> <DAV:getetag>"20110531T212332Z-0"</DAV:getetag> <DAV:current-user-principal> <DAV:href>/ucaldav/principals/users/admin/</DAV:href> </DAV:current-user-principal> <DAV:resourcetype/> <DAV:getcontentlanguage>en</DAV:getcontentlanguage> <DAV:creationdate>20110531T212332Z</DAV:creationdate> </DAV:prop> <DAV:status>HTTP/1.1 200 ok</DAV:status> </DAV:propstat> </DAV:response> … more entries snipped … </DAV:multistatus>
  • 32. …which you then must get with a REPORT method curl –u user:pass -X REPORT http://localhost:8080/ucaldav/user/mtwain/calendar/ -d "<?xml version="1.0" encoding="UTF-8"?> <C:calendar-multiget xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:"> <D:prop> <D:getetag/> <C:calendar-data/> </D:prop> <D:href>http://localhost:8080/ucaldav/user/mtwain/calendar/d12ad881-5769-4d17-85ac-fa0a0196ec04.ics </D:href> </C:calendar-multiget>" (Note: Double quotes not escaped for clarity)
  • 33. REPORT method's response <DAV:response> <DAV:href>/ucaldav/user/mtwain/calendar/02bfa2cd-6c04-40f4-93da-896d54fc2987.ics</DAV:href> <DAV:propstat> <DAV:prop> <DAV:getetag>"20110602T211046Z-0"</DAV:getetag> <calendar-data><![CDATA[BEGIN:VCALENDAR PRODID://Bedework.org//BedeWork V3.7//EN VERSION:2.0 BEGIN:VTODO CATEGORIES:MyBerkeley-Required CATEGORIES:MyBerkeley-Archived CREATED:20110601T171120Z DESCRIPTION:test … [ snip ] ]]></calendar-data> </DAV:prop> <DAV:status>HTTP/1.1 200 ok</DAV:status> </DAV:propstat> </DAV:response>
  • 34. Hairy REPORT Syntax CalDAV's REPORT syntax is hairy XML Have to extend Jackrabbit's ReportInfo classes This blog entry by Ricardo Martin Camarero has very useful starter code: http://blogs.nologin.es/rickyepoderi/index.php?/archives/15-Introducing-CalDAV-Part-II.html
  • 35. REPORT is foundation for search REPORT methods with parameters let you search by date
  • 36. Bedework difficulties Bedework's search implementation spotty No support for search on X-props No support for search on CATEGORIES
  • 37. Filtering on myBerkeley server Due to Bedework bugs we're forced to do some filtering on the myBerkeley server side E.g. Required/Not Required fields
  • 38. Caching on myBerkeley server CalendarURI wrapper class URI: Locates calendar Etag: Uniquely identifies its contents (sort of like a SHA hash) Caching not implemented yet, but easy enough since all calendars are keyed by CalendarURI
  • 39. Implementing CalDavProxyServlet Also done with test-driven development Write tests against JSON files that contain servlet's expected inputs When servlet's finished, UI devs use those JSON files as an API reference
  • 40. Improvements to Nakamura's CalendarService Future work: Store and search calendars using Nakamura's CalendarService Add support for VTODO to CalendarService
  • 41. Beyond CalendarService Future: Create a full-blown CalDAV Calendar Provider component for Nakamura Store calendars externally, transparently refer to them within Nakamura code