/* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.xml.internal.ws.policy.jaxws; import com.sun.xml.internal.ws.api.policy.ModelUnmarshaller; import com.sun.xml.internal.ws.policy.PolicyException; import com.sun.xml.internal.ws.policy.privateutil.PolicyLogger; import com.sun.xml.internal.ws.policy.sourcemodel.PolicySourceModel; import com.sun.xml.internal.ws.policy.sourcemodel.wspolicy.NamespaceVersion; import com.sun.xml.internal.ws.policy.sourcemodel.wspolicy.XmlToken; import com.sun.xml.internal.ws.resources.PolicyMessages; import java.io.StringReader; import java.util.HashSet; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.ws.WebServiceException; /** * Provides methods to unmarshal policies from a XMLStreamReader safely * * @author Fabian Ritzmann */ public class SafePolicyReader { private static final PolicyLogger LOGGER = PolicyLogger.getLogger(SafePolicyReader.class); // urls of xml docs policies were read from private final Set<String> urlsRead = new HashSet<String>(); private final Set<String> qualifiedPolicyUris = new HashSet<String>(); public final class PolicyRecord { PolicyRecord next; PolicySourceModel policyModel; Set<String> unresolvedURIs; private String uri; PolicyRecord() { // nothing to initialize } PolicyRecord insert(final PolicyRecord insertedRec) { if (null==insertedRec.unresolvedURIs || insertedRec.unresolvedURIs.isEmpty()) { insertedRec.next = this; return insertedRec; } final PolicyRecord head = this; PolicyRecord oneBeforeCurrent = null; PolicyRecord current; for (current = head ; null != current.next ; ) { if ((null != current.unresolvedURIs) && current.unresolvedURIs.contains(insertedRec.uri)) { if (null == oneBeforeCurrent) { insertedRec.next = current; return insertedRec; } else { // oneBeforeCurrent != null oneBeforeCurrent.next = insertedRec; insertedRec.next = current; return head; } // end-if-else oneBeforeCurrent == null }// end-if current record depends on inserted one if (insertedRec.unresolvedURIs.remove(current.uri) && (insertedRec.unresolvedURIs.isEmpty())) { insertedRec.next = current.next; current.next = insertedRec; return head; } // end-if one of unresolved URIs resolved by current record and thus unresolvedURIs empty oneBeforeCurrent = current; current = current.next; } // end for (current = head; null!=current.next; ) insertedRec.next = null; current.next = insertedRec; return head; } /** * Set the URI that identifies the policy. * * @param uri The fully qualified URI of the policy. May be a relative URI * if JAX-WS did not pass on any system id. * @param id The short ID of the policy. Used for error reporting. * @throws PolicyException If there already is a policy recorded with the * same id. */ public void setUri(final String uri, final String id) throws PolicyException { if (qualifiedPolicyUris.contains(uri)) { throw LOGGER.logSevereException(new PolicyException(PolicyMessages.WSP_1020_DUPLICATE_ID(id))); } this.uri = uri; qualifiedPolicyUris.add(uri); } public String getUri() { return this.uri; } @Override public String toString() { String result = uri; if (null!=next) { result += "->" + next.toString(); } return result; } } /** * Reads a policy expression from the XML stream. * * The XMLStreamReader should be in START_ELEMENT state and point to the policy element. * The content of the stream is copied and then the copy is unmarshalled. The result * is returned as a PolicyRecord. * * @param reader The XMLStreamReader should be in START_ELEMENT state and point to the policy element. * @param baseUrl The system id of the document read by the reader. * @return The policy that was read from the XML stream. */ public PolicyRecord readPolicyElement(final XMLStreamReader reader, final String baseUrl) { if ((null == reader) || (!reader.isStartElement())) { return null; } final StringBuffer elementCode = new StringBuffer(); final PolicyRecord policyRec = new PolicyRecord(); final QName elementName = reader.getName(); boolean insidePolicyReferenceAttr; int depth = 0; try{ do { switch (reader.getEventType()) { case XMLStreamConstants.START_ELEMENT: // process start of next element QName curName = reader.getName(); insidePolicyReferenceAttr = NamespaceVersion.resolveAsToken(curName) == XmlToken.PolicyReference; if (elementName.equals(curName)) { // it is our element ! depth++; // we are then deeper } final StringBuffer xmlnsCode = new StringBuffer(); // take care about namespaces as well final Set<String> tmpNsSet = new HashSet<String>(); if ((null == curName.getPrefix()) || ("".equals(curName.getPrefix()))) { // no prefix elementCode .append('<') // start tag .append(curName.getLocalPart()); xmlnsCode .append(" xmlns=\"") .append(curName.getNamespaceURI()) .append('"'); } else { // prefix presented elementCode .append('<') // start tag .append(curName.getPrefix()) .append(':') .append(curName.getLocalPart()); xmlnsCode .append(" xmlns:") .append(curName.getPrefix()) .append("=\"") .append(curName.getNamespaceURI()) .append('"'); tmpNsSet.add(curName.getPrefix()); } final int attrCount = reader.getAttributeCount(); // process element attributes final StringBuffer attrCode = new StringBuffer(); for (int i=0; i < attrCount; i++) { boolean uriAttrFlg = false; if (insidePolicyReferenceAttr && "URI".equals( reader.getAttributeName(i).getLocalPart())) { // PolicyReference found uriAttrFlg = true; if (null == policyRec.unresolvedURIs) { // first such URI found policyRec.unresolvedURIs = new HashSet<String>(); // initialize URIs set } policyRec.unresolvedURIs.add( // add the URI relativeToAbsoluteUrl(reader.getAttributeValue(i), baseUrl)); } // end-if PolicyReference attribute found if ("xmlns".equals(reader.getAttributePrefix(i)) && tmpNsSet.contains(reader.getAttributeLocalName(i))) { continue; // do not append already defined ns } if ((null == reader.getAttributePrefix(i)) || ("".equals(reader.getAttributePrefix(i)))) { // no attribute prefix attrCode .append(' ') .append(reader.getAttributeLocalName(i)) .append("=\"") .append(uriAttrFlg ? relativeToAbsoluteUrl(reader.getAttributeValue(i), baseUrl) : reader.getAttributeValue(i)) .append('"'); } else { // prefix`presented attrCode .append(' ') .append(reader.getAttributePrefix(i)) .append(':') .append(reader.getAttributeLocalName(i)) .append("=\"") .append(uriAttrFlg ? relativeToAbsoluteUrl(reader.getAttributeValue(i), baseUrl) : reader.getAttributeValue(i)) .append('"'); if (!tmpNsSet.contains(reader.getAttributePrefix(i))) { xmlnsCode .append(" xmlns:") .append(reader.getAttributePrefix(i)) .append("=\"") .append(reader.getAttributeNamespace(i)) .append('"'); tmpNsSet.add(reader.getAttributePrefix(i)); } // end if prefix already processed } } // end foreach attr elementCode .append(xmlnsCode) // complete the start element tag .append(attrCode) .append('>'); break; //case XMLStreamConstants.ATTRIBUTE: Unreachable (I hope ;-) // break; //case XMLStreamConstants.NAMESPACE: Unreachable (I hope ;-) // break; case XMLStreamConstants.END_ELEMENT: curName = reader.getName(); if (elementName.equals(curName)) { // it is our element ! depth--; // go up } elementCode .append("</") // append appropriate XML code .append("".equals(curName.getPrefix())?"":curName.getPrefix()+':') .append(curName.getLocalPart()) .append('>'); // complete the end element tag break; case XMLStreamConstants.CHARACTERS: elementCode.append(reader.getText()); // append text data break; case XMLStreamConstants.CDATA: elementCode .append("<![CDATA[") // append CDATA delimiters .append(reader.getText()) .append("]]>"); break; case XMLStreamConstants.COMMENT: // Ignore any comments break; case XMLStreamConstants.SPACE: // Ignore spaces as well break; } if (reader.hasNext() && depth>0) { reader.next(); } } while (XMLStreamConstants.END_DOCUMENT!=reader.getEventType() && depth>0); policyRec.policyModel = ModelUnmarshaller.getUnmarshaller().unmarshalModel( new StringReader(elementCode.toString())); if (null != policyRec.policyModel.getPolicyId()) { policyRec.setUri(baseUrl + "#" + policyRec.policyModel.getPolicyId(), policyRec.policyModel.getPolicyId()); } else if (policyRec.policyModel.getPolicyName() != null) { policyRec.setUri(policyRec.policyModel.getPolicyName(), policyRec.policyModel.getPolicyName()); } } catch(Exception e) { throw LOGGER.logSevereException(new WebServiceException(PolicyMessages.WSP_1013_EXCEPTION_WHEN_READING_POLICY_ELEMENT(elementCode.toString()), e)); } urlsRead.add(baseUrl); return policyRec; } public Set<String> getUrlsRead() { return this.urlsRead; } /** * Reads policy reference element <wsp:PolicyReference/> and returns referenced policy URI as String * * @param reader The XMLStreamReader should be in START_ELEMENT state and point to the PolicyReference element. * @return The URI contained in the PolicyReference */ public String readPolicyReferenceElement(final XMLStreamReader reader) { try { if (NamespaceVersion.resolveAsToken(reader.getName()) == XmlToken.PolicyReference) { // "PolicyReference" element interests me for (int i = 0; i < reader.getAttributeCount(); i++) { if (XmlToken.resolveToken(reader.getAttributeName(i).getLocalPart()) == XmlToken.Uri) { final String uriValue = reader.getAttributeValue(i); reader.next(); return uriValue; } } } reader.next(); return null; } catch(XMLStreamException e) { throw LOGGER.logSevereException(new WebServiceException(PolicyMessages.WSP_1001_XML_EXCEPTION_WHEN_PROCESSING_POLICY_REFERENCE(), e)); } } /** * Utility method to construct an absolute URL from a relative URI and a base URL. * * If the relativeUri already is an absolute URL, the method returns the relativeUri. * * @param relativeUri The relative URI * @param baseUri The base URL * @return The relative URI appended to the base URL. If relativeUri already is * an absolute URL, the method returns the relativeUri. */ public static String relativeToAbsoluteUrl(final String relativeUri, final String baseUri) { if ('#' != relativeUri.charAt(0)) { // TODO: escaped char could be an issue? return relativeUri; // absolute already } return (null == baseUri) ? relativeUri : baseUri + relativeUri; } }