You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2020/06/30 12:40:33 UTC

[lucene-solr] branch master updated: SOLR-14404: use MethodHandles in AnnotatedAPI (#1624)

This is an automated email from the ASF dual-hosted git repository.

noble pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/master by this push:
     new 20c1fdb  SOLR-14404: use MethodHandles in AnnotatedAPI (#1624)
20c1fdb is described below

commit 20c1fdbf7e4573948434cfa82e1f7b0fc4d54b31
Author: Noble Paul <no...@users.noreply.github.com>
AuthorDate: Tue Jun 30 22:40:26 2020 +1000

    SOLR-14404: use MethodHandles in AnnotatedAPI (#1624)
---
 .../src/java/org/apache/solr/api/AnnotatedApi.java | 116 +++++++++++----------
 .../solr/handler/admin/TestApiFramework.java       |  15 ++-
 2 files changed, 73 insertions(+), 58 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java b/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
index b3d65d0..d9548c9 100644
--- a/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
+++ b/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
@@ -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);
+      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);
         }
-      } else {
-        throw new RuntimeException(method.toString() + " is not a public static 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);
       }
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java b/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java
index 5f0a57a..55ce63b 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java
@@ -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)