Merge branch 'master' of github.com:spring-cloud-incubator/spring-cloud-alibaba
# Conflicts: # spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosDiscoveryParameterInitListener.java # spring-cloud-alicloud-context/src/test/java/org/springframework/cloud/alicloud/context/nacos/NacosDiscoveryParameterInitListenerTests.javapull/372/head
commit
2a83f3e84e
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@link DubboTransported @DubboTransported} annotation indicates that the traditional Spring Cloud Service-to-Service call is transported
|
||||
* by Dubbo under the hood, there are two main scenarios:
|
||||
* <ol>
|
||||
* <li>{@link FeignClient @FeignClient} annotated classes:
|
||||
* <ul>
|
||||
* If {@link DubboTransported @DubboTransported} annotated classes, the invocation of all methods of
|
||||
* {@link FeignClient @FeignClient} annotated classes.
|
||||
* </ul>
|
||||
* <ul>
|
||||
* If {@link DubboTransported @DubboTransported} annotated methods of {@link FeignClient @FeignClient} annotated classes.
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>{@link LoadBalanced @LoadBalanced} {@link RestTemplate} annotated field, method and parameters</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
*
|
||||
* @see FeignClient
|
||||
* @see LoadBalanced
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
|
||||
@Documented
|
||||
public @interface DubboTransported {
|
||||
|
||||
/**
|
||||
* The protocol of Dubbo transport whose value could be used the placeholder "dubbo.transport.protocol"
|
||||
*
|
||||
* @return the default protocol is "dubbo"
|
||||
*/
|
||||
String protocol() default "${dubbo.transport.protocol:dubbo}";
|
||||
|
||||
/**
|
||||
* The cluster of Dubbo transport whose value could be used the placeholder "dubbo.transport.cluster"
|
||||
*
|
||||
* @return the default cluster is "failover"
|
||||
*/
|
||||
String cluster() default "${dubbo.transport.cluster:failover}";
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.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;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor;
|
||||
import org.springframework.cloud.alibaba.dubbo.client.loadbalancer.DubboTransporterInterceptor;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Dubbo Auto-{@link Configuration} for {@link LoadBalanced @LoadBalanced} {@link RestTemplate}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(RestTemplate.class)
|
||||
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
|
||||
public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
|
||||
|
||||
private static final String DUBBO_TRANSPORTED_CLASS_NAME = DUBBO_TRANSPORTED_CLASS.getName();
|
||||
|
||||
@Autowired
|
||||
private DubboServiceMetadataRepository repository;
|
||||
|
||||
@Autowired
|
||||
private LoadBalancerInterceptor loadBalancerInterceptor;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Autowired
|
||||
private DubboGenericServiceFactory serviceFactory;
|
||||
|
||||
@Autowired
|
||||
private DubboGenericServiceExecutionContextFactory contextFactory;
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@LoadBalanced
|
||||
@Autowired(required = false)
|
||||
private Map<String, RestTemplate> 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
|
||||
* (after the callback of {@link SmartInitializingSingleton} beans or
|
||||
* {@link RestTemplateCustomizer#customize(RestTemplate) customization})
|
||||
*/
|
||||
@EventListener(ApplicationStartedEvent.class)
|
||||
public void adaptRestTemplates() {
|
||||
for (Map.Entry<String, RestTemplate> entry : restTemplates.entrySet()) {
|
||||
String beanName = entry.getKey();
|
||||
Map<String, Object> dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName);
|
||||
if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) {
|
||||
adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the annotation attributes {@link RestTemplate} bean being annotated
|
||||
* {@link DubboTransported @DubboTransported}
|
||||
*
|
||||
* @param beanName the bean name of {@link LoadBalanced @LoadBalanced} {@link RestTemplate}
|
||||
* @return non-null {@link Map}
|
||||
*/
|
||||
private Map<String, Object> getDubboTranslatedAttributes(String beanName) {
|
||||
Map<String, Object> attributes = Collections.emptyMap();
|
||||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||
if (beanDefinition instanceof AnnotatedBeanDefinition) {
|
||||
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
|
||||
MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
|
||||
attributes = factoryMethodMetadata != null ?
|
||||
factoryMethodMetadata.getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME) : Collections.emptyMap();
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapt the instance of {@link DubboTransporterInterceptor} to the {@link LoadBalancerInterceptor} Bean.
|
||||
*
|
||||
* @param restTemplate {@link LoadBalanced @LoadBalanced} {@link RestTemplate} Bean
|
||||
* @param dubboTranslatedAttributes the annotation dubboTranslatedAttributes {@link RestTemplate} bean being annotated
|
||||
* {@link DubboTransported @DubboTransported}
|
||||
*/
|
||||
private void adaptRestTemplate(RestTemplate restTemplate, Map<String, Object> dubboTranslatedAttributes) {
|
||||
|
||||
DubboTransportedMetadata dubboTransportedMetadata = buildDubboTransportedMetadata(dubboTranslatedAttributes);
|
||||
|
||||
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(restTemplate.getInterceptors());
|
||||
|
||||
int index = interceptors.indexOf(loadBalancerInterceptor);
|
||||
|
||||
index = index < 0 ? 0 : index;
|
||||
|
||||
// Add ClientHttpRequestInterceptor instances before loadBalancerInterceptor
|
||||
interceptors.add(index++, new DubboMetadataInitializerInterceptor(repository));
|
||||
|
||||
interceptors.add(index++, new DubboTransporterInterceptor(repository, restTemplate.getMessageConverters(),
|
||||
classLoader, dubboTransportedMetadata, serviceFactory, contextFactory));
|
||||
|
||||
restTemplate.setInterceptors(interceptors);
|
||||
}
|
||||
|
||||
private DubboTransportedMetadata buildDubboTransportedMetadata(Map<String, Object> dubboTranslatedAttributes) {
|
||||
DubboTransportedMetadata dubboTransportedMetadata = new DubboTransportedMetadata();
|
||||
String protocol = (String) dubboTranslatedAttributes.get("protocol");
|
||||
String cluster = (String) dubboTranslatedAttributes.get("cluster");
|
||||
// resolve placeholders
|
||||
dubboTransportedMetadata.setProtocol(environment.resolvePlaceholders(protocol));
|
||||
dubboTransportedMetadata.setCluster(environment.resolvePlaceholders(cluster));
|
||||
return dubboTransportedMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.parameter.PathVariableServiceParameterResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestBodyServiceParameterResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestHeaderServiceParameterResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestParamServiceParameterResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Spring Boot Auto-Configuration class for Dubbo Service
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Configuration
|
||||
public class DubboServiceAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DubboGenericServiceFactory dubboGenericServiceFactory() {
|
||||
return new DubboGenericServiceFactory();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(value = {
|
||||
DubboGenericServiceExecutionContextFactory.class,
|
||||
RequestParamServiceParameterResolver.class,
|
||||
RequestBodyServiceParameterResolver.class,
|
||||
RequestHeaderServiceParameterResolver.class,
|
||||
PathVariableServiceParameterResolver.class
|
||||
})
|
||||
static class ParameterResolversConfiguration {
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Dubbo {@link ClientHttpResponse} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboTransporterInterceptor
|
||||
*/
|
||||
class DubboClientHttpResponse implements ClientHttpResponse {
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
|
||||
private final String statusText;
|
||||
|
||||
private final HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
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
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return httpStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return httpStatus.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return statusText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return httpOutputMessage.getBody().getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
@ -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.http.util.HttpMessageConverterResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class DubboClientHttpResponseFactory {
|
||||
|
||||
private final HttpMessageConverterResolver httpMessageConverterResolver;
|
||||
|
||||
public DubboClientHttpResponseFactory(List<HttpMessageConverter<?>> 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);
|
||||
}
|
||||
}
|
@ -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.client.loadbalancer;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Dubbo {@link HttpOutputMessage} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class DubboHttpOutputMessage implements HttpOutputMessage {
|
||||
|
||||
private final FastByteArrayOutputStream outputStream = new FastByteArrayOutputStream();
|
||||
|
||||
private final HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
@Override
|
||||
public FastByteArrayOutputStream getBody() throws IOException {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Dubbo Metadata {@link ClientHttpRequestInterceptor} Initializing Interceptor executes intercept before
|
||||
* {@link DubboTransporterInterceptor}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboMetadataInitializerInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private final DubboServiceMetadataRepository repository;
|
||||
|
||||
public DubboMetadataInitializerInterceptor(DubboServiceMetadataRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
|
||||
URI originalUri = request.getURI();
|
||||
|
||||
String serviceName = originalUri.getHost();
|
||||
|
||||
repository.initialize(serviceName);
|
||||
|
||||
// Execute next
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.http.MutableHttpServerRequest;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContext;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.util.UriComponentsBuilder.fromUri;
|
||||
|
||||
/**
|
||||
* Dubbo Transporter {@link ClientHttpRequestInterceptor} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see LoadBalancerInterceptor
|
||||
*/
|
||||
public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private final DubboServiceMetadataRepository repository;
|
||||
|
||||
private final DubboClientHttpResponseFactory clientHttpResponseFactory;
|
||||
|
||||
private final DubboTransportedMetadata dubboTransportedMetadata;
|
||||
|
||||
private final DubboGenericServiceFactory serviceFactory;
|
||||
|
||||
private final DubboGenericServiceExecutionContextFactory contextFactory;
|
||||
|
||||
private final PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
public DubboTransporterInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository,
|
||||
List<HttpMessageConverter<?>> messageConverters,
|
||||
ClassLoader classLoader,
|
||||
DubboTransportedMetadata dubboTransportedMetadata,
|
||||
DubboGenericServiceFactory serviceFactory,
|
||||
DubboGenericServiceExecutionContextFactory contextFactory) {
|
||||
this.repository = dubboServiceMetadataRepository;
|
||||
this.dubboTransportedMetadata = dubboTransportedMetadata;
|
||||
this.clientHttpResponseFactory = new DubboClientHttpResponseFactory(messageConverters, classLoader);
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
|
||||
URI originalUri = request.getURI();
|
||||
|
||||
String serviceName = originalUri.getHost();
|
||||
|
||||
RequestMetadata clientMetadata = buildRequestMetadata(request);
|
||||
|
||||
DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, clientMetadata);
|
||||
|
||||
if (dubboServiceMetadata == null) {
|
||||
// if DubboServiceMetadata is not found, executes next
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
|
||||
RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata();
|
||||
|
||||
GenericService genericService = serviceFactory.create(dubboServiceMetadata, dubboTransportedMetadata);
|
||||
|
||||
MutableHttpServerRequest httpServerRequest = new MutableHttpServerRequest(request, body);
|
||||
|
||||
customizeRequest(httpServerRequest, dubboRestMethodMetadata, clientMetadata);
|
||||
|
||||
DubboGenericServiceExecutionContext context = contextFactory.create(dubboRestMethodMetadata, httpServerRequest);
|
||||
|
||||
Object result = null;
|
||||
GenericException exception = null;
|
||||
|
||||
try {
|
||||
result = genericService.$invoke(context.getMethodName(), context.getParameterTypes(), context.getParameters());
|
||||
} catch (GenericException e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
return clientHttpResponseFactory.build(result, exception, clientMetadata, dubboRestMethodMetadata);
|
||||
}
|
||||
|
||||
protected void customizeRequest(MutableHttpServerRequest httpServerRequest,
|
||||
RestMethodMetadata dubboRestMethodMetadata, RequestMetadata clientMetadata) {
|
||||
|
||||
RequestMetadata dubboRequestMetadata = dubboRestMethodMetadata.getRequest();
|
||||
String pathPattern = dubboRequestMetadata.getPath();
|
||||
|
||||
Map<String, String> pathVariables = pathMatcher.extractUriTemplateVariables(pathPattern, httpServerRequest.getPath());
|
||||
|
||||
if (!CollectionUtils.isEmpty(pathVariables)) {
|
||||
// Put path variables Map into query parameters Map
|
||||
httpServerRequest.params(pathVariables);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private RequestMetadata buildRequestMetadata(HttpRequest request) {
|
||||
UriComponents uriComponents = fromUri(request.getURI()).build(true);
|
||||
RequestMetadata requestMetadata = new RequestMetadata();
|
||||
requestMetadata.setPath(uriComponents.getPath());
|
||||
requestMetadata.setMethod(request.getMethod().name());
|
||||
requestMetadata.setParams(uriComponents.getQueryParams());
|
||||
requestMetadata.setHeaders(request.getHeaders());
|
||||
return requestMetadata;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Byte array {@link HttpInputMessage} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class ByteArrayHttpInputMessage implements HttpInputMessage {
|
||||
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
private final InputStream inputStream;
|
||||
|
||||
public ByteArrayHttpInputMessage(byte[] body) {
|
||||
this(new HttpHeaders(), body);
|
||||
}
|
||||
|
||||
public ByteArrayHttpInputMessage(HttpHeaders httpHeaders, byte[] body) {
|
||||
this.httpHeaders = httpHeaders;
|
||||
this.inputStream = new UnsafeByteArrayInputStream(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.util.UriComponentsBuilder.fromPath;
|
||||
|
||||
/**
|
||||
* Default {@link HttpRequest} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DefaultHttpRequest implements HttpRequest {
|
||||
|
||||
private final String method;
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
public DefaultHttpRequest(String method, String path, Map<String, List<String>> params,
|
||||
Map<String, List<String>> headers) {
|
||||
this.method = method == null ? HttpMethod.GET.name() : method.toUpperCase();
|
||||
this.uri = buildURI(path, params);
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
|
||||
private URI buildURI(String path, Map<String, List<String>> params) {
|
||||
UriComponentsBuilder builder = fromPath(path)
|
||||
.queryParams(new LinkedMultiValueMap<>(params));
|
||||
return builder.build().toUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.resolve(getMethodValue());
|
||||
}
|
||||
|
||||
public String getMethodValue() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} Builder
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
String method;
|
||||
|
||||
String path;
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
|
||||
public Builder method(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder path(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder param(String name, String value) {
|
||||
this.params.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder header(String name, String value) {
|
||||
this.headers.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder params(Map<String, List<String>> params) {
|
||||
this.params.putAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder headers(Map<String, List<String>> headers) {
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest build() {
|
||||
return new DefaultHttpRequest(method, path, params, headers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* HTTP Server Request
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public interface HttpServerRequest extends HttpRequest, HttpInputMessage {
|
||||
|
||||
/**
|
||||
* Return a path of current HTTP request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* Return a map with parsed and decoded query parameter values.
|
||||
*/
|
||||
MultiValueMap<String, String> getQueryParams();
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.http.util.HttpUtils.getParameters;
|
||||
|
||||
/**
|
||||
* Mutable {@link HttpServerRequest} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class MutableHttpServerRequest implements HttpServerRequest {
|
||||
|
||||
private final HttpMethod httpMethod;
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final MultiValueMap<String, String> queryParams;
|
||||
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
private final HttpInputMessage httpInputMessage;
|
||||
|
||||
public MutableHttpServerRequest(HttpRequest httpRequest, byte[] body) {
|
||||
this.httpMethod = httpRequest.getMethod();
|
||||
this.uri = httpRequest.getURI();
|
||||
this.path = uri.getPath();
|
||||
this.httpHeaders = httpRequest.getHeaders();
|
||||
this.queryParams = getParameters(httpRequest);
|
||||
this.httpInputMessage = new ByteArrayHttpInputMessage(body);
|
||||
}
|
||||
|
||||
public MutableHttpServerRequest params(Map<String, String> params) {
|
||||
queryParams.setAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return httpInputMessage.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return httpMethod;
|
||||
}
|
||||
|
||||
// Override method since Spring Framework 5.0
|
||||
public String getMethodValue() {
|
||||
return httpMethod.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, String> getQueryParams() {
|
||||
return queryParams;
|
||||
}
|
||||
}
|
@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Abstract {@link HttpRequestMatcher} implementation
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class AbstractHttpRequestMatcher implements HttpRequestMatcher {
|
||||
|
||||
/**
|
||||
* Return the discrete items a request condition is composed of.
|
||||
* <p>For example URL patterns, HTTP request methods, param expressions, etc.
|
||||
*
|
||||
* @return a collection of objects, never {@code null}
|
||||
*/
|
||||
protected abstract Collection<?> getContent();
|
||||
|
||||
/**
|
||||
* The notation to use when printing discrete items of content.
|
||||
* <p>For example {@code " || "} for URL patterns or {@code " && "}
|
||||
* for param expressions.
|
||||
*/
|
||||
protected abstract String getToStringInfix();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return getContent().equals(((AbstractHttpRequestMatcher) other).getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getContent().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("[");
|
||||
for (Iterator<?> iterator = getContent().iterator(); iterator.hasNext(); ) {
|
||||
Object expression = iterator.next();
|
||||
builder.append(expression.toString());
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(getToStringInfix());
|
||||
}
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.AbstractMediaTypeExpression
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class AbstractMediaTypeExpression implements MediaTypeExpression, Comparable<AbstractMediaTypeExpression> {
|
||||
|
||||
private final MediaType mediaType;
|
||||
|
||||
private final boolean negated;
|
||||
|
||||
AbstractMediaTypeExpression(String expression) {
|
||||
if (expression.startsWith("!")) {
|
||||
this.negated = true;
|
||||
expression = expression.substring(1);
|
||||
} else {
|
||||
this.negated = false;
|
||||
}
|
||||
this.mediaType = MediaType.parseMediaType(expression);
|
||||
}
|
||||
|
||||
AbstractMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
this.mediaType = mediaType;
|
||||
this.negated = negated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNegated() {
|
||||
return this.negated;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(AbstractMediaTypeExpression other) {
|
||||
return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AbstractMediaTypeExpression otherExpr = (AbstractMediaTypeExpression) other;
|
||||
return (this.mediaType.equals(otherExpr.mediaType) && this.negated == otherExpr.negated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.mediaType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (this.negated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(this.mediaType.toString());
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.util.StringUtils.trimWhitespace;
|
||||
|
||||
/**
|
||||
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.AbstractNameValueExpression
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final T value;
|
||||
|
||||
protected final boolean negated;
|
||||
|
||||
AbstractNameValueExpression(String expression) {
|
||||
int separator = expression.indexOf('=');
|
||||
if (separator == -1) {
|
||||
this.negated = expression.startsWith("!");
|
||||
this.name = trimWhitespace((this.negated ? expression.substring(1) : expression));
|
||||
this.value = null;
|
||||
} else {
|
||||
this.negated = (separator > 0) && (expression.charAt(separator - 1) == '!');
|
||||
this.name = trimWhitespace((this.negated ? expression.substring(0, separator - 1)
|
||||
: expression.substring(0, separator)));
|
||||
String valueExpression = getValueExpression(expression, separator);
|
||||
this.value = isExcludedValue(valueExpression) ? null : parseValue(valueExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private String getValueExpression(String expression, int separator) {
|
||||
return trimWhitespace(expression.substring(separator + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude the pattern value Expression: "{value}", subclass could override this method.
|
||||
*
|
||||
* @param valueExpression
|
||||
* @return
|
||||
*/
|
||||
protected boolean isExcludedValue(String valueExpression) {
|
||||
return StringUtils.hasText(valueExpression) &&
|
||||
valueExpression.startsWith("{")
|
||||
&& valueExpression.endsWith("}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNegated() {
|
||||
return this.negated;
|
||||
}
|
||||
|
||||
public final boolean match(HttpRequest request) {
|
||||
boolean isMatch;
|
||||
if (this.value != null) {
|
||||
isMatch = matchValue(request);
|
||||
} else {
|
||||
isMatch = matchName(request);
|
||||
}
|
||||
return (this.negated ? !isMatch : isMatch);
|
||||
}
|
||||
|
||||
|
||||
protected abstract boolean isCaseSensitiveName();
|
||||
|
||||
protected abstract T parseValue(String valueExpression);
|
||||
|
||||
protected abstract boolean matchName(HttpRequest request);
|
||||
|
||||
protected abstract boolean matchValue(HttpRequest request);
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AbstractNameValueExpression<?> that = (AbstractNameValueExpression<?>) other;
|
||||
return ((isCaseSensitiveName() ? this.name.equals(that.name) : this.name.equalsIgnoreCase(that.name)) &&
|
||||
ObjectUtils.nullSafeEquals(this.value, that.value) && this.negated == that.negated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (isCaseSensitiveName() ? this.name.hashCode() : this.name.toLowerCase().hashCode());
|
||||
result = 31 * result + (this.value != null ? this.value.hashCode() : 0);
|
||||
result = 31 * result + (this.negated ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (this.value != null) {
|
||||
builder.append(this.name);
|
||||
if (this.negated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append('=');
|
||||
builder.append(this.value);
|
||||
} else {
|
||||
if (this.negated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(this.name);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Composite {@link HttpRequestMatcher} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class CompositeHttpRequestMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final List<HttpRequestMatcher> matchers = new LinkedList<>();
|
||||
|
||||
public CompositeHttpRequestMatcher(HttpRequestMatcher... matchers) {
|
||||
this.matchers.addAll(Arrays.asList(matchers));
|
||||
}
|
||||
|
||||
public CompositeHttpRequestMatcher and(HttpRequestMatcher matcher) {
|
||||
this.matchers.add(matcher);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
for (HttpRequestMatcher matcher : matchers) {
|
||||
if (!matcher.match(request)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected List<HttpRequestMatcher> getMatchers() {
|
||||
return this.matchers;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<?> getContent() {
|
||||
List<Object> content = new LinkedList<>();
|
||||
for (HttpRequestMatcher matcher : getMatchers()) {
|
||||
if (matcher instanceof AbstractHttpRequestMatcher) {
|
||||
content.addAll(((AbstractHttpRequestMatcher) matcher).getContent());
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* Parses and matches a single media type expression to a request's 'Content-Type' header.
|
||||
* <p>
|
||||
* The source code is scratched from
|
||||
* org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression {
|
||||
|
||||
ConsumeMediaTypeExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
super(mediaType, negated);
|
||||
}
|
||||
|
||||
public final boolean match(MediaType contentType) {
|
||||
boolean match = getMediaType().includes(contentType);
|
||||
return (!isNegated() ? match : !match);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Parses and matches a single header expression to a request.
|
||||
* <p>
|
||||
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.HeaderExpression
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class HeaderExpression extends AbstractNameValueExpression<String> {
|
||||
|
||||
HeaderExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseSensitiveName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(HttpRequest request) {
|
||||
HttpHeaders httpHeaders = request.getHeaders();
|
||||
return httpHeaders.containsKey(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(HttpRequest request) {
|
||||
HttpHeaders httpHeaders = request.getHeaders();
|
||||
String headerValue = httpHeaders.getFirst(this.name);
|
||||
return ObjectUtils.nullSafeEquals(this.value, headerValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} 'Content-Type' header {@link HttpRequestMatcher matcher}
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestConsumersMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final List<ConsumeMediaTypeExpression> expressions;
|
||||
|
||||
/**
|
||||
* Creates a new instance from 0 or more "consumes" expressions.
|
||||
*
|
||||
* @param consumes consumes expressions if 0 expressions are provided,
|
||||
* the condition will match to every request
|
||||
*/
|
||||
public HttpRequestConsumersMatcher(String... consumes) {
|
||||
this(consumes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with "consumes" and "header" expressions.
|
||||
* "Header" expressions where the header name is not 'Content-Type' or have
|
||||
* no header value defined are ignored. If 0 expressions are provided in
|
||||
* total, the condition will match to every request
|
||||
*
|
||||
* @param consumes consumes expressions
|
||||
* @param headers headers expressions
|
||||
*/
|
||||
public HttpRequestConsumersMatcher(String[] consumes, String[] headers) {
|
||||
this(parseExpressions(consumes, headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor accepting parsed media type expressions.
|
||||
*/
|
||||
private HttpRequestConsumersMatcher(Collection<ConsumeMediaTypeExpression> expressions) {
|
||||
this.expressions = new ArrayList<>(expressions);
|
||||
Collections.sort(this.expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
|
||||
if (expressions.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HttpHeaders httpHeaders = request.getHeaders();
|
||||
|
||||
MediaType contentType = httpHeaders.getContentType();
|
||||
|
||||
if (contentType == null) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
for (ConsumeMediaTypeExpression expression : expressions) {
|
||||
if (!expression.match(contentType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) {
|
||||
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeaderExpression expr = new HeaderExpression(header);
|
||||
if ("Content-Type".equalsIgnoreCase(expr.name) && expr.value != null) {
|
||||
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
|
||||
result.add(new ConsumeMediaTypeExpression(mediaType, expr.negated));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String consume : consumes) {
|
||||
result.add(new ConsumeMediaTypeExpression(consume));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConsumeMediaTypeExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} headers {@link HttpRequestMatcher matcher}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestHeadersMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final Set<HeaderExpression> expressions;
|
||||
|
||||
public HttpRequestHeadersMatcher(String... headers) {
|
||||
this.expressions = parseExpressions(headers);
|
||||
}
|
||||
|
||||
private static Set<HeaderExpression> parseExpressions(String... headers) {
|
||||
Set<HeaderExpression> expressions = new LinkedHashSet<>();
|
||||
for (String header : headers) {
|
||||
HeaderExpression expr = new HeaderExpression(header);
|
||||
if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) ||
|
||||
HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(expr.name)) {
|
||||
continue;
|
||||
}
|
||||
expressions.add(expr);
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
for (HeaderExpression expression : this.expressions) {
|
||||
if (!expression.match(request)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<HeaderExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
}
|
@ -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.springframework.cloud.alibaba.dubbo.http.matcher;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} Matcher
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public interface HttpRequestMatcher {
|
||||
|
||||
/**
|
||||
* Match {@link HttpRequest} or not
|
||||
*
|
||||
* @param request The {@link HttpRequest} instance
|
||||
* @return if matched, return <code>true</code>, or <code>false</code>.
|
||||
*/
|
||||
boolean match(HttpRequest request);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.http.HttpMethod.resolve;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} {@link HttpMethod methods} {@link HttpRequestMatcher matcher}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestMethodsMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final Set<HttpMethod> methods;
|
||||
|
||||
public HttpRequestMethodsMatcher(String... methods) {
|
||||
this.methods = resolveHttpMethods(methods);
|
||||
}
|
||||
|
||||
private Set<HttpMethod> resolveHttpMethods(String[] methods) {
|
||||
Set<HttpMethod> httpMethods = new LinkedHashSet<>(methods.length);
|
||||
for (String method : methods) {
|
||||
if (!StringUtils.hasText(method)) {
|
||||
continue;
|
||||
}
|
||||
HttpMethod httpMethod = resolve(method);
|
||||
httpMethods.add(httpMethod);
|
||||
}
|
||||
return httpMethods;
|
||||
}
|
||||
|
||||
public Set<HttpMethod> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
boolean matched = false;
|
||||
HttpMethod httpMethod = request.getMethod();
|
||||
if (httpMethod != null) {
|
||||
for (HttpMethod method : getMethods()) {
|
||||
if (httpMethod.equals(method)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<HttpMethod> getContent() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} parameters {@link HttpRequestMatcher matcher}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestParamsMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final Set<ParamExpression> expressions;
|
||||
|
||||
/**
|
||||
* @param params The pattern of params :
|
||||
* <ul>
|
||||
* <li>name=value</li>
|
||||
* <li>name</li>
|
||||
* </ul>
|
||||
*/
|
||||
public HttpRequestParamsMatcher(String... params) {
|
||||
this.expressions = parseExpressions(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
for (ParamExpression paramExpression : expressions) {
|
||||
if (paramExpression.match(request)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Set<ParamExpression> parseExpressions(String... params) {
|
||||
Set<ParamExpression> expressions = new LinkedHashSet<>();
|
||||
for (String param : params) {
|
||||
expressions.add(new ParamExpression(param));
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ParamExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} {@link URI} {@link HttpRequestMatcher matcher}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestPathMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final Set<String> patterns;
|
||||
|
||||
private final PathMatcher pathMatcher;
|
||||
|
||||
public HttpRequestPathMatcher(String... patterns) {
|
||||
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
|
||||
this.pathMatcher = new AntPathMatcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
List<String> matches = getMatchingPatterns(request);
|
||||
return !matches.isEmpty();
|
||||
}
|
||||
|
||||
public List<String> getMatchingPatterns(HttpRequest request) {
|
||||
String path = getPath(request);
|
||||
List<String> matches = getMatchingPatterns(path);
|
||||
return matches;
|
||||
}
|
||||
|
||||
public List<String> getMatchingPatterns(String lookupPath) {
|
||||
List<String> matches = new ArrayList<>();
|
||||
for (String pattern : this.patterns) {
|
||||
String match = getMatchingPattern(pattern, lookupPath);
|
||||
if (match != null) {
|
||||
matches.add(match);
|
||||
}
|
||||
}
|
||||
if (matches.size() > 1) {
|
||||
matches.sort(this.pathMatcher.getPatternComparator(lookupPath));
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
private String getMatchingPattern(String pattern, String lookupPath) {
|
||||
if (pattern.equals(lookupPath)) {
|
||||
return pattern;
|
||||
}
|
||||
boolean hasSuffix = pattern.indexOf('.') != -1;
|
||||
if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
|
||||
return pattern + ".*";
|
||||
}
|
||||
if (this.pathMatcher.match(pattern, lookupPath)) {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
|
||||
return pattern + "/";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getPath(HttpRequest request) {
|
||||
URI uri = request.getURI();
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
private static Set<String> prependLeadingSlash(String[] patterns) {
|
||||
Set<String> result = new LinkedHashSet<>(patterns.length);
|
||||
for (String pattern : patterns) {
|
||||
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
|
||||
pattern = "/" + pattern;
|
||||
}
|
||||
result.add(pattern);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getContent() {
|
||||
return this.patterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest} 'Accept' header {@link HttpRequestMatcher matcher}
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestProducesMatcher extends AbstractHttpRequestMatcher {
|
||||
|
||||
private final List<ProduceMediaTypeExpression> expressions;
|
||||
|
||||
/**
|
||||
* Creates a new instance from "produces" expressions. If 0 expressions
|
||||
* are provided in total, this condition will match to any request.
|
||||
*
|
||||
* @param produces produces expressions
|
||||
*/
|
||||
public HttpRequestProducesMatcher(String... produces) {
|
||||
this(produces, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with "produces" and "header" expressions. "Header"
|
||||
* expressions where the header name is not 'Accept' or have no header value
|
||||
* defined are ignored. If 0 expressions are provided in total, this condition
|
||||
* will match to any request.
|
||||
*
|
||||
* @param produces produces expressions
|
||||
* @param headers headers expressions
|
||||
*/
|
||||
public HttpRequestProducesMatcher(String[] produces, String[] headers) {
|
||||
this(parseExpressions(produces, headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor accepting parsed media type expressions.
|
||||
*/
|
||||
private HttpRequestProducesMatcher(Collection<ProduceMediaTypeExpression> expressions) {
|
||||
this.expressions = new ArrayList<>(expressions);
|
||||
Collections.sort(this.expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(HttpRequest request) {
|
||||
|
||||
if (expressions.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HttpHeaders httpHeaders = request.getHeaders();
|
||||
|
||||
List<MediaType> acceptedMediaTypes = httpHeaders.getAccept();
|
||||
|
||||
for (ProduceMediaTypeExpression expression : expressions) {
|
||||
if (!expression.match(acceptedMediaTypes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, String[] headers) {
|
||||
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeaderExpression expr = new HeaderExpression(header);
|
||||
if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) && expr.value != null) {
|
||||
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
|
||||
result.add(new ProduceMediaTypeExpression(mediaType, expr.negated));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String produce : produces) {
|
||||
result.add(new ProduceMediaTypeExpression(produce));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ProduceMediaTypeExpression> getContent() {
|
||||
return expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
}
|
@ -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.springframework.cloud.alibaba.dubbo.http.matcher;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* A contract for media type expressions (e.g. "text/plain", "!text/plain") as
|
||||
* defined in the for "consumes" and "produces".
|
||||
* <p>
|
||||
* The source code is scratched from org.springframework.web.servlet.mvc.condition.MediaTypeExpression
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
interface MediaTypeExpression {
|
||||
|
||||
MediaType getMediaType();
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
|
||||
/**
|
||||
* A contract for {@code "name!=value"} style expression used to specify request
|
||||
* parameters and request header in HTTP request
|
||||
* <p>
|
||||
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.NameValueExpression
|
||||
*
|
||||
* @param <T> the value type
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
interface NameValueExpression<T> {
|
||||
|
||||
String getName();
|
||||
|
||||
T getValue();
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
@ -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.springframework.cloud.alibaba.dubbo.http.matcher;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.http.util.HttpUtils.getParameters;
|
||||
|
||||
/**
|
||||
* Parses and matches a single param expression to a request.
|
||||
* <p>
|
||||
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.ParamExpression
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class ParamExpression extends AbstractNameValueExpression<String> {
|
||||
|
||||
ParamExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseSensitiveName() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(HttpRequest request) {
|
||||
MultiValueMap<String, String> parametersMap = getParameters(request);
|
||||
return parametersMap.containsKey(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(HttpRequest request) {
|
||||
MultiValueMap<String, String> parametersMap = getParameters(request);
|
||||
String parameterValue = parametersMap.getFirst(this.name);
|
||||
return ObjectUtils.nullSafeEquals(this.value, parameterValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses and matches a single media type expression to a request's 'Accept' header.
|
||||
* <p>
|
||||
* The source code is scratched from
|
||||
* org.springframework.web.servlet.mvc.condition.ProducesRequestCondition.ProduceMediaTypeExpression
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class ProduceMediaTypeExpression extends AbstractMediaTypeExpression {
|
||||
|
||||
ProduceMediaTypeExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
ProduceMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
super(mediaType, negated);
|
||||
}
|
||||
|
||||
public final boolean match(List<MediaType> acceptedMediaTypes) {
|
||||
boolean match = matchMediaType(acceptedMediaTypes);
|
||||
return (!isNegated() ? match : !match);
|
||||
}
|
||||
|
||||
private boolean matchMediaType(List<MediaType> acceptedMediaTypes) {
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
if (getMediaType().isCompatibleWith(acceptedMediaType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.http.util.HttpUtils.toNameAndValues;
|
||||
|
||||
/**
|
||||
* {@link RequestMetadata} {@link HttpRequestMatcher} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class RequestMetadataMatcher extends CompositeHttpRequestMatcher {
|
||||
|
||||
public RequestMetadataMatcher(RequestMetadata metadata) {
|
||||
super(
|
||||
// method
|
||||
new HttpRequestMethodsMatcher(metadata.getMethod()),
|
||||
// url
|
||||
new HttpRequestPathMatcher(metadata.getPath()),
|
||||
// params
|
||||
new HttpRequestParamsMatcher(toNameAndValues(metadata.getParams())),
|
||||
// headers
|
||||
new HttpRequestHeadersMatcher(toNameAndValues(metadata.getHeaders())),
|
||||
// consumes
|
||||
new HttpRequestConsumersMatcher(metadata.getConsumes().toArray(new String[0])),
|
||||
// produces
|
||||
new HttpRequestProducesMatcher(metadata.getProduces().toArray(new String[0]))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
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.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
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;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
/**
|
||||
* {@link HttpMessageConverter} Resolver
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpMessageConverterResolver {
|
||||
|
||||
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
private final List<MediaType> allSupportedMediaTypes;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public HttpMessageConverterResolver(List<HttpMessageConverter<?>> messageConverters, ClassLoader classLoader) {
|
||||
this.messageConverters = messageConverters;
|
||||
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public HttpMessageConverterHolder resolve(HttpRequest request, Class<?> parameterType) {
|
||||
|
||||
HttpMessageConverterHolder httpMessageConverterHolder = null;
|
||||
|
||||
HttpHeaders httpHeaders = request.getHeaders();
|
||||
|
||||
MediaType contentType = httpHeaders.getContentType();
|
||||
|
||||
if (contentType == null) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
if (converter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
|
||||
if (genericConverter.canRead(parameterType, parameterType, contentType)) {
|
||||
httpMessageConverterHolder = new HttpMessageConverterHolder(contentType, converter);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (converter.canRead(parameterType, contentType)) {
|
||||
httpMessageConverterHolder = new HttpMessageConverterHolder(contentType, converter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return httpMessageConverterHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<MediaType> requestedMediaTypes = getAcceptableMediaTypes(requestMetadata);
|
||||
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(restMethodMetadata, returnValueClass);
|
||||
|
||||
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
|
||||
for (MediaType requestedType : requestedMediaTypes) {
|
||||
for (MediaType producibleType : producibleMediaTypes) {
|
||||
if (requestedType.isCompatibleWith(producibleType)) {
|
||||
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compatibleMediaTypes.isEmpty()) {
|
||||
return httpMessageConverterHolder;
|
||||
}
|
||||
|
||||
List<MediaType> 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;
|
||||
}
|
||||
|
||||
public List<MediaType> getAllSupportedMediaTypes() {
|
||||
return unmodifiableList(allSupportedMediaTypes);
|
||||
}
|
||||
|
||||
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<MediaType> getAcceptableMediaTypes(RequestMetadata requestMetadata) {
|
||||
return requestMetadata.getProduceMediaTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns
|
||||
* the media types that can be produced: <ul> <li>The producible media types specified in the request mappings, or
|
||||
* <li>Media types of configured converters that can write the specific return value, or <li>{@link MediaType#ALL}
|
||||
* </ul>
|
||||
*
|
||||
* @param restMethodMetadata {@link RestMethodMetadata} from server side
|
||||
* @param returnValueClass the class of return value
|
||||
* @return non-null {@link List}
|
||||
*/
|
||||
private List<MediaType> getProducibleMediaTypes(RestMethodMetadata restMethodMetadata, Class<?>
|
||||
returnValueClass) {
|
||||
RequestMetadata serverRequestMetadata = restMethodMetadata.getRequest();
|
||||
List<MediaType> mediaTypes = serverRequestMetadata.getProduceMediaTypes();
|
||||
if (!CollectionUtils.isEmpty(mediaTypes)) { // Empty
|
||||
return mediaTypes;
|
||||
} else if (!this.allSupportedMediaTypes.isEmpty()) {
|
||||
List<MediaType> 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<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
}
|
||||
List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes);
|
||||
MediaType.sortBySpecificity(result);
|
||||
return 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.util.StringUtils.delimitedListToStringArray;
|
||||
import static org.springframework.util.StringUtils.hasText;
|
||||
import static org.springframework.util.StringUtils.trimAllWhitespace;
|
||||
|
||||
/**
|
||||
* Http Utilities class
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class HttpUtils {
|
||||
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
|
||||
private static final String EQUAL = "=";
|
||||
|
||||
private static final String AND = "&";
|
||||
|
||||
private static final String SEMICOLON = ";";
|
||||
|
||||
private static final String QUESTION_MASK = "?";
|
||||
|
||||
/**
|
||||
* The empty value
|
||||
*/
|
||||
private static final String EMPTY_VALUE = "";
|
||||
|
||||
/**
|
||||
* Normalize path:
|
||||
* <ol>
|
||||
* <li>To remove query string if presents</li>
|
||||
* <li>To remove duplicated slash("/") if exists</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param path path to be normalized
|
||||
* @return a normalized path if required
|
||||
*/
|
||||
public static String normalizePath(String path) {
|
||||
if (!hasText(path)) {
|
||||
return path;
|
||||
}
|
||||
String normalizedPath = path;
|
||||
int index = normalizedPath.indexOf(QUESTION_MASK);
|
||||
if (index > -1) {
|
||||
normalizedPath = normalizedPath.substring(0, index);
|
||||
}
|
||||
return StringUtils.replace(normalizedPath, "//", "/");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Parameters from the specified {@link HttpRequest request}
|
||||
*
|
||||
* @param request the specified {@link HttpRequest request}
|
||||
* @return
|
||||
*/
|
||||
public static MultiValueMap<String, String> getParameters(HttpRequest request) {
|
||||
URI uri = request.getURI();
|
||||
return getParameters(uri.getQuery());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Parameters from the specified query string.
|
||||
* <p>
|
||||
*
|
||||
* @param queryString The query string
|
||||
* @return The query parameters
|
||||
*/
|
||||
public static MultiValueMap<String, String> getParameters(String queryString) {
|
||||
return getParameters(delimitedListToStringArray(queryString, AND));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Parameters from the specified pairs of name-value.
|
||||
* <p>
|
||||
*
|
||||
* @param pairs The pairs of name-value
|
||||
* @return The query parameters
|
||||
*/
|
||||
public static MultiValueMap<String, String> getParameters(Iterable<String> pairs) {
|
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
|
||||
if (pairs != null) {
|
||||
for (String pair : pairs) {
|
||||
String[] nameAndValue = delimitedListToStringArray(pair, EQUAL);
|
||||
String name = decode(nameAndValue[0]);
|
||||
String value = nameAndValue.length < 2 ? null : nameAndValue[1];
|
||||
value = decode(value);
|
||||
addParam(parameters, name, value);
|
||||
}
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Parameters from the specified pairs of name-value.
|
||||
* <p>
|
||||
*
|
||||
* @param pairs The pairs of name-value
|
||||
* @return The query parameters
|
||||
*/
|
||||
public static MultiValueMap<String, String> getParameters(String... pairs) {
|
||||
return getParameters(Arrays.asList(pairs));
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Parse a read-only {@link MultiValueMap} of {@link HttpCookie} from {@link HttpHeaders}
|
||||
// *
|
||||
// * @param httpHeaders {@link HttpHeaders}
|
||||
// * @return non-null, the key is a cookie name , the value is {@link HttpCookie}
|
||||
// */
|
||||
// public static MultiValueMap<String, HttpCookie> parseCookies(HttpHeaders httpHeaders) {
|
||||
//
|
||||
// String cookie = httpHeaders.getFirst(COOKIE);
|
||||
//
|
||||
// String[] cookieNameAndValues = StringUtils.delimitedListToStringArray(cookie, SEMICOLON);
|
||||
//
|
||||
// MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>(cookieNameAndValues.length);
|
||||
//
|
||||
// for (String cookeNameAndValue : cookieNameAndValues) {
|
||||
// String[] nameAndValue = delimitedListToStringArray(trimWhitespace(cookeNameAndValue), EQUAL);
|
||||
// String name = nameAndValue[0];
|
||||
// String value = nameAndValue.length < 2 ? null : nameAndValue[1];
|
||||
// HttpCookie httpCookie = new HttpCookie(name, value);
|
||||
// cookies.add(name, httpCookie);
|
||||
// }
|
||||
//
|
||||
// return cookies;
|
||||
// }
|
||||
|
||||
/**
|
||||
* To the name and value line sets
|
||||
*
|
||||
* @param nameAndValuesMap {@link MultiValueMap} the map of name and values
|
||||
* @return non-null
|
||||
*/
|
||||
public static Set<String> toNameAndValuesSet(Map<String, List<String>> nameAndValuesMap) {
|
||||
Set<String> nameAndValues = new LinkedHashSet<>();
|
||||
for (Map.Entry<String, List<String>> entry : nameAndValuesMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
List<String> values = entry.getValue();
|
||||
for (String value : values) {
|
||||
String nameAndValue = name + EQUAL + value;
|
||||
nameAndValues.add(nameAndValue);
|
||||
}
|
||||
}
|
||||
return nameAndValues;
|
||||
}
|
||||
|
||||
public static String[] toNameAndValues(Map<String, List<String>> nameAndValuesMap) {
|
||||
return toNameAndValuesSet(nameAndValuesMap).toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a string of query string from the specified request parameters {@link Map}
|
||||
*
|
||||
* @param params the specified request parameters {@link Map}
|
||||
* @return non-null
|
||||
*/
|
||||
public static String toQueryString(Map<String, List<String>> params) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String line : toNameAndValuesSet(params)) {
|
||||
builder.append(line).append(AND);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode value
|
||||
*
|
||||
* @param value the value requires to decode
|
||||
* @return the decoded value
|
||||
*/
|
||||
public static String decode(String value) {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
String decodedValue = value;
|
||||
try {
|
||||
decodedValue = URLDecoder.decode(value, UTF_8);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
}
|
||||
return decodedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* encode value
|
||||
*
|
||||
* @param value the value requires to encode
|
||||
* @return the encoded value
|
||||
*/
|
||||
public static String encode(String value) {
|
||||
String encodedValue = value;
|
||||
try {
|
||||
encodedValue = URLEncoder.encode(value, UTF_8);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
}
|
||||
return encodedValue;
|
||||
}
|
||||
|
||||
private static void addParam(MultiValueMap<String, String> paramsMap, String name, String value) {
|
||||
String paramValue = trimAllWhitespace(value);
|
||||
if (!StringUtils.hasText(paramValue)) {
|
||||
paramValue = EMPTY_VALUE;
|
||||
}
|
||||
paramsMap.add(trimAllWhitespace(name), paramValue);
|
||||
}
|
||||
}
|
@ -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.springframework.cloud.alibaba.dubbo.metadata;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Dubbo Service Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboServiceMetadata {
|
||||
|
||||
private final ServiceRestMetadata serviceRestMetadata;
|
||||
|
||||
private final RestMethodMetadata restMethodMetadata;
|
||||
|
||||
public DubboServiceMetadata(ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata) {
|
||||
this.serviceRestMetadata = serviceRestMetadata;
|
||||
this.restMethodMetadata = restMethodMetadata;
|
||||
}
|
||||
|
||||
public ServiceRestMetadata getServiceRestMetadata() {
|
||||
return serviceRestMetadata;
|
||||
}
|
||||
|
||||
public RestMethodMetadata getRestMethodMetadata() {
|
||||
return restMethodMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof DubboServiceMetadata)) return false;
|
||||
DubboServiceMetadata that = (DubboServiceMetadata) o;
|
||||
return Objects.equals(serviceRestMetadata, that.serviceRestMetadata) &&
|
||||
Objects.equals(restMethodMetadata, that.restMethodMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(serviceRestMetadata, restMethodMetadata);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@link DubboTransported @DubboTransported} Metadata
|
||||
*/
|
||||
public class DubboTransportedMetadata {
|
||||
|
||||
private String protocol;
|
||||
|
||||
private String cluster;
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getCluster() {
|
||||
return cluster;
|
||||
}
|
||||
|
||||
public void setCluster(String cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof DubboTransportedMetadata)) return false;
|
||||
DubboTransportedMetadata that = (DubboTransportedMetadata) o;
|
||||
return Objects.equals(protocol, that.protocol) &&
|
||||
Objects.equals(cluster, that.cluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(protocol, cluster);
|
||||
}
|
||||
}
|
@ -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.springframework.cloud.alibaba.dubbo.metadata;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@link MethodMetadata} annotated {@link DubboTransported @DubboTransported}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboTransportedMethodMetadata {
|
||||
|
||||
private final DubboTransportedMetadata dubboTransportedMetadata;
|
||||
|
||||
private final MethodMetadata methodMetadata;
|
||||
|
||||
public DubboTransportedMethodMetadata(Method method) {
|
||||
this.methodMetadata = new MethodMetadata(method);
|
||||
this.dubboTransportedMetadata = new DubboTransportedMetadata();
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return dubboTransportedMetadata.getProtocol();
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
dubboTransportedMetadata.setProtocol(protocol);
|
||||
}
|
||||
|
||||
public String getCluster() {
|
||||
return dubboTransportedMetadata.getCluster();
|
||||
}
|
||||
|
||||
public void setCluster(String cluster) {
|
||||
dubboTransportedMetadata.setCluster(cluster);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return methodMetadata.getName();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
methodMetadata.setName(name);
|
||||
}
|
||||
|
||||
public String getReturnType() {
|
||||
return methodMetadata.getReturnType();
|
||||
}
|
||||
|
||||
public void setReturnType(String returnType) {
|
||||
methodMetadata.setReturnType(returnType);
|
||||
}
|
||||
|
||||
public List<MethodParameterMetadata> getParams() {
|
||||
return methodMetadata.getParams();
|
||||
}
|
||||
|
||||
public void setParams(List<MethodParameterMetadata> params) {
|
||||
methodMetadata.setParams(params);
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return methodMetadata.getMethod();
|
||||
}
|
||||
|
||||
public DubboTransportedMetadata getDubboTransportedMetadata() {
|
||||
return dubboTransportedMetadata;
|
||||
}
|
||||
|
||||
public MethodMetadata getMethodMetadata() {
|
||||
return methodMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof DubboTransportedMethodMetadata)) return false;
|
||||
DubboTransportedMethodMetadata that = (DubboTransportedMethodMetadata) o;
|
||||
return Objects.equals(dubboTransportedMetadata, that.dubboTransportedMetadata) &&
|
||||
Objects.equals(methodMetadata, that.methodMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(dubboTransportedMetadata, methodMetadata);
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 feign.Contract;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static feign.Feign.configKey;
|
||||
|
||||
/**
|
||||
* {@link MethodMetadata} Resolver for the {@link DubboTransported} annotated classes or methods in client side.
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboTransportedMethodMetadata
|
||||
*/
|
||||
public class DubboTransportedMethodMetadataResolver {
|
||||
|
||||
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
|
||||
|
||||
private final PropertyResolver propertyResolver;
|
||||
|
||||
private final Contract contract;
|
||||
|
||||
public DubboTransportedMethodMetadataResolver(PropertyResolver propertyResolver, Contract contract) {
|
||||
this.propertyResolver = propertyResolver;
|
||||
this.contract = contract;
|
||||
}
|
||||
|
||||
public Map<DubboTransportedMethodMetadata, RestMethodMetadata> resolve(Class<?> targetType) {
|
||||
Set<DubboTransportedMethodMetadata> dubboTransportedMethodMetadataSet =
|
||||
resolveDubboTransportedMethodMetadataSet(targetType);
|
||||
Map<String, RestMethodMetadata> restMethodMetadataMap = resolveRestRequestMetadataMap(targetType);
|
||||
return dubboTransportedMethodMetadataSet
|
||||
.stream()
|
||||
.collect(Collectors.toMap(methodMetadata -> methodMetadata, methodMetadata -> {
|
||||
RestMethodMetadata restMethodMetadata = restMethodMetadataMap.get(configKey(targetType, methodMetadata.getMethod()));
|
||||
restMethodMetadata.setMethod(methodMetadata.getMethodMetadata());
|
||||
return restMethodMetadata;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
protected Set<DubboTransportedMethodMetadata> resolveDubboTransportedMethodMetadataSet(Class<?> targetType) {
|
||||
// The public methods of target interface
|
||||
Method[] methods = targetType.getMethods();
|
||||
|
||||
Set<DubboTransportedMethodMetadata> methodMetadataSet = new LinkedHashSet<>();
|
||||
|
||||
for (Method method : methods) {
|
||||
DubboTransported dubboTransported = resolveDubboTransported(method);
|
||||
if (dubboTransported != null) {
|
||||
DubboTransportedMethodMetadata methodMetadata = createDubboTransportedMethodMetadata(method, dubboTransported);
|
||||
methodMetadataSet.add(methodMetadata);
|
||||
}
|
||||
}
|
||||
return methodMetadataSet;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, RestMethodMetadata> resolveRestRequestMetadataMap(Class<?> targetType) {
|
||||
return contract.parseAndValidatateMetadata(targetType)
|
||||
.stream().collect(Collectors.toMap(feign.MethodMetadata::configKey, this::restMethodMetadata));
|
||||
}
|
||||
|
||||
private RestMethodMetadata restMethodMetadata(feign.MethodMetadata methodMetadata) {
|
||||
return new RestMethodMetadata(methodMetadata);
|
||||
}
|
||||
|
||||
private DubboTransportedMethodMetadata createDubboTransportedMethodMetadata(Method method,
|
||||
DubboTransported dubboTransported) {
|
||||
DubboTransportedMethodMetadata methodMetadata = new DubboTransportedMethodMetadata(method);
|
||||
String protocol = propertyResolver.resolvePlaceholders(dubboTransported.protocol());
|
||||
String cluster = propertyResolver.resolvePlaceholders(dubboTransported.cluster());
|
||||
methodMetadata.setProtocol(protocol);
|
||||
methodMetadata.setCluster(cluster);
|
||||
return methodMetadata;
|
||||
}
|
||||
|
||||
private DubboTransported resolveDubboTransported(Method method) {
|
||||
DubboTransported dubboTransported = AnnotationUtils.findAnnotation(method, DUBBO_TRANSPORTED_CLASS);
|
||||
if (dubboTransported == null) { // Attempt to find @DubboTransported in the declaring class
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
dubboTransported = AnnotationUtils.findAnnotation(declaringClass, DUBBO_TRANSPORTED_CLASS);
|
||||
}
|
||||
return dubboTransported;
|
||||
}
|
||||
}
|
@ -1,97 +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.springframework.cloud.alibaba.dubbo.metadata.service;
|
||||
|
||||
import com.alibaba.nacos.api.config.ConfigService;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
|
||||
|
||||
/**
|
||||
* Nacos {@link MetadataConfigService}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class NacosMetadataConfigService implements MetadataConfigService {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Autowired
|
||||
private NacosConfigProperties nacosConfigProperties;
|
||||
|
||||
private ConfigService configService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.configService = nacosConfigProperties.configServiceInstance();
|
||||
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data Id of service rest metadata
|
||||
*/
|
||||
private static String getServiceRestMetadataDataId(String serviceName) {
|
||||
return "metadata:rest:" + serviceName + ".json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishServiceRestMetadata(String serviceName, Set<ServiceRestMetadata> serviceRestMetadata) {
|
||||
String dataId = getServiceRestMetadataDataId(serviceName);
|
||||
String json = writeValueAsString(serviceRestMetadata);
|
||||
try {
|
||||
configService.publishConfig(dataId, DEFAULT_GROUP, json);
|
||||
} catch (NacosException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ServiceRestMetadata> getServiceRestMetadata(String serviceName) {
|
||||
Set<ServiceRestMetadata> metadata = Collections.emptySet();
|
||||
String dataId = getServiceRestMetadataDataId(serviceName);
|
||||
try {
|
||||
String json = configService.getConfig(dataId, DEFAULT_GROUP, 1000 * 3);
|
||||
metadata = objectMapper.readValue(json,
|
||||
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private String writeValueAsString(Object object) {
|
||||
String content = null;
|
||||
try {
|
||||
content = objectMapper.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}
|
@ -1,65 +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.springframework.cloud.alibaba.dubbo.openfeign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Dubbo {@link Configuration} for {@link FeignClient FeignClients}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboOpenFeignAutoConfiguration
|
||||
* @see org.springframework.cloud.openfeign.FeignContext#setConfigurations(List)
|
||||
* @see FeignClientsConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
public class DubboFeignClientsConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
@Autowired
|
||||
private DubboServiceMetadataRepository dubboServiceRepository;
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor beanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof Feign.Builder) {
|
||||
Feign.Builder builder = (Feign.Builder) bean;
|
||||
builder.invocationHandlerFactory(new DubboInvocationHandlerFactory(contract, dubboServiceRepository));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,101 +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.springframework.cloud.alibaba.dubbo.openfeign;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
import feign.Contract;
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Target;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static feign.Feign.configKey;
|
||||
|
||||
/**
|
||||
* Dubbo {@link InvocationHandlerFactory}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboInvocationHandlerFactory implements InvocationHandlerFactory {
|
||||
|
||||
private final static InvocationHandlerFactory DEFAULT_INVOCATION_HANDLER_FACTORY =
|
||||
new InvocationHandlerFactory.Default();
|
||||
|
||||
private final Contract contract;
|
||||
|
||||
private final DubboServiceMetadataRepository dubboServiceRepository;
|
||||
|
||||
public DubboInvocationHandlerFactory(Contract contract, DubboServiceMetadataRepository dubboServiceRepository) {
|
||||
this.contract = contract;
|
||||
this.dubboServiceRepository = dubboServiceRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
|
||||
// The target class annotated @FeignClient
|
||||
Class<?> targetType = target.type();
|
||||
// Resolve metadata from current @FeignClient type
|
||||
Map<Method, RequestMetadata> methodRequestMetadataMap = resolveMethodRequestMetadataMap(targetType, dispatch.keySet());
|
||||
// @FeignClient specifies the service name
|
||||
String serviceName = target.name();
|
||||
// Update specified metadata
|
||||
dubboServiceRepository.updateMetadata(serviceName);
|
||||
|
||||
Map<Method, GenericService> genericServicesMap = new HashMap<>();
|
||||
|
||||
Map<Method, org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata> methodMetadataMap = new HashMap<>();
|
||||
|
||||
methodRequestMetadataMap.forEach((method, requestMetadata) -> {
|
||||
GenericService genericService = dubboServiceRepository.getGenericService(serviceName, requestMetadata);
|
||||
org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata methodMetadata =
|
||||
dubboServiceRepository.getMethodMetadata(serviceName, requestMetadata);
|
||||
genericServicesMap.put(method, genericService);
|
||||
methodMetadataMap.put(method, methodMetadata);
|
||||
});
|
||||
|
||||
InvocationHandler defaultInvocationHandler = DEFAULT_INVOCATION_HANDLER_FACTORY.create(target, dispatch);
|
||||
|
||||
DubboInvocationHandler invocationHandler = new DubboInvocationHandler(genericServicesMap, methodMetadataMap,
|
||||
defaultInvocationHandler);
|
||||
|
||||
return invocationHandler;
|
||||
}
|
||||
|
||||
private Map<Method, RequestMetadata> resolveMethodRequestMetadataMap(Class<?> targetType, Set<Method> methods) {
|
||||
Map<String, RequestMetadata> requestMetadataMap = resolveRequestMetadataMap(targetType);
|
||||
return methods.stream().collect(Collectors.toMap(method -> method, method ->
|
||||
requestMetadataMap.get(configKey(targetType, method))
|
||||
));
|
||||
}
|
||||
|
||||
private Map<String, RequestMetadata> resolveRequestMetadataMap(Class<?> targetType) {
|
||||
return contract.parseAndValidatateMetadata(targetType)
|
||||
.stream().collect(Collectors.toMap(MethodMetadata::configKey, this::requestMetadata));
|
||||
}
|
||||
|
||||
private RequestMetadata requestMetadata(MethodMetadata methodMetadata) {
|
||||
return new RequestMetadata(methodMetadata.template());
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.openfeign;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Feign {@link Method} Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class FeignMethodMetadata {
|
||||
|
||||
private final GenericService dubboGenericService;
|
||||
|
||||
private final RestMethodMetadata dubboRestMethodMetadata;
|
||||
|
||||
private final RestMethodMetadata feignMethodMetadata;
|
||||
|
||||
|
||||
FeignMethodMetadata(GenericService dubboGenericService, RestMethodMetadata dubboRestMethodMetadata,
|
||||
RestMethodMetadata feignMethodMetadata) {
|
||||
this.dubboGenericService = dubboGenericService;
|
||||
this.dubboRestMethodMetadata = dubboRestMethodMetadata;
|
||||
this.feignMethodMetadata = feignMethodMetadata;
|
||||
}
|
||||
|
||||
GenericService getDubboGenericService() {
|
||||
return dubboGenericService;
|
||||
}
|
||||
|
||||
RestMethodMetadata getDubboRestMethodMetadata() {
|
||||
return dubboRestMethodMetadata;
|
||||
}
|
||||
|
||||
RestMethodMetadata getFeignMethodMetadata() {
|
||||
return feignMethodMetadata;
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.openfeign;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
import static org.springframework.util.ClassUtils.getUserClass;
|
||||
import static org.springframework.util.ClassUtils.resolveClassName;
|
||||
|
||||
/**
|
||||
* org.springframework.cloud.openfeign.Targeter {@link BeanPostProcessor}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class TargeterBeanPostProcessor implements BeanPostProcessor, BeanClassLoaderAware {
|
||||
|
||||
private static final String TARGETER_CLASS_NAME = "org.springframework.cloud.openfeign.Targeter";
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;
|
||||
|
||||
private final DubboGenericServiceFactory dubboGenericServiceFactory;
|
||||
|
||||
private final DubboGenericServiceExecutionContextFactory contextFactory;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public TargeterBeanPostProcessor(Environment environment,
|
||||
DubboServiceMetadataRepository dubboServiceMetadataRepository,
|
||||
DubboGenericServiceFactory dubboGenericServiceFactory,
|
||||
DubboGenericServiceExecutionContextFactory contextFactory) {
|
||||
this.environment = environment;
|
||||
this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
|
||||
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
|
||||
this.contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
|
||||
Class<?> beanClass = getUserClass(bean.getClass());
|
||||
Class<?> targetClass = resolveClassName(TARGETER_CLASS_NAME, classLoader);
|
||||
if (targetClass.isAssignableFrom(beanClass)) {
|
||||
return newProxyInstance(classLoader, new Class[]{targetClass},
|
||||
new TargeterInvocationHandler(bean, environment, dubboServiceMetadataRepository,
|
||||
dubboGenericServiceFactory,contextFactory));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.openfeign;
|
||||
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Target;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboTransportedMethodMetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
|
||||
/**
|
||||
* org.springframework.cloud.openfeign.Targeter {@link InvocationHandler}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class TargeterInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final Object bean;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final DubboServiceMetadataRepository repository;
|
||||
|
||||
private final DubboGenericServiceFactory dubboGenericServiceFactory;
|
||||
|
||||
private final DubboGenericServiceExecutionContextFactory contextFactory;
|
||||
|
||||
TargeterInvocationHandler(Object bean, Environment environment, DubboServiceMetadataRepository repository,
|
||||
DubboGenericServiceFactory dubboGenericServiceFactory,
|
||||
DubboGenericServiceExecutionContextFactory contextFactory) {
|
||||
this.bean = bean;
|
||||
this.environment = environment;
|
||||
this.repository = repository;
|
||||
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
|
||||
this.contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
/**
|
||||
* args[0]: FeignClientFactoryBean factory
|
||||
* args[1]: Feign.Builder feign
|
||||
* args[2]: FeignContext context
|
||||
* args[3]: Target.HardCodedTarget<T> target
|
||||
*/
|
||||
FeignContext feignContext = cast(args[2]);
|
||||
Target.HardCodedTarget<?> target = cast(args[3]);
|
||||
|
||||
// Execute Targeter#target method first
|
||||
method.setAccessible(true);
|
||||
// Get the default proxy object
|
||||
Object defaultProxy = method.invoke(bean, args);
|
||||
// Create Dubbo Proxy if required
|
||||
return createDubboProxyIfRequired(feignContext, target, defaultProxy);
|
||||
}
|
||||
|
||||
private Object createDubboProxyIfRequired(FeignContext feignContext, Target target, Object defaultProxy) {
|
||||
|
||||
DubboInvocationHandler dubboInvocationHandler = createDubboInvocationHandler(feignContext, target, defaultProxy);
|
||||
|
||||
if (dubboInvocationHandler == null) {
|
||||
return defaultProxy;
|
||||
}
|
||||
|
||||
return newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, dubboInvocationHandler);
|
||||
}
|
||||
|
||||
private DubboInvocationHandler createDubboInvocationHandler(FeignContext feignContext, Target target,
|
||||
Object defaultFeignClientProxy) {
|
||||
|
||||
// Service name equals @FeignClient.name()
|
||||
String serviceName = target.name();
|
||||
Class<?> targetType = target.type();
|
||||
|
||||
// Get Contract Bean from FeignContext
|
||||
Contract contract = feignContext.getInstance(serviceName, Contract.class);
|
||||
|
||||
DubboTransportedMethodMetadataResolver resolver =
|
||||
new DubboTransportedMethodMetadataResolver(environment, contract);
|
||||
|
||||
Map<DubboTransportedMethodMetadata, RestMethodMetadata> feignRestMethodMetadataMap = resolver.resolve(targetType);
|
||||
|
||||
if (feignRestMethodMetadataMap.isEmpty()) { // @DubboTransported method was not found from the Client interface
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("@{} method was not found in the Feign target type[{}]",
|
||||
DubboTransported.class.getSimpleName(), targetType.getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update Metadata
|
||||
repository.initialize(serviceName);
|
||||
|
||||
Map<Method, FeignMethodMetadata> feignMethodMetadataMap = getFeignMethodMetadataMap(serviceName, feignRestMethodMetadataMap);
|
||||
|
||||
InvocationHandler defaultFeignClientInvocationHandler = Proxy.getInvocationHandler(defaultFeignClientProxy);
|
||||
|
||||
DubboInvocationHandler dubboInvocationHandler = new DubboInvocationHandler(feignMethodMetadataMap,
|
||||
defaultFeignClientInvocationHandler, contextFactory);
|
||||
|
||||
return dubboInvocationHandler;
|
||||
}
|
||||
|
||||
private Map<Method, FeignMethodMetadata> getFeignMethodMetadataMap(String serviceName,
|
||||
Map<DubboTransportedMethodMetadata, RestMethodMetadata>
|
||||
feignRestMethodMetadataMap) {
|
||||
Map<Method, FeignMethodMetadata> feignMethodMetadataMap = new HashMap<>();
|
||||
|
||||
for (Map.Entry<DubboTransportedMethodMetadata, RestMethodMetadata> entry : feignRestMethodMetadataMap.entrySet()) {
|
||||
RestMethodMetadata feignRestMethodMetadata = entry.getValue();
|
||||
RequestMetadata feignRequestMetadata = feignRestMethodMetadata.getRequest();
|
||||
DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, feignRequestMetadata);
|
||||
if (dubboServiceMetadata != null) {
|
||||
DubboTransportedMethodMetadata dubboTransportedMethodMetadata = entry.getKey();
|
||||
DubboTransportedMetadata dubboTransportedMetadata = dubboTransportedMethodMetadata.getDubboTransportedMetadata();
|
||||
Method method = dubboTransportedMethodMetadata.getMethod();
|
||||
GenericService dubboGenericService = dubboGenericServiceFactory.create(dubboServiceMetadata, dubboTransportedMetadata);
|
||||
RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata();
|
||||
MethodMetadata methodMetadata = dubboTransportedMethodMetadata.getMethodMetadata();
|
||||
FeignMethodMetadata feignMethodMetadata = new FeignMethodMetadata(dubboGenericService,
|
||||
dubboRestMethodMetadata, feignRestMethodMetadata);
|
||||
feignMethodMetadataMap.put(method, feignMethodMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
return feignMethodMetadataMap;
|
||||
}
|
||||
|
||||
private static <T> T cast(Object object) {
|
||||
return (T) object;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
/**
|
||||
* Dubbo {@link GenericService} execution context
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboGenericServiceExecutionContext {
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final String[] parameterTypes;
|
||||
|
||||
private final Object[] parameters;
|
||||
|
||||
public DubboGenericServiceExecutionContext(String methodName, String[] parameterTypes, Object[] parameters) {
|
||||
this.methodName = methodName;
|
||||
this.parameterTypes = parameterTypes;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public String[] getParameterTypes() {
|
||||
return parameterTypes;
|
||||
}
|
||||
|
||||
public Object[] getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.parameter.DubboGenericServiceParameterResolver;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link DubboGenericServiceExecutionContext} Factory
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboGenericServiceParameterResolver
|
||||
*/
|
||||
public class DubboGenericServiceExecutionContextFactory {
|
||||
|
||||
@Autowired(required = false)
|
||||
private final List<DubboGenericServiceParameterResolver> resolvers = Collections.emptyList();
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
AnnotationAwareOrderComparator.sort(resolvers);
|
||||
}
|
||||
|
||||
public DubboGenericServiceExecutionContext create(RestMethodMetadata dubboRestMethodMetadata,
|
||||
RestMethodMetadata clientMethodMetadata, Object[] arguments) {
|
||||
|
||||
MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod();
|
||||
|
||||
String methodName = dubboMethodMetadata.getName();
|
||||
|
||||
String[] parameterTypes = resolveParameterTypes(dubboMethodMetadata);
|
||||
|
||||
Object[] parameters = resolveParameters(dubboRestMethodMetadata, clientMethodMetadata, arguments);
|
||||
|
||||
return new DubboGenericServiceExecutionContext(methodName, parameterTypes, parameters);
|
||||
}
|
||||
|
||||
public DubboGenericServiceExecutionContext create(RestMethodMetadata dubboRestMethodMetadata,
|
||||
HttpServerRequest request) {
|
||||
MethodMetadata methodMetadata = dubboRestMethodMetadata.getMethod();
|
||||
|
||||
String methodName = methodMetadata.getName();
|
||||
|
||||
String[] parameterTypes = resolveParameterTypes(methodMetadata);
|
||||
|
||||
Object[] parameters = resolveParameters(dubboRestMethodMetadata, request);
|
||||
|
||||
return new DubboGenericServiceExecutionContext(methodName, parameterTypes, parameters);
|
||||
}
|
||||
|
||||
private Map<String, Integer> buildParamNameToIndex(List<MethodParameterMetadata> params) {
|
||||
Map<String, Integer> paramNameToIndex = new LinkedHashMap<>();
|
||||
for (MethodParameterMetadata param : params) {
|
||||
paramNameToIndex.put(param.getName(), param.getIndex());
|
||||
}
|
||||
return paramNameToIndex;
|
||||
}
|
||||
|
||||
protected String[] resolveParameterTypes(MethodMetadata methodMetadata) {
|
||||
|
||||
List<MethodParameterMetadata> params = methodMetadata.getParams();
|
||||
|
||||
String[] parameterTypes = new String[params.size()];
|
||||
|
||||
for (MethodParameterMetadata parameterMetadata : params) {
|
||||
int index = parameterMetadata.getIndex();
|
||||
String parameterType = parameterMetadata.getType();
|
||||
parameterTypes[index] = parameterType;
|
||||
}
|
||||
|
||||
return parameterTypes;
|
||||
}
|
||||
|
||||
protected Object[] resolveParameters(RestMethodMetadata dubboRestMethodMetadata, HttpServerRequest request) {
|
||||
|
||||
MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod();
|
||||
|
||||
List<MethodParameterMetadata> params = dubboMethodMetadata.getParams();
|
||||
|
||||
Object[] parameters = new Object[params.size()];
|
||||
|
||||
for (MethodParameterMetadata parameterMetadata : params) {
|
||||
|
||||
int index = parameterMetadata.getIndex();
|
||||
|
||||
for (DubboGenericServiceParameterResolver resolver : resolvers) {
|
||||
Object parameter = resolver.resolve(dubboRestMethodMetadata, parameterMetadata, request);
|
||||
if (parameter != null) {
|
||||
parameters[index] = parameter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
protected Object[] resolveParameters(RestMethodMetadata dubboRestMethodMetadata,
|
||||
RestMethodMetadata clientRestMethodMetadata, Object[] arguments) {
|
||||
|
||||
MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod();
|
||||
|
||||
List<MethodParameterMetadata> params = dubboMethodMetadata.getParams();
|
||||
|
||||
Object[] parameters = new Object[params.size()];
|
||||
|
||||
for (MethodParameterMetadata parameterMetadata : params) {
|
||||
|
||||
int index = parameterMetadata.getIndex();
|
||||
|
||||
for (DubboGenericServiceParameterResolver resolver : resolvers) {
|
||||
Object parameter = resolver.resolve(dubboRestMethodMetadata, parameterMetadata, clientRestMethodMetadata, arguments);
|
||||
if (parameter != null) {
|
||||
parameters[index] = parameter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static com.alibaba.dubbo.common.Constants.DEFAULT_CLUSTER;
|
||||
import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceGroup;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceInterface;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceSegments;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceVersion;
|
||||
|
||||
/**
|
||||
* Dubbo {@link GenericService} Factory
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboGenericServiceFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final ConcurrentMap<Integer, ReferenceBean<GenericService>> cache = new ConcurrentHashMap<>();
|
||||
|
||||
public GenericService create(DubboServiceMetadata dubboServiceMetadata,
|
||||
DubboTransportedMetadata dubboTransportedMetadata) {
|
||||
|
||||
ReferenceBean<GenericService> referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata);
|
||||
|
||||
return referenceBean == null ? null : referenceBean.get();
|
||||
}
|
||||
|
||||
public GenericService create(String serviceName, Class<?> serviceClass) {
|
||||
String interfaceName = serviceClass.getName();
|
||||
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null,
|
||||
DEFAULT_PROTOCOL, DEFAULT_CLUSTER);
|
||||
return referenceBean.get();
|
||||
}
|
||||
|
||||
|
||||
private ReferenceBean<GenericService> build(ServiceRestMetadata serviceRestMetadata,
|
||||
DubboTransportedMetadata dubboTransportedMetadata) {
|
||||
String dubboServiceName = serviceRestMetadata.getName();
|
||||
String[] segments = getServiceSegments(dubboServiceName);
|
||||
String interfaceName = getServiceInterface(segments);
|
||||
String version = getServiceVersion(segments);
|
||||
String group = getServiceGroup(segments);
|
||||
String protocol = dubboTransportedMetadata.getProtocol();
|
||||
String cluster = dubboTransportedMetadata.getCluster();
|
||||
|
||||
return build(interfaceName, version, group, protocol, cluster);
|
||||
}
|
||||
|
||||
private ReferenceBean<GenericService> build(String interfaceName, String version, String group, String protocol,
|
||||
String cluster) {
|
||||
|
||||
Integer key = Objects.hash(interfaceName, version, group, protocol, cluster);
|
||||
|
||||
ReferenceBean<GenericService> referenceBean = cache.get(key);
|
||||
|
||||
if (referenceBean == null) {
|
||||
referenceBean = new ReferenceBean<>();
|
||||
referenceBean.setGeneric(true);
|
||||
referenceBean.setInterface(interfaceName);
|
||||
referenceBean.setVersion(version);
|
||||
referenceBean.setGroup(group);
|
||||
referenceBean.setProtocol(protocol);
|
||||
referenceBean.setCluster(cluster);
|
||||
}
|
||||
|
||||
return referenceBean;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
destroyReferenceBeans();
|
||||
cache.values();
|
||||
}
|
||||
|
||||
private void destroyReferenceBeans() {
|
||||
Collection<ReferenceBean<GenericService>> referenceBeans = cache.values();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("The Dubbo GenericService ReferenceBeans are destroying...");
|
||||
}
|
||||
for (ReferenceBean referenceBean : referenceBeans) {
|
||||
referenceBean.destroy(); // destroy ReferenceBean
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Destroyed the ReferenceBean : {} ", referenceBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* {@link DubboMetadataConfigService} {@link InvocationHandler}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class DubboMetadataConfigServiceInvocationHandler implements InvocationHandler {
|
||||
|
||||
/**
|
||||
* The method name of {@link DubboMetadataConfigService#getServiceRestMetadata()}
|
||||
*/
|
||||
private static final String METHOD_NAME = "getServiceRestMetadata";
|
||||
|
||||
private static final String[] PARAMETER_TYPES = new String[0];
|
||||
|
||||
private static final String[] PARAMETER_VALUES = new String[0];
|
||||
|
||||
private final GenericService genericService;
|
||||
|
||||
public DubboMetadataConfigServiceInvocationHandler(String serviceName, DubboGenericServiceFactory dubboGenericServiceFactory) {
|
||||
this.genericService = dubboGenericServiceFactory.create(serviceName, DubboMetadataConfigService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
if (METHOD_NAME.equals(methodName)) {
|
||||
return genericService.$invoke(methodName, PARAMETER_TYPES, PARAMETER_VALUES);
|
||||
}
|
||||
return method.invoke(proxy, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
|
||||
/**
|
||||
* The proxy of {@link DubboMetadataConfigService}
|
||||
*/
|
||||
public class DubboMetadataConfigServiceProxy implements BeanClassLoaderAware {
|
||||
|
||||
private final DubboGenericServiceFactory dubboGenericServiceFactory;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public DubboMetadataConfigServiceProxy(DubboGenericServiceFactory dubboGenericServiceFactory) {
|
||||
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* New proxy instance of {@link DubboMetadataConfigService} via the specified service name
|
||||
*
|
||||
* @param serviceName the service name
|
||||
* @return a {@link DubboMetadataConfigService} proxy
|
||||
*/
|
||||
public DubboMetadataConfigService newProxy(String serviceName) {
|
||||
return (DubboMetadataConfigService) newProxyInstance(classLoader, new Class[]{DubboMetadataConfigService.class},
|
||||
new DubboMetadataConfigServiceInvocationHandler(serviceName, dubboGenericServiceFactory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import com.alibaba.dubbo.config.annotation.Service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration.METADATA_PROTOCOL_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Publishing {@link DubboMetadataConfigService} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Service(version = "${spring.application.name}", protocol = METADATA_PROTOCOL_BEAN_NAME)
|
||||
// Use current Spring application name as the Dubbo Service version
|
||||
public class PublishingDubboMetadataConfigService implements DubboMetadataConfigService {
|
||||
|
||||
/**
|
||||
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
|
||||
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
|
||||
*/
|
||||
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the {@link Set} of {@link ServiceRestMetadata}
|
||||
*
|
||||
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
|
||||
*/
|
||||
public void publishServiceRestMetadata(Set<ServiceRestMetadata> serviceRestMetadataSet) {
|
||||
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
|
||||
if (!CollectionUtils.isEmpty(serviceRestMetadata.getMeta())) {
|
||||
this.serviceRestMetadata.add(serviceRestMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceRestMetadata() {
|
||||
String serviceRestMetadataJsonConfig = null;
|
||||
try {
|
||||
serviceRestMetadataJsonConfig = objectMapper.writeValueAsString(serviceRestMetadata);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return serviceRestMetadataJsonConfig;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
||||
import static org.springframework.context.ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME;
|
||||
import static org.springframework.util.ClassUtils.resolveClassName;
|
||||
|
||||
/**
|
||||
* Abstract {@link DubboGenericServiceParameterResolver} implementation
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class AbstractDubboGenericServiceParameterResolver implements DubboGenericServiceParameterResolver,
|
||||
BeanClassLoaderAware {
|
||||
|
||||
private int order;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier(CONVERSION_SERVICE_BEAN_NAME)
|
||||
private ConversionService conversionService = new FormattingConversionService();
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
protected Class<?> resolveClass(String className) {
|
||||
return resolveClassName(className, classLoader);
|
||||
}
|
||||
|
||||
protected Object resolveValue(Object parameterValue, String parameterType) {
|
||||
Class<?> targetType = resolveClass(parameterType);
|
||||
return resolveValue(parameterValue, targetType);
|
||||
}
|
||||
|
||||
protected Object resolveValue(Object parameterValue, Class<?> parameterType) {
|
||||
return conversionService.convert(parameterValue, parameterType);
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.isEmpty;
|
||||
|
||||
/**
|
||||
* Abstract HTTP Names Value {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class AbstractNamedValueServiceParameterResolver extends AbstractDubboGenericServiceParameterResolver {
|
||||
|
||||
/**
|
||||
* Get the {@link MultiValueMap} of names and values
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
protected abstract MultiValueMap<String, String> getNameAndValuesMap(HttpServerRequest request);
|
||||
|
||||
@Override
|
||||
public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata,
|
||||
HttpServerRequest request) {
|
||||
|
||||
Collection<String> names = getNames(restMethodMetadata, methodParameterMetadata);
|
||||
|
||||
if (isEmpty(names)) { // index can't match
|
||||
return null;
|
||||
}
|
||||
|
||||
MultiValueMap<String, String> nameAndValues = getNameAndValuesMap(request);
|
||||
|
||||
String targetName = null;
|
||||
|
||||
for (String name : names) {
|
||||
if (nameAndValues.containsKey(name)) {
|
||||
targetName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetName == null) { // request parameter is abstract
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> parameterType = resolveClass(methodParameterMetadata.getType());
|
||||
|
||||
Object paramValue = null;
|
||||
|
||||
if (parameterType.isArray()) { // Array type
|
||||
paramValue = nameAndValues.get(targetName);
|
||||
} else {
|
||||
paramValue = nameAndValues.getFirst(targetName);
|
||||
}
|
||||
|
||||
return resolveValue(paramValue, parameterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata,
|
||||
RestMethodMetadata clientRestMethodMetadata, Object[] arguments) {
|
||||
|
||||
Collection<String> names = getNames(restMethodMetadata, methodParameterMetadata);
|
||||
|
||||
if (isEmpty(names)) { // index can't match
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer index = null;
|
||||
|
||||
Map<Integer, Collection<String>> clientIndexToName = clientRestMethodMetadata.getIndexToName();
|
||||
|
||||
for (Map.Entry<Integer, Collection<String>> entry : clientIndexToName.entrySet()) {
|
||||
|
||||
Collection<String> clientParamNames = entry.getValue();
|
||||
|
||||
if (CollectionUtils.containsAny(names, clientParamNames)) {
|
||||
index = entry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index > -1 ? arguments[index] : null;
|
||||
}
|
||||
|
||||
protected Collection<String> getNames(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata) {
|
||||
|
||||
Map<Integer, Collection<String>> indexToName = restMethodMetadata.getIndexToName();
|
||||
|
||||
int index = methodParameterMetadata.getIndex();
|
||||
|
||||
Collection<String> paramNames = indexToName.get(index);
|
||||
|
||||
return paramNames == null ? Collections.emptyList() : paramNames;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* Dubbo {@link GenericService} Parameter Resolver
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public interface DubboGenericServiceParameterResolver extends Ordered {
|
||||
|
||||
/**
|
||||
* Resolves a method parameter into an argument value from a given request.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata,
|
||||
HttpServerRequest request);
|
||||
|
||||
Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata,
|
||||
RestMethodMetadata clientRestMethodMetadata, Object[] arguments);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* HTTP Request Path Variable {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class PathVariableServiceParameterResolver extends AbstractNamedValueServiceParameterResolver {
|
||||
|
||||
public static final int DEFAULT_ORDER = 3;
|
||||
|
||||
public PathVariableServiceParameterResolver() {
|
||||
super();
|
||||
setOrder(DEFAULT_ORDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiValueMap<String, String> getNameAndValuesMap(HttpServerRequest request) {
|
||||
return request.getQueryParams();
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.cloud.alibaba.dubbo.http.converter.HttpMessageConverterHolder;
|
||||
import org.springframework.cloud.alibaba.dubbo.http.util.HttpMessageConverterResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* HTTP Request Body {@link DubboGenericServiceParameterResolver}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class RequestBodyServiceParameterResolver extends AbstractDubboGenericServiceParameterResolver {
|
||||
|
||||
public static final int DEFAULT_ORDER = 7;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<HttpMessageConverters> httpMessageConverters;
|
||||
|
||||
private HttpMessageConverterResolver httpMessageConverterResolver;
|
||||
|
||||
public RequestBodyServiceParameterResolver() {
|
||||
super();
|
||||
setOrder(DEFAULT_ORDER);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
HttpMessageConverters httpMessageConverters = this.httpMessageConverters.getIfAvailable();
|
||||
|
||||
httpMessageConverterResolver = new HttpMessageConverterResolver(httpMessageConverters == null ?
|
||||
Collections.emptyList() : httpMessageConverters.getConverters(),
|
||||
getClassLoader());
|
||||
}
|
||||
|
||||
private boolean supportParameter(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata) {
|
||||
|
||||
Integer index = methodParameterMetadata.getIndex();
|
||||
|
||||
Integer bodyIndex = restMethodMetadata.getBodyIndex();
|
||||
|
||||
if (!Objects.equals(index, bodyIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?> parameterType = resolveClass(methodParameterMetadata.getType());
|
||||
|
||||
Class<?> bodyType = resolveClass(restMethodMetadata.getBodyType());
|
||||
|
||||
return Objects.equals(parameterType, bodyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata,
|
||||
HttpServerRequest request) {
|
||||
|
||||
if (!supportParameter(restMethodMetadata, methodParameterMetadata)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
|
||||
Class<?> parameterType = resolveClass(methodParameterMetadata.getType());
|
||||
|
||||
HttpMessageConverterHolder holder = httpMessageConverterResolver.resolve(request, parameterType);
|
||||
|
||||
if (holder != null) {
|
||||
HttpMessageConverter converter = holder.getConverter();
|
||||
try {
|
||||
result = converter.read(parameterType, request);
|
||||
} catch (IOException e) {
|
||||
throw new HttpMessageNotReadableException("I/O error while reading input message", e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata,
|
||||
RestMethodMetadata clientRestMethodMetadata, Object[] arguments) {
|
||||
|
||||
if (!supportParameter(restMethodMetadata, methodParameterMetadata)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer clientBodyIndex = clientRestMethodMetadata.getBodyIndex();
|
||||
return arguments[clientBodyIndex];
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* HTTP Request Header {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class RequestHeaderServiceParameterResolver extends AbstractNamedValueServiceParameterResolver {
|
||||
|
||||
public static final int DEFAULT_ORDER = 9;
|
||||
|
||||
public RequestHeaderServiceParameterResolver() {
|
||||
super();
|
||||
setOrder(DEFAULT_ORDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiValueMap<String, String> getNameAndValuesMap(HttpServerRequest request) {
|
||||
return request.getHeaders();
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.service.parameter;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.http.HttpServerRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* HTTP Request Parameter {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class RequestParamServiceParameterResolver extends AbstractNamedValueServiceParameterResolver {
|
||||
|
||||
public static final int DEFAULT_ORDER = 1;
|
||||
|
||||
public RequestParamServiceParameterResolver() {
|
||||
super();
|
||||
setOrder(DEFAULT_ORDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiValueMap<String, String> getNameAndValuesMap(HttpServerRequest request) {
|
||||
return request.getQueryParams();
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestMetadataRegistrationAutoConfiguration
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestMetadataRegistrationAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceAutoConfiguration
|
||||
|
||||
org.springframework.context.ApplicationContextInitializer=\
|
||||
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
|
||||
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* {@link AbstractHttpRequestMatcher} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class AbstractHttpRequestMatcherTest<T extends AbstractHttpRequestMatcher> {
|
||||
|
||||
@Test
|
||||
public abstract void testEqualsAndHashCode();
|
||||
|
||||
@Test
|
||||
public abstract void testGetContent();
|
||||
|
||||
@Test
|
||||
public abstract void testGetToStringInfix();
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* {@link AbstractMediaTypeExpression} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class AbstractMediaTypeExpressionTest<T extends AbstractMediaTypeExpression> {
|
||||
|
||||
protected T createExpression(String expression) {
|
||||
ResolvableType resolvableType = ResolvableType.forType(getClass().getGenericSuperclass());
|
||||
Class<T> firstGenericType = (Class<T>) resolvableType.resolveGeneric(0);
|
||||
Constructor<T> constructor = null;
|
||||
try {
|
||||
constructor = firstGenericType.getDeclaredConstructor(String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return BeanUtils.instantiateClass(constructor, expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMediaTypeAndNegated() {
|
||||
// Normal
|
||||
AbstractMediaTypeExpression expression = createExpression(MediaType.APPLICATION_JSON_VALUE);
|
||||
Assert.assertEquals(MediaType.APPLICATION_JSON, expression.getMediaType());
|
||||
Assert.assertFalse(expression.isNegated());
|
||||
|
||||
// Negated
|
||||
expression = createExpression("!" + MediaType.APPLICATION_JSON_VALUE);
|
||||
Assert.assertEquals(MediaType.APPLICATION_JSON, expression.getMediaType());
|
||||
Assert.assertTrue(expression.isNegated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsAndHashCode() {
|
||||
Assert.assertEquals(createExpression(MediaType.APPLICATION_JSON_VALUE), createExpression(MediaType.APPLICATION_JSON_VALUE));
|
||||
Assert.assertEquals(createExpression(MediaType.APPLICATION_JSON_VALUE).hashCode(),
|
||||
createExpression(MediaType.APPLICATION_JSON_VALUE).hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareTo() {
|
||||
Assert.assertEquals(0,
|
||||
createExpression(MediaType.APPLICATION_JSON_VALUE).compareTo(createExpression(MediaType.APPLICATION_JSON_VALUE)));
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* {@link AbstractNameValueExpression} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class AbstractNameValueExpressionTest<T extends AbstractNameValueExpression> {
|
||||
|
||||
protected T createExpression(String expression) {
|
||||
ResolvableType resolvableType = ResolvableType.forType(getClass().getGenericSuperclass());
|
||||
Class<T> firstGenericType = (Class<T>) resolvableType.resolveGeneric(0);
|
||||
Constructor<T> constructor = null;
|
||||
try {
|
||||
constructor = firstGenericType.getDeclaredConstructor(String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return BeanUtils.instantiateClass(constructor, expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNameAndValue() {
|
||||
// Normal Name and value
|
||||
AbstractNameValueExpression expression = createExpression("a=1");
|
||||
Assert.assertEquals("a", expression.getName());
|
||||
Assert.assertFalse(expression.isNegated());
|
||||
|
||||
expression = createExpression("a=1");
|
||||
Assert.assertEquals("a", expression.getName());
|
||||
Assert.assertEquals("1", expression.getValue());
|
||||
Assert.assertFalse(expression.isNegated());
|
||||
|
||||
// Negated Name
|
||||
expression = createExpression("!a");
|
||||
Assert.assertEquals("a", expression.getName());
|
||||
Assert.assertTrue(expression.isNegated());
|
||||
|
||||
expression = createExpression("a!=1");
|
||||
Assert.assertEquals("a", expression.getName());
|
||||
Assert.assertEquals("1", expression.getValue());
|
||||
Assert.assertTrue(expression.isNegated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsAndHashCode() {
|
||||
Assert.assertEquals(createExpression("a"), createExpression("a"));
|
||||
Assert.assertEquals(createExpression("a").hashCode(), createExpression("a").hashCode());
|
||||
Assert.assertEquals(createExpression("a=1"), createExpression("a = 1 "));
|
||||
Assert.assertEquals(createExpression("a=1").hashCode(), createExpression("a = 1 ").hashCode());
|
||||
Assert.assertNotEquals(createExpression("a"), createExpression("b"));
|
||||
Assert.assertNotEquals(createExpression("a").hashCode(), createExpression("b").hashCode());
|
||||
}
|
||||
}
|
@ -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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* {@link ConsumeMediaTypeExpression} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class ConsumeMediaTypeExpressionTest extends AbstractMediaTypeExpressionTest<ConsumeMediaTypeExpression> {
|
||||
|
||||
@Test
|
||||
public void testMatch() {
|
||||
ConsumeMediaTypeExpression expression = createExpression(MediaType.ALL_VALUE);
|
||||
|
||||
Assert.assertTrue(expression.match(MediaType.APPLICATION_JSON_UTF8));
|
||||
|
||||
expression = createExpression(MediaType.APPLICATION_JSON_VALUE);
|
||||
Assert.assertTrue(expression.match(MediaType.APPLICATION_JSON_UTF8));
|
||||
|
||||
expression = createExpression(MediaType.APPLICATION_JSON_VALUE + ";q=0.7");
|
||||
Assert.assertTrue(expression.match(MediaType.APPLICATION_JSON_UTF8));
|
||||
|
||||
expression = createExpression(MediaType.TEXT_HTML_VALUE);
|
||||
Assert.assertFalse(expression.match(MediaType.APPLICATION_JSON_UTF8));
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.http.DefaultHttpRequest.builder;
|
||||
|
||||
/**
|
||||
* {@link HeaderExpression} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HeaderExpressionTest extends AbstractNameValueExpressionTest<HeaderExpression> {
|
||||
|
||||
@Test
|
||||
public void testIsCaseSensitiveName() {
|
||||
Assert.assertFalse(createExpression("a=1").isCaseSensitiveName());
|
||||
Assert.assertFalse(createExpression("a=!1").isCaseSensitiveName());
|
||||
Assert.assertFalse(createExpression("b=1").isCaseSensitiveName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatch() {
|
||||
|
||||
HeaderExpression expression = createExpression("a=1");
|
||||
HttpRequest request = builder().build();
|
||||
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().header("a", "").build();
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().header("a", "2").build();
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().header("", "1").build();
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().header("a", "1").build();
|
||||
Assert.assertTrue(expression.match(request));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* {@link HttpRequestMethodsMatcher} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestMethodsMatcherTest extends AbstractHttpRequestMatcherTest<HttpRequestMethodsMatcher> {
|
||||
|
||||
HttpRequestMethodsMatcher matcher = new HttpRequestMethodsMatcher("GET");
|
||||
|
||||
@Override
|
||||
public void testEqualsAndHashCode() {
|
||||
Assert.assertEquals(new HashSet<>(Arrays.asList(HttpMethod.GET)), matcher.getMethods());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetContent() {
|
||||
Assert.assertEquals(new HashSet<>(Arrays.asList(HttpMethod.GET)), matcher.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetToStringInfix() {
|
||||
Assert.assertEquals(" || ", matcher.getToStringInfix());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link HttpRequestParamsMatcher} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpRequestParamsMatcherTest {
|
||||
|
||||
// @Test
|
||||
// public void testGetParams() {
|
||||
//
|
||||
// HttpRequestParamsMatcher matcher = new HttpRequestParamsMatcher(
|
||||
// "a ",
|
||||
// "a =1",
|
||||
// "b = 2",
|
||||
// "b = 3 ",
|
||||
// " c = 4 ",
|
||||
// " d"
|
||||
// );
|
||||
//
|
||||
// Map<String, List<String>> params = matcher.getParams();
|
||||
// Assert.assertEquals(4, params.size());
|
||||
// Assert.assertTrue(params.containsKey("a"));
|
||||
// Assert.assertTrue(params.containsKey("b"));
|
||||
// Assert.assertTrue(params.containsKey("c"));
|
||||
// Assert.assertTrue(params.containsKey("d"));
|
||||
// Assert.assertFalse(params.containsKey("e"));
|
||||
//
|
||||
// List<String> values = params.get("a");
|
||||
// Assert.assertEquals(2, values.size());
|
||||
// Assert.assertEquals("", values.get(0));
|
||||
// Assert.assertEquals("1", values.get(1));
|
||||
//
|
||||
// values = params.get("b");
|
||||
// Assert.assertEquals(2, values.size());
|
||||
// Assert.assertEquals("2", values.get(0));
|
||||
// Assert.assertEquals("3", values.get(1));
|
||||
//
|
||||
// values = params.get("c");
|
||||
// Assert.assertEquals(1, values.size());
|
||||
// Assert.assertEquals("4", values.get(0));
|
||||
//
|
||||
// values = params.get("d");
|
||||
// Assert.assertEquals(1, values.size());
|
||||
// Assert.assertEquals("", values.get(0));
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
|
||||
HttpRequestParamsMatcher matcher = new HttpRequestParamsMatcher("a ", "a = 1");
|
||||
|
||||
MockClientHttpRequest request = new MockClientHttpRequest();
|
||||
|
||||
request.setURI(URI.create("http://dummy/?a"));
|
||||
Assert.assertTrue(matcher.match(request));
|
||||
request.setURI(URI.create("http://dummy/?a&a=1"));
|
||||
Assert.assertTrue(matcher.match(request));
|
||||
|
||||
matcher = new HttpRequestParamsMatcher("a ", "a =1", "b", "b=2");
|
||||
request.setURI(URI.create("http://dummy/?a&a=1&b"));
|
||||
Assert.assertTrue(matcher.match(request));
|
||||
request.setURI(URI.create("http://dummy/?a&a=1&b&b=2"));
|
||||
Assert.assertTrue(matcher.match(request));
|
||||
|
||||
matcher = new HttpRequestParamsMatcher("a ", "a =1", "b", "b=2", "b = 3 ");
|
||||
request.setURI(URI.create("http://dummy/?a&a=1&b&b=2&b=3"));
|
||||
Assert.assertTrue(matcher.match(request));
|
||||
|
||||
request.setURI(URI.create("http://dummy/?d=1"));
|
||||
Assert.assertFalse(matcher.match(request));
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.http.DefaultHttpRequest.builder;
|
||||
|
||||
/**
|
||||
* {@link ParamExpression} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class ParamExpressionTest extends AbstractNameValueExpressionTest<ParamExpression> {
|
||||
|
||||
@Test
|
||||
public void testIsCaseSensitiveName() {
|
||||
Assert.assertTrue(createExpression("a=1").isCaseSensitiveName());
|
||||
Assert.assertTrue(createExpression("a=!1").isCaseSensitiveName());
|
||||
Assert.assertTrue(createExpression("b=1").isCaseSensitiveName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatch() {
|
||||
|
||||
ParamExpression expression = createExpression("a=1");
|
||||
HttpRequest request = builder().build();
|
||||
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().param("a", "").build();
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().param("a", "2").build();
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().param("", "1").build();
|
||||
Assert.assertFalse(expression.match(request));
|
||||
|
||||
request = builder().param("a", "1").build();
|
||||
Assert.assertTrue(expression.match(request));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.matcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* {@link ProduceMediaTypeExpression} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class ProduceMediaTypeExpressionTest extends AbstractMediaTypeExpressionTest<ProduceMediaTypeExpression> {
|
||||
|
||||
@Test
|
||||
public void testMatch() {
|
||||
ProduceMediaTypeExpression expression = createExpression(MediaType.APPLICATION_JSON_VALUE);
|
||||
Assert.assertTrue(expression.match(Arrays.asList(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON)));
|
||||
|
||||
expression = createExpression(MediaType.APPLICATION_JSON_VALUE);
|
||||
Assert.assertFalse(expression.match(Arrays.asList(MediaType.APPLICATION_XML)));
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* {@link HttpUtils} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class HttpUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testEncodeAndDecode() {
|
||||
|
||||
String whitespace = " ";
|
||||
|
||||
String encodedValue = HttpUtils.encode(" ");
|
||||
|
||||
String decodedValue = HttpUtils.decode(encodedValue);
|
||||
|
||||
Assert.assertEquals(whitespace, decodedValue);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link RequestMetadata} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class RequestMetadataTest {
|
||||
|
||||
private String method = "GET";
|
||||
|
||||
private String url = "/param";
|
||||
|
||||
private Set<String> paramNames = new LinkedHashSet<>(Arrays.asList("a", "b", "c"));
|
||||
|
||||
private Set<String> headerNames = new LinkedHashSet<>(Arrays.asList("d", "e", "f"));
|
||||
|
||||
@Test
|
||||
public void testEqualsAndHashCodeAndCompareTo() {
|
||||
|
||||
RequestMetadata metadata = new RequestMetadata();
|
||||
RequestMetadata metadata2 = new RequestMetadata();
|
||||
|
||||
Assert.assertEquals(metadata, metadata2);
|
||||
Assert.assertEquals(metadata.hashCode(), metadata2.hashCode());
|
||||
|
||||
metadata.setMethod(method);
|
||||
metadata2.setMethod(method);
|
||||
|
||||
Assert.assertEquals(metadata, metadata2);
|
||||
Assert.assertEquals(metadata.hashCode(), metadata2.hashCode());
|
||||
|
||||
metadata.setPath(url);
|
||||
metadata2.setPath(url);
|
||||
|
||||
Assert.assertEquals(metadata, metadata2);
|
||||
Assert.assertEquals(metadata.hashCode(), metadata2.hashCode());
|
||||
|
||||
metadata.addParam("a", "1").addParam("b", "2").addParam("c", "3");
|
||||
metadata2.addParam("a", "1a").addParam("b", "2b").addParam("c", "3c");
|
||||
|
||||
Assert.assertEquals(metadata, metadata2);
|
||||
Assert.assertEquals(metadata.hashCode(), metadata2.hashCode());
|
||||
|
||||
metadata.addHeader("d", "1").addHeader("e", "2").addHeader("f", "3");
|
||||
metadata2.addHeader("d", "1").addHeader("e", "2");
|
||||
|
||||
Assert.assertNotEquals(metadata, metadata2);
|
||||
Assert.assertNotEquals(metadata.hashCode(), metadata2.hashCode());
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testBestMatch() {
|
||||
//
|
||||
// NavigableMap<RequestMetadata, RequestMetadata> requestMetadataMap = new TreeMap<>();
|
||||
//
|
||||
// RequestMetadata metadata = new RequestMetadata();
|
||||
// metadata.setMethod(method);
|
||||
//
|
||||
// RequestMetadata metadata1 = new RequestMetadata();
|
||||
// metadata1.setMethod(method);
|
||||
// metadata1.setPath(url);
|
||||
//
|
||||
// RequestMetadata metadata2 = new RequestMetadata();
|
||||
// metadata2.setMethod(method);
|
||||
// metadata2.setPath(url);
|
||||
// metadata2.setParams(paramNames);
|
||||
//
|
||||
// RequestMetadata metadata3 = new RequestMetadata();
|
||||
// metadata3.setMethod(method);
|
||||
// metadata3.setPath(url);
|
||||
// metadata3.setParams(paramNames);
|
||||
// metadata3.setHeaders(headerNames);
|
||||
//
|
||||
// requestMetadataMap.put(metadata, metadata);
|
||||
// requestMetadataMap.put(metadata1, metadata1);
|
||||
// requestMetadataMap.put(metadata2, metadata2);
|
||||
// requestMetadataMap.put(metadata3, metadata3);
|
||||
//
|
||||
// RequestMetadata result = getBestMatch(requestMetadataMap, metadata);
|
||||
// Assert.assertEquals(result, metadata);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, metadata1);
|
||||
// Assert.assertEquals(result, metadata1);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, metadata2);
|
||||
// Assert.assertEquals(result, metadata2);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, metadata3);
|
||||
// Assert.assertEquals(result, metadata3);
|
||||
//
|
||||
// // REDO
|
||||
// requestMetadataMap.clear();
|
||||
//
|
||||
// requestMetadataMap.put(metadata1, metadata1);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, metadata2);
|
||||
// Assert.assertEquals(metadata1, result);
|
||||
//
|
||||
// requestMetadataMap.put(metadata2, metadata2);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, metadata3);
|
||||
// Assert.assertEquals(metadata2, result);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, new RequestMetadata());
|
||||
// Assert.assertNull(result);
|
||||
//
|
||||
// result = getBestMatch(requestMetadataMap, metadata);
|
||||
// Assert.assertNull(result);
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
|
||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link DubboTransportedMethodMetadataResolver} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboTransportedMethodMetadataResolverTest {
|
||||
|
||||
private DubboTransportedMethodMetadataResolver resolver;
|
||||
|
||||
private MockEnvironment environment;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
environment = new MockEnvironment();
|
||||
resolver = new DubboTransportedMethodMetadataResolver(environment, new SpringMvcContract());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolve() {
|
||||
Set<DubboTransportedMethodMetadata> metadataSet = resolver.resolveDubboTransportedMethodMetadataSet(TestDefaultService.class);
|
||||
Assert.assertEquals(1, metadataSet.size());
|
||||
}
|
||||
|
||||
|
||||
@DubboTransported
|
||||
interface TestDefaultService {
|
||||
|
||||
String test(String message);
|
||||
|
||||
}
|
||||
}
|
@ -1,65 +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.springframework.cloud.alibaba.dubbo.service;
|
||||
|
||||
import com.alibaba.dubbo.config.annotation.Service;
|
||||
import com.alibaba.dubbo.rpc.RpcContext;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
|
||||
/**
|
||||
* Default {@link EchoService}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Service(version = "1.0.0", protocol = {"dubbo", "rest"})
|
||||
@RestController
|
||||
@Path("/")
|
||||
public class DefaultEchoService implements EchoService {
|
||||
|
||||
@Override
|
||||
@GetMapping(value = "/echo"
|
||||
// consumes = MediaType.APPLICATION_JSON_VALUE,
|
||||
// produces = MediaType.APPLICATION_JSON_UTF8_VALUE
|
||||
)
|
||||
@Path("/echo")
|
||||
@GET
|
||||
// @Consumes("application/json")
|
||||
// @Produces("application/json;charset=UTF-8")
|
||||
public String echo(@RequestParam @QueryParam("message") String message) {
|
||||
return RpcContext.getContext().getUrl() + " [echo] : " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("/plus")
|
||||
@Path("/plus")
|
||||
@POST
|
||||
public String plus(@RequestParam @QueryParam("a") int a, @RequestParam @QueryParam("b") int b) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import com.alibaba.dubbo.rpc.RpcContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
|
||||
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
|
||||
|
||||
/**
|
||||
* Default {@link RestService}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@com.alibaba.dubbo.config.annotation.Service(version = "1.0.0", protocol = {"dubbo", "rest"})
|
||||
@RestController
|
||||
@Path("/")
|
||||
public class StandardRestService implements RestService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
@GetMapping(value = "/param")
|
||||
@Path("/param")
|
||||
@GET
|
||||
public String param(@RequestParam @QueryParam("param") String param) {
|
||||
log("/param", param);
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("/params")
|
||||
@Path("/params")
|
||||
@POST
|
||||
public String params(@RequestParam @QueryParam("a") int a, @RequestParam @QueryParam("b") String b) {
|
||||
log("/params", a + b);
|
||||
return a + b;
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping("/headers")
|
||||
@Path("/headers")
|
||||
@GET
|
||||
public String headers(@RequestHeader("h") @HeaderParam("h") String header,
|
||||
@RequestHeader("h2") @HeaderParam("h2") String header2,
|
||||
@RequestParam("v") @QueryParam("v") Integer param) {
|
||||
String result = header + " , " + header2 + " , " + param;
|
||||
log("/headers", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping("/path-variables/{p1}/{p2}")
|
||||
@Path("/path-variables/{p1}/{p2}")
|
||||
@GET
|
||||
public String pathVariables(@PathVariable("p1") @PathParam("p1") String path1,
|
||||
@PathVariable("p2") @PathParam("p2") String path2,
|
||||
@RequestParam("v") @QueryParam("v") String param) {
|
||||
String result = path1 + " , " + path2 + " , " + param;
|
||||
log("/path-variables", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// @CookieParam does not support : https://github.com/OpenFeign/feign/issues/913
|
||||
// @CookieValue also does not support
|
||||
|
||||
@Override
|
||||
@PostMapping("/form")
|
||||
@Path("/form")
|
||||
@POST
|
||||
public String form(@RequestParam("f") @FormParam("f") String form) {
|
||||
return String.valueOf(form);
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(value = "/request/body/map", produces = APPLICATION_JSON_UTF8_VALUE)
|
||||
@Path("/request/body/map")
|
||||
@POST
|
||||
@Produces(APPLICATION_JSON_VALUE)
|
||||
public User requestBodyMap(@RequestBody Map<String, Object> data, @RequestParam("param") @QueryParam("param") String param) {
|
||||
User user = new User();
|
||||
user.setId(((Integer) data.get("id")).longValue());
|
||||
user.setName((String) data.get("name"));
|
||||
user.setAge((Integer) data.get("age"));
|
||||
log("/request/body/map", param);
|
||||
return user;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/request/body/user", consumes = APPLICATION_JSON_UTF8_VALUE)
|
||||
@Path("/request/body/user")
|
||||
@POST
|
||||
@Override
|
||||
@Consumes(APPLICATION_JSON_UTF8_VALUE)
|
||||
public Map<String, Object> requestBodyUser(@RequestBody User user) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("id", user.getId());
|
||||
map.put("name", user.getName());
|
||||
map.put("age", user.getAge());
|
||||
return map;
|
||||
}
|
||||
|
||||
private void log(String url, Object result) {
|
||||
String message = String.format("The client[%s] uses '%s' protocol to call %s : %s",
|
||||
RpcContext.getContext().getRemoteHostName(),
|
||||
RpcContext.getContext().getUrl() == null ? "N/A" : RpcContext.getContext().getUrl().getProtocol(),
|
||||
url,
|
||||
result
|
||||
);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import javax.ws.rs.FormParam;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* User Entity
|
||||
*/
|
||||
public class User implements Serializable {
|
||||
|
||||
@FormParam("id")
|
||||
private Long id;
|
||||
|
||||
@FormParam("name")
|
||||
private String name;
|
||||
|
||||
@FormParam("age")
|
||||
private Integer age;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", age=" + age +
|
||||
'}';
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue