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 2019/10/11 13:38:27 UTC

[lucene-solr] 02/02: SOLR-13787: Support for Payload as 3rd param

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

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

commit 71e9564e0d520449b6eeb52a6f67ede91ff091a7
Author: noble <no...@apache.org>
AuthorDate: Sat Oct 12 00:38:06 2019 +1100

    SOLR-13787: Support for Payload<T> as 3rd param
---
 .../src/java/org/apache/solr/api/AnnotatedApi.java | 59 +++++++++++++++++-----
 .../core/src/java/org/apache/solr/api/Command.java |  2 -
 .../solr/api/{Command.java => PayloadObj.java}     | 26 +++++-----
 .../org/apache/solr/util/ReflectMapWriter.java     | 58 +++++++++++++++++++++
 .../solr/handler/admin/TestApiFramework.java       |  4 +-
 5 files changed, 116 insertions(+), 33 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 b1be461..964af85 100644
--- a/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
+++ b/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
@@ -45,14 +45,14 @@ import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.security.AuthorizationContext;
 import org.apache.solr.security.PermissionNameProvider;
 
-/**This class implements an Api just from  an annotated java class
+/**
+ * This class implements an Api just from  an annotated java class
  * The class must have an annotation {@link EndPoint}
  * Each method must have an annotation {@link Command}
  * The methods that implement a command should have the first 2 parameters
  * {@link SolrQueryRequest} and {@link SolrQueryResponse} or it may optionally
  * have a third parameter which could be a java class annotated with jackson annotations.
  * The third parameter is only valid if it is using a json command payload
- *
  */
 
 public class AnnotatedApi extends Api implements PermissionNameProvider {
@@ -62,7 +62,6 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
 
   public AnnotatedApi(Object obj) {
     this(obj, null);
-
   }
 
   public AnnotatedApi(Object obj, Api fallback) {
@@ -94,21 +93,21 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
 
   private static SpecProvider readSpec(Class klas) {
     EndPoint endPoint = (EndPoint) klas.getAnnotation(EndPoint.class);
-    if (endPoint == null) throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid class :  "+ klas.getName());
-    EndPoint endPoint1 = (EndPoint) klas.getAnnotation(EndPoint.class);
+    if (endPoint == null)
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid class :  " + klas.getName());
     return () -> {
       Map map = new LinkedHashMap();
       List<String> methods = new ArrayList<>();
-      for (SolrRequest.METHOD method : endPoint1.method()) {
+      for (SolrRequest.METHOD method : endPoint.method()) {
         methods.add(method.name());
       }
       map.put("methods", methods);
-      map.put("url", new ValidatingJsonMap(Collections.singletonMap("paths", Arrays.asList(endPoint1.path()))));
+      map.put("url", new ValidatingJsonMap(Collections.singletonMap("paths", Arrays.asList(endPoint.path()))));
       Map<String, Object> cmds = new HashMap<>();
 
       for (Method method : klas.getMethods()) {
         Command command = method.getAnnotation(Command.class);
-        if (command != null && !command.name().isBlank()) {
+        if (command != null && !command.name().isEmpty()) {
           cmds.put(command.name(), AnnotatedApi.createSchema(method));
         }
       }
@@ -132,7 +131,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
       }
     }
 
-    List<CommandOperation> cmds = req.getCommands(true);
+    List<CommandOperation> cmds = req.getCommands(false);
     boolean allExists = true;
     for (CommandOperation cmd : cmds) {
       if (!commands.containsKey(cmd.name)) {
@@ -168,6 +167,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
     ObjectMapper mapper = new ObjectMapper();
     int paramsCount;
     Class c;
+    boolean isWrappedInPayloadObj = false;
 
 
     Cmd(Command command, Object obj, Method method) {
@@ -181,7 +181,23 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
           throw new RuntimeException("Invalid params for method " + method);
         }
         if (parameterTypes.length == 3) {
-          c = parameterTypes[2];
+          Type t = method.getGenericParameterTypes()[2];
+          if (t instanceof ParameterizedType) {
+            ParameterizedType typ = (ParameterizedType) t;
+            if (typ.getRawType() == PayloadObj.class) {
+              isWrappedInPayloadObj = true;
+              Type t1 = typ.getActualTypeArguments()[0];
+              if (t1 instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType) t1;
+                c = (Class) parameterizedType.getRawType();
+              } else {
+                c = (Class) typ.getActualTypeArguments()[0];
+              }
+            }
+          } else {
+            c = (Class) t;
+          }
+
         }
         if (parameterTypes.length > 3) {
           throw new RuntimeException("Invalid params count for method " + method);
@@ -195,7 +211,6 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
 
     void invoke(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation cmd) {
       try {
-
         if (paramsCount == 2) {
           method.invoke(obj, req, rsp);
         } else {
@@ -203,14 +218,26 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
           if (o instanceof Map && c != null) {
             o = mapper.readValue(Utils.toJSONString(o), c);
           }
-          method.invoke(obj, req, rsp, o);
+          if (isWrappedInPayloadObj) {
+            PayloadObj<Object> payloadObj = new PayloadObj<>(cmd.name, cmd.getCommandData(), o);
+            cmd = payloadObj;
+            method.invoke(obj, req, rsp, payloadObj);
+          } else {
+            method.invoke(obj, req, rsp, o);
+          }
+          if (cmd.hasError()) {
+            throw new ApiBag.ExceptionWithErrObject(SolrException.ErrorCode.BAD_REQUEST, "Error executing command",
+                CommandOperation.captureErrors(Collections.singletonList(cmd)));
+          }
         }
 
+
       } catch (SolrException se) {
         throw se;
       } catch (InvocationTargetException ite) {
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, ite.getCause());
       } catch (Exception e) {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
       }
 
     }
@@ -243,11 +270,15 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
 
   private static Map<String, Object> createSchemaFromType(Type t) {
     Map<String, Object> map = new LinkedHashMap<>();
+    if (t instanceof ParameterizedType) {
+      ParameterizedType typ = (ParameterizedType) t;
+      if (typ.getRawType() == PayloadObj.class) {
+        t = typ.getActualTypeArguments()[0];
+      }
+    }
 
     if (primitives.containsKey(t)) {
       map.put("type", primitives.get(t));
-    } else if (t == List.class) {
-
     } else if (t instanceof ParameterizedType && ((ParameterizedType) t).getRawType() == List.class) {
       Type typ = ((ParameterizedType) t).getActualTypeArguments()[0];
       map.put("type", "array");
diff --git a/solr/core/src/java/org/apache/solr/api/Command.java b/solr/core/src/java/org/apache/solr/api/Command.java
index d18d064..25de077 100644
--- a/solr/core/src/java/org/apache/solr/api/Command.java
+++ b/solr/core/src/java/org/apache/solr/api/Command.java
@@ -32,6 +32,4 @@ public @interface Command {
    */
   String name() default "";
 
-  String jsonSchema() default "";
-
 }
diff --git a/solr/core/src/java/org/apache/solr/api/Command.java b/solr/core/src/java/org/apache/solr/api/PayloadObj.java
similarity index 64%
copy from solr/core/src/java/org/apache/solr/api/Command.java
copy to solr/core/src/java/org/apache/solr/api/PayloadObj.java
index d18d064..c09c442 100644
--- a/solr/core/src/java/org/apache/solr/api/Command.java
+++ b/solr/core/src/java/org/apache/solr/api/PayloadObj.java
@@ -17,21 +17,19 @@
 
 package org.apache.solr.api;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.apache.solr.common.util.CommandOperation;
 
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface Command {
-  /**if this is not a json command , leave it empty.
-   * Keep in mind that you cannot have duplicates.
-   * Only one method per name
-   *
-   */
-  String name() default "";
+public  class PayloadObj<T> extends CommandOperation {
 
-  String jsonSchema() default "";
+  private T obj;
 
+
+  public PayloadObj(String operationName, Object metaData, T obj) {
+    super(operationName, metaData);
+    this.obj = obj;
+  }
+
+  public T get(){
+    return obj;
+  }
 }
diff --git a/solr/core/src/java/org/apache/solr/util/ReflectMapWriter.java b/solr/core/src/java/org/apache/solr/util/ReflectMapWriter.java
new file mode 100644
index 0000000..9555740
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/util/ReflectMapWriter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.solr.util;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.solr.common.MapWriter;
+
+public interface ReflectMapWriter extends MapWriter {
+
+  @Override
+  default void writeMap(EntryWriter ew) throws IOException {
+    for (Field field : this.getClass().getDeclaredFields()) {
+      JsonProperty prop = field.getAnnotation(JsonProperty.class);
+      if (prop == null) continue;
+      int modifiers = field.getModifiers();
+      if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
+        String fname = prop.value().isEmpty() ? field.getName() : prop.value();
+        try {
+          if (field.getType() == int.class) {
+            ew.put(fname, field.getInt(this));
+          } else if (field.getType() == float.class) {
+            ew.put(fname, field.getFloat(this));
+          } else if (field.getType() == double.class) {
+            ew.put(fname, field.getDouble(this));
+          } else if (field.getType() == boolean.class) {
+            ew.put(fname, field.getBoolean(this));
+          } else if (field.getType() == long.class) {
+            ew.put(fname, field.getLong(this));
+          } else {
+            ew.putIfNotNull(fname, field.get(this));
+          }
+        } catch (IllegalAccessException e) {
+          //it should not happen
+        }
+      }
+    }
+  }
+
+}
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 933b862..fde0335 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
@@ -168,7 +168,7 @@ public class TestApiFramework extends SolrTestCaseJ4 {
 
   }
 
-  public void testPayload() throws IOException {
+  public void testPayload() {
     String json = "{package:pkg1, version: '0.1', files  :[a.jar, b.jar]}";
     Utils.fromJSONString(json);
 
@@ -213,8 +213,6 @@ public class TestApiFramework extends SolrTestCaseJ4 {
 
     }
 
-
-
   }
 
   public static class AddVersion {