/* | |
* Copyright 2004,2005 The Apache Software Foundation. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.ws.commons.soap.impl.llom.builder; | |
import org.apache.ws.commons.om.OMAbstractFactory; | |
import org.apache.ws.commons.om.OMDocument; | |
import org.apache.ws.commons.om.OMElement; | |
import org.apache.ws.commons.om.OMException; | |
import org.apache.ws.commons.om.OMNamespace; | |
import org.apache.ws.commons.om.OMNode; | |
import org.apache.ws.commons.om.impl.OMNodeEx; | |
import org.apache.ws.commons.om.impl.llom.builder.StAXOMBuilder; | |
import org.apache.ws.commons.soap.SOAP11Constants; | |
import org.apache.ws.commons.soap.SOAP12Constants; | |
import org.apache.ws.commons.soap.SOAPBody; | |
import org.apache.ws.commons.soap.SOAPConstants; | |
import org.apache.ws.commons.soap.SOAPEnvelope; | |
import org.apache.ws.commons.soap.SOAPFactory; | |
import org.apache.ws.commons.soap.SOAPHeader; | |
import org.apache.ws.commons.soap.SOAPMessage; | |
import org.apache.ws.commons.soap.SOAPProcessingException; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import javax.xml.stream.XMLStreamReader; | |
/** | |
* Class StAXSOAPModelBuilder | |
*/ | |
public class StAXSOAPModelBuilder extends StAXOMBuilder { | |
SOAPMessage soapMessage; | |
/** | |
* Field envelope | |
*/ | |
private SOAPEnvelope envelope; | |
private OMNamespace envelopeNamespace; | |
private SOAPFactory soapFactory; | |
/** | |
* Field headerPresent | |
*/ | |
private boolean headerPresent = false; | |
/** | |
* Field bodyPresent | |
*/ | |
private boolean bodyPresent = false; | |
/** | |
* Field log | |
*/ | |
private Log log = LogFactory.getLog(getClass()); | |
/** | |
* element level 1 = envelope level element level 2 = Header or Body level | |
* element level 3 = HeaderElement or BodyElement level | |
*/ | |
protected int elementLevel = 0; | |
private boolean processingFault = false; | |
//added | |
/* This is used to indicate whether detail element is processing in soap 1.2 builderhelper | |
*/ | |
private boolean processingDetailElements = false; | |
private SOAPBuilderHelper builderHelper; | |
private String senderfaultCode; | |
private String receiverfaultCode; | |
private boolean processingMandatoryFaultElements; | |
// We need to have soap factory, temporary, until we find out the correct SOAP version. If user has not provided | |
// a SOAP factory, internally we are creating a default one. This flag will be set if we create one internally, to | |
// warn that this should be replaced later. | |
private boolean isTempSOAPFactory = true; | |
/** | |
* Constructor StAXSOAPModelBuilder | |
* soapVersion parameter is to give the soap version from the transport. For example, in HTTP case | |
* you can identify the version of the soap message u have recd by looking at the HTTP headers. | |
* It is used to check whether the actual soap message contained is of that version. | |
* If one is creates the builder from the transport, then can just pass null for version. | |
* | |
* @param parser | |
* @param soapVersion parameter is to give the soap version for the transport. | |
*/ | |
public StAXSOAPModelBuilder(XMLStreamReader parser, String soapVersion) { | |
super(parser); | |
soapFactory = OMAbstractFactory.getDefaultSOAPFactory(); | |
isTempSOAPFactory = true; | |
soapMessage = soapFactory.createSOAPMessage(this); | |
this.document = soapMessage; | |
if(parser.getCharacterEncodingScheme() != null) { | |
document.setCharsetEncoding(parser.getCharacterEncodingScheme()); | |
} | |
identifySOAPVersion(soapVersion); | |
} | |
/** | |
* @param parser | |
* @param factory | |
* @param soapVersion parameter is to give the soap version from the transport. For example, in | |
* HTTP case you can identify the version of the soap message u have recd by looking at | |
* the HTTP headers. It is used to check whether the actual soap message | |
* contained is of that version.If one is creates the builder from the transport, | |
* then can just pass null for version. | |
*/ | |
public StAXSOAPModelBuilder(XMLStreamReader parser, SOAPFactory factory, String soapVersion) { | |
super(factory, parser); | |
soapFactory = factory; | |
isTempSOAPFactory = false; | |
soapMessage = soapFactory.createSOAPMessage(this); | |
this.document = soapMessage; | |
identifySOAPVersion(soapVersion); | |
} | |
protected void identifySOAPVersion(String soapVersionURIFromTransport) { | |
SOAPEnvelope soapEnvelope = getSOAPEnvelope(); | |
if (soapEnvelope == null) { | |
throw new SOAPProcessingException("SOAP Message does not contain an Envelope", | |
SOAPConstants.FAULT_CODE_VERSION_MISMATCH); | |
} | |
envelopeNamespace = soapEnvelope.getNamespace(); | |
String namespaceName = envelopeNamespace.getName(); | |
if ((soapVersionURIFromTransport != null) && !(soapVersionURIFromTransport.equals(namespaceName))) { | |
throw new SOAPProcessingException("Transport level information does not match with SOAP" + | |
" Message namespace URI", SOAPConstants.FAULT_CODE_VERSION_MISMATCH); | |
} | |
if(isTempSOAPFactory) { | |
if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceName)) { | |
soapFactory = OMAbstractFactory.getSOAP12Factory(); | |
log.debug("Starting to process SOAP 1.2 message"); | |
} else if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceName)) { | |
soapFactory = OMAbstractFactory.getSOAP11Factory(); | |
log.debug("Starting to process SOAP 1.1 message"); | |
} else { | |
throw new SOAPProcessingException("Only SOAP 1.1 or SOAP 1.2 messages are supported in the" + | |
" system", SOAPConstants.FAULT_CODE_VERSION_MISMATCH); | |
} | |
} | |
} | |
/** | |
* Method getSOAPEnvelope. | |
* | |
* @return Returns SOAPEnvelope. | |
* @throws OMException | |
*/ | |
public SOAPEnvelope getSOAPEnvelope() throws OMException { | |
while ((envelope == null) && !done) { | |
next(); | |
} | |
return envelope; | |
} | |
/** | |
* Method createOMElement. | |
* | |
* @return Returns OMNode. | |
* @throws OMException | |
*/ | |
protected OMNode createOMElement() throws OMException { | |
elementLevel++; | |
OMElement node; | |
String elementName = parser.getLocalName(); | |
if (lastNode == null) { | |
node = constructNode(null, elementName, true); | |
setSOAPEnvelope(node); | |
} else if (lastNode.isComplete()) { | |
node = | |
constructNode((OMElement) lastNode.getParent(), | |
elementName, | |
false); | |
((OMNodeEx) lastNode).setNextOMSibling(node); | |
((OMNodeEx) node).setPreviousOMSibling(lastNode); | |
} else { | |
OMElement e = (OMElement) lastNode; | |
node = constructNode((OMElement) lastNode, elementName, false); | |
e.setFirstChild(node); | |
} | |
log.debug("Build the OMElelment " + node.getLocalName() + | |
"By the StaxSOAPModelBuilder"); | |
return node; | |
} | |
protected void setSOAPEnvelope(OMElement node) { | |
soapMessage.setSOAPEnvelope((SOAPEnvelope) node); | |
soapMessage.setXMLVersion(parser.getVersion()); | |
soapMessage.setCharsetEncoding(parser.getCharacterEncodingScheme()); | |
} | |
/** | |
* Method constructNode | |
* | |
* @param parent | |
* @param elementName | |
* @param isEnvelope | |
*/ | |
protected OMElement constructNode(OMElement parent, String elementName, | |
boolean isEnvelope) { | |
OMElement element = null; | |
if (parent == null) { | |
if (!elementName.equalsIgnoreCase(SOAPConstants.SOAPENVELOPE_LOCAL_NAME)) { | |
throw new SOAPProcessingException("First Element must contain the local name, " | |
+ SOAPConstants.SOAPENVELOPE_LOCAL_NAME, SOAPConstants.FAULT_CODE_VERSION_MISMATCH); | |
} | |
envelope = soapFactory.createSOAPEnvelope(this); | |
element = envelope; | |
processNamespaceData(element, true); | |
// fill in the attributes | |
processAttributes(element); | |
} else if (elementLevel == 2) { | |
// this is either a header or a body | |
if (elementName.equals(SOAPConstants.HEADER_LOCAL_NAME)) { | |
if (headerPresent) { | |
throw new SOAPProcessingException("Multiple headers encountered!", getSenderFaultCode()); | |
} | |
if (bodyPresent) { | |
throw new SOAPProcessingException("Header Body wrong order!", getSenderFaultCode()); | |
} | |
headerPresent = true; | |
element = | |
soapFactory.createSOAPHeader((SOAPEnvelope) parent, | |
this); | |
processNamespaceData(element, true); | |
processAttributes(element); | |
} else if (elementName.equals(SOAPConstants.BODY_LOCAL_NAME)) { | |
if (bodyPresent) { | |
throw new SOAPProcessingException("Multiple body elements encountered", getSenderFaultCode()); | |
} | |
bodyPresent = true; | |
element = | |
soapFactory.createSOAPBody((SOAPEnvelope) parent, | |
this); | |
processNamespaceData(element, true); | |
processAttributes(element); | |
} else { | |
throw new SOAPProcessingException(elementName | |
+ | |
" is not supported here. Envelope can not have elements other than Header and Body.", getSenderFaultCode()); | |
} | |
} else if ((elementLevel == 3) | |
&& | |
parent.getLocalName().equalsIgnoreCase(SOAPConstants.HEADER_LOCAL_NAME)) { | |
// this is a headerblock | |
try { | |
element = | |
soapFactory.createSOAPHeaderBlock(elementName, null, | |
(SOAPHeader) parent, this); | |
} catch (SOAPProcessingException e) { | |
throw new SOAPProcessingException("Can not create SOAPHeader block", getReceiverFaultCode(), e); | |
} | |
processNamespaceData(element, false); | |
processAttributes(element); | |
} else if ((elementLevel == 3) && | |
parent.getLocalName().equalsIgnoreCase(SOAPConstants.BODY_LOCAL_NAME) && | |
elementName.equalsIgnoreCase(SOAPConstants.BODY_FAULT_LOCAL_NAME)) { | |
// this is a headerblock | |
element = soapFactory.createSOAPFault((SOAPBody) parent, this); | |
processNamespaceData(element, false); | |
processAttributes(element); | |
processingFault = true; | |
processingMandatoryFaultElements = true; | |
if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelopeNamespace.getName())) { | |
builderHelper = new SOAP12BuilderHelper(this); | |
} else if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelopeNamespace.getName())) { | |
builderHelper = new SOAP11BuilderHelper(this); | |
} | |
} else if (elementLevel > 3 && processingFault) { | |
element = builderHelper.handleEvent(parser, parent, elementLevel); | |
} else { | |
// this is neither of above. Just create an element | |
element = soapFactory.createOMElement(elementName, null, | |
parent, this); | |
processNamespaceData(element, false); | |
processAttributes(element); | |
} | |
return element; | |
} | |
private String getSenderFaultCode() { | |
if (senderfaultCode == null) { | |
senderfaultCode = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelopeNamespace.getName()) ? SOAP12Constants.FAULT_CODE_SENDER : SOAP11Constants.FAULT_CODE_SENDER; | |
} | |
return senderfaultCode; | |
} | |
private String getReceiverFaultCode() { | |
if (receiverfaultCode == null) { | |
receiverfaultCode = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelopeNamespace.getName()) ? SOAP12Constants.FAULT_CODE_RECEIVER : SOAP11Constants.FAULT_CODE_RECEIVER; | |
} | |
return receiverfaultCode; | |
} | |
public void endElement() { | |
if (lastNode.isComplete()) { | |
OMElement parent = (OMElement) lastNode.getParent(); | |
((OMNodeEx) parent).setComplete(true); | |
lastNode = parent; | |
} else { | |
OMNode e = lastNode; | |
((OMNodeEx) e).setComplete(true); | |
} | |
elementLevel--; | |
} | |
/** | |
* Method createDTD. | |
* Overriding the default behaviour as a SOAPMessage should not have a DTD. | |
*/ | |
protected OMNode createDTD() throws OMException { | |
throw new OMException("SOAP message MUST NOT contain a Document Type Declaration(DTD)"); | |
} | |
/** | |
* Method createPI. | |
* Overriding the default behaviour as a SOAP Message should not have a PI. | |
*/ | |
protected OMNode createPI() throws OMException { | |
throw new OMException("SOAP message MUST NOT contain Processing Instructions(PI)"); | |
} | |
/** | |
* Method getDocumentElement. | |
* | |
* @return Returns OMElement. | |
*/ | |
public OMElement getDocumentElement() { | |
return getSOAPEnvelope(); | |
} | |
/** | |
* Method processNamespaceData. | |
* | |
* @param node | |
* @param isSOAPElement | |
*/ | |
protected void processNamespaceData(OMElement node, boolean isSOAPElement) { | |
super.processNamespaceData(node, isSOAPElement); | |
if (isSOAPElement) { | |
if (node.getNamespace() != null && | |
!node.getNamespace().getName().equals(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI) && | |
!node.getNamespace().getName().equals(SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI)) { | |
throw new SOAPProcessingException("invalid SOAP namespace URI. " + | |
"Only " + SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI + | |
" and " + SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI + | |
" are supported.", SOAP12Constants.FAULT_CODE_SENDER); | |
} | |
} | |
} | |
/*these three methods to set and check detail element processing or mandatory fault element are present | |
*/ | |
public OMNamespace getEnvelopeNamespace() { | |
return envelopeNamespace; | |
} | |
public void setBooleanProcessingMandatoryFaultElements(boolean value) { | |
this.processingMandatoryFaultElements = value; | |
} | |
public boolean isProcessingDetailElements() { | |
return processingDetailElements; | |
} | |
public void setProcessingDetailElements(boolean value) { | |
processingDetailElements = value; | |
} | |
public SOAPMessage getSoapMessage() { | |
return soapMessage; | |
} | |
public OMDocument getDocument() { | |
return (OMDocument) this.soapMessage; | |
} | |
/** | |
* @return Returns the soapFactory. | |
*/ | |
protected SOAPFactory getSoapFactory() { | |
return soapFactory; | |
} | |
} |