Commit 9742023a authored by 侯力峰's avatar 侯力峰
Browse files

首次开源发布

parent 444abeb0
Pipeline #2755 canceled with stages
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
/target
/.settings
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>spatiotemporal-core</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
<filteredResources>
<filter>
<id>1705544878952</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
# spatiotemporal-core
时空开放平台核心模块
\ No newline at end of file
时空开放平台核心模块。以下简称核心模块(core),提供平台的最基础的核心公共功能。
## 引用方式
核心模块(core)一般被Web端核心模块(web-core)和独立组件核心模块(comp-core)所引用。
如果应用工程在不依赖Web端核心模块和独立组件核心模块时,希望单独引用核心模块可以在POM.xml文件的dependencies中加入以下依赖。
```xml
<dependency>
<groupId>cn.spatiotemporal</groupId>
<artifactId>spatiotemporal-core</artifactId>
<version>1.0.0-RELEASE</version>
</dependency>
```
注意选择合适的版本。
## 核心公共功能
### 分布式支持
为支持应用工程的分布式部署,以部署模块为单位自动生成本地实例ID,用以区分复数个相同模块的不同运行实例。
### 远程异步调用
基于RabbitMQ,提供统一便捷的远程异步调用支持。
远程异步调用指的是两个分开部署的模块之间,通过异步的方式进行调用并返回结果的过程。涉及异步调用的两个模块分别是客户端(调用者)和服务端(被调用者),调用命令通过MQ从客户端发往服务端,服务端执行完后将结果再通过MQ发往客户端,在客户端以回调的方式反馈给应用程序。
### Redis缓存支持
基于Spring的RedisTemplate提供公共的redis访问对象RedisDao,以支持对redis的各种数据类型的操作。
### FTP支持
基于Apache的FTPClient提供公共的FTP工具类,以支持对FTP的各种文件操作。
### 统一动态配置管理
基于Nacos提供统一的配置管理,支持工程动态载入配置。(目前暂时未完工)
## 公共工具类
### 日期工具类DateUtils
提供线程安全和时区管理的日期工具类,包含基本的日期格式转换等。
### Spring对象工具类SpringBeanUtils
提供用于查找、获取Spring所管理的对象实例的工具类,一般用于应用程序中手动获取各种Bean。
### 对象/Map转换工具类BeanMapUtils
提供用于Java对象和Map的相互快速转换的工具类。
### Json工具类FastJsonUtils
基于FastJson,提供json与Java对象之间进行快速序列化/反序列化的工具类。
### protostuff序列化器工具类ProtostuffUtils
提供protostuff序列化、反序列化的工具类。
protostuff是Google的一种跨平台的二进制编码格式,其序列化/反序列化的速度及存储空间均优于json。
### 字符串工具类StringUtils
基于apache-commons的StringUtils扩展。
## License
时空开放平台是一个基于[Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html)发布的开源软件。
# 版本1.0.0-RELEASE
发布时间: 2023/3/1
更新内容:
- 提供基于SpringBoot的分布式工程支持
- 提供基于RabbitMQ的跨模块异步调用
- 提供基于RedisTemplate的Redis缓存支持的公共访问类RedisDao
- 提供基于FtpClient的FTP访问工具FtpDao
- 提供以下公共工具类:
- DateUtils:日期工具类
- SpringBeanUtils:Spring对象工具类
- BeanMapUtils:对象/Map转换工具类
- FastJsonUtils:Json序列化/反序列化工具类
- ProtostuffUtils:Protostuff序列化/反序列化工具类
- StringUtils:字符串工具类(继承apache-commons的StringUtils)
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.spatiotemporal</groupId>
<artifactId>spatiotemporal-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>时空开放平台核心模块</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--rabbitmq-->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>net.jodah</groupId>
<artifactId>typetools</artifactId>
<version>0.5.0</version>
</dependency>
<!--JSON封装-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- 指定使用inzyme maven私有仓库 -->
<repositories>
<repository>
<id>public</id>
<name>inzyme-public</name>
<url>https://tydicdata.com/nexus/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<!-- 指定使用inzyme maven私有仓库 -->
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>inzyme-public</name>
<url>https://tydicdata.com/nexus/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<!-- 部署到inzyme maven私有仓库 -->
<distributionManagement>
<!-- 部署到快照版本的仓库,即测试版本仓库 -->
<snapshotRepository>
<id>inzyme-snapshots</id>
<url>https://tydicdata.com/nexus/repository/maven-snapshots/</url>
</snapshotRepository>
<!-- 部署到发行版本的仓库中,即正式版本仓库 -->
<repository>
<id>inzyme-releases</id>
<url>https://tydicdata.com/nexus/repository/maven-releases/</url>
</repository>
</distributionManagement>
</project>
package cn.spatiotemporal.core.async;
/**
* 异步调用相关常量
* @author marquis
*
*/
public class AsyncConstants {
/**
* 异步调用通讯用MQ
*/
public static final String ASYNC_RABBIT_MQ = "ASYNC_RABBIT_MQ";
/**
* 异步调用消息队列的Exchange
*/
public static final String ASYNC_RABBIT_MQ_EXCHANGE = "ASYNC_RABBIT_MQ_EXCHANGE";
/**
* 异步调用发送消息的Routing Key前缀
*/
public static final String ASYNC_CALL_ROUTING_KEY = "asyncCall.";
/**
* 异步调用返回消息的Routing Key前缀
*/
public static final String ASYNC_RETURN_ROUTING_KEY = "asyncReturn.";
/**
* 异步调用发送消息的RabbitMQ前缀
*/
public static final String ASYNC_CALL_MQ = "asyncCall.";
/**
* 异步调用返回消息的RabbitMQ前缀
*/
public static final String ASYNC_RETURN_MQ = "asyncReturn.";
}
package cn.spatiotemporal.core.async;
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
/**
* 异步调用 呼出参数消息体
* @author marquis
*
*/
@Getter
@Setter
public class AsyncParamMessage implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5277322553039584612L;
/**
* Caller ID调用者的模块ID
*/
private String cid;
/**
* 异步调用实例ID
*/
private String instanceId;
/**
* 调用对象Class名
*/
private String target;
/**
* 调用方法名
*/
private String method;
/**
* 调用参数json
*/
private String params;
}
package cn.spatiotemporal.core.async;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* 异步处理阶段
* @author marquis
*
*/
@Getter
@NoArgsConstructor
public enum AsyncProcessPhase {
PRE_CALL("准备异步调用指令"),
CALL_MSG_SENT("调用指令已发送"),
CALL_MSG_RECIEVED("调用指令已接收"),
CALL_MSG_ARRIVED("调用指令已送达"),
MID_RESULT_SENT("中间结果已发送"),
MID_RESULT_RECIEVED("中间结果已接收"),
MID_RESULT_ARRIVED("中间结果已送达"),
RESULT_SENT("结果已发送"),
RESULT_RECIEVED("结果已接收"),
RESULT_ARRIVED("结果已送达"),
POST_CALL("异步调用后处理(回调处理)"),
FINISHED("异步调用处理结束")
;
private String desc;
AsyncProcessPhase(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return this.desc;
}
}
package cn.spatiotemporal.core.async;
import lombok.Getter;
import lombok.Setter;
/**
* 异步返回值
* @author marquis
*
*/
@Getter
@Setter
public class AsyncResult<T> {
/**
* 调用ID
*/
private long callId;
/**
* 执行状态:成功、失败
*/
private boolean success;
/**
* 执行阶段
*/
private AsyncProcessPhase phase;
/**
* 返回信息
*/
private String message;
/**
* 回调
*/
private Callback<T> callback;
public void callback(Callback<T> callback) {
this.callback = callback;
}
}
package cn.spatiotemporal.core.async;
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
/**
* 异步调用 返回值消息体
* @author marquis
*
*/
@Getter
@Setter
public class AsyncReturnMessage implements Serializable{
/**
*
*/
private static final long serialVersionUID = 8106963297158624280L;
/**
* Executor ID执行者的模块ID
*/
private String eid;
/**
* 异步调用实例ID
*/
private String instanceId;
/**
* 返回值json
*/
private String returnValue;
/**
* 调用是否有异常
*/
private boolean hasException;
/**
* 错误信息
*/
private String errorMessage;
}
package cn.spatiotemporal.core.async;
import lombok.Data;
/**
* 异步调用状态
* @author marquis
*
*/
@Data
public class CallStatus {
/**
* 调用ID
*/
private long callId;
/**
* 执行状态:成功、失败
*/
private boolean success;
/**
* 执行阶段
*/
private AsyncProcessPhase phase;
/**
* 返回信息
*/
private String message;
}
package cn.spatiotemporal.core.async;
import cn.spatiotemporal.core.async.exception.AsyncProcessException;
/**
* 异步调用统一回调接口
* @author marquis
*
*/
@FunctionalInterface
public interface Callback<R> {
/**
* 执行成功时触发的回调处理,一般由业务代码自行编写
* @param status
* @param result
*/
public void onSuccess(AsyncResult<R> status, R result);
/**
* 执行失败时的回调处理,默认处理为抛出异常
* @param status
* @param result
*/
default void onError(AsyncResult<R> status, R result) {
throw new AsyncProcessException(status.getMessage());
}
/**
* 默认的回调处理
* @param status
* @param result
*/
default void onDone(AsyncResult<R> status, R result) {
if (status.isSuccess()) {
onSuccess(status, result);
} else {
onError(status, result);
}
}
}
package cn.spatiotemporal.core.async.agent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import cn.spatiotemporal.core.async.agent.InvocationHandlerFactory.MethodHandler;
import cn.spatiotemporal.core.async.agent.reflect.ReflectUtils;
import cn.spatiotemporal.core.async.agent.reflect.Target;
import cn.spatiotemporal.core.async.agent.reflect.Target.EmptyTarget;
import cn.spatiotemporal.core.async.agent.reflect.Types;
public class AsyncAgent {
private final InvocationHandlerFactory factory;
private String topic;
public AsyncAgent(InvocationHandlerFactory factory, String topic) {
this.factory = factory;
this.topic = topic;
}
public static AsyncAgent builder(String topic) {
Builder builder = new Builder();
return builder.build(topic);
}
public static String configKey(Class targetType, Method method) {
StringBuilder builder = new StringBuilder();
builder.append(targetType.getSimpleName());
builder.append('#').append(method.getName()).append('(');
for (Type param : method.getGenericParameterTypes()) {
param = Types.resolve(targetType, targetType, param);
builder.append(Types.getRawType(param).getSimpleName()).append(',');
}
if (method.getParameterTypes().length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.append(')').toString();
}
public <T> T newInstance(Target<T> target) {
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
List<AsyncMethodHandler> asyncMethodHandlers = new LinkedList<AsyncMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (ReflectUtils.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
AsyncMethodHandler handler = new AsyncMethodHandler(target, method);
asyncMethodHandlers.add(handler);
methodToHandler.put(method, handler);
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] { target.type() }, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
static class AsyncInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
AsyncInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = target;
this.dispatch = ReflectUtils.checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0])
: null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AsyncInvocationHandler) {
AsyncInvocationHandler other = (AsyncInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
}
public <T> T build(Class<T> type, String name, Map<String, String> attrs) {
EmptyTarget<T> target = EmptyTarget.create(type, name);
return newInstance(target);
}
public static class Builder {
// TODO 获取MQ配置
//private Producer producer = ConnectionFactory.openConnect().getProducer();
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
public AsyncAgent build(String topic) {
return new AsyncAgent(invocationHandlerFactory, topic);
}
}
}
package cn.spatiotemporal.core.async.agent;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import cn.spatiotemporal.core.async.AsyncResult;
import cn.spatiotemporal.core.async.agent.InvocationHandlerFactory.MethodHandler;
import cn.spatiotemporal.core.async.agent.reflect.Target;
/**
* 动态代理的各个方法的处理类
* @author marquis
*
*/
public class AsyncMethodHandler implements MethodHandler {
// handle is effectively final after bindTo has been called.
private Target<?> target;
MethodHandle handle;
public AsyncMethodHandler(Target<?> target, Method method) {
this.target = target;
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
try {
handle = lookup.findVirtual(method.getDeclaringClass(), method.getName(), methodType);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException("接口["+method.getDeclaringClass().getSimpleName()+"]里找不到对应的方法["+method.getName()+"]", e);
}
}
/**
* Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called
* on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler
*/
public void bindTo(Object proxy) {
handle = handle.bindTo(proxy);
}
/**
* Invoke this method. DefaultMethodHandler#bindTo must be called before the first
* time invoke is called.
*/
@Override
public Object invoke(Object[] argv) throws Throwable {
// 创建callId,异步调用的唯一标识
// 序列化参数,构造远程调用的消息头
// 通过MQ发送远程调用消息
// 构造异步调用状态,需要绑定callId进行存储
AsyncResult result = new AsyncResult();
return result;
}
}
package cn.spatiotemporal.core.async.agent;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import cn.spatiotemporal.core.async.agent.InvocationHandlerFactory.MethodHandler;
/**
* 动态代理的各个方法的处理类
* @author marquis
*
*/
public class DefaultMethodHandler implements MethodHandler {
// Uses Java 7 MethodHandle based reflection. As default methods will only exist when
// run on a Java 8 JVM this will not affect use on legacy JVMs.
// When Feign upgrades to Java 7, remove the @IgnoreJRERequirement annotation.
private final MethodHandle unboundHandle;
// handle is effectively final after bindTo has been called.
private MethodHandle handle;
public DefaultMethodHandler(Method defaultMethod) {
try {
Class<?> declaringClass = defaultMethod.getDeclaringClass();
Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP");
field.setAccessible(true);
Lookup lookup = (Lookup) field.get(null);
this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass);
} catch (NoSuchFieldException ex) {
throw new IllegalStateException(ex);
} catch (IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
/**
* Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called
* on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler
*/
public void bindTo(Object proxy) {
if(handle != null) {
throw new IllegalStateException("Attempted to rebind a default method handler that was already bound");
}
handle = unboundHandle.bindTo(proxy);
}
/**
* Invoke this method. DefaultMethodHandler#bindTo must be called before the first
* time invoke is called.
*/
@Override
public Object invoke(Object[] argv) throws Throwable {
if(handle == null) {
throw new IllegalStateException("Default method handler invoked before proxy has been bound.");
}
return handle.invokeWithArguments(argv);
}
}
package cn.spatiotemporal.core.async.agent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import cn.spatiotemporal.core.async.agent.reflect.Target;
public interface InvocationHandlerFactory {
InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
/**
* Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
* single method.
*/
interface MethodHandler {
Object invoke(Object[] argv) throws Throwable;
}
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new AsyncAgent.AsyncInvocationHandler(target, dispatch);
}
}
}
package cn.spatiotemporal.core.async.agent.reflect;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
public class ReflectUtils {
/**
* The HTTP Content-Length header field name.
*/
public static final String CONTENT_LENGTH = "Content-Length";
/**
* The HTTP Content-Encoding header field name.
*/
public static final String CONTENT_ENCODING = "Content-Encoding";
/**
* The HTTP Retry-After header field name.
*/
public static final String RETRY_AFTER = "Retry-After";
/**
* Value for the Content-Encoding header that indicates that GZIP encoding is in use.
*/
public static final String ENCODING_GZIP = "gzip";
/**
* Value for the Content-Encoding header that indicates that DEFLATE encoding is in use.
*/
public static final String ENCODING_DEFLATE = "deflate";
/**
* UTF-8: eight-bit UCS Transformation Format.
*/
public static final Charset UTF_8 = Charset.forName("UTF-8");
// com.google.common.base.Charsets
/**
* ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1).
*/
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
/**
* Type literal for {@code Map<String, ?>}.
*/
public static final Type MAP_STRING_WILDCARD =
new Types.ParameterizedTypeImpl(null, Map.class, String.class,
new Types.WildcardTypeImpl(new Type[]{Object.class}, new Type[0]));
private ReflectUtils() { // no instances
}
/**
* Copy of {@code com.google.common.base.Preconditions#checkArgument}.
*/
public static void checkArgument(boolean expression,
String errorMessageTemplate,
Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(
String.format(errorMessageTemplate, errorMessageArgs));
}
}
/**
* Copy of {@code com.google.common.base.Preconditions#checkNotNull}.
*/
public static <T> T checkNotNull(T reference,
String errorMessageTemplate,
Object... errorMessageArgs) {
if (reference == null) {
// If either of these parameters is null, the right thing happens anyway
throw new NullPointerException(
String.format(errorMessageTemplate, errorMessageArgs));
}
return reference;
}
/**
* Copy of {@code com.google.common.base.Preconditions#checkState}.
*/
public static void checkState(boolean expression,
String errorMessageTemplate,
Object... errorMessageArgs) {
if (!expression) {
throw new IllegalStateException(
String.format(errorMessageTemplate, errorMessageArgs));
}
}
/**
* Identifies a method as a default instance method.
*/
public static boolean isDefault(Method method) {
// Default methods are public non-abstract, non-synthetic, and non-static instance methods
// declared in an interface.
// method.isDefault() is not sufficient for our usage as it does not check
// for synthetic methods. As a result, it picks up overridden methods as well as actual default methods.
final int SYNTHETIC = 0x00001000;
return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) ==
Modifier.PUBLIC) && method.getDeclaringClass().isInterface();
}
/**
* Adapted from {@code com.google.common.base.Strings#emptyToNull}.
*/
public static String emptyToNull(String string) {
return string == null || string.isEmpty() ? null : string;
}
/**
* Adapted from {@code com.google.common.base.Strings#emptyToNull}.
*/
@SuppressWarnings("unchecked")
public static <T> T[] toArray(Iterable<? extends T> iterable, Class<T> type) {
Collection<T> collection;
if (iterable instanceof Collection) {
collection = (Collection<T>) iterable;
} else {
collection = new ArrayList<T>();
for (T element : iterable) {
collection.add(element);
}
}
T[] array = (T[]) Array.newInstance(type, collection.size());
return collection.toArray(array);
}
/**
* Returns an unmodifiable collection which may be empty, but is never null.
*/
public static <T> Collection<T> valuesOrEmpty(Map<String, Collection<T>> map, String key) {
return map.containsKey(key) && map.get(key) != null ? map.get(key) : Collections.<T>emptyList();
}
public static void ensureClosed(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ignored) { // NOPMD
}
}
}
/**
* Resolves the last type parameter of the parameterized {@code supertype}, based on the {@code
* genericContext}, into its upper bounds. <p/> Implementation copied from {@code
* retrofit.RestMethodInfo}.
*
* @param genericContext Ex. {@link java.lang.reflect.Field#getGenericType()}
* @param supertype Ex. {@code Decoder.class}
* @return in the example above, the type parameter of {@code Decoder}.
* @throws IllegalStateException if {@code supertype} cannot be resolved into a parameterized type
* using {@code context}.
*/
public static Type resolveLastTypeParameter(Type genericContext, Class<?> supertype)
throws IllegalStateException {
Type resolvedSuperType =
Types.getSupertype(genericContext, Types.getRawType(genericContext), supertype);
checkState(resolvedSuperType instanceof ParameterizedType,
"could not resolve %s into a parameterized type %s",
genericContext, supertype);
Type[] types = ParameterizedType.class.cast(resolvedSuperType).getActualTypeArguments();
for (int i = 0; i < types.length; i++) {
Type type = types[i];
if (type instanceof WildcardType) {
types[i] = ((WildcardType) type).getUpperBounds()[0];
}
}
return types[types.length - 1];
}
/**
* This returns well known empty values for well-known java types. This returns null for types not
* in the following list.
*
* <ul>
* <li>{@code [Bb]oolean}</li>
* <li>{@code byte[]}</li>
* <li>{@code Collection}</li>
* <li>{@code Iterator}</li>
* <li>{@code List}</li>
* <li>{@code Map}</li>
* <li>{@code Set}</li>
* </ul>
*
* <p/> When {@link Feign.Builder#decode404() decoding HTTP 404 status}, you'll need to teach
* decoders a default empty value for a type. This method cheaply supports typical types by only
* looking at the raw type (vs type hierarchy). Decorate for sophistication.
*/
public static Object emptyValueOf(Type type) {
return EMPTIES.get(Types.getRawType(type));
}
private static final Map<Class<?>, Object> EMPTIES;
static {
Map<Class<?>, Object> empties = new LinkedHashMap<Class<?>, Object>();
empties.put(boolean.class, false);
empties.put(Boolean.class, false);
empties.put(byte[].class, new byte[0]);
empties.put(Collection.class, Collections.emptyList());
empties.put(Iterator.class, new Iterator<Object>() { // Collections.emptyIterator is a 1.7 api
public boolean hasNext() {
return false;
}
public Object next() {
throw new NoSuchElementException();
}
public void remove() {
throw new IllegalStateException();
}
});
empties.put(List.class, Collections.emptyList());
empties.put(Map.class, Collections.emptyMap());
empties.put(Set.class, Collections.emptySet());
EMPTIES = Collections.unmodifiableMap(empties);
}
/**
* Adapted from {@code com.google.common.io.CharStreams.toString()}.
*/
public static String toString(Reader reader) throws IOException {
if (reader == null) {
return null;
}
try {
StringBuilder to = new StringBuilder();
CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
while (reader.read(buf) != -1) {
buf.flip();
to.append(buf);
buf.clear();
}
return to.toString();
} finally {
ensureClosed(reader);
}
}
/**
* Adapted from {@code com.google.common.io.ByteStreams.toByteArray()}.
*/
public static byte[] toByteArray(InputStream in) throws IOException {
checkNotNull(in, "in");
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
copy(in, out);
return out.toByteArray();
} finally {
ensureClosed(in);
}
}
/**
* Adapted from {@code com.google.common.io.ByteStreams.copy()}.
*/
private static long copy(InputStream from, OutputStream to)
throws IOException {
checkNotNull(from, "from");
checkNotNull(to, "to");
byte[] buf = new byte[BUF_SIZE];
long total = 0;
while (true) {
int r = from.read(buf);
if (r == -1) {
break;
}
to.write(buf, 0, r);
total += r;
}
return total;
}
static String decodeOrDefault(byte[] data, Charset charset, String defaultValue) {
if (data == null) {
return defaultValue;
}
checkNotNull(charset, "charset");
try {
return charset.newDecoder().decode(ByteBuffer.wrap(data)).toString();
} catch (CharacterCodingException ex) {
return defaultValue;
}
}
}
package cn.spatiotemporal.core.async.agent.reflect;
import cn.spatiotemporal.core.env.Module;
/**
* 空白的目标模板
* @author marquis
*
* @param <T>
*/
public interface Target<T> {
/*
* 异步调用接口类型
*/
Class<T> type();
/*
* 异步调用的MQ主题
*/
String topic();
/*
* 调用方的实例
*/
long client();
public static final class EmptyTarget<T> implements Target<T> {
private final Class<T> type;
private final String topic;
private final long client = Module.getInstanceId();
EmptyTarget(Class<T> type, String topic) {
this.type = type;
this.topic = topic;
}
public static <T> EmptyTarget<T> create(Class<T> type, String name) {
return new EmptyTarget<T>(type, name);
}
@Override
public Class<T> type() {
return type;
}
@Override
public String topic() {
return topic;
}
@Override
public long client() {
return client;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof EmptyTarget) {
EmptyTarget<?> other = (EmptyTarget) obj;
return type.equals(other.type) && topic.equals(other.topic);
}
return false;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + type.hashCode();
result = 31 * result + topic.hashCode();
return result;
}
@Override
public String toString() {
if (topic.equals("empty:" + type.getSimpleName())) {
return "EmptyTarget(type=" + type.getSimpleName() + ")";
}
return "EmptyTarget(type=" + type.getSimpleName() + ", name=" + topic + ")";
}
}
}
package cn.spatiotemporal.core.async.agent.reflect;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.NoSuchElementException;
public final class Types {
private static final Type[] EMPTY_TYPE_ARRAY = new Type[0];
private Types() {
// No instances.
}
public static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// Type is a normal class.
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class. Neal
// isn't either but
// suspects some pathological case related to nested classes exists.
Type rawType = parameterizedType.getRawType();
if (!(rawType instanceof Class)) {
throw new IllegalArgumentException();
}
return (Class<?>) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
// We could use the variable's bounds, but that won't work if there are
// multiple. Having a raw
// type that's more general than necessary is okay.
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <"
+ type + "> is of type " + className);
}
}
/**
* Returns true if {@code a} and {@code b} are equal.
*/
static boolean equals(Type a, Type b) {
if (a == b) {
return true; // Also handles (a == null && b == null).
} else if (a instanceof Class) {
return a.equals(b); // Class already specifies equals().
} else if (a instanceof ParameterizedType) {
if (!(b instanceof ParameterizedType)) {
return false;
}
ParameterizedType pa = (ParameterizedType) a;
ParameterizedType pb = (ParameterizedType) b;
return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType().equals(pb.getRawType())
&& Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
} else if (a instanceof GenericArrayType) {
if (!(b instanceof GenericArrayType)) {
return false;
}
GenericArrayType ga = (GenericArrayType) a;
GenericArrayType gb = (GenericArrayType) b;
return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
} else if (a instanceof WildcardType) {
if (!(b instanceof WildcardType)) {
return false;
}
WildcardType wa = (WildcardType) a;
WildcardType wb = (WildcardType) b;
return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
&& Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
} else if (a instanceof TypeVariable) {
if (!(b instanceof TypeVariable)) {
return false;
}
TypeVariable<?> va = (TypeVariable<?>) a;
TypeVariable<?> vb = (TypeVariable<?>) b;
return va.getGenericDeclaration() == vb.getGenericDeclaration() && va.getName().equals(vb.getName());
} else {
return false; // This isn't a type we support!
}
}
/**
* Returns the generic supertype for {@code supertype}. For example, given a
* class {@code
* IntegerSet}, the result for when supertype is {@code Set.class} is
* {@code Set<Integer>} and the result when the supertype is
* {@code Collection.class} is {@code Collection<Integer>}.
*/
static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
if (toResolve == rawType) {
return context;
}
// We skip searching through interfaces if unknown is an interface.
if (toResolve.isInterface()) {
Class<?>[] interfaces = rawType.getInterfaces();
for (int i = 0, length = interfaces.length; i < length; i++) {
if (interfaces[i] == toResolve) {
return rawType.getGenericInterfaces()[i];
} else if (toResolve.isAssignableFrom(interfaces[i])) {
return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
}
}
}
// Check our supertypes.
if (!rawType.isInterface()) {
while (rawType != Object.class) {
Class<?> rawSupertype = rawType.getSuperclass();
if (rawSupertype == toResolve) {
return rawType.getGenericSuperclass();
} else if (toResolve.isAssignableFrom(rawSupertype)) {
return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
}
rawType = rawSupertype;
}
}
// We can't resolve this further.
return toResolve;
}
private static int indexOf(Object[] array, Object toFind) {
for (int i = 0; i < array.length; i++) {
if (toFind.equals(array[i])) {
return i;
}
}
throw new NoSuchElementException();
}
private static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
private static int hashCodeOrZero(Object o) {
return o != null ? o.hashCode() : 0;
}
static String typeToString(Type type) {
return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
}
/**
* Returns the generic form of {@code supertype}. For example, if this is {@code
* ArrayList<String>}, this returns {@code Iterable<String>} given the input
* {@code
* Iterable.class}.
*
* @param supertype a superclass of, or interface implemented by, this.
*/
static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
if (!supertype.isAssignableFrom(contextRawType)) {
throw new IllegalArgumentException();
}
return resolve(context, contextRawType, getGenericSupertype(context, contextRawType, supertype));
}
public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
// This implementation is made a little more complicated in an attempt to avoid
// object-creation.
while (true) {
if (toResolve instanceof TypeVariable) {
TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
if (toResolve == typeVariable) {
return toResolve;
}
} else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
Class<?> original = (Class<?>) toResolve;
Type componentType = original.getComponentType();
Type newComponentType = resolve(context, contextRawType, componentType);
return componentType == newComponentType ? original : new GenericArrayTypeImpl(newComponentType);
} else if (toResolve instanceof GenericArrayType) {
GenericArrayType original = (GenericArrayType) toResolve;
Type componentType = original.getGenericComponentType();
Type newComponentType = resolve(context, contextRawType, componentType);
return componentType == newComponentType ? original : new GenericArrayTypeImpl(newComponentType);
} else if (toResolve instanceof ParameterizedType) {
ParameterizedType original = (ParameterizedType) toResolve;
Type ownerType = original.getOwnerType();
Type newOwnerType = resolve(context, contextRawType, ownerType);
boolean changed = newOwnerType != ownerType;
Type[] args = original.getActualTypeArguments();
for (int t = 0, length = args.length; t < length; t++) {
Type resolvedTypeArgument = resolve(context, contextRawType, args[t]);
if (resolvedTypeArgument != args[t]) {
if (!changed) {
args = args.clone();
changed = true;
}
args[t] = resolvedTypeArgument;
}
}
return changed ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args) : original;
} else if (toResolve instanceof WildcardType) {
WildcardType original = (WildcardType) toResolve;
Type[] originalLowerBound = original.getLowerBounds();
Type[] originalUpperBound = original.getUpperBounds();
if (originalLowerBound.length == 1) {
Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]);
if (lowerBound != originalLowerBound[0]) {
return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { lowerBound });
}
} else if (originalUpperBound.length == 1) {
Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
if (upperBound != originalUpperBound[0]) {
return new WildcardTypeImpl(new Type[] { upperBound }, EMPTY_TYPE_ARRAY);
}
}
return original;
} else {
return toResolve;
}
}
}
private static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
Class<?> declaredByRaw = declaringClassOf(unknown);
// We can't reduce this further.
if (declaredByRaw == null) {
return unknown;
}
Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
if (declaredBy instanceof ParameterizedType) {
int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
}
return unknown;
}
/**
* Returns the declaring class of {@code typeVariable}, or {@code null} if it
* was not declared by a class.
*/
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
}
private static void checkNotPrimitive(Type type) {
if (type instanceof Class<?> && ((Class<?>) type).isPrimitive()) {
throw new IllegalArgumentException();
}
}
static final class ParameterizedTypeImpl implements ParameterizedType {
private final Type ownerType;
private final Type rawType;
private final Type[] typeArguments;
ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
// Require an owner type if the raw type needs it.
if (rawType instanceof Class<?>
&& (ownerType == null) != (((Class<?>) rawType).getEnclosingClass() == null)) {
throw new IllegalArgumentException();
}
this.ownerType = ownerType;
this.rawType = rawType;
this.typeArguments = typeArguments.clone();
for (Type typeArgument : this.typeArguments) {
if (typeArgument == null) {
throw new NullPointerException();
}
checkNotPrimitive(typeArgument);
}
}
public Type[] getActualTypeArguments() {
return typeArguments.clone();
}
public Type getRawType() {
return rawType;
}
public Type getOwnerType() {
return ownerType;
}
@Override
public boolean equals(Object other) {
return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other);
}
@Override
public int hashCode() {
return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
result.append(typeToString(rawType));
if (typeArguments.length == 0) {
return result.toString();
}
result.append("<").append(typeToString(typeArguments[0]));
for (int i = 1; i < typeArguments.length; i++) {
result.append(", ").append(typeToString(typeArguments[i]));
}
return result.append(">").toString();
}
}
private static final class GenericArrayTypeImpl implements GenericArrayType {
private final Type componentType;
GenericArrayTypeImpl(Type componentType) {
this.componentType = componentType;
}
public Type getGenericComponentType() {
return componentType;
}
@Override
public boolean equals(Object o) {
return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o);
}
@Override
public int hashCode() {
return componentType.hashCode();
}
@Override
public String toString() {
return typeToString(componentType) + "[]";
}
}
/**
* The WildcardType interface supports multiple upper bounds and multiple lower
* bounds. We only support what the Java 6 language needs - at most one bound.
* If a lower bound is set, the upper bound must be Object.class.
*/
static final class WildcardTypeImpl implements WildcardType {
private final Type upperBound;
private final Type lowerBound;
WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
if (lowerBounds.length > 1) {
throw new IllegalArgumentException();
}
if (upperBounds.length != 1) {
throw new IllegalArgumentException();
}
if (lowerBounds.length == 1) {
if (lowerBounds[0] == null) {
throw new NullPointerException();
}
checkNotPrimitive(lowerBounds[0]);
if (upperBounds[0] != Object.class) {
throw new IllegalArgumentException();
}
this.lowerBound = lowerBounds[0];
this.upperBound = Object.class;
} else {
if (upperBounds[0] == null) {
throw new NullPointerException();
}
checkNotPrimitive(upperBounds[0]);
this.lowerBound = null;
this.upperBound = upperBounds[0];
}
}
public Type[] getUpperBounds() {
return new Type[] { upperBound };
}
public Type[] getLowerBounds() {
return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
}
@Override
public boolean equals(Object other) {
return other instanceof WildcardType && Types.equals(this, (WildcardType) other);
}
@Override
public int hashCode() {
// This equals Arrays.hashCode(getLowerBounds()) ^
// Arrays.hashCode(getUpperBounds()).
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
}
@Override
public String toString() {
if (lowerBound != null) {
return "? super " + typeToString(lowerBound);
}
if (upperBound == Object.class) {
return "?";
}
return "? extends " + typeToString(upperBound);
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment