001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.policy.sourcemodel;
038:
039: import java.util.ArrayList;
040: import java.util.Collection;
041: import java.util.Collections;
042: import java.util.HashMap;
043: import java.util.LinkedList;
044: import java.util.List;
045: import java.util.Map;
046: import java.util.Queue;
047:
048: import com.sun.xml.ws.policy.AssertionSet;
049: import com.sun.xml.ws.policy.Policy;
050: import com.sun.xml.ws.policy.PolicyAssertion;
051: import com.sun.xml.ws.policy.PolicyException;
052: import com.sun.xml.ws.policy.privateutil.LocalizationMessages;
053: import com.sun.xml.ws.policy.privateutil.PolicyLogger;
054: import com.sun.xml.ws.policy.privateutil.PolicyUtils;
055: import com.sun.xml.ws.policy.spi.AssertionCreationException;
056: import com.sun.xml.ws.policy.spi.PolicyAssertionCreator;
057:
058: /**
059: * This class provides method for translating {@link PolicySourceModel} structure into normalized {@link Policy} expression.
060: * The resulting Policy is disconnected from its model, thus any additional changes in model will have no effect on the Policy
061: * expression.
062: *
063: * @author Marek Potociar
064: */
065: public final class PolicyModelTranslator {
066:
067: private static final class ContentDecomposition {
068: final List<Collection<ModelNode>> exactlyOneContents = new LinkedList<Collection<ModelNode>>();
069: final List<ModelNode> assertions = new LinkedList<ModelNode>();
070:
071: void reset() {
072: exactlyOneContents.clear();
073: assertions.clear();
074: }
075: }
076:
077: private static final class RawAssertion {
078: ModelNode originalNode; // used to initialize nestedPolicy and nestedAssertions in the constructor of RawAlternative
079: Collection<RawAlternative> nestedAlternatives = null;
080: final Collection<ModelNode> parameters;
081:
082: RawAssertion(ModelNode originalNode,
083: Collection<ModelNode> parameters) {
084: this .parameters = parameters;
085: this .originalNode = originalNode;
086: }
087: }
088:
089: private static final class RawAlternative {
090: private static final PolicyLogger LOGGER = PolicyLogger
091: .getLogger(PolicyModelTranslator.RawAlternative.class);
092:
093: final List<RawPolicy> allNestedPolicies = new LinkedList<RawPolicy>(); // used to track the nested policies which need to be normalized
094: final Collection<RawAssertion> nestedAssertions;
095:
096: RawAlternative(Collection<ModelNode> assertionNodes)
097: throws PolicyException {
098: this .nestedAssertions = new LinkedList<RawAssertion>();
099: for (ModelNode node : assertionNodes) {
100: RawAssertion assertion = new RawAssertion(node,
101: new LinkedList<ModelNode>());
102: nestedAssertions.add(assertion);
103:
104: for (ModelNode assertionNodeChild : assertion.originalNode
105: .getContent()) {
106: switch (assertionNodeChild.getType()) {
107: case ASSERTION_PARAMETER_NODE:
108: assertion.parameters.add(assertionNodeChild);
109: break;
110: case POLICY:
111: case POLICY_REFERENCE:
112: if (assertion.nestedAlternatives == null) {
113: assertion.nestedAlternatives = new LinkedList<RawAlternative>();
114: RawPolicy nestedPolicy;
115: if (assertionNodeChild.getType() == ModelNode.Type.POLICY) {
116: nestedPolicy = new RawPolicy(
117: assertionNodeChild,
118: assertion.nestedAlternatives);
119: } else {
120: nestedPolicy = new RawPolicy(
121: getReferencedModelRootNode(assertionNodeChild),
122: assertion.nestedAlternatives);
123: }
124: this .allNestedPolicies.add(nestedPolicy);
125: } else {
126: throw LOGGER
127: .logSevereException(new PolicyException(
128: LocalizationMessages
129: .WSP_0006_UNEXPECTED_MULTIPLE_POLICY_NODES()));
130: }
131: break;
132: default:
133: throw LOGGER
134: .logSevereException(new PolicyException(
135: LocalizationMessages
136: .WSP_0008_UNEXPECTED_CHILD_MODEL_TYPE(assertionNodeChild
137: .getType())));
138: }
139: }
140: }
141: }
142:
143: }
144:
145: private static final class RawPolicy {
146: final Collection<ModelNode> originalContent;
147: final Collection<RawAlternative> alternatives;
148:
149: RawPolicy(ModelNode policyNode,
150: Collection<RawAlternative> alternatives) {
151: originalContent = policyNode.getContent();
152: this .alternatives = alternatives;
153: }
154: }
155:
156: private static final PolicyLogger LOGGER = PolicyLogger
157: .getLogger(PolicyModelTranslator.class);
158: private static final PolicyModelTranslator translator = new PolicyModelTranslator();
159:
160: private static final PolicyAssertionCreator defaultCreator = new DefaultPolicyAssertionCreator();
161: private static final Map<String, PolicyAssertionCreator> assertionCreators;
162: private static final PolicyException initialException;
163:
164: static {
165: Map<String, PolicyAssertionCreator> tempMap = null;
166: PolicyException tempException = null;
167: try {
168: tempMap = initPolicyAssertionCreatorsMap();
169: } catch (PolicyException ex) {
170: tempException = ex;
171: } finally {
172: assertionCreators = tempMap;
173: initialException = tempException;
174: }
175: }
176:
177: /**
178: * Initializes the map of domain-specific policy policy assertion creators
179: */
180: private static Map<String, PolicyAssertionCreator> initPolicyAssertionCreatorsMap()
181: throws PolicyException {
182: LOGGER.entering();
183: Map<String, PolicyAssertionCreator> pacMap = new HashMap<String, PolicyAssertionCreator>();
184:
185: final PolicyAssertionCreator[] creators = PolicyUtils.ServiceProvider
186: .load(PolicyAssertionCreator.class);
187: for (PolicyAssertionCreator creator : creators) {
188: final String[] supportedURIs = creator
189: .getSupportedDomainNamespaceURIs();
190: final String creatorClassName = creator.getClass()
191: .getName();
192:
193: if (supportedURIs == null || supportedURIs.length == 0) {
194: LOGGER
195: .warning(LocalizationMessages
196: .WSP_0077_ASSERTION_CREATOR_DOES_NOT_SUPPORT_ANY_URI(creatorClassName));
197: continue;
198: }
199:
200: for (String supportedURI : supportedURIs) {
201: LOGGER.config(LocalizationMessages
202: .WSP_0078_ASSERTION_CREATOR_DISCOVERED(
203: creatorClassName, supportedURI));
204: if (supportedURI == null || supportedURI.length() == 0) {
205: throw LOGGER
206: .logSevereException(new PolicyException(
207: LocalizationMessages
208: .WSP_0070_ERROR_REGISTERING_ASSERTION_CREATOR(creatorClassName)));
209: }
210:
211: final PolicyAssertionCreator oldCreator = pacMap.put(
212: supportedURI, creator);
213: if (oldCreator != null) {
214: throw LOGGER
215: .logSevereException(new PolicyException(
216: LocalizationMessages
217: .WSP_0071_ERROR_MULTIPLE_ASSERTION_CREATORS_FOR_NAMESPACE(
218: supportedURI,
219: oldCreator
220: .getClass()
221: .getName(),
222: creator.getClass()
223: .getName())));
224: }
225: }
226: }
227:
228: pacMap = Collections.unmodifiableMap(pacMap);
229: LOGGER.exiting(pacMap);
230: return pacMap;
231: }
232:
233: /**
234: * Method returns thread-safe policy model translator instance.
235: *
236: * @return a policy model translator instance.
237: */
238: public static PolicyModelTranslator getTranslator()
239: throws PolicyException {
240: if (initialException != null) {
241: throw LOGGER.logSevereException(initialException);
242: }
243:
244: return translator;
245: }
246:
247: /**
248: * The method translates {@link PolicySourceModel} structure into normalized {@link Policy} expression. The resulting Policy
249: * is disconnected from its model, thus any additional changes in model will have no effect on the Policy expression.
250: *
251: * @param model the model to be translated into normalized policy expression. Must not be {@code null}.
252: * @return translated policy expression in it's normalized form.
253: * @throws PolicyException in case of translation failure
254: */
255: public Policy translate(final PolicySourceModel model)
256: throws PolicyException {
257: LOGGER.entering(model);
258:
259: if (model == null) {
260: throw LOGGER
261: .logSevereException(new PolicyException(
262: LocalizationMessages
263: .WSP_0043_POLICY_MODEL_TRANSLATION_ERROR_INPUT_PARAM_NULL()));
264: }
265:
266: PolicySourceModel localPolicyModelCopy;
267: try {
268: localPolicyModelCopy = model.clone();
269: } catch (CloneNotSupportedException e) {
270: throw LOGGER
271: .logSevereException(new PolicyException(
272: LocalizationMessages
273: .WSP_0016_UNABLE_TO_CLONE_POLICY_SOURCE_MODEL(),
274: e));
275: }
276:
277: final String policyId = localPolicyModelCopy.getPolicyId();
278: final String policyName = localPolicyModelCopy.getPolicyName();
279:
280: final Collection<AssertionSet> alternatives = createPolicyAlternatives(localPolicyModelCopy);
281: LOGGER
282: .finest(LocalizationMessages
283: .WSP_0052_NUMBER_OF_ALTERNATIVE_COMBINATIONS_CREATED(alternatives
284: .size()));
285:
286: Policy policy = null;
287: if (alternatives.size() == 0) {
288: policy = Policy.createNullPolicy(policyName, policyId);
289: LOGGER.finest(LocalizationMessages
290: .WSP_0055_NO_ALTERNATIVE_COMBINATIONS_CREATED());
291: } else if (alternatives.size() == 1
292: && alternatives.iterator().next().isEmpty()) {
293: policy = Policy.createEmptyPolicy(policyName, policyId);
294: LOGGER
295: .finest(LocalizationMessages
296: .WSP_0026_SINGLE_EMPTY_ALTERNATIVE_COMBINATION_CREATED());
297: } else {
298: policy = Policy.createPolicy(policyName, policyId,
299: alternatives);
300: LOGGER
301: .finest(LocalizationMessages
302: .WSP_0057_N_ALTERNATIVE_COMBINATIONS_M_POLICY_ALTERNATIVES_CREATED(
303: alternatives.size(), policy
304: .getNumberOfAssertionSets()));
305: }
306:
307: LOGGER.exiting(policy);
308: return policy;
309: }
310:
311: /**
312: * Method creates policy alternatives according to provided model. The model structure is modified in the process.
313: *
314: * @return created policy alternatives resulting from policy source model.
315: */
316: private Collection<AssertionSet> createPolicyAlternatives(
317: final PolicySourceModel model) throws PolicyException {
318: // creating global method variables
319: final ContentDecomposition decomposition = new ContentDecomposition();
320:
321: // creating processing queue and starting the processing iterations
322: final Queue<RawPolicy> policyQueue = new LinkedList<RawPolicy>();
323: final Queue<Collection<ModelNode>> contentQueue = new LinkedList<Collection<ModelNode>>();
324:
325: final RawPolicy rootPolicy = new RawPolicy(model.getRootNode(),
326: new LinkedList<RawAlternative>());
327: RawPolicy processedPolicy = rootPolicy;
328: do {
329: Collection<ModelNode> processedContent = processedPolicy.originalContent;
330: do {
331: decompose(processedContent, decomposition);
332: if (decomposition.exactlyOneContents.isEmpty()) {
333: final RawAlternative alternative = new RawAlternative(
334: decomposition.assertions);
335: processedPolicy.alternatives.add(alternative);
336: if (!alternative.allNestedPolicies.isEmpty()) {
337: policyQueue
338: .addAll(alternative.allNestedPolicies);
339: }
340: } else { // we have a non-empty collection of exactly ones
341: final Collection<Collection<ModelNode>> combinations = PolicyUtils.Collections
342: .combine(decomposition.assertions,
343: decomposition.exactlyOneContents,
344: false);
345: if (combinations != null && !combinations.isEmpty()) {
346: // processed alternative was split into some new alternatives, which we need to process
347: contentQueue.addAll(combinations);
348: }
349: }
350: } while ((processedContent = contentQueue.poll()) != null);
351: } while ((processedPolicy = policyQueue.poll()) != null);
352:
353: // normalize nested policies to contain single alternative only
354: final Collection<AssertionSet> assertionSets = new LinkedList<AssertionSet>();
355: for (RawAlternative rootAlternative : rootPolicy.alternatives) {
356: final Collection<AssertionSet> normalizedAlternatives = normalizeRawAlternative(rootAlternative);
357: assertionSets.addAll(normalizedAlternatives);
358: }
359:
360: return assertionSets;
361: }
362:
363: /**
364: * Decomposes the unprocessed alternative content into two different collections:
365: * <p/>
366: * Content of 'EXACTLY_ONE' child nodes is expanded and placed in one list and
367: * 'ASSERTION' nodes are placed into other list. Direct 'ALL' and 'POLICY' child nodes are 'dissolved' in the process.
368: *
369: * Method reuses precreated ContentDecomposition object, which is reset before reuse.
370: */
371: private void decompose(final Collection<ModelNode> content,
372: final ContentDecomposition decomposition)
373: throws PolicyException {
374: decomposition.reset();
375:
376: final Queue<ModelNode> allContentQueue = new LinkedList<ModelNode>(
377: content);
378: ModelNode node;
379: while ((node = allContentQueue.poll()) != null) {
380: // dissolving direct 'POLICY', 'POLICY_REFERENCE' and 'ALL' child nodes
381: switch (node.getType()) {
382: case POLICY:
383: case ALL:
384: allContentQueue.addAll(node.getContent());
385: break;
386: case POLICY_REFERENCE:
387: allContentQueue.addAll(getReferencedModelRootNode(node)
388: .getContent());
389: break;
390: case EXACTLY_ONE:
391: decomposition.exactlyOneContents
392: .add(expandsExactlyOneContent(node.getContent()));
393: break;
394: case ASSERTION:
395: decomposition.assertions.add(node);
396: break;
397: default:
398: throw LOGGER
399: .logSevereException(new PolicyException(
400: LocalizationMessages
401: .WSP_0007_UNEXPECTED_MODEL_NODE_TYPE_FOUND(node
402: .getType())));
403: }
404: }
405: }
406:
407: private static ModelNode getReferencedModelRootNode(
408: final ModelNode policyReferenceNode) throws PolicyException {
409: final PolicySourceModel referencedModel = policyReferenceNode
410: .getReferencedModel();
411: if (referencedModel == null) {
412: final PolicyReferenceData refData = policyReferenceNode
413: .getPolicyReferenceData();
414: if (refData == null) {
415: throw LOGGER
416: .logSevereException(new PolicyException(
417: LocalizationMessages
418: .WSP_0041_POLICY_REFERENCE_NODE_FOUND_WITH_NO_POLICY_REFERENCE_IN_IT()));
419: } else {
420: throw LOGGER
421: .logSevereException(new PolicyException(
422: LocalizationMessages
423: .WSP_0010_UNEXPANDED_POLICY_REFERENCE_NODE_FOUND_REFERENCING(refData
424: .getReferencedModelUri())));
425: }
426: } else {
427: return referencedModel.getRootNode();
428: }
429: }
430:
431: /**
432: * Expands content of 'EXACTLY_ONE' node. Direct 'EXACTLY_ONE' child nodes are dissolved in the process.
433: */
434: private Collection<ModelNode> expandsExactlyOneContent(
435: final Collection<ModelNode> content) throws PolicyException {
436: final Collection<ModelNode> result = new LinkedList<ModelNode>();
437:
438: final Queue<ModelNode> eoContentQueue = new LinkedList<ModelNode>(
439: content);
440: ModelNode node;
441: while ((node = eoContentQueue.poll()) != null) {
442: // dissolving direct 'EXACTLY_ONE' child nodes
443: switch (node.getType()) {
444: case POLICY:
445: case ALL:
446: case ASSERTION:
447: result.add(node);
448: break;
449: case POLICY_REFERENCE:
450: result.add(getReferencedModelRootNode(node));
451: break;
452: case EXACTLY_ONE:
453: eoContentQueue.addAll(node.getContent());
454: break;
455: default:
456: throw LOGGER
457: .logSevereException(new PolicyException(
458: LocalizationMessages
459: .WSP_0001_UNSUPPORTED_MODEL_NODE_TYPE(node
460: .getType())));
461: }
462: }
463:
464: return result;
465: }
466:
467: private List<AssertionSet> normalizeRawAlternative(
468: final RawAlternative alternative)
469: throws AssertionCreationException, PolicyException {
470: final List<PolicyAssertion> normalizedContentBase = new LinkedList<PolicyAssertion>();
471: final Collection<List<PolicyAssertion>> normalizedContentOptions = new LinkedList<List<PolicyAssertion>>();
472: if (!alternative.nestedAssertions.isEmpty()) {
473: final Queue<RawAssertion> nestedAssertionsQueue = new LinkedList<RawAssertion>(
474: alternative.nestedAssertions);
475: RawAssertion rawAssertion;
476: while ((rawAssertion = nestedAssertionsQueue.poll()) != null) {
477: final List<PolicyAssertion> normalized = normalizeRawAssertion(rawAssertion);
478: // if there is only a single result, we can add it direclty to the content base collection
479: // more elements in the result indicate that we will have to create combinations
480: if (normalized.size() == 1) {
481: normalizedContentBase.addAll(normalized);
482: } else {
483: normalizedContentOptions.add(normalized);
484: }
485: }
486: }
487:
488: final List<AssertionSet> options = new LinkedList<AssertionSet>();
489: if (normalizedContentOptions.isEmpty()) {
490: // we do not have any options to combine => returning this assertion
491: options.add(AssertionSet
492: .createAssertionSet(normalizedContentBase));
493: } else {
494: // we have some options to combine => creating assertion options based on content combinations
495: final Collection<Collection<PolicyAssertion>> contentCombinations = PolicyUtils.Collections
496: .combine(normalizedContentBase,
497: normalizedContentOptions, true);
498: for (Collection<PolicyAssertion> contentOption : contentCombinations) {
499: options.add(AssertionSet
500: .createAssertionSet(contentOption));
501: }
502: }
503: return options;
504: }
505:
506: private List<PolicyAssertion> normalizeRawAssertion(
507: final RawAssertion assertion)
508: throws AssertionCreationException, PolicyException {
509: List<PolicyAssertion> parameters;
510: if (assertion.parameters.isEmpty()) {
511: parameters = null;
512: } else {
513: parameters = new ArrayList<PolicyAssertion>(
514: assertion.parameters.size());
515: for (ModelNode parameterNode : assertion.parameters) {
516: parameters
517: .add(createPolicyAssertionParameter(parameterNode));
518: }
519: }
520:
521: final List<AssertionSet> nestedAlternatives = new LinkedList<AssertionSet>();
522: if (assertion.nestedAlternatives != null
523: && !assertion.nestedAlternatives.isEmpty()) {
524: final Queue<RawAlternative> nestedAlternativeQueue = new LinkedList<RawAlternative>(
525: assertion.nestedAlternatives);
526: RawAlternative rawAlternative;
527: while ((rawAlternative = nestedAlternativeQueue.poll()) != null) {
528: nestedAlternatives
529: .addAll(normalizeRawAlternative(rawAlternative));
530: }
531: // if there is only a single result, we can add it direclty to the content base collection
532: // more elements in the result indicate that we will have to create combinations
533: }
534:
535: final List<PolicyAssertion> assertionOptions = new LinkedList<PolicyAssertion>();
536: final boolean nestedAlternativesAvailable = !nestedAlternatives
537: .isEmpty();
538: if (nestedAlternativesAvailable) {
539: for (AssertionSet nestedAlternative : nestedAlternatives) {
540: assertionOptions.add(createPolicyAssertion(
541: assertion.originalNode.getNodeData(),
542: parameters, nestedAlternative));
543: }
544: } else {
545: assertionOptions.add(createPolicyAssertion(
546: assertion.originalNode.getNodeData(), parameters,
547: null));
548: }
549: return assertionOptions;
550: }
551:
552: private static PolicyAssertion createPolicyAssertionParameter(
553: final ModelNode parameterNode)
554: throws AssertionCreationException, PolicyException {
555: if (parameterNode.getType() != ModelNode.Type.ASSERTION_PARAMETER_NODE) {
556: throw LOGGER
557: .logSevereException(new PolicyException(
558: LocalizationMessages
559: .WSP_0065_INCONSISTENCY_IN_POLICY_SOURCE_MODEL(parameterNode
560: .getType())));
561: }
562:
563: List<PolicyAssertion> childParameters = null;
564: if (parameterNode.hasChildren()) {
565: childParameters = new ArrayList<PolicyAssertion>(
566: parameterNode.childrenSize());
567: for (ModelNode childParameterNode : parameterNode) {
568: childParameters
569: .add(createPolicyAssertionParameter(childParameterNode));
570: }
571: }
572:
573: return createPolicyAssertion(parameterNode.getNodeData(),
574: childParameters, null /* parameters do not have any nested alternatives */);
575: }
576:
577: private static PolicyAssertion createPolicyAssertion(
578: final AssertionData data,
579: final Collection<PolicyAssertion> assertionParameters,
580: final AssertionSet nestedAlternative)
581: throws AssertionCreationException {
582: final String assertionNamespace = data.getName()
583: .getNamespaceURI();
584: final PolicyAssertionCreator domainSpecificPAC = assertionCreators
585: .get(assertionNamespace);
586:
587: if (domainSpecificPAC == null) {
588: return defaultCreator.createAssertion(data,
589: assertionParameters, nestedAlternative, null);
590: } else {
591: return domainSpecificPAC.createAssertion(data,
592: assertionParameters, nestedAlternative,
593: defaultCreator);
594: }
595: }
596: }
|