/** * Copyright (c) Members of the EGEE Collaboration. 2006-2009. * See http://www.eu-egee.org/partners/ for details on the copyright holders. * * Licensed under the Apache 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.apache.org/licenses/LICENSE-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.glite.authz.pap.common.xacml.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.glite.authz.pap.common.exceptions.XMLObjectException; import org.glite.authz.pap.common.exceptions.XMLObjectMarshallingException; import org.glite.authz.pap.common.exceptions.XMLObjectParserException; import org.glite.authz.pap.common.exceptions.XMLObjectUnmarshallingException; import org.glite.authz.pap.common.xacml.impl.PolicySetTypeString; import org.glite.authz.pap.common.xacml.impl.PolicyTypeString; import org.opensaml.xacml.policy.PolicySetType; import org.opensaml.xacml.policy.PolicyType; import org.opensaml.xml.Configuration; import org.opensaml.xml.XMLObject; import org.opensaml.xml.XMLObjectBuilderFactory; import org.opensaml.xml.io.Marshaller; import org.opensaml.xml.io.MarshallerFactory; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.io.Unmarshaller; import org.opensaml.xml.io.UnmarshallerFactory; import org.opensaml.xml.io.UnmarshallingException; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.parse.XMLParserException; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * The <code>XMLObjectHelper</code> class contains helper methods to deal with OpenSAML * <code>XMLObject</code> objects. * * @param <T> a class extending <code>org.opensaml.xml.XMLObject</code> */ public class XMLObjectHelper<T extends XMLObject> { protected static final XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); private static final Object lock = new Object(); protected XMLObjectHelper() {} /** * Unmarshalls an <code>Element</code> into an <code>XMLObject</code>. Thread safe method. * <p> * This unmarshalling method is used inside the Axis deserialization process. If * <code>Element</code> is unmarshalled into a <code>PolicyType</code> or * <code>PolicySetType</code> object then, in order to cut on memory usage, the implementing * classes to represent the given element are, respectively, <code>PolicyTypeString</code> or * <code>PolicySetTypeString</code>. * * @param element * @return * @throws UnmarshallingException */ public static XMLObject axisUnmarshall(Element element) throws UnmarshallingException { synchronized (lock) { XMLObject xmlObject; UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element); XMLObject xmlObjectDOM = unmarshaller.unmarshall(element); if (xmlObjectDOM instanceof PolicyType) { xmlObject = new PolicyTypeString((PolicyType) xmlObjectDOM); xmlObject.releaseDOM(); } else if (xmlObjectDOM instanceof PolicySetType) { xmlObject = new PolicySetTypeString((PolicySetType) xmlObjectDOM); xmlObject.releaseDOM(); } else { xmlObject = xmlObjectDOM; } return xmlObject; } } /** * Build an <code>XMLObject</code> from an <code>Element</code>. * * @param element an element representing an OpenSAML <code>XMLObject</code>. * @return the given element as an OpenSAML <code>XMLObject</code>. * * @throws XMLObjectUnmarshallingException if unmarshalling fails. */ public static XMLObject buildXMLObject(Element element) { XMLObject xmlObject; try { xmlObject = unmarshall(element); } catch (UnmarshallingException e) { throw new XMLObjectUnmarshallingException(e); } return xmlObject; } /** * Build an <code>XMLObject</code> from an <code>InputStream</code>. * * @param inputStream input stream representing an OpenSAML <code>XMLObject</code>. * @return the given input stream as an OpenSAML <code>XMLObject</code>. * * @throws XMLObjectParserException if the XML parsing fails. */ public static XMLObject buildXMLObject(InputStream inputStream) { Document doc = readDocument(inputStream); return buildXMLObject(doc.getDocumentElement()); } /** * Build an <code>XMLObject</code> from file. * * @param file a file containing an OpenSAML <code>XMLObject</code>. * @return the OpenSAML <code>XMLObject</code>. * * @throws XMLObjectParserException if the XML parsing fails. * @throws XMLObjectException wrapping a {@link FileNotFoundException} if file does not exist. */ public static XMLObject buildXMLObjectFromFile(File file) { Document doc = readDocument(file); return buildXMLObject(doc.getDocumentElement()); } /** * Build an <code>XMLObject</code> from file. * * @param fileName <code>String</code> representing the file name containing an OpenSAML * <code>XMLObject</code>. * @return the OpenSAML <code>XMLObject</code>. * * @throws XMLObjectParserException if the XML parsing fails. * @throws XMLObjectException wrapping a {@link FileNotFoundException} if file does not exist. */ public static XMLObject buildXMLObjectFromFile(String fileName) { return buildXMLObjectFromFile(new File(fileName)); } /** * Get the DOM of an <code>XMLObject</code>. * * @param xmlObject * @return the DOM of the given object. * * @throws XMLObjectMarshallingException if the marshalling fails. */ public static Element getDOM(XMLObject xmlObject) { Element element; try { element = marshall(xmlObject); } catch (MarshallingException e) { throw new XMLObjectMarshallingException(e); } return element; } /** * Marshalls an <code>XMLObject</code>. Thread safe method. * * @param xmlObject * @return the marshalled <code>XMLObject</code>. * * @throws MarshallingException */ public static Element marshall(XMLObject xmlObject) throws MarshallingException { Element element; synchronized (lock) { MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory(); Marshaller marshaller = marshallerFactory.getMarshaller(xmlObject); element = marshaller.marshall(xmlObject); } return element; } /** * Write an <code>XMLObject</code> to a file. * * @param file * @param xmlObject * * @throws XMLObjectException wrapping a {@link FileNotFoundException}, a * {@link TransformerConfigurationException} or a {@link TransformerException}. */ public static void toFile(File file, XMLObject xmlObject) { FileOutputStream fos; try { fos = new FileOutputStream(file); } catch (FileNotFoundException e) { throw new XMLObjectException("Cannot write to file: " + file.getAbsolutePath(), e); } write(fos, xmlObject, 4); } /** * Write an <code>XMLObject</code> to a file. * * @param fileName the file name as <code>String</code>. * @param xmlObject * * @throws XMLObjectException wrapping a {@link FileNotFoundException}, a * {@link TransformerConfigurationException} or a {@link TransformerException}. */ public static void toFile(String fileName, XMLObject xmlObject) { File file = new File(fileName); toFile(file, xmlObject); } /** * Returns the <code>String</code> representation of the given <code>Element<code>. * * @param element * @return <code>String</code> representation of the element. * * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException}. */ public static String toString(Element element) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); write(bos, element); return bos.toString(); } /** * Returns the <code>String</code> representation of the given <code>XMLObject<code>. * * @param element * @return <code>String</code> representation of the element. * * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException}. */ public static String toString(XMLObject xmlObject) { return toString(xmlObject, 4); } /** * Returns the <code>String</code> representation of the given * <code>XMLObject<code> allowing to specify an indent value. * * @param element * @param indent indent value. * @return <code>String</code> representation of the element. * * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException}. */ public static String toString(XMLObject xmlObject, int indent) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); write(bos, xmlObject, indent); return bos.toString(); } /** * Unmarshalls an <code>Element</code> into an <code>XMLObject</code>. Thread safe method. * * @param element the <code>Element</code> to be marshalled. * @return the corresponding <code>XMLObject</code>. * * @throws UnmarshallingException */ public static XMLObject unmarshall(Element element) throws UnmarshallingException { synchronized (lock) { UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element); XMLObject xmlObject = unmarshaller.unmarshall(element); return xmlObject; } } /** * Writes an <code>Element</code> into an <code>OutputStream</code>. * * @param outputStream * @param element * * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException}. */ public static void write(OutputStream outputStream, Element element) { try { Transformer tr = TransformerFactory.newInstance().newTransformer(); tr.setOutputProperty(OutputKeys.METHOD, "xml"); tr.transform(new DOMSource(element), new StreamResult(outputStream)); } catch (TransformerConfigurationException e) { throw new XMLObjectException(e); } catch (TransformerException e) { throw new XMLObjectException(e); } } /** * Writes an <code>XMLObject</code> into an <code>OutputStream</code>. * * @param outputStream * @param xmlObject * @param indent indentation value. * * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException}. */ public static void write(OutputStream outputStream, XMLObject xmlObject, int indent) { try { Transformer tr = TransformerFactory.newInstance().newTransformer(); tr.setOutputProperty(OutputKeys.INDENT, "yes"); tr.setOutputProperty(OutputKeys.METHOD, "xml"); tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent)); tr.transform(new DOMSource(getDOM(xmlObject)), new StreamResult(outputStream)); } catch (TransformerConfigurationException e) { throw new XMLObjectException(e); } catch (TransformerException e) { throw new XMLObjectException(e); } } /** * Get a <code>Document</code> from a <code>File</code>. * * @param file the file to read the document from. * @return the file content as <code>Document</code>. * * @throws XMLObjectParserException if the XML parsing fails. * @throws XMLObjectException wrapping a {@link FileNotFoundException} if file does not exist. */ private static Document readDocument(File file) { FileInputStream fileInputStream; try { fileInputStream = new FileInputStream(file); } catch (FileNotFoundException e) { throw new XMLObjectException("File not found: " + file.getAbsolutePath(), e); } return readDocument(fileInputStream); } /** * Get a <code>Document</code> from an <code>InputStream</code>. * * @param inputStream * @return the given input stream as <code>Document</code>. * * @throws XMLObjectParserException if the XML parsing fails. */ private static Document readDocument(InputStream inputStream) { Document doc; try { BasicParserPool ppMgr = new BasicParserPool(); ppMgr.setNamespaceAware(true); doc = ppMgr.parse(inputStream); } catch (XMLParserException e) { throw new XMLObjectParserException(e); } return doc; } /** * Get a <code>Document</code> from a <code>Reader</code>. * * @param reader * @return the given reader stream as <code>Document</code>. * * @throws XMLObjectParserException if the XML parsing fails. */ private static Document readDocument(Reader reader) { Document doc; try { BasicParserPool ppMgr = new BasicParserPool(); ppMgr.setNamespaceAware(true); doc = ppMgr.parse(reader); } catch (XMLParserException e) { throw new XMLObjectException(e); } return doc; } /** * Builds an object of type <code>T</code> from an <code>Element</code>. * * @param element an element representing an OpenSAML <code>XMLObject</code>. * @return the OpenSAML object of class <code>T</code> extending <code>XMLObject</code>. * * @throws XMLObjectUnmarshallingException if unmarshalling fails. */ @SuppressWarnings("unchecked") public T build(Element element) { T xmlObject = (T) buildXMLObject(element); return xmlObject; } /** * Build an object of type <code>T</code> from file. * * @param file * @return the object <code>T</code> representing the content of the file. * * @throws XMLObjectParserException if the XML parsing fails. * @throws XMLObjectException wrapping a {@link FileNotFoundException} if file does not exist. */ public T buildFromFile(File file) { Document doc = readDocument(file); return build(doc.getDocumentElement()); } /** * Build an object of type <code>T</code> from a <code>String</code>. * * @param s the <code>String</code> to be parsered into an object <code>T</code>. * @return the object <code>T</code> representing the content of the string. * * @throws XMLObjectParserException if the XML parsing fails. */ public T buildFromString(String s) { StringReader sr = new StringReader(s); Document doc = readDocument(sr); return build(doc.getDocumentElement()); } /** * Clone an <code>XMLObject</code>. * * @param xmlObject <code>XMLObject</code> to be cloned. * @return a clone of the given object. * * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException} . * @throws XMLObjectParserException if the XML parsing fails. */ public T clone(T xmlObject) { // TODO: just marshalling and unmarshalling doesn't work, it gets a null // pointer exception during unmarshalling... find out what's wrong ByteArrayOutputStream bos = new ByteArrayOutputStream(); write(bos, xmlObject, 0); ByteArrayInputStream ios = new ByteArrayInputStream(bos.toByteArray()); Document doc = readDocument(ios); return build(doc.getDocumentElement()); } /** * Reads an <code>XMLObject</code> from file and returns the <code>String</code> representation. * * @param file the file to read. * @return <code>String</code> representation of the <code>XMLObject</code> contained in the * file. * * @throws XMLObjectParserException if the XML parsing fails. * @throws XMLObjectException wrapping a {@link FileNotFoundException} if file does not exist. * @throws XMLObjectException wrapping a {@link TransformerConfigurationException} or a * {@link TransformerException} . */ public String readFromFileAsString(File file) { T xmlObject = buildFromFile(file); return toString(xmlObject); } }