/*
* 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 DataObject
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 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 {
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 = "";
String endS = "urn:publicid:gv.at:baseid";
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)));
Logger.info(new LogMsg(e.getMessage()));
}
// 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 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();
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 StreamEntityResolver
from a List
of
* supplements.
*
* @param supplements The supplements, given as
* XMLDataObjectAssociation
s.
* @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
{
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 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 {
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 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 {
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 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 {
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 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 :
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 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 + "\"");
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 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 {
NodeList nodes = xmlContent.getXMLContent();
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 {
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;
}
}