/* * 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.core.runtime.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Logger; import javax.xml.namespace.QName; import org.apache.tuscany.sca.assembly.AssemblyFactory; import org.apache.tuscany.sca.assembly.Base; import org.apache.tuscany.sca.assembly.Binding; import org.apache.tuscany.sca.assembly.Callback; import org.apache.tuscany.sca.assembly.ComponentReference; import org.apache.tuscany.sca.assembly.ComponentService; import org.apache.tuscany.sca.assembly.Endpoint; import org.apache.tuscany.sca.assembly.EndpointReference; import org.apache.tuscany.sca.assembly.Multiplicity; import org.apache.tuscany.sca.assembly.SCABinding; import org.apache.tuscany.sca.assembly.builder.BindingBuilder; import org.apache.tuscany.sca.assembly.builder.BuilderContext; import org.apache.tuscany.sca.assembly.builder.BuilderExtensionPoint; import org.apache.tuscany.sca.assembly.builder.CompositeBuilder; import org.apache.tuscany.sca.assembly.builder.PolicyBuilder; import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.core.FactoryExtensionPoint; import org.apache.tuscany.sca.core.UtilityExtensionPoint; import org.apache.tuscany.sca.core.assembly.impl.RuntimeEndpointImpl; import org.apache.tuscany.sca.core.assembly.impl.RuntimeEndpointReferenceImpl; import org.apache.tuscany.sca.definitions.Definitions; import org.apache.tuscany.sca.interfacedef.InterfaceContract; import org.apache.tuscany.sca.interfacedef.InterfaceContractMapper; import org.apache.tuscany.sca.interfacedef.impl.TuscanyInterfaceContractImpl; import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract; import org.apache.tuscany.sca.interfacedef.util.Audit; import org.apache.tuscany.sca.monitor.Monitor; import org.apache.tuscany.sca.monitor.MonitorFactory; import org.apache.tuscany.sca.policy.BindingType; 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.PolicySet; import org.apache.tuscany.sca.policy.Qualifier; import org.apache.tuscany.sca.provider.EndpointReferenceAsyncProvider; import org.apache.tuscany.sca.provider.ReferenceBindingProvider; import org.apache.tuscany.sca.runtime.CompositeActivator; import org.apache.tuscany.sca.runtime.DomainRegistry; import org.apache.tuscany.sca.runtime.EndpointReferenceBinder; import org.apache.tuscany.sca.runtime.RuntimeEndpoint; import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; import org.apache.tuscany.sca.runtime.UnknownEndpointHandler; import org.oasisopen.sca.ServiceRuntimeException; /** * A builder that takes endpoint references and resolves them. It either finds local * service endpoints if they are available or asks the domain. The main function here * is to perform binding and policy matching. * * This is a separate from the builders so that the mechanism for reference/service matching * can be used at runtime as well as build time and can also be replaced independently * * @version $Rev$ $Date$ */ public class EndpointReferenceBinderImpl implements EndpointReferenceBinder { private static final Logger logger = Logger.getLogger(EndpointReferenceBinderImpl.class.getName()); protected ExtensionPointRegistry extensionPoints; protected AssemblyFactory assemblyFactory; protected InterfaceContractMapper interfaceContractMapper; protected BuilderExtensionPoint builders; protected CompositeActivator compositeActivator; protected Monitor monitor; protected UnknownEndpointHandler unknownEndpointHandler; protected CompositeBuilder policyAppliesToBuilder; public EndpointReferenceBinderImpl(ExtensionPointRegistry extensionPoints) { this.extensionPoints = extensionPoints; FactoryExtensionPoint factories = extensionPoints.getExtensionPoint(FactoryExtensionPoint.class); this.assemblyFactory = factories.getFactory(AssemblyFactory.class); UtilityExtensionPoint utils = extensionPoints.getExtensionPoint(UtilityExtensionPoint.class); this.interfaceContractMapper = utils.getUtility(InterfaceContractMapper.class); MonitorFactory monitorFactory = utils.getUtility(MonitorFactory.class); monitor = monitorFactory.createMonitor(); this.unknownEndpointHandler = utils.getUtility(UnknownEndpointHandler.class); this.builders = extensionPoints.getExtensionPoint(BuilderExtensionPoint.class); this.compositeActivator = extensionPoints.getExtensionPoint(CompositeActivator.class); } /** * Bind a single endpoint reference at build time. Here we only expect the * registry to have a record of local endpoints * * @param domainRegistry * @param endpointReference */ public void bindBuildTime(DomainRegistry domainRegistry, EndpointReference endpointReference, BuilderContext builderContext) { bind(domainRegistry, endpointReference, builderContext, false); } /** * Bind a single endpoint reference at run time. Here we expect the * registry to be populated with endpoints from across the domain * * @param domainRegistry * @param endpointReference */ public void bindRunTime(DomainRegistry domainRegistry, EndpointReference endpointReference) { bind(domainRegistry, endpointReference, null, true); } /** * Bind a reference to a service endpoint * * @param domainRegistry * @param endpointReference * @param runtime set true if called from the runtime */ public void bind(DomainRegistry domainRegistry, EndpointReference endpointReference, BuilderContext builderContext, boolean runtime){ logger.fine("Binding " + endpointReference.toString()); Audit matchAudit = new Audit(); // This logic does post build autowire matching but isn't actually used at the moment // as problems with dependencies mean we still do this during build if (endpointReference.getStatus() == EndpointReference.Status.AUTOWIRE_PLACEHOLDER){ // do autowire matching // will only be called at build time at the moment Multiplicity multiplicity = endpointReference.getReference().getMultiplicity(); for (Endpoint endpoint : domainRegistry.getEndpoints()){ // if (endpoint is in the same composite as endpoint reference){ if ((multiplicity == Multiplicity.ZERO_ONE || multiplicity == Multiplicity.ONE_ONE) && (endpointReference.getReference().getEndpointReferences().size() > 1)) { break; } // Prevent autowire connecting to self if (endpointReference.getComponent() == endpoint.getComponent()) { continue; } if (haveMatchingPolicy(endpointReference, endpoint, matchAudit, builderContext) && haveMatchingInterfaceContracts(endpointReference, endpoint, matchAudit)){ // matching service so find if this reference already has // an endpoint reference for this endpoint Endpoint autowireEndpoint = null; for (EndpointReference epr : endpointReference.getReference().getEndpointReferences()){ if (epr.getTargetEndpoint() == endpoint){ autowireEndpoint = endpoint; break; } } if (autowireEndpoint == null){ // create new EPR for autowire EndpointReference autowireEndpointRefrence = null; try { autowireEndpointRefrence = (EndpointReference)endpointReference.clone(); } catch (Exception ex){ // won't happen as clone is supported } autowireEndpointRefrence.setTargetEndpoint(endpoint); autowireEndpointRefrence.setBinding(endpoint.getBinding()); autowireEndpointRefrence.setStatus(EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED); endpointReference.getReference().getEndpointReferences().add(autowireEndpointRefrence); } } // } } if (multiplicity == Multiplicity.ONE_N || multiplicity == Multiplicity.ONE_ONE) { if (endpointReference.getReference().getEndpointReferences().size() == 1) { Monitor.error(monitor, this, "endpoint-validation-messages", "NoComponentReferenceTarget", endpointReference.getReference().getName()); //throw new ServiceRuntimeException("Unable to bind " + // monitor.getLastProblem().toString()); } } setSingleAutoWireTarget(endpointReference.getReference()); } else if ( endpointReference.getStatus() == EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED|| endpointReference.getStatus() == EndpointReference.Status.RESOLVED_BINDING ) { // The endpoint reference is already resolved to either // a service endpoint local to this composite or it has // a remote binding. Just make sure the binding is built build(endpointReference); // still need to check that the callback endpoint is set correctly if (hasCallback(endpointReference) && (endpointReference.getCallbackEndpoint() == null || endpointReference.getCallbackEndpoint().isUnresolved())) { selectCallbackEndpoint(endpointReference, endpointReference.getReference().getCallbackService(), matchAudit, builderContext, runtime); } } else if (endpointReference.getStatus() == EndpointReference.Status.WIRED_TARGET_FOUND_READY_FOR_MATCHING ){ // The endpoint reference is already resolved to either // a service endpoint but no binding was specified in the // target URL and/or the policies have yet to be matched. // Used when $self references are resolved selectForwardEndpoint(endpointReference, endpointReference.getTargetEndpoint().getService().getEndpoints(), matchAudit, builderContext, runtime); if (hasCallback(endpointReference)){ selectCallbackEndpoint(endpointReference, endpointReference.getReference().getCallbackService(), matchAudit, builderContext, runtime); } } else if (endpointReference.getStatus() == EndpointReference.Status.WIRED_TARGET_IN_BINDING_URI || endpointReference.getStatus() == EndpointReference.Status.WIRED_TARGET_NOT_FOUND || endpointReference.getStatus() == EndpointReference.Status.NOT_CONFIGURED){ // The reference is not yet matched to a service // find the service in the endpoint registry List endpoints = domainRegistry.findEndpoint(endpointReference); if (endpoints.size() > 0){ selectForwardEndpoint(endpointReference, endpoints, matchAudit, builderContext, runtime); // If the reference was matched try to match the callback if (endpointReference.getStatus().equals(EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED) && hasCallback(endpointReference)){ selectCallbackEndpoint(endpointReference, endpointReference.getReference().getCallbackService(), matchAudit, builderContext, runtime); } } else if (runtime) { // tweak to test if this could be a resolved binding. This is the back end of the test // in the builder that pulls the URI out of the binding if there are no targets // on the reference. have to wait until here to see if the binding uri matches any // available services. If not we assume here that it's a resolved binding if (endpointReference.getStatus() == EndpointReference.Status.WIRED_TARGET_IN_BINDING_URI){ endpointReference.getTargetEndpoint().setBinding(endpointReference.getBinding()); endpointReference.setStatus(EndpointReference.Status.RESOLVED_BINDING); } else { processUnknownEndpoint(endpointReference, matchAudit); if (!endpointReference.getStatus().equals(EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED)){ Monitor.error(monitor, this, "endpoint-validation-messages", "NoEndpointsFound", endpointReference.toString()); throw new ServiceRuntimeException(monitor.getMessageString(EndpointReferenceBinderImpl.class.getName(), "endpoint-validation-messages", "UnableToBind") + " " + monitor.getLastProblem().toString()); } } } else { // it's build time so just give the UnknownEndpoint code a chance // without regard for the result processUnknownEndpoint(endpointReference, matchAudit); } } logger.fine(matchAudit.toString()); if (endpointReference.getStatus() != EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED && endpointReference.getStatus() != EndpointReference.Status.RESOLVED_BINDING){ if (runtime){ Monitor.error(monitor, this, "endpoint-validation-messages", "EndpointReferenceCantBeMatched", endpointReference.toString(), matchAudit); throw new ServiceRuntimeException(monitor.getMessageString(EndpointReferenceBinderImpl.class.getName(), "endpoint-validation-messages", "UnableToBind") + " " + monitor.getLastProblem().toString()); } else { Monitor.warning(monitor, this, "endpoint-validation-messages", "ComponentReferenceTargetNotFound", endpointReference.toString()); return; } } // [rfeng] Setup the target endpoint if the reference uses an explicit binding if (endpointReference.getTargetEndpoint().getBinding() == null) { endpointReference.getTargetEndpoint().setBinding(endpointReference.getBinding()); } // Now the endpoint reference is resolved check that the binding interfaces contract // and the reference contract are compatible try { ((RuntimeEndpointReference)endpointReference).validateReferenceInterfaceCompatibility(); } catch (ServiceRuntimeException ex) { // don't re-throw this exception at build time just record the // error. If it's thrown here is messes up the order in which // build time errors are reported and that in turn messes // up the output of the compliance tests. if (runtime){ throw ex; } else { Monitor.error(monitor, this, "endpoint-validation-messages", "EndpointReferenceCantBeMatched", endpointReference.toString(), ex.getMessage()); } } // TUSCANY-3783 // if the reference is an async reference and the binding doesn't support // async natively fluff up the response service/endpoint ReferenceBindingProvider referenceBindingProvider = ((RuntimeEndpointReference)endpointReference).getBindingProvider(); if ( referenceBindingProvider instanceof EndpointReferenceAsyncProvider && !((EndpointReferenceAsyncProvider)referenceBindingProvider).supportsNativeAsync() && endpointReference.isAsyncInvocation() && endpointReference.getCallbackEndpoint() == null) { ((RuntimeEndpointReference)endpointReference).createAsyncCallbackEndpoint(); } // System.out.println("MATCH AUDIT:" + matchAudit.toString()); } private void processUnknownEndpoint(EndpointReference endpointReference, Audit matchAudit){ Binding b = null; if (unknownEndpointHandler != null) { b = unknownEndpointHandler.handleUnknownEndpoint(endpointReference); } if (b != null) { Endpoint matchedEndpoint = new RuntimeEndpointImpl(extensionPoints); matchedEndpoint.setBinding(b); matchedEndpoint.setRemote(true); endpointReference.setTargetEndpoint(matchedEndpoint); endpointReference.setBinding(b); endpointReference.setUnresolved(false); endpointReference.setStatus(EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED); matchAudit.append("Match because the UnknownEndpointHandler provided a binding: " + b.getType() + " uri: " + b.getURI()); matchAudit.appendSeperator(); } } /** * Returns true if the reference has a callback */ private boolean hasCallback(EndpointReference endpointReference){ if (endpointReference.getReference().getInterfaceContract() == null || endpointReference.getReference().getInterfaceContract().getCallbackInterface() == null || endpointReference.getReference().getName().startsWith("$self$.")){ return false; } else { return true; } } /** * Selects a forward endpoint from a list of possible candidates * * @param endpointReference * @param endpoints */ private void selectForwardEndpoint(EndpointReference endpointReference, List endpoints, Audit matchAudit, BuilderContext builderContext, boolean runtime) { Endpoint matchedEndpoint = null; if (endpointReference.getReference().getName().startsWith("$self$.")){ // just select the first one and don't do any policy matching if (endpointReference.getTargetEndpoint() != null && !endpointReference.getTargetEndpoint().isUnresolved()) { matchedEndpoint = endpointReference.getTargetEndpoint(); } else { matchedEndpoint = endpoints.get(0); } } else { // find the endpoints that match this endpoint reference List matchedEndpoints = new ArrayList(); for (Endpoint endpoint : endpoints){ if (haveMatchingPolicy(endpointReference, endpoint, matchAudit, builderContext) && haveMatchingInterfaceContracts(endpointReference, endpoint, matchAudit)){ matchedEndpoints.add(endpoint); } } // TUSCANY-4005 - raise an error if a reference target that only specifies the // component name matches more than one component service if (endpointReference.getTargetEndpoint().getService() == null && endpointReference.getTargetEndpoint().getBinding() == null && matchedEndpoints.size() > 1 ) { String serviceName = null; for (Endpoint endpoint : matchedEndpoints){ // ignore service names called "default" as these indicate dynamic services // created for the likes of implementation.python if (serviceName == null && !endpoint.getService().getName().equals("default")){ serviceName = endpoint.getService().getName(); } if (serviceName != null && !endpoint.getService().getName().equals("default") && !endpoint.getService().getName().equals(serviceName)){ if (runtime){ Monitor.error(monitor, this, "endpoint-validation-messages", "TooManyTargetServices", endpointReference.toString(), endpointReference.getTargetEndpoint().toString(), matchAudit); throw new ServiceRuntimeException("Unable to bind " + monitor.getLastProblem().toString()); } else { Monitor.warning(monitor, this, "endpoint-validation-messages", "TooManyTargetServices", endpointReference.toString(), endpointReference.getTargetEndpoint().toString()); return; } } } } // TUSCANY-3941 check for the case where the user has provided a // binding.sca at the reference and make sure we pick // a binding.sca at the service regardless of how many // other bindings are provided if (endpointReference.getBinding() != null && endpointReference.getBinding() instanceof SCABinding ){ for (Endpoint endpoint : matchedEndpoints){ if (endpoint.getBinding() instanceof SCABinding){ matchedEndpoint = endpoint; break; } } } if (matchedEndpoint == null) { // just take the first matched endpoint from the list if (matchedEndpoints.size() > 0){ matchedEndpoint = matchedEndpoints.get(0); } } } if (matchedEndpoint == null){ return; } else { endpointReference.setTargetEndpoint(matchedEndpoint); Binding binding = matchedEndpoint.getBinding(); try { endpointReference.setBinding((Binding)binding.clone()); } catch (CloneNotSupportedException e) { // shouldn't happen throw new RuntimeException(e); } // TUSCANY-3873 - add policy from the service // we don't care about intents at this stage endpointReference.getPolicySets().addAll(matchedEndpoint.getPolicySets()); // TODO - we need to re-run the appliesTo processing here but there is some question about what // appliesTo means. It's also difficult to get to the PolicyAppliesToBuilder from here and // need a new EntensionInterface to support access. So for now I'm just cheating and looking to // see if the XPath expression contains the binding type as a string while we discuss appliesTo List psToRemove = new ArrayList(); for (PolicySet ps : endpointReference.getPolicySets() ) { if (!ps.getAppliesTo().contains(endpointReference.getBinding().getType().getLocalPart())){ psToRemove.add(ps); } } endpointReference.getPolicySets().removeAll(psToRemove); build(endpointReference); endpointReference.setStatus(EndpointReference.Status.WIRED_TARGET_FOUND_AND_MATCHED); endpointReference.setUnresolved(false); } } private void build(EndpointReference endpointReference) { BindingBuilder builder = builders.getBindingBuilder(endpointReference.getBinding().getType()); if (builder != null) { builder.build(endpointReference.getComponent(), endpointReference.getReference(), endpointReference.getBinding(), new BuilderContext(extensionPoints), false); } } /** * This code is very similar to the code in EnpointReferenceBuilder.fixUpCallbackLinks() * but here we are selecting endpoint/endpoint references at runtime. * * To recap the general model is as follows: * * Forward EPR -----------> Forward EP (target EP) * | | * \/ \/ * Callback EP <------------ Callback EPR * * The question then becomes how to construct this model given that * target resolution can happen at runtime and EPR and EP can be * distributed across the domain. * * The forward link is resolved by searching in the registry for an EP * that matches the EPR. * * At build time we've made the assumption that, if the user configures * a callback binding at all, they will choose the same binding as the * forward call (TODO - this may be a bold assumption). So here we just * assume that the callback endpoint that is set on the reference * at build time matches. This may not be true if the callback bindings * are configured differently on the reference and service. * * @param endpointReference * @param endpoints */ private void selectCallbackEndpoint(EndpointReference endpointReference, ComponentService callbackService, Audit matchAudit, BuilderContext builderContext, boolean runtime) { // A simplifying assumption here is that the user will only specify a callback binding // of the same type as the forward binding in order to specify further configuration // We can then assume that if there is no callback endpoint that matches already then we have // picked up binding config from the service and we can create a callback endpoint to match // So having said this we look in three places for the callback binding. // 1/ in callback endpoints at the reference // - will find it here if the user has manually configured a callback binding at the reference // 2/ in the service callback structure // - will find it here if the user has manually configured a callback binding at the service // 3/ in the service callback reference structure // - will find it here if the system has constructed the callback binding based on the forward binding // // 2 and 3 are conflated in the distributed case as be write any derived callback bindings out into the // callback structure as the endpoint is serialized across the domain RuntimeEndpoint callbackEndpoint = null; // 1/ look in callback endpoints at the reference // - exploiting the assumption that the user will specific callback bindings of // the same type as the forward binding match1: for(Endpoint loopCallbackEndpoint : callbackService.getEndpoints()){ if(loopCallbackEndpoint.getBinding().getType().equals(endpointReference.getBinding().getType())){ callbackEndpoint = (RuntimeEndpoint)loopCallbackEndpoint; break match1; } } // if no callback endpoint was found then create a new callback endpoint if (callbackEndpoint == null ){ callbackEndpoint = (RuntimeEndpoint)assemblyFactory.createEndpoint(); callbackEndpoint.setComponent(endpointReference.getComponent()); callbackEndpoint.setService(callbackService); Binding forwardBinding = endpointReference.getBinding(); Binding callbackBinding = null; // 2/ look in the service callback structure Callback serviceCallback = endpointReference.getTargetEndpoint().getService().getCallback(); if (serviceCallback != null){ for(Binding loopCallbackBinding : serviceCallback.getBindings()){ if(loopCallbackBinding.getType().equals(endpointReference.getBinding().getType())){ callbackBinding = loopCallbackBinding; break; } } } // 3/ look in the service endpoint callback reference structure ComponentReference callbackReference = endpointReference.getTargetEndpoint().getService().getCallbackReference(); if (callbackReference != null){ for (EndpointReference loopEndpointReference : callbackReference.getEndpointReferences()){ if (loopEndpointReference.getBinding().getType().equals(endpointReference.getBinding().getType())){ callbackBinding = loopEndpointReference.getBinding(); break; } } } // if all else fails clone the forward binding // TODO - do we ever get here? if (callbackBinding == null){ try { callbackBinding = (Binding)forwardBinding.clone(); } catch (CloneNotSupportedException ex){ } } // get the callback binding URI by looking at the SCA binding // that will have been added at build time callbackBinding.setURI(null); for (Endpoint endpoint : callbackService.getEndpoints()){ if (endpoint.getBinding().getType().equals(SCABinding.TYPE)){ callbackBinding.setURI(endpoint.getBinding().getURI()); } } callbackEndpoint.setBinding(callbackBinding); callbackService.getBindings().add(callbackBinding); callbackEndpoint.setUnresolved(false); callbackService.getEndpoints().add(callbackEndpoint); // build it build(callbackEndpoint); // Only activate the callback endpoint if the bind is being done at runtime // and hence everything else is running. If it's build time then the // endpoint will be activated at the same time as all the other endpoints if (runtime) { // activate it compositeActivator.activate(((RuntimeEndpointReferenceImpl)endpointReference).getCompositeContext(), callbackEndpoint); // start it compositeActivator.start(((RuntimeEndpointReferenceImpl)endpointReference).getCompositeContext(), callbackEndpoint); } } // finally set the callback endpoint into the forward reference endpointReference.setCallbackEndpoint(callbackEndpoint); } private void build(Endpoint endpoint) { BindingBuilder builder = builders.getBindingBuilder(endpoint.getBinding().getType()); if (builder != null) { builder.build(endpoint.getComponent(), endpoint.getService(), endpoint.getBinding(), new BuilderContext(extensionPoints), true); } } /** * Determine if endpoint reference and endpoint policies match. We know by this stage * that * - a given policy set will only contain expressions from a single language * - a given endpoint or endpoint reference's policy sets will only contain * expressions from a single language * * Matching algorithm (read from the top down): * - FAIL if there are intents that are mutually exclusive between reference and service * - PASS if there are no intents or policies present at reference and service * - FAIL if there are unresolved intents (intents with no policy set) at the reference (service should have been checked previously) * the wrinkle here is that we need to adopt policy from the service if the reference doesn't define a binding * - PASS if there are no policies at reference and service (now we know all intents are resolved) * - FAIL if there are some policies on one side but not on the other * - PASS if the QName of the policy sets on each side match * - FAIL if the policy languages on both sides are different * - Perform policy specific match * */ private boolean haveMatchingPolicy(EndpointReference endpointReference, Endpoint endpoint, Audit matchAudit, BuilderContext builderContext){ matchAudit.append("Match policy of " + endpointReference.toString() + " to " + endpoint.toString() + " "); if (!endpoint.getSpecVersion().equals(Base.SCA11_NS)){ // the thing we need to check here is asyncInvocation as only OASIS supports that if (endpointReference.isAsyncInvocation()){ // this definitely won't mactch anything but OASIS so fail matchAudit.append("No match because the endpoint reference is configured for asyncInvocation " + "and the target endpoint is not an OASIS endpoint, specVersion = " + endpoint.getSpecVersion()); matchAudit.appendSeperator(); return false; } else { // Assume it matches as we don't know how to do policy // matching with anything but OASIS endpoints matchAudit.append("Match because the target endpoint is not an OASIS endpoint, specVersion = " + endpoint.getSpecVersion()); matchAudit.appendSeperator(); return true; } } List referencePolicySets = new ArrayList(); Binding binding = null; if (endpointReference.getBinding() == null){ binding = endpoint.getBinding(); } else { binding = endpointReference.getBinding(); } // if there are any intents that are mutually exclusive between // service and reference then they don't match for (Intent eprIntent : endpointReference.getRequiredIntents()){ for (Intent epIntent : endpoint.getRequiredIntents()){ if (eprIntent.getExcludedIntents().contains(epIntent) || epIntent.getExcludedIntents().contains(eprIntent) || checkQualifiedMutualExclusion(eprIntent.getExcludedIntents(), epIntent) || checkQualifiedMutualExclusion(epIntent.getExcludedIntents(), eprIntent)){ matchAudit.append("No match because the following intents are mutually exclusive " + eprIntent.toString() + " " + epIntent.toString() + " "); matchAudit.appendSeperator(); return false; } } } // Find the set of policy sets from this reference. This includes // the policy sets that are specific to the service binding and // any policy sets that are not binding specific for (PolicySet policySet : endpointReference.getPolicySets()){ PolicyBuilder policyBuilder = null; if (policySet.getPolicies().size() > 0){ QName policyType = policySet.getPolicies().get(0).getName(); policyBuilder = builders.getPolicyBuilder(policyType); } if ((policyBuilder == null) || (policyBuilder != null && policyBuilder.getSupportedBindings() == null) || (policyBuilder != null && policyBuilder.getSupportedBindings().contains(binding.getType()))){ referencePolicySets.add(policySet); } } // if there are no policy sets on the reference take the policy sets from the // service binding we are matching against if (referencePolicySets.isEmpty()) { for (PolicySet policySet : endpoint.getPolicySets()){ PolicyBuilder policyBuilder = null; if (policySet.getPolicies().size() > 0){ QName policyType = policySet.getPolicies().get(0).getName(); policyBuilder = builders.getPolicyBuilder(policyType); } if ((policyBuilder == null) || (policyBuilder != null && policyBuilder.getSupportedBindings() == null) || (policyBuilder != null && policyBuilder.getSupportedBindings().contains(binding.getType()))){ referencePolicySets.add(policySet); } } } // the "appliesTo" algorithm to remove any policy sets that // don't apply to the service binding will already have been // run during the build phase // Determine if there are any reference policies boolean noEndpointReferencePolicies = true; for (PolicySet policySet : referencePolicySets){ if (policySet.getPolicies().size() > 0){ noEndpointReferencePolicies = false; break; } } // Determine of there are any service policies boolean noEndpointPolicies = true; for (PolicySet policySet : endpoint.getPolicySets()){ if (policySet.getPolicies().size() > 0){ noEndpointPolicies = false; break; } } // if no policy sets or intents are present then they match if ((endpointReference.getRequiredIntents().size() == 0) && (endpoint.getRequiredIntents().size() == 0) && (noEndpointReferencePolicies) && (noEndpointPolicies)) { matchAudit.append("Match because there are no intents or policies "); matchAudit.appendSeperator(); return true; } // check that the intents on the reference side are resolved // can't do this until this point as the service binding // may come into play. Intents may be satisfied by the default // or optional intents that the binding type provides. Failing // this they must be satisfied by reference policy sets // Failing this the intent is unresolved and the reference and // service don't match // TODO - seems that we should do this loop on a binding by binding basis // rather than each time we do matching BindingType bindingType = null; Definitions systemDefinitions = null; if (builderContext != null){ systemDefinitions = builderContext.getDefinitions(); } else { systemDefinitions = ((RuntimeEndpoint)endpoint).getCompositeContext().getSystemDefinitions(); } bindingType = systemDefinitions.getBindingType(binding.getType()); // Before we start examining intents, remove any whose constrained // types don't include the binding type removeConstrainedIntents(endpointReference, bindingType); List eprIntents = new ArrayList(); List eprMayProvideInterationIntents = new ArrayList(); eprIntents.addAll(endpointReference.getRequiredIntents()); // first check the binding type for (Intent intent : endpointReference.getRequiredIntents()){ if (bindingType != null && bindingType.getAlwaysProvidedIntents().contains(intent)){ eprIntents.remove(intent); } else if (bindingType != null && bindingType.getMayProvidedIntents().contains(intent)){ eprIntents.remove(intent); if (intent.getType().equals(Intent.Type.interaction)){ eprMayProvideInterationIntents.add(intent); } } else { // TODO - this code also appears in the ComponentPolicyBuilder // so should rationalize loop: for (PolicySet policySet : referencePolicySets){ if (policySet.getProvidedIntents().contains(intent)){ eprIntents.remove(intent); break; } for (Intent psProvidedIntent : policySet.getProvidedIntents()){ if (isQualifiedBy(psProvidedIntent, intent)){ eprIntents.remove(intent); break loop; } } for (IntentMap map : policySet.getIntentMaps()) { for (Qualifier q : map.getQualifiers()) { if (intent.equals(q.getIntent())) { eprIntents.remove(intent); break loop; } } } } } } // if there are unresolved intents the service and reference don't match if (eprIntents.size() > 0){ matchAudit.append("No match because there are unresolved intents " + eprIntents.toString() + " "); matchAudit.appendSeperator(); return false; } // TUSCANY-3959 - something that's not explicitly stated in the spec. mayProvides intents don't // lead to policy sets as the binding natively implements the intent. So // we need to check that these intents match explicitly between reference and service // sides if (eprMayProvideInterationIntents.size() > 0){ for (Intent eprIntent : eprMayProvideInterationIntents){ boolean match = false; for (Intent epIntent : endpoint.getRequiredIntents()){ if (epIntent.equals(eprIntent)){ match = true; break; } } if (!match){ matchAudit.append("No match because the reference has a mayProvide intent that the service doesn't have " + eprIntent.getName()); matchAudit.appendSeperator(); return false; } } } // if there are no policies on epr or ep side then // they match if (noEndpointPolicies && noEndpointReferencePolicies){ matchAudit.append("Match because the intents are resolved and there are no policy sets "); matchAudit.appendSeperator(); return true; } // if there are some policies on one side and not the other then // the don't match if (noEndpointPolicies && !noEndpointReferencePolicies) { matchAudit.append("No match because there are policy sets at the endpoint reference but not at the endpoint "); matchAudit.appendSeperator(); return false; } if (!noEndpointPolicies && noEndpointReferencePolicies){ matchAudit.append("No match because there are policy sets at the endpoint but not at the endpoint reference "); matchAudit.appendSeperator(); return false; } // If policy set QNames from epr and er match exactly then the reference and // service policies are compatible Set referencePolicySet = new HashSet(referencePolicySets); Set servicePolicySet = new HashSet(endpoint.getPolicySets()); if(referencePolicySet.equals(servicePolicySet)){ matchAudit.append("Match because the policy sets on both sides are eactly the same "); matchAudit.appendSeperator(); return true; } // if policy set language at ep and epr are not the same then there is no // match. We get the policy language by looking at the first expression // of the first policy set. By this stage we know that all the policy sets // in an endpoint or endpoint reference will use a single language and we know // that there is at least one policy set with at least one policy QName eprLanguage = null; for (PolicySet policySet : referencePolicySets){ if (policySet.getPolicies().size() > 0){ eprLanguage = policySet.getPolicies().get(0).getName(); break; } } QName epLanguage = null; for (PolicySet policySet : endpoint.getPolicySets()){ if (policySet.getPolicies().size() > 0){ epLanguage = policySet.getPolicies().get(0).getName(); break; } } if(!eprLanguage.getNamespaceURI().equals(epLanguage.getNamespaceURI())){ matchAudit.append("No match because the policy sets on either side have policies in differnt languages " + eprLanguage + " and " + epLanguage + " "); matchAudit.appendSeperator(); return false; } // now do a policy specific language match PolicyBuilder builder = builders.getPolicyBuilder(eprLanguage); boolean match = false; // switch the derived list of policy sets into the reference // it will be left there if there is a match List originalPolicySets = endpointReference.getPolicySets(); endpointReference.getPolicySets().clear(); endpointReference.getPolicySets().addAll(referencePolicySets); if (builder != null) { if (builderContext == null){ builderContext = new BuilderContext(monitor); } match = builder.build(endpointReference, endpoint, builderContext); } if (!match){ matchAudit.append("No match because the language specific matching failed "); matchAudit.appendSeperator(); endpointReference.getPolicySets().clear(); endpointReference.getPolicySets().addAll(originalPolicySets); } else { matchAudit.append("Match because the language specific matching succeeded "); matchAudit.appendSeperator(); } return match; } // Copied from ComponentPolicyBuilder, should probably be refactored protected void removeConstrainedIntents(EndpointReference subject, BindingType bindingType) { List intents = subject.getRequiredIntents(); // Remove the intents whose @contrains do not include the current element if(bindingType != 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(bindingType.getType()) || constrainedType.getType().equals(bindingType.getBaseType())){ constraintFound = true; break; } } if(!constraintFound){ intents.remove(i); } } } } } protected boolean isQualifiedBy(Intent qualifiableIntent, Intent qualifiedIntent){ if (qualifiedIntent.getQualifiableIntent() == qualifiableIntent){ return true; } else { return false; } } 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; } /** * Determine if endpoint reference and endpoint interface contracts match */ private boolean haveMatchingInterfaceContracts(EndpointReference endpointReference, Endpoint endpoint, Audit matchAudit){ matchAudit.append("Match interface of " + endpointReference.toString() + " to " + endpoint.toString() + " "); InterfaceContract endpointReferenceContract = endpointReference.getReference().getInterfaceContract(); InterfaceContract endpointContract = endpoint.getComponentServiceInterfaceContract(); if (endpointReferenceContract == null){ matchAudit.append("Match because there is no interface contract on the reference "); matchAudit.appendSeperator(); return true; } if (!endpoint.getSpecVersion().equals(Base.SCA11_NS)){ // Assume it matches as we don't know how to do interface // matching with anything but OASIS endpoint matchAudit.append("Match because the target endpoint is not an OASIS endpoint, specVersion = " + endpoint.getSpecVersion()); matchAudit.appendSeperator(); return true; } /* For testing this code turns off remote interface matching completely if (endpoint.isRemote()){ matchAudit.append("Match because endpoint is remote"); matchAudit.appendSeperator(); return true; } */ // If the remote interface was not retrieved successfully from the registry for whatever reason // then assume the interfaces match and leave the checking until runtime. We looking for an interface // with no operations defined to tell us this. if ((endpointContract.getInterface().getOperations().size() == 0 && endpointContract.getNormalizedWSDLContract() == null) || (endpointContract.getNormalizedWSDLContract() != null && endpointContract.getNormalizedWSDLContract().getInterface().getOperations().size() == 0)){ matchAudit.append("Match because the endpoint is remote and we don't have a copy of it's interface contract "); matchAudit.appendSeperator(); return true; } // TUSCANY-4033 // Detect the case where the interfaces are both Java but the Endpoint is remote in which case // we have to match the local Java interface with the remote Tuscany interface. But we can do this // without running WSDLGen if (endpointReferenceContract.getClass() == endpointContract.getClass() && endpointReferenceContract instanceof JavaInterfaceContract && endpointContract.getNormalizedWSDLContract() != null && endpointContract.getNormalizedWSDLContract() instanceof TuscanyInterfaceContractImpl){ // use the TuscanyInterfaceContractImpl to compare against the Java contract endpointContract = endpointContract.getNormalizedWSDLContract(); } else if (endpointReferenceContract.getClass() != endpointContract.getClass() || endpointReferenceContract.getNormalizedWSDLContract() != null || endpointContract.getNormalizedWSDLContract() != null) { // If the contracts are not of the same type use normailized interfaces endpointReferenceContract = ((RuntimeEndpointReference)endpointReference).getGeneratedWSDLContract(endpointReferenceContract); endpointContract = ((RuntimeEndpoint)endpoint).getGeneratedWSDLContract(endpointContract); } boolean match = false; match = interfaceContractMapper.isCompatibleSubset(endpointReferenceContract, endpointContract, matchAudit); if (!match){ matchAudit.append("Match failed because the interface contract mapper failed "); } else { matchAudit.append("Match because the interface contract mapper succeeded "); } matchAudit.appendSeperator(); return match; } /** * Checks to see if the registry has been updated since the reference was last matched * * @return true is the registry has changed */ public boolean isOutOfDate(DomainRegistry domainRegistry, EndpointReference endpointReference) { Endpoint te = endpointReference.getTargetEndpoint(); if (te != null && !te.isUnresolved() && te.getURI() != null && endpointReference.getStatus() != EndpointReference.Status.RESOLVED_BINDING) { return domainRegistry.isOutOfDate(endpointReference); } return false; } /** * ASM_5021: where a of a has @autowire=true * and where the has a child element which * declares a single target service, the reference is wired only to * the single service identified by the element */ private void setSingleAutoWireTarget(ComponentReference reference) { if (reference.getEndpointReferences().size() > 1 && reference.getBindings() != null && reference.getBindings().size() == 1) { String uri = reference.getBindings().get(0).getURI(); if (uri != null) { if (uri.indexOf('/') > -1) { // TODO: must be a way to avoid this fiddling int i = uri.indexOf('/'); String c = uri.substring(0, i); String s = uri.substring(i + 1); uri = c + "#service(" + s + ")"; } for (EndpointReference er : reference.getEndpointReferences()) { if (er.getTargetEndpoint() != null && uri.equals(er.getTargetEndpoint().getURI())) { reference.getEndpointReferences().clear(); reference.getEndpointReferences().add(er); return; } } } } } }