/*****************************************************************************
* Copyright [2013] [Jules White] *
* *
* Licensed 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.magnum.soda.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.engio.mbassy.listener.Listener;
import net.engio.mbassy.listener.Mode;
import org.magnum.soda.MsgBus;
import org.magnum.soda.svc.InvocationInfo;
import org.magnum.soda.svc.InvocationInfoBuilder;
import org.magnum.soda.svc.ObjInvocationMsg;
import org.magnum.soda.svc.ObjInvocationMsgBuilder;
import org.magnum.soda.svc.ObjInvocationRespMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe;
public class ObjProxy implements InvocationHandler {
private static final Logger Log = LoggerFactory.getLogger(ObjProxy.class);
public class ResponseCatcher {
private String respId_;
private MsgBus msgBus_;
private ObjInvocationRespMsg response_;
private Object condition_ = new Object();
public ResponseCatcher(MsgBus bus, String respId) {
super();
respId_ = respId;
msgBus_ = bus;
msgBus_.subscribe(this);
}
@Subscribe
@Listener(delivery=Mode.Concurrent)
public void handleResponse(ObjInvocationRespMsg resp) {
if (respId_.equals(resp.getResponseTo())) {
response_ = resp;
synchronized (condition_) {
condition_.notify();
}
}
}
public ObjInvocationRespMsg getResponse() {
if (response_ == null) {
synchronized (condition_) {
try {
if (response_ == null) {
condition_.wait();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return response_;
}
}
private MsgBus msgBus_;
private ProxyFactory factory_;
private ObjRef objectRef_;
private InvocationSettings invocationSettings_;
public ObjProxy(ProxyFactory fact, InvocationSettings settings, MsgBus msgBus, ObjRef objid) {
super();
invocationSettings_ = settings;
objectRef_ = objid;
msgBus_ = msgBus;
factory_ = fact;
}
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
if(arg1.getName().equals("equals")){
Object to = arg2[0];
if(to != null && Proxy.isProxyClass(to.getClass())){
Object i = Proxy.getInvocationHandler(to);
if(i instanceof ObjProxy){
return objectRef_.equals(((ObjProxy)i).objectRef_);
}
}
}
else if(arg1.getName().equals("hashCode")){
return hashCode();
}
else if(arg1.getName().equals("toString")){
return toString();
}
Object[] args = factory_.convertToObjectRefsIfNeeded(arg2);
InvocationInfo inv = InvocationInfoBuilder.invocationInfo()
.withMethod(arg1.getName())
.withParameterTypes(arg1.getParameterTypes())
.withParameters(args).build();
ObjInvocationMsg msg = ObjInvocationMsgBuilder.objInvocationMsg()
.withInvocation(inv).withTargetObjectId(objectRef_)
.withDestination(objectRef_.getHost())
.build();
Object rslt = null;
if(invocationSettings_.shouldInvokeAsync(arg0, arg1, arg2)){
invokeAsync(msg);
}
else {
Class<?> type = arg1.getReturnType();
SodaInferReturnTypeFromArgument anno = arg1.getAnnotation(SodaInferReturnTypeFromArgument.class);
if(anno != null){
type = (Class<?>)arg2[anno.index()];
}
rslt = invokeSync(msg, msg.getId(), type);
}
return rslt;
}
private void invokeAsync(ObjInvocationMsg msg){
msgBus_.publish(msg);
}
private Object invokeSync(ObjInvocationMsg msg, String respid, Class<?> returntype)
throws Throwable {
ResponseCatcher catcher = new ResponseCatcher(msgBus_, msg.getId());
msgBus_.publish(msg);
ObjInvocationRespMsg resp = catcher.getResponse();
Log.debug("Recv'd response to invocation: [{}] response: [{}]",msg,resp);
if (resp.getException() != null) {
throw resp.getException();
}
resp.bindResultType(returntype);
Object returnval = resp.getResult();
returnval = factory_.createProxiesFromRefsIfNeeded(returnval);
return returnval;
}
@Override
public String toString() {
return "ObjProxy [objectRef_=" + objectRef_ + "]";
}
@Override
public int hashCode() {
return objectRef_.hashCode();
}
public ObjRef getObjectRef() {
return objectRef_;
}
public void setObjectRef(ObjRef objectRef) {
objectRef_ = objectRef;
}
}