Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
时空开放平台
spatiotemporal-core
Commits
9742023a
Commit
9742023a
authored
Jan 18, 2024
by
侯力峰
Browse files
首次开源发布
parent
444abeb0
Pipeline
#2755
canceled with stages
Changes
52
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
.classpath
0 → 100644
View file @
9742023a
<?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>
.gitignore
0 → 100644
View file @
9742023a
/target
/.settings
.project
0 → 100644
View file @
9742023a
<?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>
README.md
View file @
9742023a
# 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
)
发布的开源软件。
ReleaseNotes.md
0 → 100644
View file @
9742023a
# 版本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)
pom.xml
0 → 100644
View file @
9742023a
<?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>
src/main/java/cn/spatiotemporal/core/async/AsyncConstants.java
0 → 100644
View file @
9742023a
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."
;
}
src/main/java/cn/spatiotemporal/core/async/AsyncParamMessage.java
0 → 100644
View file @
9742023a
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
;
}
src/main/java/cn/spatiotemporal/core/async/AsyncProcessPhase.java
0 → 100644
View file @
9742023a
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
;
}
}
src/main/java/cn/spatiotemporal/core/async/AsyncResult.java
0 → 100644
View file @
9742023a
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
;
}
}
src/main/java/cn/spatiotemporal/core/async/AsyncReturnMessage.java
0 → 100644
View file @
9742023a
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
;
}
src/main/java/cn/spatiotemporal/core/async/CallStatus.java
0 → 100644
View file @
9742023a
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
;
}
src/main/java/cn/spatiotemporal/core/async/Callback.java
0 → 100644
View file @
9742023a
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
);
}
}
}
src/main/java/cn/spatiotemporal/core/async/agent/AsyncAgent.java
0 → 100644
View file @
9742023a
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
);
}
}
}
src/main/java/cn/spatiotemporal/core/async/agent/AsyncMethodHandler.java
0 → 100644
View file @
9742023a
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
;
}
}
src/main/java/cn/spatiotemporal/core/async/agent/DefaultMethodHandler.java
0 → 100644
View file @
9742023a
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
);
}
}
src/main/java/cn/spatiotemporal/core/async/agent/InvocationHandlerFactory.java
0 → 100644
View file @
9742023a
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
);
}
}
}
src/main/java/cn/spatiotemporal/core/async/agent/reflect/ReflectUtils.java
0 → 100644
View file @
9742023a
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
;
}
}
}
src/main/java/cn/spatiotemporal/core/async/agent/reflect/Target.java
0 → 100644
View file @
9742023a
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
+
")"
;
}
}
}
src/main/java/cn/spatiotemporal/core/async/agent/reflect/Types.java
0 → 100644
View file @
9742023a
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
);
}
}
}
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment