SOLR-13787: Added support for PayLoad<T> as 3rd param

This commit is contained in:
noble 2019-10-12 00:23:40 +11:00
parent 8bfd8a55c9
commit 2d32f0b5a6
5 changed files with 19 additions and 139 deletions

View File

@ -45,14 +45,14 @@ import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.AuthorizationContext; import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.PermissionNameProvider; 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} * The class must have an annotation {@link EndPoint}
* Each method must have an annotation {@link Command} * Each method must have an annotation {@link Command}
* The methods that implement a command should have the first 2 parameters * The methods that implement a command should have the first 2 parameters
* {@link SolrQueryRequest} and {@link SolrQueryResponse} or it may optionally * {@link SolrQueryRequest} and {@link SolrQueryResponse} or it may optionally
* have a third parameter which could be a java class annotated with jackson annotations. * 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 * The third parameter is only valid if it is using a json command payload
*
*/ */
public class AnnotatedApi extends Api implements PermissionNameProvider { public class AnnotatedApi extends Api implements PermissionNameProvider {
@ -62,6 +62,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
public AnnotatedApi(Object obj) { public AnnotatedApi(Object obj) {
this(obj, null); this(obj, null);
} }
public AnnotatedApi(Object obj, Api fallback) { public AnnotatedApi(Object obj, Api fallback) {
@ -93,21 +94,21 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
private static SpecProvider readSpec(Class klas) { private static SpecProvider readSpec(Class klas) {
EndPoint endPoint = (EndPoint) klas.getAnnotation(EndPoint.class); EndPoint endPoint = (EndPoint) klas.getAnnotation(EndPoint.class);
if (endPoint == null) if (endPoint == null) throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid class : "+ klas.getName());
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid class : " + klas.getName()); EndPoint endPoint1 = (EndPoint) klas.getAnnotation(EndPoint.class);
return () -> { return () -> {
Map map = new LinkedHashMap(); Map map = new LinkedHashMap();
List<String> methods = new ArrayList<>(); List<String> methods = new ArrayList<>();
for (SolrRequest.METHOD method : endPoint.method()) { for (SolrRequest.METHOD method : endPoint1.method()) {
methods.add(method.name()); methods.add(method.name());
} }
map.put("methods", methods); map.put("methods", methods);
map.put("url", new ValidatingJsonMap(Collections.singletonMap("paths", Arrays.asList(endPoint.path())))); map.put("url", new ValidatingJsonMap(Collections.singletonMap("paths", Arrays.asList(endPoint1.path()))));
Map<String, Object> cmds = new HashMap<>(); Map<String, Object> cmds = new HashMap<>();
for (Method method : klas.getMethods()) { for (Method method : klas.getMethods()) {
Command command = method.getAnnotation(Command.class); Command command = method.getAnnotation(Command.class);
if (command != null && !command.name().isEmpty()) { if (command != null && !command.name().isBlank()) {
cmds.put(command.name(), AnnotatedApi.createSchema(method)); cmds.put(command.name(), AnnotatedApi.createSchema(method));
} }
} }
@ -131,7 +132,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
} }
} }
List<CommandOperation> cmds = req.getCommands(false); List<CommandOperation> cmds = req.getCommands(true);
boolean allExists = true; boolean allExists = true;
for (CommandOperation cmd : cmds) { for (CommandOperation cmd : cmds) {
if (!commands.containsKey(cmd.name)) { if (!commands.containsKey(cmd.name)) {
@ -167,7 +168,6 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
int paramsCount; int paramsCount;
Class c; Class c;
boolean isWrappedInPayloadObj = false;
Cmd(Command command, Object obj, Method method) { Cmd(Command command, Object obj, Method method) {
@ -181,23 +181,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
throw new RuntimeException("Invalid params for method " + method); throw new RuntimeException("Invalid params for method " + method);
} }
if (parameterTypes.length == 3) { if (parameterTypes.length == 3) {
Type t = method.getGenericParameterTypes()[2]; c = parameterTypes[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) { if (parameterTypes.length > 3) {
throw new RuntimeException("Invalid params count for method " + method); throw new RuntimeException("Invalid params count for method " + method);
@ -211,6 +195,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
void invoke(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation cmd) { void invoke(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation cmd) {
try { try {
if (paramsCount == 2) { if (paramsCount == 2) {
method.invoke(obj, req, rsp); method.invoke(obj, req, rsp);
} else { } else {
@ -218,26 +203,14 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
if (o instanceof Map && c != null) { if (o instanceof Map && c != null) {
o = mapper.readValue(Utils.toJSONString(o), c); o = mapper.readValue(Utils.toJSONString(o), c);
} }
if (isWrappedInPayloadObj) { method.invoke(obj, req, rsp, o);
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) { } catch (SolrException se) {
throw se; throw se;
} catch (InvocationTargetException ite) { } catch (InvocationTargetException ite) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, ite.getCause()); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, ite.getCause());
} catch (Exception e) { } catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
} }
} }
@ -270,15 +243,11 @@ public class AnnotatedApi extends Api implements PermissionNameProvider {
private static Map<String, Object> createSchemaFromType(Type t) { private static Map<String, Object> createSchemaFromType(Type t) {
Map<String, Object> map = new LinkedHashMap<>(); 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)) { if (primitives.containsKey(t)) {
map.put("type", primitives.get(t)); map.put("type", primitives.get(t));
} else if (t == List.class) {
} else if (t instanceof ParameterizedType && ((ParameterizedType) t).getRawType() == List.class) { } else if (t instanceof ParameterizedType && ((ParameterizedType) t).getRawType() == List.class) {
Type typ = ((ParameterizedType) t).getActualTypeArguments()[0]; Type typ = ((ParameterizedType) t).getActualTypeArguments()[0];
map.put("type", "array"); map.put("type", "array");

View File

@ -32,4 +32,6 @@ public @interface Command {
*/ */
String name() default ""; String name() default "";
String jsonSchema() default "";
} }

View File

@ -1,35 +0,0 @@
/*
* 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.api;
import org.apache.solr.common.util.CommandOperation;
public class PayloadObj<T> extends CommandOperation {
private T obj;
public PayloadObj(String operationName, Object metaData, T obj) {
super(operationName, metaData);
this.obj = obj;
}
public T get(){
return obj;
}
}

View File

@ -1,58 +0,0 @@
/*
* 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
}
}
}
}
}

View File

@ -168,7 +168,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]}"; String json = "{package:pkg1, version: '0.1', files :[a.jar, b.jar]}";
Utils.fromJSONString(json); Utils.fromJSONString(json);
@ -213,6 +213,8 @@ public class TestApiFramework extends SolrTestCaseJ4 {
} }
} }
public static class AddVersion { public static class AddVersion {