From e5b7380c874745c989d1816b8f552504f038e1bc Mon Sep 17 00:00:00 2001 From: lresende Date: Thu, 26 Sep 2013 20:33:20 +0000 Subject: 2.0 branch for possible maintenance release git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1526672 13f79535-47bb-0310-9956-ffa450edef68 --- .../runtime/impl/EndpointReferenceBinderImpl.java | 1206 ++++++++++++++++++++ 1 file changed, 1206 insertions(+) create mode 100644 sca-java-2.x/branches/2.0/modules/core/src/main/java/org/apache/tuscany/sca/core/runtime/impl/EndpointReferenceBinderImpl.java (limited to 'sca-java-2.x/branches/2.0/modules/core/src/main/java/org/apache/tuscany/sca/core/runtime/impl/EndpointReferenceBinderImpl.java') diff --git a/sca-java-2.x/branches/2.0/modules/core/src/main/java/org/apache/tuscany/sca/core/runtime/impl/EndpointReferenceBinderImpl.java b/sca-java-2.x/branches/2.0/modules/core/src/main/java/org/apache/tuscany/sca/core/runtime/impl/EndpointReferenceBinderImpl.java new file mode 100644 index 0000000000..395d154779 --- /dev/null +++ b/sca-java-2.x/branches/2.0/modules/core/src/main/java/org/apache/tuscany/sca/core/runtime/impl/EndpointReferenceBinderImpl.java @@ -0,0 +1,1206 @@ +/* + * 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; + } + } + } + } + } + + +} -- cgit v1.2.3