Java tutorial
/** * 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.capture.impl; import org.opencastproject.capture.CaptureParameters; import org.opencastproject.capture.admin.api.RecordingState; import org.opencastproject.capture.api.AgentRecording; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.mediapackage.MediaPackageBuilderFactory; import org.opencastproject.mediapackage.MediaPackageElement; import org.opencastproject.mediapackage.MediaPackageElements; import org.opencastproject.mediapackage.MediaPackageException; import org.opencastproject.mediapackage.MediaPackageImpl; import org.opencastproject.mediapackage.MediaPackageParser; import org.opencastproject.mediapackage.identifier.IdImpl; import org.opencastproject.util.ConfigurationException; import org.opencastproject.util.XProperties; import org.apache.commons.io.FileUtils; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; import java.util.TimeZone; /** * This class is a container for the properties relating a certain recording -- a set of Properties and a MediaPackage * with all the metadata/attachments/etc. associated */ public class RecordingImpl implements AgentRecording, Serializable { private static final long serialVersionUID = 1L; private static transient Logger logger = LoggerFactory.getLogger(RecordingImpl.class); /** Date formatter for the metadata file */ private static transient SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS'Z'"); /** Unique identifier for this ID */ protected String id = null; /** Directory in the filesystem where the files related with this recording are */ protected File baseDir = null; /** The recording's state. Defined in {@code RecordingState}. */ protected String state = RecordingState.UNKNOWN; /** * The time at which the recording last checked in with this service. Note that this is an absolute timestamp (ie, * milliseconds since 1970) rather than a relative timestamp (ie, it's been 3000 ms since it last checked in). */ protected Long lastHeardFrom; /** Keeps the properties associated with this recording */ protected XProperties props = null; /** The MediaPackage containing all the metadata/attachments/any file related with this recording */ protected transient MediaPackage mPkg = null; /** * Constructs a RecordingImpl object using the Properties and MediaPackage provided * * @param Xproperties * The {@code XProperties} object associated to this recording * @param mp * The {@code MediaPackage} with this recording files * @throws IOException * If the base directory could not be fetched */ public RecordingImpl(MediaPackage mp, XProperties properties) throws IOException { // Stores the MediaPackage this.mPkg = mp; if (properties != null) { this.props = (XProperties) properties.clone(); } else { logger.warn("Properties parameter was null, this recording will be in a very weird state!"); } // If the mediapackage is null create a new one if (mPkg == null) { try { mPkg = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew(); } catch (ConfigurationException e) { throw new RuntimeException("ConfigurationException building default mediapackage!", e); } catch (MediaPackageException e) { throw new RuntimeException("MediaPackageException building default mediapackage!", e); } } determineRootURLandID(); mPkg.setIdentifier(new IdImpl(id)); // Setup the root capture dir, also make sure that it exists. if (!baseDir.exists()) { try { FileUtils.forceMkdir(baseDir); } catch (IOException e) { logger.error("IOException creating required directory {}.", baseDir.toString()); // setRecordingState(recordingID, RecordingState.CAPTURE_ERROR); throw e; } // Should have been created. Let's make sure of that. if (!baseDir.exists()) { logger.error("Unable to start capture, could not create required directory {}.", baseDir.toString()); // setRecordingState(recordingID, RecordingState.CAPTURE_ERROR); throw new IOException("Unable to create base directory"); } } // Write out the metadata file needed by the core if it's not present // TODO: make this a constant? File metadataFile = new File(baseDir, "episode.xml"); if (!metadataFile.exists()) { FileWriter out = new FileWriter(metadataFile); out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); out.write("<dublincore xmlns=\"http://www.opencastproject.org/xsd/1.0/dublincore/\"" + " xmlns:dcterms=\"http://purl.org/dc/terms/\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"); out.write("<dcterms:created xsi:type=\"dcterms:W3CDTF\">" + formatDate(new Date()) + "</dcterms:created>"); out.write("<dcterms:identifier>" + id + "</dcterms:identifier>"); out.write("<dcterms:title>" + id + "</dcterms:title>"); out.write("</dublincore>"); out.flush(); out.close(); mPkg.add(metadataFile.toURI(), MediaPackageElement.Type.Catalog, MediaPackageElements.EPISODE); } } /** * Determines the root URL and ID from the recording's properties //TODO: What if the properties object contains a * character in the recording id or root url fields that is invalid for the filesystem? * * @throws IOException */ private void determineRootURLandID() throws IOException { if (props == null) { logger.info( "Properties are null for recording, guessing that the root capture dir is java.io.tmpdir..."); props = new XProperties(); props.setProperty(CaptureParameters.CAPTURE_FILESYSTEM_CAPTURE_CACHE_URL, System.getProperty("java.io.tmpdir")); } // Figures out where captureDir lives if (this.props.containsKey(CaptureParameters.RECORDING_ROOT_URL)) { baseDir = new File(props.getProperty(CaptureParameters.RECORDING_ROOT_URL)); if (props.containsKey(CaptureParameters.RECORDING_ID)) { // In this case they've set both the root URL and the recording ID, so we're done. id = props.getProperty(CaptureParameters.RECORDING_ID); } else { // In this case they've set the root URL, but not the recording ID. Get the id from that url instead then. logger.debug("{} was set, but not {}.", CaptureParameters.RECORDING_ROOT_URL, CaptureParameters.RECORDING_ID); id = new File(props.getProperty(CaptureParameters.RECORDING_ROOT_URL)).getName(); props.put(CaptureParameters.RECORDING_ID, id); } } else { File cacheDir = new File(props.getProperty(CaptureParameters.CAPTURE_FILESYSTEM_CAPTURE_CACHE_URL)); // If there is a recording ID use it, otherwise it's unscheduled so just grab a timestamp if (props.containsKey(CaptureParameters.RECORDING_ID)) { id = props.getProperty(CaptureParameters.RECORDING_ID); baseDir = new File(cacheDir, id); } else { // Unscheduled capture, use a timestamp value instead id = "Unscheduled-" + props.getProperty(CaptureParameters.AGENT_NAME) + "-" + System.currentTimeMillis(); props.setProperty(CaptureParameters.RECORDING_ID, id); baseDir = new File(cacheDir, id); } props.put(CaptureParameters.RECORDING_ROOT_URL, baseDir.getCanonicalPath()); } } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#getProperties() */ public XProperties getProperties() { return props; } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#setProperties(java.util.Properties) */ public void setProps(XProperties props) { this.props = props; } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#setProperties(java.util.Properties) */ public void setProperties(Properties props) { // Preserve the bundle context between property lists BundleContext ctx = null; if (this.props != null) { ctx = this.props.getBundleContext(); } this.props = new XProperties(); this.props.setBundleContext(ctx); for (String key : props.stringPropertyNames()) { props.put(key, props.getProperty(key)); } } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#getMediaPackage() */ public MediaPackage getMediaPackage() { return mPkg; } /** * {@inheritDoc} * * @see org.opencastproject.capture.admin.api.Recording#getID() */ public String getID() { return id; } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#getDir() */ public File getBaseDir() { return baseDir; } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#getProperty(java.lang.String) */ public String getProperty(String key) { return props.getProperty(key); } /** * {@inheritDoc} * * @see org.opencastproject.capture.api.AgentRecording#setProperty(java.lang.String, java.lang.String) */ public String setProperty(String key, String value) { return (String) props.setProperty(key, value); } /** * {@inheritDoc} * * @see org.opencastproject.capture.admin.api.Recording#setState(java.lang.String) */ public void setState(String state) { if (state == null) { this.state = RecordingState.UNKNOWN; } else { this.state = state; } lastHeardFrom = System.currentTimeMillis(); } /** * {@inheritDoc} * * @see org.opencastproject.capture.admin.api.Recording#getState() */ public String getState() { return state; } /** * {@inheritDoc} * * @see org.opencastproject.capture.admin.api.Recording#getLastCheckinTime() */ public Long getLastCheckinTime() { return lastHeardFrom; } /** * Formats a Date object to UTC time and according to the dublin core rules for dcterms:created * * @param d * The Date to format * @return The formatted Date */ private static synchronized String formatDate(Date d) { sdf.setTimeZone(TimeZone.getTimeZone("UTC")); return sdf.format(d); } /** * Overrides the default serialization behaviour. This method writes the mediapackage to the mediapackage.xml file in * the base directory of this capture * * @param out * The ObjectOutputStream for the serialization * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); try { MediaPackageParser.getAsXml(mPkg, out, true); } catch (MediaPackageException e) { logger.error("Unable to write mediapackage to disk! Error was: {}.", e); } } /** * Overrides the default serialization behaviour. This method reads the mediapackage from the mediapackage.xml file in * the base directory of this capture * * @param in * The ObjectInputStream for the serialization * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); try { mPkg = MediaPackageImpl.valueOf(in); } catch (MediaPackageException e) { logger.error("Unable to read mediapackage from disk! Error was: {}.", e); } } }