Java tutorial
/* * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the * License at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sakaiproject.calendaring.api; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; import lombok.Setter; import lombok.extern.apachecommons.CommonsLog; import net.fortuna.ical4j.data.CalendarOutputter; import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.Property; import net.fortuna.ical4j.model.TimeZone; import net.fortuna.ical4j.model.TimeZoneRegistry; import net.fortuna.ical4j.model.TimeZoneRegistryFactory; import net.fortuna.ical4j.model.ValidationException; import net.fortuna.ical4j.model.component.VEvent; import net.fortuna.ical4j.model.component.VTimeZone; import net.fortuna.ical4j.model.parameter.Cn; import net.fortuna.ical4j.model.parameter.PartStat; import net.fortuna.ical4j.model.parameter.Role; import net.fortuna.ical4j.model.parameter.Rsvp; import net.fortuna.ical4j.model.property.*; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.sakaiproject.calendar.api.CalendarEvent; import org.sakaiproject.calendaring.logic.SakaiProxy; import org.sakaiproject.time.api.TimeRange; import org.sakaiproject.user.api.User; /** * Implementation of {@link ExternalCalendaringService} * * @author Steve Swinsburg (steve.swinsburg@gmail.com) * */ @CommonsLog public class ExternalCalendaringServiceImpl implements ExternalCalendaringService { /** * {@inheritDoc} */ public VEvent createEvent(CalendarEvent event) { return createEvent(event, null); } /** * {@inheritDoc} */ public VEvent createEvent(CalendarEvent event, List<User> attendees) { if (!isIcsEnabled()) { log.debug( "ExternalCalendaringService is disabled. Enable via calendar.ics.generation.enabled=true in sakai.properties"); return null; } //timezone. All dates are in GMT so we need to explicitly set that TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); TimeZone timezone = registry.getTimeZone("GMT"); VTimeZone tz = timezone.getVTimeZone(); //start and end date DateTime start = new DateTime(getStartDate(event.getRange()).getTime()); DateTime end = new DateTime(getEndDate(event.getRange()).getTime()); //create event incl title/summary VEvent vevent = new VEvent(start, end, event.getDisplayName()); //add timezone vevent.getProperties().add(tz.getTimeZoneId()); //add uid to event //could come from the vevent_uuid field in the calendar event, otherwise from event ID. String uuid = null; if (StringUtils.isNotBlank(event.getField("vevent_uuid"))) { uuid = event.getField("vevent_uuid"); } else { uuid = event.getId(); } vevent.getProperties().add(new Uid(uuid)); //add sequence to event //will come from the vevent_sequnece field in the calendar event, otherwise skip it String sequence = null; if (StringUtils.isNotBlank(event.getField("vevent_sequence"))) { sequence = event.getField("vevent_sequence"); vevent.getProperties().add(new Sequence(sequence)); } //add description to event vevent.getProperties().add(new Description(event.getDescription())); //add location to event vevent.getProperties().add(new Location(event.getLocation())); //add organiser to event if (StringUtils.isNotBlank(event.getCreator())) { String creatorEmail = sakaiProxy.getUserEmail(event.getCreator()); URI mailURI = createMailURI(creatorEmail); Cn commonName = new Cn(sakaiProxy.getUserDisplayName(event.getCreator())); Organizer organizer = new Organizer(mailURI); organizer.getParameters().add(commonName); vevent.getProperties().add(organizer); } //add attendees to event with 'required participant' role vevent = addAttendeesToEvent(vevent, attendees); //add URL to event, if present String url = null; if (StringUtils.isNotBlank(event.getField("vevent_url"))) { url = event.getField("vevent_url"); Url u = new Url(); try { u.setValue(url); vevent.getProperties().add(u); } catch (URISyntaxException e) { //it doesnt matter, ignore it } } if (log.isDebugEnabled()) { log.debug("VEvent:" + vevent); } return vevent; } /** * {@inheritDoc} */ public VEvent addAttendeesToEvent(VEvent vevent, List<User> attendees) { return addAttendeesToEventWithRole(vevent, attendees, Role.REQ_PARTICIPANT); } /** * {@inheritDoc} */ @Override public VEvent addChairAttendeesToEvent(VEvent vevent, List<User> attendees) { return addAttendeesToEventWithRole(vevent, attendees, Role.CHAIR); } /** * Adds attendees to an existing event with a given role * Common logic for addAttendeesToEvent and addChairAttendeestoEvent * * @param vevent the VEvent to add the attendess too * @param attendees list of Users that have been invited to the event * @param role the role with which to add each user * @return the VEvent for the given event or null if there was an error */ protected VEvent addAttendeesToEventWithRole(VEvent vevent, List<User> attendees, Role role) { if (!isIcsEnabled()) { log.debug( "ExternalCalendaringService is disabled. Enable via calendar.ics.generation.enabled=true in sakai.properties"); return null; } //add attendees to event with 'required participant' role if (attendees != null) { for (User u : attendees) { Attendee a = new Attendee(createMailURI(u.getEmail())); a.getParameters().add(role); a.getParameters().add(new Cn(u.getDisplayName())); a.getParameters().add(PartStat.ACCEPTED); a.getParameters().add(Rsvp.FALSE); vevent.getProperties().add(a); } } if (log.isDebugEnabled()) { log.debug("VEvent with attendees:" + vevent); } return vevent; } /** * {@inheritDoc} */ public VEvent cancelEvent(VEvent vevent) { if (!isIcsEnabled()) { log.debug( "ExternalCalendaringService is disabled. Enable via calendar.ics.generation.enabled=true in sakai.properties"); return null; } // You can only have one status so make sure we remove any previous ones. vevent.getProperties().removeAll(vevent.getProperties(Property.STATUS)); vevent.getProperties().add(Status.VEVENT_CANCELLED); // Must define a sequence for cancellations. If one was not defined when the event was created use 1 if (vevent.getProperties().getProperty(Property.SEQUENCE) == null) { vevent.getProperties().add(new Sequence("1")); } if (log.isDebugEnabled()) { log.debug("VEvent cancelled:" + vevent); } return vevent; } /** * {@inheritDoc} */ public Calendar createCalendar(List<VEvent> events) { return createCalendar(events, null); } /** * {@inheritDoc} */ public Calendar createCalendar(List<VEvent> events, String method) { if (!isIcsEnabled()) { log.debug( "ExternalCalendaringService is disabled. Enable via calendar.ics.generation.enabled=true in sakai.properties"); return null; } //setup calendar Calendar calendar = setupCalendar(method); //null check if (CollectionUtils.isEmpty(events)) { log.error("List of VEvents was null or empty, no calendar will be created."); return null; } //add vevents to calendar calendar.getComponents().addAll(events); //validate try { calendar.validate(true); } catch (ValidationException e) { e.printStackTrace(); return null; } if (log.isDebugEnabled()) { log.debug("Calendar:" + calendar); } return calendar; } /** * {@inheritDoc} */ public String toFile(Calendar calendar) { if (!isIcsEnabled()) { log.debug( "ExternalCalendaringService is disabled. Enable via calendar.ics.generation.enabled=true in sakai.properties"); return null; } //null check if (calendar == null) { log.error("Calendar is null, cannot generate ICS file."); return null; } String path = generateFilePath(UUID.randomUUID().toString()); //test file File file = new File(path); try { if (!file.createNewFile()) { log.error("Couldn't write file to: " + path); return null; } } catch (IOException e) { log.error("An error occurred trying to write file to: " + path + " : " + e.getClass() + " : " + e.getMessage()); return null; } //if cleanup enabled, mark for deletion when the JVM exits. if (sakaiProxy.isCleanupEnabled()) { file.deleteOnExit(); } FileOutputStream fout; try { fout = new FileOutputStream(file); CalendarOutputter outputter = new CalendarOutputter(); outputter.output(calendar, fout); fout.flush(); fout.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ValidationException e) { // TODO Auto-generated catch block e.printStackTrace(); } return path; } /** * {@inheritDoc} */ public boolean isIcsEnabled() { return sakaiProxy.isIcsEnabled(); } /** * Helper method to setup the standard parts of the calendar * @return */ private Calendar setupCalendar(String method) { String serverName = sakaiProxy.getServerName(); //setup calendar Calendar calendar = new Calendar(); calendar.getProperties().add(new ProdId("-//" + serverName + "//Sakai External Calendaring Service//EN")); calendar.getProperties().add(Version.VERSION_2_0); calendar.getProperties().add(CalScale.GREGORIAN); if (method != null) { calendar.getProperties().add(new Method(method)); } return calendar; } /** * Helper to extract the startDate of a TimeRange into a java.util.Calendar object. * @param range * @return */ private java.util.Calendar getStartDate(TimeRange range) { java.util.Calendar c = new GregorianCalendar(); c.setTimeInMillis(range.firstTime().getTime()); return c; } /** * Helper to extract the endDate of a TimeRange into a java.util.Calendar object. * @param range * @return */ private java.util.Calendar getEndDate(TimeRange range) { java.util.Calendar c = new GregorianCalendar(); c.setTimeInMillis(range.lastTime().getTime()); return c; } /** * Helper to create the name of the ICS file we are to write * @param filename * @return */ private String generateFilePath(String filename) { StringBuilder sb = new StringBuilder(); String base = sakaiProxy.getCalendarFilePath(); sb.append(base); //add slash if reqd if (!StringUtils.endsWith(base, File.separator)) { sb.append(File.separator); } sb.append(filename); sb.append(".ics"); return sb.toString(); } /** * Create a URI to be used for a person's email address that degrades nicely if one is not defined * @param email The email address as a string, can be empty or even <code>null</code> * @return the URI object */ private URI createMailURI(String email) { if (email == null || email.isEmpty()) { return URI.create("noemail"); } else { return URI.create("mailto:" + email); } } /** * init */ public void init() { log.info("init"); } @Setter private SakaiProxy sakaiProxy; }