aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.ws.policy.sourcemodel; aoqi@0: aoqi@0: import com.sun.xml.internal.ws.policy.PolicyConstants; aoqi@0: import com.sun.xml.internal.ws.policy.PolicyException; aoqi@0: import com.sun.xml.internal.ws.policy.privateutil.LocalizationMessages; aoqi@0: import com.sun.xml.internal.ws.policy.privateutil.PolicyLogger; aoqi@0: import com.sun.xml.internal.ws.policy.sourcemodel.wspolicy.NamespaceVersion; aoqi@0: import com.sun.xml.internal.ws.policy.sourcemodel.wspolicy.XmlToken; aoqi@0: aoqi@0: import java.io.Reader; aoqi@0: import java.net.URI; aoqi@0: import java.net.URISyntaxException; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.Map; aoqi@0: import javax.xml.namespace.QName; aoqi@0: import javax.xml.stream.XMLEventReader; aoqi@0: import javax.xml.stream.XMLInputFactory; aoqi@0: import javax.xml.stream.XMLStreamConstants; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.stream.events.Attribute; aoqi@0: import javax.xml.stream.events.Characters; aoqi@0: import javax.xml.stream.events.EndElement; aoqi@0: import javax.xml.stream.events.StartElement; aoqi@0: import javax.xml.stream.events.XMLEvent; aoqi@0: aoqi@0: /** aoqi@0: * Unmarshal XML policy expressions. aoqi@0: * aoqi@0: * @author Marek Potociar aoqi@0: * @author Fabian Ritzmann aoqi@0: */ aoqi@0: public class XmlPolicyModelUnmarshaller extends PolicyModelUnmarshaller { aoqi@0: aoqi@0: private static final PolicyLogger LOGGER = PolicyLogger.getLogger(XmlPolicyModelUnmarshaller.class); aoqi@0: aoqi@0: /** aoqi@0: * Creates a new instance of XmlPolicyModelUnmarshaller aoqi@0: */ aoqi@0: protected XmlPolicyModelUnmarshaller() { aoqi@0: // nothing to initialize aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * See {@link PolicyModelUnmarshaller#unmarshalModel(Object) base method documentation}. aoqi@0: */ aoqi@0: public PolicySourceModel unmarshalModel(final Object storage) throws PolicyException { aoqi@0: final XMLEventReader reader = createXMLEventReader(storage); aoqi@0: PolicySourceModel model = null; aoqi@0: aoqi@0: loop: aoqi@0: while (reader.hasNext()) { aoqi@0: try { aoqi@0: final XMLEvent event = reader.peek(); aoqi@0: switch (event.getEventType()) { aoqi@0: case XMLStreamConstants.START_DOCUMENT: aoqi@0: case XMLStreamConstants.COMMENT: aoqi@0: reader.nextEvent(); aoqi@0: break; // skipping the comments and start document events aoqi@0: case XMLStreamConstants.CHARACTERS: aoqi@0: processCharacters(ModelNode.Type.POLICY, event.asCharacters(), null); aoqi@0: // we advance the reader only if there is no exception thrown from aoqi@0: // the processCharacters(...) call. Otherwise we don't modify the stream aoqi@0: reader.nextEvent(); aoqi@0: break; aoqi@0: case XMLStreamConstants.START_ELEMENT: aoqi@0: if (NamespaceVersion.resolveAsToken(event.asStartElement().getName()) == XmlToken.Policy) { aoqi@0: StartElement rootElement = reader.nextEvent().asStartElement(); aoqi@0: aoqi@0: model = initializeNewModel(rootElement); aoqi@0: unmarshalNodeContent(model.getNamespaceVersion(), model.getRootNode(), rootElement.getName(), reader); aoqi@0: aoqi@0: break loop; aoqi@0: } else { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0048_POLICY_ELEMENT_EXPECTED_FIRST())); aoqi@0: } aoqi@0: default: aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0048_POLICY_ELEMENT_EXPECTED_FIRST())); aoqi@0: } aoqi@0: } catch (XMLStreamException e) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0068_FAILED_TO_UNMARSHALL_POLICY_EXPRESSION(), e)); aoqi@0: } aoqi@0: } aoqi@0: return model; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Allow derived classes to pass in a custom instance of PolicySourceModel. aoqi@0: * aoqi@0: * @param nsVersion aoqi@0: * @param id aoqi@0: * @param name aoqi@0: * @return aoqi@0: */ aoqi@0: protected PolicySourceModel createSourceModel(NamespaceVersion nsVersion, String id, String name) { aoqi@0: return PolicySourceModel.createPolicySourceModel(nsVersion, id, name); aoqi@0: } aoqi@0: aoqi@0: private PolicySourceModel initializeNewModel(final StartElement element) throws PolicyException, XMLStreamException { aoqi@0: PolicySourceModel model; aoqi@0: aoqi@0: final NamespaceVersion nsVersion = NamespaceVersion.resolveVersion(element.getName().getNamespaceURI()); aoqi@0: aoqi@0: final Attribute policyName = getAttributeByName(element, nsVersion.asQName(XmlToken.Name)); aoqi@0: final Attribute xmlId = getAttributeByName(element, PolicyConstants.XML_ID); aoqi@0: Attribute policyId = getAttributeByName(element, PolicyConstants.WSU_ID); aoqi@0: aoqi@0: if (policyId == null) { aoqi@0: policyId = xmlId; aoqi@0: } else if (xmlId != null) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0058_MULTIPLE_POLICY_IDS_NOT_ALLOWED())); aoqi@0: } aoqi@0: aoqi@0: model = createSourceModel(nsVersion, aoqi@0: (policyId == null) ? null : policyId.getValue(), aoqi@0: (policyName == null) ? null : policyName.getValue()); aoqi@0: aoqi@0: return model; aoqi@0: } aoqi@0: aoqi@0: private ModelNode addNewChildNode(final NamespaceVersion nsVersion, final ModelNode parentNode, final StartElement childElement) throws PolicyException { aoqi@0: ModelNode childNode; aoqi@0: final QName childElementName = childElement.getName(); aoqi@0: if (parentNode.getType() == ModelNode.Type.ASSERTION_PARAMETER_NODE) { aoqi@0: childNode = parentNode.createChildAssertionParameterNode(); aoqi@0: } else { aoqi@0: XmlToken token = NamespaceVersion.resolveAsToken(childElementName); aoqi@0: aoqi@0: switch (token) { aoqi@0: case Policy: aoqi@0: childNode = parentNode.createChildPolicyNode(); aoqi@0: break; aoqi@0: case All: aoqi@0: childNode = parentNode.createChildAllNode(); aoqi@0: break; aoqi@0: case ExactlyOne: aoqi@0: childNode = parentNode.createChildExactlyOneNode(); aoqi@0: break; aoqi@0: case PolicyReference: aoqi@0: final Attribute uri = getAttributeByName(childElement, nsVersion.asQName(XmlToken.Uri)); aoqi@0: if (uri == null) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0040_POLICY_REFERENCE_URI_ATTR_NOT_FOUND())); aoqi@0: } else { aoqi@0: try { aoqi@0: final URI reference = new URI(uri.getValue()); aoqi@0: final Attribute digest = getAttributeByName(childElement, nsVersion.asQName(XmlToken.Digest)); aoqi@0: PolicyReferenceData refData; aoqi@0: if (digest == null) { aoqi@0: refData = new PolicyReferenceData(reference); aoqi@0: } else { aoqi@0: final Attribute digestAlgorithm = getAttributeByName(childElement, nsVersion.asQName(XmlToken.DigestAlgorithm)); aoqi@0: URI algorithmRef = null; aoqi@0: if (digestAlgorithm != null) { aoqi@0: algorithmRef = new URI(digestAlgorithm.getValue()); aoqi@0: } aoqi@0: refData = new PolicyReferenceData(reference, digest.getValue(), algorithmRef); aoqi@0: } aoqi@0: childNode = parentNode.createChildPolicyReferenceNode(refData); aoqi@0: } catch (URISyntaxException e) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0012_UNABLE_TO_UNMARSHALL_POLICY_MALFORMED_URI(), e)); aoqi@0: } aoqi@0: } aoqi@0: break; aoqi@0: default: aoqi@0: if (parentNode.isDomainSpecific()) { aoqi@0: childNode = parentNode.createChildAssertionParameterNode(); aoqi@0: } else { aoqi@0: childNode = parentNode.createChildAssertionNode(); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return childNode; aoqi@0: } aoqi@0: aoqi@0: private void parseAssertionData(NamespaceVersion nsVersion, String value, ModelNode childNode, final StartElement childElement) throws IllegalArgumentException, PolicyException { aoqi@0: // finish assertion node processing: create and set assertion data... aoqi@0: final Map attributeMap = new HashMap(); aoqi@0: boolean optional = false; aoqi@0: boolean ignorable = false; aoqi@0: aoqi@0: final Iterator iterator = childElement.getAttributes(); aoqi@0: while (iterator.hasNext()) { aoqi@0: final Attribute nextAttribute = (Attribute) iterator.next(); aoqi@0: final QName name = nextAttribute.getName(); aoqi@0: if (attributeMap.containsKey(name)) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0059_MULTIPLE_ATTRS_WITH_SAME_NAME_DETECTED_FOR_ASSERTION(nextAttribute.getName(), childElement.getName()))); aoqi@0: } else { aoqi@0: if (nsVersion.asQName(XmlToken.Optional).equals(name)) { aoqi@0: optional = parseBooleanValue(nextAttribute.getValue()); aoqi@0: } else if (nsVersion.asQName(XmlToken.Ignorable).equals(name)) { aoqi@0: ignorable = parseBooleanValue(nextAttribute.getValue()); aoqi@0: } else { aoqi@0: attributeMap.put(name, nextAttribute.getValue()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: final AssertionData nodeData = new AssertionData(childElement.getName(), value, attributeMap, childNode.getType(), optional, ignorable); aoqi@0: aoqi@0: // check visibility value syntax if present... aoqi@0: if (nodeData.containsAttribute(PolicyConstants.VISIBILITY_ATTRIBUTE)) { aoqi@0: final String visibilityValue = nodeData.getAttributeValue(PolicyConstants.VISIBILITY_ATTRIBUTE); aoqi@0: if (!PolicyConstants.VISIBILITY_VALUE_PRIVATE.equals(visibilityValue)) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0004_UNEXPECTED_VISIBILITY_ATTR_VALUE(visibilityValue))); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: childNode.setOrReplaceNodeData(nodeData); aoqi@0: } aoqi@0: aoqi@0: private Attribute getAttributeByName(final StartElement element, aoqi@0: final QName attributeName) { aoqi@0: // call standard API method to retrieve the attribute by name aoqi@0: Attribute attribute = element.getAttributeByName(attributeName); aoqi@0: aoqi@0: // try to find the attribute without a prefix. aoqi@0: if (attribute == null) { aoqi@0: final String localAttributeName = attributeName.getLocalPart(); aoqi@0: final Iterator iterator = element.getAttributes(); aoqi@0: while (iterator.hasNext()) { aoqi@0: final Attribute nextAttribute = (Attribute) iterator.next(); aoqi@0: final QName aName = nextAttribute.getName(); aoqi@0: final boolean attributeFoundByWorkaround = aName.equals(attributeName) || (aName.getLocalPart().equals(localAttributeName) && (aName.getPrefix() == null || "".equals(aName.getPrefix()))); aoqi@0: if (attributeFoundByWorkaround) { aoqi@0: attribute = nextAttribute; aoqi@0: break; aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return attribute; aoqi@0: } aoqi@0: aoqi@0: private String unmarshalNodeContent(final NamespaceVersion nsVersion, final ModelNode node, final QName nodeElementName, final XMLEventReader reader) throws PolicyException { aoqi@0: StringBuilder valueBuffer = null; aoqi@0: aoqi@0: loop: aoqi@0: while (reader.hasNext()) { aoqi@0: try { aoqi@0: final XMLEvent xmlParserEvent = reader.nextEvent(); aoqi@0: switch (xmlParserEvent.getEventType()) { aoqi@0: case XMLStreamConstants.COMMENT: aoqi@0: break; // skipping the comments aoqi@0: case XMLStreamConstants.CHARACTERS: aoqi@0: valueBuffer = processCharacters(node.getType(), xmlParserEvent.asCharacters(), valueBuffer); aoqi@0: break; aoqi@0: case XMLStreamConstants.END_ELEMENT: aoqi@0: checkEndTagName(nodeElementName, xmlParserEvent.asEndElement()); aoqi@0: break loop; // data exctraction for currently processed policy node is done aoqi@0: case XMLStreamConstants.START_ELEMENT: aoqi@0: final StartElement childElement = xmlParserEvent.asStartElement(); aoqi@0: aoqi@0: ModelNode childNode = addNewChildNode(nsVersion, node, childElement); aoqi@0: String value = unmarshalNodeContent(nsVersion, childNode, childElement.getName(), reader); aoqi@0: aoqi@0: if (childNode.isDomainSpecific()) { aoqi@0: parseAssertionData(nsVersion, value, childNode, childElement); aoqi@0: } aoqi@0: break; aoqi@0: default: aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0011_UNABLE_TO_UNMARSHALL_POLICY_XML_ELEM_EXPECTED())); aoqi@0: } aoqi@0: } catch (XMLStreamException e) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0068_FAILED_TO_UNMARSHALL_POLICY_EXPRESSION(), e)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return (valueBuffer == null) ? null : valueBuffer.toString().trim(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Method checks if the storage type is supported and transforms it to XMLEventReader instance which is then returned. aoqi@0: * Throws PolicyException if the transformation is not succesfull or if the storage type is not supported. aoqi@0: * aoqi@0: * @param storage An XMLEventReader instance. aoqi@0: * @return The storage cast to an XMLEventReader. aoqi@0: * @throws PolicyException If the XMLEventReader cast failed. aoqi@0: */ aoqi@0: private XMLEventReader createXMLEventReader(final Object storage) aoqi@0: throws PolicyException { aoqi@0: if (storage instanceof XMLEventReader) { aoqi@0: return (XMLEventReader) storage; aoqi@0: } aoqi@0: else if (!(storage instanceof Reader)) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0022_STORAGE_TYPE_NOT_SUPPORTED(storage.getClass().getName()))); aoqi@0: } aoqi@0: aoqi@0: try { aoqi@0: return XMLInputFactory.newInstance().createXMLEventReader((Reader) storage); aoqi@0: } catch (XMLStreamException e) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0014_UNABLE_TO_INSTANTIATE_READER_FOR_STORAGE(), e)); aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Method checks whether the actual name of the end tag is equal to the expected name - the name of currently unmarshalled aoqi@0: * XML policy model element. Throws exception, if the two FQNs are not equal as expected. aoqi@0: * aoqi@0: * @param expected The expected element name. aoqi@0: * @param element The actual element. aoqi@0: * @throws PolicyException If the actual element name did not match the expected element. aoqi@0: */ aoqi@0: private void checkEndTagName(final QName expected, final EndElement element) throws PolicyException { aoqi@0: final QName actual = element.getName(); aoqi@0: if (!expected.equals(actual)) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0003_UNMARSHALLING_FAILED_END_TAG_DOES_NOT_MATCH(expected, actual))); aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: aoqi@0: private StringBuilder processCharacters(final ModelNode.Type currentNodeType, final Characters characters, aoqi@0: final StringBuilder currentValueBuffer) aoqi@0: throws PolicyException { aoqi@0: if (characters.isWhiteSpace()) { aoqi@0: return currentValueBuffer; aoqi@0: } else { aoqi@0: final StringBuilder buffer = (currentValueBuffer == null) ? new StringBuilder() : currentValueBuffer; aoqi@0: final String data = characters.getData(); aoqi@0: if (currentNodeType == ModelNode.Type.ASSERTION || currentNodeType == ModelNode.Type.ASSERTION_PARAMETER_NODE) { aoqi@0: return buffer.append(data); aoqi@0: } else { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0009_UNEXPECTED_CDATA_ON_SOURCE_MODEL_NODE(currentNodeType, data))); aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return true if the value is "true" or "1". Return false if the value is aoqi@0: * "false" or "0". Throw an exception otherwise. The test is case sensitive. aoqi@0: * aoqi@0: * @param value The String representation of the value. Must not be null. aoqi@0: * @return True if the value is "true" or "1". False if the value is aoqi@0: * "false" or "0". aoqi@0: * @throws PolicyException If the value is not "true", "false", "0" or "1". aoqi@0: */ aoqi@0: private boolean parseBooleanValue(String value) throws PolicyException { aoqi@0: if ("true".equals(value) || "1".equals(value)) { aoqi@0: return true; aoqi@0: } aoqi@0: else if ("false".equals(value) || "0".equals(value)) { aoqi@0: return false; aoqi@0: } aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0095_INVALID_BOOLEAN_VALUE(value))); aoqi@0: } aoqi@0: aoqi@0: }