package org.deephacks.westty.internal.protobuf;
import com.google.common.base.Strings;
import org.deephacks.westty.protobuf.FailureMessages.Failure;
import org.deephacks.westty.protobuf.ProtobufException;
import org.deephacks.westty.protobuf.ProtobufException.FailureCode;
import org.deephacks.westty.protobuf.VoidMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Alternative
public class ProtobufEndpoints {
private ConcurrentHashMap<Class<?>, Method> methodEndpoints = new ConcurrentHashMap<>();
private ConcurrentHashMap<Method, ProtobufEndpointProxy> proxies = new ConcurrentHashMap<>();
private BeanManager beanManager;
public ProtobufEndpoints(BeanManager beanManager){
this.beanManager = beanManager;
}
public void put(Class<?> cls, Method method){
methodEndpoints.put(cls, method);
}
public ProtobufEndpointProxy get(Object protoMsg) {
Class<?> cls = protoMsg.getClass();
Method method = methodEndpoints.get(cls);
if (method == null) {
return null;
}
ProtobufEndpointProxy proxy = proxies.get(method);
if(proxy != null){
return proxy;
}
Class<?> methodDeclaringClass = method.getDeclaringClass();
Set<Bean<?>> protoBeans = beanManager.getBeans(methodDeclaringClass);
Bean<?> protoBean = beanManager.resolve(protoBeans);
CreationalContext<?> cc = beanManager.createCreationalContext(protoBean);
Object endpoint = beanManager.getReference(protoBean, Object.class, cc);
proxy = new ProtobufEndpointProxy(endpoint, method);
proxies.put(method, proxy);
return proxy;
}
public static class ProtobufEndpointProxy {
private static final Logger log = LoggerFactory.getLogger(ProtobufEndpointProxy.class);
private Object endpoint;
private Method method;
private boolean voidReturnType;
private ProtobufEndpointProxy(Object endpoint, Method method){
this.endpoint = endpoint;
this.method = method;
Class<?> returnType = method.getReturnType();
voidReturnType = returnType.equals(Void.TYPE);
}
public Object invoke(Object protoMsg){
Object res = null;
try {
res = method.invoke(endpoint, protoMsg);
} catch (InvocationTargetException e) {
Throwable ex = e.getCause();
log.debug("", ex);
if (ex instanceof ProtobufException) {
ProtobufException pex = (ProtobufException) ex;
res = Failure.newBuilder().setCode(pex.getCode()).setMsg(pex.getProtobufMessage())
.build();
} else if (ex instanceof IllegalArgumentException) {
res = Failure.newBuilder().setCode(FailureCode.BAD_REQUEST.getCode())
.setMsg(ex.getMessage()).build();
} else if (ex instanceof UnsupportedOperationException) {
res = Failure.newBuilder().setCode(FailureCode.NOT_IMPLEMENTED.getCode())
.setMsg(ex.getMessage()).build();
} else if (ex instanceof IllegalStateException) {
res = Failure.newBuilder().setCode(FailureCode.CONFLICT.getCode())
.setMsg(ex.getMessage()).build();
} else if (ex instanceof Exception) {
String message = Strings.nullToEmpty(ex.getMessage());
res = Failure.newBuilder().setCode(FailureCode.INTERNAL_ERROR.getCode())
.setMsg(message).build();
}
} catch (Exception ex) {
res = Failure.newBuilder().setCode(FailureCode.INTERNAL_ERROR.getCode())
.setMsg(ex.getMessage()).build();
}
// if the proxy returns null we must make sure to deliver
// a response to the client in order to release the callback
if(res == null && !voidReturnType){
res = VoidMessage.Void.newBuilder().build();
}
return res;
}
}
}