/* * Copyright 2003 Federal Chancellery Austria * MOA-SPSS has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egovernment.moa.spss.server.invoke; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.crypto.Data; import javax.xml.crypto.NodeSetData; import javax.xml.crypto.OctetStreamData; import javax.xml.crypto.URIReference; import javax.xml.parsers.ParserConfigurationException; import org.apache.xerces.dom.CoreDocumentImpl; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.EntityResolver; import org.xml.sax.SAXException; import at.gv.egovernment.moa.spss.MOAApplicationException; import at.gv.egovernment.moa.spss.MOASystemException; import at.gv.egovernment.moa.spss.api.common.Content; import at.gv.egovernment.moa.spss.api.common.ContentBinary; import at.gv.egovernment.moa.spss.api.common.ContentLocRef; import at.gv.egovernment.moa.spss.api.common.ContentXML; import at.gv.egovernment.moa.spss.api.common.MetaInfo; import at.gv.egovernment.moa.spss.api.common.XMLDataObjectAssociation; import at.gv.egovernment.moa.spss.api.xmlverify.TransformParameter; import at.gv.egovernment.moa.spss.api.xmlverify.TransformParameterBinary; import at.gv.egovernment.moa.spss.server.iaik.xml.ByteArrayDataObjectImpl; import at.gv.egovernment.moa.spss.server.iaik.xml.ByteStreamDataObjectImpl; import at.gv.egovernment.moa.spss.server.iaik.xml.DataObjectImpl; import at.gv.egovernment.moa.spss.server.iaik.xml.XMLDataObjectImpl; import at.gv.egovernment.moa.spss.server.iaik.xml.XMLNodeListDataObjectImpl; import at.gv.egovernment.moa.spss.server.transaction.TransactionContext; import at.gv.egovernment.moa.spss.server.transaction.TransactionContextManager; import at.gv.egovernment.moa.spss.util.MOASPSSEntityResolver; import at.gv.egovernment.moa.spss.util.MessageProvider; import at.gv.egovernment.moaspss.logging.LogMsg; import at.gv.egovernment.moaspss.logging.Logger; import at.gv.egovernment.moaspss.util.Constants; import at.gv.egovernment.moaspss.util.DOMUtils; import at.gv.egovernment.moaspss.util.EntityResolverChain; import at.gv.egovernment.moaspss.util.MOAErrorHandler; import at.gv.egovernment.moaspss.util.StreamEntityResolver; import at.gv.egovernment.moaspss.util.StreamUtils; import at.gv.egovernment.moaspss.util.XPathUtils; import iaik.server.modules.xml.DataObject; import iaik.server.modules.xml.NodeListImplementation; import iaik.server.modules.xml.URIReferenceImpl; import iaik.server.modules.xml.XMLDataObject; import iaik.xml.crypto.utils.URIDereferencerImpl; /** * A class to create DataObjects contained in different locations * of the MOA XML request format. * * @author Patrick Peck * @author Gregor Karlinger * @version $Id$ */ public class DataObjectFactory { /** * XPATH for registering ID attributes of known schemas if validating parsing * fails. */ private static final String XPATH = "descendant-or-self::node()[" + "namespace-uri()='http://www.w3.org/2000/09/xmldsig#' " + "or namespace-uri()='http://reference.e-government.gv.at/namespace/persondata/20020228#' " + "or starts-with(namespace-uri(), 'http://uri.etsi.org/01903/')" + "]/attribute::Id"; /** The single instance of this class. */ private static DataObjectFactory instance = null; /** * Return the only instance of this class. * * @return The only instance of this class. */ public static synchronized DataObjectFactory getInstance() { if (instance == null) { instance = new DataObjectFactory(); } return instance; } /** * Create a new DataObjectFactory. * * Protected to disallow multiple instances. */ protected DataObjectFactory() { } /** * Return the signature environment, i.e., the root element of the document, * into which the signature will be inserted (if created) or which contains the * signature (if verified). * * @param content The Content object containing the signature * environment. * @param supplements Additional schema or DTD information. * @return The signature environment or null, if no signature * environment exists. * @throws MOASystemException A system error occurred building the * signature environment (see message for * details). * @throws MOAApplicationException An error occurred building the signature * environment (see message for details). */ public XMLDataObject createSignatureEnvironment( Content content, List supplements) throws MOASystemException, MOAApplicationException { final String reference = content.getReference(); EntityResolver entityResolver; byte[] contentBytes; // check for content and reference not being set at the same time checkAllowContentAndReference(content, false); // build the EntityResolver for validating parsing if (supplements == null || supplements.isEmpty()) { entityResolver = new MOASPSSEntityResolver(); } else { final EntityResolverChain chain = new EntityResolverChain(); chain.addEntityResolver(buildSupplementEntityResolver(supplements)); chain.addEntityResolver(new MOASPSSEntityResolver()); entityResolver = chain; } // convert the content into a byte array try { switch (content.getContentType()) { case Content.BINARY_CONTENT: { final InputStream is = ((ContentBinary) content).getBinaryContent(); contentBytes = StreamUtils.readStream(is); break; } case Content.LOCREF_CONTENT: { final String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); InputStream is = null; try { final TransactionContext context = TransactionContextManager.getInstance() .getTransactionContext(); is = context.ResolveURI(locRefURI); if (is == null) { final ExternalURIResolver uriResolver = new ExternalURIResolver(); is = uriResolver.resolve(locRefURI); } contentBytes = StreamUtils.readStream(is); } catch (final MOAApplicationException e) { throw new MOAApplicationException("3203", new Object[] { reference, locRefURI }, e); } finally { closeInputStream(is); } break; } case Content.REFERENCE_CONTENT: { final ExternalURIResolver uriResolver = new ExternalURIResolver(); InputStream is = null; try { is = uriResolver.resolve(reference); contentBytes = StreamUtils.readStream(is); } catch (final Exception e) { throw e; } finally { closeInputStream(is); } break; } case Content.XML_CONTENT: { final Element element = checkForSingleElement(((ContentXML) content).getXMLContent()); contentBytes = DOMUtils.serializeNode(element, "UTF-8"); break; } default: { contentBytes = null; // this will not happen } } } catch (final MOAApplicationException e) { throw e; } catch (final Exception e) { throw new MOAApplicationException("2219", null); } if (Logger.isTraceEnabled()) { // For logging in Debug-Mode: Mask baseid with xxx final String logString = new String(contentBytes); // TODO use RegExp final String startS = ""; final String endS = "urn:publicid:gv.at:baseid"; String logWithMaskedBaseid = logString; final int start = logString.indexOf(startS); if (start > -1) { final int end = logString.indexOf(endS); if (end > -1) { logWithMaskedBaseid = logString.substring(0, start); logWithMaskedBaseid += startS; logWithMaskedBaseid += "xxxxxxxxxxxxxxxxxxxxxxxx"; logWithMaskedBaseid += logString.substring(end, logString.length()); } } // try to parse validating Logger.trace(">>> parsing the following content: \n" + logWithMaskedBaseid); } try { final ByteArrayInputStream is = new ByteArrayInputStream(contentBytes); final Document doc = DOMUtils.parseDocument( is, true, Constants.ALL_SCHEMA_LOCATIONS, null, entityResolver, new MOAErrorHandler()); Logger.trace("<<< parsed"); return new XMLDataObjectImpl(doc.getDocumentElement()); } catch (final Exception e) { // never mind, we'll try non-validating final MessageProvider msg = MessageProvider.getInstance(); Logger.info(new LogMsg(msg.getMessage("invoker.00", null))); Logger.info(new LogMsg(e.getMessage())); } // try to parse non-validating try { final ByteArrayInputStream is = new ByteArrayInputStream(contentBytes); final Document doc = DOMUtils.parseDocument(is, false, null, null); // Since the parse tree will not contain any post schema validation information, // we need to register any attributes known to be of type xsd:Id manually. final NodeList idAttributes = XPathUtils.selectNodeList(doc.getDocumentElement(), XPATH); for (int i = 0; i < idAttributes.getLength(); i++) { final Node item = idAttributes.item(i); if (item instanceof Attr) { final Attr attr = (Attr) item; final Element owner = attr.getOwnerElement(); // Only available in DOM-Level 3 (Java 1.5): // owner.setIdAttributeNode(attr, true); if (doc instanceof CoreDocumentImpl) { ((CoreDocumentImpl) doc).putIdentifier(attr.getValue(), owner); } } } return new XMLDataObjectImpl(doc.getDocumentElement()); } catch (final Exception e) { throw new MOAApplicationException("2218", null); } } /** * Create an XMLDataObject from the given signature environment. * * @param signatureEnvironment The signature environment contained in the * result. * @param uri The URI identifying the data. This must be either * the empty URI, an URI starting with * "#xpointer", "#xmlns" * or "#element"; or an URI starting * with "#" and followed by an element * ID. * @param referenceID The reference ID to set for the data object. * @return A data object containing the signature environment. */ public DataObject createFromSignatureEnvironment( Element signatureEnvironment, String uri, String referenceID) throws MOAApplicationException { DataObjectImpl dataObject = null; if ("".equals(uri)) { dataObject = new XMLDataObjectImpl(signatureEnvironment); } else if (uri.startsWith("#xpointer") || uri.startsWith("#xmlns") || uri.startsWith("#element")) { try { // CHANGE IXSIL to XSECT // maybe use URIDereferencerImpl or XPath ...?? // XPointerReferenceResolver resolver = new XPointerReferenceResolver(); final URIDereferencerImpl uriDereferencer = new URIDereferencerImpl(); final URIReference uriReference = new URIReferenceImpl(uri, null, signatureEnvironment); final Data returnedData = uriDereferencer.dereference(uriReference, null); if (returnedData instanceof NodeSetData) { final NodeSetData nodeSetData = (NodeSetData) returnedData; final Iterator nodesIterator = nodeSetData.iterator(); final List nodeList = new ArrayList(); while (nodesIterator.hasNext()) { nodeList.add(nodesIterator.next()); } final NodeList nodes = new NodeListImplementation(nodeList); dataObject = new XMLNodeListDataObjectImpl(nodes); } else if (returnedData instanceof OctetStreamData) { final OctetStreamData streamData = (OctetStreamData) returnedData; dataObject = new ByteStreamDataObjectImpl(streamData.getOctetStream()); } else { throw new MOAApplicationException("2237", new Object[] { uri }); } // URI uriObj = new URI(uri); // NodeList nodes = // resolver.resolveForest( // uriObj, // signatureEnvironment.getOwnerDocument(), // null); } catch (final Exception e) { throw new MOAApplicationException("2237", new Object[] { uri }); } } else if (uri.startsWith("#")) { final String id = uri.substring(1); final Element refElem = signatureEnvironment.getOwnerDocument().getElementById(id); if (refElem == null) { throw new MOAApplicationException("2237", new Object[] { id }); } dataObject = new XMLDataObjectImpl(refElem); } dataObject.setReferenceID(referenceID); dataObject.setURI(uri); return dataObject; } /** * Build a StreamEntityResolver from a List of * supplements. * * @param supplements The supplements, given as * XMLDataObjectAssociations. * @return A StreamEntityResolver mapping the supplements by their * reference URI to an InputStream of their respective * content. */ private static StreamEntityResolver buildSupplementEntityResolver(List supplements) throws MOAApplicationException { final Map entities = new HashMap(); Iterator iter; for (iter = supplements.iterator(); iter.hasNext();) { final XMLDataObjectAssociation supplement = (XMLDataObjectAssociation) iter.next(); final Content content = supplement.getContent(); final String reference = content.getReference(); switch (content.getContentType()) { case Content.BINARY_CONTENT: { entities.put(reference, ((ContentBinary) content).getBinaryContent()); break; } case Content.LOCREF_CONTENT: { final String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); final TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); if (context.FindResolvedEntity(locRefURI) == null) { final ExternalURIResolver uriResolver = new ExternalURIResolver(); InputStream uriStream = null; byte[] contentBytes; String contentType = null; try { uriStream = uriResolver.resolve(locRefURI); contentBytes = StreamUtils.readStream(uriStream); contentType = uriResolver.getContentType(); } catch (final Exception e) { throw new MOAApplicationException("3202", new Object[] { reference, locRefURI }, e); } finally { closeInputStream(uriStream); } context.PutResolvedEntity(locRefURI, contentBytes, contentType); } final InputStream contentIS = context.ResolveURI(locRefURI); entities.put(reference, contentIS); break; } case Content.XML_CONTENT: { // serialize the first element node that is found in the supplement // and make it available as a stream final NodeList nodes = ((ContentXML) content).getXMLContent(); int i = 0; // find the first element node while (i < nodes.getLength() && nodes.item(i).getNodeType() != Node.ELEMENT_NODE) { i++; } // serialize the node if (i < nodes.getLength()) { try { final byte[] serialized = DOMUtils.serializeNode(nodes.item(i), "UTF-8"); entities.put(reference, new ByteArrayInputStream(serialized)); } catch (final Exception e) { throw new MOAApplicationException("2281", new Object[] { reference }, e); } } break; } } } return new StreamEntityResolver(entities); } /** * Create a DataObject from a Content object. * * @param content The Content object containing * the data. * @param finalDataMetaInfo The meta information corresponding with * content. * @param referenceID The reference ID to set in the resulting * DataObject. May be * null. * @param allowContentAndReference If true, then * content is allowed to contain * both a Reference attribute and * content. Otherwise, either a * Reference attribute or content * must be set. * @param binaryAsXml If true, a content child given * as Base64Content must contain * XML data. * @param xmlAsNodeList If true, the children of a * XMLContent child element are * returned as a * XMLNodeListDataObject. * Otherwise, XMLContent may only * contain a single child node, which must be an * element and which is returned as an * XMLDataObject. * @param referenceAsXml If true, then content loaded * from the URI given as the * Reference attribute must be XML * data. If false, an attempt is * made to parse the data as XML and return an * XMLDataObject but if this fails, * a BinaryDataObject is returned * containing a byte stream to the data. * @return A DataObject representing the data in * content. If base64AsXml==true and * xmlAsNodeList==false and * referenceAsXml==true, then the result can safely be cast * to an XMLDataObject. * @throws MOASystemException An error indicating an internal problem. See * the wrapped exception for details. * @throws MOAApplicationException An error occurred handling the content * (probably while opening a reference or * parsing the data). See the wrapped exception * for details. */ public DataObject createFromContentOptionalRefType( Content content, MetaInfo finalDataMetaInfo, String referenceID, boolean allowContentAndReference, boolean binaryAsXml, boolean xmlAsNodeList, boolean referenceAsXml) throws MOASystemException, MOAApplicationException { final String reference = content.getReference(); DataObjectImpl dataObject = null; checkAllowContentAndReference(content, allowContentAndReference); // ok, build the data object; use content first, if available switch (content.getContentType()) { case Content.XML_CONTENT: { final ContentXML contentXml = (ContentXML) content; dataObject = createFromXmlContent(contentXml, xmlAsNodeList); break; } case Content.BINARY_CONTENT: { final ContentBinary contentBinary = (ContentBinary) content; dataObject = createFromBinaryContent(contentBinary, binaryAsXml, false); break; } case Content.LOCREF_CONTENT: { final String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); try { dataObject = createFromURIImpl(locRefURI, referenceAsXml); } catch (final MOAApplicationException e) { throw new MOAApplicationException("3201", new Object[] { reference, locRefURI }, e); } break; } case Content.REFERENCE_CONTENT: { dataObject = createFromURIImpl(reference, referenceAsXml); break; } } // set URI and reference ID dataObject.setURI(reference); dataObject.setReferenceID(referenceID); // set Type gathered from corresponding meta information dataObject.setTypeURI(finalDataMetaInfo.getType()); return dataObject; } /** * Check, if content and reference URIs are allowed in the content an throw an * exception if an illegal combination of the two occurs. * * @param content The Content to check. * @param allowContentAndReference Whether explicit content and a reference are * allowed at the same time. * @throws MOAApplicationException If allowContentAndRefernece is * false and both explicit content * and reference are set, an exception is * thrown. */ private static void checkAllowContentAndReference( Content content, boolean allowContentAndReference) throws MOAApplicationException { final String reference = content.getReference(); // check for content and reference not being set if (content.getContentType() == Content.REFERENCE_CONTENT && reference == null) { final String errorCode = allowContentAndReference ? "1111" : "1110"; throw new MOAApplicationException(errorCode, null); } // if we only allow either content or reference being set at once, check if (!allowContentAndReference && content.getContentType() != Content.REFERENCE_CONTENT && reference != null) { throw new MOAApplicationException("1110", null); } } /** * Create a DataObject from a XMLDataObjectAssociation * object. * * @param xmlDataObjAssoc The XMLDataObjectAssociation * object. * @param xmlContentAllowed Whether the content contained in the * xmlDataObjAssoc is allowed to be * of type XML_CONTENT. * @param binaryContentRepeatable If binary content must be provided as a * DataObject that can be read * multiple times. * @return A DataObject representing the data in * xmlDataObjAssoc. * @throws MOASystemException An error indicating an internal problem. See * the wrapped exception for details. * @throws MOAApplicationException An error occurred handling the content * (probably while parsing the data). See the * wrapped exception for details. */ public DataObject createFromXmlDataObjectAssociation( XMLDataObjectAssociation xmlDataObjAssoc, boolean xmlContentAllowed, boolean binaryContentRepeatable) throws MOASystemException, MOAApplicationException { final Content content = xmlDataObjAssoc.getContent(); final MetaInfo metaInfo = xmlDataObjAssoc.getMetaInfo(); final String mimeType = metaInfo != null ? metaInfo.getMimeType() : null; DataObjectImpl dataObject = null; switch (content.getContentType()) { case Content.XML_CONTENT: { if (xmlContentAllowed) { dataObject = createFromXmlContent((ContentXML) content, true); } else { throw new MOAApplicationException("2280", null); } break; } case Content.BINARY_CONTENT: { dataObject = createFromBinaryContent( (ContentBinary) content, false, binaryContentRepeatable); break; } case Content.LOCREF_CONTENT: { final String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); try { dataObject = createFromURIImpl(locRefURI, false); } catch (final MOAApplicationException e) { throw new MOAApplicationException("3201", new Object[] { content.getReference(), locRefURI }, e); } break; } } dataObject.setURI(content.getReference()); dataObject.setMimeType(mimeType); return dataObject; } /** * Create a DataObject from a TransformParameter * object. * * @param transformParameter The TransformParameter object * containing the data. * @return A DataObject representing the data in root. * @throws MOASystemException An error indicating an internal problem. See * the wrapped exception for details. * @throws MOAApplicationException An error occurred handling the content * (probably while opening a reference or * parsing the data). See the wrapped exception * for details. */ public DataObject createFromTransformParameter(TransformParameter transformParameter) throws MOASystemException, MOAApplicationException { DataObjectImpl dataObject; switch (transformParameter.getTransformParameterType()) { case TransformParameter.BINARY_TRANSFORMPARAMETER: final TransformParameterBinary tpBinary = (TransformParameterBinary) transformParameter; try { // dataObject = new // ByteArrayDataObjectImpl(Base64Utils.encode(tpBinary.getBinaryContent())); dataObject = new ByteArrayDataObjectImpl( StreamUtils.readStream(tpBinary.getBinaryContent())); } catch (final Exception e) { return null; } // dataObject = new ByteStreamDataObjectImpl(tpBinary.getBinaryContent()); break; default: // resolve uri and build the content final ExternalURIResolver resolver = new ExternalURIResolver(); final InputStream is = resolver.resolve(transformParameter.getURI()); ByteArrayInputStream bis; try { bis = new ByteArrayInputStream(StreamUtils.readStream(is)); } catch (final IOException e) { throw new MOAApplicationException("2238", new Object[] { transformParameter.getURI() }, e); } finally { closeInputStream(is); } final String contentType = resolver.getContentType(); dataObject = new ByteStreamDataObjectImpl(bis); dataObject.setMimeType(contentType); break; } dataObject.setURI(transformParameter.getURI()); return dataObject; } /** * Create a DataObject from data located at the given URI. * * @param uri The URI where the data is located. This method uses * an ExternalURIResolver to resolve URIs. * @param asXml If true, a DataObject is only * returned, if the content consists of XML data. If it does not * consist of XML data, an MOAApplicationException * will be thrown. If this parameter is false and the * content consists of XML data, this method will still attempt to * parse it. * @return The DataObject contained at the URI. * @throws MOASystemException A system error parsing the XML content. * @throws MOAApplicationException An error occurred on opening, reading or * parsing the data behind the URI. */ public DataObject createFromURI(String uri, boolean asXml) throws MOASystemException, MOAApplicationException { return createFromURIImpl(uri, asXml); } /** * Create a DataObject from data located at the given URI. * * @param uri The URI where the data is located. This method uses * an ExternalURIResolver to resolve URIs. * @param asXml If true, a DataObject is only * returned, if the content consists of XML data. If it does not * consist of XML data, an MOAApplicationException * will be thrown. If this parameter is false and the * content type is detected as being XML data, this method will * still attemt to parse it. * @return The DataObject contained at the URI. * @throws MOASystemException A system error parsing the XML content. * @throws MOAApplicationException An error occurred on opening, reading or * parsing the data behind the URI. */ private DataObjectImpl createFromURIImpl(String uri, boolean asXml) throws MOASystemException, MOAApplicationException { Logger.trace(">>> resolving uri \"" + uri + "\""); final ExternalURIResolver resolver = new ExternalURIResolver(); final TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); InputStream is = context.ResolveURI(uri); String contentType = null; boolean foundURI = false; if (is == null) { is = resolver.resolve(uri); contentType = resolver.getContentType(); } else { foundURI = true; contentType = (String) context.FindResolvedEntity(uri).get(1); Logger.trace("found \"" + uri + "\" InputStream in preread Supplements!, do not read any more. Content=" + contentType); } DataObjectImpl dataObject; // read the content if (contentType != null && contentTypeIsXml(contentType)) { Document doc; if (asXml) { try { // try parsing non-validating: this has to succeed or we // bail out by throwing an exception is = resolver.resolve(uri); doc = DOMUtils.parseDocument(is, false, null, null); dataObject = new XMLDataObjectImpl(doc.getDocumentElement()); } catch (final ParserConfigurationException e) { throw new MOASystemException("1106", null, e); } catch (final SAXException e) { throw new MOAApplicationException("2209", null, e); } catch (final IOException e) { throw new MOAApplicationException("2210", null, e); } finally { closeInputStream(is); } } else { try { // try parsing non-validating: need not succeed is = resolver.resolve(uri); doc = DOMUtils.parseDocument(is, false, null, null); closeInputStream(is); dataObject = new XMLDataObjectImpl(doc.getDocumentElement()); } catch (final Exception e) { // this is the last chance: return the data as a byte stream Logger.trace(">>> reading stream for \"" + uri + "\""); is = resolver.resolve(uri); ByteArrayInputStream bis; try { bis = new ByteArrayInputStream(StreamUtils.readStream(is)); dataObject = new ByteStreamDataObjectImpl(bis); } catch (final IOException e1) { throw new MOAApplicationException("2210", new Object[] { uri }, e1); } finally { closeInputStream(is); } Logger.trace(">>> read stream for \"" + uri + "\""); } } } else if (asXml) { // if we need XML data, we're in the wrong place here closeInputStream(is); throw new MOAApplicationException("2211", new Object[] { uri }); } else { // content is binary: make it available as a binary input stream Logger.trace(">>> getting binary input for \"" + uri + "\""); byte[] contentBytes; ByteArrayInputStream bis; try { contentBytes = StreamUtils.readStream(is); bis = new ByteArrayInputStream(contentBytes); } catch (final IOException e) { throw new MOAApplicationException("2210", null, e); } finally { closeInputStream(is); } if (!foundURI) { context.PutResolvedEntity(uri, contentBytes, contentType); } dataObject = new ByteStreamDataObjectImpl(bis); Logger.trace("<<< got binary input for \"" + uri + "\""); } dataObject.setMimeType(contentType); dataObject.setURI(uri); Logger.trace("<<< resolved uri \"" + uri + "\""); return dataObject; } /** * Savely closes the specified input stream. * * @param is The input stream to be closed. */ private static void closeInputStream(InputStream is) { try { if (is != null) { is.close(); } } catch (final Throwable t) { // Intentionally do nothing here } } /** * Determine whether the content type is XML. * * Content types recognized as XML start with text/xml and * application/xml. * * @param contentType The content MIME type. * @return boolean If true, the content type is XML, otherwise not. */ private static boolean contentTypeIsXml(String contentType) { return contentType.startsWith("text/xml") || contentType.startsWith("application/xml"); } /** * Create a DataObject from a ContentXML object. * * @param xmlContent The ContentXML object from which the * DataObject is to be built. * @param xmlAsNodeList If true, the children of * xmlContent are returned as a * XMLNodeListDataObject. Otherwise, * xmlContent may only contain a single child * node, which must be an element and which is returned as * an XMLDataObject. * @return A DataObject representing the XML content in * xmlContent. * @throws MOAApplicationException If xmlAsNodeList is * false and * xmlContent does not have a * single child element. */ private DataObjectImpl createFromXmlContent( ContentXML xmlContent, boolean xmlAsNodeList) throws MOAApplicationException { DataObjectImpl dataObject; if (xmlAsNodeList) { dataObject = new XMLNodeListDataObjectImpl(xmlContent.getXMLContent()); } else { final NodeList nodes = xmlContent.getXMLContent(); final Element element = checkForSingleElement(nodes); // build the XMLDataObject dataObject = new XMLDataObjectImpl(element); } return dataObject; } /** * Check, that the given NodeList contains a single DOM element * node and return it, otherwise throw an exception. * * @param nodes The NodeList to check for a single element. * @return The single element contained in nodes. * @throws MOAApplicationException Thrown, if nodes does not * contain exactly 1 element node. */ private Element checkForSingleElement(NodeList nodes) throws MOAApplicationException { Element element = null; int i; // check for a single element node for (i = 0; i < nodes.getLength(); i++) { if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) { if (element == null) { element = (Element) nodes.item(i); } else { throw new MOAApplicationException("1109", null); } } } // return the element node if (element == null) { throw new MOAApplicationException("1107", null); } else { return element; } } /** * Create a DataObject from a ContentBinary object. * * @param binaryContent The ContentBinary object containing the * data. * @param asXml If true, binaryContent must * contain XML data. Otherwise, a * BinaryDataObject will be returned * containing a byte stream to the decoded Base64 data. * @param repeatable If multiple calls to getInputStream() must * repeatedly return the content of the data object. * @return A DataObject representing the content contained in * binaryContent. * @throws MOASystemException An error indicating an internal problem. See * the wrapped exception for details. * @throws MOAApplicationException An error occurred handling the content * (probably while parsing the data). See the * wrapped exception for details. */ private DataObjectImpl createFromBinaryContent( ContentBinary binaryContent, boolean asXml, boolean repeatable) throws MOASystemException, MOAApplicationException { final InputStream byteStream = binaryContent.getBinaryContent(); DataObjectImpl dataObject; if (asXml) { Document doc; try { doc = DOMUtils.parseDocument(byteStream, false, null, null); dataObject = new XMLDataObjectImpl(doc.getDocumentElement()); } catch (final ParserConfigurationException e) { throw new MOASystemException("1106", null, e); } catch (final SAXException e) { throw new MOAApplicationException("2209", null, e); } catch (final IOException e) { throw new MOAApplicationException("2210", null, e); } } else { if (repeatable) { try { dataObject = new ByteArrayDataObjectImpl(StreamUtils.readStream(byteStream)); } catch (final IOException e) { throw new MOAApplicationException("2210", null); } } else { dataObject = new ByteStreamDataObjectImpl(byteStream); } } return dataObject; } }