/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC
* LICENSE as published by the Free Software Foundation under
* version 3 of the License
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* LESSER GENERAL PUBLIC LICENSE v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.ovfmanager.ovf.xml;
import java.io.InputStream;
import java.io.OutputStream;
import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import org.dmtf.schemas.ovf.envelope._1.AbicloudNetworkType;
import org.dmtf.schemas.ovf.envelope._1.AnnotationSectionType;
import org.dmtf.schemas.ovf.envelope._1.DeploymentOptionSectionType;
import org.dmtf.schemas.ovf.envelope._1.DiskSectionType;
import org.dmtf.schemas.ovf.envelope._1.EnvelopeType;
import org.dmtf.schemas.ovf.envelope._1.EulaSectionType;
import org.dmtf.schemas.ovf.envelope._1.InstallSectionType;
import org.dmtf.schemas.ovf.envelope._1.NetworkSectionType;
import org.dmtf.schemas.ovf.envelope._1.OperatingSystemSectionType;
import org.dmtf.schemas.ovf.envelope._1.ProductSectionType;
import org.dmtf.schemas.ovf.envelope._1.ResourceAllocationSectionType;
import org.dmtf.schemas.ovf.envelope._1.SectionType;
import org.dmtf.schemas.ovf.envelope._1.StartupSectionType;
import org.dmtf.schemas.ovf.envelope._1.VirtualHardwareSectionType;
import org.dmtf.schemas.ovf.envelope._1.VirtualSystemType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.abiquo.ovfmanager.ovf.exceptions.XMLException;
/**
* Use JAXB to bind standard OVF-envelope and OVFIndex objects into/from XML documents.<br/>
*
* @see Stax2Factory, where Woodstox is used as StAX implementation of the underlying XML parser.
* @see ANT build file "jaxb" target, where the binding classes are generated.
* @TODO XMLStreamWriter can specify the encoding ... useful ?
* @TODO close writers even when an exception occurs.
*/
public class OVFSerializer
{
private final static Logger logger = LoggerFactory.getLogger(OVFSerializer.class);
/** Define the allowed objects to be binded from/into the OVF-envelope schema definition. */
private final JAXBContext contextEnvelope;
/** Generated factory to create XML elements in OVF-envelope name space. */
private final org.dmtf.schemas.ovf.envelope._1.ObjectFactory factoryEnvelop;
/** Determines if the marshalling process to format the XML document. */
private boolean formatOutput = true;
/** The singleton instance. */
private static OVFSerializer instance;
/** Used to bind an envelope into a DOM document. **/
private static DocumentBuilderFactory docBuilderFact = DocumentBuilderFactory.newInstance();
// ** Define the allowed objects to be binded form/into the OVFIndex schema definition. */
// private final JAXBContext contextIndex;
// /** Generated factory to create XML elements on OVFIndex name space. */
// private final com.abiquo.repositoryspace.ObjectFactory factoryIndex;
/**
* Get the OVFSerializer singelton instance.
*
* @return the OVFSerializer instance or null if it can not be created.
*/
public static OVFSerializer getInstance()
{
if (instance == null)
{
try
{
instance = new OVFSerializer();
}
catch (JAXBException e)
{
logger.error("OVFSerializer instance can not be created ", e);
}
}
return instance;
}
/**
* Instantiate a new OVFSerializer.
*
* @throws JAXBException, if some JAXB context can not be created.
*/
private OVFSerializer() throws JAXBException
{
contextEnvelope = JAXBContext.newInstance(EnvelopeType.class);
factoryEnvelop = new org.dmtf.schemas.ovf.envelope._1.ObjectFactory();
}
/**
* Wrap into an DOM document the provided OVF envelope.
*
* @param envelope, the OVF envelope the be wrapped
* @param isNamespaceAware, determine if the created document is XML name space aware.
* @return a DOM document containing the provided OVF envelope. TODO rename to "toDocument"
*/
public Document bindToDocument(final EnvelopeType envelope, final boolean isNamespaceAware)
throws ParserConfigurationException, JAXBException
{
// Now serialize the Java Content tree back to XML data
docBuilderFact.setNamespaceAware(isNamespaceAware);
DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Binder<Node> binder = contextEnvelope.createBinder();
binder.marshal(toJAXBElement(envelope), doc);
return doc;
}
/**
* Read an expected OVF-envelope form the provided source.
*
* @param is, the input stream source where read XML documents.
* @return the OVF-envelope read from source.
* @throws XMLException, if it is not an envelope type or any XML problem.
*/
public EnvelopeType readXMLEnvelope(final InputStream is) throws XMLException
{
XMLStreamReader reader = null;
Unmarshaller unmarshall;
JAXBElement<EnvelopeType> jaxbEnvelope;
try
{
reader = Stax2Factory.getStreamReaderFactory().createXMLStreamReader(is);
unmarshall = contextEnvelope.createUnmarshaller();
unmarshall.setSchema(null); // never try to validate an OVF
jaxbEnvelope = unmarshall.unmarshal(reader, EnvelopeType.class);
}
catch (JAXBException ea)
{
throw new XMLException(ea);
}
catch (XMLStreamException ex)
{
throw new XMLException(ex);
}
finally
{
try
{
if (reader != null)
{
reader.close();
}
}
catch (XMLStreamException e)
{
e.printStackTrace();
}
}
return jaxbEnvelope.getValue();
}
/** Wrap into an JAXBElement the provided OVF envelope. **/
public JAXBElement<EnvelopeType> toJAXBElement(final EnvelopeType envelope)
{
return factoryEnvelop.createEnvelope(envelope);
}
/** Wrap into an JAXBElement the provided OVF envelope section. **/
@SuppressWarnings("unchecked")
public <T extends SectionType> JAXBElement<T> toJAXBElement(final T section)
{
JAXBElement<T> jaxB;
if (section instanceof DeploymentOptionSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop
.createDeploymentOptionSection((DeploymentOptionSectionType) section);
}
else if (section instanceof StartupSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop.createStartupSection((StartupSectionType) section);
}
else if (section instanceof InstallSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop.createInstallSection((InstallSectionType) section);
}
else if (section instanceof OperatingSystemSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop
.createOperatingSystemSection((OperatingSystemSectionType) section);
}
else if (section instanceof AnnotationSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop
.createAnnotationSection((AnnotationSectionType) section);
}
else if (section instanceof NetworkSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop.createNetworkSection((NetworkSectionType) section);
}
else if (section instanceof EulaSectionType)
{
jaxB = (JAXBElement<T>) factoryEnvelop.createEulaSection((EulaSectionType) section);
}
else if (section instanceof ProductSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop.createProductSection((ProductSectionType) section);
}
else if (section instanceof VirtualHardwareSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop
.createVirtualHardwareSection((VirtualHardwareSectionType) section);
}
else if (section instanceof ResourceAllocationSectionType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop
.createResourceAllocationSection((ResourceAllocationSectionType) section);
}
else if (section instanceof DiskSectionType)
{
jaxB = (JAXBElement<T>) factoryEnvelop.createDiskSection((DiskSectionType) section);
}
else if (section instanceof AbicloudNetworkType)
{
jaxB =
(JAXBElement<T>) factoryEnvelop
.createCustomNetworkSection((AbicloudNetworkType) section);
}
else
{
// TODO throws an exception for invalid section
jaxB = null;
}
return jaxB;
}
/**
* Creates an XML document representing the provided OVF-envelop and write it to output stream.
*
* @param envelope, the object to be binded into an XML document.
* @param os, the destination of the XML document.
* @throws XMLException, any XML problem.
*/
public void writeXML(final EnvelopeType envelope, final OutputStream os) throws XMLException
{
XMLStreamWriter writer = null;
Marshaller marshall;
try
{
writer = Stax2Factory.getStreamWriterFactory().createXMLStreamWriter(os);
marshall = contextEnvelope.createMarshaller();
if (formatOutput)
{
marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
}
JAXBElement<EnvelopeType> jaxbElem = factoryEnvelop.createEnvelope(envelope);
marshall.marshal(jaxbElem, writer);
}
catch (JAXBException ea)
{
throw new XMLException(ea);
}
catch (XMLStreamException ex)
{
throw new XMLException(ex);
}
finally
{
if (writer != null)
{
try
{
writer.close();
}
catch (XMLStreamException e)
{
e.printStackTrace();
}
}
}
}
/**
* Creates an XML document representing the provided OVF-section and write it to output stream.
*
* @param envelope, the object to be binded into an XML document.
* @param os, the destination of the XML document.
* @throws XMLException, any XML problem.
*/
public void writeXML(final SectionType section, final OutputStream os) throws XMLException
{
XMLStreamWriter writer;
Marshaller marshall;
try
{
writer = Stax2Factory.getStreamWriterFactory().createXMLStreamWriter(os);
marshall = contextEnvelope.createMarshaller();
if (formatOutput)
{
marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
}
marshall.marshal(toJAXBElement(section), writer);
writer.close();
}
catch (JAXBException ea)
{
throw new XMLException(ea);
}
catch (XMLStreamException ex)
{
throw new XMLException(ex);
}
}
/**
* Creates an XML document representing an OVF-envelop with the provided virtual system and
* write it to output stream.
*
* @param virtualSystem, the object inside the envelope to be binded into an XML document.
* @param os, the destination of the XML document.
* @throws XMLException, any XML problem.
*/
public void writeXML(final VirtualSystemType virtualSystem, final OutputStream os)
throws XMLException
{
EnvelopeType envelope;
envelope = factoryEnvelop.createEnvelopeType();
envelope.setContent(factoryEnvelop.createVirtualSystem(virtualSystem));
writeXML(envelope, os);
}
}