/* * 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.invocation; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.List; 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.Contract; import org.apache.tuscany.sca.assembly.OptimizableBinding; import org.apache.tuscany.sca.core.assembly.EndpointReferenceImpl; import org.apache.tuscany.sca.core.assembly.RuntimeComponentReferenceImpl; import org.apache.tuscany.sca.core.assembly.RuntimeWireImpl; import org.apache.tuscany.sca.core.context.CallableReferenceImpl; import org.apache.tuscany.sca.core.context.ComponentContextHelper; import org.apache.tuscany.sca.interfacedef.InterfaceContract; import org.apache.tuscany.sca.interfacedef.java.JavaInterface; import org.apache.tuscany.sca.invocation.Message; import org.apache.tuscany.sca.runtime.EndpointReference; import org.apache.tuscany.sca.runtime.RuntimeComponent; import org.apache.tuscany.sca.runtime.RuntimeComponentReference; import org.apache.tuscany.sca.runtime.RuntimeComponentService; import org.apache.tuscany.sca.runtime.RuntimeWire; /** * Returns proxy instance for a wire callback * * @version $Rev: 576055 $ $Date: 2007-09-16 08:11:45 +0100 (Sun, 16 Sep 2007) $ */ public class CallbackReferenceImpl extends CallableReferenceImpl { private RuntimeWire wire; private List wires; private EndpointReference resolvedEndpoint; private Object convID; public static CallbackReferenceImpl newInstance(Class interfaze, ProxyFactory proxyFactory, List wires) { if (getCallbackEndpoint(ThreadMessageContext.getMessageContext()) != null) { return new CallbackReferenceImpl(interfaze, proxyFactory, wires); } else { return null; } } /** * Public constructor for Externalizable serialization/deserialization. */ public CallbackReferenceImpl() { super(); } private CallbackReferenceImpl(Class interfaze, ProxyFactory proxyFactory, List wires) { super(interfaze, null, proxyFactory); this.wires = wires; init(); } public void init() { Message msgContext = ThreadMessageContext.getMessageContext(); wire = selectCallbackWire(msgContext); if (wire == null) { //FIXME: need better exception throw new RuntimeException("No callback binding found for " + msgContext.getTo().getURI()); } resolvedEndpoint = getCallbackEndpoint(msgContext); convID = msgContext.getFrom().getReferenceParameters().getConversationID(); callbackID = msgContext.getFrom().getReferenceParameters().getCallbackID(); } @Override protected Object createProxy() throws Exception { return proxyFactory.createCallbackProxy(this); } protected RuntimeWire getCallbackWire() { if (resolvedEndpoint == null) { return null; } else { return cloneAndBind(wire); } } protected Object getConvID() { return convID; } protected EndpointReference getResolvedEndpoint() { return resolvedEndpoint; } private RuntimeWire selectCallbackWire(Message msgContext) { // look for callback binding with same name as service binding EndpointReference to = msgContext.getTo(); if (to == null) { //FIXME: need better exception throw new RuntimeException("Destination for forward call is not available"); } for (RuntimeWire wire : wires) { if (wire.getSource().getBinding().getName().equals(to.getBinding().getName())) { return wire; } } // if no match, look for callback binding with same type as service binding for (RuntimeWire wire : wires) { if (wire.getSource().getBinding().getClass() == to.getBinding().getClass()) { return wire; } } // no suitable callback wire was found return null; } /** * @param msgContext */ private static EndpointReference getCallbackEndpoint(Message msgContext) { EndpointReference from = msgContext.getFrom(); if (from == null) { return null; } return from.getReferenceParameters().getCallbackReference(); } private RuntimeWire cloneAndBind(RuntimeWire wire) { RuntimeWire boundWire = null; if (resolvedEndpoint != null) { boundWire = ((RuntimeWireImpl)wire).lookupCache(resolvedEndpoint); if (boundWire != null) { return boundWire; } try { Contract contract = resolvedEndpoint.getContract(); RuntimeComponentReference ref = null; if (contract == null) { boundWire = (RuntimeWire)wire.clone(); } else if (contract instanceof RuntimeComponentReference) { ref = (RuntimeComponentReference)contract; boundWire = ref.getRuntimeWire(resolvedEndpoint.getBinding()); } else { // contract instanceof RuntimeComponentService ref = bind((RuntimeComponentReference)wire.getSource().getContract(), resolvedEndpoint.getComponent(), (RuntimeComponentService)contract); boundWire = ref.getRuntimeWires().get(0); } configureWire(boundWire); ((RuntimeWireImpl)wire).addToCache(resolvedEndpoint, boundWire); } catch (CloneNotSupportedException e) { // will not happen } } return boundWire; } private static RuntimeComponentReference bind(RuntimeComponentReference reference, RuntimeComponent component, RuntimeComponentService service) throws CloneNotSupportedException { RuntimeComponentReference ref = (RuntimeComponentReference)reference.clone(); ref.getTargets().add(service); ref.getBindings().clear(); for (Binding binding : service.getBindings()) { if (binding instanceof OptimizableBinding) { OptimizableBinding optimizableBinding = (OptimizableBinding)((OptimizableBinding)binding).clone(); optimizableBinding.setTargetBinding(binding); optimizableBinding.setTargetComponent(component); optimizableBinding.setTargetComponentService(service); ref.getBindings().add(optimizableBinding); } else { ref.getBindings().add(binding); } } return ref; } private void configureWire(RuntimeWire wire) { // need to set the endpoint on the binding also so that when the chains are created next // the sca binding can decide whether to provide local or remote invokers. // TODO - there is a problem here though in that I'm setting a target on a // binding that may possibly be trying to point at two things in the multi threaded // case. Need to confirm the general model here and how the clone and bind part // is intended to work Binding binding = wire.getSource().getBinding(); binding.setURI(resolvedEndpoint.getURI()); // also need to set the target contract as it varies for the sca binding depending on // whether it is local or remote RuntimeComponentReference ref = (RuntimeComponentReference)wire.getSource().getContract(); wire.getTarget().setInterfaceContract(ref.getBindingProvider(binding).getBindingInterfaceContract()); } /** * {@inheritDoc} */ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); this.callbackID = in.readObject(); this.convID = in.readObject(); this.compositeActivator = ComponentContextHelper.getCurrentCompositeActivator(); // Get the target Component and Service from the URI final String uri = in.readUTF(); final Component targetComponent = super.resolveComponentURI(uri); final ComponentService targetService = super.resolveServiceURI(uri, targetComponent); final InterfaceContract targetServiceIfaceContract = targetService.getInterfaceContract(); // Re-create the resolved Endpoint this.resolvedEndpoint = new EndpointReferenceImpl( (RuntimeComponent) targetComponent, targetService, null, targetServiceIfaceContract); // Copy the Java Interface from the Service final JavaInterface ji = (JavaInterface) targetServiceIfaceContract.getInterface(); this.businessInterface = (Class) ji.getJavaClass(); // We need to re-create the callback wire. We need to do this on a clone of the Service // wire since we need to change some details on it. // FIXME: Is this the best way to do this? final RuntimeWire cbWire = ((RuntimeComponentService) targetService).getRuntimeWires().get(0); try { this.wire = (RuntimeWireImpl) cbWire.clone(); } catch (CloneNotSupportedException e) { throw new IOException(e.toString()); } // Setup the reference on the cloned wire final RuntimeComponentReference ref = new RuntimeComponentReferenceImpl(); ref.setComponent((RuntimeComponent) targetComponent); ref.setInterfaceContract(targetServiceIfaceContract); ((EndpointReferenceImpl) this.wire.getSource()).setContract(ref); } /** * {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(this.callbackID); out.writeObject(this.convID); out.writeUTF(this.resolvedEndpoint.getURI()); } }