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));
+ }
+}