/** * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is available at https://abicloud.svn.sourceforge.net/svnroot/abicloud * * The Initial Developer of the Original Code is Soluciones Grid, S.L. (www.abiquo.com), * Consell de Cent 296 principal 2ยบ, 08007 Barcelona, Spain. * No portions of the Code have been created by third parties. * All Rights Reserved. * * Contributor(s): * Telefonica Investigacion y Desarrollo S.A.U. (http://www.tid.es) * Emilio Vargas 6, 28043 Madrid, Spain. * */ package com.abiquo.ovf.xml; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.net.URL; 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 javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.dmtf.schemas.ovf.envelope._1.AnnotationSectionType; import org.dmtf.schemas.ovf.envelope._1.ContentType; 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.ObjectFactory; 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 org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.abiquo.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; /** Define the allowed objects to be binded from/into the OVF-envelope schema definition. */ private final JAXBContext contextVirtualSystem; /** Generated factory to create XML elements in OVF-envelope name space. */ private final ObjectFactory factoryEnvelop; /** Determines if the marshalling process to format the XML document.*/ private boolean formatOutput = true; /** Determines if the unmarshalling process is to validate the XML document.*/ private boolean validateXML = true; /** Path to the OVF XSD schema document (in order to validate documents). */ private final static String OVF_ENVELOPE_SCHEMA_LOCATION = "resources/schemas/TCloud_Reservoir.xsd"; /** Used to validate XML documents. */ private Schema envelopeSchema; /** 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; } /** * Configure the ''formatOutput'' property. * Determines if the marshalling process to format the XML document. * */ public void setFormatOutput(boolean formatOutput) { this.formatOutput = formatOutput; } /** * Configure the ''validateXML'' property. * Determines if the unmarshalling process is to validate the XML document. * @throws JAXBException, if the schema can not be located. * */ public void setValidateXML(boolean validateXML) throws JAXBException { this.validateXML = validateXML; if(validateXML) { configureValidation(); } } /** * Set up the schema to be used during validation. * @throws JAXBException * */ private void configureValidation() throws JAXBException { //SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.DEFAULT_NS_PREFIX); URL schemaURL = OVFSerializer.class.getClassLoader().getResource(OVF_ENVELOPE_SCHEMA_LOCATION); try { logger.debug("Using schema from [{}]", schemaURL.toExternalForm()); envelopeSchema = sf.newSchema(schemaURL); } catch (Exception e) // SAXException or Null pointer { throw new JAXBException("Can not localize the OVFEnvelope schema in order to validate documents",e); } } /** * Instantiate a new OVFSerializer. * * @throws JAXBException, if some JAXB context can not be created. */ private OVFSerializer() throws JAXBException { contextEnvelope = JAXBContext.newInstance(EnvelopeType.class); contextVirtualSystem = JAXBContext.newInstance(VirtualSystemType.class); factoryEnvelop = new ObjectFactory(); //contextIndex = JAXBContext.newInstance(new Class[]{EnvelopeType.class});//RepositorySpace.class,OVFIndex.class}); //factoryIndex = new com.abiquo.repositoryspace.ObjectFactory(); } /** * 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(EnvelopeType envelope, 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, new Boolean(true)); } marshall.marshal(factoryEnvelop.createEnvelope(envelope), writer); writer.close(); } catch (JAXBException ea) { throw new XMLException(ea); } catch (XMLStreamException ex) { throw new XMLException(ex); } } /** * 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(SectionType section, 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 the provided OVF-section and write it to formatter string. * * @param envelope, the object to be binded into an XML document. * @throws XMLException, any XML problem. */ public String writeXML(SectionType section) throws XMLException { Marshaller marshall; StringWriter writer = new StringWriter(); try { marshall = contextEnvelope.createMarshaller(); if (formatOutput) { marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } marshall.marshal(toJAXBElement(section), writer); return writer.toString(); } catch (JAXBException ea) { throw new XMLException(ea); } } /** * Creates an XML document representing an OVF-envelop with the provided virtual system and * write it to output stream. * * @param virutalSystem, 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(VirtualSystemType virtualSystem, OutputStream os) throws XMLException { EnvelopeType envelope; envelope = factoryEnvelop.createEnvelopeType(); envelope.setContent(factoryEnvelop.createVirtualSystem(virtualSystem)); writeXML(envelope, os); } public String writeXML(VirtualSystemType virtualSystem) throws XMLException { Marshaller marshall; StringWriter writer = new StringWriter(); try { marshall = contextEnvelope.createMarshaller(); if (formatOutput) { marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } marshall.marshal(toJAXBElement(virtualSystem), writer); return writer.toString(); } catch (JAXBException ea) { throw new XMLException(ea); } } public String writeXML(EnvelopeType env) throws XMLException { Marshaller marshall; StringWriter writer = new StringWriter(); try { marshall = contextEnvelope.createMarshaller(); if (formatOutput) { marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } marshall.marshal(toJAXBElement(env), writer); return writer.toString(); } catch (JAXBException ea) { throw new XMLException(ea); } } /** * 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(InputStream is) throws XMLException { XMLStreamReader reader; Unmarshaller unmarshall; JAXBElement<EnvelopeType> jaxbEnvelope; try { reader = Stax2Factory.getStreamReaderFactory().createXMLStreamReader(is); unmarshall = contextEnvelope.createUnmarshaller(); if(validateXML) { unmarshall.setSchema(envelopeSchema); } jaxbEnvelope = unmarshall.unmarshal(reader, EnvelopeType.class); reader.close(); } catch (JAXBException ea) { ea.printStackTrace(); // TODO remove throw new XMLException(ea); } catch (XMLStreamException ex) { ex.printStackTrace(); // TODO remove throw new XMLException(ex); } return jaxbEnvelope.getValue(); } /** * Read an expected OVF-Product Section form the provided source. * * @param is, the input stream source where read XML documents. * @return the ProductSectionType read from source. * @throws XMLException, if it is not an envelope type or any XML problem. */ public ProductSectionType readXMLProductSection(InputStream is) throws XMLException { XMLStreamReader reader; Unmarshaller unmarshall; JAXBElement<ProductSectionType> jaxbEnvelope; try { reader = Stax2Factory.getStreamReaderFactory().createXMLStreamReader(is); unmarshall = contextEnvelope.createUnmarshaller(); if(validateXML) { unmarshall.setSchema(envelopeSchema); } jaxbEnvelope = unmarshall.unmarshal(reader, ProductSectionType.class); reader.close(); } catch (JAXBException ea) { ea.printStackTrace(); // TODO remove throw new XMLException(ea.getLinkedException().getMessage()); } catch (XMLStreamException ex) { ex.printStackTrace(); // TODO remove throw new XMLException(ex); } return jaxbEnvelope.getValue(); } public VirtualSystemType readXMLVirtualSystem (InputStream is) throws XMLException { XMLStreamReader reader; Unmarshaller unmarshall; JAXBElement<VirtualSystemType> jaxbEnvelope; try { reader = Stax2Factory.getStreamReaderFactory().createXMLStreamReader(is); unmarshall = contextVirtualSystem.createUnmarshaller(); jaxbEnvelope = unmarshall.unmarshal(reader, VirtualSystemType.class); reader.close(); } catch (JAXBException ea) { ea.printStackTrace(); // TODO remove throw new XMLException(ea.getLinkedException().getMessage()); } catch (XMLStreamException ex) { ex.printStackTrace(); // TODO remove throw new XMLException(ex); } return jaxbEnvelope.getValue(); } /** * Read an expected OVF-envelope form the provided source. * @param strEnvelope, an string representation of an XML OVF envelope file. * @param isNamespaceAware, determine if the created document is XML name space aware. * @return the OVF-envelope read from source. * @deprecated use readXMLEnvelope with InputStream argument */ public EnvelopeType readXMLEnvelope(String strEnvelope, boolean isNamespaceAware) throws ParserConfigurationException, JAXBException, SAXException, IOException { // Now serialize the Java Content tree back to XML data StringReader stReader = new StringReader(strEnvelope); InputSource source = new InputSource(stReader); docBuilderFact.setNamespaceAware(isNamespaceAware); DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder(); Document doc = docBuilder.parse(source); Binder<Node> binder = contextEnvelope.createBinder(); JAXBElement<EnvelopeType> jaxb = binder.unmarshal(doc, EnvelopeType.class); EnvelopeType envelope = jaxb.getValue(); stReader.close(); return envelope; } /** Wrap into an JAXBElement the provided OVF envelope.**/ public JAXBElement<EnvelopeType> toJAXBElement(EnvelopeType envelope) { return factoryEnvelop.createEnvelope(envelope); } public JAXBElement<VirtualSystemType> toJAXBElement(VirtualSystemType vs) { return factoryEnvelop.createVirtualSystem(vs); } /** Wrap into an JAXBElement the provided OVF envelope section.**/ @SuppressWarnings("unchecked") public <T extends SectionType> JAXBElement<T> toJAXBElement(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 { // TODO throws an exception for invalid section jaxB = null; } return jaxB; } /** * 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(EnvelopeType envelope, 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; } }