org.opencastproject.scheduler.impl.CalendarGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.opencastproject.scheduler.impl.CalendarGenerator.java

Source

/**
 *  Copyright 2009, 2010 The Regents of the University of California
 *  Licensed 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://www.osedu.org/licenses/ECL-2.0
 *
 *  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.opencastproject.scheduler.impl;

import org.opencastproject.metadata.dublincore.DCMIPeriod;
import org.opencastproject.metadata.dublincore.DublinCore;
import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
import org.opencastproject.metadata.dublincore.DublinCoreValue;
import org.opencastproject.metadata.dublincore.EncodingSchemeUtils;
import org.opencastproject.security.api.UnauthorizedException;
import org.opencastproject.series.api.SeriesException;
import org.opencastproject.series.api.SeriesService;
import org.opencastproject.util.NotFoundException;

import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.ParameterList;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.parameter.Cn;
import net.fortuna.ical4j.model.parameter.Encoding;
import net.fortuna.ical4j.model.parameter.FmtType;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.parameter.XParameter;
import net.fortuna.ical4j.model.property.Attach;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.RelatedTo;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Version;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.Date;
import java.util.Properties;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * Create an iCalendar from the provided dublin core events.
 * 
 */
public class CalendarGenerator {
    /** Logging utility */
    private static final Logger logger = LoggerFactory.getLogger(CalendarGenerator.class);

    /** iCalendar */
    protected Calendar cal;
    /** Series service for Series DC retrieval */
    protected SeriesService seriesService;

    /**
     * Default constructor that creates a CalendarGenerator object
     * 
     * @param seriesService
     *          Series service for retrieving series Dublin Core (if event has one)
     */
    public CalendarGenerator(SeriesService seriesService) {
        cal = new Calendar();
        cal.getProperties().add(new ProdId("Opencast Matterhorn Calendar File 0.5"));
        cal.getProperties().add(Version.VERSION_2_0);
        cal.getProperties().add(CalScale.GREGORIAN);
        this.seriesService = seriesService;
    }

    /**
     * gets the iCalendar creates by this object.
     * 
     * @return the iCalendar
     */
    public Calendar getCalendar() {
        return cal;
    }

    /**
     * Sets an iCalender to work with
     * 
     * @param cal
     *          the iCalendar to set
     */
    public void setCalendar(Calendar cal) {
        this.cal = cal;
    }

    /**
     * Adds an SchedulerEvent as a new entry to this iCalendar
     * 
     * @param catalog
     *          {@link DublinCoreCatalog} of event
     * @param captureAgentMetadata
     *          properties for capture agent metadata
     * 
     * @return true if the event could be added.
     */
    public boolean addEvent(DublinCoreCatalog catalog, Properties captureAgentMetadata) {
        String eventId = catalog.getFirst(DublinCore.PROPERTY_IDENTIFIER);

        logger.debug("Creating iCaleandar VEvent from scheduled event '{}'", eventId);

        DCMIPeriod period = EncodingSchemeUtils
                .decodeMandatoryPeriod(catalog.getFirst(DublinCore.PROPERTY_TEMPORAL));
        if (!period.hasStart()) {
            logger.debug("Couldn't get startdate from event!");
            return false;
        }
        if (!period.hasEnd()) {
            logger.debug("Couldn't get enddate from event!");
            return false;
        }

        DateTime startDate = new DateTime(period.getStart());
        DateTime endDate = new DateTime(period.getEnd());
        if (endDate.before(new Date())) {
            logger.debug("Event has already passed, skipping!");
            return false;
        }
        startDate.setUtc(true);
        endDate.setUtc(true);
        String seriesID = null;

        VEvent event = new VEvent(startDate, endDate, catalog.getFirst(DublinCore.PROPERTY_TITLE));
        try {
            ParameterList pl = new ParameterList();
            for (DublinCoreValue creator : catalog.get(DublinCore.PROPERTY_CREATOR)) {
                pl.add(new Cn(creator.getValue()));
            }
            event.getProperties().add(new Uid(eventId));

            // TODO Organizer should be URI (email-address?) created fake address
            if (StringUtils.isNotEmpty(catalog.getFirst(DublinCore.PROPERTY_CREATOR))) {
                URI organizer = new URI("mailto",
                        catalog.getFirst(DublinCore.PROPERTY_CREATOR) + "@matterhorn.opencast", null);
                event.getProperties().add(new Organizer(pl, organizer));
            }
            if (StringUtils.isNotEmpty(catalog.getFirst(DublinCore.PROPERTY_DESCRIPTION))) {
                event.getProperties().add(new Description(catalog.getFirst(DublinCore.PROPERTY_DESCRIPTION)));
            }
            // location corresponds to spatial? or is location part of CA configuration?
            if (StringUtils.isNotEmpty(catalog.getFirst(DublinCore.PROPERTY_SPATIAL))) {
                event.getProperties().add(new Location(catalog.getFirst(DublinCore.PROPERTY_SPATIAL)));
            }
            if (StringUtils.isNotEmpty(catalog.getFirst(DublinCore.PROPERTY_IS_PART_OF))) {
                seriesID = catalog.getFirst(DublinCore.PROPERTY_IS_PART_OF);
                event.getProperties().add(new RelatedTo(seriesID));
            }

            ParameterList dcParameters = new ParameterList();
            dcParameters.add(new FmtType("application/xml"));
            dcParameters.add(Value.BINARY);
            dcParameters.add(Encoding.BASE64);
            dcParameters.add(new XParameter("X-APPLE-FILENAME", "episode.xml"));
            Attach metadataAttachment = new Attach(dcParameters, getDublinCoreAsString(catalog).getBytes("UTF-8"));
            event.getProperties().add(metadataAttachment);

            String seriesDC = getSeriesDublinCoreAsString(seriesID);
            if (seriesDC != null) {
                logger.debug("Attaching series {} information to event {}", seriesID, eventId);
                ParameterList sDcParameters = new ParameterList();
                sDcParameters.add(new FmtType("application/xml"));
                sDcParameters.add(Value.BINARY);
                sDcParameters.add(Encoding.BASE64);
                sDcParameters.add(new XParameter("X-APPLE-FILENAME", "series.xml"));
                Attach seriesAttachment = new Attach(sDcParameters, seriesDC.getBytes("UTF-8"));
                event.getProperties().add(seriesAttachment);
            } else {
                logger.debug("No series provided for event {}.", eventId);
            }

            ParameterList caParameters = new ParameterList();
            caParameters.add(new FmtType("application/text"));
            caParameters.add(Value.BINARY);
            caParameters.add(Encoding.BASE64);
            caParameters.add(new XParameter("X-APPLE-FILENAME", "org.opencastproject.capture.agent.properties"));
            Attach agentsAttachment = new Attach(caParameters,
                    getCaptureAgentPropertiesAsString(captureAgentMetadata).getBytes("UTF-8"));
            event.getProperties().add(agentsAttachment);

        } catch (Exception e) {
            logger.error("Unable to add event '{}' to recording calendar: {} ", eventId, e.getMessage());
            return false;
        }

        cal.getComponents().add(event);

        logger.debug("new VEvent = {} ", event.toString());
        return true;
    }

    /**
     * Returns provided Dublin Core as String or null of serialization fails.
     * 
     * @param catalog
     *          {@link DublinCoreCatalog} to be serialized
     * @return string representation of DC
     */
    private String getDublinCoreAsString(DublinCoreCatalog catalog) {
        try {
            Document doc = catalog.toXml();

            Source source = new DOMSource(doc);
            StringWriter stringWriter = new StringWriter();
            Result result = new StreamResult(stringWriter);
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.transform(source, result);

            return stringWriter.getBuffer().toString().trim();
        } catch (ParserConfigurationException e) {
            logger.error("Could not parse DublinCoreCatalog for Series: {}", e.getMessage());
        } catch (IOException e) {
            logger.error("Could not open DublinCoreCatalog for Series to parse it: {}", e.getMessage());
        } catch (TransformerException e) {
            logger.error("Could not transform DublinCoreCatalog for Series: {}", e.getMessage());
        }
        return null;
    }

    /**
     * Returns series DC associated with this event or null if {@link SeriesService} is not available or does not contain
     * entry for series with specified ID.
     * 
     * @param seriesID
     *          {@link DublinCoreCatalog} to be retrieved
     * @return DC serialized to string or null
     * @throws UnauthorizedException
     *           if the current user is not allowed to view this series
     * @throws NotFoundException
     *           if the series cannot be found
     */
    private String getSeriesDublinCoreAsString(String seriesID) throws UnauthorizedException, NotFoundException {
        if (StringUtils.isBlank(seriesID))
            return null;
        if (seriesService == null) {
            logger.warn("No SeriesService available");
            return null;
        }

        DublinCoreCatalog seriesDC;
        try {
            seriesDC = seriesService.getSeries(seriesID);
        } catch (SeriesException e) {
            logger.error("Error loading DublinCoreCatalog for series '{}': {}", seriesID, e.getMessage());
            return null;
        }

        return getDublinCoreAsString(seriesDC);
    }

    /**
     * Returns capture agent properties as string.
     * 
     * @param properties
     *          CA properties to be serialized to string
     * @return string representation of properties
     */
    private String getCaptureAgentPropertiesAsString(Properties properties) {
        StringWriter writer = new StringWriter();
        try {
            properties.store(writer, "Capture Agent specific data");
            return writer.toString();
        } catch (IOException e) {
            logger.error("Could not convert Capture Agent Data to String");
        }
        return null;
    }
}