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      try {
73        Marshaller marshaller = MediaPackageImpl.context.createMarshaller();
74        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
75        StringWriter writer = new StringWriter();
76        marshaller.marshal(mediaPackage, writer);
77        return writer.toString();
78      } catch (JAXBException e) {
79        throw new IllegalStateException(e.getLinkedException() != null ? e.getLinkedException() : e);
80      }
81    }
82  
83    /**
84     * Serializes the media package to a JSON string.
85     *
86     * @param mediaPackage
87     *          the media package
88     * @return the serialized media package
89     */
90    public static String getAsJSON(MediaPackage mediaPackage) {
91      if (mediaPackage == null) {
92        throw new IllegalArgumentException("Mediapackage must not be null");
93      }
94      try {
95        Marshaller marshaller = MediaPackageImpl.context.createMarshaller();
96  
97        Configuration config = new Configuration();
98        config.setSupressAtAttributes(true);
99        MappedNamespaceConvention con = new MappedNamespaceConvention(config);
100       StringWriter writer = new StringWriter();
101       XMLStreamWriter xmlStreamWriter = new MappedXMLStreamWriter(con, writer) {
102         @Override
103         public void writeStartElement(String prefix, String local, String uri) throws XMLStreamException {
104           super.writeStartElement("", local, "");
105         }
106 
107         @Override
108         public void writeStartElement(String uri, String local) throws XMLStreamException {
109           super.writeStartElement("", local, "");
110         }
111 
112         @Override
113         public void setPrefix(String pfx, String uri) throws XMLStreamException {
114         }
115 
116         @Override
117         public void setDefaultNamespace(String uri) throws XMLStreamException {
118         }
119       };
120 
121       marshaller.marshal(mediaPackage, xmlStreamWriter);
122       return writer.toString();
123     } catch (JAXBException e) {
124       throw new IllegalStateException(e.getLinkedException() != null ? e.getLinkedException() : e);
125     }
126   }
127 
128   /** Serializes a media package to a {@link Document} without any further processing. */
129   public static Document getAsXmlDocument(MediaPackage mp) {
130     try {
131       final Marshaller marshaller = MediaPackageImpl.context.createMarshaller();
132       final Document doc = newDocument();
133       marshaller.marshal(mp, doc);
134       return doc;
135     } catch (JAXBException e) {
136       return chuck(e);
137     }
138   }
139 
140   /** Create a new DOM document. */
141   private static Document newDocument() {
142     final DocumentBuilderFactory docBuilderFactory = XmlSafeParser.newDocumentBuilderFactory();
143     docBuilderFactory.setNamespaceAware(true);
144     try {
145       return docBuilderFactory.newDocumentBuilder().newDocument();
146     } catch (ParserConfigurationException e) {
147       return chuck(e);
148     }
149   }
150 
151   /**
152    * Serializes the media package to a {@link org.w3c.dom.Document}.
153    * <p>
154    * todo Implementation is currently defective since it misses various properties. See
155    * http://opencast.jira.com/browse/MH-9489 Use {@link #getAsXmlDocument(MediaPackage)} instead if you do not need a
156    * serializer.
157    *
158    * @param mediaPackage
159    *          the mediapackage
160    * @param serializer
161    *          the serializer
162    * @return the serialized media package
163    * @throws MediaPackageException
164    *           if serializing fails
165    */
166   public static Document getAsXml(MediaPackage mediaPackage, MediaPackageSerializer serializer)
167           throws MediaPackageException {
168     DocumentBuilderFactory docBuilderFactory = XmlSafeParser.newDocumentBuilderFactory();
169     docBuilderFactory.setNamespaceAware(true);
170 
171     DocumentBuilder docBuilder = null;
172     try {
173       docBuilder = docBuilderFactory.newDocumentBuilder();
174     } catch (ParserConfigurationException e1) {
175       throw new MediaPackageException(e1);
176     }
177 
178     Document doc = docBuilder.newDocument();
179 
180     // Root element "mediapackage"
181     Element mpXml = doc.createElement("mediapackage");
182     doc.appendChild(mpXml);
183 
184     // Handle
185     if (mediaPackage.getIdentifier() != null)
186       mpXml.setAttribute("id", mediaPackage.getIdentifier().toString());
187 
188     // Start time
189     if (mediaPackage.getDate() != null && mediaPackage.getDate().getTime() > 0)
190       mpXml.setAttribute("start", DateTimeSupport.toUTC(mediaPackage.getDate().getTime()));
191 
192     // Duration
193     if (mediaPackage.getDuration() != null)
194       mpXml.setAttribute("duration", Long.toString(mediaPackage.getDuration()));
195 
196     // Separate the media package members
197     List<Track> tracks = new ArrayList<Track>();
198     List<Attachment> attachments = new ArrayList<Attachment>();
199     List<Catalog> metadata = new ArrayList<Catalog>();
200     List<MediaPackageElement> others = new ArrayList<MediaPackageElement>();
201 
202     // Sort media package elements
203     for (MediaPackageElement e : mediaPackage.elements()) {
204       if (e instanceof Track)
205         tracks.add((Track) e);
206       else if (e instanceof Attachment)
207         attachments.add((Attachment) e);
208       else if (e instanceof Catalog)
209         metadata.add((Catalog) e);
210       else
211         others.add(e);
212     }
213 
214     // Tracks
215     if (tracks.size() > 0) {
216       Element tracksNode = doc.createElement("media");
217       Collections.sort(tracks);
218       for (Track t : tracks) {
219         tracksNode.appendChild(t.toManifest(doc, serializer));
220       }
221       mpXml.appendChild(tracksNode);
222     }
223 
224     // Metadata
225     if (metadata.size() > 0) {
226       Element metadataNode = doc.createElement("metadata");
227       Collections.sort(metadata);
228       for (Catalog m : metadata) {
229         metadataNode.appendChild(m.toManifest(doc, serializer));
230       }
231       mpXml.appendChild(metadataNode);
232     }
233 
234     // Attachments
235     if (attachments.size() > 0) {
236       Element attachmentsNode = doc.createElement("attachments");
237       Collections.sort(attachments);
238       for (Attachment a : attachments) {
239         attachmentsNode.appendChild(a.toManifest(doc, serializer));
240       }
241       mpXml.appendChild(attachmentsNode);
242     }
243 
244     // Unclassified
245     if (others.size() > 0) {
246       Element othersNode = doc.createElement("unclassified");
247       Collections.sort(others);
248       for (MediaPackageElement e : others) {
249         othersNode.appendChild(e.toManifest(doc, serializer));
250       }
251       mpXml.appendChild(othersNode);
252     }
253 
254     return mpXml.getOwnerDocument();
255   }
256 
257   /**
258    * Parses the media package and returns its object representation.
259    *
260    * @param xml
261    *          the serialized media package
262    * @return the media package instance
263    * @throws MediaPackageException
264    *           if de-serializing the media package fails
265    */
266   public static MediaPackage getFromXml(String xml) throws MediaPackageException {
267     MediaPackageBuilder builder = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder();
268     return builder.loadFromXml(xml);
269   }
270 
271   /**
272    * Serializes media package list to a string.
273    *
274    * @param mediaPackages
275    *          media package list to be serialized
276    * @return serialized media package list
277    * @throws MediaPackageException
278    *           if serialization fails
279    */
280   public static String getArrayAsXml(List<MediaPackage> mediaPackages) throws MediaPackageException {
281     try {
282       StringBuilder builder = new StringBuilder();
283       if (mediaPackages.isEmpty())
284         return builder.toString();
285       builder.append(getAsXml(mediaPackages.get(0)));
286       for (int i = 1; i < mediaPackages.size(); i++) {
287         builder.append("###");
288         builder.append(getAsXml(mediaPackages.get(i)));
289       }
290       return builder.toString();
291     } catch (Exception e) {
292       if (e instanceof MediaPackageException) {
293         throw (MediaPackageException) e;
294       } else {
295         throw new MediaPackageException(e);
296       }
297     }
298   }
299 
300   /**
301    * Parses the serialized media package list.
302    *
303    * @param xml
304    *          String to be parsed
305    * @return parsed media package list
306    * @throws MediaPackageException
307    *           if de-serialization fails
308    */
309   public static List<MediaPackage> getArrayFromXml(String xml) throws MediaPackageException {
310     try {
311       List<MediaPackage> mediaPackages = new LinkedList<MediaPackage>();
312       if (StringUtils.isBlank(xml))
313         return mediaPackages;
314       String[] xmlArray = xml.split("###");
315       for (String xmlElement : xmlArray) {
316         mediaPackages.add(getFromXml(xmlElement.trim()));
317       }
318       return mediaPackages;
319     } catch (Exception e) {
320       if (e instanceof MediaPackageException) {
321         throw (MediaPackageException) e;
322       } else {
323         throw new MediaPackageException(e);
324       }
325     }
326   }
327 
328 }