diff options
Diffstat (limited to 'sandbox/sebastien/java/embed/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentPolicyBuilderImpl.java')
-rw-r--r-- | sandbox/sebastien/java/embed/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentPolicyBuilderImpl.java | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/sandbox/sebastien/java/embed/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentPolicyBuilderImpl.java b/sandbox/sebastien/java/embed/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentPolicyBuilderImpl.java new file mode 100644 index 0000000000..6ce0a3a7d5 --- /dev/null +++ b/sandbox/sebastien/java/embed/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentPolicyBuilderImpl.java @@ -0,0 +1,582 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.builder.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.namespace.QName; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.assembly.Component; +import org.apache.tuscany.sca.assembly.ComponentReference; +import org.apache.tuscany.sca.assembly.ComponentService; +import org.apache.tuscany.sca.assembly.CompositeReference; +import org.apache.tuscany.sca.assembly.CompositeService; +import org.apache.tuscany.sca.assembly.Contract; +import org.apache.tuscany.sca.assembly.EndpointReference; +import org.apache.tuscany.sca.assembly.Reference; +import org.apache.tuscany.sca.assembly.Service; +import org.apache.tuscany.sca.assembly.builder.BuilderContext; +import org.apache.tuscany.sca.assembly.builder.BuilderExtensionPoint; +import org.apache.tuscany.sca.assembly.builder.Messages; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.definitions.Definitions; +import org.apache.tuscany.sca.monitor.Monitor; +import org.apache.tuscany.sca.policy.ExtensionType; +import org.apache.tuscany.sca.policy.Intent; +import org.apache.tuscany.sca.policy.IntentMap; +import org.apache.tuscany.sca.policy.PolicyExpression; +import org.apache.tuscany.sca.policy.PolicySet; +import org.apache.tuscany.sca.policy.PolicySubject; +import org.apache.tuscany.sca.policy.Qualifier; + +/** + * A composite builder that computes policy sets based on attached intents and policy sets. + * Useful if you want to build the model without making any runtime decisions such as + * reference/services matching + * + * @version $Rev$ $Date$ + */ +public class ComponentPolicyBuilderImpl { + + protected BuilderExtensionPoint builders; + + public ComponentPolicyBuilderImpl(ExtensionPointRegistry registry) { + this.builders = registry.getExtensionPoint(BuilderExtensionPoint.class); + } + + /** + * Report a warning. + * + * @param monitor + * @param problems + * @param message + * @param model + */ + protected void warning(Monitor monitor, String message, Object model, Object... messageParameters) { + Monitor.warning(monitor, this, Messages.BUILDER_VALIDATION_BUNDLE, message, messageParameters); + } + + /** + * Report a error. + * + * @param monitor + * @param problems + * @param message + * @param model + */ + protected void error(Monitor monitor, String message, Object model, Object... messageParameters) { + Monitor.error(monitor, this, Messages.BUILDER_VALIDATION_BUNDLE, message, messageParameters); + } + + + /** + * Inherit the intents and policySets from the list of models + * + * @param policySubject - the subject to which intents will be added + * @param intentType - choose to copy interaction or implementation intents. Null = both + * @param ignoreExclusiveIntents - when set true mutually exclusive intents won't be copied + * @param models - the subjects from which intents will be copied + */ + protected void inherit(PolicySubject policySubject, Intent.Type intentType, boolean ignoreExclusiveIntents, Object... models) { + for (Object model : models) { + if (model instanceof PolicySubject) { + PolicySubject subject = (PolicySubject)model; + + if (!ignoreExclusiveIntents) { + // The intents are merged and the exclusion check will be done after + for (Intent intent : subject.getRequiredIntents()) { + if (!policySubject.getRequiredIntents().contains(intent)){ + if ((intent.getType() != null) && (intentType != null) ) { + if (intent.getType().equals(intentType)){ + policySubject.getRequiredIntents().add(intent); + } + } else { + policySubject.getRequiredIntents().add(intent); + } + } + } + } else { + Set<Intent> intents = new HashSet<Intent>(); + for (Intent i1 : subject.getRequiredIntents()) { + boolean exclusive = false; + for (Intent i2 : policySubject.getRequiredIntents()) { + if (i1.getExcludedIntents().contains(i2) || i2.getExcludedIntents().contains(i1)) { + exclusive = true; + break; + } + } + if (!exclusive) { + if (!intents.contains(i1)){ + if (intentType != null) { + if (i1.getType().equals(intentType)){ + intents.add(i1); + } + } else { + intents.add(i1); + } + } + } + } + policySubject.getRequiredIntents().addAll(intents); + } + //FIXME this duplicates the intents for a implementation + //e.g <implementation.java requires="managedTransaction.local managedTransaction.local" + //becomes twice + //[{http://docs.oasis-open.org/ns/opencsa/sca/200912}managedTransaction.local, + //{http://docs.oasis-open.org/ns/opencsa/sca/200912}managedTransaction.local] + for (PolicySet policySet : subject.getPolicySets()){ + if (!policySubject.getPolicySets().contains(policySet)){ + policySubject.getPolicySets().add(policySet); + } + } + } + } + } + + protected void configure(PolicySubject subject1, PolicySubject subject2, Intent.Type intentType, BuilderContext context) { + if (subject1 != null) { + resolveAndCheck(subject1, context); + } + if (subject2 != null) { + resolveAndCheck(subject2, context); + } + inherit(subject1, intentType, false, subject2); + checkMutualExclusion(subject1, context); + } + + protected void configure(ComponentService componentService, BuilderContext context) { + Service service = componentService.getService(); + if (service != null) { + configure(componentService, service, null, context); + configureBindings(componentService, service, context); + } + } + + private void configureBindings(Contract componentContract, Contract componentTypeContract, BuilderContext context) { + if (componentTypeContract == null) { + return; + } + Map<String, Binding> componentTypeContractBindings = new HashMap<String, Binding>(); + for (Binding binding : componentTypeContract.getBindings()) { + componentTypeContractBindings.put(binding.getName(), binding); + } + for (Binding binding : componentContract.getBindings()) { + Binding componentTypeBinding = componentTypeContractBindings.get(binding.getName()); + if (binding instanceof PolicySubject && + componentTypeBinding instanceof PolicySubject) { + configure((PolicySubject)binding, (PolicySubject)componentTypeBinding, Intent.Type.interaction, context); + } + } + } + + protected void configure(ComponentReference componentReference, BuilderContext context) { + Reference reference = componentReference.getReference(); + if (reference != null) { + configure(componentReference, reference, null, context); + configureBindings(componentReference, reference, context); + } + } + + protected void configure(CompositeService compositeService, BuilderContext context) { + configure(compositeService, compositeService.getPromotedService(), null, context); + } + + protected void configure(CompositeReference compositeReference, BuilderContext context) { + for (ComponentReference reference : compositeReference.getPromotedReferences()) { + configure(compositeReference, reference, null, context); + } + } + + public void configure(Component component, BuilderContext context) { + Monitor monitor = context.getMonitor(); + + // fix up the component type by copying all implementation level + // interaction intents to *all* the component type services + for (ComponentService componentService : component.getServices()) { + monitor.pushContext("Service: " + componentService.getName()); + try { + configure(componentService, component.getImplementation(), Intent.Type.interaction, context); + removeConstrainedIntents(componentService, context); + } finally { + monitor.popContext(); + } + } + + // Inherit the intents and policySets from the componentType + for (ComponentReference componentReference : component.getReferences()) { + monitor.pushContext("Reference: " + componentReference.getName()); + try { + configure(componentReference, context); + removeConstrainedIntents(componentReference, context); + } finally { + monitor.popContext(); + } + } + + for (ComponentService componentService : component.getServices()) { + monitor.pushContext("Service: " + componentService.getName()); + try { + configure(componentService, context); + removeConstrainedIntents(componentService, context); + } finally { + monitor.popContext(); + } + } + } + + /** + * Checks if any qualifiable intents of intents in an excluded intent list match + * with a second intent. looking for the case where + * + * <intent name="intentA" excludes="intentB"/> + * <intent name="intentB" > + * <sca:qualifier name="q1" default="true"/> + * <sca:qualifier name="q2" default="true"/> + * </intent> + * + * And were + * + * requires="intentA intentB.q1" appears on an element + * + * @param excludedIntentList + * @param intent + * @return + */ + protected boolean checkQualifiedMutualExclusion(List<Intent> excludedIntentList, Intent intent){ + for (Intent excludedIntent : excludedIntentList){ + if (intent.getQualifiableIntent() != null && + excludedIntent != null && + intent.getQualifiableIntent().equals(excludedIntent)){ + return true; + } + } + return false; + } + + /** + * Check if two intents are mutually exclusive + * + * @param i1 + * @param i2 + * @param context + * @return + */ + protected boolean checkMutualExclusion(Intent i1, Intent i2, BuilderContext context){ + if ((i1 != i2) && + (i1.getExcludedIntents().contains(i2) || + i2.getExcludedIntents().contains(i1) || + checkQualifiedMutualExclusion(i1.getExcludedIntents(), i2) || + checkQualifiedMutualExclusion(i2.getExcludedIntents(), i1))) { + error(context.getMonitor(), "MutuallyExclusiveIntentsAtBuild", this, i1, i2); + return true; + } + + return false; + } + + /** + * Check if a single policy subject requires mutually exclusive intents + * @param subject1 - the policy subject to check + * @param context - context containing useful things like the monitor instance + * @return true if the policy subject contains mutually exclusive intents + */ + protected boolean checkMutualExclusion(PolicySubject subject1, BuilderContext context) { + if (subject1 == null) { + return false; + } + for (Intent i1 : subject1.getRequiredIntents()) { + for (Intent i2 : subject1.getRequiredIntents()) { + if (checkMutualExclusion(i1, i2, context)){ + return true; + } + } + } + return false; + } + + /** + * Check if two policy subjects requires mutually exclusive intents + * @param subject1 + * @param subject2 + * @param monitor + * @return + */ + protected boolean checkMutualExclusion(PolicySubject subject1, PolicySubject subject2, BuilderContext context) { + if (subject1 == subject2 || subject1 == null || subject2 == null) { + return false; + } + for (Intent i1 : subject1.getRequiredIntents()) { + for (Intent i2 : subject2.getRequiredIntents()) { + if (checkMutualExclusion(i1, i2, context)){ + return true; + } + } + } + return false; + } + + protected boolean resolveAndCheck(PolicySubject subject, BuilderContext context) { + if (subject == null) { + return false; + } + // FIXME: [rfeng] Should we resolve the intents during the "build" phase? + resolveAndNormalize(subject, context); + + checkMutualExclusion(subject, context); + + return false; + } + + /** + * Check if two names are equal + * @param name1 + * @param name2 + * @return + */ + protected boolean isEqual(String name1, String name2) { + if (name1 == name2) { + return true; + } + if (name1 != null) { + return name1.equals(name2); + } else { + return name2.equals(name1); + } + } + + protected static Intent resolve(Definitions definitions, Intent proxy) { + for (Intent i : definitions.getIntents()) { + if (i.equals(proxy)) { + return i; + } + for (Intent qi : i.getQualifiedIntents()) { + if (qi.equals(proxy)) { + return qi; + } + } + } + return null; + } + + // Replace qualifiable intents with their default qualifier. This can't be done until + // after inheritance. + protected void expandDefaultIntents(PolicySubject subject, BuilderContext context) { + + Set<Intent> copy = new HashSet<Intent>(subject.getRequiredIntents()); + for (Intent i : copy) { + if (i.getDefaultQualifiedIntent() != null) { + subject.getRequiredIntents().remove(i); + subject.getRequiredIntents().add(i.getDefaultQualifiedIntent()); + } + } + } + protected void resolveAndNormalize(PolicySubject subject, BuilderContext context) { + Definitions definitions = context.getDefinitions(); + Set<Intent> intents = new HashSet<Intent>(); + if (definitions != null) { + for (Intent i : subject.getRequiredIntents()) { + Intent resolved = resolve(definitions, i); + if (resolved != null) { + intents.add(resolved); + } else { + error(context.getMonitor(), "IntentNotFoundAtBuild", subject, i); + // Intent cannot be resolved + } + } + } + + // Replace profile intents with their required intents + while (!intents.isEmpty()) { + boolean profileIntentsFound = false; + Set<Intent> copy = new HashSet<Intent>(intents); + for (Intent i : copy) { + if (!i.getRequiredIntents().isEmpty()) { + intents.remove(i); + intents.addAll(i.getRequiredIntents()); + profileIntentsFound = true; + } + } + if (!profileIntentsFound) { + // No more profileIntents + break; + } + } + + // Replace unqualified intents if there is a qualified intent in the list + Set<Intent> copy = new HashSet<Intent>(intents); + for (Intent i : copy) { + if (i.getQualifiableIntent() != null) { + intents.remove(i.getQualifiableIntent()); + } + + } + + + subject.getRequiredIntents().clear(); + subject.getRequiredIntents().addAll(intents); + + // TUSCANY-3503 - policy sets now only applied through direct + // or external attachement + // resolve policy set names that have been specified for the + // policy subject against the real policy sets from the + // definitions files + Set<PolicySet> policySets = new HashSet<PolicySet>(); + if (definitions != null) { + for (PolicySet policySet : subject.getPolicySets()) { + int index = definitions.getPolicySets().indexOf(policySet); + if (index != -1) { + policySets.add(definitions.getPolicySets().get(index)); + } else { + // PolicySet cannot be resolved + warning(context.getMonitor(), "PolicySetNotFoundAtBuild", subject, policySet); + } + } + } + + subject.getPolicySets().clear(); + subject.getPolicySets().addAll(policySets); + } + + protected void removeConstrainedIntents(PolicySubject subject, BuilderContext context) { + List<Intent> intents = subject.getRequiredIntents(); + + // Remove the intents whose @contrains do not include the current element + ExtensionType extensionType = subject.getExtensionType(); + if(extensionType != null){ + List<Intent> copy = new ArrayList<Intent>(intents); + for (Intent i : copy) { + List<ExtensionType> constrainedTypes = i.getConstrainedTypes(); + if (( constrainedTypes.size() == 0 ) && ( i.getQualifiableIntent() != null ) ) + constrainedTypes = i.getQualifiableIntent().getConstrainedTypes(); + + if (constrainedTypes.size() > 0){ + boolean constraintFound = false; + for (ExtensionType constrainedType : constrainedTypes){ + if (constrainedType.getType().equals(extensionType.getType()) || + constrainedType.getType().equals(extensionType.getBaseType())){ + constraintFound = true; + break; + } + } + if(!constraintFound){ + intents.remove(i); + } + } + } + } + } + + protected void checkIntentsResolved(PolicySubject subject, BuilderContext context) { + // find the policy sets that satisfy the intents that are now + // attached to the policy subject. From the OASIS policy + // spec CD02 rev7: + // 1272 A policySet provides an intent if any of the statements are true: + // 1273 1. The intent is contained in the policySet @provides list. + // 1274 2. The intent is a qualified intent and the unqualified form of the intent is contained in the policySet + // 1275 @provides list. + // 1276 3. The policySet @provides list contains a qualified form of the intent (where the intent is qualifiable). + for (Intent intent : subject.getRequiredIntents()) { + boolean intentMatched = false; + + loop: for (PolicySet ps : subject.getPolicySets()) { + // FIXME: We will have to check the policy references and intentMap too + // as well as the appliesTo + if (ps.getProvidedIntents().contains(intent)) { + intentMatched = true; + break; + } + + for (Intent psProvidedIntent : ps.getProvidedIntents()){ + if (isQualifiedBy(psProvidedIntent, intent)){ + intentMatched = true; + break loop; + } + } + + for (IntentMap map : ps.getIntentMaps()) { + for (Qualifier q : map.getQualifiers()) { + if (intent.equals(q.getIntent())) { + intentMatched = true; + break loop; + } + } + } + } + + if (!intentMatched){ + + // Reference side intents can still be resolved by the service binding, so we can only issue a + // warning here. + if ( subject instanceof EndpointReference ) { + warning(context.getMonitor(), "IntentNotSatisfiedAtBuild", subject, intent.getName(), subject.toString()); + } else { + // Need to check the ExtensionType to see if the intent is provided there. If not, throw an error + ExtensionType type = subject.getExtensionType(); + + + if ( type == null ) { + error(context.getMonitor(), "IntentNotSatisfiedAtBuild", subject, intent.getName(), subject.toString()); + } else { + // The ExtensionType on the subject only has the binding name. The one in the system + // definitions will have the mayProvide/alwaysProvides values + for ( ExtensionType et : context.getDefinitions().getBindingTypes() ) { + if ( type.getType().equals(et.getType()) ) { + type = et; + } + } + + if ( !type.getAlwaysProvidedIntents().contains(intent) && !type.getMayProvidedIntents().contains(intent)) { + error(context.getMonitor(), "IntentNotSatisfiedAtBuild", subject, intent.getName(), subject.toString()); + } + } + } + } + } + } + + protected Set<QName> getPolicyNames(PolicySubject subject) { + if (subject == null) { + return Collections.emptySet(); + } + Set<QName> names = new HashSet<QName>(); + for (PolicySet ps : subject.getPolicySets()) { + for (PolicyExpression exp : ps.getPolicies()) { + names.add(exp.getName()); + } + } + return names; + } + + protected boolean isQualifiedBy(Intent qualifiableIntent, Intent qualifiedIntent){ + if (qualifiedIntent.getQualifiableIntent() == qualifiableIntent){ + return true; + } else { + return false; + } + } + +} |