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
26package com.sun.xml.internal.ws.policy;
27
28import com.sun.xml.internal.ws.policy.privateutil.PolicyLogger;
29import com.sun.xml.internal.ws.policy.privateutil.LocalizationMessages;
30import com.sun.xml.internal.ws.policy.spi.PolicyAssertionValidator.Fitness;
31import java.util.Collection;
32import java.util.LinkedList;
33
34/**
35 * Contains static methods for policy alternative selection. Given policy map is changed so that
36 * each effective policy contains at most one policy alternative. Uses domain
37 * specific @see com.sun.xml.internal.ws.policy.spi.PolicySelector
38 * to find out whether particular policy assertion is actually supported.
39 *
40 * If you are using JAX-WS, use the com.sun.xml.internal.ws.api.policy.AlternativeSelector
41 * instead of this class.
42 *
43 * @author Jakub Podlesak (jakub.podlesak at sun.com)
44 * @author Fabian Ritzmann
45 */
46public class EffectiveAlternativeSelector {
47    private enum AlternativeFitness {
48        UNEVALUATED {
49            AlternativeFitness combine(final Fitness assertionFitness) {
50                switch (assertionFitness) {
51                    case UNKNOWN:
52                        return UNKNOWN;
53                    case UNSUPPORTED:
54                        return UNSUPPORTED;
55                    case SUPPORTED:
56                        return SUPPORTED;
57                    case INVALID:
58                        return INVALID;
59                    default:
60                        return UNEVALUATED;
61                }
62            }
63        },
64        INVALID {
65            AlternativeFitness combine(final Fitness assertionFitness) {
66                return INVALID;
67            }
68        },
69        UNKNOWN {
70            AlternativeFitness combine(final Fitness assertionFitness) {
71                switch (assertionFitness) {
72                    case UNKNOWN:
73                        return UNKNOWN;
74                    case UNSUPPORTED:
75                        return UNSUPPORTED;
76                    case SUPPORTED:
77                        return PARTIALLY_SUPPORTED;
78                    case INVALID:
79                        return INVALID;
80                    default:
81                        return UNEVALUATED;
82                }
83            }
84        },
85        UNSUPPORTED {
86            AlternativeFitness combine(final Fitness assertionFitness) {
87                switch (assertionFitness) {
88                    case UNKNOWN:
89                    case UNSUPPORTED:
90                        return UNSUPPORTED;
91                    case SUPPORTED:
92                        return PARTIALLY_SUPPORTED;
93                    case INVALID:
94                        return INVALID;
95                    default:
96                        return UNEVALUATED;
97                }
98            }
99        },
100        PARTIALLY_SUPPORTED {
101            AlternativeFitness combine(final Fitness assertionFitness) {
102                switch (assertionFitness) {
103                    case UNKNOWN:
104                    case UNSUPPORTED:
105                    case SUPPORTED:
106                        return PARTIALLY_SUPPORTED;
107                    case INVALID:
108                        return INVALID;
109                    default:
110                        return UNEVALUATED;
111                }
112            }
113        },
114        SUPPORTED_EMPTY {
115            AlternativeFitness combine(final Fitness assertionFitness) {
116                // will not localize - this exception may not occur if there is no programatic error in this class
117                throw new UnsupportedOperationException("Combine operation was called unexpectedly on 'SUPPORTED_EMPTY' alternative fitness enumeration state.");
118            }
119        },
120        SUPPORTED {
121            AlternativeFitness combine(final Fitness assertionFitness) {
122                switch (assertionFitness) {
123                    case UNKNOWN:
124                    case UNSUPPORTED:
125                        return PARTIALLY_SUPPORTED;
126                    case SUPPORTED:
127                        return SUPPORTED;
128                    case INVALID:
129                        return INVALID;
130                    default:
131                        return UNEVALUATED;
132                }
133            }
134        };
135
136        abstract AlternativeFitness combine(Fitness assertionFitness);
137    }
138
139    private static final PolicyLogger LOGGER = PolicyLogger.getLogger(EffectiveAlternativeSelector.class);
140
141    /**
142     * Does the selection for policy map bound to given modifier.
143     *
144     * If you are using JAX-WS, use the com.sun.xml.internal.ws.api.policy.AlternativeSelector
145     * instead of this class.
146     *
147     * @param modifier Holds the policy map
148     * @throws PolicyException Most likely an internal error if a policy could not be read or set on the policy map
149     * @see EffectivePolicyModifier which the map is bound to
150     */
151    public static void doSelection(final EffectivePolicyModifier modifier) throws PolicyException {
152        final AssertionValidationProcessor validationProcessor = AssertionValidationProcessor.getInstance();
153        selectAlternatives(modifier, validationProcessor);
154    }
155
156    /**
157     * This method is intended to be called by extension classes that need to
158     * override the behavior of {@link #doSelection}.
159     *
160     * @param modifier
161     * @param validationProcessor
162     * @throws PolicyException
163     */
164    protected static void selectAlternatives(final EffectivePolicyModifier modifier,
165            final AssertionValidationProcessor validationProcessor)
166            throws PolicyException {
167        final PolicyMap map = modifier.getMap();
168        for (PolicyMapKey mapKey : map.getAllServiceScopeKeys()) {
169            final Policy oldPolicy = map.getServiceEffectivePolicy(mapKey);
170            modifier.setNewEffectivePolicyForServiceScope(mapKey, selectBestAlternative(oldPolicy, validationProcessor));
171        }
172        for (PolicyMapKey mapKey : map.getAllEndpointScopeKeys()) {
173            final Policy oldPolicy = map.getEndpointEffectivePolicy(mapKey);
174            modifier.setNewEffectivePolicyForEndpointScope(mapKey, selectBestAlternative(oldPolicy, validationProcessor));
175        }
176        for (PolicyMapKey mapKey : map.getAllOperationScopeKeys()) {
177            final Policy oldPolicy = map.getOperationEffectivePolicy(mapKey);
178            modifier.setNewEffectivePolicyForOperationScope(mapKey, selectBestAlternative(oldPolicy, validationProcessor));
179        }
180        for (PolicyMapKey mapKey : map.getAllInputMessageScopeKeys()) {
181            final Policy oldPolicy = map.getInputMessageEffectivePolicy(mapKey);
182            modifier.setNewEffectivePolicyForInputMessageScope(mapKey, selectBestAlternative(oldPolicy, validationProcessor));
183        }
184        for (PolicyMapKey mapKey : map.getAllOutputMessageScopeKeys()) {
185            final Policy oldPolicy = map.getOutputMessageEffectivePolicy(mapKey);
186            modifier.setNewEffectivePolicyForOutputMessageScope(mapKey, selectBestAlternative(oldPolicy, validationProcessor));
187        }
188        for (PolicyMapKey mapKey : map.getAllFaultMessageScopeKeys()) {
189            final Policy oldPolicy = map.getFaultMessageEffectivePolicy(mapKey);
190            modifier.setNewEffectivePolicyForFaultMessageScope(mapKey, selectBestAlternative(oldPolicy, validationProcessor));
191        }
192    }
193
194    private static Policy selectBestAlternative(final Policy policy, final AssertionValidationProcessor validationProcessor) throws PolicyException {
195        AssertionSet bestAlternative = null;
196        AlternativeFitness bestAlternativeFitness = AlternativeFitness.UNEVALUATED;
197        for (AssertionSet alternative : policy) {
198            AlternativeFitness alternativeFitness = (alternative.isEmpty()) ? AlternativeFitness.SUPPORTED_EMPTY : AlternativeFitness.UNEVALUATED;
199            for ( PolicyAssertion assertion : alternative ) {
200
201                final Fitness assertionFitness = validationProcessor.validateClientSide(assertion);
202                switch(assertionFitness) {
203                    case UNKNOWN:
204                    case UNSUPPORTED:
205                    case INVALID:
206                        LOGGER.warning(LocalizationMessages.WSP_0075_PROBLEMATIC_ASSERTION_STATE(assertion.getName(), assertionFitness));
207                        break;
208                    default:
209                        break;
210                }
211
212                alternativeFitness = alternativeFitness.combine(assertionFitness);
213            }
214
215            if (bestAlternativeFitness.compareTo(alternativeFitness) < 0) {
216                // better alternative found
217                bestAlternative = alternative;
218                bestAlternativeFitness = alternativeFitness;
219            }
220
221            if (bestAlternativeFitness == AlternativeFitness.SUPPORTED) {
222                // all assertions supported by at least one selector
223                break;
224            }
225        }
226
227        switch (bestAlternativeFitness) {
228            case INVALID:
229                throw LOGGER.logSevereException(new PolicyException(LocalizationMessages.WSP_0053_INVALID_CLIENT_SIDE_ALTERNATIVE()));
230            case UNKNOWN:
231            case UNSUPPORTED:
232            case PARTIALLY_SUPPORTED:
233                LOGGER.warning(LocalizationMessages.WSP_0019_SUBOPTIMAL_ALTERNATIVE_SELECTED(bestAlternativeFitness));
234                break;
235            default:
236                break;
237        }
238
239        Collection<AssertionSet> alternativeSet = null;
240        if (bestAlternative != null) {
241            // return a policy containing just the picked alternative
242            alternativeSet = new LinkedList<AssertionSet>();
243            alternativeSet.add(bestAlternative);
244        }
245        return Policy.createPolicy(policy.getNamespaceVersion(), policy.getName(), policy.getId(), alternativeSet);
246    }
247}
248