View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.mediapackage;
23  
24  import static org.opencastproject.util.data.functions.Misc.chuck;
25  
26  import org.opencastproject.util.DateTimeSupport;
27  import org.opencastproject.util.XmlSafeParser;
28  
29  import org.apache.commons.lang3.StringUtils;
30  import org.codehaus.jettison.mapped.Configuration;
31  import org.codehaus.jettison.mapped.MappedNamespaceConvention;
32  import org.codehaus.jettison.mapped.MappedXMLStreamWriter;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  
36  import java.io.StringWriter;
37  import java.util.ArrayList;
38  import java.util.Collections;
39  import java.util.LinkedList;
40  import java.util.List;
41  
42  import javax.xml.bind.JAXBException;
43  import javax.xml.bind.Marshaller;
44  import javax.xml.parsers.DocumentBuilder;
45  import javax.xml.parsers.DocumentBuilderFactory;
46  import javax.xml.parsers.ParserConfigurationException;
47  import javax.xml.stream.XMLStreamException;
48  import javax.xml.stream.XMLStreamWriter;
49  
50  /**
51   * Convenience implementation that supports serializing and deserializing media packages.
52   */
53  public final class MediaPackageParser {
54  
55    /**
56     * Private constructor to prohibit instances of this static utility class.
57     */
58    private MediaPackageParser() {
59      // Nothing to do
60    }
61  
62    /**
63     * Serializes the media package to a string.
64     *
65     * @param mediaPackage
66     *          the media package
67     * @return the serialized media package
68     */
69    public static String getAsXml(MediaPackage mediaPackage) {
70      if (mediaPackage == null) {
71        throw new IllegalArgumentException("Mediapackage must not be null");
72      }
73      try {
74        Marshaller marshaller = MediaPackageImpl.context.createMarshaller();
75        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
76        StringWriter writer = new StringWriter();
77        marshaller.marshal(mediaPackage, writer);
78        return writer.toString();
79      } catch (JAXBException e) {
80        throw new IllegalStateException(e.getLinkedException() != null ? e.getLinkedException() : e);
81      }
82    }
83  
84    /**
85     * Serializes the media package to a JSON string.
86     *
87     * @param mediaPackage
88     *          the media package
89     * @return the serialized media package
90     */
91    public static String getAsJSON(MediaPackage mediaPackage) {
92      if (mediaPackage == null) {
93        throw new IllegalArgumentException("Mediapackage must not be null");
94      }
95      try {
96        Marshaller marshaller = MediaPackageImpl.context.createMarshaller();
97  
98        Configuration config = new Configuration();
99        config.setSupressAtAttributes(true);
100       MappedNamespaceConvention con = new MappedNamespaceConvention(config);
101       StringWriter writer = new StringWriter();
102       XMLStreamWriter xmlStreamWriter = new MappedXMLStreamWriter(con, writer) {
103         @Override
104         public void writeStartElement(String prefix, String local, String uri) throws XMLStreamException {
105           super.writeStartElement("", local, "");
106         }
107 
108         @Override
109         public void writeStartElement(String uri, String local) throws XMLStreamException {
110           super.writeStartElement("", local, "");
111         }
112 
113         @Override
114         public void setPrefix(String pfx, String uri) throws XMLStreamException {
115         }
116 
117         @Override
118         public void setDefaultNamespace(String uri) throws XMLStreamException {
119         }
120       };
121 
122       marshaller.marshal(mediaPackage, xmlStreamWriter);
123       return writer.toString();
124     } catch (JAXBException e) {
125       throw new IllegalStateException(e.getLinkedException() != null ? e.getLinkedException() : e);
126     }
127   }
128 
129   /** Serializes a media package to a {@link Document} without any further processing. */
130   public static Document getAsXmlDocument(MediaPackage mp) {
131     try {
132       final Marshaller marshaller = MediaPackageImpl.context.createMarshaller();
133       final Document doc = newDocument();
134       marshaller.marshal(mp, doc);
135       return doc;
136     } catch (JAXBException e) {
137       return chuck(e);
138     }
139   }
140 
141   /** Create a new DOM document. */
142   private static Document newDocument() {
143     final DocumentBuilderFactory docBuilderFactory = XmlSafeParser.newDocumentBuilderFactory();
144     docBuilderFactory.setNamespaceAware(true);
145     try {
146       return docBuilderFactory.newDocumentBuilder().newDocument();
147     } catch (ParserConfigurationException e) {
148       return chuck(e);
149     }
150   }
151 
152   /**
153    * Serializes the media package to a {@link org.w3c.dom.Document}.
154    * <p>
155    * todo Implementation is currently defective since it misses various properties. See
156    * http://opencast.jira.com/browse/MH-9489 Use {@link #getAsXmlDocument(MediaPackage)} instead if you do not need a
157    * serializer.
158    *
159    * @param mediaPackage
160    *          the mediapackage
161    * @param serializer
162    *          the serializer
163    * @return the serialized media package
164    * @throws MediaPackageException
165    *           if serializing fails
166    */
167   public static Document getAsXml(MediaPackage mediaPackage, MediaPackageSerializer serializer)
168           throws MediaPackageException {
169     DocumentBuilderFactory docBuilderFactory = XmlSafeParser.newDocumentBuilderFactory();
170     docBuilderFactory.setNamespaceAware(true);
171 
172     DocumentBuilder docBuilder = null;
173     try {
174       docBuilder = docBuilderFactory.newDocumentBuilder();
175     } catch (ParserConfigurationException e1) {
176       throw new MediaPackageException(e1);
177     }
178 
179     Document doc = docBuilder.newDocument();
180 
181     // Root element "mediapackage"
182     Element mpXml = doc.createElement("mediapackage");
183     doc.appendChild(mpXml);
184 
185     // Handle
186     if (mediaPackage.getIdentifier() != null) {
187       mpXml.setAttribute("id", mediaPackage.getIdentifier().toString());
188     }
189 
190     // Start time
191     if (mediaPackage.getDate() != null && mediaPackage.getDate().getTime() > 0) {
192       mpXml.setAttribute("start", DateTimeSupport.toUTC(mediaPackage.getDate().getTime()));
193     }
194 
195     // Duration
196     if (mediaPackage.getDuration() != null) {
197       mpXml.setAttribute("duration", Long.toString(mediaPackage.getDuration()));
198     }
199 
200     // Separate the media package members
201     List<Track> tracks = new ArrayList<Track>();
202     List<Attachment> attachments = new ArrayList<Attachment>();
203     List<Catalog> metadata = new ArrayList<Catalog>();
204     List<MediaPackageElement> others = new ArrayList<MediaPackageElement>();
205 
206     // Sort media package elements
207     for (MediaPackageElement e : mediaPackage.elements()) {
208       if (e instanceof Track) {
209         tracks.add((Track) e);
210       } else if (e instanceof Attachment) {
211         attachments.add((Attachment) e);
212       } else if (e instanceof Catalog) {
213         metadata.add((Catalog) e);
214       } else {
215         others.add(e);
216       }
217     }
218 
219     // Tracks
220     if (tracks.size() > 0) {
221       Element tracksNode = doc.createElement("media");
222       Collections.sort(tracks);
223       for (Track t : tracks) {
224         tracksNode.appendChild(t.toManifest(doc, serializer));
225       }
226       mpXml.appendChild(tracksNode);
227     }
228 
229     // Metadata
230     if (metadata.size() > 0) {
231       Element metadataNode = doc.createElement("metadata");
232       Collections.sort(metadata);
233       for (Catalog m : metadata) {
234         metadataNode.appendChild(m.toManifest(doc, serializer));
235       }
236       mpXml.appendChild(metadataNode);
237     }
238 
239     // Attachments
240     if (attachments.size() > 0) {
241       Element attachmentsNode = doc.createElement("attachments");
242       Collections.sort(attachments);
243       for (Attachment a : attachments) {
244         attachmentsNode.appendChild(a.toManifest(doc, serializer));
245       }
246       mpXml.appendChild(attachmentsNode);
247     }
248 
249     // Unclassified
250     if (others.size() > 0) {
251       Element othersNode = doc.createElement("unclassified");
252       Collections.sort(others);
253       for (MediaPackageElement e : others) {
254         othersNode.appendChild(e.toManifest(doc, serializer));
255       }
256       mpXml.appendChild(othersNode);
257     }
258 
259     return mpXml.getOwnerDocument();
260   }
261 
262   /**
263    * Parses the media package and returns its object representation.
264    *
265    * @param xml
266    *          the serialized media package
267    * @return the media package instance
268    * @throws MediaPackageException
269    *           if de-serializing the media package fails
270    */
271   public static MediaPackage getFromXml(String xml) throws MediaPackageException {
272     MediaPackageBuilder builder = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder();
273     return builder.loadFromXml(xml);
274   }
275 
276   /**
277    * Serializes media package list to a string.
278    *
279    * @param mediaPackages
280    *          media package list to be serialized
281    * @return serialized media package list
282    * @throws MediaPackageException
283    *           if serialization fails
284    */
285   public static String getArrayAsXml(List<MediaPackage> mediaPackages) throws MediaPackageException {
286     try {
287       StringBuilder builder = new StringBuilder();
288       if (mediaPackages.isEmpty()) {
289         return builder.toString();
290       }
291       builder.append(getAsXml(mediaPackages.get(0)));
292       for (int i = 1; i < mediaPackages.size(); i++) {
293         builder.append("###");
294         builder.append(getAsXml(mediaPackages.get(i)));
295       }
296       return builder.toString();
297     } catch (Exception e) {
298       if (e instanceof MediaPackageException) {
299         throw (MediaPackageException) e;
300       } else {
301         throw new MediaPackageException(e);
302       }
303     }
304   }
305 
306   /**
307    * Parses the serialized media package list.
308    *
309    * @param xml
310    *          String to be parsed
311    * @return parsed media package list
312    * @throws MediaPackageException
313    *           if de-serialization fails
314    */
315   public static List<MediaPackage> getArrayFromXml(String xml) throws MediaPackageException {
316     try {
317       List<MediaPackage> mediaPackages = new LinkedList<MediaPackage>();
318       if (StringUtils.isBlank(xml)) {
319         return mediaPackages;
320       }
321       String[] xmlArray = xml.split("###");
322       for (String xmlElement : xmlArray) {
323         mediaPackages.add(getFromXml(xmlElement.trim()));
324       }
325       return mediaPackages;
326     } catch (Exception e) {
327       if (e instanceof MediaPackageException) {
328         throw (MediaPackageException) e;
329       } else {
330         throw new MediaPackageException(e);
331       }
332     }
333   }
334 
335 }