Skip to content

Commit

Permalink
feature: add http support (apache#1746)
Browse files Browse the repository at this point in the history
  • Loading branch information
wxbty authored and slievrly committed Jan 19, 2020
1 parent 02ad392 commit 7c00335
Show file tree
Hide file tree
Showing 13 changed files with 948 additions and 1 deletion.
11 changes: 10 additions & 1 deletion all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@
<artifactId>seata-dubbo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-dubbo-alibaba</artifactId>
Expand Down Expand Up @@ -312,7 +317,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- the 3rd part -->
<dependency>
<groupId>io.netty</groupId>
Expand Down Expand Up @@ -616,6 +624,7 @@
<include>io.seata:seata-dubbo-alibaba</include>
<include>io.seata:seata-motan</include>
<include>io.seata:seata-grpc</include>
<include>io.seata:seata-http</include>
<include>io.seata:seata-rm</include>
<include>io.seata:seata-rm-datasource</include>
<include>io.seata:seata-sqlparser-core</include>
Expand Down
57 changes: 57 additions & 0 deletions integration/http/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.seata</groupId>
<artifactId>seata-parent</artifactId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>seata-http</artifactId>
<packaging>jar</packaging>
<name>seata-http ${project.version}</name>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-core</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>


</dependencies>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed 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 io.seata.integration.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Maps;
import io.seata.core.context.RootContext;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Abstract http executor.
*
* @author wangxb
*/
public abstract class AbstractHttpExecutor implements HttpExecutor {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractHttpExecutor.class);

@Override
public <T, K> K executePost(String host, String path, T paramObject, Class<K> returnType) throws IOException {

Args.notNull(returnType, "returnType");
Args.notNull(host, "host");
Args.notNull(path, "path");

CloseableHttpClient httpClient = initHttpClientInstance(paramObject);
HttpPost httpPost = new HttpPost(host + path);
StringEntity entity = null;
if (paramObject != null) {
String content;
if (paramObject instanceof String) {
String sParam = (String) paramObject;
JSONObject jsonObject = null;
try {
jsonObject = JSON.parseObject(sParam);
content = jsonObject.toJSONString();
} catch (JSONException e) {
//Interface provider process parse exception
if (LOGGER.isWarnEnabled()) {
LOGGER.warn(e.getMessage());
}
content = sParam;
}

} else {
content = JSON.toJSONString(paramObject);
}
entity = new StringEntity(content, ContentType.APPLICATION_JSON);
}

entity = buildEntity(entity, paramObject);
if (entity != null) {
httpPost.setEntity(entity);
}
Map<String, String> headers = Maps.newHashMap();

buildPostHeaders(headers, paramObject);
return wrapHttpExecute(returnType, httpClient, httpPost, headers);
}

@Override
public <K> K executeGet(String host, String path, Map<String, String> paramObject, Class<K> returnType) throws IOException {

Args.notNull(returnType, "returnType");
Args.notNull(host, "host");
Args.notNull(path, "path");

CloseableHttpClient httpClient = initHttpClientInstance(paramObject);

HttpGet httpGet = new HttpGet(initGetUrl(host, path, paramObject));
Map<String, String> headers = Maps.newHashMap();

buildGetHeaders(headers, paramObject);
return wrapHttpExecute(returnType, httpClient, httpGet, headers);
}

private <T> CloseableHttpClient initHttpClientInstance(T paramObject) {
CloseableHttpClient httpClient = HttpClients.createDefault();
buildClientEntity(httpClient, paramObject);
return httpClient;
}

protected abstract <T> void buildClientEntity(CloseableHttpClient httpClient, T paramObject);

private <K> K wrapHttpExecute(Class<K> returnType, CloseableHttpClient httpClient, HttpUriRequest httpUriRequest, Map<String, String> headers) throws IOException {
CloseableHttpResponse response;
String xid = RootContext.getXID();
if (xid != null) {
headers.put(RootContext.KEY_XID, xid);
}
if (!headers.isEmpty()) {
headers.keySet().forEach(key -> httpUriRequest.addHeader(key, headers.get(key)));
}
response = httpClient.execute(httpUriRequest);
int statusCode = response.getStatusLine().getStatusCode();
/** 2xx is success. */
if (statusCode < HttpStatus.SC_OK || statusCode > HttpStatus.SC_MULTI_STATUS) {
throw new RuntimeException("Failed to invoke the http method "
+ httpUriRequest.getURI() + " in the service "
+ ". return status by: " + response.getStatusLine().getStatusCode());
}

return convertResult(response, returnType);
}

protected abstract <T> void buildGetHeaders(Map<String, String> headers, T paramObject);

protected abstract String initGetUrl(String host, String path, Map<String, String> paramObject);


protected abstract <T> void buildPostHeaders(Map<String, String> headers, T t);

protected abstract <T> StringEntity buildEntity(StringEntity entity, T t);

protected abstract <K> K convertResult(HttpResponse response, Class<K> clazz);


public static Map<String, String> convertParamOfBean(Object sourceParam) {
return convert(JSON.parseObject(JSON.toJSONString(sourceParam, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue), Map.class));
}

public static <T> Map<String, String> convertParamOfJsonString(String jsonstr, Class<T> returnType) {
return convertParamOfBean(JSON.parseObject(jsonstr, returnType));
}

public static Map<String, String> convert(Map<String, Object> param) {
return param.keySet().stream().filter(key -> param.get(key) != null && param.get(key) != null).collect(Collectors.toMap(key -> key, key -> param.get(key).toString()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed 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 io.seata.integration.http;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;

/**
* Default http executor.
*
* @author wangxb
*/
public class DefaultHttpExecutor extends AbstractHttpExecutor {

private static DefaultHttpExecutor instance = new DefaultHttpExecutor();

private DefaultHttpExecutor() {
}

public static DefaultHttpExecutor getInstance() {
return instance;
}

@Override
public <T> void buildClientEntity(CloseableHttpClient httpClient, T paramObject) {

}

@Override
public <T> void buildGetHeaders(Map<String, String> headers, T paramObject) {

}


@Override
public String initGetUrl(String host, String path, Map<String, String> querys) {

if (querys.isEmpty()) {
return host + path;
}
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(host);
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}

StringBuilder sbQuery = new StringBuilder();
Iterator queryKeys = querys.entrySet().iterator();

while (queryKeys.hasNext()) {
Map.Entry<String, String> query = (Map.Entry) queryKeys.next();
if (0 < sbQuery.length()) {
sbQuery.append("&");
}

if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}

if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
try {
sbQuery.append(URLEncoder.encode(query.getValue(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}

if (sbQuery.length() > 0) {
sbUrl.append("?").append(sbQuery);
}

return sbUrl.toString();

}

@Override
public <T> void buildPostHeaders(Map<String, String> headers, T t) {

}

@Override
public <T> StringEntity buildEntity(StringEntity entity, T t) {
return entity;
}

@Override
public <K> K convertResult(HttpResponse response, Class<K> clazz) {


if (clazz == HttpResponse.class) {
return (K) response;
}
return null;
}

}
Loading

0 comments on commit 7c00335

Please sign in to comment.