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.sourcemodel.wspolicy.NamespaceVersion; 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.privateutil.PolicyUtils; aoqi@0: import com.sun.xml.internal.ws.policy.spi.PrefixMapper; aoqi@0: aoqi@0: import java.util.Collection; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.HashSet; aoqi@0: import java.util.LinkedList; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import java.util.Map.Entry; aoqi@0: import java.util.Queue; aoqi@0: import java.util.Set; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: /** aoqi@0: * This class is a root of unmarshaled policy source structure. Each instance of the class contains factory method aoqi@0: * to create new {@link com.sun.xml.internal.ws.policy.sourcemodel.ModelNode} instances associated with the actual model instance. aoqi@0: * aoqi@0: * @author Marek Potociar aoqi@0: * @author Fabian Ritzmann aoqi@0: */ aoqi@0: public class PolicySourceModel implements Cloneable { aoqi@0: aoqi@0: private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicySourceModel.class); aoqi@0: aoqi@0: private static final Map DEFAULT_NAMESPACE_TO_PREFIX = new HashMap(); aoqi@0: static { aoqi@0: PrefixMapper[] prefixMappers = PolicyUtils.ServiceProvider.load(PrefixMapper.class); aoqi@0: if (prefixMappers != null) { aoqi@0: for (PrefixMapper mapper: prefixMappers) { aoqi@0: DEFAULT_NAMESPACE_TO_PREFIX.putAll(mapper.getPrefixMap()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: for (NamespaceVersion version : NamespaceVersion.values()) { aoqi@0: DEFAULT_NAMESPACE_TO_PREFIX.put(version.toString(), version.getDefaultNamespacePrefix()); aoqi@0: } aoqi@0: DEFAULT_NAMESPACE_TO_PREFIX.put(PolicyConstants.WSU_NAMESPACE_URI, aoqi@0: PolicyConstants.WSU_NAMESPACE_PREFIX); aoqi@0: DEFAULT_NAMESPACE_TO_PREFIX.put(PolicyConstants.SUN_POLICY_NAMESPACE_URI, aoqi@0: PolicyConstants.SUN_POLICY_NAMESPACE_PREFIX); aoqi@0: } aoqi@0: aoqi@0: // Map namespaces to prefixes aoqi@0: private final Map namespaceToPrefix = aoqi@0: new HashMap(DEFAULT_NAMESPACE_TO_PREFIX); aoqi@0: aoqi@0: private ModelNode rootNode; aoqi@0: private final String policyId; aoqi@0: private final String policyName; aoqi@0: private final NamespaceVersion nsVersion; aoqi@0: private final List references = new LinkedList(); // links to policy reference nodes aoqi@0: private boolean expanded = false; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Factory method that creates new policy source model instance. aoqi@0: * aoqi@0: * This method is only intended to be used by code that has no dependencies on aoqi@0: * JAX-WS. Otherwise use com.sun.xml.internal.ws.policy.api.SourceModel. aoqi@0: * aoqi@0: * @param nsVersion The policy version aoqi@0: * @return Newly created policy source model instance. aoqi@0: */ aoqi@0: public static PolicySourceModel createPolicySourceModel(final NamespaceVersion nsVersion) { aoqi@0: return new PolicySourceModel(nsVersion); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Factory method that creates new policy source model instance and initializes it according to parameters provided. aoqi@0: * aoqi@0: * This method is only intended to be used by code that has no dependencies on aoqi@0: * JAX-WS. Otherwise use com.sun.xml.internal.ws.policy.api.SourceModel. aoqi@0: * aoqi@0: * @param nsVersion The policy version aoqi@0: * @param policyId local policy identifier - relative URI. May be {@code null}. aoqi@0: * @param policyName global policy identifier - absolute policy expression URI. May be {@code null}. aoqi@0: * @return Newly created policy source model instance with its name and id properly set. aoqi@0: */ aoqi@0: public static PolicySourceModel createPolicySourceModel(final NamespaceVersion nsVersion, final String policyId, final String policyName) { aoqi@0: return new PolicySourceModel(nsVersion, policyId, policyName); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Constructor that creates a new policy source model instance without any aoqi@0: * id or name identifier. The namespace-to-prefix map is initialized with mapping aoqi@0: * of policy namespace to the default value set by aoqi@0: * {@link PolicyConstants#POLICY_NAMESPACE_PREFIX POLICY_NAMESPACE_PREFIX constant}. aoqi@0: * aoqi@0: * @param nsVersion The WS-Policy version. aoqi@0: */ aoqi@0: private PolicySourceModel(NamespaceVersion nsVersion) { aoqi@0: this(nsVersion, null, null); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Constructor that creates a new policy source model instance with given aoqi@0: * id or name identifier. aoqi@0: * aoqi@0: * @param nsVersion The WS-Policy version. aoqi@0: * @param policyId Relative policy reference within an XML document. May be {@code null}. aoqi@0: * @param policyName Absolute IRI of policy expression. May be {@code null}. aoqi@0: */ aoqi@0: private PolicySourceModel(NamespaceVersion nsVersion, String policyId, String policyName) { aoqi@0: this(nsVersion, policyId, policyName, null); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Constructor that creates a new policy source model instance with given aoqi@0: * id or name identifier and a set of PrefixMappers. aoqi@0: * aoqi@0: * This constructor is intended to be used by the JAX-WS com.sun.xml.internal.ws.policy.api.SourceModel. aoqi@0: * aoqi@0: * @param nsVersion The WS-Policy version. aoqi@0: * @param policyId Relative policy reference within an XML document. May be {@code null}. aoqi@0: * @param policyName Absolute IRI of policy expression. May be {@code null}. aoqi@0: * @param prefixMappers A collection of PrefixMappers to be used with this instance. May be {@code null}. aoqi@0: */ aoqi@0: protected PolicySourceModel(NamespaceVersion nsVersion, String policyId, aoqi@0: String policyName, Collection prefixMappers) { aoqi@0: this.rootNode = ModelNode.createRootPolicyNode(this); aoqi@0: this.nsVersion = nsVersion; aoqi@0: this.policyId = policyId; aoqi@0: this.policyName = policyName; aoqi@0: if (prefixMappers != null) { aoqi@0: for (PrefixMapper prefixMapper : prefixMappers) { aoqi@0: this.namespaceToPrefix.putAll(prefixMapper.getPrefixMap()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a root node of this policy source model. It is allways of POLICY type. aoqi@0: * aoqi@0: * @return root policy source model node - allways of POLICY type. aoqi@0: */ aoqi@0: public ModelNode getRootNode() { aoqi@0: return rootNode; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a policy name of this policy source model. aoqi@0: * aoqi@0: * @return policy name. aoqi@0: */ aoqi@0: public String getPolicyName() { aoqi@0: return policyName; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a policy ID of this policy source model. aoqi@0: * aoqi@0: * @return policy ID. aoqi@0: */ aoqi@0: public String getPolicyId() { aoqi@0: return policyId; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns an original namespace version of this policy source model. aoqi@0: * aoqi@0: * @return namespace version. aoqi@0: */ aoqi@0: public NamespaceVersion getNamespaceVersion() { aoqi@0: return nsVersion; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Provides information about how namespaces used in this {@link PolicySourceModel} aoqi@0: * instance should be mapped to their default prefixes when marshalled. aoqi@0: * aoqi@0: * @return immutable map that holds information about namespaces used in the aoqi@0: * model and their mapping to prefixes that should be used when marshalling aoqi@0: * this model. aoqi@0: * @throws PolicyException Thrown if one of the prefix mappers threw an exception. aoqi@0: */ aoqi@0: Map getNamespaceToPrefixMapping() throws PolicyException { aoqi@0: final Map nsToPrefixMap = new HashMap(); aoqi@0: aoqi@0: final Collection namespaces = getUsedNamespaces(); aoqi@0: for (String namespace : namespaces) { aoqi@0: final String prefix = getDefaultPrefix(namespace); aoqi@0: if (prefix != null) { aoqi@0: nsToPrefixMap.put(namespace, prefix); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return nsToPrefixMap; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * An {@code Object.equals(Object obj)} method override. aoqi@0: *

aoqi@0: * When child nodes are tested for equality, the parent policy source model is not considered. Thus two different aoqi@0: * policy source models instances may be equal based on their node content. aoqi@0: */ aoqi@0: @Override aoqi@0: public boolean equals(final Object obj) { aoqi@0: if (this == obj) { aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: if (!(obj instanceof PolicySourceModel)) { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: boolean result = true; aoqi@0: final PolicySourceModel that = (PolicySourceModel) obj; aoqi@0: aoqi@0: result = result && ((this.policyId == null) ? that.policyId == null : this.policyId.equals(that.policyId)); aoqi@0: result = result && ((this.policyName == null) ? that.policyName == null : this.policyName.equals(that.policyName)); aoqi@0: result = result && this.rootNode.equals(that.rootNode); aoqi@0: aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * An {@code Object.hashCode()} method override. aoqi@0: */ aoqi@0: @Override aoqi@0: public int hashCode() { aoqi@0: int result = 17; aoqi@0: aoqi@0: result = 37 * result + ((this.policyId == null) ? 0 : this.policyId.hashCode()); aoqi@0: result = 37 * result + ((this.policyName == null) ? 0 : this.policyName.hashCode()); aoqi@0: result = 37 * result + this.rootNode.hashCode(); aoqi@0: aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a string representation of the object. In general, the toString method aoqi@0: * returns a string that "textually represents" this object. aoqi@0: * aoqi@0: * @return a string representation of the object. aoqi@0: */ aoqi@0: @Override aoqi@0: public String toString() { aoqi@0: final String innerIndent = PolicyUtils.Text.createIndent(1); aoqi@0: final StringBuffer buffer = new StringBuffer(60); aoqi@0: aoqi@0: buffer.append("Policy source model {").append(PolicyUtils.Text.NEW_LINE); aoqi@0: buffer.append(innerIndent).append("policy id = '").append(policyId).append('\'').append(PolicyUtils.Text.NEW_LINE); aoqi@0: buffer.append(innerIndent).append("policy name = '").append(policyName).append('\'').append(PolicyUtils.Text.NEW_LINE); aoqi@0: rootNode.toString(1, buffer).append(PolicyUtils.Text.NEW_LINE).append('}'); aoqi@0: aoqi@0: return buffer.toString(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected PolicySourceModel clone() throws CloneNotSupportedException { aoqi@0: final PolicySourceModel clone = (PolicySourceModel) super.clone(); aoqi@0: aoqi@0: clone.rootNode = this.rootNode.clone(); aoqi@0: try { aoqi@0: clone.rootNode.setParentModel(clone); aoqi@0: } catch (IllegalAccessException e) { aoqi@0: throw LOGGER.logSevereException(new CloneNotSupportedException(LocalizationMessages.WSP_0013_UNABLE_TO_SET_PARENT_MODEL_ON_ROOT()), e); aoqi@0: } aoqi@0: aoqi@0: return clone; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a boolean value indicating whether this policy source model contains references to another policy source models. aoqi@0: *

aoqi@0: * Every source model that references other policies must be expanded before it can be translated into a Policy objects. See aoqi@0: * {@link #expand(PolicySourceModelContext)} and {@link #isExpanded()} for more details. aoqi@0: * aoqi@0: * @return {@code true} or {code false} depending on whether this policy source model contains references to another policy source models. aoqi@0: */ aoqi@0: public boolean containsPolicyReferences() { aoqi@0: return !references.isEmpty(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a boolean value indicating whether this policy source model contains is already expanded (i.e. contains no unexpanded aoqi@0: * policy references) or not. This means that if model does not originally contain any policy references, it is considered as expanded, aoqi@0: * thus this method returns {@code true} in such case. Also this method does not check whether the references policy source models are expanded aoqi@0: * as well, so after expanding this model a value of {@code true} is returned even if referenced models are not expanded yet. Thus each model aoqi@0: * can be considered to be fully expanded only if all policy source models stored in PolicySourceModelContext instance are expanded, provided the aoqi@0: * PolicySourceModelContext instance contains full set of policy source models. aoqi@0: *

aoqi@0: * Every source model that references other policies must be expanded before it can be translated into a Policy object. See aoqi@0: * {@link #expand(PolicySourceModelContext)} and {@link #containsPolicyReferences()} for more details. aoqi@0: * aoqi@0: * @return {@code true} or {@code false} depending on whether this policy source model contains is expanded or not. aoqi@0: */ aoqi@0: private boolean isExpanded() { aoqi@0: return references.isEmpty() || expanded; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Expands current policy model. This means, that if this model contains any (unexpanded) policy references, then the method expands those aoqi@0: * references by placing the content of the referenced policy source models under the policy reference nodes. This operation merely creates aoqi@0: * a link between this and referenced policy source models. Thus any change in the referenced models will be visible wihtin this model as well. aoqi@0: *

aoqi@0: * Please, notice that the method does not check if the referenced models are already expanded nor does the method try to expand unexpanded aoqi@0: * referenced models. This must be preformed manually within client's code. Consecutive calls of this method will have no effect. aoqi@0: *

aoqi@0: * Every source model that references other policies must be expanded before it can be translated into a Policy object. See aoqi@0: * {@link #isExpanded()} and {@link #containsPolicyReferences()} for more details. aoqi@0: * aoqi@0: * @param context a policy source model context holding the set of unmarshalled policy source models within the same context. aoqi@0: * @throws PolicyException Thrown if a referenced policy could not be resolved aoqi@0: */ aoqi@0: public synchronized void expand(final PolicySourceModelContext context) throws PolicyException { aoqi@0: if (!isExpanded()) { aoqi@0: for (ModelNode reference : references) { aoqi@0: final PolicyReferenceData refData = reference.getPolicyReferenceData(); aoqi@0: final String digest = refData.getDigest(); aoqi@0: PolicySourceModel referencedModel; aoqi@0: if (digest == null) { aoqi@0: referencedModel = context.retrieveModel(refData.getReferencedModelUri()); aoqi@0: } else { aoqi@0: referencedModel = context.retrieveModel(refData.getReferencedModelUri(), refData.getDigestAlgorithmUri(), digest); aoqi@0: } aoqi@0: aoqi@0: reference.setReferencedModel(referencedModel); aoqi@0: } aoqi@0: expanded = true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Adds new policy reference to the policy source model. The method is used by aoqi@0: * the ModelNode instances of type POLICY_REFERENCE that need to register themselves aoqi@0: * as policy references in the model. aoqi@0: * aoqi@0: * @param node policy reference model node to be registered as a policy reference aoqi@0: * in this model. aoqi@0: */ aoqi@0: void addNewPolicyReference(final ModelNode node) { aoqi@0: if (node.getType() != ModelNode.Type.POLICY_REFERENCE) { aoqi@0: throw new IllegalArgumentException(LocalizationMessages.WSP_0042_POLICY_REFERENCE_NODE_EXPECTED_INSTEAD_OF(node.getType())); aoqi@0: } aoqi@0: aoqi@0: references.add(node); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Iterates through policy vocabulary and extracts set of namespaces used in aoqi@0: * the policy expression. aoqi@0: * aoqi@0: * @return collection of used namespaces within given policy instance aoqi@0: * @throws PolicyException Thrown if internal processing failed. aoqi@0: */ aoqi@0: private Collection getUsedNamespaces() throws PolicyException { aoqi@0: final Set namespaces = new HashSet(); aoqi@0: namespaces.add(getNamespaceVersion().toString()); aoqi@0: aoqi@0: if (this.policyId != null) { aoqi@0: namespaces.add(PolicyConstants.WSU_NAMESPACE_URI); aoqi@0: } aoqi@0: aoqi@0: final Queue nodesToBeProcessed = new LinkedList(); aoqi@0: nodesToBeProcessed.add(rootNode); aoqi@0: aoqi@0: ModelNode processedNode; aoqi@0: while ((processedNode = nodesToBeProcessed.poll()) != null) { aoqi@0: for (ModelNode child : processedNode.getChildren()) { aoqi@0: if (child.hasChildren()) { aoqi@0: if (!nodesToBeProcessed.offer(child)) { aoqi@0: throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0081_UNABLE_TO_INSERT_CHILD(nodesToBeProcessed, child))); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (child.isDomainSpecific()) { aoqi@0: final AssertionData nodeData = child.getNodeData(); aoqi@0: namespaces.add(nodeData.getName().getNamespaceURI()); aoqi@0: if (nodeData.isPrivateAttributeSet()) { aoqi@0: namespaces.add(PolicyConstants.SUN_POLICY_NAMESPACE_URI); aoqi@0: } aoqi@0: aoqi@0: for (Entry attribute : nodeData.getAttributesSet()) { aoqi@0: namespaces.add(attribute.getKey().getNamespaceURI()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return namespaces; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Method retrieves default prefix for given namespace. Method returns null if aoqi@0: * no default prefix is defined.. aoqi@0: * aoqi@0: * @param namespace to get default prefix for. aoqi@0: * @return default prefix for given namespace. May return {@code null} if the aoqi@0: * default prefix for given namespace is not defined. aoqi@0: */ aoqi@0: private String getDefaultPrefix(final String namespace) { aoqi@0: return namespaceToPrefix.get(namespace); aoqi@0: } aoqi@0: }