chore(project): 添加项目配置文件和忽略规则

- 添加 Babel 配置文件支持 ES6+ 语法转换
- 添加 ESLint 忽略规则和配置文件
- 添加 Git 忽略规则文件
- 添加 Travis CI 配置文件
- 添加 1.4.2 版本变更日志文件
- 添加 Helm 图表辅助模板文件
- 添加 Helm 忽略规则文件
This commit is contained in:
2026-03-27 17:36:48 +08:00
commit c2453d6434
1703 changed files with 277582 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
/*
* 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 io.seata.common.util.CollectionUtils;
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.HashMap;
import java.util.Map;
/**
* 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 = new HashMap<>();
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 = new HashMap<>();
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.forEach(httpUriRequest::addHeader);
}
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 CollectionUtils.toStringMap(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));
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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 java.io.IOException;
import java.util.Map;
/**
* Http executor.
*
* @author wangxb
*/
public interface HttpExecutor {
/**
* Execute post k.
*
* @param <T> the type parameter
* @param <K> the type parameter
* @param host the host
* @param path the path
* @param paramObject the param object
* @param returnType the return type
* @return the k
* @throws IOException the io exception
*/
<T, K> K executePost(String host, String path, T paramObject, Class<K> returnType) throws IOException;
/**
* get method only support param type of Map<String,String>
*
* @param <K> the type parameter
* @param host the host
* @param path the path
* @param paramObject the param object
* @param returnType the return type
* @return K k
* @throws IOException the io exception
*/
<K> K executeGet(String host, String path, Map<String, String> paramObject, Class<K> returnType) throws IOException;
}

View File

@@ -0,0 +1,39 @@
/*
* 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 io.seata.core.context.RootContext;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Http exception handle.
*
* @author wangxb
*/
public class HttpHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse httpServletResponse, Object o, Exception e) {
XidResource.cleanXid(request.getHeader(RootContext.KEY_XID));
return null;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
* Springmvc Intercepter.
*
* @author wangxb
*/
public class TransactionPropagationInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionPropagationInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String xid = RootContext.getXID();
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("xid in RootContext[{}] xid in HttpContext[{}]", xid, rpcXid);
}
if (xid == null && rpcXid != null) {
RootContext.bind(rpcXid);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("bind[{}] to RootContext", rpcXid);
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
if (RootContext.inGlobalTransaction()) {
XidResource.cleanXid(request.getHeader(RootContext.KEY_XID));
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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 io.seata.common.util.StringUtils;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Xid handler.
*
* @author wangxb
*/
public class XidResource {
private static final Logger LOGGER = LoggerFactory.getLogger(XidResource.class);
public static void cleanXid(String rpcXid) {
String xid = RootContext.getXID();
if (xid != null) {
String unbindXid = RootContext.unbind();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("unbind[{}] from RootContext", unbindXid);
}
if (!StringUtils.equalsIgnoreCase(rpcXid, unbindXid)) {
LOGGER.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
LOGGER.warn("bind [{}] back to RootContext", unbindXid);
}
}
}
}
}

View File

@@ -0,0 +1,234 @@
/*
* 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.JSONObject;
import io.seata.core.context.RootContext;
import org.apache.http.HttpResponse;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.HashMap;
import java.util.Map;
import static io.seata.integration.http.AbstractHttpExecutor.convertParamOfBean;
import static io.seata.integration.http.AbstractHttpExecutor.convertParamOfJsonString;
/**
* @author wangxb
*/
class HttpTest {
private static final String host = "http://127.0.0.1:8081";
private static final String testException = "/testException";
private static final String getPath = "/testGet";
private static final String postPath = "/testPost";
public static final String XID = "127.0.0.1:8081:87654321";
private static final int PARAM_TYPE_MAP = 1;
private static final int PARAM_TYPE_BEAN = 2;
@Test
void testGetProviderXID() {
RootContext.bind(XID);
providerStart();
String result = consumerGetStart(PARAM_TYPE_MAP);
Assertions.assertEquals("Person{name='zhangsan', age=15}", result);
RootContext.unbind();
}
@Test
void testPostProviderXID() {
RootContext.bind(XID);
providerStart();
String result = consumerPostStart(PARAM_TYPE_MAP);
Assertions.assertEquals("Person{name='zhangsan', age=15}", result);
RootContext.unbind();
}
@Test
void testGetExceptionRemoveXID() {
RootContext.bind(XID);
providerStart();
String result = consumerGetExceptionStart();
Assertions.assertEquals("Callee remove local xid success", result);
RootContext.unbind();
}
public void providerStart() {
new MockWebServer().start(8081);
}
public static class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
private String consumerPostStart(int param_type) {
DefaultHttpExecutor httpExecuter = DefaultHttpExecutor.getInstance();
String str = "{\n" +
" \"name\":\"zhangsan\",\n" +
" \"age\":15\n" +
"}";
Person person = JSON.parseObject(str, Person.class);
Map<String, Object> map = new HashMap<>();
map.put("name", "zhangsan");
map.put("age", 15);
JSONObject json = new JSONObject();
json.put("name", "zhangsan");
json.put("age", 15);
//The body parameter of post supports the above types (str,person,map,json)
try {
HttpResponse response;
if (param_type == PARAM_TYPE_MAP) {
response = httpExecuter.executePost(host, postPath, map, HttpResponse.class);
} else if (param_type == PARAM_TYPE_BEAN) {
response = httpExecuter.executePost(host, postPath, person, HttpResponse.class);
} else {
response = httpExecuter.executePost(host, postPath, str, HttpResponse.class);
}
return readStreamAsStr(response.getEntity().getContent());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String consumerGetStart(int param_type) {
DefaultHttpExecutor httpExecuter = DefaultHttpExecutor.getInstance();
Map<String, String> params = new HashMap<>();
params.put("name", "zhangsan");
params.put("age", "15");
String str = "{\n" +
" \"name\":\"zhangsan\",\n" +
" \"age\":15\n" +
"}";
Person person = JSON.parseObject(str, Person.class);
try {
//support all type of parameter types
HttpResponse response;
if (param_type == PARAM_TYPE_MAP) {
response = httpExecuter.executeGet(host, getPath, params, HttpResponse.class);
} else if (param_type == PARAM_TYPE_BEAN) {
response = httpExecuter.executeGet(host, getPath, convertParamOfBean(person), HttpResponse.class);
} else {
response = httpExecuter.executeGet(host, getPath, convertParamOfJsonString(str, Person.class), HttpResponse.class);
}
return readStreamAsStr(response.getEntity().getContent());
} catch (IOException e) {
/* if in Travis CI env, only mock method call */
MockHttpExecuter mockHttpExecuter = new MockHttpExecuter();
try {
return mockHttpExecuter.executeGet(host, getPath, params, String.class);
} catch (IOException ex) {
throw new RuntimeException(e);
}
}
}
private String consumerGetExceptionStart() {
DefaultHttpExecutor httpExecuter = DefaultHttpExecutor.getInstance();
Map<String, String> params = new HashMap<>();
params.put("name", "zhangsan");
params.put("age", "15");
HttpResponse response;
try {
response = httpExecuter.executeGet(host, testException, params, HttpResponse.class);
return readStreamAsStr(response.getEntity().getContent());
} catch (IOException e) {
/* if in Travis CI inv, only mock method call */
MockHttpExecuter mockHttpExecuter = new MockHttpExecuter();
try {
return mockHttpExecuter.executeGet(host, testException, params, String.class);
} catch (IOException ex) {
throw new RuntimeException(e);
}
}
}
public static String readStreamAsStr(InputStream is) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
WritableByteChannel dest = Channels.newChannel(bos);
ReadableByteChannel src = Channels.newChannel(is);
ByteBuffer bb = ByteBuffer.allocate(4096);
while (src.read(bb) != -1) {
bb.flip();
dest.write(bb);
bb.clear();
}
src.close();
dest.close();
return new String(bos.toByteArray(), "UTF-8");
}
@Test
void convertParamOfJsonStringTest() {
String targetParam = "{name=zhangsan, age=15}";
String str = "{\n" +
" \"name\":\"zhangsan\",\n" +
" \"age\":15\n" +
"}";
Map<String, String> map = convertParamOfJsonString(str, Person.class);
Assertions.assertEquals(map.toString(), targetParam);
Person person = JSON.parseObject(str, Person.class);
map = convertParamOfBean(person);
Assertions.assertEquals(map.toString(), targetParam);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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 io.seata.core.context.RootContext;
import org.junit.jupiter.api.Assertions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author : wangxb
* @Description: Mock springmvc controller
*/
@Controller
public class MockController {
@RequestMapping("/testGet")
@ResponseBody
public String testGet(HttpTest.Person person) {
/* verify xid propagate by test case */
Assertions.assertEquals(HttpTest.XID,RootContext.getXID());
return person.toString();
}
@ResponseBody
@PostMapping("/testPost")
public String testPost(@RequestBody HttpTest.Person person) {
/* verify xid propagate by test case */
Assertions.assertEquals(HttpTest.XID,RootContext.getXID());
return person.toString();
}
@RequestMapping("/testException")
@ResponseBody
public String testException(HttpTest.Person person) {
throw new RuntimeException();
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 io.seata.core.context.RootContext;
import org.apache.http.HttpResponse;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.Args;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author : wangxb
*/
public class MockHttpExecuter extends AbstractHttpExecutor {
DefaultHttpExecutor httpExecutor = DefaultHttpExecutor.getInstance();
@Override
public <K> K executeGet(String host, String path, Map<String, String> paramObject, Class<K> returnType) throws IOException {
Args.notNull(host, "host");
Args.notNull(path, "path");
String getUrl = initGetUrl(host, path, paramObject);
Map<String, String> headers = new HashMap<>();
MockRequest mockRequest = new MockRequest(getUrl, headers, null, path, "get");
MockResponse mockResponse = new MockResponse(null);
String xid = RootContext.getXID();
if (xid != null) {
headers.put(RootContext.KEY_XID, xid);
}
MockWebServer webServer = new MockWebServer();
webServer.initServletMapping();
return (K) webServer.dispatch(mockRequest, mockResponse);
}
@Override
protected <T> void buildClientEntity(CloseableHttpClient httpClient, T paramObject) {
}
@Override
protected <T> void buildGetHeaders(Map<String, String> headers, T paramObject) {
}
@Override
protected String initGetUrl(String host, String path, Map<String, String> paramObject) {
return httpExecutor.initGetUrl(host, path, paramObject);
}
@Override
protected <T> void buildPostHeaders(Map<String, String> headers, T t) {
}
@Override
protected <T> StringEntity buildEntity(StringEntity entity, T t) {
return null;
}
@Override
protected <K> K convertResult(HttpResponse response, Class<K> clazz) {
return null;
}
}

View File

@@ -0,0 +1,315 @@
/*
* 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 io.seata.core.context.RootContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.security.Principal;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
/**
* @author : wangxb
*/
public class MockHttpServletRequest implements HttpServletRequest {
private MockRequest myRequest;
public MockHttpServletRequest(MockRequest myRequest) {
this.myRequest = myRequest;
}
@Override
public String getAuthType() {
return null;
}
@Override
public Cookie[] getCookies() {
return new Cookie[0];
}
@Override
public long getDateHeader(String name) {
return 0;
}
@Override
public String getHeader(String name) {
if (RootContext.KEY_XID.equals(name))
return myRequest.getHeader().get(RootContext.KEY_XID);
else {
return null;
}
}
@Override
public Enumeration getHeaders(String name) {
return null;
}
@Override
public Enumeration getHeaderNames() {
return null;
}
@Override
public int getIntHeader(String name) {
return 0;
}
@Override
public String getMethod() {
return null;
}
@Override
public String getPathInfo() {
return null;
}
@Override
public String getPathTranslated() {
return null;
}
@Override
public String getContextPath() {
return null;
}
@Override
public String getQueryString() {
return null;
}
@Override
public String getRemoteUser() {
return null;
}
@Override
public boolean isUserInRole(String role) {
return false;
}
@Override
public Principal getUserPrincipal() {
return null;
}
@Override
public String getRequestedSessionId() {
return null;
}
@Override
public String getRequestURI() {
return null;
}
@Override
public StringBuffer getRequestURL() {
return null;
}
@Override
public String getServletPath() {
return null;
}
@Override
public HttpSession getSession(boolean create) {
return null;
}
@Override
public HttpSession getSession() {
return null;
}
@Override
public boolean isRequestedSessionIdValid() {
return false;
}
@Override
public boolean isRequestedSessionIdFromCookie() {
return false;
}
@Override
public boolean isRequestedSessionIdFromURL() {
return false;
}
@Override
public boolean isRequestedSessionIdFromUrl() {
return false;
}
@Override
public Object getAttribute(String name) {
return null;
}
@Override
public Enumeration getAttributeNames() {
return null;
}
@Override
public String getCharacterEncoding() {
return null;
}
@Override
public void setCharacterEncoding(String env) {
}
@Override
public int getContentLength() {
return 0;
}
@Override
public String getContentType() {
return null;
}
@Override
public ServletInputStream getInputStream() {
return null;
}
@Override
public String getParameter(String name) {
return null;
}
@Override
public Enumeration getParameterNames() {
return null;
}
@Override
public String[] getParameterValues(String name) {
return new String[0];
}
@Override
public Map getParameterMap() {
return null;
}
@Override
public String getProtocol() {
return null;
}
@Override
public String getScheme() {
return null;
}
@Override
public String getServerName() {
return null;
}
@Override
public int getServerPort() {
return 0;
}
@Override
public BufferedReader getReader() {
return null;
}
@Override
public String getRemoteAddr() {
return null;
}
@Override
public String getRemoteHost() {
return null;
}
@Override
public void setAttribute(String name, Object o) {
}
@Override
public void removeAttribute(String name) {
}
@Override
public Locale getLocale() {
return null;
}
@Override
public Enumeration getLocales() {
return null;
}
@Override
public boolean isSecure() {
return false;
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
@Override
public String getRealPath(String path) {
return null;
}
@Override
public int getRemotePort() {
return 0;
}
@Override
public String getLocalName() {
return null;
}
@Override
public String getLocalAddr() {
return null;
}
@Override
public int getLocalPort() {
return 0;
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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 io.seata.core.context.RootContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @author : wangxb
*/
public class MockRequest {
private String url;
private Map<String, String> header = new HashMap<>();
private String body;
private String path;
private String method = "get";
public String getBody() {
return body;
}
public Map<String, String> getHeader() {
return header;
}
public String getMethod() {
return method;
}
public String getPath() {
return path;
}
public MockRequest(String url, Map<String, String> header, String body, String path,String method) {
this.url = url;
this.header = header;
this.body = body;
this.path = path;
this.method = method;
}
public MockRequest(InputStream inputStream) throws IOException {
String httpRequest = "";
byte[] httpRequestBytes = new byte[2048];
int length = 0;
if ((length = inputStream.read(httpRequestBytes)) > 0) {
httpRequest = new String(httpRequestBytes, 0, length);
}
String httpHead = httpRequest.split("\n")[0];
url = httpHead.split("\\s")[1];
String xid = httpRequest.split("\\n")[1];
if (xid.startsWith(RootContext.KEY_XID)) {
xid = xid.split(RootContext.KEY_XID + ":")[1].trim();
}
header.put(RootContext.KEY_XID, xid);
path = url.split("\\?")[0];
if (httpRequest.startsWith("POST")) {
body = httpRequest.split("\\n")[9];
method = "post";
}
}
public String getUrl() {
return url;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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 java.io.IOException;
import java.io.OutputStream;
/**
* @author : wangxb
*/
public class MockResponse {
private OutputStream outputStream;
public MockResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
public String write(String content) throws IOException {
StringBuilder httpResponse = new StringBuilder();
httpResponse.append("HTTP/1.1 200 OK\n")
.append("Content-Type:application/json\n")
.append("\r\n")
.append(content);
if (outputStream == null) {
//local call
return content;
}
else {
outputStream.write(httpResponse.toString().getBytes());
outputStream.close();
return "success";
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.JSONObject;
import io.seata.common.util.StringUtils;
import io.seata.core.context.RootContext;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import static io.seata.integration.http.AbstractHttpExecutor.convertParamOfJsonString;
/**
* @author : wangxb
*/
public class MockWebServer {
private Map<String, String> urlServletMap = new HashMap<>();
public void start(int port) {
initServletMapping();
new Thread(() -> {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
MockRequest myRequest = new MockRequest(inputStream);
MockResponse myResponse = new MockResponse(outputStream);
dispatch(myRequest, myResponse);
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void initServletMapping() {
for (ServletMapping servletMapping : ServletMapping.servletMappingList) {
urlServletMap.put(servletMapping.getPath(), servletMapping.getClazz() + "_" + servletMapping.getMethod());
}
}
public String dispatch(MockRequest myRequest, MockResponse mockResponse) {
String clazz = urlServletMap.get(myRequest.getPath()).split("_")[0];
String methodName = urlServletMap.get(myRequest.getPath()).split("_")[1];
HttpServletRequest request = new MockHttpServletRequest(myRequest);
try {
Class<MockController> myServletClass = (Class<MockController>) Class.forName(clazz);
MockController myServlet = myServletClass.newInstance();
HttpTest.Person person = boxing(myRequest);
Method method = myServletClass.getDeclaredMethod(methodName, HttpTest.Person.class);
/* mock request intercepter */
TransactionPropagationInterceptor intercepter = new TransactionPropagationInterceptor();
intercepter.preHandle(request, null, null);
Object result = method.invoke(myServlet, person);
return mockResponse.write(result.toString());
} catch (Exception e) {
HttpHandlerExceptionResolver resolver = new HttpHandlerExceptionResolver();
resolver.doResolveException(request, null, null, e);
if (RootContext.getXID() == null) {
try {
return mockResponse.write("Callee remove local xid success");
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
throw new RuntimeException(e);
}
}
private HttpTest.Person boxing(MockRequest myRequest) {
Map params = null;
if ("get".equals(myRequest.getMethod()))
params = getUrlParams(myRequest.getUrl());
else if ("post".equals(myRequest.getMethod())) {
params = getBodyParams(myRequest.getBody());
}
return JSONObject.parseObject(JSONObject.toJSONString(params), HttpTest.Person.class);
}
private Map<String, String> getBodyParams(String body) {
Map<String, String> map = convertParamOfJsonString(body, HttpTest.Person.class);
return map;
}
public static Map<String, Object> getUrlParams(String param) {
Map<String, Object> map = new HashMap<String, Object>(0);
if (StringUtils.isBlank(param)) {
return map;
}
String[] urlPath = param.split("\\?");
if (urlPath.length < 2) {
return map;
}
String[] params = urlPath[1].split("&");
for (int i = 0; i < params.length; i++) {
String[] p = params[i].split("=");
if (p.length == 2) {
map.put(p[0], p[1]);
}
}
return map;
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
/**
* @author : wangxb
*/
public class ServletMapping {
public static List<ServletMapping> servletMappingList = new ArrayList<>();
static {
servletMappingList.add(new ServletMapping("/testGet", "testGet", "io.seata.integration.http.MockController"));
servletMappingList.add(new ServletMapping("/testPost", "testPost", "io.seata.integration.http.MockController"));
servletMappingList.add(new ServletMapping("/testException", "testException", "io.seata.integration.http.MockController"));
}
private String method;
private String path;
private String clazz;
public ServletMapping(String path, String method, String clazz) {
this.method = method;
this.path = path;
this.clazz = clazz;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}