blob: 714268a634d9fc45aec45dae63476db6522b79cc [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.context;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.xml.stream.XMLStreamReader;
import org.apache.tuscany.sca.assembly.Binding;
import org.apache.tuscany.sca.assembly.Component;
import org.apache.tuscany.sca.assembly.ComponentService;
import org.apache.tuscany.sca.assembly.CompositeService;
import org.apache.tuscany.sca.assembly.ConfiguredOperation;
import org.apache.tuscany.sca.assembly.OperationsConfigurator;
import org.apache.tuscany.sca.assembly.OptimizableBinding;
import org.apache.tuscany.sca.assembly.Reference;
import org.apache.tuscany.sca.assembly.SCABinding;
import org.apache.tuscany.sca.assembly.Service;
import org.apache.tuscany.sca.assembly.builder.BindingBuilderExtension;
import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
import org.apache.tuscany.sca.contribution.resolver.ResolverExtension;
import org.apache.tuscany.sca.core.assembly.BusinessInterfaceImpl;
import org.apache.tuscany.sca.core.assembly.CompositeActivator;
import org.apache.tuscany.sca.core.assembly.CompositeActivatorImpl;
import org.apache.tuscany.sca.core.assembly.EndpointReferenceImpl;
import org.apache.tuscany.sca.core.assembly.EndpointWireImpl;
import org.apache.tuscany.sca.core.assembly.ReferenceParametersImpl;
import org.apache.tuscany.sca.core.conversation.ConversationManager;
import org.apache.tuscany.sca.core.conversation.ConversationState;
import org.apache.tuscany.sca.core.conversation.ExtendedConversation;
import org.apache.tuscany.sca.core.factory.ObjectCreationException;
import org.apache.tuscany.sca.core.invocation.ProxyFactory;
import org.apache.tuscany.sca.interfacedef.Interface;
import org.apache.tuscany.sca.interfacedef.InterfaceContract;
import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
import org.apache.tuscany.sca.policy.PolicySet;
import org.apache.tuscany.sca.policy.PolicySetAttachPoint;
import org.apache.tuscany.sca.runtime.BusinessInterface;
import org.apache.tuscany.sca.runtime.EndpointReference;
import org.apache.tuscany.sca.runtime.ReferenceParameters;
import org.apache.tuscany.sca.runtime.RuntimeComponent;
import org.apache.tuscany.sca.runtime.RuntimeComponentReference;
import org.apache.tuscany.sca.runtime.RuntimeWire;
import org.osoa.sca.CallableReference;
import org.osoa.sca.Conversation;
import org.osoa.sca.ServiceRuntimeException;
/**
* Base class for implementations of service and callback references.
*
* @version $Rev$ $Date$
* @param <B> the type of the business interface
*/
public class CallableReferenceImpl<B> implements CallableReference<B>, Externalizable {
static final long serialVersionUID = -521548304761848325L;
protected transient CompositeActivator compositeActivator;
protected transient ProxyFactory proxyFactory;
protected transient Class<B> businessInterface;
protected transient Object proxy;
// if the wire targets a conversational service this holds the conversation state
protected transient ConversationManager conversationManager;
protected transient ExtendedConversation conversation;
protected transient Object conversationID;
protected Object callbackID; // The callbackID should be serializable
protected transient RuntimeComponent component;
protected transient RuntimeComponentReference reference;
protected transient Binding binding;
protected String scdl;
private transient RuntimeComponentReference clonedRef;
private transient ReferenceParameters refParams;
private transient XMLStreamReader xmlReader;
private transient RuntimeWire endpointWire;
/*
* Public constructor for Externalizable serialization/deserialization
*/
public CallableReferenceImpl() {
super();
}
/*
* Public constructor for use by XMLStreamReader2CallableReference
*/
public CallableReferenceImpl(XMLStreamReader xmlReader) throws Exception {
this.xmlReader = xmlReader;
resolve();
}
protected CallableReferenceImpl(Class<B> businessInterface,
RuntimeComponent component,
RuntimeComponentReference reference,
Binding binding,
ProxyFactory proxyFactory,
CompositeActivator compositeActivator) {
this.proxyFactory = proxyFactory;
this.businessInterface = businessInterface;
this.component = component;
this.reference = reference;
this.binding = binding;
// FIXME: The SCA Specification is not clear how we should handle multiplicity
// for CallableReference
if (this.binding == null) {
this.binding = this.reference.getBinding(SCABinding.class);
if (this.binding == null) {
// TODO: TUSCANY-2580: if the refernece doesn't have a binding yet then instead of NPE use a candidate one if its avaialable
if (reference.getBindings() != null && reference.getBindings().size() > 0) {
this.binding = this.reference.getBindings().get(0);
} else {
this.binding = this.reference.getEndpoints().get(0).getCandidateBindings().get(0);
if (this.reference.getEndpoints().get(0).getInterfaceContract() == null) {
this.reference.getEndpoints().get(0).setInterfaceContract(reference.getInterfaceContract());
}
}
}
}
// FIXME: Should we normalize the componentName/serviceName URI into an absolute SCA URI in the SCA binding?
// sca:component1/component11/component112/service1?
this.compositeActivator = compositeActivator;
this.conversationManager = this.compositeActivator.getConversationManager();
initCallbackID();
}
public CallableReferenceImpl(Class<B> businessInterface, RuntimeWire wire, ProxyFactory proxyFactory) {
this.proxyFactory = proxyFactory;
this.businessInterface = businessInterface;
bind(wire);
}
public RuntimeWire getRuntimeWire() {
try {
resolve();
if (endpointWire != null){
return endpointWire;
} else if (reference != null) {
return reference.getRuntimeWire(binding);
} else {
return null;
}
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
protected void bind(RuntimeWire wire) {
if (wire != null) {
if (wire instanceof EndpointWireImpl){
endpointWire = wire;
}
this.component = wire.getSource().getComponent();
this.reference = (RuntimeComponentReference)wire.getSource().getContract();
this.binding = wire.getSource().getBinding();
this.compositeActivator = ((ComponentContextImpl)component.getComponentContext()).getCompositeActivator();
this.conversationManager = this.compositeActivator.getConversationManager();
initCallbackID();
}
}
protected void initCallbackID() {
if (reference.getInterfaceContract() != null) {
if (reference.getInterfaceContract().getCallbackInterface() != null) {
this.callbackID = createCallbackID();
}
}
}
public B getProxy() throws ObjectCreationException {
try {
if (proxy == null) {
proxy = createProxy();
}
return businessInterface.cast(proxy);
} catch (Exception e) {
throw new ObjectCreationException(e);
}
}
public void setProxy(Object proxy) {
this.proxy = proxy;
}
protected Object createProxy() throws Exception {
return proxyFactory.createProxy(this);
}
public B getService() {
try {
resolve();
return getProxy();
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
public Class<B> getBusinessInterface() {
try {
resolve();
return businessInterface;
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
public boolean isConversational() {
try {
resolve();
return reference == null ? false : reference.getInterfaceContract().getInterface().isConversational();
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
public Conversation getConversation() {
try {
// resolve from XML just in case this CallableReference is the result of
// passing a CallableReference as a parameter
resolve();
if (conversation == null || conversation.getState() == ConversationState.ENDED) {
conversation = null;
}
return conversation;
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
public Object getCallbackID() {
try {
resolve();
return callbackID;
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
/**
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
final boolean hasSCDL = in.readBoolean();
if (hasSCDL) {
this.scdl = in.readUTF();
} else {
this.scdl = null;
}
}
/**
* @throws IOException
*/
private synchronized void resolve() throws Exception {
if ((scdl != null || xmlReader != null) && component == null && reference == null) {
ComponentContextHelper componentContextHelper = ComponentContextHelper.getCurrentComponentContextHelper();
if (componentContextHelper != null) {
this.compositeActivator = ComponentContextHelper.getCurrentCompositeActivator();
this.conversationManager = this.compositeActivator.getConversationManager();
Component c;
if (xmlReader != null) {
c = componentContextHelper.fromXML(xmlReader);
xmlReader = null; // OK to GC this now
} else {
c = componentContextHelper.fromXML(scdl);
scdl = null; // OK to GC this now
}
this.component = (RuntimeComponent)c;
compositeActivator.configureComponentContext(this.component);
this.reference = (RuntimeComponentReference)c.getReferences().get(0);
this.reference.setComponent(this.component);
clonedRef = reference;
ReferenceParameters parameters = null;
BusinessInterface businessInterfaceExt = null;
for (Object ext : reference.getExtensions()) {
if (ext instanceof ReferenceParameters) {
parameters = (ReferenceParameters)ext;
} else if (ext instanceof BusinessInterface) {
// this extension will always be present
businessInterfaceExt = (BusinessInterface)ext;
}
}
if (parameters != null) {
refParams = parameters;
this.callbackID = parameters.getCallbackID();
attachConversation(parameters.getConversationID());
}
for (Binding binding : reference.getBindings()) {
if (binding instanceof OptimizableBinding) {
// Resolve the Component
final String bindingURI = binding.getURI();
final Component targetComponent = resolveComponentURI(bindingURI);
// Find the Service
ComponentService targetService = resolveServiceURI(bindingURI, targetComponent);
// if the target service is a promoted service then find the
// service it promotes
if ((targetService != null) && (targetService.getService() instanceof CompositeService)) {
CompositeService compositeService = (CompositeService) targetService.getService();
// Find the promoted component service
ComponentService promotedComponentService = getPromotedComponentService(compositeService);
if (promotedComponentService != null && !promotedComponentService.isUnresolved()) {
targetService = promotedComponentService;
}
}
OptimizableBinding optimizableBinding = (OptimizableBinding)binding;
optimizableBinding.setTargetComponent(targetComponent);
optimizableBinding.setTargetComponentService(targetService);
if (targetService != null) {
for (Binding serviceBinding : targetService.getBindings()) {
if (serviceBinding.getClass() == binding.getClass()) {
optimizableBinding.setTargetBinding(serviceBinding);
break;
}
}
}
}
}
// FIXME: The SCA Specification is not clear how we should handle multiplicity
// for CallableReference
if (binding == null) {
binding = reference.getBinding(SCABinding.class);
if (binding == null) {
binding = reference.getBindings().get(0);
}
}
ModelResolver resolver = ((ResolverExtension)ComponentContextHelper.getCurrentComponent()).getModelResolver();
componentContextHelper.resolveInterfaceContract(reference.getInterfaceContract(), resolver);
Interface i = reference.getInterfaceContract().getInterface();
if (i instanceof JavaInterface) {
JavaInterface javaInterface = (JavaInterface)i;
if (javaInterface.isUnresolved()) {
// Allow privileged access to get ClassLoader. Requires RuntimePermission in
// security policy.
ClassLoader classLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
});
javaInterface.setJavaClass(classLoader.loadClass(javaInterface.getName()));
compositeActivator.getJavaInterfaceFactory().createJavaInterface(javaInterface,
javaInterface.getJavaClass());
//FIXME: If the interface needs XSDs to be loaded (e.g., for static SDO),
// this needs to be done here. We usually search for XSDs in the current
// contribution at resolve time. If we need to add code here to do this,
// we can resolve XSDs using the model resolver for the current component.
}
this.businessInterface = (Class<B>)javaInterface.getJavaClass();
} else {
// Allow privileged access to get ClassLoader. Requires RuntimePermission in
// security policy.
ClassLoader classLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
});
businessInterface = (Class<B>)classLoader.loadClass(businessInterfaceExt.getInterface());
reference.setReference(componentContextHelper.createReference(businessInterface));
}
componentContextHelper.resolveBinding(binding, resolver);
if (binding instanceof PolicySetAttachPoint) {
PolicySetAttachPoint policiedBinding = (PolicySetAttachPoint)binding;
List<PolicySet> policySets = policiedBinding.getPolicySets();
resolvePolicySets(policySets, resolver);
List<PolicySet> applicablePolicySets = policiedBinding.getApplicablePolicySets();
applicablePolicySets.addAll(policySets);
}
if (binding instanceof OperationsConfigurator) {
OperationsConfigurator opConfigurator = (OperationsConfigurator)binding;
for (ConfiguredOperation confOp : opConfigurator.getConfiguredOperations()) {
List<PolicySet> policySets = confOp.getPolicySets();
resolvePolicySets(policySets, resolver);
List<PolicySet> applicablePolicySets = confOp.getApplicablePolicySets();
applicablePolicySets.addAll(policySets);
}
}
if (binding instanceof BindingBuilderExtension) {
((BindingBuilderExtension)binding).getBuilder().build(component, reference, binding, null);
}
this.proxyFactory = compositeActivator.getProxyFactory();
}
} else if (compositeActivator == null || proxyFactory == null) {
this.compositeActivator = ComponentContextHelper.getCurrentCompositeActivator();
if (this.compositeActivator != null) {
this.proxyFactory = this.compositeActivator.getProxyFactory();
}
}
}
/**
* Resolve policy sets attached to a specific SCA Construct
* @param policySets list of attached policy sets
* @param resolver
*/
private void resolvePolicySets(List<PolicySet> policySets, ModelResolver resolver) {
List<PolicySet> resolvedPolicySets = new ArrayList<PolicySet>();
PolicySet resolvedPolicySet = null;
for (PolicySet policySet : policySets) {
if (policySet.isUnresolved()) {
resolvedPolicySet = resolver.resolveModel(PolicySet.class, policySet);
resolvedPolicySets.add(resolvedPolicySet);
} else {
resolvedPolicySets.add(policySet);
}
}
policySets.clear();
policySets.addAll(resolvedPolicySets);
}
/**
* Follow a service promotion chain down to the inner most (non composite)
* component service.
*
* @param topCompositeService
* @return
*/
private ComponentService getPromotedComponentService(CompositeService compositeService) {
ComponentService componentService = compositeService.getPromotedService();
if (componentService != null) {
Service service = componentService.getService();
if (componentService.getName() != null && service instanceof CompositeService) {
// Continue to follow the service promotion chain
return getPromotedComponentService((CompositeService)service);
} else {
// Found a non-composite service
return componentService;
}
} else {
// No promoted service
return null;
}
}
/**
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException {
try {
final String xml = toXMLString();
if (xml == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeUTF(xml);
}
} catch (Exception e) {
// e.printStackTrace();
throw new IOException(e.toString());
}
}
public String toXMLString() throws IOException {
if (reference != null) {
if (clonedRef == null) {
try {
clonedRef = (RuntimeComponentReference)reference.clone();
} catch (CloneNotSupportedException e) {
// will not happen
}
BusinessInterface bizInterface = new BusinessInterfaceImpl();
bizInterface.setInterface(businessInterface.getName());
clonedRef.getExtensions().add(bizInterface);
}
if (refParams == null) {
refParams = new ReferenceParametersImpl();
// remove any existing reference parameters from the clone
Object toRemove = null;
for (Object extension : clonedRef.getExtensions()){
if (extension instanceof ReferenceParameters){
toRemove = extension;
}
}
if (toRemove != null){
clonedRef.getExtensions().remove(toRemove);
}
// add the new reference parameter object
clonedRef.getExtensions().add(refParams);
}
refParams.setCallbackID(callbackID);
if (conversation != null){
refParams.setConversationID(conversation.getConversationID());
}
return ((CompositeActivatorImpl)compositeActivator).getComponentContextHelper()
.toXML(component, clonedRef);
} else {
return scdl;
}
}
/**
* Create a callback id
*
* @return the callback id
*/
private String createCallbackID() {
return UUID.randomUUID().toString();
}
public void attachCallbackID(Object callbackID) {
this.callbackID = callbackID;
}
public void attachConversationID(Object conversationID) {
this.conversationID = conversationID;
}
public void attachConversation(ExtendedConversation conversation) {
this.conversation = conversation;
}
public void attachConversation(Object conversationID) {
if (conversationID != null) {
ExtendedConversation conversation = conversationManager.getConversation(conversationID);
if (conversation == null){
conversation = conversationManager.startConversation(conversationID);
}
this.conversation = conversation;
} else {
this.conversation = null;
}
}
protected ReferenceParameters getReferenceParameters() {
ReferenceParameters parameters = new ReferenceParametersImpl();
parameters.setCallbackID(callbackID);
if (getConversation() != null) {
parameters.setConversationID(conversation.getConversationID());
}
return parameters;
}
public EndpointReference getEndpointReference() {
try {
resolve();
// Use the interface contract of the reference on the component type
Reference componentTypeRef = reference.getReference();
InterfaceContract sourceContract =
componentTypeRef == null ? reference.getInterfaceContract() : componentTypeRef.getInterfaceContract();
sourceContract = sourceContract.makeUnidirectional(false);
EndpointReference epr = new EndpointReferenceImpl(component, reference, binding, sourceContract);
ReferenceParameters parameters = getReferenceParameters();
epr.setReferenceParameters(parameters);
return epr;
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
}
public XMLStreamReader getXMLReader() {
return xmlReader;
}
/**
* Resolves the specified URI to a Component using the compositeActivator.
* There are two cases that we need to handle:
* <ul>
* <li>URI containing just Composite name(s) (i.e. no Service name specified)
* <li>URI containing Composite name(s) and a Service Name
* </ul>
*
* @param componentURI The URI of the Component to resolve
* @return The Component for the specified URI or null if not founds
*/
protected Component resolveComponentURI(String componentURI) {
// If the URI has come from a binding, it may well start with a '/'. We will need
// to remove this so we can match it to the composite names.
if (componentURI.startsWith("/")) {
componentURI = componentURI.substring(1);
}
// First assume that we are dealing with a Component URI without a Service Name
Component component = compositeActivator.resolve(componentURI);
if (component != null) {
return component;
}
// Perhaps we have a ComponentURI that has a ServiceName on the end of it
final int index = componentURI.lastIndexOf('/');
if (index > -1) {
componentURI = componentURI.substring(0, index);
return compositeActivator.resolve(componentURI);
}
// We could not resolve the Component URI
return null;
}
/**
* Examines the Services on the specified Component and returns the Service that matches the
* specified Binding URI.
*
* @param bindingURI The Binding URI to resolve on the Component
* @param targetComponent The Component containing the Services
* @return The Service with the specified serviceName or null if no such Service found.
*/
protected ComponentService resolveServiceURI(String bindingURI, Component targetComponent) {
ComponentService targetService = null;
if (targetComponent != null) {
if (bindingURI.startsWith("/")) {
bindingURI = bindingURI.substring(1);
}
final String componentURI = targetComponent.getURI();
final String serviceName;
if (componentURI.equals(bindingURI)) {
// No service specified
serviceName = "";
} else {
// Get the Service name from the Binding URI
serviceName = bindingURI.substring(componentURI.length() + 1);
}
if ("".equals(serviceName)) {
targetService = ComponentContextHelper.getSingleService(targetComponent);
} else {
for (ComponentService service : targetComponent.getServices()) {
if (service.getName().equals(serviceName)) {
targetService = service;
break;
}
}
}
}
return targetService;
}
}