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

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

View File

@ -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);
}
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");

View File

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

View File

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

@ -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
}
}
}
}
}

View File

@ -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 {