1 /* 2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.ws.policy.sourcemodel; 27 28 import com.sun.xml.internal.ws.policy.sourcemodel.wspolicy.NamespaceVersion; 29 import com.sun.xml.internal.ws.policy.PolicyConstants; 30 import com.sun.xml.internal.ws.policy.PolicyException; 31 import com.sun.xml.internal.ws.policy.privateutil.LocalizationMessages; 32 import com.sun.xml.internal.ws.policy.privateutil.PolicyLogger; 33 import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils; 34 import com.sun.xml.internal.ws.policy.spi.PrefixMapper; 35 36 import java.util.Collection; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.LinkedList; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Map.Entry; 43 import java.util.Queue; 44 import java.util.Set; 45 import javax.xml.namespace.QName; 46 47 /** 48 * This class is a root of unmarshaled policy source structure. Each instance of the class contains factory method 49 * to create new {@link com.sun.xml.internal.ws.policy.sourcemodel.ModelNode} instances associated with the actual model instance. 50 * 51 * @author Marek Potociar 52 * @author Fabian Ritzmann 53 */ 54 public class PolicySourceModel implements Cloneable { 55 56 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicySourceModel.class); 57 58 private static final Map<String, String> DEFAULT_NAMESPACE_TO_PREFIX = new HashMap<String, String>(); 59 static { 60 PrefixMapper[] prefixMappers = PolicyUtils.ServiceProvider.load(PrefixMapper.class); 61 if (prefixMappers != null) { 62 for (PrefixMapper mapper: prefixMappers) { 63 DEFAULT_NAMESPACE_TO_PREFIX.putAll(mapper.getPrefixMap()); 64 } 65 } 66 67 for (NamespaceVersion version : NamespaceVersion.values()) { 68 DEFAULT_NAMESPACE_TO_PREFIX.put(version.toString(), version.getDefaultNamespacePrefix()); 69 } 70 DEFAULT_NAMESPACE_TO_PREFIX.put(PolicyConstants.WSU_NAMESPACE_URI, 71 PolicyConstants.WSU_NAMESPACE_PREFIX); 72 DEFAULT_NAMESPACE_TO_PREFIX.put(PolicyConstants.SUN_POLICY_NAMESPACE_URI, 73 PolicyConstants.SUN_POLICY_NAMESPACE_PREFIX); 74 } 75 76 // Map namespaces to prefixes 77 private final Map<String, String> namespaceToPrefix = 78 new HashMap<String, String>(DEFAULT_NAMESPACE_TO_PREFIX); 79 80 private ModelNode rootNode; 81 private final String policyId; 82 private final String policyName; 83 private final NamespaceVersion nsVersion; 84 private final List<ModelNode> references = new LinkedList<ModelNode>(); // links to policy reference nodes 85 private boolean expanded = false; 86 87 88 /** 89 * Factory method that creates new policy source model instance. 90 * 91 * This method is only intended to be used by code that has no dependencies on 92 * JAX-WS. Otherwise use com.sun.xml.internal.ws.policy.api.SourceModel. 93 * 94 * @param nsVersion The policy version 95 * @return Newly created policy source model instance. 96 */ 97 public static PolicySourceModel createPolicySourceModel(final NamespaceVersion nsVersion) { 98 return new PolicySourceModel(nsVersion); 99 } 100 101 /** 102 * Factory method that creates new policy source model instance and initializes it according to parameters provided. 103 * 104 * This method is only intended to be used by code that has no dependencies on 105 * JAX-WS. Otherwise use com.sun.xml.internal.ws.policy.api.SourceModel. 106 * 107 * @param nsVersion The policy version 108 * @param policyId local policy identifier - relative URI. May be {@code null}. 109 * @param policyName global policy identifier - absolute policy expression URI. May be {@code null}. 110 * @return Newly created policy source model instance with its name and id properly set. 111 */ 112 public static PolicySourceModel createPolicySourceModel(final NamespaceVersion nsVersion, final String policyId, final String policyName) { 113 return new PolicySourceModel(nsVersion, policyId, policyName); 114 } 115 116 /** 117 * Constructor that creates a new policy source model instance without any 118 * id or name identifier. The namespace-to-prefix map is initialized with mapping 119 * of policy namespace to the default value set by 120 * {@link PolicyConstants#POLICY_NAMESPACE_PREFIX POLICY_NAMESPACE_PREFIX constant}. 121 * 122 * @param nsVersion The WS-Policy version. 123 */ 124 private PolicySourceModel(NamespaceVersion nsVersion) { 125 this(nsVersion, null, null); 126 } 127 128 /** 129 * Constructor that creates a new policy source model instance with given 130 * id or name identifier. 131 * 132 * @param nsVersion The WS-Policy version. 133 * @param policyId Relative policy reference within an XML document. May be {@code null}. 134 * @param policyName Absolute IRI of policy expression. May be {@code null}. 135 */ 136 private PolicySourceModel(NamespaceVersion nsVersion, String policyId, String policyName) { 137 this(nsVersion, policyId, policyName, null); 138 } 139 140 /** 141 * Constructor that creates a new policy source model instance with given 142 * id or name identifier and a set of PrefixMappers. 143 * 144 * This constructor is intended to be used by the JAX-WS com.sun.xml.internal.ws.policy.api.SourceModel. 145 * 146 * @param nsVersion The WS-Policy version. 147 * @param policyId Relative policy reference within an XML document. May be {@code null}. 148 * @param policyName Absolute IRI of policy expression. May be {@code null}. 149 * @param prefixMappers A collection of PrefixMappers to be used with this instance. May be {@code null}. 150 */ 151 protected PolicySourceModel(NamespaceVersion nsVersion, String policyId, 152 String policyName, Collection<PrefixMapper> prefixMappers) { 153 this.rootNode = ModelNode.createRootPolicyNode(this); 154 this.nsVersion = nsVersion; 155 this.policyId = policyId; 156 this.policyName = policyName; 157 if (prefixMappers != null) { 158 for (PrefixMapper prefixMapper : prefixMappers) { 159 this.namespaceToPrefix.putAll(prefixMapper.getPrefixMap()); 160 } 161 } 162 } 163 164 /** 165 * Returns a root node of this policy source model. It is allways of POLICY type. 166 * 167 * @return root policy source model node - allways of POLICY type. 168 */ 169 public ModelNode getRootNode() { 170 return rootNode; 171 } 172 173 /** 174 * Returns a policy name of this policy source model. 175 * 176 * @return policy name. 177 */ 178 public String getPolicyName() { 179 return policyName; 180 } 181 182 /** 183 * Returns a policy ID of this policy source model. 184 * 185 * @return policy ID. 186 */ 187 public String getPolicyId() { 188 return policyId; 189 } 190 191 /** 192 * Returns an original namespace version of this policy source model. 193 * 194 * @return namespace version. 195 */ 196 public NamespaceVersion getNamespaceVersion() { 197 return nsVersion; 198 } 199 200 /** 201 * Provides information about how namespaces used in this {@link PolicySourceModel} 202 * instance should be mapped to their default prefixes when marshalled. 203 * 204 * @return immutable map that holds information about namespaces used in the 205 * model and their mapping to prefixes that should be used when marshalling 206 * this model. 207 * @throws PolicyException Thrown if one of the prefix mappers threw an exception. 208 */ 209 Map<String, String> getNamespaceToPrefixMapping() throws PolicyException { 210 final Map<String, String> nsToPrefixMap = new HashMap<String, String>(); 211 212 final Collection<String> namespaces = getUsedNamespaces(); 213 for (String namespace : namespaces) { 214 final String prefix = getDefaultPrefix(namespace); 215 if (prefix != null) { 216 nsToPrefixMap.put(namespace, prefix); 217 } 218 } 219 220 return nsToPrefixMap; 221 } 222 223 /** 224 * An {@code Object.equals(Object obj)} method override. 225 * <p/> 226 * When child nodes are tested for equality, the parent policy source model is not considered. Thus two different 227 * policy source models instances may be equal based on their node content. 228 */ 229 @Override 230 public boolean equals(final Object obj) { 231 if (this == obj) { 232 return true; 233 } 234 235 if (!(obj instanceof PolicySourceModel)) { 236 return false; 237 } 238 239 boolean result = true; 240 final PolicySourceModel that = (PolicySourceModel) obj; 241 242 result = result && ((this.policyId == null) ? that.policyId == null : this.policyId.equals(that.policyId)); 243 result = result && ((this.policyName == null) ? that.policyName == null : this.policyName.equals(that.policyName)); 244 result = result && this.rootNode.equals(that.rootNode); 245 246 return result; 247 } 248 249 /** 250 * An {@code Object.hashCode()} method override. 251 */ 252 @Override 253 public int hashCode() { 254 int result = 17; 255 256 result = 37 * result + ((this.policyId == null) ? 0 : this.policyId.hashCode()); 257 result = 37 * result + ((this.policyName == null) ? 0 : this.policyName.hashCode()); 258 result = 37 * result + this.rootNode.hashCode(); 259 260 return result; 261 } 262 263 /** 264 * Returns a string representation of the object. In general, the <code>toString</code> method 265 * returns a string that "textually represents" this object. 266 * 267 * @return a string representation of the object. 268 */ 269 @Override 270 public String toString() { 271 final String innerIndent = PolicyUtils.Text.createIndent(1); 272 final StringBuffer buffer = new StringBuffer(60); 273 274 buffer.append("Policy source model {").append(PolicyUtils.Text.NEW_LINE); 275 buffer.append(innerIndent).append("policy id = '").append(policyId).append('\'').append(PolicyUtils.Text.NEW_LINE); 276 buffer.append(innerIndent).append("policy name = '").append(policyName).append('\'').append(PolicyUtils.Text.NEW_LINE); 277 rootNode.toString(1, buffer).append(PolicyUtils.Text.NEW_LINE).append('}'); 278 279 return buffer.toString(); 280 } 281 282 @Override 283 protected PolicySourceModel clone() throws CloneNotSupportedException { 284 final PolicySourceModel clone = (PolicySourceModel) super.clone(); 285 286 clone.rootNode = this.rootNode.clone(); 287 try { 288 clone.rootNode.setParentModel(clone); 289 } catch (IllegalAccessException e) { 290 throw LOGGER.logSevereException(new CloneNotSupportedException(LocalizationMessages.WSP_0013_UNABLE_TO_SET_PARENT_MODEL_ON_ROOT()), e); 291 } 292 293 return clone; 294 } 295 296 /** 297 * Returns a boolean value indicating whether this policy source model contains references to another policy source models. 298 * <p/> 299 * Every source model that references other policies must be expanded before it can be translated into a Policy objects. See 300 * {@link #expand(PolicySourceModelContext)} and {@link #isExpanded()} for more details. 301 * 302 * @return {@code true} or {@code false} depending on whether this policy source model contains references to another policy source models. 303 */ 304 public boolean containsPolicyReferences() { 305 return !references.isEmpty(); 306 } 307 308 /** 309 * Returns a boolean value indicating whether this policy source model contains is already expanded (i.e. contains no unexpanded 310 * policy references) or not. This means that if model does not originally contain any policy references, it is considered as expanded, 311 * thus this method returns {@code true} in such case. Also this method does not check whether the references policy source models are expanded 312 * 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 313 * can be considered to be fully expanded only if all policy source models stored in PolicySourceModelContext instance are expanded, provided the 314 * PolicySourceModelContext instance contains full set of policy source models. 315 * <p/> 316 * Every source model that references other policies must be expanded before it can be translated into a Policy object. See 317 * {@link #expand(PolicySourceModelContext)} and {@link #containsPolicyReferences()} for more details. 318 * 319 * @return {@code true} or {@code false} depending on whether this policy source model contains is expanded or not. 320 */ 321 private boolean isExpanded() { 322 return references.isEmpty() || expanded; 323 } 324 325 /** 326 * Expands current policy model. This means, that if this model contains any (unexpanded) policy references, then the method expands those 327 * references by placing the content of the referenced policy source models under the policy reference nodes. This operation merely creates 328 * a link between this and referenced policy source models. Thus any change in the referenced models will be visible wihtin this model as well. 329 * <p/> 330 * Please, notice that the method does not check if the referenced models are already expanded nor does the method try to expand unexpanded 331 * referenced models. This must be preformed manually within client's code. Consecutive calls of this method will have no effect. 332 * <p/> 333 * Every source model that references other policies must be expanded before it can be translated into a Policy object. See 334 * {@link #isExpanded()} and {@link #containsPolicyReferences()} for more details. 335 * 336 * @param context a policy source model context holding the set of unmarshalled policy source models within the same context. 337 * @throws PolicyException Thrown if a referenced policy could not be resolved 338 */ 339 public synchronized void expand(final PolicySourceModelContext context) throws PolicyException { 340 if (!isExpanded()) { 341 for (ModelNode reference : references) { 342 final PolicyReferenceData refData = reference.getPolicyReferenceData(); 343 final String digest = refData.getDigest(); 344 PolicySourceModel referencedModel; 345 if (digest == null) { 346 referencedModel = context.retrieveModel(refData.getReferencedModelUri()); 347 } else { 348 referencedModel = context.retrieveModel(refData.getReferencedModelUri(), refData.getDigestAlgorithmUri(), digest); 349 } 350 351 reference.setReferencedModel(referencedModel); 352 } 353 expanded = true; 354 } 355 } 356 357 /** 358 * Adds new policy reference to the policy source model. The method is used by 359 * the ModelNode instances of type POLICY_REFERENCE that need to register themselves 360 * as policy references in the model. 361 * 362 * @param node policy reference model node to be registered as a policy reference 363 * in this model. 364 */ 365 void addNewPolicyReference(final ModelNode node) { 366 if (node.getType() != ModelNode.Type.POLICY_REFERENCE) { 367 throw new IllegalArgumentException(LocalizationMessages.WSP_0042_POLICY_REFERENCE_NODE_EXPECTED_INSTEAD_OF(node.getType())); 368 } 369 370 references.add(node); 371 } 372 373 /** 374 * Iterates through policy vocabulary and extracts set of namespaces used in 375 * the policy expression. 376 * 377 * @return collection of used namespaces within given policy instance 378 * @throws PolicyException Thrown if internal processing failed. 379 */ 380 private Collection<String> getUsedNamespaces() throws PolicyException { 381 final Set<String> namespaces = new HashSet<String>(); 382 namespaces.add(getNamespaceVersion().toString()); 383 384 if (this.policyId != null) { 385 namespaces.add(PolicyConstants.WSU_NAMESPACE_URI); 386 } 387 388 final Queue<ModelNode> nodesToBeProcessed = new LinkedList<ModelNode>(); 389 nodesToBeProcessed.add(rootNode); 390 391 ModelNode processedNode; 392 while ((processedNode = nodesToBeProcessed.poll()) != null) { 393 for (ModelNode child : processedNode.getChildren()) { 394 if (child.hasChildren()) { 395 if (!nodesToBeProcessed.offer(child)) { 396 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0081_UNABLE_TO_INSERT_CHILD(nodesToBeProcessed, child))); 397 } 398 } 399 400 if (child.isDomainSpecific()) { 401 final AssertionData nodeData = child.getNodeData(); 402 namespaces.add(nodeData.getName().getNamespaceURI()); 403 if (nodeData.isPrivateAttributeSet()) { 404 namespaces.add(PolicyConstants.SUN_POLICY_NAMESPACE_URI); 405 } 406 407 for (Entry<QName, String> attribute : nodeData.getAttributesSet()) { 408 namespaces.add(attribute.getKey().getNamespaceURI()); 409 } 410 } 411 } 412 } 413 414 return namespaces; 415 } 416 417 /** 418 * Method retrieves default prefix for given namespace. Method returns null if 419 * no default prefix is defined.. 420 * 421 * @param namespace to get default prefix for. 422 * @return default prefix for given namespace. May return {@code null} if the 423 * default prefix for given namespace is not defined. 424 */ 425 private String getDefaultPrefix(final String namespace) { 426 return namespaceToPrefix.get(namespace); 427 } 428 }