blob: 647cd12a32df294478f0f74a0c86f4f05d931a54 [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.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<Endpoint> 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<Endpoint> 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<Endpoint> matchedEndpoints = new ArrayList<Endpoint>();
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<PolicySet> psToRemove = new ArrayList<PolicySet>();
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<PolicySet> referencePolicySets = new ArrayList<PolicySet>();
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 {
if (((RuntimeEndpoint)endpoint).getCompositeContext() == null) {
return true;
}
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<Intent> eprIntents = new ArrayList<Intent>();
List<Intent> eprMayProvideInterationIntents = new ArrayList<Intent>();
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<PolicySet> referencePolicySet = new HashSet<PolicySet>(referencePolicySets);
Set<PolicySet> servicePolicySet = new HashSet<PolicySet>(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<PolicySet> 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<Intent> intents = subject.getRequiredIntents();
// Remove the intents whose @contrains do not include the current element
if(bindingType != 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(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<Intent> 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 || endpointContract == 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 <reference/> of a <component/> has @autowire=true
* and where the <reference/> has a <binding/> child element which
* declares a single target service, the reference is wired only to
* the single service identified by the <wire/> 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;
}
}
}
}
}
}