Thu, 12 Oct 2017 19:44:07 +0800
merge
1 /*
2 * Copyright (c) 1997, 2010, 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 */
26 package com.sun.xml.internal.ws.policy.sourcemodel;
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;
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;
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 {
56 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicySourceModel.class);
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 }
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 }
76 // Map namespaces to prefixes
77 private final Map<String, String> namespaceToPrefix =
78 new HashMap<String, String>(DEFAULT_NAMESPACE_TO_PREFIX);
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;
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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>();
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 }
220 return nsToPrefixMap;
221 }
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 }
235 if (!(obj instanceof PolicySourceModel)) {
236 return false;
237 }
239 boolean result = true;
240 final PolicySourceModel that = (PolicySourceModel) obj;
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);
246 return result;
247 }
249 /**
250 * An {@code Object.hashCode()} method override.
251 */
252 @Override
253 public int hashCode() {
254 int result = 17;
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();
260 return result;
261 }
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);
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('}');
279 return buffer.toString();
280 }
282 @Override
283 protected PolicySourceModel clone() throws CloneNotSupportedException {
284 final PolicySourceModel clone = (PolicySourceModel) super.clone();
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 }
293 return clone;
294 }
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 }
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 }
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 }
351 reference.setReferencedModel(referencedModel);
352 }
353 expanded = true;
354 }
355 }
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 }
370 references.add(node);
371 }
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());
384 if (this.policyId != null) {
385 namespaces.add(PolicyConstants.WSU_NAMESPACE_URI);
386 }
388 final Queue<ModelNode> nodesToBeProcessed = new LinkedList<ModelNode>();
389 nodesToBeProcessed.add(rootNode);
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 }
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 }
407 for (Entry<QName, String> attribute : nodeData.getAttributesSet()) {
408 namespaces.add(attribute.getKey().getNamespaceURI());
409 }
410 }
411 }
412 }
414 return namespaces;
415 }
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 }