mirror of https://github.com/apache/lucene.git
SOLR-14404: use MethodHandles in AnnotatedAPI (#1624)
This commit is contained in:
parent
f0764dbb54
commit
20c1fdbf7e
|
@ -20,10 +20,9 @@ package org.apache.solr.api;
|
|||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
|
@ -38,10 +37,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import org.apache.solr.client.solrj.SolrRequest;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.SpecProvider;
|
||||
import org.apache.solr.common.util.CommandOperation;
|
||||
import org.apache.solr.common.util.JsonSchemaCreator;
|
||||
import org.apache.solr.common.util.Utils;
|
||||
import org.apache.solr.common.util.ValidatingJsonMap;
|
||||
import org.apache.solr.common.util.*;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.security.AuthorizationContext;
|
||||
|
@ -87,16 +83,18 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
|||
public static List<Api> getApis(Object obj) {
|
||||
return getApis(obj.getClass(), obj);
|
||||
}
|
||||
public static List<Api> getApis(Class<? extends Object> klas , Object obj) {
|
||||
if (!Modifier.isPublic(klas.getModifiers())) {
|
||||
throw new RuntimeException(klas.getName() + " is not public");
|
||||
public static List<Api> getApis(Class<? extends Object> theClass , Object obj) {
|
||||
Class<?> klas = null;
|
||||
try {
|
||||
klas = MethodHandles.publicLookup().accessClass(theClass);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Method may be non-public/inaccessible", e);
|
||||
}
|
||||
|
||||
if (klas.getAnnotation(EndPoint.class) != null) {
|
||||
EndPoint endPoint = klas.getAnnotation(EndPoint.class);
|
||||
List<Method> methods = new ArrayList<>();
|
||||
Map<String, Cmd> commands = new HashMap<>();
|
||||
for (Method m : klas.getDeclaredMethods()) {
|
||||
for (Method m : klas.getMethods()) {
|
||||
Command command = m.getAnnotation(Command.class);
|
||||
if (command != null) {
|
||||
methods.add(m);
|
||||
|
@ -113,12 +111,9 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
|||
return Collections.singletonList(new AnnotatedApi(specProvider, endPoint, commands, null));
|
||||
} else {
|
||||
List<Api> apis = new ArrayList<>();
|
||||
for (Method m : klas.getDeclaredMethods()) {
|
||||
for (Method m : klas.getMethods()) {
|
||||
EndPoint endPoint = m.getAnnotation(EndPoint.class);
|
||||
if (endPoint == null) continue;
|
||||
if (!Modifier.isPublic(m.getModifiers())) {
|
||||
throw new RuntimeException("Non public method " + m.toGenericString());
|
||||
}
|
||||
Cmd cmd = new Cmd("", obj, m);
|
||||
SpecProvider specProvider = readSpec(endPoint, Collections.singletonList(m));
|
||||
apis.add(new AnnotatedApi(specProvider, endPoint, Collections.singletonMap("", cmd), null));
|
||||
|
@ -212,40 +207,40 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
|||
|
||||
static class Cmd {
|
||||
final String command;
|
||||
final Method method;
|
||||
final MethodHandle method;
|
||||
final Object obj;
|
||||
ObjectMapper mapper = SolrJacksonAnnotationInspector.createObjectMapper();
|
||||
int paramsCount;
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
Class c;
|
||||
Class parameterClass;
|
||||
boolean isWrappedInPayloadObj = false;
|
||||
|
||||
|
||||
Cmd(String command, Object obj, Method method) {
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
this.command = command;
|
||||
this.obj = obj;
|
||||
this.method = method;
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
paramsCount = parameterTypes.length;
|
||||
if (parameterTypes.length == 1) {
|
||||
readPayloadType(method.getGenericParameterTypes()[0]);
|
||||
} else if (parameterTypes.length == 3) {
|
||||
if (parameterTypes[0] != SolrQueryRequest.class || parameterTypes[1] != SolrQueryResponse.class) {
|
||||
throw new RuntimeException("Invalid params for method " + method);
|
||||
}
|
||||
Type t = method.getGenericParameterTypes()[2];
|
||||
readPayloadType(t);
|
||||
}
|
||||
if (parameterTypes.length > 3) {
|
||||
throw new RuntimeException("Invalid params count for method " + method);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException(method.toString() + " is not a public static method");
|
||||
this.command = command;
|
||||
this.obj = obj;
|
||||
try {
|
||||
this.method = MethodHandles.publicLookup().unreflect(method);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to access method, may be not public or accessible ", e);
|
||||
}
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
paramsCount = parameterTypes.length;
|
||||
if (parameterTypes.length == 1) {
|
||||
readPayloadType(method.getGenericParameterTypes()[0]);
|
||||
} else if (parameterTypes.length == 3) {
|
||||
if (parameterTypes[0] != SolrQueryRequest.class || parameterTypes[1] != SolrQueryResponse.class) {
|
||||
throw new RuntimeException("Invalid params for method " + method);
|
||||
}
|
||||
Type t = method.getGenericParameterTypes()[2];
|
||||
readPayloadType(t);
|
||||
}
|
||||
if (parameterTypes.length > 3) {
|
||||
throw new RuntimeException("Invalid params count for method " + method);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void readPayloadType(Type t) {
|
||||
if (t instanceof ParameterizedType) {
|
||||
ParameterizedType typ = (ParameterizedType) t;
|
||||
|
@ -253,19 +248,19 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
|||
isWrappedInPayloadObj = true;
|
||||
if(typ.getActualTypeArguments().length == 0){
|
||||
//this is a raw type
|
||||
c = Map.class;
|
||||
parameterClass = Map.class;
|
||||
return;
|
||||
}
|
||||
Type t1 = typ.getActualTypeArguments()[0];
|
||||
if (t1 instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) t1;
|
||||
c = (Class) parameterizedType.getRawType();
|
||||
parameterClass = (Class) parameterizedType.getRawType();
|
||||
} else {
|
||||
c = (Class) typ.getActualTypeArguments()[0];
|
||||
parameterClass = (Class) typ.getActualTypeArguments()[0];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c = (Class) t;
|
||||
parameterClass = (Class) t;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,21 +268,35 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
|||
@SuppressWarnings({"unchecked"})
|
||||
void invoke(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation cmd) {
|
||||
try {
|
||||
if(paramsCount ==1) {
|
||||
Object o = cmd.getCommandData();
|
||||
if (o instanceof Map && c != null && c != Map.class) {
|
||||
o = mapper.readValue(Utils.toJSONString(o), c);
|
||||
Object o = null;
|
||||
String commandName = null;
|
||||
if(paramsCount == 1) {
|
||||
if(cmd == null) {
|
||||
if(parameterClass != null) {
|
||||
try {
|
||||
ContentStream stream = req.getContentStreams().iterator().next();
|
||||
o = mapper.readValue(stream.getStream(), parameterClass);
|
||||
} catch (IOException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "invalid payload", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
commandName = cmd.name;
|
||||
o = cmd.getCommandData();
|
||||
if (o instanceof Map && parameterClass != null && parameterClass != Map.class) {
|
||||
o = mapper.readValue(Utils.toJSONString(o), parameterClass);
|
||||
}
|
||||
}
|
||||
PayloadObj<Object> payloadObj = new PayloadObj<>(cmd.name, cmd.getCommandData(), o, req, rsp);
|
||||
PayloadObj<Object> payloadObj = new PayloadObj<>(commandName, o, o, req, rsp);
|
||||
cmd = payloadObj;
|
||||
method.invoke(obj, payloadObj);
|
||||
checkForErrorInPayload(cmd);
|
||||
} else if (paramsCount == 2) {
|
||||
method.invoke(obj, req, rsp);
|
||||
} else {
|
||||
Object o = cmd.getCommandData();
|
||||
if (o instanceof Map && c != null) {
|
||||
o = mapper.readValue(Utils.toJSONString(o), c);
|
||||
o = cmd.getCommandData();
|
||||
if (o instanceof Map && parameterClass != null) {
|
||||
o = mapper.readValue(Utils.toJSONString(o), parameterClass);
|
||||
}
|
||||
if (isWrappedInPayloadObj) {
|
||||
PayloadObj<Object> payloadObj = new PayloadObj<>(cmd.name, cmd.getCommandData(), o, req, rsp);
|
||||
|
@ -298,15 +307,10 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
|||
}
|
||||
checkForErrorInPayload(cmd);
|
||||
}
|
||||
|
||||
|
||||
} catch (SolrException se) {
|
||||
} catch (RuntimeException se) {
|
||||
log.error("Error executing command ", se);
|
||||
throw se;
|
||||
} catch (InvocationTargetException ite) {
|
||||
log.error("Error executing command ", ite);
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, ite.getCause());
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
log.error("Error executing command : ", e);
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ public class TestApiFramework extends SolrTestCaseJ4 {
|
|||
|
||||
}
|
||||
|
||||
public void testPayload() {
|
||||
public void testPayload() throws IOException {
|
||||
String json = "{package:pkg1, version: '0.1', files :[a.jar, b.jar]}";
|
||||
Utils.fromJSONString(json);
|
||||
|
||||
|
@ -176,7 +176,18 @@ public class TestApiFramework extends SolrTestCaseJ4 {
|
|||
assertEquals("b.jar", addversion.files.get(1));
|
||||
|
||||
|
||||
apiBag.registerObject(new C());
|
||||
rsp = v2ApiInvoke(apiBag, "/path1", "POST", new ModifiableSolrParams(),
|
||||
new ByteArrayInputStream("{\"package\":\"mypkg\", \"version\": \"1.0\", \"files\" : [\"a.jar\", \"b.jar\"]}".getBytes(UTF_8)));
|
||||
assertEquals("mypkg", rsp.getValues()._getStr("payload/package", null));
|
||||
assertEquals("1.0", rsp.getValues()._getStr("payload/version", null));
|
||||
}
|
||||
|
||||
public static class C {
|
||||
@EndPoint(path = "/path1", method = POST, permission = PermissionNameProvider.Name.ALL)
|
||||
public void m1(PayloadObj<AddVersion> add) {
|
||||
add.getResponse().add("payload",add.get());
|
||||
}
|
||||
}
|
||||
|
||||
@EndPoint(method = POST, path = "/cluster/package", permission = PermissionNameProvider.Name.ALL)
|
||||
|
@ -195,7 +206,7 @@ public class TestApiFramework extends SolrTestCaseJ4 {
|
|||
|
||||
}
|
||||
|
||||
public static class AddVersion {
|
||||
public static class AddVersion implements ReflectMapWriter {
|
||||
@JsonProperty(value = "package", required = true)
|
||||
public String pkg;
|
||||
@JsonProperty(value = "version", required = true)
|
||||
|
|
Loading…
Reference in New Issue