blob: 6ce0a3a7d5c17965b700c393e655494b03173e76 [file] [log] [blame]
/*
* 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;
}
}
}