Spring应用中如何通过iCal4j连接自建CalDAV(DaviCal)日历?
Hey there! I’ve worked with both DaviCal and iCal4j before, so let’s break down how to get your Spring app connected and performing basic operations. You’re right about caldav4j—it’s pretty much abandoned at this point, so iCal4j is the way to go (it’s actively maintained and has solid CalDAV support).
Step 1: Add Dependencies
First, add the necessary iCal4j modules to your project. You’ll need the core library plus the CalDAV extension, along with an HTTP client (ical4j uses Apache HttpClient under the hood):
Maven (pom.xml)
<dependencies> <dependency> <groupId>org.mnode.ical4j</groupId> <artifactId>ical4j</artifactId> <version>4.0.0</version> <!-- Check for the latest stable version --> </dependency> <dependency> <groupId>org.mnode.ical4j</groupId> <artifactId>ical4j-caldav</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency> </dependencies>
Gradle (build.gradle)
dependencies { implementation 'org.mnode.ical4j:ical4j:4.0.0' implementation 'org.mnode.ical4j:ical4j-caldav:4.0.0' implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1' }
Step 2: Configure CalDAV Client & Authentication
DaviCal relies on HTTP Basic Authentication, so we’ll set up a reusable client bean in Spring. Avoid hardcoding credentials in production—use environment variables or Spring’s config support instead.
import org.mnode.ical4j.caldav.CalDavClient; import org.mnode.ical4j.caldav.CalDavSession; import org.mnode.ical4j.caldav.session.BasicAuthSession; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CalDavConfig { @Value("${davical.base-url}") private String baseUrl; @Value("${davical.username}") private String username; @Value("${davical.password}") private String password; @Bean public CalDavClient calDavClient() { // Set up credentials for basic auth UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); // Build HTTP client with auth enabled CloseableHttpClient httpClient = HttpClients.custom() .setDefaultCredentialsProvider(provider -> { provider.setCredentials(org.apache.hc.client5.http.auth.AUTH.WWW_RESP, null, credentials); }) .build(); // Initialize CalDAV session and client CalDavSession session = new BasicAuthSession(httpClient, baseUrl); return new CalDavClient(session); } }
Step 3: Verify Connection & Fetch Calendars
Once the client is set up, you can fetch the user’s calendar collections to confirm the connection works. Here’s a service method example:
import org.mnode.ical4j.caldav.CalDavClient; import org.mnode.ical4j.caldav.response.CalendarHome; import org.springframework.stereotype.Service; @Service public class DaviCalService { private final CalDavClient calDavClient; public DaviCalService(CalDavClient calDavClient) { this.calDavClient = calDavClient; } public void listUserCalendars() throws Exception { // Fetch the user's calendar home (DaviCal uses this path structure) CalendarHome calendarHome = calDavClient.getCalendarHome(username); // Print out available calendars calendarHome.getCalendars().forEach(calendar -> { System.out.println("Calendar Name: " + calendar.getName()); System.out.println("Calendar URL: " + calendar.getUrl()); }); } }
Step 4: Create an Event in DaviCal
To add an event, build an iCal4j Calendar object and push it to your target calendar URL:
import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.component.VEvent; import net.fortuna.ical4j.model.property.*; import org.mnode.ical4j.caldav.CalendarCollection; import org.springframework.stereotype.Service; import java.net.URI; import java.time.ZonedDateTime; import java.util.UUID; @Service public DaviCalEventService { private final CalDavClient calDavClient; public DaviCalEventService(CalDavClient calDavClient) { this.calDavClient = calDavClient; } public void createEvent(String calendarUrl, String summary, ZonedDateTime start, ZonedDateTime end) throws Exception { // Get the target calendar collection CalendarCollection calendar = calDavClient.getCalendarCollection(URI.create(calendarUrl)); // Build the event with start/end times and summary VEvent event = new VEvent( new DateTime(start.toInstant()), new DateTime(end.toInstant()), summary ); // Add mandatory properties (required for RFC compliance) event.getProperties().add(new Uid(UUID.randomUUID().toString())); event.getProperties().add(new DtStamp(new DateTime())); event.getProperties().add(new Organizer("mailto:" + username + "@your-domain.com")); // Wrap the event in a calendar object Calendar ical = new Calendar(); ical.getProperties().add(new ProdId("-//Your Spring App//EN")); ical.getProperties().add(Version.VERSION_2_0); ical.getProperties().add(CalScale.GREGORIAN); ical.getComponents().add(event); // Push the event to DaviCal calendar.add(ical); } }
Key DaviCal Compatibility Tips
- URL Structure: DaviCal’s calendar paths follow the pattern
http://your-server/caldav.php/your-username/your-calendar/—use thelistUserCalendars()method to get exact URLs. - ETag Handling: For updates/deletes, fetch the event’s ETag first to prevent conflicts. Use
calendar.getEvents()to retrieve existing events with their ETags. - RFC Compliance: DaviCal is strict about standards, so ensure all mandatory iCal properties (like
UID,DTSTAMP) are included in your events.
That should get you up and running with connecting to DaviCal and performing core operations. Let me know if you hit snags with more advanced features like user management or recurring events!
内容的提问来源于stack exchange,提问作者Ihor Biedin

