diff options
Diffstat (limited to 'moaSig/moa-sig-lib/src/main/java/at/gv/egovernment/moa/spss/server/invoke/DataObjectFactory.java')
-rw-r--r-- | moaSig/moa-sig-lib/src/main/java/at/gv/egovernment/moa/spss/server/invoke/DataObjectFactory.java | 1039 |
1 files changed, 1039 insertions, 0 deletions
diff --git a/moaSig/moa-sig-lib/src/main/java/at/gv/egovernment/moa/spss/server/invoke/DataObjectFactory.java b/moaSig/moa-sig-lib/src/main/java/at/gv/egovernment/moa/spss/server/invoke/DataObjectFactory.java new file mode 100644 index 0000000..d775fdb --- /dev/null +++ b/moaSig/moa-sig-lib/src/main/java/at/gv/egovernment/moa/spss/server/invoke/DataObjectFactory.java @@ -0,0 +1,1039 @@ +/* + * 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.logging.LogMsg; +import at.gv.egovernment.moa.logging.Logger; +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.moa.util.Constants; +import at.gv.egovernment.moa.util.DOMUtils; +import at.gv.egovernment.moa.util.EntityResolverChain; +import at.gv.egovernment.moa.util.MOAErrorHandler; +import at.gv.egovernment.moa.util.StreamEntityResolver; +import at.gv.egovernment.moa.util.StreamUtils; +import at.gv.egovernment.moa.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 <code>DataObject</code>s 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 <code>DataObjectFactory</code>. + * + * 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 <code>Content</code> object containing the signature + * environment. + * @param supplements Additional schema or DTD information. + * @return The signature environment or <code>null</code>, 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 { + + 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 { + 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 : + { + InputStream is = ((ContentBinary) content).getBinaryContent(); + contentBytes = StreamUtils.readStream(is); + break; + } + case Content.LOCREF_CONTENT: + { + String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); + InputStream is = null; + try + { + TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); + is = context.ResolveURI(locRefURI); + if (is == null) { + ExternalURIResolver uriResolver = new ExternalURIResolver(); + is = uriResolver.resolve(locRefURI); + } + contentBytes = StreamUtils.readStream(is); + } + catch (MOAApplicationException e) + { + throw new MOAApplicationException("3203", new Object[]{reference, locRefURI}, e); + } + finally + { + closeInputStream(is); + } + break; + } + case Content.REFERENCE_CONTENT : + { + ExternalURIResolver uriResolver = new ExternalURIResolver(); + InputStream is = null; + try + { + is = uriResolver.resolve(reference); + contentBytes = StreamUtils.readStream(is); + } + catch (Exception e) + { + throw e; + } + finally + { + closeInputStream(is); + } + break; + } + case Content.XML_CONTENT : + { + Element element = + checkForSingleElement(((ContentXML) content).getXMLContent()); + contentBytes = DOMUtils.serializeNode(element, "UTF-8"); + + break; + } + default : { + contentBytes = null; // this will not happen + } + } + } catch (MOAApplicationException e) { + throw e; + } catch (Exception e) { + throw new MOAApplicationException("2219", null); + } + + if (Logger.isTraceEnabled()) { + // For logging in Debug-Mode: Mask baseid with xxx + String logString = new String(contentBytes); + // TODO use RegExp + String startS = "<pr:Identification><pr:Value>"; + String endS = "</pr:Value><pr:Type>urn:publicid:gv.at:baseid</pr:Type>"; + String logWithMaskedBaseid = logString; + int start = logString.indexOf(startS); + if (start > -1) { + 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 { + ByteArrayInputStream is = new ByteArrayInputStream(contentBytes); + Document doc = + DOMUtils.parseDocument( + is, + true, + Constants.ALL_SCHEMA_LOCATIONS, + null, + entityResolver, + new MOAErrorHandler()); + Logger.trace("<<< parsed"); + + return new XMLDataObjectImpl(doc.getDocumentElement()); + } catch (Exception e) { + // never mind, we'll try non-validating + MessageProvider msg = MessageProvider.getInstance(); + Logger.info(new LogMsg(msg.getMessage("invoker.00", null))); + } + + // try to parse non-validating + try { + ByteArrayInputStream is = new ByteArrayInputStream(contentBytes); + 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. + NodeList idAttributes = XPathUtils.selectNodeList(doc.getDocumentElement(), XPATH); + for (int i = 0; i < idAttributes.getLength(); i++) { + Node item = idAttributes.item(i); + if (item instanceof Attr) { + Attr attr = (Attr) item; + 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 (Exception e) { + throw new MOAApplicationException("2218", null); + } + } + + /** + * Create an <code>XMLDataObject</code> 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 <code>"#xpointer"</code>, <code>"#xmlns"</code> + * or <code>"#element"</code>; or an URI starting with <code>"#"</code> 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(); + URIDereferencerImpl uriDereferencer = new URIDereferencerImpl(); + URIReference uriReference = new URIReferenceImpl(uri, null, signatureEnvironment); + Data returnedData = uriDereferencer.dereference(uriReference, null); + + if(returnedData instanceof NodeSetData) { + NodeSetData nodeSetData = (NodeSetData)returnedData; + Iterator nodesIterator = nodeSetData.iterator(); + List nodeList = new ArrayList(); + + while(nodesIterator.hasNext()) { + nodeList.add(nodesIterator.next()); + } + + NodeList nodes = new NodeListImplementation(nodeList); + dataObject = new XMLNodeListDataObjectImpl(nodes); + } else if(returnedData instanceof OctetStreamData) { + 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 (Exception e) { + throw new MOAApplicationException("2237", new Object[] { uri }); + } + } else if (uri.startsWith("#")) { + String id = uri.substring(1); + 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 <code>StreamEntityResolver</code> from a <code>List</code> of + * supplements. + * + * @param supplements The supplements, given as + * <code>XMLDataObjectAssociation</code>s. + * @return A <code>StreamEntityResolver</code> mapping the supplements by + * their reference URI to an <code>InputStream</code> of their respective + * content. + */ + private static StreamEntityResolver buildSupplementEntityResolver(List supplements) + throws MOAApplicationException + { + Map entities = new HashMap(); + Iterator iter; + + for (iter = supplements.iterator(); iter.hasNext();) { + XMLDataObjectAssociation supplement = + (XMLDataObjectAssociation) iter.next(); + Content content = supplement.getContent(); + String reference = content.getReference(); + + switch (content.getContentType()) { + case Content.BINARY_CONTENT : + { + entities.put(reference, ((ContentBinary) content).getBinaryContent()); + break; + } + case Content.LOCREF_CONTENT: + { + String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); + + TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); + if (context.FindResolvedEntity(locRefURI)==null) { + + 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 (Exception e) + { + throw new MOAApplicationException("3202", new Object[]{reference, locRefURI}, e); + } + finally + { + closeInputStream(uriStream); + } + context.PutResolvedEntity(locRefURI, contentBytes, contentType); + } + 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 + 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 + { + byte[] serialized = DOMUtils.serializeNode(nodes.item(i), "UTF-8"); + entities.put(reference, new ByteArrayInputStream(serialized)); + } + catch (Exception e) + { + throw new MOAApplicationException("2281", new Object[]{reference}, e); + } + } + break; + } + } + } + + return new StreamEntityResolver(entities); + } + + /** + * Create a <code>DataObject</code> from a <code>Content</code> object. + * + * @param content The <code>Content</code> object containing the data. + * @param finalDataMetaInfo The meta information corresponding with <code>content</code>. + * @param referenceID The reference ID to set in the resulting + * <code>DataObject</code>. May be <code>null</code>. + * @param allowContentAndReference If <code>true</code>, then + * <code>content</code> is allowed to contain both a <code>Reference</code> + * attribute and content. Otherwise, either a <code>Reference</code> + * attribute or content must be set. + * @param binaryAsXml If <code>true</code>, a content child given as + * <code>Base64Content</code> must contain XML data. + * @param xmlAsNodeList If <code>true</code>, the children of a + * <code>XMLContent</code> child element are returned as a + * <code>XMLNodeListDataObject</code>. Otherwise, <code>XMLContent</code> may + * only contain a single child node, which must be an element and which is + * returned as an <code>XMLDataObject</code>. + * @param referenceAsXml If <code>true</code>, then content loaded from the + * URI given as the <code>Reference</code> attribute must be XML data. + * If <code>false</code>, an attempt is made to parse the data as XML and + * return an <code>XMLDataObject</code> but if this fails, a + * <code>BinaryDataObject</code> is returned containing a byte stream to the + * data. + * @return A <code>DataObject</code> representing the data in + * <code>content</code>. If <code>base64AsXml==true</code> and + * <code>xmlAsNodeList==false</code> and <code>referenceAsXml==true</code>, + * then the result can safely be cast to an <code>XMLDataObject</code>. + * @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 { + + 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 : + { + ContentXML contentXml = (ContentXML) content; + dataObject = createFromXmlContent(contentXml, xmlAsNodeList); + break; + } + case Content.BINARY_CONTENT : + { + ContentBinary contentBinary = (ContentBinary) content; + dataObject = createFromBinaryContent(contentBinary, binaryAsXml, false); + break; + } + case Content.LOCREF_CONTENT : + { + String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); + try + { + dataObject = createFromURIImpl(locRefURI, referenceAsXml); + } + catch (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 <code>Content</code> to check. + * @param allowContentAndReference Whether explicit content and a reference + * are allowed at the same time. + * @throws MOAApplicationException If <code>allowContentAndRefernece</code> + * is <code>false</code> and both explicit content and reference are set, + * an exception is thrown. + */ + private static void checkAllowContentAndReference( + Content content, + boolean allowContentAndReference) + throws MOAApplicationException { + String reference = content.getReference(); + + // check for content and reference not being set + if ((content.getContentType() == Content.REFERENCE_CONTENT) + && (reference == null)) { + 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 <code>DataObject</code> from a + * <code>XMLDataObjectAssociation</code> object. + * + * @param xmlDataObjAssoc The <code>XMLDataObjectAssociation</code> object. + * @param xmlContentAllowed Whether the content contained in the + * <code>xmlDataObjAssoc</code> is allowed to be of type + * <code>XML_CONTENT</code>. + * @param binaryContentRepeatable If binary content must be provided as a + * <code>DataObject</code> that can be read multiple times. + * @return A <code>DataObject</code> representing the data in + * <code>xmlDataObjAssoc</code>. + * @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 { + + Content content = xmlDataObjAssoc.getContent(); + MetaInfo metaInfo = xmlDataObjAssoc.getMetaInfo(); + 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 : + { + String locRefURI = ((ContentLocRef) content).getLocationReferenceURI(); + try + { + dataObject = createFromURIImpl(locRefURI, false); + } + catch (MOAApplicationException e) + { + throw new MOAApplicationException("3201", new Object[]{content.getReference(), locRefURI}, e); + } + break; + } + } + + dataObject.setURI(content.getReference()); + dataObject.setMimeType(mimeType); + return dataObject; + } + + /** + * Create a <code>DataObject</code> from a <code>TransformParameter</code> + * object. + * + * @param transformParameter The <code>TransformParameter</code> object + * containing the data. + * @return A <code>DataObject</code> representing the data in + * <code>root</code>. + * @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 : + TransformParameterBinary tpBinary = + (TransformParameterBinary) transformParameter; + + try { + //dataObject = new ByteArrayDataObjectImpl(Base64Utils.encode(tpBinary.getBinaryContent())); + dataObject = + new ByteArrayDataObjectImpl( + StreamUtils.readStream(tpBinary.getBinaryContent())); + } catch (Exception e) { + return null; + } + //dataObject = new ByteStreamDataObjectImpl(tpBinary.getBinaryContent()); + break; + default : + // resolve uri and build the content + ExternalURIResolver resolver = new ExternalURIResolver(); + InputStream is = resolver.resolve(transformParameter.getURI()); + ByteArrayInputStream bis; + try + { + bis = new ByteArrayInputStream(StreamUtils.readStream(is)); + } + catch (IOException e) + { + throw new MOAApplicationException("2238", new Object[] {transformParameter.getURI()}, e); + } + finally + { + closeInputStream(is); + } + String contentType = resolver.getContentType(); + dataObject = new ByteStreamDataObjectImpl(bis); + dataObject.setMimeType(contentType); + break; + } + + dataObject.setURI(transformParameter.getURI()); + + return dataObject; + } + + /** + * Create a <code>DataObject</code> from data located at the given URI. + * + * @param uri The <code>URI</code> where the data is located. This method uses + * an <code>ExternalURIResolver</code> to resolve URIs. + * @param asXml If <code>true</code>, a <code>DataObject</code> is only + * returned, if the content consists of XML data. If it does not consist of + * XML data, an <code>MOAApplicationException</code> will be thrown. If this + * parameter is <code>false</code> and the content consists of XML data, this + * method will still attempt to parse it. + * @return The <code>DataObject</code> 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 <code>DataObject</code> from data located at the given URI. + * + * @param uri The <code>URI</code> where the data is located. This method uses + * an <code>ExternalURIResolver</code> to resolve URIs. + * @param asXml If <code>true</code>, a <code>DataObject</code> is only + * returned, if the content consists of XML data. If it does not consist of + * XML data, an <code>MOAApplicationException</code> will be thrown. If this + * parameter is <code>false</code> and the content type is detected as being + * XML data, this method will still attemt to parse it. + * @return The <code>DataObject</code> 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 + "\""); + + ExternalURIResolver resolver = new ExternalURIResolver(); + + 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 (ParserConfigurationException e) { + throw new MOASystemException("1106", null, e); + } catch (SAXException e) { + throw new MOAApplicationException("2209", null, e); + } catch (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 (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 (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 (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 (Throwable t) + { + // Intentionally do nothing here + } + } + + /** + * Determine whether the content type is XML. + * + * Content types recognized as XML start with <code>text/xml</code> and + * <code>application/xml</code>. + * + * @param contentType The content MIME type. + * @return boolean If <code>true</code>, the content type is XML, otherwise + * not. + */ + private static boolean contentTypeIsXml(String contentType) { + return contentType.startsWith("text/xml") + || (contentType.startsWith("application/xml")); + } + + /** + * Create a <code>DataObject</code> from a <code>ContentXML</code> object. + * + * @param xmlContent The <code>ContentXML</code> object from + * which the <code>DataObject</code> is to be built. + * @param xmlAsNodeList If <code>true</code>, the children of + * <code>xmlContent</code> are returned as a + * <code>XMLNodeListDataObject</code>. Otherwise, + * <code>xmlContent</code> may only contain a single child node, which must be + * an element and which is returned as an <code>XMLDataObject</code>. + * @return A <code>DataObject</code> representing the XML content in + * <code>xmlContent</code>. + * @throws MOAApplicationException If <code>xmlAsNodeList</code> is + * <code>false</code> and <code>xmlContent</code> 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 { + NodeList nodes = xmlContent.getXMLContent(); + Element element = checkForSingleElement(nodes); + + // build the XMLDataObject + dataObject = new XMLDataObjectImpl(element); + } + return dataObject; + } + + /** + * Check, that the given <code>NodeList</code> contains a single DOM element + * node and return it, otherwise throw an exception. + * + * @param nodes The <code>NodeList</code> to check for a single element. + * @return The single element contained in <code>nodes</code>. + * @throws MOAApplicationException Thrown, if <code>nodes</code> 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 <code>DataObject</code> from a <code>ContentBinary</code> object. + * + * @param binaryContent The <code>ContentBinary</code> object containing the + * data. + * @param asXml If <code>true</code>, <code>binaryContent</code> must + * contain XML data. Otherwise, a <code>BinaryDataObject</code> will be + * returned containing a byte stream to the decoded Base64 data. + * @param repeatable If multiple calls to <code>getInputStream()</code> must + * repeatedly return the content of the data object. + * @return A <code>DataObject</code> representing the content contained in + * <code>binaryContent</code>. + * @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 { + + InputStream byteStream = binaryContent.getBinaryContent(); + DataObjectImpl dataObject; + + if (asXml) { + Document doc; + + try { + doc = DOMUtils.parseDocument(byteStream, false, null, null); + dataObject = new XMLDataObjectImpl(doc.getDocumentElement()); + } catch (ParserConfigurationException e) { + throw new MOASystemException("1106", null, e); + } catch (SAXException e) { + throw new MOAApplicationException("2209", null, e); + } catch (IOException e) { + throw new MOAApplicationException("2210", null, e); + } + } else { + if (repeatable) { + try { + dataObject = + new ByteArrayDataObjectImpl(StreamUtils.readStream(byteStream)); + } catch (IOException e) { + throw new MOAApplicationException("2210", null); + } + } else { + dataObject = new ByteStreamDataObjectImpl(byteStream); + } + } + + return dataObject; + } + +} |