0.2.1 增加gaea.app gaea.mongo模块功能

This commit is contained in:
2020-05-16 14:49:16 +08:00
parent 69dbf4afd1
commit 12d6332d6d
57 changed files with 992 additions and 135 deletions

View File

@@ -2,9 +2,9 @@ publishing {
publications {
mavenJava(MavenPublication) {
group 'com.synebula'
artifactId 'gaea.data'
version project.version
artifactId 'gaea'
version "$version"
from components.java
}
}
}
}

View File

@@ -1,15 +1,47 @@
package com.synebula.gaea.data.message
import java.util.Date
import java.util.*
open class Message<T> {
/**
*
* 用来统一Http返回消息类型通常使用json格式传递
*
* @param status http编码。200成功400错误500异常
* @tparam T 消息数据类型
*/
open class Message<T>(var status: Int = Status.Success) {
/**
* 传递的业务数据
*/
var data: T? = null
/**
* 附带提示消息
*/
var message = ""
/**
* 消息时间戳
*/
val timestamp: Long = Date().time
constructor(data: T) : this(Status.Success) {
this.data = data
}
constructor(status: Int, message: String) : this(status) {
this.message = message
}
constructor(status: Int, data: T, message: String) : this(status) {
this.data = data
this.message = message
}
open fun from(other: Message<T>) {
this.status = other.status
this.data = other.data
this.message = other.message
}
}

View File

@@ -0,0 +1,18 @@
package com.synebula.gaea.data.message
object Status {
/**
* 成功
*/
val Success = 200
/**
* 失败
*/
val Failure = 400
/**
* 错误
*/
val Error = 500
}

View File

@@ -1,37 +0,0 @@
package com.synebula.gaea.data.message.http
import com.synebula.gaea.data.message.Message
/**
*
* 用来统一Http返回消息类型通常使用json格式传递
*
* @param status http编码。200成功400错误500异常
* @tparam T 消息数据类型
*/
class HttpMessage<T>(var status: Int = HttpStatus.Success.code) : Message<T>() {
/**
* 附带提示消息
*/
var message = ""
constructor(data: T) : this(HttpStatus.Success.code) {
this.data = data
}
constructor(status: Int, message: String) : this(status) {
this.message = message
}
constructor(status: Int, data: T, message: String) : this(status) {
this.data = data
this.message = message
}
fun from(other: HttpMessage<T>) {
this.status = other.status
this.data = other.data
this.message = message
}
}

View File

@@ -1,36 +0,0 @@
package com.synebula.gaea.data.message.http
/**
* 状态类型。
*
* @author alex
* @version 0.0.1
* @since 2016年9月6日 下午3:27:55
*/
enum class HttpStatus(val code: Int) {
/**
* 成功
*/
Success(200),
/**
* 失败
*/
Failure(400),
/**
* 错误
*/
Error(500);
companion object {
fun valueOf(code: Int): HttpStatus {
return when (code) {
200 -> HttpStatus.Success
400 -> HttpStatus.Failure
else -> HttpStatus.Error
}
}
}
}

View File

@@ -0,0 +1,3 @@
package com.synebula.gaea.domain.model
abstract class AggregateRoot<TKey> : Entity<TKey>(), IAggregateRoot<TKey>

View File

@@ -0,0 +1,3 @@
package com.synebula.gaea.domain.model
abstract class Entity<TKey> : IEntity<TKey>

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.domain.model
/**
* 继承本接口,说明对象为聚合根。
*
* @author alex
**/
interface IAggregateRoot<TKey> : IEntity<TKey>

View File

@@ -0,0 +1,21 @@
package com.synebula.gaea.domain.model
/**
* 继承本接口,说明对象为实体类型。
*
* @author alex
*
* @param <TKey> 主键的类型。
**/
interface IEntity<TKey> {
/**
* 实体ID
*/
var id: TKey
/**
* 实体对象是否有效。
*/
var alive: Boolean
}

View File

@@ -0,0 +1,7 @@
package com.synebula.gaea.domain.model
/**
* 继承本接口,说明对象为值类型。
* @author alex
*/
interface IValue

View File

@@ -0,0 +1,3 @@
package com.synebula.gaea.domain.model.complex
abstract class ComplexAggregateRoot<TKey, TSecond> : ComplexEntity<TKey, TSecond>(), IComplexAggregateRoot<TKey, TSecond>

View File

@@ -0,0 +1,5 @@
package com.synebula.gaea.domain.model.complex
abstract class ComplexEntity<TKey, TSecond> : IComplexEntity<TKey, TSecond> {
}

View File

@@ -0,0 +1,9 @@
package com.synebula.gaea.domain.model.complex
/**
* 继承本接口,说明对象为聚合根。
*
* @param <TKey> 主键的类型。
* @author alex
*/
interface IComplexAggregateRoot<TKey, TSecond> : IComplexEntity<TKey, TSecond>

View File

@@ -0,0 +1,7 @@
package com.synebula.gaea.domain.model.complex
import com.synebula.gaea.domain.model.IEntity
interface IComplexEntity<TKey, TSecond> : IEntity<TKey> {
var secondary: TSecond
}

View File

@@ -0,0 +1,45 @@
package com.synebula.gaea.domain.repository
import com.synebula.gaea.domain.model.complex.IComplexAggregateRoot
/**
* 继承本接口表示对象为仓储类。
*
* @param <TAggregateRoot> this T is the parameter
* @author alex
*/
interface IComplexRepository<TAggregateRoot : IComplexAggregateRoot<TKey, TSecond>, TKey, TSecond> {
/**
* 插入单个对象。
*
* @param obj 需要插入的对象。
* @return 返回原对象如果对象ID为自增则补充自增ID。
*/
fun add(obj: TAggregateRoot)
/**
* 更新对象。
*
* @param obj 需要更新的对象。
* @return
*/
fun update(obj: TAggregateRoot)
/**
* 通过id删除该条数据
*
* @param key 对象ID。
* @param secondary 对象副主键。
* @return
*/
fun remove(key: TKey, secondary: TSecond)
/**
* 根据ID获取对象。
*
* @param key 对象ID。
* @return
*/
operator fun get(key: TKey, secondary: TSecond): TAggregateRoot
}

View File

@@ -0,0 +1,44 @@
package com.synebula.gaea.domain.repository
import com.synebula.gaea.domain.model.IAggregateRoot
/**
* 继承本接口表示对象为仓储类。
*
* @param <TAggregateRoot> this T is the parameter
* @author alex
*/
interface IRepository<TAggregateRoot : IAggregateRoot<TKey>, TKey> {
/**
* 插入单个对象。
*
* @param obj 需要插入的对象。
* @return 返回原对象如果对象ID为自增则补充自增ID。
*/
fun add(obj: TAggregateRoot)
/**
* 更新对象。
*
* @param obj 需要更新的对象。
* @return
*/
fun update(obj: TAggregateRoot)
/**
* 通过id删除该条数据
*
* @param id
* @return
*/
fun remove(id: TKey)
/**
* 根据ID获取对象。
*
* @param id 对象ID。
* @return
*/
operator fun get(id: TKey): TAggregateRoot
}

View File

@@ -0,0 +1,38 @@
package com.synebula.gaea.domain.repository
import com.synebula.gaea.domain.model.IAggregateRoot
interface ITypedRepository<TAggregateRoot : IAggregateRoot<TKey>, TKey> {
/**
* 插入单个对象。
*
* @param obj 需要插入的对象。
* @return 返回原对象如果对象ID为自增则补充自增ID。
*/
fun add(obj: TAggregateRoot)
/**
* 更新对象。
*
* @param obj 需要更新的对象。
* @return
*/
fun update(obj: TAggregateRoot)
/**
* 通过id删除该条数据
*
* @param id id
* @param clazz 操作数据的类型
*/
fun remove(id: TKey, clazz: Class<TAggregateRoot>)
/**
* 根据ID获取对象。
*
* @param id id
* @param clazz 操作数据的类型
* @return 聚合根
*/
fun get(id: TKey, clazz: Class<TAggregateRoot>): TAggregateRoot
}

View File

@@ -0,0 +1,30 @@
package com.synebula.gaea.domain.repository.context
import com.synebula.gaea.domain.model.IAggregateRoot
/**
* 继承自IUnitOfWork表示实现了工作单元模式的上下文接口。
*
* @author alex
*/
interface IContext : IUnitOfWork {
/**
* 将指定的聚合根标注为“新建”状态。
* @param obj
*/
fun <TType : IAggregateRoot<TKey>, TKey> add(obj: TType)
/**
* 将指定的聚合根标注为“更改”状态。
*
* @param obj
*/
fun <TType : IAggregateRoot<TKey>, TKey> update(obj: TType)
/**
* 将指定的聚合根标注为“删除”状态。
*
* @param obj
*/
fun <TType : IAggregateRoot<TKey>, TKey> remove(obj: TType)
}

View File

@@ -0,0 +1,26 @@
package com.synebula.gaea.domain.repository.context
/**
* 表示所有继承于该接口的类型都是Unit Of Work的一种实现。
*
* @author alex
*/
interface IUnitOfWork {
/**
* 获得一个boolean值该值表述了当前的Unit Of Work事务是否已被提交。
*
* @return 返回Unit Of Work事务是否已被提交。
*/
val isCommitted: Boolean
/**
* 提交当前的Unit Of Work事务。
*/
fun commit()
/**
* 回滚当前的Unit Of Work事务。
*/
fun rollback()
}

View File

@@ -0,0 +1,26 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 表示第一个规约对象和第二个规约对象反转的结果取与。
*
* @author alex
*
* @param <T>
* 规约对象的类型。
*/
class AndNotSpecification<T>
/**
* 构造一个新的混合规约对象。
*
* @param left
* 表达式左侧规约对象。
* @param right
* 表达式右侧规约对象。
*/
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
override fun isSatisfiedBy(obj: T): Boolean {
return left.isSatisfiedBy(obj) && NotSpecification(right).isSatisfiedBy(obj)
}
}

View File

@@ -0,0 +1,26 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 表示第一个规约对象和第二个规约对象取与。
*
* @author alex
*
* @param <T>
* 规约对象的类型。
*/
class AndSpecification<T>
/**
* 构造一个新的混合规约对象。
*
* @param left
* 表达式左侧规约对象。
* @param right
* 表达式右侧规约对象。
*/
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
override fun isSatisfiedBy(obj: T): Boolean {
return left.isSatisfiedBy(obj) && right.isSatisfiedBy(obj)
}
}

View File

@@ -0,0 +1,17 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 任何对象都返回真。
*
* @author alex
*
* @param <T>
* 规约对象的类型。
*/
class AnySpecification<T> : Specification<T>() {
override fun isSatisfiedBy(obj: T): Boolean {
return true
}
}

View File

@@ -0,0 +1,26 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 实现了接口ICompositeSpecification<T>,混合规约的基类。
* @author alex
*
* @param <T>
</T> */
abstract class CompositeSpecification<T>
/**
* 构造一个新的混合规约对象。
*
* @param left
* 表达式左侧规约对象。
* @param right
* 表达式右侧规约对象。
*/
(
/**
* 表达式左侧规约对象。
*/
override val left: ISpecification<T>,
/**
* 表达式右侧规约对象。
*/
override val right: ISpecification<T>) : Specification<T>(), ICompositeSpecification<T>

View File

@@ -0,0 +1,26 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 定义一个有两个规约对象混合规约接口。
*
* @author alex
*
* @param <T>
* 规约对象的类型。
*/
interface ICompositeSpecification<T> : ISpecification<T> {
/**
* 获取左侧规约对象。
*
* @return 返回左侧规约对象。
*/
val left: ISpecification<T>
/**
* 获取右侧规约对象。
*
* @return 返回右侧规约对象。
*/
val right: ISpecification<T>
}

View File

@@ -0,0 +1,62 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 声明规约对象的公共接口。
*
* @author alex
*
* @param <T>
*/
interface ISpecification<T> {
/**
* 判断是否符合条件。
*
* @param obj
* 规约类型的对象。
* @return 符合条件的返回True。
*/
fun isSatisfiedBy(obj: T): Boolean
/**
* 结合当前规约对象和另一规约对象进行与判断。
*
* @param other
* 需要进行与结合的另一个规约对象。
* @return 结合后的规约对象。
*/
fun and(other: ISpecification<T>): ISpecification<T>
/**
* 结合当前规约对象和另一规约对象进行或判断。
*
* @param other
* 需要进行或结合的另一个规约对象。
* @return 结合后的规约对象。
*/
fun or(other: ISpecification<T>): ISpecification<T>
/**
* 结合当前规约对象和另一规约对象进行与且非判断
*
* @param other
* 需要进行非结合的另一个规约对象。
* @return 结合后的规约对象。
*/
fun andNot(other: ISpecification<T>): ISpecification<T>
/**
* 结合当前规约对象和另一规约对象进行或非判断。
*
* @param other
* 需要进行或非结合的另一个规约对象。
* @return 结合后的规约对象。
*/
fun orNot(other: ISpecification<T>): ISpecification<T>
/**
* 反转当前规约的判断结果。
*
* @return 反转后的规约对象。
*/
operator fun not(): ISpecification<T>
}

View File

@@ -0,0 +1,17 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 任何对象都返回假。
*
* @author alex
*
* @param <T>
* 规约对象的类型。
*/
class NoneSpecification<T> : Specification<T>() {
override fun isSatisfiedBy(obj: T): Boolean {
return false
}
}

View File

@@ -0,0 +1,19 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 逆反规约。
*
* @author alex
*
* @param <T> 规约对象的类型。
*
* @param spec 需要逆反的规约对象。
*
*/
class NotSpecification<T>(private val spec: ISpecification<T>) : Specification<T>() {
override fun isSatisfiedBy(obj: T): Boolean {
return !spec.isSatisfiedBy(obj)
}
}

View File

@@ -0,0 +1,26 @@
package com.synebula.gaea.domain.repository.specifications
/**
* 表示第一个规约对象和第二个规约对象反转的结果取或。
*
* @author alex
*
* @param <T>
* 规约对象的类型。
*/
class OrNotSpecification<T>
/**
* 构造一个新的混合规约对象。
*
* @param left
* 表达式左侧规约对象。
* @param right
* 表达式右侧规约对象。
*/
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
override fun isSatisfiedBy(obj: T): Boolean {
return left.isSatisfiedBy(obj) || NotSpecification(right).isSatisfiedBy(obj)
}
}

View File

@@ -0,0 +1,17 @@
package com.synebula.gaea.domain.repository.specifications
class OrSpecification<T>
/**
* 构造一个新的混合规约对象。
*
* @param left
* 表达式左侧规约对象。
* @param right
* 表达式右侧规约对象。
*/
(left: ISpecification<T>, right: ISpecification<T>) : CompositeSpecification<T>(left, right) {
override fun isSatisfiedBy(obj: T): Boolean {
return left.isSatisfiedBy(obj) || right.isSatisfiedBy(obj)
}
}

View File

@@ -0,0 +1,27 @@
package com.synebula.gaea.domain.repository.specifications
abstract class Specification<T> : ISpecification<T> {
abstract override fun isSatisfiedBy(obj: T): Boolean
override fun and(other: ISpecification<T>): ISpecification<T> {
return AndSpecification(this, other)
}
override fun or(other: ISpecification<T>): ISpecification<T> {
return OrSpecification(this, other)
}
override fun andNot(other: ISpecification<T>): ISpecification<T> {
return AndNotSpecification(this, other)
}
override fun orNot(other: ISpecification<T>): ISpecification<T> {
return OrNotSpecification(this, other)
}
override fun not(): ISpecification<T> {
return NotSpecification(this)
}
}

View File

@@ -0,0 +1,14 @@
package com.synebula.gaea.domain.service
import java.util.*
/**
* 命令基础实现类发给service的命令。
*
* @author alex
* @version 0.1
* @since 2018 18-2-8
*/
class Command : ICommand {
override var timestamp = 0L
}

View File

@@ -0,0 +1,54 @@
package com.synebula.gaea.domain.service
import com.synebula.gaea.data.IObjectConverter
import com.synebula.gaea.data.message.Message
import com.synebula.gaea.domain.model.complex.IComplexAggregateRoot
import com.synebula.gaea.domain.repository.IComplexRepository
import com.synebula.gaea.log.ILogger
/**
* 复合主键的服务基类
*
* @author alex
* @version 0.1
* @since 2018 18-2-8
*/
open class ComplexService<TAggregateRoot : IComplexAggregateRoot<TKey, TSecond>, TKey, TSecond>
(var logger: ILogger, protected var repository: IComplexRepository<TAggregateRoot, TKey, TSecond>,
protected var converter: IObjectConverter, protected var aggregateRootClass: Class<TAggregateRoot>)
: IComplexService<TKey, TSecond> {
override fun add(command: ICommand): Message<Pair<TKey, TSecond>> {
val msg = Message<Pair<TKey, TSecond>>()
val root = this.convert(command)
repository.add(root)
msg.data = Pair<TKey, TSecond>(root.id, root.secondary)
return msg
}
override fun update(key: TKey, secondary: TSecond, command: ICommand) {
val root = this.convert(command)
root.id = key
root.secondary = secondary
repository.update(root)
}
override fun remove(key: TKey, secondary: TSecond) {
repository.remove(key, secondary)
}
/**
* 转换ICommand类型到聚合根类型默认实现根据需要进行覆写。
*
* @param command
* @return
*/
protected fun convert(command: ICommand): TAggregateRoot {
try {
return converter.convert(command, aggregateRootClass)
} catch (ex: Exception) {
throw RuntimeException("command not match aggregate root", ex)
}
}
}

View File

@@ -0,0 +1,15 @@
package com.synebula.gaea.domain.service
/**
* 命令接口发给service的命令。
*
* @author alex
* @version 0.1
* @since 2018 18-2-8
*/
interface ICommand {
/**
* 时间戳。
*/
var timestamp: Long
}

View File

@@ -0,0 +1,19 @@
package com.synebula.gaea.domain.service
import com.synebula.gaea.data.message.Message
/**
* class IFlatService
*
* @author alex
* @version 0.1
* @since 2018 18-2-8
*/
interface IComplexService<TKey, TSecond> {
fun add(command: ICommand): Message<Pair<TKey, TSecond>>
fun update(key: TKey, secondary: TSecond, command: ICommand)
fun remove(key: TKey, secondary: TSecond)
}

View File

@@ -0,0 +1,24 @@
package com.synebula.gaea.domain.service
import com.synebula.gaea.data.message.Message
import com.synebula.gaea.log.ILogger
/**
* 继承该接口,表明对象为领域服务。
* @author alex
* @version 0.0.1
* @since 2016年9月18日 下午2:23:15
*/
interface IService<TKey> {
/**
* 日志组件。若无日志组件则默认为NullLogger对象不会为null。
*/
var logger: ILogger
fun add(command: ICommand): Message<TKey>
fun update(key: TKey, command: ICommand)
fun remove(key: TKey)
}

View File

@@ -0,0 +1,55 @@
package com.synebula.gaea.domain.service
import com.synebula.gaea.data.IObjectConverter
import com.synebula.gaea.data.message.Message
import com.synebula.gaea.domain.model.IAggregateRoot
import com.synebula.gaea.domain.repository.IRepository
import com.synebula.gaea.log.ILogger
/**
* class FlatService
*
* @author alex
* @version 0.1
* @since 2018 18-2-8
*/
open class Service<TAggregateRoot : IAggregateRoot<TKey>, TKey>
(override var logger: ILogger,
protected var repository: IRepository<TAggregateRoot, TKey>,
protected var converter: IObjectConverter,
protected var aggregateRootClass: Class<TAggregateRoot>) : IService<TKey> {
override fun add(command: ICommand): Message<TKey> {
val msg = Message<TKey>()
val root = this.convert(command)
repository.add(root)
msg.data = root.id
return msg
}
override fun update(key: TKey, command: ICommand) {
val root = this.convert(command)
root.id = key
repository.update(root)
}
override fun remove(key: TKey) {
repository.remove(key)
}
/**
* 转换ICommand类型到聚合根类型默认实现根据需要进行覆写。
*
* @param command
* @return
*/
protected fun convert(command: ICommand): TAggregateRoot {
try {
return converter.convert(command, aggregateRootClass)
} catch (ex: Exception) {
throw RuntimeException("command not match aggregate root", ex)
}
}
}

View File

@@ -0,0 +1,23 @@
package com.synebula.gaea.io.scan
/**
*
* @author alex
* @version 0.0.1
* @since 2016年9月20日 上午10:50:06
*/
object ClassLoaderContext {
/**
* 获得class loader<br></br>
* 若当前线程class loader不存在取当前类的class loader
*
* @return 类加载器
*/
fun get(): ClassLoader {
var classLoader: ClassLoader? = Thread.currentThread().contextClassLoader
if (classLoader == null) {
classLoader = ClassLoaderContext::class.java.classLoader
}
return classLoader!!
}
}

View File

@@ -0,0 +1,49 @@
package com.synebula.gaea.io.scan
import java.io.IOException
import java.net.URL
import java.util.Enumeration
import java.util.HashSet
/**
*
* @author alex
* @version 0.0.1
* @since 2016年9月20日 下午2:59:47
*/
object ClassPath {
/**
* 获得Java ClassPath路径
* @return 获得Java ClassPath路径不包括 jre
*/
fun get(): Array<String> {
val paths = System.getProperty("java.class.path")
val separator = System.getProperty("path.separator")
return paths.split(separator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
}
/**
* 获得ClassPath
*
* @param packageName 包名称
* @return ClassPath路径字符串集合
*/
fun get(packageName: String): Array<String> {
val paths = HashSet<String>()
var path = packageName.replace(".", "/")
// 判断路径最后一个字符是不是"/",如果不是加上
path = if (path.lastIndexOf("/") != path.length - 1) "$path/" else path
var resources: Enumeration<URL>? = null
try {
resources = ClassLoaderContext.get().getResources(path)
} catch (e: IOException) {
}
while (resources!!.hasMoreElements()) {
paths.add(resources.nextElement().path)
}
return paths.toTypedArray()
}
}

View File

@@ -0,0 +1,202 @@
package com.synebula.gaea.io.scan
import java.io.File
import java.io.FileFilter
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import java.nio.charset.Charset
import java.util.Collections
import java.util.HashSet
import java.util.LinkedList
import java.util.jar.JarFile
/**
* 包扫描组件
* @author alex
* @version 0.0.1
* @since 2016年9月20日 下午2:56:50
*
* @param packageName 扫描的包名
*/
class ClassScanner(private var packageName: String) {
/**
* 类过滤器列表
*/
private var classFilters: Array<out IClassFilter>? = null
/**
* 扫描的类
*/
private val classes: MutableSet<Class<*>> = HashSet()
/**
* 文件过滤器
*/
private val fileFilter = FileFilter { file -> file.isDirectory || file.name.endsWith(".class") || file.name.endsWith(".jar") }
/**
* 构造方法。
*
* @param packageName 需要扫描的包名。
* @param classFilter
*/
constructor(packageName: String, vararg classFilter: IClassFilter) : this(packageName) {
this.classFilters = classFilter
}
/**
* 开始扫描。
*
* @return
*/
fun scan(): Collection<Class<*>> {
// 如果类集合不为空,先清空。
if (classes.isNotEmpty())
classes.clear()
var paths = ClassPath.get(packageName)// 先扫描当前类路径下文件
this.scanPath(paths)
if (classes.isEmpty()) {// 若当前类路径下文件没有,扫描java路径
paths = ClassPath.get()
this.scanPath(paths)
}
return classes
}
/*
* Private
*
*/
/**
* 填充满足条件的class 填充到 classes<br></br>
* 同时会判断给定的路径是否为Jar包内的路径如果是则扫描此Jar包
*
* @param paths Class文件路径或者所在目录Jar包路径
*/
private fun scanPath(paths: Array<String>) {
for (path in paths) {
try {
// 路径编码防止由于路径中空格和中文导致的Jar找不到
val realPath = URLDecoder.decode(path, Charset.defaultCharset().name())
val files = scanDirectory(realPath)
for (file in files) {
val fileName = file.toString()
if (fileName.contains(".jar") && !fileName.endsWith(".class"))
scanJar(file)
else if (fileName.endsWith(".class"))
scanClass(realPath, file)
}
} catch (e: UnsupportedEncodingException) {
}
}
}
/**
* 扫描文件,过滤添加类文件。
*
* @param path 扫描的磁盘路径
*/
private fun scanDirectory(path: String): Collection<File> {
var realPath = path
val files = LinkedList<File>()
val index = realPath.lastIndexOf(".jar")
if (index != -1) {// 首先判path是不是jar包下如果是说明当前path是文件
// jar文件
realPath = realPath.substring(0, index + 4)// 截取jar路径 [xxx/xxx.jar]
if (realPath.startsWith("file:"))
realPath = realPath.substring(5) // 去掉文件前缀[file:]
files.add(File(realPath))
} else {// 否则扫描文件夹下文件
val directory = LinkedList<File>()
directory.add(File(realPath))
var list: Array<File>?
var file: File
while (directory.size != 0) {
file = directory.poll()
list = file.listFiles(fileFilter)
if (list!!.isNotEmpty()) {
for (item in list) {
if (item.isDirectory)
directory.add(item)
else
files.add(item)
}
}
}
}
return files
}
/**
* 扫描jar文件过滤添加类文件。
*
* @param file 扫描的jar文件
*/
private fun scanJar(file: File) {
try {
val jar = JarFile(file)
val entries = jar.entries()
val list = Collections.list(entries)
for (entry in list) {
if (entry.name.endsWith(".class")) {
val className = entry.name.replace("/", ".").replace(".class", "")
filterClass(className)
}
}
jar.close()
} catch (ex: Throwable) {
}
}
/**
* 扫描.class类文件过滤添加类文件。
*
* @param path 路径
* @param file 文件
*/
private fun scanClass(path: String, file: File) {
var realPath = path
if (!realPath.endsWith(File.separator)) {
realPath += File.separator
}
var absolutePath = file.absolutePath
if (packageName.isEmpty())
absolutePath = absolutePath.substring(realPath.length)
val filePathWithDot = absolutePath.replace(File.separator, ".")
val subIndex = filePathWithDot.indexOf(packageName)
if (subIndex != -1) {
val endIndex = filePathWithDot.lastIndexOf(".class")
val className = filePathWithDot.substring(subIndex, endIndex)
filterClass(className)
}
}
/**
* 过滤符合要求的类,添加到结果列表中。
*
* @param className 类名
*/
private fun filterClass(className: String) {
if (className.startsWith(packageName)) {
try {
val clazz = Class.forName(className, false, ClassLoaderContext.get())
if (classFilters == null)
classes.add(clazz)
else {
for (iClassFilter in classFilters!!) {
if (iClassFilter.accept(clazz))
classes.add(clazz)
}
}
} catch (ex: Throwable) {
}
}
}
}

View File

@@ -0,0 +1,11 @@
package com.synebula.gaea.io.scan
/**
* 类过滤器,用于过滤不需要加载的类<br></br>
* @author alex
* @version 0.0.1
* @since 2016年9月18日 下午4:41:29
*/
interface IClassFilter {
fun accept(clazz: Class<*>): Boolean
}

View File

@@ -0,0 +1,42 @@
package com.synebula.gaea.query
/**
* 查询基接口。
*
* @author wxf
*/
interface IComplexQuery<TView, TKey, TSecond> {
/**
* 根据Key获取对象。
*
* @param key 对象Key。
* @return
*/
fun get(key: TKey, secondary: TSecond): TView
/**
* 根据实体类条件查询所有符合条件记录
*
* @param parameters 查询条件。
* @return list
*/
fun list(parameters: Map<String, Any>): List<TView>
/**
* 根据条件查询符合条件记录的数量
*
* @param parameters 查询条件。
* @return int
*/
fun count(parameters: Map<String, Any>): Int
/**
* 根据实体类条件查询所有符合条件记录(分页查询)
*
* @param param 分页条件
* @return
*/
fun paging(param: PagingParam): PagingData<TView>
}

View File

@@ -0,0 +1,42 @@
package com.synebula.gaea.query
/**
* 查询基接口。
*
* @author alex
*/
interface IQuery<TView, TKey> {
/**
* 根据Key获取对象。
*
* @param key 对象Key。
* @return
*/
fun get(key: TKey): TView?
/**
* 根据实体类条件查询所有符合条件记录
*
* @param params 查询条件。
* @return list
*/
fun list(params: Map<String, Any>?): List<TView>
/**
* 根据条件查询符合条件记录的数量
*
* @param params 查询条件。
* @return int
*/
fun count(params: Map<String, Any>?): Int
/**
* 根据实体类条件查询所有符合条件记录(分页查询)
*
* @param params 分页条件
* @return 分页数据
*/
fun paging(params: PagingParam): PagingData<TView>
}

View File

@@ -0,0 +1,20 @@
package com.synebula.gaea.query
/**
* class OrderType
*
* @author alex
* @version 0.1
* @since 2018 18-2-6
*/
enum class OrderType {
/**
* 升序排列
*/
ASC,
/**
* 降序排序
*/
DESC
}

View File

@@ -0,0 +1,67 @@
package com.synebula.gaea.query
/**
* 分页数据。
* 真实数据行无后台遍历需求,直接使用object类型表示即可.
*
* @author alex
*/
class PagingData<T> {
/**
* 页码从1开始。
*/
var page: Int = 0
/**
* 每页数据量。
*/
var size: Int = 0
/**
* 总数据量。
*/
var total: Int = 0
/**
* 数据索引从0开始。表示数据在总量的第几条。index = (page - 1) * size
*/
var index: Int
get() = (page - 1) * size
/**
* 结果数据。
*/
var data = listOf<T>()
/**
* 数据构造。
*
* @param page 页码从1开始。
* @param size 每页数据量。
*/
constructor(page: Int, size: Int) : super() {
this.page = page
this.size = size
this.index = (page - 1) * size + 1
}
/**
* 数据构造。
*
* @param page 页码从1开始。
* @param size 每页数据量。
* @param total 总数据量。
* @param data 结果数据。
*/
constructor(page: Int, size: Int, total: Int, data: List<T>) : super() {
this.page = page
this.size = size
this.total = total
this.index = (page - 1) * size + 1
this.data = data
}
}

View File

@@ -0,0 +1,44 @@
package com.synebula.gaea.query
/**
* class PagingParam
*
* @author alex
* @version 0.1
* @since 2018 18-2-5
*/
data class PagingParam(var page: Int = 1, var size: Int = 10) {
/**
* 数据索引从0开始。表示数据在总量的第几条。index = (page - 1) * size
*/
var index: Long = 0
get() = (page.toLong() - 1) * size.toLong()
private set
/**
* 排序条件。
*/
var orderBy: MutableMap<String, OrderType> = hashMapOf()
/**
* 查询条件。
*/
var parameters: MutableMap<String, Any> = hashMapOf()
/**
* 添加查询条件
*/
fun addParameter(field: String, value: Any): PagingParam {
parameters[field] = value
return this
}
/**
* 添加排序条件
*/
fun addOrderBy(field: String, type: OrderType = OrderType.ASC): PagingParam {
orderBy[field] = type
return this
}
}