All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tuscany.sca.builder.impl.ComponentPolicyBuilderImpl Maven / Gradle / Ivy

/*
 * 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.DefaultIntent;
import org.apache.tuscany.sca.policy.DefaultingPolicySubject;
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: 1304507 $ $Date: 2012-03-23 10:24:36 -0700 (Fri, 23 Mar 2012) $
 */
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 intents = new HashSet();
                    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  componentTypeContractBindings = new HashMap();
        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 
     * 
     *  
     *  
     *      
     *      
     *  
     *  
     *  And were
     *  
     *  requires="intentA intentB.q1" appears on an element
     *  
     * @param excludedIntentList
     * @param intent
     * @return
     */
    protected boolean checkQualifiedMutualExclusion(List 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 copy = new HashSet(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 intents = new HashSet();
        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 copy = new HashSet(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 copy = new HashSet(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. 
        // Some policy plugins, e.g. JSR250, add resolved policy sets
        // on the fly as they read details from annotations. So check 
        // that policy sets are unresolved befor blowing them away with 
        // a warning
        Set policySets = new HashSet();
        if (definitions != null) {
            for (PolicySet policySet : subject.getPolicySets()) {
                if (policySet.isUnresolved()){
                    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);
                    }
                } else {
                    policySets.add(policySet);
                }
            }
        }
        
        subject.getPolicySets().clear();
        subject.getPolicySets().addAll(policySets);
    }
    
    protected void removeConstrainedIntents(PolicySubject subject, BuilderContext context) {
        List intents = subject.getRequiredIntents();
        
        // Remove the intents whose @contrains do not include the current element
        ExtensionType extensionType = subject.getExtensionType();
        if(extensionType != null){
            List copy = new ArrayList(intents);
            for (Intent i : copy) {
            	List 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){              

                // 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 ) {
                    // The ExtensionType on the subject only has the binding name. The one in the system
                    // definitions will have the mayProvide/alwaysProvides values
                    if (type.getType().getLocalPart().startsWith("implementation")) {
                        for ( ExtensionType et : context.getDefinitions().getImplementationTypes() ) {
                            if ( type.getType().equals(et.getType()) ) {
                                type = et;
                            }
                        }
                    } else {
                        for ( ExtensionType et : context.getDefinitions().getBindingTypes() ) {
                            if ( type.getType().equals(et.getType()) ) {
                                type = et;
                            }
                        }
                    }
                }

                if ( type == null || !type.getAlwaysProvidedIntents().contains(intent) && !type.getMayProvidedIntents().contains(intent)) {
                    // 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 {
                        error(context.getMonitor(), "IntentNotSatisfiedAtBuild", subject, intent.getName(), subject.toString());
                    }
                }
            }
        }
    }

    protected Set getPolicyNames(PolicySubject subject) {
        if (subject == null) {
            return Collections.emptySet();
        }
        Set names = new HashSet();
        for (PolicySet ps : subject.getPolicySets()) {
            for (PolicyExpression exp : ps.getPolicies()) {
                names.add(exp.getName().getNamespaceURI());
            }
        }
        return names;
    }
    
    protected boolean isQualifiedBy(Intent qualifiableIntent, Intent qualifiedIntent){
        if (qualifiedIntent.getQualifiableIntent() == qualifiableIntent){
            return true;
        } else {
            return false;
        }
    }
    
    // Add in default intents. This looks at the default intent map for
    // the policy subject and. If none of the mutually exclusive intents are 
    // already present adds in the default intent. 
    protected void addDefaultIntents(PolicySubject subject, Object defaultSubjectObject, BuilderContext context){
        if (!(defaultSubjectObject instanceof DefaultingPolicySubject)){
            return;
        }
        
        DefaultingPolicySubject defaultSubject = (DefaultingPolicySubject)defaultSubjectObject;
        List defaultIntents = defaultSubject.getDefaultIntents();
        
        for(DefaultIntent defaultIntent : defaultIntents){
            // check default intent against the intents already in the subject
            boolean addDefaultIntent = true;
            
            // first check the intents which, if present prevent the default being applied
            for (Intent mutualExclusion : defaultIntent.getMutuallyExclusiveIntents()){
                if (subject.getRequiredIntents().contains(mutualExclusion)){
                    addDefaultIntent = false;
                    break;
                }
            }
            
            // then check that the default intent itself is not mutually exclusive
            for (Intent userDefinedIntent : subject.getRequiredIntents()){
                if (!checkMutualExclusionNoError(defaultIntent.getIntent(), userDefinedIntent, context)){
                    addDefaultIntent = false;
                    break;
                }
            }
            
            if (addDefaultIntent == true){
                subject.getRequiredIntents().add(defaultIntent.getIntent());
            }
        }  
    }
    
    /**
     * Same as checkMutualExclusion but doesn't throw and error on failure
     */
    protected boolean checkMutualExclusionNoError(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))) {
            return true;
        }
        
        return false;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy