Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.axiom.soap.impl.builder; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMContainer; import org.apache.axiom.om.OMDocument; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMException; import org.apache.axiom.om.OMMetaFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axiom.om.OMNode; import org.apache.axiom.om.impl.OMContainerEx; import org.apache.axiom.om.impl.OMNodeEx; import org.apache.axiom.om.impl.builder.CustomBuilder; import org.apache.axiom.om.impl.builder.StAXOMBuilder; import org.apache.axiom.soap.SOAP11Constants; import org.apache.axiom.soap.SOAP12Constants; import org.apache.axiom.soap.SOAPBody; import org.apache.axiom.soap.SOAPConstants; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axiom.soap.SOAPFactory; import org.apache.axiom.soap.SOAPHeader; import org.apache.axiom.soap.SOAPMessage; import org.apache.axiom.soap.SOAPModelBuilder; import org.apache.axiom.soap.SOAPProcessingException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.stream.XMLStreamReader; /** * StAX based builder that produces a SOAP infoset model. * It builds SOAP specific objects such as {@link SOAPEnvelope}, {@link SOAPHeader}, * {@link org.apache.axiom.soap.SOAPHeaderBlock} and {@link SOAPBody}. * <p> * This builder offers two different ways to handle SOAP versions: * <ul> * <li>Either the SOAP version is specified when the builder is constructed. If the specified * version doesn't match the envelope namespace of the actual message, an exception is * triggered. This approach should be used when the SOAP version is known from information * other than the content of the message. For example, in the HTTP case it is possible * to identify the SOAP version based on the <tt>Content-Type</tt> header.</li> * <li>If no SOAP version is specified, the builder will automatically detect it from the * envelope namespace. It will then build the object model using the * {@link SOAPFactory} implementation corresponding to that SOAP version.</li> * </ul> * In both cases, the {@link SOAPFactory} is retrieved either from the {@link OMMetaFactory} * specified when the builder is constructed, or if none is specified, from the default * meta factory returned by {@link OMAbstractFactory#getMetaFactory()}. */ public class StAXSOAPModelBuilder extends StAXOMBuilder implements SOAPModelBuilder { SOAPMessage soapMessage; /** Field envelope */ private SOAPEnvelope envelope; private OMNamespace envelopeNamespace; private String namespaceURI; /** * The meta factory used to get the SOAPFactory implementation when SOAP version detection * is enabled. This is only used if <code>soapFactory</code> is <code>null</code>. */ private OMMetaFactory metaFactory; private SOAPFactory soapFactory; /** Field headerPresent */ private boolean headerPresent = false; /** Field bodyPresent */ private boolean bodyPresent = false; /** Field log */ private static final Log log = LogFactory.getLog(StAXSOAPModelBuilder.class); 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 parserVersion = null; private static final boolean isDebugEnabled = log.isDebugEnabled(); /** * Constructor. * * @param parser the parser to read the SOAP message from * @param soapVersion the namespace URI corresponding to the expected SOAP version * of the message */ public StAXSOAPModelBuilder(XMLStreamReader parser, String soapVersion) { this(OMAbstractFactory.getMetaFactory(), parser, soapVersion); } /** * Constructor. * * @param metaFactory the meta factory used to get the appropriate {@link SOAPFactory} * implementation * @param parser the parser to read the SOAP message from * @param soapVersion the namespace URI corresponding to the expected SOAP version * of the message */ public StAXSOAPModelBuilder(OMMetaFactory metaFactory, XMLStreamReader parser, String soapVersion) { super(metaFactory.getOMFactory(), parser); this.metaFactory = metaFactory; parserVersion = parser.getVersion(); identifySOAPVersion(soapVersion); } /** * Constructor. * * @param parser the parser to read the SOAP message from */ public StAXSOAPModelBuilder(XMLStreamReader parser) { this(OMAbstractFactory.getMetaFactory(), parser); } /** * Constructor. * * @param metaFactory the meta factory used to get the appropriate {@link SOAPFactory} * implementation * @param parser the parser to read the SOAP message from */ public StAXSOAPModelBuilder(OMMetaFactory metaFactory, XMLStreamReader parser) { super(metaFactory.getOMFactory(), parser); this.metaFactory = metaFactory; parserVersion = parser.getVersion(); SOAPEnvelope soapEnvelope = getSOAPEnvelope(); envelopeNamespace = soapEnvelope.getNamespace(); } /** * Constructor. * * @param parser the parser to read the SOAP message from * @param factory the SOAP factory to use * @param soapVersion the namespace URI corresponding to the expected SOAP version * of the message */ public StAXSOAPModelBuilder(XMLStreamReader parser, SOAPFactory factory, String soapVersion) { super(factory, parser); soapFactory = factory; parserVersion = parser.getVersion(); identifySOAPVersion(soapVersion); } /** @param soapVersionURIFromTransport */ 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(); if (soapVersionURIFromTransport != null) { String namespaceName = envelopeNamespace.getNamespaceURI(); if (!(soapVersionURIFromTransport.equals(namespaceName))) { throw new SOAPProcessingException( "Transport level information does not match with SOAP" + " Message namespace URI", envelopeNamespace.getPrefix() + ":" + SOAPConstants.FAULT_CODE_VERSION_MISMATCH); } } } public SOAPEnvelope getSOAPEnvelope() throws OMException { while ((envelope == null) && !done) { next(); } return envelope; } protected OMNode createNextOMElement() { OMNode newElement = null; if (elementLevel == 3 && customBuilderForPayload != null) { OMNode parent = lastNode; if (parent != null && parent.isComplete()) { parent = (OMNode) lastNode.getParent(); } if (parent instanceof SOAPBody) { newElement = createWithCustomBuilder(customBuilderForPayload, soapFactory); } } if (newElement == null && customBuilders != null && elementLevel <= maxDepthForCustomBuilders) { String namespace = parser.getNamespaceURI(); String localPart = parser.getLocalName(); CustomBuilder customBuilder = getCustomBuilder(namespace, localPart); if (customBuilder != null) { newElement = createWithCustomBuilder(customBuilder, soapFactory); } } if (newElement == null) { newElement = createOMElement(); } else { elementLevel--; // Decrease level since custom builder read the end element event } return newElement; } /** * Method createOMElement. * * @return Returns OMNode. * @throws OMException */ protected OMNode createOMElement() throws OMException { OMElement node; String elementName = parser.getLocalName(); if (lastNode == null) { node = constructNode(null, elementName, true); setSOAPEnvelope(node); } else if (lastNode.isComplete()) { OMContainer parent = lastNode.getParent(); if (parent == document) { // If we get here, this means that we found the SOAP envelope, but that it was // preceded by a comment node. Since constructNode will create a new document // based on the SOAP version of the envelope, we simply discard the last node // and do as if we just encountered the first node in the document. lastNode = null; node = constructNode(null, elementName, true); setSOAPEnvelope(node); } else { node = constructNode((OMElement) parent, elementName, false); ((OMNodeEx) lastNode).setNextOMSibling(node); ((OMNodeEx) node).setPreviousOMSibling(lastNode); } } else { OMContainerEx e = (OMContainerEx) lastNode; node = constructNode((OMElement) lastNode, elementName, false); e.setFirstChild(node); } if (isDebugEnabled) { log.debug("Build the OMElement " + node.getLocalName() + " by the StaxSOAPModelBuilder"); } return node; } protected void setSOAPEnvelope(OMElement node) { soapMessage.setSOAPEnvelope((SOAPEnvelope) node); soapMessage.setXMLVersion(parserVersion); soapMessage.setCharsetEncoding(charEncoding); } /** * Method constructNode * * @param parent * @param elementName * @param isEnvelope */ protected OMElement constructNode(OMElement parent, String elementName, boolean isEnvelope) { OMElement element; if (parent == null) { // Now I've found a SOAP Envelope, now create SOAPDocument and SOAPEnvelope here. if (!elementName.equals(SOAPConstants.SOAPENVELOPE_LOCAL_NAME)) { throw new SOAPProcessingException("First Element must contain the local name, " + SOAPConstants.SOAPENVELOPE_LOCAL_NAME + " , but found " + elementName, SOAPConstants.FAULT_CODE_SENDER); } // determine SOAP version and from that determine a proper factory here. if (soapFactory == null) { namespaceURI = this.parser.getNamespaceURI(); if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) { soapFactory = metaFactory.getSOAP12Factory(); if (isDebugEnabled) { log.debug("Starting to process SOAP 1.2 message"); } } else if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) { soapFactory = metaFactory.getSOAP11Factory(); if (isDebugEnabled) { 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); } } else { namespaceURI = soapFactory.getSoapVersionURI(); } // create a SOAPMessage to hold the SOAP envelope and assign the SOAP envelope in that. soapMessage = soapFactory.createSOAPMessage(this); // TODO: this needs to be reviewed; why do we create an OMDocument in StAXOMBuilder and // then replace it here OMDocument orgDocument = this.document; soapMessage.setXMLEncoding(orgDocument.getXMLEncoding()); this.document = soapMessage; if (charEncoding != null) { document.setCharsetEncoding(charEncoding); } envelope = soapFactory.createSOAPEnvelope(this); element = envelope; processNamespaceData(element, true); // fill in the attributes processAttributes(element); } else if (elementLevel == 2) { // Must be in the right namespace regardless String elementNS = parser.getNamespaceURI(); if (!(namespaceURI.equals(elementNS))) { if (!bodyPresent || !SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) { throw new SOAPProcessingException( "Disallowed element found inside Envelope : {" + elementNS + "}" + elementName); } } // 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().equals(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().equals(SOAPConstants.BODY_LOCAL_NAME) && elementName.equals(SOAPConstants.BODY_FAULT_LOCAL_NAME)) { // this is a headerblock element = soapFactory.createSOAPFault((SOAPBody) parent, this); processNamespaceData(element, false); processAttributes(element); processingFault = true; if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelopeNamespace.getNamespaceURI())) { builderHelper = new SOAP12BuilderHelper(this); } else if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelopeNamespace.getNamespaceURI())) { 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() { return envelope.getVersion().getSenderFaultCode().getLocalPart(); } private String getReceiverFaultCode() { return envelope.getVersion().getReceiverFaultCode().getLocalPart(); } 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); } } /** 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 envelope != null ? envelope : getSOAPEnvelope(); } /** * Method processNamespaceData. * * @param node * @param isSOAPElement */ protected void processNamespaceData(OMElement node, boolean isSOAPElement) { super.processNamespaceData(node); if (isSOAPElement) { OMNamespace omNS = node.getNamespace(); if (omNS != null) { String uri = omNS.getNamespaceURI(); if (uri.equals(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI) || uri.equals(SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI)) { // okay } else { 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 boolean isProcessingDetailElements() { return processingDetailElements; } public void setProcessingDetailElements(boolean value) { processingDetailElements = value; } public SOAPMessage getSoapMessage() { return soapMessage; } public OMDocument getDocument() { return this.soapMessage; } /** @return Returns the soapFactory. */ protected SOAPFactory getSoapFactory() { return soapFactory; } /** * Increase or decrease the element level by the desired amount. * This is needed by the SOAP11BuilderHelper to account for the different * depths for the SOAP fault sytax. * @param value */ void adjustElementLevel(int value) { elementLevel = elementLevel + value; } }