package io.muoncore.spring.repository;
import io.muoncore.Muon;
import io.muoncore.exception.MuonException;
import io.muoncore.api.MuonFuture;
import io.muoncore.protocol.requestresponse.Response;
import io.muoncore.spring.annotations.Request;
import org.springframework.util.StringValueResolver;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class RequestRepositoryMethodHandler implements RepositoryMethodHandler {
public static final String CONFIGURATION_EXCEPTION_TEXT = "Repository method should contain either multiple @Parameter annotated methods, or single object method";
protected final Type returnType;
protected final Muon muon;
protected String muonUrl;
private Method method;
protected boolean keepMuonFuture;
private List<String> parameterNames = new ArrayList<>();
private QueryMethodParameterCombinationType queryMethodParameterCombinationType;
protected int queryTimeout;
protected TimeUnit timeoutUnit;
public RequestRepositoryMethodHandler(Method method, Muon muon, StringValueResolver resolver) {
this.method = method;
this.muon = muon;
initParameterHandlers();
returnType = initReturnType(method);
Request queryAnnotation = method.getAnnotation(Request.class);
this.queryTimeout = queryAnnotation.timeout();
this.timeoutUnit = queryAnnotation.timeUnit();
this.muonUrl = resolver.resolveStringValue(queryAnnotation.value());
}
private Type initReturnType(Method method) {
Type returnType = method.getGenericReturnType();
if (returnType == void.class) {
returnType = Object.class;
}
//TODO Implement MuonFuture pass-through here
/*
if (MuonFuture.class.isAssignableFrom(returnType)) {
keepMuonFuture = true;
returnType = method.getReturnType();
Type returnGenericType = method.getGenericReturnType();
if (returnGenericType instanceof ParameterizedType) {
Type[] returnTypes = ((ParameterizedType) returnGenericType).getActualTypeArguments();
if (returnTypes != null && returnTypes.length > 0) {
returnType = (Class<?>) returnTypes[0];
}
}
}
*/
return returnType;
}
private void initParameterHandlers() {
for (Parameter parameter : method.getParameters()) {
if (parameter.isAnnotationPresent(io.muoncore.spring.annotations.parameterhandlers.Parameter.class)) {
registerParameterName(parameter.getAnnotation(io.muoncore.spring.annotations.parameterhandlers.Parameter.class).value());
} else {
registerObjectQueryType();
}
}
if (queryMethodParameterCombinationType == null) {
queryMethodParameterCombinationType = QueryMethodParameterCombinationType.EMPTY;
}
}
private void registerObjectQueryType() {
if (queryMethodParameterCombinationType != null) {
throw new MuonException(CONFIGURATION_EXCEPTION_TEXT);
}
queryMethodParameterCombinationType = QueryMethodParameterCombinationType.OBJECT;
}
private void registerParameterName(String value) {
if (queryMethodParameterCombinationType != null && queryMethodParameterCombinationType != QueryMethodParameterCombinationType.PARAMETERS) {
throw new MuonException(CONFIGURATION_EXCEPTION_TEXT);
}
queryMethodParameterCombinationType = QueryMethodParameterCombinationType.PARAMETERS;
parameterNames.add(value);
}
@Override
public Object processRepositoryRequest(Object[] args) {
switch (queryMethodParameterCombinationType) {
case PARAMETERS:
return processParametersQuery(args);
case OBJECT:
return processObjectQuery(args);
case EMPTY:
return processEmptyQuery();
}
throw new IllegalStateException("Wrong Query method type");
}
private Object processEmptyQuery() {
return processMuonOperation(null);
}
private Object processMuonOperation(Object payload) {
try {
MuonFuture<Response> future = executeMuonOperation(payload);
if (keepMuonFuture) {
return future;
} else {
return future.get(queryTimeout, timeoutUnit).getPayload(Object.class);
}
} catch (InterruptedException | ExecutionException | TimeoutException | URISyntaxException e) {
throw new MuonException("Error performing query " + muonUrl + "\nParameters: " + payload
, e);
}
}
protected MuonFuture<Response> executeMuonOperation(Object payload) throws URISyntaxException {
return muon.request(muonUrl, payload);
}
private Object processObjectQuery(Object[] args) {
return processMuonOperation(args[0]);
}
private Object processParametersQuery(Object[] args) {
Map<String, Object> parametersMap = new HashMap<>();
for (int i = 0; i < parameterNames.size(); i++) {
parametersMap.put(parameterNames.get(i), args[i]);
}
return processMuonOperation(parametersMap);
}
private enum QueryMethodParameterCombinationType {PARAMETERS, OBJECT, EMPTY}
}