From 4b1c3c6454637564efb3ad18c458306ccf5e8b0f Mon Sep 17 00:00:00 2001 From: mercyblitz Date: Tue, 19 Feb 2019 17:59:46 +0800 Subject: [PATCH] Polish spring-cloud-incubator/spring-cloud-alibaba#348 : @DubboTransported supports RestTemplate (part 2) --- ...BalancedRestTemplateAutoConfiguration.java | 11 +- .../DubboAdapterLoadBalancerInterceptor.java | 33 +++- .../loadbalancer/DubboClientHttpResponse.java | 15 +- .../DubboClientHttpResponseFactory.java | 64 ++++++ .../loadbalancer/DubboHttpOutputMessage.java | 3 +- .../converter/HttpMessageConverterHolder.java | 45 +++++ .../dubbo/metadata/RequestMetadata.java | 44 +++-- .../HttpMessageConverterResolver.java | 186 ++++++++++++++++++ .../metadata/resolver/ParameterResolver.java | 21 +- .../bootstrap/DubboSpringCloudBootstrap.java | 19 +- 10 files changed, 383 insertions(+), 58 deletions(-) create mode 100644 spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java create mode 100644 spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/http/converter/HttpMessageConverterHolder.java create mode 100644 spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/HttpMessageConverterResolver.java diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java index 2a8587d6..6eea3233 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java @@ -16,6 +16,7 @@ */ package org.springframework.cloud.alibaba.dubbo.autoconfigure; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.Autowired; @@ -50,7 +51,7 @@ import java.util.Map; @Configuration @ConditionalOnClass(RestTemplate.class) @AutoConfigureAfter(LoadBalancerAutoConfiguration.class) -public class DubboLoadBalancedRestTemplateAutoConfiguration { +public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClassLoaderAware { private static final Class DUBBO_TRANSPORTED_CLASS = DubboTransported.class; @@ -69,6 +70,8 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration { @Autowired(required = false) private Map restTemplates = Collections.emptyMap(); + private ClassLoader classLoader; + /** * Adapt the {@link RestTemplate} beans that are annotated {@link LoadBalanced @LoadBalanced} and * {@link LoadBalanced @LoadBalanced} when Spring Boot application started @@ -117,10 +120,14 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration { if (index > -1) { interceptors.set(index, new DubboAdapterLoadBalancerInterceptor(repository, loadBalancerInterceptor, - restTemplate.getMessageConverters())); + restTemplate.getMessageConverters(),classLoader)); } restTemplate.setInterceptors(interceptors); } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboAdapterLoadBalancerInterceptor.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboAdapterLoadBalancerInterceptor.java index 7654c79b..089504ec 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboAdapterLoadBalancerInterceptor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboAdapterLoadBalancerInterceptor.java @@ -17,6 +17,7 @@ package org.springframework.cloud.alibaba.dubbo.client.loadbalancer; import com.alibaba.dubbo.config.spring.ReferenceBean; +import com.alibaba.dubbo.rpc.service.GenericException; import com.alibaba.dubbo.rpc.service.GenericService; import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata; @@ -53,12 +54,16 @@ public class DubboAdapterLoadBalancerInterceptor implements ClientHttpRequestInt private final List> messageConverters; + private final DubboClientHttpResponseFactory clientHttpResponseFactory; + public DubboAdapterLoadBalancerInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository, LoadBalancerInterceptor loadBalancerInterceptor, - List> messageConverters) { + List> messageConverters, + ClassLoader classLoader) { this.repository = dubboServiceMetadataRepository; this.loadBalancerInterceptor = loadBalancerInterceptor; this.messageConverters = messageConverters; + this.clientHttpResponseFactory = new DubboClientHttpResponseFactory(messageConverters, classLoader); } @Override @@ -72,30 +77,44 @@ public class DubboAdapterLoadBalancerInterceptor implements ClientHttpRequestInt repository.initialize(serviceName); - RequestMetadata requestMetadata = buildRequestMetadata(request, uriComponents); + RequestMetadata clientMetadata = buildRequestMetadata(request, uriComponents); - ReferenceBean referenceBean = - repository.getReferenceBean(serviceName, requestMetadata); + RestMethodMetadata restMethodMetadata = repository.getRestMethodMetadata(serviceName, clientMetadata); - RestMethodMetadata restMethodMetadata = repository.getRestMethodMetadata(serviceName, requestMetadata); + ReferenceBean referenceBean = repository.getReferenceBean(serviceName, clientMetadata); if (referenceBean == null || restMethodMetadata == null) { return loadBalancerInterceptor.intercept(request, body, execution); } + Object result = null; + GenericException exception = null; + + try { + result = invokeService(restMethodMetadata, referenceBean, clientMetadata); + } catch (GenericException e) { + exception = e; + } + + return clientHttpResponseFactory.build(result, exception, clientMetadata, restMethodMetadata); + } + + private Object invokeService(RestMethodMetadata restMethodMetadata, ReferenceBean referenceBean, + RequestMetadata clientMetadata) throws GenericException { + MethodMetadata methodMetadata = restMethodMetadata.getMethod(); String methodName = methodMetadata.getName(); String[] parameterTypes = parameterResolver.resolveParameterTypes(methodMetadata); - Object[] parameters = parameterResolver.resolveParameters(restMethodMetadata, request, uriComponents); + Object[] parameters = parameterResolver.resolveParameters(restMethodMetadata, clientMetadata); GenericService genericService = referenceBean.get(); Object result = genericService.$invoke(methodName, parameterTypes, parameters); - return null; + return result; } public static RequestMetadata buildRequestMetadata(HttpRequest request, UriComponents uriComponents) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponse.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponse.java index 3d0d537d..f82d8dbd 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponse.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponse.java @@ -33,21 +33,19 @@ import java.io.InputStream; */ class DubboClientHttpResponse implements ClientHttpResponse { - private final Object result; - - private final GenericException exception; - private final HttpStatus httpStatus; private final String statusText; private final HttpHeaders httpHeaders = new HttpHeaders(); - public DubboClientHttpResponse(Object result, GenericException exception) { - this.result = result; - this.exception = exception; + private final DubboHttpOutputMessage httpOutputMessage; + + public DubboClientHttpResponse(DubboHttpOutputMessage httpOutputMessage, GenericException exception) { this.httpStatus = exception != null ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK; this.statusText = exception != null ? exception.getExceptionMessage() : httpStatus.getReasonPhrase(); + this.httpOutputMessage = httpOutputMessage; + this.httpHeaders.putAll(httpOutputMessage.getHeaders()); } @Override @@ -67,12 +65,11 @@ class DubboClientHttpResponse implements ClientHttpResponse { @Override public void close() { - } @Override public InputStream getBody() throws IOException { - return null; + return httpOutputMessage.getBody().getInputStream(); } @Override diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java new file mode 100644 index 00000000..f652273e --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java @@ -0,0 +1,64 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.client.loadbalancer; + +import com.alibaba.dubbo.rpc.service.GenericException; + +import org.springframework.cloud.alibaba.dubbo.http.converter.HttpMessageConverterHolder; +import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata; +import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata; +import org.springframework.cloud.alibaba.dubbo.metadata.resolver.HttpMessageConverterResolver; +import org.springframework.http.MediaType; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.converter.HttpMessageConverter; + +import java.io.IOException; +import java.util.List; + +/** + * Dubbo {@link ClientHttpResponse} Factory + * + * @author Mercy + */ +class DubboClientHttpResponseFactory { + + private final HttpMessageConverterResolver httpMessageConverterResolver; + + public DubboClientHttpResponseFactory(List> messageConverters, ClassLoader classLoader) { + this.httpMessageConverterResolver = new HttpMessageConverterResolver(messageConverters, classLoader); + } + + public ClientHttpResponse build(Object result, GenericException exception, + RequestMetadata requestMetadata, RestMethodMetadata restMethodMetadata) { + + DubboHttpOutputMessage httpOutputMessage = new DubboHttpOutputMessage(); + + HttpMessageConverterHolder httpMessageConverterHolder = httpMessageConverterResolver.resolve(requestMetadata, restMethodMetadata); + + if (httpMessageConverterHolder != null) { + MediaType mediaType = httpMessageConverterHolder.getMediaType(); + HttpMessageConverter converter = httpMessageConverterHolder.getConverter(); + try { + converter.write(result, mediaType, httpOutputMessage); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return new DubboClientHttpResponse(httpOutputMessage, exception); + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboHttpOutputMessage.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboHttpOutputMessage.java index 047045ee..2cffc471 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboHttpOutputMessage.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboHttpOutputMessage.java @@ -21,7 +21,6 @@ import org.springframework.http.HttpOutputMessage; import org.springframework.util.FastByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; /** * Dubbo {@link HttpOutputMessage} implementation @@ -31,7 +30,7 @@ import java.io.OutputStream; class DubboHttpOutputMessage implements HttpOutputMessage { @Override - public OutputStream getBody() throws IOException { + public FastByteArrayOutputStream getBody() throws IOException { return new FastByteArrayOutputStream(); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/http/converter/HttpMessageConverterHolder.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/http/converter/HttpMessageConverterHolder.java new file mode 100644 index 00000000..e0d7d11f --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/http/converter/HttpMessageConverterHolder.java @@ -0,0 +1,45 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.http.converter; + +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; + +/** + * {@link HttpMessageConverter} Holder with {@link MediaType}. + * + * @author Mercy + */ +public class HttpMessageConverterHolder { + + private final MediaType mediaType; + + private final HttpMessageConverter converter; + + public HttpMessageConverterHolder(MediaType mediaType, HttpMessageConverter converter) { + this.mediaType = mediaType; + this.converter = converter; + } + + public MediaType getMediaType() { + return mediaType; + } + + public HttpMessageConverter getConverter() { + return converter; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java index 56b7c9a5..4b3b1794 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java @@ -21,12 +21,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; import feign.RequestTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -54,9 +56,9 @@ public class RequestMetadata { @JsonProperty("headers") private HttpHeaders headers = new HttpHeaders(); - private List consumes = new LinkedList<>(); + private Set consumes = new LinkedHashSet<>(); - private List produces = new LinkedList<>(); + private Set produces = new LinkedHashSet<>(); public RequestMetadata() { } @@ -100,19 +102,19 @@ public class RequestMetadata { headers(headers); } - public List getConsumes() { + public Set getConsumes() { return consumes; } - public void setConsumes(List consumes) { + public void setConsumes(Set consumes) { this.consumes = consumes; } - public List getProduces() { + public Set getProduces() { return produces; } - public void setProduces(List produces) { + public void setProduces(Set produces) { this.produces = produces; } @@ -137,6 +139,14 @@ public class RequestMetadata { return toMediaTypes(produces); } + public String getParameter(String name) { + return this.params.getFirst(name); + } + + public String getHeader(String name) { + return this.headers.getFirst(name); + } + public RequestMetadata addParam(String name, String value) { add(name, value, this.params); return this; @@ -153,13 +163,15 @@ public class RequestMetadata { } private > RequestMetadata headers(Map headers) { - HttpHeaders httpHeaders = new HttpHeaders(); - // Add all headers - addAll(headers, httpHeaders); - // Handles "Content-Type" and "Accept" headers if present - mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes); - mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces); - this.headers.putAll(httpHeaders); + if (!CollectionUtils.isEmpty(headers)) { + HttpHeaders httpHeaders = new HttpHeaders(); + // Add all headers + addAll(headers, httpHeaders); + // Handles "Content-Type" and "Accept" headers if present + mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes); + mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces); + this.headers.putAll(httpHeaders); + } return this; } @@ -199,7 +211,7 @@ public class RequestMetadata { } } - private static void mediaTypes(HttpHeaders httpHeaders, String headerName, List destination) { + private static void mediaTypes(HttpHeaders httpHeaders, String headerName, Collection destination) { List value = httpHeaders.get(headerName); List mediaTypes = parseMediaTypes(value); destination.addAll(toMediaTypeValues(mediaTypes)); @@ -213,11 +225,11 @@ public class RequestMetadata { return list; } - private static List toMediaTypes(List mediaTypeValues) { + private static List toMediaTypes(Collection mediaTypeValues) { if (mediaTypeValues.isEmpty()) { return Collections.singletonList(MediaType.ALL); } - return parseMediaTypes(mediaTypeValues); + return parseMediaTypes(new LinkedList<>(mediaTypeValues)); } @Override diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/HttpMessageConverterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/HttpMessageConverterResolver.java new file mode 100644 index 00000000..cbde9e46 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/HttpMessageConverterResolver.java @@ -0,0 +1,186 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.metadata.resolver; + +import org.springframework.cloud.alibaba.dubbo.http.converter.HttpMessageConverterHolder; +import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata; +import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * {@link HttpMessageConverter} Resolver + * + * @author Mercy + */ +public class HttpMessageConverterResolver { + + private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); + + private final List> messageConverters; + + private final List allSupportedMediaTypes; + + private final ClassLoader classLoader; + + public HttpMessageConverterResolver(List> messageConverters, ClassLoader classLoader) { + this.messageConverters = messageConverters; + this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters); + this.classLoader = classLoader; + } + + /** + * Resolve the most match {@link HttpMessageConverter} from {@link RequestMetadata} + * + * @param requestMetadata {@link RequestMetadata} + * @param restMethodMetadata {@link RestMethodMetadata} + * @return + */ + public HttpMessageConverterHolder resolve(RequestMetadata requestMetadata, RestMethodMetadata restMethodMetadata) { + + HttpMessageConverterHolder httpMessageConverterHolder = null; + + Class returnValueClass = resolveReturnValueClass(restMethodMetadata); + + /** + * @see AbstractMessageConverterMethodProcessor#writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse) + */ + List requestedMediaTypes = getAcceptableMediaTypes(requestMetadata); + List producibleMediaTypes = getProducibleMediaTypes(restMethodMetadata, returnValueClass); + + Set compatibleMediaTypes = new LinkedHashSet(); + for (MediaType requestedType : requestedMediaTypes) { + for (MediaType producibleType : producibleMediaTypes) { + if (requestedType.isCompatibleWith(producibleType)) { + compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); + } + } + } + + if (compatibleMediaTypes.isEmpty()) { + return httpMessageConverterHolder; + } + + List mediaTypes = new ArrayList<>(compatibleMediaTypes); + + MediaType.sortBySpecificityAndQuality(mediaTypes); + + MediaType selectedMediaType = null; + for (MediaType mediaType : mediaTypes) { + if (mediaType.isConcrete()) { + selectedMediaType = mediaType; + break; + } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { + selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; + break; + } + } + + if (selectedMediaType != null) { + selectedMediaType = selectedMediaType.removeQualityValue(); + for (HttpMessageConverter messageConverter : this.messageConverters) { + if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { + httpMessageConverterHolder = new HttpMessageConverterHolder(selectedMediaType, messageConverter); + break; + } + } + } + + return httpMessageConverterHolder; + } + + private Class resolveReturnValueClass(RestMethodMetadata restMethodMetadata) { + String returnClassName = restMethodMetadata.getMethod().getReturnType(); + return ClassUtils.resolveClassName(returnClassName, classLoader); + } + + /** + * Resolve the {@link MediaType media-types} + * + * @param requestMetadata {@link RequestMetadata} from client side + * @return non-null {@link List} + */ + private List getAcceptableMediaTypes(RequestMetadata requestMetadata) { + return requestMetadata.getProduceMediaTypes(); + } + + /** + * Returns + * the media types that can be produced:
  • The producible media types specified in the request mappings, or + *
  • Media types of configured converters that can write the specific return value, or
  • {@link MediaType#ALL} + *
+ * + * @param restMethodMetadata {@link RestMethodMetadata} from server side + * @param returnValueClass the class of return value + * @return non-null {@link List} + */ + private List getProducibleMediaTypes(RestMethodMetadata restMethodMetadata, Class returnValueClass) { + RequestMetadata serverRequestMetadata = restMethodMetadata.getRequest(); + List mediaTypes = serverRequestMetadata.getProduceMediaTypes(); + if (!CollectionUtils.isEmpty(mediaTypes)) { // Empty + return mediaTypes; + } else if (!this.allSupportedMediaTypes.isEmpty()) { + List result = new ArrayList<>(); + for (HttpMessageConverter converter : this.messageConverters) { + if (converter.canWrite(returnValueClass, null)) { + result.addAll(converter.getSupportedMediaTypes()); + } + } + return result; + } else { + return Collections.singletonList(MediaType.ALL); + } + } + + /** + * Return the media types + * supported by all provided message converters sorted by specificity via {@link + * MediaType#sortBySpecificity(List)}. + * + * @param messageConverters + * @return + */ + private List getAllSupportedMediaTypes(List> messageConverters) { + Set allSupportedMediaTypes = new LinkedHashSet(); + for (HttpMessageConverter messageConverter : messageConverters) { + allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); + } + List result = new ArrayList(allSupportedMediaTypes); + MediaType.sortBySpecificity(result); + return Collections.unmodifiableList(result); + } + + /** + * Return the more specific of the acceptable and the producible media types + * with the q-value of the former. + */ + private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) { + MediaType produceTypeToUse = produceType.copyQualityValue(acceptType); + return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse); + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/ParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/ParameterResolver.java index 7f0b9da4..3c627207 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/ParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/ParameterResolver.java @@ -20,9 +20,7 @@ import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata; -import org.springframework.http.HttpRequest; import org.springframework.util.CollectionUtils; -import org.springframework.web.util.UriComponents; import java.util.Collection; import java.util.Iterator; @@ -37,8 +35,7 @@ import java.util.Set; */ public class ParameterResolver { - - public Object[] resolveParameters(RestMethodMetadata restMethodMetadata, HttpRequest request, UriComponents uriComponents) { + public Object[] resolveParameters(RestMethodMetadata restMethodMetadata, RequestMetadata clientMetadata) { MethodMetadata methodMetadata = restMethodMetadata.getMethod(); @@ -56,26 +53,24 @@ public class ParameterResolver { String name = getName(indexToName, index); - parameters[index] = getValue(requestMetadata, request, uriComponents, name); + parameters[index] = getValue(requestMetadata, clientMetadata, name); } return parameters; } - private String getValue(RequestMetadata requestMetadata, HttpRequest request, UriComponents uriComponents, String name) { - + private String getValue(RequestMetadata serverMetadata, RequestMetadata clientMetadata, String name) { String value = null; - Set paramNames = requestMetadata.getParamNames(); - Set headerNames = requestMetadata.getHeaderNames(); - + Set paramNames = serverMetadata.getParamNames(); + Set headerNames = serverMetadata.getHeaderNames(); if (paramNames.contains(name)) { - value = uriComponents.getQueryParams().getFirst(name); + value = clientMetadata.getParameter(name); } else if (headerNames.contains(name)) { - value = request.getHeaders().getFirst(name); + value = clientMetadata.getHeader(name); } - return value; + } private String getName(Map> indexToName, int index) { diff --git a/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java index 5f254f4c..2334ebe1 100644 --- a/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java +++ b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java @@ -30,14 +30,12 @@ import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Lazy; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; -import java.util.HashMap; -import java.util.Map; - import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -111,12 +109,15 @@ public class DubboSpringCloudBootstrap { @Bean public ApplicationRunner restTemplateRunner() { return arguments -> { - System.out.println(restTemplate.getForEntity("http://spring-cloud-alibaba-dubbo/echo?message=小马哥", String.class)); - Map data = new HashMap<>(); - data.put("name", "小马哥"); - data.put("age", 33); - data.put("height", 173); - System.out.println(restTemplate.postForEntity("http://spring-cloud-alibaba-dubbo/toString", data, String.class)); + ResponseEntity entity = restTemplate.getForEntity("http://spring-cloud-alibaba-dubbo/echo?message=小马哥", String.class); + System.out.println(entity.getHeaders()); + System.out.println(entity.getBody()); + // Still issue +// Map data = new HashMap<>(); +// data.put("name", "小马哥"); +// data.put("age", 33); +// data.put("height", 173); +// System.out.println(restTemplate.postForEntity("http://spring-cloud-alibaba-dubbo/toString", data, String.class)); }; }