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 java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Queue;
37 import com.sun.xml.internal.ws.policy.AssertionSet;
38 import com.sun.xml.internal.ws.policy.Policy;
39 import com.sun.xml.internal.ws.policy.PolicyAssertion;
40 import com.sun.xml.internal.ws.policy.PolicyException;
41 import com.sun.xml.internal.ws.policy.privateutil.LocalizationMessages;
42 import com.sun.xml.internal.ws.policy.privateutil.PolicyLogger;
43 import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
44 import com.sun.xml.internal.ws.policy.spi.AssertionCreationException;
45 import com.sun.xml.internal.ws.policy.spi.PolicyAssertionCreator;
47 /**
48 * This class provides a method for translating a {@link PolicySourceModel} structure to a normalized {@link Policy} expression.
49 * The resulting Policy is disconnected from its model, thus any additional changes in the model will have no effect on the Policy
50 * expression.
51 *
52 * @author Marek Potociar
53 * @author Fabian Ritzmann
54 */
55 public class PolicyModelTranslator {
57 private static final class ContentDecomposition {
58 final List<Collection<ModelNode>> exactlyOneContents = new LinkedList<Collection<ModelNode>>();
59 final List<ModelNode> assertions = new LinkedList<ModelNode>();
61 void reset() {
62 exactlyOneContents.clear();
63 assertions.clear();
64 }
65 }
67 private static final class RawAssertion {
68 ModelNode originalNode; // used to initialize nestedPolicy and nestedAssertions in the constructor of RawAlternative
69 Collection<RawAlternative> nestedAlternatives = null;
70 final Collection<ModelNode> parameters;
72 RawAssertion(ModelNode originalNode, Collection<ModelNode> parameters) {
73 this.parameters = parameters;
74 this.originalNode = originalNode;
75 }
76 }
78 private static final class RawAlternative {
79 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicyModelTranslator.RawAlternative.class);
81 final List<RawPolicy> allNestedPolicies = new LinkedList<RawPolicy>(); // used to track the nested policies which need to be normalized
82 final Collection<RawAssertion> nestedAssertions;
84 RawAlternative(Collection<ModelNode> assertionNodes) throws PolicyException {
85 this.nestedAssertions = new LinkedList<RawAssertion>();
86 for (ModelNode node : assertionNodes) {
87 RawAssertion assertion = new RawAssertion(node, new LinkedList<ModelNode>());
88 nestedAssertions.add(assertion);
90 for (ModelNode assertionNodeChild : assertion.originalNode.getChildren()) {
91 switch (assertionNodeChild.getType()) {
92 case ASSERTION_PARAMETER_NODE:
93 assertion.parameters.add(assertionNodeChild);
94 break;
95 case POLICY:
96 case POLICY_REFERENCE:
97 if (assertion.nestedAlternatives == null) {
98 assertion.nestedAlternatives = new LinkedList<RawAlternative>();
99 RawPolicy nestedPolicy;
100 if (assertionNodeChild.getType() == ModelNode.Type.POLICY) {
101 nestedPolicy = new RawPolicy(assertionNodeChild, assertion.nestedAlternatives);
102 } else {
103 nestedPolicy = new RawPolicy(getReferencedModelRootNode(assertionNodeChild), assertion.nestedAlternatives);
104 }
105 this.allNestedPolicies.add(nestedPolicy);
106 } else {
107 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0006_UNEXPECTED_MULTIPLE_POLICY_NODES()));
108 }
109 break;
110 default:
111 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0008_UNEXPECTED_CHILD_MODEL_TYPE(assertionNodeChild.getType())));
112 }
113 }
114 }
115 }
117 }
119 private static final class RawPolicy {
120 final Collection<ModelNode> originalContent;
121 final Collection<RawAlternative> alternatives;
123 RawPolicy(ModelNode policyNode, Collection<RawAlternative> alternatives) {
124 originalContent = policyNode.getChildren();
125 this.alternatives = alternatives;
126 }
127 }
129 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(PolicyModelTranslator.class);
131 private static final PolicyAssertionCreator defaultCreator = new DefaultPolicyAssertionCreator();
133 private final Map<String, PolicyAssertionCreator> assertionCreators;
136 private PolicyModelTranslator() throws PolicyException {
137 this(null);
138 }
140 protected PolicyModelTranslator(final Collection<PolicyAssertionCreator> creators) throws PolicyException {
141 LOGGER.entering(creators);
143 final Collection<PolicyAssertionCreator> allCreators = new LinkedList<PolicyAssertionCreator>();
144 final PolicyAssertionCreator[] discoveredCreators = PolicyUtils.ServiceProvider.load(PolicyAssertionCreator.class);
145 for (PolicyAssertionCreator creator : discoveredCreators) {
146 allCreators.add(creator);
147 }
148 if (creators != null) {
149 for (PolicyAssertionCreator creator : creators) {
150 allCreators.add(creator);
151 }
152 }
154 final Map<String, PolicyAssertionCreator> pacMap = new HashMap<String, PolicyAssertionCreator>();
155 for (PolicyAssertionCreator creator : allCreators) {
156 final String[] supportedURIs = creator.getSupportedDomainNamespaceURIs();
157 final String creatorClassName = creator.getClass().getName();
159 if (supportedURIs == null || supportedURIs.length == 0) {
160 LOGGER.warning(LocalizationMessages.WSP_0077_ASSERTION_CREATOR_DOES_NOT_SUPPORT_ANY_URI(creatorClassName));
161 continue;
162 }
164 for (String supportedURI : supportedURIs) {
165 LOGGER.config(LocalizationMessages.WSP_0078_ASSERTION_CREATOR_DISCOVERED(creatorClassName, supportedURI));
166 if (supportedURI == null || supportedURI.length() == 0) {
167 throw LOGGER.logSevereException(new PolicyException(
168 LocalizationMessages.WSP_0070_ERROR_REGISTERING_ASSERTION_CREATOR(creatorClassName)));
169 }
171 final PolicyAssertionCreator oldCreator = pacMap.put(supportedURI, creator);
172 if (oldCreator != null) {
173 throw LOGGER.logSevereException(new PolicyException(
174 LocalizationMessages.WSP_0071_ERROR_MULTIPLE_ASSERTION_CREATORS_FOR_NAMESPACE(
175 supportedURI, oldCreator.getClass().getName(), creator.getClass().getName())));
176 }
177 }
178 }
180 this.assertionCreators = Collections.unmodifiableMap(pacMap);
181 LOGGER.exiting();
182 }
184 /**
185 * Method returns thread-safe policy model translator instance.
186 *
187 * This method is only intended to be used by code that has no dependencies on
188 * JAX-WS. Otherwise use com.sun.xml.internal.ws.policy.api.ModelTranslator.
189 *
190 * @return A policy model translator instance.
191 * @throws PolicyException If instantiating a PolicyAssertionCreator failed.
192 */
193 public static PolicyModelTranslator getTranslator() throws PolicyException {
194 return new PolicyModelTranslator();
195 }
197 /**
198 * The method translates {@link PolicySourceModel} structure into normalized {@link Policy} expression. The resulting Policy
199 * is disconnected from its model, thus any additional changes in model will have no effect on the Policy expression.
200 *
201 * @param model the model to be translated into normalized policy expression. Must not be {@code null}.
202 * @return translated policy expression in it's normalized form.
203 * @throws PolicyException in case of translation failure
204 */
205 public Policy translate(final PolicySourceModel model) throws PolicyException {
206 LOGGER.entering(model);
208 if (model == null) {
209 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0043_POLICY_MODEL_TRANSLATION_ERROR_INPUT_PARAM_NULL()));
210 }
212 PolicySourceModel localPolicyModelCopy;
213 try {
214 localPolicyModelCopy = model.clone();
215 } catch (CloneNotSupportedException e) {
216 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0016_UNABLE_TO_CLONE_POLICY_SOURCE_MODEL(), e));
217 }
219 final String policyId = localPolicyModelCopy.getPolicyId();
220 final String policyName = localPolicyModelCopy.getPolicyName();
222 final Collection<AssertionSet> alternatives = createPolicyAlternatives(localPolicyModelCopy);
223 LOGGER.finest(LocalizationMessages.WSP_0052_NUMBER_OF_ALTERNATIVE_COMBINATIONS_CREATED(alternatives.size()));
225 Policy policy = null;
226 if (alternatives.size() == 0) {
227 policy = Policy.createNullPolicy(model.getNamespaceVersion(), policyName, policyId);
228 LOGGER.finest(LocalizationMessages.WSP_0055_NO_ALTERNATIVE_COMBINATIONS_CREATED());
229 } else if (alternatives.size() == 1 && alternatives.iterator().next().isEmpty()) {
230 policy = Policy.createEmptyPolicy(model.getNamespaceVersion(), policyName, policyId);
231 LOGGER.finest(LocalizationMessages.WSP_0026_SINGLE_EMPTY_ALTERNATIVE_COMBINATION_CREATED());
232 } else {
233 policy = Policy.createPolicy(model.getNamespaceVersion(), policyName, policyId, alternatives);
234 LOGGER.finest(LocalizationMessages.WSP_0057_N_ALTERNATIVE_COMBINATIONS_M_POLICY_ALTERNATIVES_CREATED(alternatives.size(), policy.getNumberOfAssertionSets()));
235 }
237 LOGGER.exiting(policy);
238 return policy;
239 }
241 /**
242 * Method creates policy alternatives according to provided model. The model structure is modified in the process.
243 *
244 * @return created policy alternatives resulting from policy source model.
245 */
246 private Collection<AssertionSet> createPolicyAlternatives(final PolicySourceModel model) throws PolicyException {
247 // creating global method variables
248 final ContentDecomposition decomposition = new ContentDecomposition();
250 // creating processing queue and starting the processing iterations
251 final Queue<RawPolicy> policyQueue = new LinkedList<RawPolicy>();
252 final Queue<Collection<ModelNode>> contentQueue = new LinkedList<Collection<ModelNode>>();
254 final RawPolicy rootPolicy = new RawPolicy(model.getRootNode(), new LinkedList<RawAlternative>());
255 RawPolicy processedPolicy = rootPolicy;
256 do {
257 Collection<ModelNode> processedContent = processedPolicy.originalContent;
258 do {
259 decompose(processedContent, decomposition);
260 if (decomposition.exactlyOneContents.isEmpty()) {
261 final RawAlternative alternative = new RawAlternative(decomposition.assertions);
262 processedPolicy.alternatives.add(alternative);
263 if (!alternative.allNestedPolicies.isEmpty()) {
264 policyQueue.addAll(alternative.allNestedPolicies);
265 }
266 } else { // we have a non-empty collection of exactly ones
267 final Collection<Collection<ModelNode>> combinations = PolicyUtils.Collections.combine(decomposition.assertions, decomposition.exactlyOneContents, false);
268 if (combinations != null && !combinations.isEmpty()) {
269 // processed alternative was split into some new alternatives, which we need to process
270 contentQueue.addAll(combinations);
271 }
272 }
273 } while ((processedContent = contentQueue.poll()) != null);
274 } while ((processedPolicy = policyQueue.poll()) != null);
276 // normalize nested policies to contain single alternative only
277 final Collection<AssertionSet> assertionSets = new LinkedList<AssertionSet>();
278 for (RawAlternative rootAlternative : rootPolicy.alternatives) {
279 final Collection<AssertionSet> normalizedAlternatives = normalizeRawAlternative(rootAlternative);
280 assertionSets.addAll(normalizedAlternatives);
281 }
283 return assertionSets;
284 }
286 /**
287 * Decomposes the unprocessed alternative content into two different collections:
288 * <p/>
289 * Content of 'EXACTLY_ONE' child nodes is expanded and placed in one list and
290 * 'ASSERTION' nodes are placed into other list. Direct 'ALL' and 'POLICY' child nodes are 'dissolved' in the process.
291 *
292 * Method reuses precreated ContentDecomposition object, which is reset before reuse.
293 */
294 private void decompose(final Collection<ModelNode> content, final ContentDecomposition decomposition) throws PolicyException {
295 decomposition.reset();
297 final Queue<ModelNode> allContentQueue = new LinkedList<ModelNode>(content);
298 ModelNode node;
299 while ((node = allContentQueue.poll()) != null) {
300 // dissolving direct 'POLICY', 'POLICY_REFERENCE' and 'ALL' child nodes
301 switch (node.getType()) {
302 case POLICY :
303 case ALL :
304 allContentQueue.addAll(node.getChildren());
305 break;
306 case POLICY_REFERENCE :
307 allContentQueue.addAll(getReferencedModelRootNode(node).getChildren());
308 break;
309 case EXACTLY_ONE :
310 decomposition.exactlyOneContents.add(expandsExactlyOneContent(node.getChildren()));
311 break;
312 case ASSERTION :
313 decomposition.assertions.add(node);
314 break;
315 default :
316 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0007_UNEXPECTED_MODEL_NODE_TYPE_FOUND(node.getType())));
317 }
318 }
319 }
321 private static ModelNode getReferencedModelRootNode(final ModelNode policyReferenceNode) throws PolicyException {
322 final PolicySourceModel referencedModel = policyReferenceNode.getReferencedModel();
323 if (referencedModel == null) {
324 final PolicyReferenceData refData = policyReferenceNode.getPolicyReferenceData();
325 if (refData == null) {
326 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0041_POLICY_REFERENCE_NODE_FOUND_WITH_NO_POLICY_REFERENCE_IN_IT()));
327 } else {
328 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0010_UNEXPANDED_POLICY_REFERENCE_NODE_FOUND_REFERENCING(refData.getReferencedModelUri())));
329 }
330 } else {
331 return referencedModel.getRootNode();
332 }
333 }
335 /**
336 * Expands content of 'EXACTLY_ONE' node. Direct 'EXACTLY_ONE' child nodes are dissolved in the process.
337 */
338 private Collection<ModelNode> expandsExactlyOneContent(final Collection<ModelNode> content) throws PolicyException {
339 final Collection<ModelNode> result = new LinkedList<ModelNode>();
341 final Queue<ModelNode> eoContentQueue = new LinkedList<ModelNode>(content);
342 ModelNode node;
343 while ((node = eoContentQueue.poll()) != null) {
344 // dissolving direct 'EXACTLY_ONE' child nodes
345 switch (node.getType()) {
346 case POLICY :
347 case ALL :
348 case ASSERTION :
349 result.add(node);
350 break;
351 case POLICY_REFERENCE :
352 result.add(getReferencedModelRootNode(node));
353 break;
354 case EXACTLY_ONE :
355 eoContentQueue.addAll(node.getChildren());
356 break;
357 default :
358 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0001_UNSUPPORTED_MODEL_NODE_TYPE(node.getType())));
359 }
360 }
362 return result;
363 }
365 private List<AssertionSet> normalizeRawAlternative(final RawAlternative alternative) throws AssertionCreationException, PolicyException {
366 final List<PolicyAssertion> normalizedContentBase = new LinkedList<PolicyAssertion>();
367 final Collection<List<PolicyAssertion>> normalizedContentOptions = new LinkedList<List<PolicyAssertion>>();
368 if (!alternative.nestedAssertions.isEmpty()) {
369 final Queue<RawAssertion> nestedAssertionsQueue = new LinkedList<RawAssertion>(alternative.nestedAssertions);
370 RawAssertion rawAssertion;
371 while((rawAssertion = nestedAssertionsQueue.poll()) != null) {
372 final List<PolicyAssertion> normalized = normalizeRawAssertion(rawAssertion);
373 // if there is only a single result, we can add it direclty to the content base collection
374 // more elements in the result indicate that we will have to create combinations
375 if (normalized.size() == 1) {
376 normalizedContentBase.addAll(normalized);
377 } else {
378 normalizedContentOptions.add(normalized);
379 }
380 }
381 }
383 final List<AssertionSet> options = new LinkedList<AssertionSet>();
384 if (normalizedContentOptions.isEmpty()) {
385 // we do not have any options to combine => returning this assertion
386 options.add(AssertionSet.createAssertionSet(normalizedContentBase));
387 } else {
388 // we have some options to combine => creating assertion options based on content combinations
389 final Collection<Collection<PolicyAssertion>> contentCombinations = PolicyUtils.Collections.combine(normalizedContentBase, normalizedContentOptions, true);
390 for (Collection<PolicyAssertion> contentOption : contentCombinations) {
391 options.add(AssertionSet.createAssertionSet(contentOption));
392 }
393 }
394 return options;
395 }
397 private List<PolicyAssertion> normalizeRawAssertion(final RawAssertion assertion) throws AssertionCreationException, PolicyException {
398 List<PolicyAssertion> parameters;
399 if (assertion.parameters.isEmpty()) {
400 parameters = null;
401 } else {
402 parameters = new ArrayList<PolicyAssertion>(assertion.parameters.size());
403 for (ModelNode parameterNode : assertion.parameters) {
404 parameters.add(createPolicyAssertionParameter(parameterNode));
405 }
406 }
408 final List<AssertionSet> nestedAlternatives = new LinkedList<AssertionSet>();
409 if (assertion.nestedAlternatives != null && !assertion.nestedAlternatives.isEmpty()) {
410 final Queue<RawAlternative> nestedAlternativeQueue = new LinkedList<RawAlternative>(assertion.nestedAlternatives);
411 RawAlternative rawAlternative;
412 while((rawAlternative = nestedAlternativeQueue.poll()) != null) {
413 nestedAlternatives.addAll(normalizeRawAlternative(rawAlternative));
414 }
415 // if there is only a single result, we can add it direclty to the content base collection
416 // more elements in the result indicate that we will have to create combinations
417 }
419 final List<PolicyAssertion> assertionOptions = new LinkedList<PolicyAssertion>();
420 final boolean nestedAlternativesAvailable = !nestedAlternatives.isEmpty();
421 if (nestedAlternativesAvailable) {
422 for (AssertionSet nestedAlternative : nestedAlternatives) {
423 assertionOptions.add(createPolicyAssertion(assertion.originalNode.getNodeData(), parameters, nestedAlternative));
424 }
425 } else {
426 assertionOptions.add(createPolicyAssertion(assertion.originalNode.getNodeData(), parameters, null));
427 }
428 return assertionOptions;
429 }
431 private PolicyAssertion createPolicyAssertionParameter(final ModelNode parameterNode) throws AssertionCreationException, PolicyException {
432 if (parameterNode.getType() != ModelNode.Type.ASSERTION_PARAMETER_NODE) {
433 throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0065_INCONSISTENCY_IN_POLICY_SOURCE_MODEL(parameterNode.getType())));
434 }
436 List<PolicyAssertion> childParameters = null;
437 if (parameterNode.hasChildren()) {
438 childParameters = new ArrayList<PolicyAssertion>(parameterNode.childrenSize());
439 for (ModelNode childParameterNode : parameterNode) {
440 childParameters.add(createPolicyAssertionParameter(childParameterNode));
441 }
442 }
444 return createPolicyAssertion(parameterNode.getNodeData(), childParameters, null /* parameters do not have any nested alternatives */);
445 }
447 private PolicyAssertion createPolicyAssertion(final AssertionData data, final Collection<PolicyAssertion> assertionParameters, final AssertionSet nestedAlternative) throws AssertionCreationException {
448 final String assertionNamespace = data.getName().getNamespaceURI();
449 final PolicyAssertionCreator domainSpecificPAC = assertionCreators.get(assertionNamespace);
452 if (domainSpecificPAC == null) {
453 return defaultCreator.createAssertion(data, assertionParameters, nestedAlternative, null);
454 } else {
455 return domainSpecificPAC.createAssertion(data, assertionParameters, nestedAlternative, defaultCreator);
456 }
457 }
459 }