You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by te...@apache.org on 2020/08/21 06:52:28 UTC

[shardingsphere-elasticjob] branch restful-api updated: Improve flexibility of retrieving parameters. (#1392)

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

technoboy pushed a commit to branch restful-api
in repository https://gitbox.apache.org/repos/asf/shardingsphere-elasticjob.git


The following commit(s) were added to refs/heads/restful-api by this push:
     new a863437  Improve flexibility of retrieving parameters. (#1392)
a863437 is described below

commit a8634373bb8a641e5befd6cf9070fb8949efb568
Author: 吴伟杰 <ro...@me.com>
AuthorDate: Fri Aug 21 14:52:20 2020 +0800

    Improve flexibility of retrieving parameters. (#1392)
---
 elasticjob-infra/elasticjob-restful/README.md      |   2 +-
 .../elasticjob/restful/NettyRestfulService.java    |   1 -
 .../elasticjob/restful/annotation/RequestBody.java |   7 ++
 .../restful/handler/ExceptionHandleResult.java     |   7 +-
 .../elasticjob/restful/handler/Handler.java        |   9 +-
 .../restful/handler/HandlerParameter.java          |   2 +
 .../restful/pipeline/HandleMethodExecutor.java     |  13 ++-
 .../restful/pipeline/HandlerParameterDecoder.java  |  37 +++++--
 .../restful/wrapper/QueryParameterMap.java         | 106 +++++++++++++++++++++
 .../restful/wrapper/QueryParameterMapTest.java     |  62 ++++++++++++
 10 files changed, 224 insertions(+), 22 deletions(-)

diff --git a/elasticjob-infra/elasticjob-restful/README.md b/elasticjob-infra/elasticjob-restful/README.md
index c203953..5c77fa7 100644
--- a/elasticjob-infra/elasticjob-restful/README.md
+++ b/elasticjob-infra/elasticjob-restful/README.md
@@ -7,7 +7,7 @@
 @ContextPath("/job")
 public class JobController implements RestfulController {
     
-    @Mapping(method = Http.POST, pattern = "/{group}/{jobName}")
+    @Mapping(method = Http.POST, path = "/{group}/{jobName}")
     public JobPojo createJob(@Param(name = "group", source = ParamSource.PATH) final String group,
                              @Param(name = "jobName", source = ParamSource.PATH) final String jobName,
                              @Param(name = "cron", source = ParamSource.QUERY) final String cron,
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/NettyRestfulService.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/NettyRestfulService.java
index 6ea0362..6eca44a 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/NettyRestfulService.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/NettyRestfulService.java
@@ -50,7 +50,6 @@ public final class NettyRestfulService implements RestfulService {
     private void initServerBootstrap() {
         bossEventLoopGroup = new NioEventLoopGroup();
         workerEventLoopGroup = new NioEventLoopGroup(DEFAULT_WORKER_GROUP_THREADS);
-        
         serverBootstrap = new ServerBootstrap()
                 .group(bossEventLoopGroup, workerEventLoopGroup)
                 .channel(NioServerSocketChannel.class)
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/annotation/RequestBody.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/annotation/RequestBody.java
index 20b41f5..45089c1 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/annotation/RequestBody.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/annotation/RequestBody.java
@@ -28,4 +28,11 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.PARAMETER)
 public @interface RequestBody {
+    
+    /**
+     * If request body is required.
+     *
+     * @return Required
+     */
+    boolean required() default true;
 }
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/ExceptionHandleResult.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/ExceptionHandleResult.java
index 722d3a8..2b6e8c6 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/ExceptionHandleResult.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/ExceptionHandleResult.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.elasticjob.restful.handler;
 
 import lombok.Builder;
 import lombok.Getter;
+import org.apache.shardingsphere.elasticjob.restful.Http;
 
 @Builder
 @Getter
@@ -26,7 +27,9 @@ public final class ExceptionHandleResult {
 
     private final Object result;
     
-    private final int statusCode;
+    @Builder.Default
+    private final int statusCode = 500;
     
-    private final String contentType;
+    @Builder.Default
+    private final String contentType = Http.DEFAULT_CONTENT_TYPE;
 }
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/Handler.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/Handler.java
index 790e596..32b7b15 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/Handler.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/Handler.java
@@ -84,12 +84,13 @@ public final class Handler {
             Parameter parameter = parameters[i];
             Param annotation = parameter.getAnnotation(Param.class);
             HandlerParameter handlerParameter;
+            RequestBody requestBody;
             if (null != annotation) {
-                handlerParameter = new HandlerParameter(i, parameter.getType(), annotation.source(), annotation.name());
-            } else if (null != parameter.getAnnotation(RequestBody.class)) {
-                handlerParameter = new HandlerParameter(i, parameter.getType(), ParamSource.BODY, parameter.getName());
+                handlerParameter = new HandlerParameter(i, parameter.getType(), annotation.source(), annotation.name(), annotation.required());
+            } else if (null != (requestBody = parameter.getAnnotation(RequestBody.class))) {
+                handlerParameter = new HandlerParameter(i, parameter.getType(), ParamSource.BODY, parameter.getName(), requestBody.required());
             } else {
-                handlerParameter = new HandlerParameter(i, parameter.getType(), ParamSource.UNKNOWN, parameter.getName());
+                handlerParameter = new HandlerParameter(i, parameter.getType(), ParamSource.UNKNOWN, parameter.getName(), false);
             }
             params.add(handlerParameter);
         }
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/HandlerParameter.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/HandlerParameter.java
index 8a08ecc..70cd2c5 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/HandlerParameter.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/handler/HandlerParameter.java
@@ -35,4 +35,6 @@ public final class HandlerParameter {
     private final ParamSource paramSource;
     
     private final String name;
+    
+    private final boolean required;
 }
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandleMethodExecutor.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandleMethodExecutor.java
index 2dcfa92..1b5c3fb 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandleMethodExecutor.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandleMethodExecutor.java
@@ -49,10 +49,15 @@ public final class HandleMethodExecutor extends ChannelInboundHandlerAdapter {
         
         Object handleResult = handler.execute(args);
         
-        String mimeType = HttpUtil.getMimeType(handler.getProducing()).toString();
-        ResponseBodySerializer serializer = ResponseBodySerializerFactory.getResponseBodySerializer(mimeType);
-        byte[] bodyBytes = serializer.serialize(handleResult);
-        FullHttpResponse response = createHttpResponse(handler.getProducing(), bodyBytes, handler.getHttpStatusCode());
+        FullHttpResponse response;
+        if (null != handleResult) {
+            String mimeType = HttpUtil.getMimeType(handler.getProducing()).toString();
+            ResponseBodySerializer serializer = ResponseBodySerializerFactory.getResponseBodySerializer(mimeType);
+            byte[] bodyBytes = serializer.serialize(handleResult);
+            response = createHttpResponse(handler.getProducing(), bodyBytes, handler.getHttpStatusCode());
+        } else {
+            response = createHttpResponse(handler.getProducing(), new byte[0], handler.getHttpStatusCode());
+        }
         ctx.writeAndFlush(response);
     }
     
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandlerParameterDecoder.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandlerParameterDecoder.java
index 736f198..1fd80f7 100644
--- a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandlerParameterDecoder.java
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/pipeline/HandlerParameterDecoder.java
@@ -27,15 +27,16 @@ import io.netty.handler.codec.http.FullHttpRequest;
 import io.netty.handler.codec.http.HttpUtil;
 import io.netty.handler.codec.http.QueryStringDecoder;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shardingsphere.elasticjob.restful.Http;
+import org.apache.shardingsphere.elasticjob.restful.deserializer.RequestBodyDeserializer;
+import org.apache.shardingsphere.elasticjob.restful.deserializer.RequestBodyDeserializerFactory;
 import org.apache.shardingsphere.elasticjob.restful.handler.HandleContext;
 import org.apache.shardingsphere.elasticjob.restful.handler.Handler;
 import org.apache.shardingsphere.elasticjob.restful.handler.HandlerParameter;
-import org.apache.shardingsphere.elasticjob.restful.Http;
 import org.apache.shardingsphere.elasticjob.restful.mapping.MappingContext;
 import org.apache.shardingsphere.elasticjob.restful.mapping.PathMatcher;
 import org.apache.shardingsphere.elasticjob.restful.mapping.RegexPathMatcher;
-import org.apache.shardingsphere.elasticjob.restful.deserializer.RequestBodyDeserializer;
-import org.apache.shardingsphere.elasticjob.restful.deserializer.RequestBodyDeserializerFactory;
+import org.apache.shardingsphere.elasticjob.restful.wrapper.QueryParameterMap;
 
 import java.text.MessageFormat;
 import java.util.List;
@@ -73,17 +74,27 @@ public final class HandlerParameterDecoder extends ChannelInboundHandlerAdapter
         for (int i = 0; i < handlerParameters.size(); i++) {
             HandlerParameter handlerParameter = handlerParameters.get(i);
             Object parsedValue = null;
+            String parameterName = handlerParameter.getName();
+            Class<?> targetType = handlerParameter.getType();
+            boolean nullable = !handlerParameter.isRequired();
             switch (handlerParameter.getParamSource()) {
                 case PATH:
-                    parsedValue = deserializeBuiltInType(handlerParameter.getType(), templateVariables.get(handlerParameter.getName()));
+                    String rawPathValue = templateVariables.get(parameterName);
+                    Object parsedPathValue = deserializeBuiltInType(targetType, rawPathValue);
+                    Preconditions.checkArgument(nullable || null != parsedPathValue, "Missing path variable [%s].", parameterName);
+                    parsedValue = parsedPathValue;
                     break;
                 case QUERY:
-                    List<String> queryValues = queryParameters.get(handlerParameter.getName());
-                    parsedValue = deserializeQueryParameter(handlerParameter.getType(), queryValues);
+                    List<String> rawQueryValues = queryParameters.get(parameterName);
+                    Object parsedQueryValue = deserializeQueryParameter(targetType, rawQueryValues);
+                    Preconditions.checkArgument(nullable || null != parsedQueryValue, "Missing query parameter [%s].", parameterName);
+                    parsedValue = parsedQueryValue;
                     break;
                 case HEADER:
-                    String headerValue = httpRequest.headers().get(handlerParameter.getName());
-                    parsedValue = deserializeBuiltInType(handlerParameter.getType(), headerValue);
+                    String rawHeaderValue = httpRequest.headers().get(parameterName);
+                    Object parsedHeaderValue = deserializeBuiltInType(targetType, rawHeaderValue);
+                    Preconditions.checkArgument(nullable || null != parsedHeaderValue, "Missing header value [%s].", parameterName);
+                    parsedValue = parsedHeaderValue;
                     break;
                 case BODY:
                     Preconditions.checkState(!requestBodyAlreadyParsed, "@RequestBody duplicated on handle method.");
@@ -94,11 +105,17 @@ public final class HandlerParameterDecoder extends ChannelInboundHandlerAdapter
                     if (null == deserializer) {
                         throw new UnsupportedMessageTypeException(MessageFormat.format("Unsupported MIME type [{0}]", mimeType));
                     }
-                    parsedValue = deserializer.deserialize(handlerParameter.getType(), bytes);
+                    Object parsedBodyValue = deserializer.deserialize(targetType, bytes);
+                    parsedValue = parsedBodyValue;
+                    Preconditions.checkArgument(nullable || null != parsedBodyValue, "Missing request body");
                     requestBodyAlreadyParsed = true;
                     break;
                 case UNKNOWN:
-                    log.warn("Unknown source argument [{}] on index [{}].", handlerParameter.getName(), handlerParameter.getIndex());
+                    if (QueryParameterMap.class.isAssignableFrom(targetType)) {
+                        parsedValue = new QueryParameterMap(queryParameters);
+                    } else {
+                        log.warn("Unknown source argument [{}] on index [{}].", parameterName, handlerParameter.getIndex());
+                    }
                     break;
                 default:
             }
diff --git a/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/wrapper/QueryParameterMap.java b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/wrapper/QueryParameterMap.java
new file mode 100644
index 0000000..f78b2d4
--- /dev/null
+++ b/elasticjob-infra/elasticjob-restful/src/main/java/org/apache/shardingsphere/elasticjob/restful/wrapper/QueryParameterMap.java
@@ -0,0 +1,106 @@
+/*
+ * 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.shardingsphere.elasticjob.restful.wrapper;
+
+import java.util.AbstractMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Wrap a multi-value map. Helps handle method receiving all query parameters.
+ */
+public final class QueryParameterMap extends AbstractMap<String, List<String>> {
+    
+    private final Map<String, List<String>> queryMap;
+    
+    public QueryParameterMap() {
+        queryMap = new LinkedHashMap<>();
+    }
+    
+    public QueryParameterMap(final Map<String, List<String>> map) {
+        queryMap = new LinkedHashMap<>(map);
+    }
+    
+    /**
+     * Get values by parameter name.
+     *
+     * @param parameterName Parameter name
+     * @return Values
+     */
+    public List<String> get(final String parameterName) {
+        return queryMap.get(parameterName);
+    }
+    
+    /**
+     * Get the first from values.
+     *
+     * @param parameterName Parameter name
+     * @return The first value
+     */
+    public String getFirst(final String parameterName) {
+        String firstValue = null;
+        List<String> values = queryMap.get(parameterName);
+        if (values != null && !values.isEmpty()) {
+            firstValue = values.get(0);
+        }
+        return firstValue;
+    }
+    
+    @Override
+    public boolean isEmpty() {
+        return queryMap.isEmpty();
+    }
+    
+    @Override
+    public Set<Entry<String, List<String>>> entrySet() {
+        return queryMap.entrySet();
+    }
+    
+    /**
+     * Add a value.
+     *
+     * @param parameterName Parameter name
+     * @param value         Value
+     */
+    public void add(final String parameterName, final String value) {
+        List<String> values = queryMap.get(parameterName);
+        if (null == values) {
+            values = new LinkedList<>();
+        }
+        values.add(value);
+        put(parameterName, values);
+    }
+    
+    @Override
+    public List<String> put(final String parameterName, final List<String> value) {
+        return queryMap.put(parameterName, value);
+    }
+    
+    /**
+     * Convert to a single value map, abandon values except the first of each parameter.
+     *
+     * @return Single value map
+     */
+    public Map<String, String> toSingleValueMap() {
+        return queryMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get(0)));
+    }
+}
diff --git a/elasticjob-infra/elasticjob-restful/src/test/java/org/apache/shardingsphere/elasticjob/restful/wrapper/QueryParameterMapTest.java b/elasticjob-infra/elasticjob-restful/src/test/java/org/apache/shardingsphere/elasticjob/restful/wrapper/QueryParameterMapTest.java
new file mode 100644
index 0000000..3ba2a19
--- /dev/null
+++ b/elasticjob-infra/elasticjob-restful/src/test/java/org/apache/shardingsphere/elasticjob/restful/wrapper/QueryParameterMapTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.shardingsphere.elasticjob.restful.wrapper;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+public class QueryParameterMapTest {
+    
+    @Test
+    public void assertGetFirst() {
+        QueryParameterMap queryParameterMap = new QueryParameterMap();
+        queryParameterMap.add("name", "foo");
+        assertThat(queryParameterMap.getFirst("name"), is("foo"));
+        assertFalse(queryParameterMap.isEmpty());
+    }
+    
+    @Test
+    public void assertConvertToSingleValueMap() {
+        Map<String, List<String>> queries = new LinkedHashMap<>(1 << 2);
+        queries.put("foo", new LinkedList<>(Arrays.asList("first_foo", "second_foo")));
+        queries.put("bar", new LinkedList<>(Arrays.asList("first_bar", "second_bar")));
+        QueryParameterMap queryParameterMap = new QueryParameterMap(queries);
+        Map<String, String> singleValueMap = queryParameterMap.toSingleValueMap();
+        assertThat(singleValueMap.get("foo"), is("first_foo"));
+        assertThat(singleValueMap.get("bar"), is("first_bar"));
+    }
+    
+    @Test
+    public void assertGetEntrySet() {
+        QueryParameterMap queryParameterMap = new QueryParameterMap();
+        queryParameterMap.put("foo", new LinkedList<>(Arrays.asList("first_foo", "second_foo")));
+        queryParameterMap.put("bar", new LinkedList<>(Arrays.asList("first_bar", "second_bar")));
+        Set<Map.Entry<String, List<String>>> entrySet = queryParameterMap.entrySet();
+        assertThat(entrySet.size(), is(2));
+    }
+}