From e050c7438d30d43087c2569f0aa43d8d5064aca8 Mon Sep 17 00:00:00 2001
From: RedKale <22250530@qq.com>
Date: Thu, 31 Mar 2016 18:40:13 +0800
Subject: [PATCH]
---
src/org/redkale/boot/Application.java | 656 +++++++
src/org/redkale/boot/ClassFilter.java | 431 ++++
src/org/redkale/boot/LogFileHandler.java | 276 +++
src/org/redkale/boot/NodeHttpServer.java | 145 ++
src/org/redkale/boot/NodeProtocol.java | 24 +
src/org/redkale/boot/NodeServer.java | 493 +++++
src/org/redkale/boot/NodeSncpServer.java | 79 +
src/org/redkale/boot/package-info.java | 4 +
src/org/redkale/convert/AnyEncoder.java | 42 +
src/org/redkale/convert/ArrayDecoder.java | 82 +
src/org/redkale/convert/ArrayEncoder.java | 78 +
.../redkale/convert/CollectionDecoder.java | 72 +
.../redkale/convert/CollectionEncoder.java | 66 +
src/org/redkale/convert/Convert.java | 29 +
src/org/redkale/convert/ConvertColumn.java | 47 +
.../redkale/convert/ConvertColumnEntry.java | 68 +
src/org/redkale/convert/ConvertColumns.java | 25 +
src/org/redkale/convert/ConvertEntity.java | 27 +
src/org/redkale/convert/ConvertException.java | 29 +
src/org/redkale/convert/ConvertFactory.java | 486 +++++
src/org/redkale/convert/ConvertType.java | 29 +
src/org/redkale/convert/DeMember.java | 85 +
src/org/redkale/convert/Decodeable.java | 30 +
src/org/redkale/convert/EnMember.java | 78 +
src/org/redkale/convert/Encodeable.java | 30 +
src/org/redkale/convert/MapDecoder.java | 79 +
src/org/redkale/convert/MapEncoder.java | 65 +
src/org/redkale/convert/ObjectDecoder.java | 234 +++
src/org/redkale/convert/ObjectEncoder.java | 278 +++
src/org/redkale/convert/Reader.java | 170 ++
src/org/redkale/convert/SimpledCoder.java | 45 +
src/org/redkale/convert/Writer.java | 205 ++
.../convert/bson/BsonByteBufferReader.java | 172 ++
.../convert/bson/BsonByteBufferWriter.java | 142 ++
src/org/redkale/convert/bson/BsonConvert.java | 214 ++
src/org/redkale/convert/bson/BsonFactory.java | 72 +
src/org/redkale/convert/bson/BsonReader.java | 324 +++
.../convert/bson/BsonSimpledCoder.java | 20 +
.../convert/bson/BsonStreamReader.java | 60 +
.../convert/bson/BsonStreamWriter.java | 48 +
src/org/redkale/convert/bson/BsonWriter.java | 301 +++
.../redkale/convert/bson/package-info.java | 4 +
.../convert/ext/BigIntegerSimpledCoder.java | 68 +
.../convert/ext/BoolArraySimpledCoder.java | 71 +
.../redkale/convert/ext/BoolSimpledCoder.java | 36 +
.../convert/ext/ByteArraySimpledCoder.java | 71 +
.../redkale/convert/ext/ByteSimpledCoder.java | 36 +
.../convert/ext/CharArraySimpledCoder.java | 69 +
.../convert/ext/CharSequenceSimpledCoder.java | 31 +
.../redkale/convert/ext/CharSimpledCoder.java | 35 +
.../ext/CompletionHandlerSimpledCoder.java | 36 +
.../convert/ext/DLongSimpledCoder.java | 71 +
.../redkale/convert/ext/DateSimpledCoder.java | 35 +
.../convert/ext/DoubleArraySimpledCoder.java | 69 +
.../convert/ext/DoubleSimpledCoder.java | 34 +
.../redkale/convert/ext/EnumSimpledCoder.java | 48 +
.../convert/ext/FloatArraySimpledCoder.java | 69 +
.../convert/ext/FloatSimpledCoder.java | 34 +
.../convert/ext/InetAddressSimpledCoder.java | 145 ++
.../convert/ext/IntArraySimpledCoder.java | 69 +
.../redkale/convert/ext/IntSimpledCoder.java | 34 +
.../convert/ext/LongArraySimpledCoder.java | 71 +
.../redkale/convert/ext/LongSimpledCoder.java | 35 +
.../convert/ext/NumberSimpledCoder.java | 36 +
.../convert/ext/PatternSimpledCoder.java | 40 +
.../convert/ext/ShortArraySimpledCoder.java | 69 +
.../convert/ext/ShortSimpledCoder.java | 34 +
.../convert/ext/StringArraySimpledCoder.java | 69 +
.../convert/ext/StringSimpledCoder.java | 34 +
.../redkale/convert/ext/TypeSimpledCoder.java | 46 +
.../redkale/convert/ext/URISimpledCoder.java | 39 +
.../redkale/convert/ext/URLSimpledCoder.java | 39 +
src/org/redkale/convert/ext/package-info.java | 4 +
.../convert/json/JsonByteBufferReader.java | 336 ++++
.../convert/json/JsonByteBufferWriter.java | 352 ++++
src/org/redkale/convert/json/JsonConvert.java | 207 ++
src/org/redkale/convert/json/JsonFactory.java | 74 +
src/org/redkale/convert/json/JsonReader.java | 582 ++++++
.../convert/json/JsonSimpledCoder.java | 20 +
.../convert/json/JsonStreamReader.java | 40 +
.../convert/json/JsonStreamWriter.java | 151 ++
src/org/redkale/convert/json/JsonWriter.java | 381 ++++
.../redkale/convert/json/package-info.java | 4 +
src/org/redkale/convert/package-info.java | 4 +
src/org/redkale/net/AsyncConnection.java | 575 ++++++
src/org/redkale/net/Context.java | 133 ++
src/org/redkale/net/PrepareRunner.java | 99 +
src/org/redkale/net/PrepareServlet.java | 92 +
src/org/redkale/net/ProtocolServer.java | 184 ++
src/org/redkale/net/Request.java | 117 ++
src/org/redkale/net/Response.java | 230 +++
src/org/redkale/net/Server.java | 199 ++
src/org/redkale/net/Servlet.java | 33 +
src/org/redkale/net/Transport.java | 253 +++
src/org/redkale/net/WorkThread.java | 32 +
.../redkale/net/http/BasedHttpServlet.java | 325 +++
src/org/redkale/net/http/HttpContext.java | 56 +
.../redkale/net/http/HttpPrepareServlet.java | 144 ++
src/org/redkale/net/http/HttpRequest.java | 813 ++++++++
.../redkale/net/http/HttpResourceServlet.java | 262 +++
src/org/redkale/net/http/HttpResponse.java | 739 +++++++
src/org/redkale/net/http/HttpServer.java | 131 ++
src/org/redkale/net/http/HttpServlet.java | 31 +
src/org/redkale/net/http/MimeType.java | 199 ++
src/org/redkale/net/http/MultiContext.java | 245 +++
src/org/redkale/net/http/MultiPart.java | 116 ++
src/org/redkale/net/http/WebInitParam.java | 26 +
src/org/redkale/net/http/WebServlet.java | 31 +
src/org/redkale/net/http/WebSocket.java | 453 +++++
src/org/redkale/net/http/WebSocketBinary.java | 24 +
src/org/redkale/net/http/WebSocketEngine.java | 101 +
src/org/redkale/net/http/WebSocketGroup.java | 139 ++
src/org/redkale/net/http/WebSocketNode.java | 235 +++
src/org/redkale/net/http/WebSocketPacket.java | 123 ++
src/org/redkale/net/http/WebSocketRunner.java | 526 +++++
.../redkale/net/http/WebSocketServlet.java | 148 ++
src/org/redkale/net/http/package-info.java | 4 +
src/org/redkale/net/package-info.java | 4 +
src/org/redkale/net/sncp/ServiceWrapper.java | 143 ++
src/org/redkale/net/sncp/Sncp.java | 1102 +++++++++++
src/org/redkale/net/sncp/SncpClient.java | 544 ++++++
src/org/redkale/net/sncp/SncpContext.java | 29 +
src/org/redkale/net/sncp/SncpDyn.java | 29 +
src/org/redkale/net/sncp/SncpDynServlet.java | 428 ++++
.../redkale/net/sncp/SncpPrepareServlet.java | 69 +
src/org/redkale/net/sncp/SncpRequest.java | 139 ++
src/org/redkale/net/sncp/SncpResponse.java | 78 +
src/org/redkale/net/sncp/SncpServer.java | 73 +
src/org/redkale/net/sncp/SncpServlet.java | 36 +
src/org/redkale/net/sncp/package-info.java | 4 +
.../redkale/service/CacheSourceService.java | 492 +++++
.../service/DataCacheListenerService.java | 45 +
.../service/DataSQLListenerService.java | 128 ++
.../redkale/service/DataSourceService.java | 561 ++++++
src/org/redkale/service/DynAttachment.java | 25 +
src/org/redkale/service/DynCall.java | 25 +
src/org/redkale/service/DynRemote.java | 24 +
src/org/redkale/service/DynSourceAddress.java | 25 +
src/org/redkale/service/DynTargetAddress.java | 25 +
src/org/redkale/service/LocalService.java | 25 +
src/org/redkale/service/MultiRun.java | 33 +
src/org/redkale/service/RetResult.java | 116 ++
src/org/redkale/service/Service.java | 47 +
.../redkale/service/WebSocketNodeService.java | 82 +
src/org/redkale/service/package-info.java | 4 +
src/org/redkale/source/CacheSource.java | 86 +
src/org/redkale/source/DataCacheListener.java | 22 +
.../source/DataCallArrayAttribute.java | 60 +
src/org/redkale/source/DataCallAttribute.java | 73 +
src/org/redkale/source/DataDefaultSource.java | 1738 +++++++++++++++++
src/org/redkale/source/DataSQLListener.java | 21 +
src/org/redkale/source/DataSource.java | 325 +++
.../redkale/source/DistributeGenerator.java | 29 +
src/org/redkale/source/DistributeTables.java | 31 +
src/org/redkale/source/EntityCache.java | 657 +++++++
src/org/redkale/source/EntityInfo.java | 381 ++++
src/org/redkale/source/FilterBean.java | 16 +
src/org/redkale/source/FilterColumn.java | 49 +
src/org/redkale/source/FilterExpress.java | 63 +
src/org/redkale/source/FilterFunc.java | 25 +
src/org/redkale/source/FilterGroup.java | 69 +
src/org/redkale/source/FilterJoinColumn.java | 41 +
src/org/redkale/source/FilterJoinNode.java | 316 +++
src/org/redkale/source/FilterNode.java | 1256 ++++++++++++
src/org/redkale/source/FilterNodeBean.java | 356 ++++
src/org/redkale/source/FilterValue.java | 66 +
src/org/redkale/source/Flipper.java | 120 ++
src/org/redkale/source/JDBCPoolSource.java | 260 +++
src/org/redkale/source/Range.java | 325 +++
src/org/redkale/source/VirtualEntity.java | 24 +
src/org/redkale/source/package-info.java | 4 +
src/org/redkale/util/AnyValue.java | 446 +++++
src/org/redkale/util/AsmMethodVisitor.java | 152 ++
src/org/redkale/util/Attribute.java | 555 ++++++
src/org/redkale/util/AutoLoad.java | 27 +
src/org/redkale/util/ByteArray.java | 194 ++
src/org/redkale/util/Creator.java | 358 ++++
src/org/redkale/util/DLong.java | 119 ++
src/org/redkale/util/LogLevel.java | 25 +
src/org/redkale/util/ObjectPool.java | 102 +
src/org/redkale/util/Reproduce.java | 141 ++
src/org/redkale/util/ResourceFactory.java | 451 +++++
src/org/redkale/util/ResourceType.java | 26 +
src/org/redkale/util/SelectColumn.java | 139 ++
src/org/redkale/util/Sheet.java | 92 +
src/org/redkale/util/TypeToken.java | 192 ++
src/org/redkale/util/Utility.java | 578 ++++++
src/org/redkale/util/package-info.java | 4 +
src/org/redkale/watch/WatchFactory.java | 100 +
src/org/redkale/watch/WatchNode.java | 22 +
src/org/redkale/watch/WatchNumber.java | 52 +
src/org/redkale/watch/WatchSupplier.java | 48 +
src/org/redkale/watch/Watchable.java | 37 +
src/org/redkale/watch/package-info.java | 4 +
194 files changed, 31002 insertions(+)
create mode 100644 src/org/redkale/boot/Application.java
create mode 100644 src/org/redkale/boot/ClassFilter.java
create mode 100644 src/org/redkale/boot/LogFileHandler.java
create mode 100644 src/org/redkale/boot/NodeHttpServer.java
create mode 100644 src/org/redkale/boot/NodeProtocol.java
create mode 100644 src/org/redkale/boot/NodeServer.java
create mode 100644 src/org/redkale/boot/NodeSncpServer.java
create mode 100644 src/org/redkale/boot/package-info.java
create mode 100644 src/org/redkale/convert/AnyEncoder.java
create mode 100644 src/org/redkale/convert/ArrayDecoder.java
create mode 100644 src/org/redkale/convert/ArrayEncoder.java
create mode 100644 src/org/redkale/convert/CollectionDecoder.java
create mode 100644 src/org/redkale/convert/CollectionEncoder.java
create mode 100644 src/org/redkale/convert/Convert.java
create mode 100644 src/org/redkale/convert/ConvertColumn.java
create mode 100644 src/org/redkale/convert/ConvertColumnEntry.java
create mode 100644 src/org/redkale/convert/ConvertColumns.java
create mode 100644 src/org/redkale/convert/ConvertEntity.java
create mode 100644 src/org/redkale/convert/ConvertException.java
create mode 100644 src/org/redkale/convert/ConvertFactory.java
create mode 100644 src/org/redkale/convert/ConvertType.java
create mode 100644 src/org/redkale/convert/DeMember.java
create mode 100644 src/org/redkale/convert/Decodeable.java
create mode 100644 src/org/redkale/convert/EnMember.java
create mode 100644 src/org/redkale/convert/Encodeable.java
create mode 100644 src/org/redkale/convert/MapDecoder.java
create mode 100644 src/org/redkale/convert/MapEncoder.java
create mode 100644 src/org/redkale/convert/ObjectDecoder.java
create mode 100644 src/org/redkale/convert/ObjectEncoder.java
create mode 100644 src/org/redkale/convert/Reader.java
create mode 100644 src/org/redkale/convert/SimpledCoder.java
create mode 100644 src/org/redkale/convert/Writer.java
create mode 100644 src/org/redkale/convert/bson/BsonByteBufferReader.java
create mode 100644 src/org/redkale/convert/bson/BsonByteBufferWriter.java
create mode 100644 src/org/redkale/convert/bson/BsonConvert.java
create mode 100644 src/org/redkale/convert/bson/BsonFactory.java
create mode 100644 src/org/redkale/convert/bson/BsonReader.java
create mode 100644 src/org/redkale/convert/bson/BsonSimpledCoder.java
create mode 100644 src/org/redkale/convert/bson/BsonStreamReader.java
create mode 100644 src/org/redkale/convert/bson/BsonStreamWriter.java
create mode 100644 src/org/redkale/convert/bson/BsonWriter.java
create mode 100644 src/org/redkale/convert/bson/package-info.java
create mode 100644 src/org/redkale/convert/ext/BigIntegerSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/BoolArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/BoolSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/ByteArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/ByteSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/CharArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/CharSequenceSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/CharSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/DLongSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/DateSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/DoubleArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/DoubleSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/EnumSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/FloatArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/FloatSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/InetAddressSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/IntArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/IntSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/LongArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/LongSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/NumberSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/PatternSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/ShortArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/ShortSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/StringArraySimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/StringSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/TypeSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/URISimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/URLSimpledCoder.java
create mode 100644 src/org/redkale/convert/ext/package-info.java
create mode 100644 src/org/redkale/convert/json/JsonByteBufferReader.java
create mode 100644 src/org/redkale/convert/json/JsonByteBufferWriter.java
create mode 100644 src/org/redkale/convert/json/JsonConvert.java
create mode 100644 src/org/redkale/convert/json/JsonFactory.java
create mode 100644 src/org/redkale/convert/json/JsonReader.java
create mode 100644 src/org/redkale/convert/json/JsonSimpledCoder.java
create mode 100644 src/org/redkale/convert/json/JsonStreamReader.java
create mode 100644 src/org/redkale/convert/json/JsonStreamWriter.java
create mode 100644 src/org/redkale/convert/json/JsonWriter.java
create mode 100644 src/org/redkale/convert/json/package-info.java
create mode 100644 src/org/redkale/convert/package-info.java
create mode 100644 src/org/redkale/net/AsyncConnection.java
create mode 100644 src/org/redkale/net/Context.java
create mode 100644 src/org/redkale/net/PrepareRunner.java
create mode 100644 src/org/redkale/net/PrepareServlet.java
create mode 100644 src/org/redkale/net/ProtocolServer.java
create mode 100644 src/org/redkale/net/Request.java
create mode 100644 src/org/redkale/net/Response.java
create mode 100644 src/org/redkale/net/Server.java
create mode 100644 src/org/redkale/net/Servlet.java
create mode 100644 src/org/redkale/net/Transport.java
create mode 100644 src/org/redkale/net/WorkThread.java
create mode 100644 src/org/redkale/net/http/BasedHttpServlet.java
create mode 100644 src/org/redkale/net/http/HttpContext.java
create mode 100644 src/org/redkale/net/http/HttpPrepareServlet.java
create mode 100644 src/org/redkale/net/http/HttpRequest.java
create mode 100644 src/org/redkale/net/http/HttpResourceServlet.java
create mode 100644 src/org/redkale/net/http/HttpResponse.java
create mode 100644 src/org/redkale/net/http/HttpServer.java
create mode 100644 src/org/redkale/net/http/HttpServlet.java
create mode 100644 src/org/redkale/net/http/MimeType.java
create mode 100644 src/org/redkale/net/http/MultiContext.java
create mode 100644 src/org/redkale/net/http/MultiPart.java
create mode 100644 src/org/redkale/net/http/WebInitParam.java
create mode 100644 src/org/redkale/net/http/WebServlet.java
create mode 100644 src/org/redkale/net/http/WebSocket.java
create mode 100644 src/org/redkale/net/http/WebSocketBinary.java
create mode 100644 src/org/redkale/net/http/WebSocketEngine.java
create mode 100644 src/org/redkale/net/http/WebSocketGroup.java
create mode 100644 src/org/redkale/net/http/WebSocketNode.java
create mode 100644 src/org/redkale/net/http/WebSocketPacket.java
create mode 100644 src/org/redkale/net/http/WebSocketRunner.java
create mode 100644 src/org/redkale/net/http/WebSocketServlet.java
create mode 100644 src/org/redkale/net/http/package-info.java
create mode 100644 src/org/redkale/net/package-info.java
create mode 100644 src/org/redkale/net/sncp/ServiceWrapper.java
create mode 100644 src/org/redkale/net/sncp/Sncp.java
create mode 100644 src/org/redkale/net/sncp/SncpClient.java
create mode 100644 src/org/redkale/net/sncp/SncpContext.java
create mode 100644 src/org/redkale/net/sncp/SncpDyn.java
create mode 100644 src/org/redkale/net/sncp/SncpDynServlet.java
create mode 100644 src/org/redkale/net/sncp/SncpPrepareServlet.java
create mode 100644 src/org/redkale/net/sncp/SncpRequest.java
create mode 100644 src/org/redkale/net/sncp/SncpResponse.java
create mode 100644 src/org/redkale/net/sncp/SncpServer.java
create mode 100644 src/org/redkale/net/sncp/SncpServlet.java
create mode 100644 src/org/redkale/net/sncp/package-info.java
create mode 100644 src/org/redkale/service/CacheSourceService.java
create mode 100644 src/org/redkale/service/DataCacheListenerService.java
create mode 100644 src/org/redkale/service/DataSQLListenerService.java
create mode 100644 src/org/redkale/service/DataSourceService.java
create mode 100644 src/org/redkale/service/DynAttachment.java
create mode 100644 src/org/redkale/service/DynCall.java
create mode 100644 src/org/redkale/service/DynRemote.java
create mode 100644 src/org/redkale/service/DynSourceAddress.java
create mode 100644 src/org/redkale/service/DynTargetAddress.java
create mode 100644 src/org/redkale/service/LocalService.java
create mode 100644 src/org/redkale/service/MultiRun.java
create mode 100644 src/org/redkale/service/RetResult.java
create mode 100644 src/org/redkale/service/Service.java
create mode 100644 src/org/redkale/service/WebSocketNodeService.java
create mode 100644 src/org/redkale/service/package-info.java
create mode 100644 src/org/redkale/source/CacheSource.java
create mode 100644 src/org/redkale/source/DataCacheListener.java
create mode 100644 src/org/redkale/source/DataCallArrayAttribute.java
create mode 100644 src/org/redkale/source/DataCallAttribute.java
create mode 100644 src/org/redkale/source/DataDefaultSource.java
create mode 100644 src/org/redkale/source/DataSQLListener.java
create mode 100644 src/org/redkale/source/DataSource.java
create mode 100644 src/org/redkale/source/DistributeGenerator.java
create mode 100644 src/org/redkale/source/DistributeTables.java
create mode 100644 src/org/redkale/source/EntityCache.java
create mode 100644 src/org/redkale/source/EntityInfo.java
create mode 100644 src/org/redkale/source/FilterBean.java
create mode 100644 src/org/redkale/source/FilterColumn.java
create mode 100644 src/org/redkale/source/FilterExpress.java
create mode 100644 src/org/redkale/source/FilterFunc.java
create mode 100644 src/org/redkale/source/FilterGroup.java
create mode 100644 src/org/redkale/source/FilterJoinColumn.java
create mode 100644 src/org/redkale/source/FilterJoinNode.java
create mode 100644 src/org/redkale/source/FilterNode.java
create mode 100644 src/org/redkale/source/FilterNodeBean.java
create mode 100644 src/org/redkale/source/FilterValue.java
create mode 100644 src/org/redkale/source/Flipper.java
create mode 100644 src/org/redkale/source/JDBCPoolSource.java
create mode 100644 src/org/redkale/source/Range.java
create mode 100644 src/org/redkale/source/VirtualEntity.java
create mode 100644 src/org/redkale/source/package-info.java
create mode 100644 src/org/redkale/util/AnyValue.java
create mode 100644 src/org/redkale/util/AsmMethodVisitor.java
create mode 100644 src/org/redkale/util/Attribute.java
create mode 100644 src/org/redkale/util/AutoLoad.java
create mode 100644 src/org/redkale/util/ByteArray.java
create mode 100644 src/org/redkale/util/Creator.java
create mode 100644 src/org/redkale/util/DLong.java
create mode 100644 src/org/redkale/util/LogLevel.java
create mode 100644 src/org/redkale/util/ObjectPool.java
create mode 100644 src/org/redkale/util/Reproduce.java
create mode 100644 src/org/redkale/util/ResourceFactory.java
create mode 100644 src/org/redkale/util/ResourceType.java
create mode 100644 src/org/redkale/util/SelectColumn.java
create mode 100644 src/org/redkale/util/Sheet.java
create mode 100644 src/org/redkale/util/TypeToken.java
create mode 100644 src/org/redkale/util/Utility.java
create mode 100644 src/org/redkale/util/package-info.java
create mode 100644 src/org/redkale/watch/WatchFactory.java
create mode 100644 src/org/redkale/watch/WatchNode.java
create mode 100644 src/org/redkale/watch/WatchNumber.java
create mode 100644 src/org/redkale/watch/WatchSupplier.java
create mode 100644 src/org/redkale/watch/Watchable.java
create mode 100644 src/org/redkale/watch/package-info.java
diff --git a/src/org/redkale/boot/Application.java b/src/org/redkale/boot/Application.java
new file mode 100644
index 000000000..27142ff15
--- /dev/null
+++ b/src/org/redkale/boot/Application.java
@@ -0,0 +1,656 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import org.redkale.boot.ClassFilter.FilterEntry;
+import org.redkale.util.AnyValue.DefaultAnyValue;
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import java.util.logging.*;
+import javax.xml.parsers.*;
+import org.redkale.convert.bson.*;
+import org.redkale.convert.json.*;
+import org.redkale.net.*;
+import org.redkale.net.http.*;
+import org.redkale.net.sncp.*;
+import org.redkale.service.*;
+import org.redkale.source.*;
+import org.redkale.util.*;
+import org.redkale.watch.*;
+import org.w3c.dom.*;
+
+/**
+ * 编译时需要加入: -XDignore.symbol.file=true
+ *
+ * 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet
+ * 优先加载所有SNCP协议的服务, 再加载其他协议服务,
+ * 最后进行Service、Servlet与其他资源之间的依赖注入。
+ *
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+public final class Application {
+
+ //当前进程启动的时间, 类型: long
+ public static final String RESNAME_APP_TIME = "APP_TIME";
+
+ //当前进程的根目录, 类型:String、File、Path
+ public static final String RESNAME_APP_HOME = "APP_HOME";
+
+ //application.xml 文件中resources节点的内容, 类型: AnyValue
+ public static final String RESNAME_APP_GRES = "APP_GRES";
+
+ //当前进程节点的name, 类型:String
+ public static final String RESNAME_APP_NODE = "APP_NODE";
+
+ //当前进程节点的IP地址, 类型:InetAddress、String
+ public static final String RESNAME_APP_ADDR = "APP_ADDR";
+
+ //当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String
+ public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR";
+
+ //当前SNCP Server所属的组 类型: String
+ public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP";
+
+ //当前Server的ROOT目录 类型:String、File、Path
+ public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
+
+ final Map globalNodes = new HashMap<>();
+
+ final Map> globalGroups = new HashMap<>();
+
+ final Map globalGroupProtocols = new HashMap<>();
+
+ final InetAddress localAddress;
+
+ final List cacheSources = new CopyOnWriteArrayList<>();
+
+ final List dataSources = new CopyOnWriteArrayList<>();
+
+ final List servers = new CopyOnWriteArrayList<>();
+
+ CountDownLatch servicecdl; //会出现两次赋值
+
+ final ObjectPool transportBufferPool;
+
+ final ExecutorService transportExecutor;
+
+ final AsynchronousChannelGroup transportChannelGroup;
+
+ final ResourceFactory resourceFactory = ResourceFactory.root();
+
+ //--------------------------------------------------------------------------------------------
+ private final boolean singletonrun;
+
+ private final WatchFactory watchFactory = WatchFactory.root();
+
+ private final File home;
+
+ private final Logger logger;
+
+ private final AnyValue config;
+
+ private final long startTime = System.currentTimeMillis();
+
+ private final CountDownLatch serversLatch;
+
+ private Application(final AnyValue config) {
+ this(false, config);
+ }
+
+ private Application(final boolean singletonrun, final AnyValue config) {
+ this.singletonrun = singletonrun;
+ this.config = config;
+
+ final File root = new File(System.getProperty(RESNAME_APP_HOME));
+ this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime);
+ this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath());
+ this.resourceFactory.register(RESNAME_APP_HOME, File.class, root);
+ try {
+ this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
+ this.home = root.getCanonicalFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ String localaddr = config.getValue("address", "").trim();
+ this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress();
+ this.resourceFactory.register(RESNAME_APP_ADDR, this.localAddress.getHostAddress());
+ this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, this.localAddress);
+ {
+ String node = config.getValue("node", "").trim();
+ if (node.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ byte[] bs = this.localAddress.getAddress();
+ int v1 = bs[bs.length - 2] & 0xff;
+ int v2 = bs[bs.length - 1] & 0xff;
+ if (v1 <= 0xf) sb.append('0');
+ sb.append(Integer.toHexString(v1));
+ if (v2 <= 0xf) sb.append('0');
+ sb.append(Integer.toHexString(v2));
+ node = sb.toString();
+ }
+ this.resourceFactory.register(RESNAME_APP_NODE, node);
+ System.setProperty(RESNAME_APP_NODE, node);
+ }
+ //以下是初始化日志配置
+ final File logconf = new File(root, "conf/logging.properties");
+ if (logconf.isFile() && logconf.canRead()) {
+ try {
+ final String rootpath = root.getCanonicalPath().replace('\\', '/');
+ FileInputStream fin = new FileInputStream(logconf);
+ Properties properties = new Properties();
+ properties.load(fin);
+ fin.close();
+ properties.entrySet().stream().forEach(x -> {
+ x.setValue(x.getValue().toString().replace("${APP_HOME}", rootpath));
+ });
+
+ if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) {
+ properties.setProperty("java.util.logging.FileHandler.formatter", LogFileHandler.LoggingFormater.class.getName());
+ }
+ if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) {
+ properties.setProperty("java.util.logging.ConsoleHandler.formatter", LogFileHandler.LoggingFormater.class.getName());
+ }
+ String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern");
+ if (fileHandlerPattern != null && fileHandlerPattern.contains("%d")) {
+ final String fileHandlerClass = LogFileHandler.class.getName();
+ Properties prop = new Properties();
+ final String handlers = properties.getProperty("handlers");
+ if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
+ prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", fileHandlerClass));
+ }
+ if (!prop.isEmpty()) {
+ String prefix = fileHandlerClass + ".";
+ properties.entrySet().stream().forEach(x -> {
+ if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) {
+ prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue());
+ }
+ });
+ prop.entrySet().stream().forEach(x -> {
+ properties.put(x.getKey(), x.getValue());
+ });
+ }
+ properties.put(SncpClient.class.getSimpleName() + ".handlers", LogFileHandler.SncpLogFileHandler.class.getName());
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final PrintStream ps = new PrintStream(out);
+ properties.forEach((x, y) -> ps.println(x + "=" + y));
+ LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
+ } catch (Exception e) {
+ Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e);
+ }
+ }
+ this.logger = Logger.getLogger(this.getClass().getSimpleName());
+ this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
+ //------------------配置 节点 ------------------
+ ObjectPool transportPool = null;
+ ExecutorService transportExec = null;
+ AsynchronousChannelGroup transportGroup = null;
+ final AnyValue resources = config.getAnyValue("resources");
+ if (resources != null) {
+ AnyValue transportConf = resources.getAnyValue("transport");
+ int groupsize = resources.getAnyValues("group").length;
+ if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
+ if (transportConf != null) {
+ //--------------transportBufferPool-----------
+ AtomicLong createBufferCounter = watchFactory == null ? new AtomicLong() : watchFactory.createWatchNumber(Transport.class.getSimpleName() + ".Buffer.creatCounter");
+ AtomicLong cycleBufferCounter = watchFactory == null ? new AtomicLong() : watchFactory.createWatchNumber(Transport.class.getSimpleName() + ".Buffer.cycleCounter");
+ final int bufferCapacity = transportConf.getIntValue("bufferCapacity", 8 * 1024);
+ final int bufferPoolSize = transportConf.getIntValue("bufferPoolSize", groupsize * Runtime.getRuntime().availableProcessors() * 8);
+ final int threads = transportConf.getIntValue("threads", groupsize * Runtime.getRuntime().availableProcessors() * 8);
+ transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
+ (Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
+ if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
+ e.clear();
+ return true;
+ });
+ //-----------transportChannelGroup--------------
+ try {
+ final AtomicInteger counter = new AtomicInteger();
+ transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ t.setName("Transport-Thread-" + counter.incrementAndGet());
+ return t;
+ });
+ transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
+ }
+ }
+ this.transportBufferPool = transportPool;
+ this.transportExecutor = transportExec;
+ this.transportChannelGroup = transportGroup;
+ }
+
+ public ResourceFactory getResourceFactory() {
+ return resourceFactory;
+ }
+
+ public WatchFactory getWatchFactory() {
+ return watchFactory;
+ }
+
+ public File getHome() {
+ return home;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ private void initLogging() {
+
+ }
+
+ public void init() throws Exception {
+ System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
+ System.setProperty("convert.bson.tiny", "true");
+ System.setProperty("convert.json.tiny", "true");
+ System.setProperty("convert.bson.pool.size", "128");
+ System.setProperty("convert.json.pool.size", "128");
+ System.setProperty("convert.bson.writer.buffer.defsize", "4096");
+ System.setProperty("convert.json.writer.buffer.defsize", "4096");
+
+ File persist = new File(this.home, "conf/persistence.xml");
+ final String homepath = this.home.getCanonicalPath();
+ if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.getCanonicalPath());
+ logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
+ String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
+ lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
+ Server.loadLib(logger, lib);
+ initLogging();
+ //------------------------------------------------------------------------
+ final AnyValue resources = config.getAnyValue("resources");
+ if (resources != null) {
+ resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources);
+ final AnyValue properties = resources.getAnyValue("properties");
+ if (properties != null) {
+ String dfloads = properties.getValue("load");
+ if (dfloads != null) {
+ for (String dfload : dfloads.split(";")) {
+ if (dfload.trim().isEmpty()) continue;
+ dfload = dfload.trim().replace("${APP_HOME}", home.getCanonicalPath()).replace('\\', '/');
+ final File df = (dfload.indexOf('/') < 0) ? new File(home, "conf/" + dfload) : new File(dfload);
+ if (df.isFile()) {
+ Properties ps = new Properties();
+ InputStream in = new FileInputStream(df);
+ ps.load(in);
+ in.close();
+ ps.forEach((x, y) -> resourceFactory.register("property." + x, y));
+ }
+ }
+ }
+ for (AnyValue prop : properties.getAnyValues("property")) {
+ String name = prop.getValue("name");
+ String value = prop.getValue("value");
+ if (name == null || value == null) continue;
+ if (name.startsWith("system.property.")) {
+ System.setProperty(name.substring("system.property.".length()), value);
+ } else if (name.startsWith("mimetype.property.")) {
+ MimeType.add(name.substring("mimetype.property.".length()), value);
+ } else {
+ resourceFactory.register("property." + name, value);
+ }
+ }
+ }
+ }
+ if (this.localAddress != null && this.resourceFactory.find("property.datasource.nodeid", String.class) == null) {
+ byte[] bs = this.localAddress.getAddress();
+ int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
+ this.resourceFactory.register("property.datasource.nodeid", "" + v);
+ }
+ this.resourceFactory.register(BsonFactory.root());
+ this.resourceFactory.register(JsonFactory.root());
+ this.resourceFactory.register(BsonFactory.root().getConvert());
+ this.resourceFactory.register(JsonFactory.root().getConvert());
+ initResources();
+ }
+
+ private void initResources() throws Exception {
+ //-------------------------------------------------------------------------
+ final AnyValue resources = config.getAnyValue("resources");
+ if (resources != null) {
+ //------------------------------------------------------------------------
+
+ for (AnyValue conf : resources.getAnyValues("group")) {
+ final String group = conf.getValue("name", "");
+ final String protocol = conf.getValue("protocol", Transport.DEFAULT_PROTOCOL).toUpperCase();
+ if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
+ throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
+ }
+ Set addrs = globalGroups.get(group);
+ if (addrs == null) {
+ addrs = new LinkedHashSet<>();
+ globalGroupProtocols.put(group, protocol);
+ globalGroups.put(group, addrs);
+ }
+ for (AnyValue node : conf.getAnyValues("node")) {
+ final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
+ addrs.add(addr);
+ String oldgroup = globalNodes.get(addr);
+ if (oldgroup != null) throw new RuntimeException(addr + " had one more group " + (globalNodes.get(addr)));
+ globalNodes.put(addr, group);
+ }
+ }
+ }
+ //------------------------------------------------------------------------
+ }
+
+ private void startSelfServer() throws Exception {
+ final Application application = this;
+ new Thread() {
+ {
+ setName("Application-Control-Thread");
+ }
+
+ @Override
+ public void run() {
+ try {
+ final DatagramChannel channel = DatagramChannel.open();
+ channel.configureBlocking(true);
+ channel.socket().setSoTimeout(3000);
+ channel.bind(new InetSocketAddress(config.getValue("host", "127.0.0.1"), config.getIntValue("port")));
+ boolean loop = true;
+ ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
+ while (loop) {
+ buffer.clear();
+ SocketAddress address = channel.receive(buffer);
+ buffer.flip();
+ byte[] bytes = new byte[buffer.remaining()];
+ buffer.get(bytes);
+ if ("SHUTDOWN".equalsIgnoreCase(new String(bytes))) {
+ try {
+ long s = System.currentTimeMillis();
+ logger.info(application.getClass().getSimpleName() + " shutdowning");
+ application.shutdown();
+ buffer.clear();
+ buffer.put("SHUTDOWN OK".getBytes());
+ buffer.flip();
+ channel.send(buffer, address);
+ long e = System.currentTimeMillis() - s;
+ logger.info(application.getClass().getSimpleName() + " shutdown in " + e + " ms");
+ application.serversLatch.countDown();
+ System.exit(0);
+ } catch (Exception ex) {
+ logger.log(Level.INFO, "SHUTDOWN FAIL", ex);
+ buffer.clear();
+ buffer.put("SHUTDOWN FAIL".getBytes());
+ buffer.flip();
+ channel.send(buffer, address);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.log(Level.INFO, "Control fail", e);
+ System.exit(1);
+ }
+ }
+ }.start();
+ }
+
+ private void sendShutDown() throws Exception {
+ final DatagramChannel channel = DatagramChannel.open();
+ channel.configureBlocking(true);
+ channel.connect(new InetSocketAddress(config.getValue("host", "127.0.0.1"), config.getIntValue("port")));
+ ByteBuffer buffer = ByteBuffer.allocate(128);
+ buffer.put("SHUTDOWN".getBytes());
+ buffer.flip();
+ channel.write(buffer);
+ buffer.clear();
+ channel.configureBlocking(false);
+ channel.read(buffer);
+ buffer.flip();
+ byte[] bytes = new byte[buffer.remaining()];
+ buffer.get(bytes);
+ channel.close();
+ logger.info(new String(bytes));
+ Thread.sleep(500);
+ }
+
+ public void start() throws Exception {
+ final AnyValue[] entrys = config.getAnyValues("server");
+ CountDownLatch timecd = new CountDownLatch(entrys.length);
+ final List sncps = new ArrayList<>();
+ final List others = new ArrayList<>();
+ for (final AnyValue entry : entrys) {
+ if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) {
+ sncps.add(entry);
+ } else {
+ others.add(entry);
+ }
+ }
+ //单向SNCP服务不需要对等group
+ //if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found node info.");
+
+ runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议
+ runServers(timecd, others);
+ timecd.await();
+ logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms");
+ if (!singletonrun) this.serversLatch.await();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void runServers(CountDownLatch timecd, final List serconfs) throws Exception {
+ this.servicecdl = new CountDownLatch(serconfs.size());
+ CountDownLatch sercdl = new CountDownLatch(serconfs.size());
+ final AtomicBoolean inited = new AtomicBoolean(false);
+ final Map> nodeClasses = new HashMap<>();
+ for (final AnyValue serconf : serconfs) {
+ Thread thread = new Thread() {
+ {
+ String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
+ setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
+ this.setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ try {
+ //Thread ctd = Thread.currentThread();
+ //ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
+ final String protocol = serconf.getValue("protocol", "").replaceFirst("\\..+", "").toUpperCase();
+ NodeServer server = null;
+ if ("SNCP".equals(protocol)) {
+ server = new NodeSncpServer(Application.this, serconf);
+ } else if ("HTTP".equals(protocol)) {
+ server = new NodeHttpServer(Application.this, serconf);
+ } else {
+ if (!inited.get()) {
+ synchronized (nodeClasses) {
+ if (!inited.get()) {
+ inited.set(true);
+ ClassFilter profilter = new ClassFilter(NodeProtocol.class, NodeServer.class);
+ ClassFilter.Loader.load(home, profilter);
+ final Set> entrys = profilter.getFilterEntrys();
+ for (FilterEntry entry : entrys) {
+ final Class extends NodeServer> type = entry.getType();
+ NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
+ for (String p : pros.value()) {
+ p = p.toUpperCase();
+ if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
+ final Class extends NodeServer> old = nodeClasses.get(p);
+ if (old != null && old != type) throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
+ nodeClasses.put(p, type);
+ }
+ }
+ }
+ }
+ }
+ Class extends NodeServer> nodeClass = nodeClasses.get(protocol);
+ if (nodeClass != null) server = NodeServer.create(nodeClass, Application.this, serconf);
+ }
+ if (server == null) {
+ logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol"));
+ System.exit(0);
+ }
+ servers.add(server);
+ server.init(serconf);
+ if (!singletonrun) server.start();
+ timecd.countDown();
+ sercdl.countDown();
+ } catch (Exception ex) {
+ logger.log(Level.WARNING, serconf + " runServers error", ex);
+ Application.this.serversLatch.countDown();
+ }
+ }
+ };
+ thread.start();
+ }
+ sercdl.await();
+ }
+
+ public static T singleton(Class serviceClass) throws Exception {
+ return singleton("", serviceClass);
+ }
+
+ public static T singleton(String name, Class serviceClass) throws Exception {
+ final Application application = Application.create(true);
+ application.init();
+ application.start();
+ for (NodeServer server : application.servers) {
+ T service = server.resourceFactory.find(name, serviceClass);
+ if (service != null) return service;
+ }
+ return null;
+ }
+
+ private static Application create(final boolean singleton) throws IOException {
+ final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath();
+ System.setProperty(RESNAME_APP_HOME, home);
+ File appfile = new File(home, "conf/application.xml");
+ return new Application(singleton, load(new FileInputStream(appfile)));
+ }
+
+ public static void main(String[] args) throws Exception {
+ Utility.midnight(); //先初始化一下Utility
+ //运行主程序
+ final Application application = Application.create(false);
+ if (System.getProperty("SHUTDOWN") != null) {
+ application.sendShutDown();
+ return;
+ }
+ application.init();
+ application.startSelfServer();
+ try {
+ application.start();
+ } catch (Exception e) {
+ application.logger.log(Level.SEVERE, "Application start error", e);
+ System.exit(0);
+ }
+ System.exit(0);
+ }
+
+ Set findSncpGroups(Transport sameGroupTransport, Collection diffGroupTransports) {
+ Set gs = new HashSet<>();
+ if (sameGroupTransport != null) gs.add(sameGroupTransport.getName());
+ if (diffGroupTransports != null) {
+ for (Transport t : diffGroupTransports) {
+ gs.add(t.getName());
+ }
+ }
+ return gs;
+ }
+
+ NodeSncpServer findNodeSncpServer(final InetSocketAddress sncpAddr) {
+ for (NodeServer node : servers) {
+ if (node.isSNCP() && sncpAddr.equals(node.getSncpAddress())) {
+ return (NodeSncpServer) node;
+ }
+ }
+ return null;
+ }
+
+ String findGroupProtocol(String group) {
+ if (group == null) return null;
+ return globalGroupProtocols.get(group);
+ }
+
+ Set findGlobalGroup(String group) {
+ if (group == null) return null;
+ Set set = globalGroups.get(group);
+ return set == null ? null : new LinkedHashSet<>(set);
+ }
+
+ private void shutdown() throws Exception {
+ servers.stream().forEach((server) -> {
+ try {
+ server.shutdown();
+ } catch (Exception t) {
+ logger.log(Level.WARNING, " shutdown server(" + server.getSocketAddress() + ") error", t);
+ } finally {
+ serversLatch.countDown();
+ }
+ });
+
+ for (DataSource source : dataSources) {
+ try {
+ source.getClass().getMethod("close").invoke(source);
+ } catch (Exception e) {
+ logger.log(Level.FINER, "close DataSource erroneous", e);
+ }
+ }
+ for (CacheSource source : cacheSources) {
+ try {
+ source.getClass().getMethod("close").invoke(source);
+ } catch (Exception e) {
+ logger.log(Level.FINER, "close CacheSource erroneous", e);
+ }
+ }
+ if (this.transportChannelGroup != null) {
+ try {
+ this.transportChannelGroup.shutdownNow();
+ } catch (Exception e) {
+ logger.log(Level.FINER, "close transportChannelGroup erroneous", e);
+ }
+ }
+ }
+
+ private static AnyValue load(final InputStream in0) {
+ final DefaultAnyValue any = new DefaultAnyValue();
+ try (final InputStream in = in0) {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(in);
+ Element root = doc.getDocumentElement();
+ load(any, root);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ return any;
+ }
+
+ private static void load(final DefaultAnyValue any, final Node root) {
+ final String home = System.getProperty(RESNAME_APP_HOME);
+ NamedNodeMap nodes = root.getAttributes();
+ if (nodes == null) return;
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ any.addValue(node.getNodeName(), node.getNodeValue().replace("${APP_HOME}", home));
+ }
+ NodeList children = root.getChildNodes();
+ if (children == null) return;
+ for (int i = 0; i < children.getLength(); i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() != Node.ELEMENT_NODE) continue;
+ DefaultAnyValue sub = new DefaultAnyValue();
+ load(sub, node);
+ any.addValue(node.getNodeName(), sub);
+ }
+
+ }
+}
diff --git a/src/org/redkale/boot/ClassFilter.java b/src/org/redkale/boot/ClassFilter.java
new file mode 100644
index 000000000..259daf58b
--- /dev/null
+++ b/src/org/redkale/boot/ClassFilter.java
@@ -0,0 +1,431 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import java.io.*;
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.jar.*;
+import java.util.logging.*;
+import java.util.regex.*;
+import org.redkale.util.AnyValue;
+import org.redkale.util.AnyValue.DefaultAnyValue;
+import org.redkale.util.AutoLoad;
+
+/**
+ * class过滤器, 符合条件的class会保留下来存入FilterEntry。
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param 泛型
+ */
+@SuppressWarnings("unchecked")
+public final class ClassFilter {
+
+ private final Set> entrys = new HashSet<>();
+
+ private boolean refused;
+
+ private Class superClass;
+
+ private Class extends Annotation> annotationClass;
+
+ private Pattern[] includePatterns;
+
+ private Pattern[] excludePatterns;
+
+ private List ors;
+
+ private List ands;
+
+ private AnyValue conf;
+
+ public ClassFilter(Class extends Annotation> annotationClass, Class superClass) {
+ this(annotationClass, superClass, null);
+ }
+
+ public ClassFilter(Class extends Annotation> annotationClass, Class superClass, AnyValue conf) {
+ this.annotationClass = annotationClass;
+ this.superClass = superClass;
+ this.conf = conf;
+ }
+
+ public ClassFilter or(ClassFilter filter) {
+ if (ors == null) ors = new ArrayList<>();
+ ors.add(filter);
+ return this;
+ }
+
+ public ClassFilter and(ClassFilter filter) {
+ if (ands == null) ands = new ArrayList<>();
+ ands.add(filter);
+ return this;
+ }
+
+ /**
+ * 获取符合条件的class集合
+ *
+ * @return Set<FilterEntry<T>>
+ */
+ public final Set> getFilterEntrys() {
+ return entrys;
+ }
+
+ /**
+ * 自动扫描地过滤指定的class
+ *
+ * @param property AnyValue
+ * @param clazzname String
+ */
+ @SuppressWarnings("unchecked")
+ public final void filter(AnyValue property, String clazzname) {
+ filter(property, clazzname, true);
+ }
+
+ /**
+ * 过滤指定的class
+ *
+ * @param property application.xml中对应class节点下的property属性项
+ * @param clazzname class名称
+ * @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略
+ */
+ public final void filter(AnyValue property, String clazzname, boolean autoscan) {
+ boolean r = accept0(property, clazzname);
+ ClassFilter cf = r ? this : null;
+ if (r && ands != null) {
+ for (ClassFilter filter : ands) {
+ if (!filter.accept(property, clazzname)) return;
+ }
+ }
+ if (!r && ors != null) {
+ for (ClassFilter filter : ors) {
+ if (filter.accept(property, clazzname)) {
+ cf = filter;
+ break;
+ }
+ }
+ }
+ if (cf == null) return;
+ try {
+ Class clazz = Class.forName(clazzname);
+ if (!cf.accept(property, clazz, autoscan)) return;
+ if (cf.conf != null) {
+ if (property == null) {
+ property = cf.conf;
+ } else if (property instanceof DefaultAnyValue) {
+ ((DefaultAnyValue) property).addAll(cf.conf);
+ } else {
+ DefaultAnyValue dav = new DefaultAnyValue();
+ dav.addAll(property);
+ dav.addAll(cf.conf);
+ property = dav;
+ }
+ }
+ entrys.add(new FilterEntry(clazz, autoscan, property));
+ } catch (Throwable cfe) {
+ }
+ }
+
+ private static Pattern[] toPattern(String[] regs) {
+ if (regs == null) return null;
+ int i = 0;
+ Pattern[] rs = new Pattern[regs.length];
+ for (String reg : regs) {
+ if (reg == null || reg.trim().isEmpty()) continue;
+ rs[i++] = Pattern.compile(reg.trim());
+ }
+ if (i == 0) return null;
+ if (i == rs.length) return rs;
+ Pattern[] ps = new Pattern[i];
+ System.arraycopy(rs, 0, ps, 0, i);
+ return ps;
+ }
+
+ /**
+ * 判断class是否有效
+ *
+ * @param property AnyValue
+ * @param classname String
+ * @return boolean
+ */
+ public boolean accept(AnyValue property, String classname) {
+ boolean r = accept0(property, classname);
+ if (r && ands != null) {
+ for (ClassFilter filter : ands) {
+ if (!filter.accept(property, classname)) return false;
+ }
+ }
+ if (!r && ors != null) {
+ for (ClassFilter filter : ors) {
+ if (filter.accept(property, classname)) return true;
+ }
+ }
+ return r;
+ }
+
+ private boolean accept0(AnyValue property, String classname) {
+ if (this.refused) return false;
+ if (classname.startsWith("java.") || classname.startsWith("javax.")) return false;
+ if (excludePatterns != null) {
+ for (Pattern reg : excludePatterns) {
+ if (reg.matcher(classname).matches()) return false;
+ }
+ }
+ if (includePatterns != null) {
+ for (Pattern reg : includePatterns) {
+ if (reg.matcher(classname).matches()) return true;
+ }
+ }
+ return includePatterns == null;
+ }
+
+ /**
+ * 判断class是否有效
+ *
+ * @param property AnyValue
+ * @param clazz Class
+ * @param autoscan boolean
+ * @return boolean
+ */
+ @SuppressWarnings("unchecked")
+ public boolean accept(AnyValue property, Class clazz, boolean autoscan) {
+ if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false;
+ if (autoscan) {
+ AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class);
+ if (auto != null && !auto.value()) return false;
+ }
+ if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false;
+ return superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz));
+ }
+
+ public void setSuperClass(Class superClass) {
+ this.superClass = superClass;
+ }
+
+ public void setAnnotationClass(Class extends Annotation> annotationClass) {
+ this.annotationClass = annotationClass;
+ }
+
+ public Pattern[] getIncludePatterns() {
+ return includePatterns;
+ }
+
+ public void setIncludePatterns(String[] includePatterns) {
+ this.includePatterns = toPattern(includePatterns);
+ }
+
+ public Pattern[] getExcludePatterns() {
+ return excludePatterns;
+ }
+
+ public void setExcludePatterns(String[] excludePatterns) {
+ this.excludePatterns = toPattern(excludePatterns);
+ }
+
+ public Class extends Annotation> getAnnotationClass() {
+ return annotationClass;
+ }
+
+ public Class getSuperClass() {
+ return superClass;
+ }
+
+ public boolean isRefused() {
+ return refused;
+ }
+
+ public void setRefused(boolean refused) {
+ this.refused = refused;
+ }
+
+ /**
+ * 存放符合条件的class与class指定的属性项
+ *
+ * @param 泛型
+ */
+ public static final class FilterEntry {
+
+ private final HashSet groups = new LinkedHashSet<>();
+
+ private final String name;
+
+ private final Class type;
+
+ private final AnyValue property;
+
+ private final boolean autoload;
+
+ public FilterEntry(Class type, AnyValue property) {
+ this(type, false, property);
+ }
+
+ public FilterEntry(Class type, final boolean autoload, AnyValue property) {
+ this.type = type;
+ String str = property == null ? null : property.getValue("groups");
+ if (str != null) {
+ str = str.trim();
+ if (str.endsWith(";")) str = str.substring(0, str.length() - 1);
+ }
+ if (str != null) groups.addAll(Arrays.asList(str.split(";")));
+ this.property = property;
+ this.autoload = autoload;
+ this.name = property == null ? "" : property.getValue("name", "");
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
+ + ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + groups + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return this.type.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ return (this.type == ((FilterEntry>) obj).type && this.groups.equals(((FilterEntry>) obj).groups) && this.name.equals(((FilterEntry>) obj).name));
+ }
+
+ public Class getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public AnyValue getProperty() {
+ return property;
+ }
+
+ public boolean isEmptyGroups() {
+ return groups == null || groups.isEmpty();
+ }
+
+ public HashSet getGroups() {
+ return groups;
+ }
+
+ public boolean isAutoload() {
+ return autoload;
+ }
+
+ }
+
+ /**
+ * class加载类
+ */
+ public static class Loader {
+
+ protected static final Logger logger = Logger.getLogger(Loader.class.getName());
+
+ protected static final ConcurrentMap> cache = new ConcurrentHashMap<>();
+
+ public static void close() {
+ cache.clear();
+ }
+
+ /**
+ * 加载当前线程的classpath扫描所有class进行过滤
+ *
+ * @param exclude 不需要扫描的文件夹, 可以为null
+ * @param filters 过滤器
+ * @throws IOException 异常
+ */
+ public static void load(final File exclude, final ClassFilter... filters) throws IOException {
+ URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
+ List urlfiles = new ArrayList<>(2);
+ List urljares = new ArrayList<>(2);
+ final URL exurl = exclude != null ? exclude.toURI().toURL() : null;
+ for (URL url : loader.getURLs()) {
+ if (exurl != null && exurl.sameFile(url)) continue;
+ if (url.getPath().endsWith(".jar")) {
+ urljares.add(url);
+ } else {
+ urlfiles.add(url);
+ }
+ }
+ List files = new ArrayList<>();
+ boolean debug = logger.isLoggable(Level.FINEST);
+ StringBuilder debugstr = new StringBuilder();
+ for (final URL url : urljares) {
+ Set classes = cache.get(url);
+ if (classes == null) {
+ classes = new LinkedHashSet<>();
+ try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), "UTF-8"))) {
+ Enumeration it = jar.entries();
+ while (it.hasMoreElements()) {
+ String entryname = it.nextElement().getName().replace('/', '.');
+ if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) {
+ String classname = entryname.substring(0, entryname.length() - 6);
+ if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
+ classes.add(classname);
+ if (debug) debugstr.append(classname).append("\r\n");
+ for (final ClassFilter filter : filters) {
+ if (filter != null) filter.filter(null, classname);
+ }
+ }
+ }
+ }
+ cache.put(url, classes);
+ } else {
+ for (String classname : classes) {
+ for (final ClassFilter filter : filters) {
+ if (filter != null) filter.filter(null, classname);
+ }
+ }
+ }
+ }
+ for (final URL url : urlfiles) {
+ Set classes = cache.get(url);
+ if (classes == null) {
+ classes = new LinkedHashSet<>();
+ files.clear();
+ File root = new File(url.getFile());
+ String rootpath = root.getPath();
+ loadClassFiles(exclude, root, files);
+ for (File f : files) {
+ String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.');
+ if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
+ classes.add(classname);
+ if (debug) debugstr.append(classname).append("\r\n");
+ for (final ClassFilter filter : filters) {
+ if (filter != null) filter.filter(null, classname);
+ }
+ }
+ cache.put(url, classes);
+ } else {
+ for (String classname : classes) {
+ for (final ClassFilter filter : filters) {
+ if (filter != null) filter.filter(null, classname);
+ }
+ }
+ }
+ }
+ //if (debug) logger.log(Level.INFO, "scan classes: \r\n{0}", debugstr);
+ }
+
+ private static void loadClassFiles(File exclude, File root, List files) {
+ if (root.isFile() && root.getName().endsWith(".class")) {
+ files.add(root);
+ } else if (root.isDirectory()) {
+ if (exclude != null && exclude.equals(root)) return;
+ for (File f : root.listFiles()) {
+ loadClassFiles(exclude, f, files);
+ }
+ }
+ }
+ }
+}
diff --git a/src/org/redkale/boot/LogFileHandler.java b/src/org/redkale/boot/LogFileHandler.java
new file mode 100644
index 000000000..0039890d0
--- /dev/null
+++ b/src/org/redkale/boot/LogFileHandler.java
@@ -0,0 +1,276 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import java.io.*;
+import java.nio.file.*;
+import static java.nio.file.StandardCopyOption.*;
+import java.time.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import java.util.logging.*;
+import java.util.logging.Formatter;
+
+/**
+ * 自定义的日志输出类
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+public class LogFileHandler extends Handler {
+
+ /**
+ * SNCP的日志输出Handler
+ */
+ public static class SncpLogFileHandler extends LogFileHandler {
+
+ @Override
+ public String getPrefix() {
+ return "sncp-";
+ }
+ }
+
+ /**
+ * 默认的日志时间格式化类
+ *
+ */
+ public static class LoggingFormater extends Formatter {
+
+ private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
+
+ @Override
+ public String format(LogRecord record) {
+ String source;
+ if (record.getSourceClassName() != null) {
+ source = record.getSourceClassName();
+ if (record.getSourceMethodName() != null) {
+ source += " " + record.getSourceMethodName();
+ }
+ } else {
+ source = record.getLoggerName();
+ }
+ String message = formatMessage(record);
+ String throwable = "";
+ if (record.getThrown() != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw) {
+ @Override
+ public void println() {
+ super.print("\r\n");
+ }
+ };
+ pw.println();
+ record.getThrown().printStackTrace(pw);
+ pw.close();
+ throwable = sw.toString();
+ }
+ return String.format(format,
+ System.currentTimeMillis(),
+ source,
+ record.getLoggerName(),
+ record.getLevel().getName(),
+ message,
+ throwable);
+ }
+
+ }
+
+ protected final LinkedBlockingQueue records = new LinkedBlockingQueue();
+
+ private String pattern;
+
+ private int limit; //文件大小限制
+
+ private final AtomicInteger index = new AtomicInteger();
+
+ private int count = 1; //文件限制
+
+ private long tomorrow;
+
+ private boolean append;
+
+ private final AtomicLong length = new AtomicLong();
+
+ private File logfile;
+
+ private OutputStream stream;
+
+ public LogFileHandler() {
+ updateTomorrow();
+ configure();
+ open();
+ }
+
+ private void updateTomorrow() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.add(Calendar.DAY_OF_YEAR, 1);
+ long t = cal.getTimeInMillis();
+ if (this.tomorrow != t) index.set(0);
+ this.tomorrow = t;
+ }
+
+ private void open() {
+ final String name = "Logging-" + getClass().getSimpleName() + "-Thread";
+ new Thread() {
+ {
+ setName(name);
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ LogRecord record = records.take();
+ final boolean bigger = (limit > 0 && limit <= length.get());
+ if (bigger || tomorrow <= record.getMillis()) {
+ updateTomorrow();
+ if (stream != null) {
+ stream.close();
+ if (bigger) {
+ for (int i = Math.min(count - 2, index.get() - 1); i > 0; i--) {
+ File greater = new File(logfile.getPath() + "." + i);
+ if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
+ }
+ Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
+ }
+ stream = null;
+ }
+ }
+ if (stream == null) {
+ index.incrementAndGet();
+ java.time.LocalDate date = LocalDate.now();
+ logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
+ logfile.getParentFile().mkdirs();
+ length.set(logfile.length());
+ stream = new FileOutputStream(logfile, append);
+ }
+ //----------------------写日志-------------------------
+ String message = getFormatter().format(record);
+ String encoding = getEncoding();
+ byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
+ stream.write(bytes);
+ length.addAndGet(bytes.length);
+ } catch (Exception e) {
+ ErrorManager err = getErrorManager();
+ if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
+ }
+ }
+
+ }
+ }.start();
+ }
+
+ public String getPrefix() {
+ return "";
+ }
+
+ private void configure() {
+ LogManager manager = LogManager.getLogManager();
+ String cname = LogFileHandler.class.getName();
+ pattern = manager.getProperty(cname + ".pattern");
+ if (pattern == null) {
+ pattern = "logs-%m/" + getPrefix() + "log-%d.log";
+ } else {
+ int pos = pattern.lastIndexOf('/');
+ if (pos > 0) {
+ pattern = pattern.substring(0, pos + 1) + getPrefix() + pattern.substring(pos + 1);
+ } else {
+ pattern = getPrefix() + pattern;
+ }
+ }
+ String limitstr = manager.getProperty(cname + ".limit");
+ try {
+ if (limitstr != null) limit = Math.abs(Integer.decode(limitstr));
+ } catch (Exception e) {
+ }
+ String countstr = manager.getProperty(cname + ".count");
+ try {
+ if (countstr != null) count = Math.max(1, Math.abs(Integer.decode(countstr)));
+ } catch (Exception e) {
+ }
+ String appendstr = manager.getProperty(cname + ".append");
+ try {
+ if (appendstr != null) append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
+ } catch (Exception e) {
+ }
+ String levelstr = manager.getProperty(cname + ".level");
+ try {
+ if (levelstr != null) {
+ Level l = Level.parse(levelstr);
+ setLevel(l != null ? l : Level.ALL);
+ }
+ } catch (Exception e) {
+ }
+ String filterstr = manager.getProperty(cname + ".filter");
+ try {
+ if (filterstr != null) {
+ Class> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
+ setFilter((Filter) clz.newInstance());
+ }
+ } catch (Exception e) {
+ }
+ String formatterstr = manager.getProperty(cname + ".formatter");
+ try {
+ if (formatterstr != null) {
+ Class> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
+ setFormatter((Formatter) clz.newInstance());
+ }
+ } catch (Exception e) {
+ }
+ if (getFormatter() == null) setFormatter(new SimpleFormatter());
+
+ String encodingstr = manager.getProperty(cname + ".encoding");
+ try {
+ if (encodingstr != null) setEncoding(encodingstr);
+ } catch (Exception e) {
+ }
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ final String sourceClassName = record.getSourceClassName();
+ if (sourceClassName == null || true) {
+ StackTraceElement[] ses = new Throwable().getStackTrace();
+ for (int i = 2; i < ses.length; i++) {
+ if (ses[i].getClassName().startsWith("java.util.logging")) continue;
+ record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName());
+ record.setSourceMethodName(ses[i].getMethodName());
+ break;
+ }
+ } else {
+ record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
+ }
+ records.offer(record);
+ }
+
+ @Override
+ public void flush() {
+ try {
+ if (stream != null) stream.flush();
+ } catch (Exception e) {
+ ErrorManager err = getErrorManager();
+ if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
+ }
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ try {
+ if (stream != null) stream.close();
+ } catch (Exception e) {
+ ErrorManager err = getErrorManager();
+ if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);
+ }
+ }
+
+}
diff --git a/src/org/redkale/boot/NodeHttpServer.java b/src/org/redkale/boot/NodeHttpServer.java
new file mode 100644
index 000000000..f81af74c9
--- /dev/null
+++ b/src/org/redkale/boot/NodeHttpServer.java
@@ -0,0 +1,145 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import org.redkale.net.http.WebServlet;
+import org.redkale.net.http.HttpServer;
+import org.redkale.net.http.HttpServlet;
+import org.redkale.util.AnyValue;
+import org.redkale.boot.ClassFilter.FilterEntry;
+import org.redkale.util.AnyValue.DefaultAnyValue;
+import java.lang.reflect.*;
+import java.net.InetSocketAddress;
+import java.util.*;
+import java.util.logging.*;
+import javax.annotation.*;
+import org.redkale.net.*;
+import org.redkale.net.http.*;
+import org.redkale.net.sncp.*;
+import org.redkale.service.*;
+import org.redkale.util.*;
+
+/**
+ * HTTP Server节点的配置Server
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+@NodeProtocol({"HTTP"})
+public final class NodeHttpServer extends NodeServer {
+
+ private final HttpServer httpServer;
+
+ public NodeHttpServer(Application application, AnyValue serconf) {
+ super(application, createServer(application, serconf));
+ this.httpServer = (HttpServer) server;
+ }
+
+ private static Server createServer(Application application, AnyValue serconf) {
+ return new HttpServer(application.getStartTime(), application.getWatchFactory());
+ }
+
+ @Override
+ public InetSocketAddress getSocketAddress() {
+ return httpServer == null ? null : httpServer.getSocketAddress();
+ }
+
+ @Override
+ protected ClassFilter createServletClassFilter() {
+ return createClassFilter(null, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
+ }
+
+ @Override
+ protected void loadServlet(ClassFilter extends Servlet> servletFilter) throws Exception {
+ if (httpServer != null) loadHttpServlet(this.serverConf.getAnyValue("servlets"), servletFilter);
+ }
+
+ @Override
+ protected void loadService(ClassFilter serviceFilter) throws Exception {
+ super.loadService(serviceFilter);
+ initWebSocketService();
+ }
+
+ private void initWebSocketService() {
+ final NodeServer self = this;
+ final ResourceFactory regFactory = application.getResourceFactory();
+ resourceFactory.add(WebSocketNode.class, (ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
+ try {
+ if (field.getAnnotation(Resource.class) == null) return;
+ if (!(src instanceof WebSocketServlet)) return;
+ synchronized (regFactory) {
+ Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
+ if (nodeService == null) {
+ nodeService = Sncp.createLocalService(resourceName, getExecutor(), application.getResourceFactory(), WebSocketNodeService.class, (InetSocketAddress) null, (Transport) null, (Collection) null);
+ regFactory.register(resourceName, WebSocketNode.class, nodeService);
+ resourceFactory.inject(nodeService, self);
+ logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
+ }
+ field.set(src, nodeService);
+ }
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "WebSocketNode inject error", e);
+ }
+ });
+ }
+
+ protected void loadHttpServlet(final AnyValue conf, final ClassFilter extends Servlet> filter) throws Exception {
+ final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
+ final String prefix = conf == null ? "" : conf.getValue("path", "");
+ final String threadName = "[" + Thread.currentThread().getName() + "] ";
+ List> list = new ArrayList(filter.getFilterEntrys());
+ list.sort((FilterEntry extends Servlet> o1, FilterEntry extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载, 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
+ boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
+ boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType());
+ if (ws1 == ws2) return o1.getType().getName().compareTo(o2.getType().getName());
+ return ws1 ? -1 : 1;
+ });
+ final List> ss = sb == null ? null : new ArrayList<>();
+ for (FilterEntry extends Servlet> en : list) {
+ Class clazz = (Class) en.getType();
+ if (Modifier.isAbstract(clazz.getModifiers())) continue;
+ WebServlet ws = clazz.getAnnotation(WebServlet.class);
+ if (ws == null || ws.value().length == 0) continue;
+ final HttpServlet servlet = clazz.newInstance();
+ resourceFactory.inject(servlet, this);
+ final String[] mappings = ws.value();
+ String pref = ws.repair() ? prefix : "";
+ DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
+ WebInitParam[] webparams = ws.initParams();
+ if (webparams.length > 0) {
+ if (servletConf == null) servletConf = new DefaultAnyValue();
+ for (WebInitParam webparam : webparams) {
+ servletConf.addValue(webparam.name(), webparam.value());
+ }
+ }
+ this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
+ if (ss != null) {
+ for (int i = 0; i < mappings.length; i++) {
+ mappings[i] = pref + mappings[i];
+ }
+ ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
+ }
+ }
+ if (ss != null) {
+ Collections.sort(ss, (AbstractMap.SimpleEntry o1, AbstractMap.SimpleEntry o2) -> o1.getKey().compareTo(o2.getKey()));
+ int max = 0;
+ for (AbstractMap.SimpleEntry as : ss) {
+ if (as.getKey().length() > max) max = as.getKey().length();
+ }
+ for (AbstractMap.SimpleEntry as : ss) {
+ sb.append(threadName).append(" Loaded ").append(as.getKey());
+ for (int i = 0; i < max - as.getKey().length(); i++) {
+ sb.append(' ');
+ }
+ sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
+ }
+ }
+ if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
+ }
+
+}
diff --git a/src/org/redkale/boot/NodeProtocol.java b/src/org/redkale/boot/NodeProtocol.java
new file mode 100644
index 000000000..76c03dff9
--- /dev/null
+++ b/src/org/redkale/boot/NodeProtocol.java
@@ -0,0 +1,24 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import java.lang.annotation.*;
+
+/**
+ * 根据application.xml中的server节点中的protocol值来适配Server的加载逻辑
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface NodeProtocol {
+
+ String[] value();
+}
diff --git a/src/org/redkale/boot/NodeServer.java b/src/org/redkale/boot/NodeServer.java
new file mode 100644
index 000000000..600d1f517
--- /dev/null
+++ b/src/org/redkale/boot/NodeServer.java
@@ -0,0 +1,493 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import java.io.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.net.InetSocketAddress;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+import java.util.logging.*;
+import java.util.stream.Collectors;
+import javax.annotation.Resource;
+import javax.persistence.Transient;
+import static org.redkale.boot.Application.*;
+import org.redkale.boot.ClassFilter.FilterEntry;
+import org.redkale.net.*;
+import org.redkale.net.sncp.*;
+import org.redkale.service.*;
+import org.redkale.source.*;
+import org.redkale.util.AnyValue.DefaultAnyValue;
+import org.redkale.util.*;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+@SuppressWarnings("unchecked")
+public abstract class NodeServer {
+
+ //INFO日志的换行符
+ public static final String LINE_SEPARATOR = "\r\n";
+
+ //日志输出对象
+ protected final Logger logger;
+
+ //日志是否为FINE级别
+ protected final boolean fine;
+
+ //日志是否为FINE级别
+ protected final boolean finer;
+
+ //进程主类
+ protected final Application application;
+
+ //依赖注入工厂类
+ protected final ResourceFactory resourceFactory;
+
+ //当前Server对象
+ protected final Server server;
+
+ private String sncpGroup = null; //当前Server的SNCP协议的组
+
+ private InetSocketAddress sncpAddress; //SNCP服务的地址, 非SNCP为null
+
+ protected Consumer consumer;
+
+ protected AnyValue serverConf;
+
+ protected final Set localServiceWrappers = new LinkedHashSet<>();
+
+ protected final Set remoteServiceWrappers = new LinkedHashSet<>();
+
+ public NodeServer(Application application, Server server) {
+ this.application = application;
+ this.resourceFactory = application.getResourceFactory().createChild();
+ this.server = server;
+ this.logger = Logger.getLogger(this.getClass().getSimpleName());
+ this.fine = logger.isLoggable(Level.FINE);
+ this.finer = logger.isLoggable(Level.FINER);
+ }
+
+ protected Consumer getExecutor() throws Exception {
+ if (server == null) return null;
+ final Field field = Server.class.getDeclaredField("context");
+ field.setAccessible(true);
+ return new Consumer() {
+
+ private Context context;
+
+ @Override
+ public void accept(Runnable t) {
+ if (context == null && server != null) {
+ try {
+ this.context = (Context) field.get(server);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Server (" + server.getSocketAddress() + ") cannot find Context", e);
+ }
+ }
+ context.submit(t);
+ }
+
+ };
+ }
+
+ public static NodeServer create(Class clazz, Application application, AnyValue serconf) {
+ try {
+ return clazz.getConstructor(Application.class, AnyValue.class).newInstance(application, serconf);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void init(AnyValue config) throws Exception {
+ this.serverConf = config == null ? AnyValue.create() : config;
+ if (isSNCP()) { // SNCP协议
+ String host = this.serverConf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
+ this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getHostAddress() : host, this.serverConf.getIntValue("port"));
+ this.sncpGroup = application.globalNodes.get(this.sncpAddress);
+ //单向SNCP服务不需要对等group
+ //if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found info");
+ }
+
+ if (this.sncpAddress != null) this.resourceFactory.register(RESNAME_SERVER_ADDR, this.sncpAddress); //单点服务不会有 sncpAddress、sncpGroup
+ if (this.sncpGroup != null) this.resourceFactory.register(RESNAME_SERVER_GROUP, this.sncpGroup);
+ {
+ //设置root文件夹
+ String webroot = config.getValue("root", "root");
+ File myroot = new File(webroot);
+ if (!webroot.contains(":") && !webroot.startsWith("/")) {
+ myroot = new File(System.getProperty(Application.RESNAME_APP_HOME), webroot);
+ }
+
+ resourceFactory.register(Server.RESNAME_SERVER_ROOT, String.class, myroot.getCanonicalPath());
+ resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile());
+ resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
+
+ final String homepath = myroot.getCanonicalPath();
+ Server.loadLib(logger, config.getValue("lib", "").replace("${APP_HOME}", homepath) + ";" + homepath + "/lib/*;" + homepath + "/classes");
+ if (server != null) server.init(config);
+ }
+
+ initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
+
+ ClassFilter servletFilter = createServletClassFilter();
+ ClassFilter serviceFilter = createServiceClassFilter();
+ long s = System.currentTimeMillis();
+ if (servletFilter == null) {
+ ClassFilter.Loader.load(application.getHome(), serviceFilter);
+ } else {
+ ClassFilter.Loader.load(application.getHome(), serviceFilter, servletFilter);
+ }
+ long e = System.currentTimeMillis() - s;
+ logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
+ loadService(serviceFilter); //必须在servlet之前
+ loadServlet(servletFilter);
+ }
+
+ protected abstract void loadServlet(ClassFilter extends Servlet> servletFilter) throws Exception;
+
+ private void initResource() {
+ final NodeServer self = this;
+ //---------------------------------------------------------------------------------------------
+ final ResourceFactory appResFactory = application.getResourceFactory();
+ resourceFactory.add(DataSource.class, (ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
+ try {
+ if (field.getAnnotation(Resource.class) == null) return;
+ if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
+ DataSource source = new DataDefaultSource(resourceName);
+ application.dataSources.add(source);
+ appResFactory.register(resourceName, DataSource.class, source);
+
+ SncpClient client = null;
+ Transport sameGroupTransport = null;
+ List diffGroupTransports = null;
+ try {
+ Field ts = src.getClass().getDeclaredField("_sameGroupTransport");
+ ts.setAccessible(true);
+ sameGroupTransport = (Transport) ts.get(src);
+
+ ts = src.getClass().getDeclaredField("_diffGroupTransports");
+ ts.setAccessible(true);
+ diffGroupTransports = Arrays.asList((Transport[]) ts.get(src));
+
+ ts = src.getClass().getDeclaredField("_client");
+ ts.setAccessible(true);
+ client = (SncpClient) ts.get(src);
+ } catch (Exception e) {
+ throw new RuntimeException(src.getClass().getName() + " not found _sameGroupTransport or _diffGroupTransports at " + field, e);
+ }
+ final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
+ if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener
+ Service cacheListenerService = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, DataCacheListenerService.class, sncpAddr, sameGroupTransport, diffGroupTransports);
+ appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
+ final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
+ Set gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
+ ServiceWrapper wrapper = new ServiceWrapper(DataCacheListenerService.class, cacheListenerService, resourceName, sncpServer.getSncpGroup(), gs, null);
+ localServiceWrappers.add(wrapper);
+ sncpServer.consumerAccept(wrapper);
+ rf.inject(cacheListenerService, self);
+ if (fine) logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
+ }
+ field.set(src, source);
+ rf.inject(source, self); // 给 "datasource.nodeid" 赋值;
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "DataSource inject error", e);
+ }
+ });
+ resourceFactory.add(CacheSource.class, (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
+ try {
+ if (field.getAnnotation(Resource.class) == null) return;
+ if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 CacheSource
+
+ SncpClient client = null;
+ Transport sameGroupTransport = null;
+ List diffGroupTransports = null;
+ try {
+ Field ts = src.getClass().getDeclaredField("_sameGroupTransport");
+ ts.setAccessible(true);
+ sameGroupTransport = (Transport) ts.get(src);
+
+ ts = src.getClass().getDeclaredField("_diffGroupTransports");
+ ts.setAccessible(true);
+ Transport[] dts = (Transport[]) ts.get(src);
+ if (dts != null) diffGroupTransports = Arrays.asList(dts);
+
+ ts = src.getClass().getDeclaredField("_client");
+ ts.setAccessible(true);
+ client = (SncpClient) ts.get(src);
+ } catch (Exception e) {
+ throw new RuntimeException(src.getClass().getName() + " not found _sameGroupTransport or _diffGroupTransports at " + field, e);
+ }
+ final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
+ final CacheSourceService source = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, CacheSourceService.class, sncpAddr, sameGroupTransport, diffGroupTransports);
+ Type genericType = field.getGenericType();
+ ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
+ Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
+ source.setStoreType(pt == null ? Serializable.class : (Class) pt.getActualTypeArguments()[0], valType instanceof Class ? (Class) valType : Object.class);
+ if (field.getAnnotation(Transient.class) != null) source.setNeedStore(false); //必须在setStoreType之后
+ application.cacheSources.add(source);
+ appResFactory.register(resourceName, CacheSource.class, source);
+ field.set(src, source);
+ rf.inject(source, self); //
+ ((Service) source).init(null);
+
+ if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheSourceService
+ NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
+ Set gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
+ ServiceWrapper wrapper = new ServiceWrapper(CacheSourceService.class, (Service) source, resourceName, sncpServer.getSncpGroup(), gs, null);
+ sncpServer.getSncpServer().addService(wrapper);
+ if (finer) logger.finer("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
+ }
+ logger.finer("[" + Thread.currentThread().getName() + "] Load Source " + source);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "DataSource inject error", e);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void loadService(ClassFilter serviceFilter) throws Exception {
+ if (serviceFilter == null) return;
+ final String threadName = "[" + Thread.currentThread().getName() + "] ";
+ final Set> entrys = serviceFilter.getFilterEntrys();
+ ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
+
+ for (FilterEntry entry : entrys) { //service实现类
+ final Class extends Service> type = entry.getType();
+ if (Modifier.isFinal(type.getModifiers())) continue; //修饰final的类跳过
+ if (!Modifier.isPublic(type.getModifiers())) continue;
+ if (entry.getName().contains("$")) throw new RuntimeException(" value cannot contains '$' in " + entry.getProperty());
+ if (resourceFactory.find(entry.getName(), type) != null) continue; //Server加载Service时需要判断是否已经加载过了。
+ final HashSet groups = entry.getGroups(); //groups.isEmpty()表示没有配置groups属性。
+ if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup);
+
+ final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) //非SNCP的Server,通常是单点服务
+ || groups.contains(this.sncpGroup) //本地IP含在内的
+ || (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置
+ || type.getAnnotation(LocalService.class) != null;//本地模式
+ if (localed && (type.isInterface() || Modifier.isAbstract(type.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
+
+ Service service;
+ if (localed) { //本地模式
+ service = Sncp.createLocalService(entry.getName(), getExecutor(), application.getResourceFactory(), type, this.sncpAddress, loadTransport(this.sncpGroup), loadTransports(groups));
+ } else {
+ service = Sncp.createRemoteService(entry.getName(), getExecutor(), type, this.sncpAddress, loadTransport(groups));
+ }
+ if(SncpClient.parseMethod(type).isEmpty()) continue; //class没有可用的方法, 通常为BaseService
+ final ServiceWrapper wrapper = new ServiceWrapper(type, service, entry.getName(), localed ? this.sncpGroup : null, groups, entry.getProperty());
+ for (final Class restype : wrapper.getTypes()) {
+ if (resourceFactory.find(wrapper.getName(), restype) == null) {
+ regFactory.register(wrapper.getName(), restype, wrapper.getService());
+ } else if (isSNCP() && !entry.isAutoload()) {
+ throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
+ }
+ }
+ if (wrapper.isRemote()) {
+ remoteServiceWrappers.add(wrapper);
+ } else {
+ localServiceWrappers.add(wrapper);
+ if (consumer != null) consumer.accept(wrapper);
+ }
+ }
+ application.servicecdl.countDown();
+ application.servicecdl.await();
+
+ final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
+ //---------------- inject ----------------
+ new ArrayList<>(localServiceWrappers).forEach(y -> {
+ resourceFactory.inject(y.getService(), NodeServer.this);
+ });
+ remoteServiceWrappers.forEach(y -> {
+ resourceFactory.inject(y.getService(), NodeServer.this);
+ if (sb != null) {
+ sb.append(threadName).append(y.toSimpleString()).append(" loaded and injected").append(LINE_SEPARATOR);
+ }
+ });
+ //----------------- init -----------------
+ List swlist = new ArrayList<>(localServiceWrappers);
+ Collections.sort(swlist);
+ localServiceWrappers.clear();
+ localServiceWrappers.addAll(swlist);
+ final List slist = sb == null ? null : new CopyOnWriteArrayList<>();
+ localServiceWrappers.parallelStream().forEach(y -> {
+ long s = System.currentTimeMillis();
+ y.getService().init(y.getConf());
+ long e = System.currentTimeMillis() - s;
+ if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" loaded and init ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
+ });
+ Collections.sort(slist);
+ if (slist != null && sb != null) {
+ for (String s : slist) {
+ sb.append(s);
+ }
+ }
+ if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
+ }
+
+ protected List loadTransports(final HashSet groups) {
+ if (groups == null) return null;
+ final List transports = new ArrayList<>();
+ for (String group : groups) {
+ if (this.sncpGroup == null || !this.sncpGroup.equals(group)) {
+ transports.add(loadTransport(group));
+ }
+ }
+ return transports;
+ }
+
+ protected Transport loadTransport(final HashSet groups) {
+ if (groups == null || groups.isEmpty()) return null;
+ List tmpgroup = new ArrayList<>(groups);
+ Collections.sort(tmpgroup); //按字母排列顺序
+ final String groupid = tmpgroup.stream().collect(Collectors.joining(";"));
+ Transport transport = application.resourceFactory.find(groupid, Transport.class);
+ if (transport != null) return transport;
+ final List transports = new ArrayList<>();
+ for (String group : groups) {
+ transports.add(loadTransport(group));
+ }
+ Set addrs = new HashSet();
+ transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
+ Transport first = transports.get(0);
+ Transport newTransport = new Transport(groupid, application.findGroupProtocol(first.getName()), application.getWatchFactory(),
+ application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
+ synchronized (application.resourceFactory) {
+ transport = application.resourceFactory.find(groupid, Transport.class);
+ if (transport == null) {
+ transport = newTransport;
+ application.resourceFactory.register(groupid, transport);
+ }
+ }
+ return transport;
+ }
+
+ protected Transport loadTransport(final String group) {
+ if (group == null) return null;
+ Transport transport;
+ synchronized (application.resourceFactory) {
+ transport = application.resourceFactory.find(group, Transport.class);
+ if (transport != null) {
+ if (this.sncpAddress != null && !this.sncpAddress.equals(transport.getClientAddress())) {
+ throw new RuntimeException(transport + "repeat create on newClientAddress = " + this.sncpAddress + ", oldClientAddress = " + transport.getClientAddress());
+ }
+ return transport;
+ }
+ Set addrs = application.findGlobalGroup(group);
+ if (addrs == null) throw new RuntimeException("Not found = " + group + " on ");
+ transport = new Transport(group, application.findGroupProtocol(group), application.getWatchFactory(),
+ application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
+ application.resourceFactory.register(group, transport);
+ }
+ return transport;
+ }
+
+ protected abstract ClassFilter createServletClassFilter();
+
+ protected ClassFilter createServiceClassFilter() {
+ return createClassFilter(this.sncpGroup, null, Service.class, Annotation.class, "services", "service");
+ }
+
+ protected ClassFilter createClassFilter(final String localGroup, Class extends Annotation> ref,
+ Class inter, Class extends Annotation> ref2, String properties, String property) {
+ ClassFilter cf = new ClassFilter(ref, inter, null);
+ if (properties == null && properties == null) return cf;
+ if (this.serverConf == null) return cf;
+ AnyValue[] proplist = this.serverConf.getAnyValues(properties);
+ if (proplist == null || proplist.length < 1) return cf;
+ cf = null;
+ for (AnyValue list : proplist) {
+ DefaultAnyValue prop = null;
+ String sc = list.getValue("groups");
+ if (sc != null) {
+ sc = sc.trim();
+ if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1);
+ }
+ if (sc == null) sc = localGroup;
+ if (sc != null) {
+ prop = new AnyValue.DefaultAnyValue();
+ prop.addValue("groups", sc);
+ }
+ ClassFilter filter = new ClassFilter(ref, inter, prop);
+ for (AnyValue av : list.getAnyValues(property)) {
+ final AnyValue[] items = av.getAnyValues("property");
+ if (av instanceof DefaultAnyValue && items.length > 0) {
+ DefaultAnyValue dav = DefaultAnyValue.create();
+ final AnyValue.Entry[] strings = av.getStringEntrys();
+ if (strings != null) {
+ for (AnyValue.Entry en : strings) {
+ dav.addValue(en.name, en.getValue());
+ }
+ }
+ final AnyValue.Entry[] anys = av.getAnyEntrys();
+ if (anys != null) {
+ for (AnyValue.Entry en : anys) {
+ if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue());
+ }
+ }
+ DefaultAnyValue ps = DefaultAnyValue.create();
+ for (AnyValue item : items) {
+ ps.addValue(item.getValue("name"), item.getValue("value"));
+ }
+ dav.addValue("property", ps);
+ av = dav;
+ }
+ filter.filter(av, av.getValue("value"), false);
+ }
+ if (list.getBoolValue("autoload", true)) {
+ String includes = list.getValue("includes", "");
+ String excludes = list.getValue("excludes", "");
+ filter.setIncludePatterns(includes.split(";"));
+ filter.setExcludePatterns(excludes.split(";"));
+ } else if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载
+ filter.setRefused(true);
+ } else if (ref2 != Annotation.class) {
+ filter.setAnnotationClass(ref2);
+ }
+ cf = (cf == null) ? filter : cf.or(filter);
+ }
+ return cf;
+ }
+
+ public abstract InetSocketAddress getSocketAddress();
+
+ public boolean isSNCP() {
+ return false;
+ }
+
+ public InetSocketAddress getSncpAddress() {
+ return sncpAddress;
+ }
+
+ public String getSncpGroup() {
+ return sncpGroup;
+ }
+
+ public void start() throws IOException {
+ server.start();
+ }
+
+ public void shutdown() throws IOException {
+ final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
+ localServiceWrappers.forEach(y -> {
+ long s = System.currentTimeMillis();
+ y.getService().destroy(y.getConf());
+ long e = System.currentTimeMillis() - s;
+ if (e > 2 && sb != null) {
+ sb.append(y.toSimpleString()).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);
+ }
+ });
+ if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
+ server.shutdown();
+ }
+
+}
diff --git a/src/org/redkale/boot/NodeSncpServer.java b/src/org/redkale/boot/NodeSncpServer.java
new file mode 100644
index 000000000..538f3394e
--- /dev/null
+++ b/src/org/redkale/boot/NodeSncpServer.java
@@ -0,0 +1,79 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.boot;
+
+import java.net.*;
+import java.util.*;
+import java.util.logging.*;
+import org.redkale.net.*;
+import org.redkale.net.sncp.*;
+import org.redkale.util.*;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+@NodeProtocol({"SNCP"})
+public final class NodeSncpServer extends NodeServer {
+
+ private final SncpServer sncpServer;
+
+ public NodeSncpServer(Application application, AnyValue serconf) {
+ super(application, createServer(application, serconf));
+ this.sncpServer = (SncpServer) this.server;
+ this.consumer = sncpServer == null ? null : x -> sncpServer.addService(x);
+ }
+
+ private static Server createServer(Application application, AnyValue serconf) {
+ return new SncpServer(application.getStartTime(), application.getWatchFactory());
+ }
+
+ @Override
+ public InetSocketAddress getSocketAddress() {
+ return sncpServer == null ? null : sncpServer.getSocketAddress();
+ }
+
+ public void consumerAccept(ServiceWrapper wrapper) {
+ if (this.consumer != null) this.consumer.accept(wrapper);
+ }
+
+ @Override
+ public void init(AnyValue config) throws Exception {
+ super.init(config);
+ //-------------------------------------------------------------------
+ if (sncpServer == null) return; //调试时server才可能为null
+ final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
+ final String threadName = "[" + Thread.currentThread().getName() + "] ";
+ List servlets = sncpServer.getSncpServlets();
+ Collections.sort(servlets);
+ for (SncpServlet en : servlets) {
+ if (sb != null) sb.append(threadName).append(" Loaded ").append(en).append(LINE_SEPARATOR);
+ }
+ if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
+ }
+
+ @Override
+ public boolean isSNCP() {
+ return true;
+ }
+
+ public SncpServer getSncpServer() {
+ return sncpServer;
+ }
+
+ @Override
+ protected void loadServlet(ClassFilter extends Servlet> servletFilter) throws Exception {
+ }
+
+ @Override
+ protected ClassFilter createServletClassFilter() {
+ return null;
+ }
+
+}
diff --git a/src/org/redkale/boot/package-info.java b/src/org/redkale/boot/package-info.java
new file mode 100644
index 000000000..569d4b0ee
--- /dev/null
+++ b/src/org/redkale/boot/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 提供RedKale服务器的启动、初始化和加载功能
+ */
+package org.redkale.boot;
diff --git a/src/org/redkale/convert/AnyEncoder.java b/src/org/redkale/convert/AnyEncoder.java
new file mode 100644
index 000000000..9a2ff74fb
--- /dev/null
+++ b/src/org/redkale/convert/AnyEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.Type;
+
+/**
+ * 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。
+ *
+ * 详情见: http://www.redkale.org
+ * @author zhangjx
+ * @param 序列化的泛型类型
+ */
+public final class AnyEncoder implements Encodeable {
+
+ final ConvertFactory factory;
+
+ AnyEncoder(ConvertFactory factory) {
+ this.factory = factory;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void convertTo(final Writer out, final T value) {
+ if (value == null) {
+ out.wirteClassName(null);
+ out.writeNull();
+ } else {
+ out.wirteClassName(factory.getEntity(value.getClass()));
+ factory.loadEncoder(value.getClass()).convertTo(out, value);
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return Object.class;
+ }
+
+}
diff --git a/src/org/redkale/convert/ArrayDecoder.java b/src/org/redkale/convert/ArrayDecoder.java
new file mode 100644
index 000000000..3aa1c5e31
--- /dev/null
+++ b/src/org/redkale/convert/ArrayDecoder.java
@@ -0,0 +1,82 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * 对象数组的序列化,不包含int[]、long[]这样的primitive class数组.
+ * 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param 反解析的数组元素类型
+ */
+@SuppressWarnings("unchecked")
+public final class ArrayDecoder implements Decodeable {
+
+ private final Type type;
+
+ private final Type componentType;
+
+ private final Class componentClass;
+
+ private final Decodeable decoder;
+
+ public ArrayDecoder(final ConvertFactory factory, final Type type) {
+ this.type = type;
+ if (type instanceof GenericArrayType) {
+ Type t = ((GenericArrayType) type).getGenericComponentType();
+ this.componentType = t instanceof TypeVariable ? Object.class : t;
+ } else if ((type instanceof Class) && ((Class) type).isArray()) {
+ this.componentType = ((Class) type).getComponentType();
+ } else {
+ throw new ConvertException("(" + type + ") is not a array type");
+ }
+ if (this.componentType instanceof ParameterizedType) {
+ this.componentClass = (Class) ((ParameterizedType) this.componentType).getRawType();
+ } else {
+ this.componentClass = (Class) this.componentType;
+ }
+ factory.register(type, this);
+ this.decoder = factory.loadDecoder(this.componentType);
+ }
+
+ @Override
+ public T[] convertFrom(Reader in) {
+ final int len = in.readArrayB();
+ if (len == Reader.SIGN_NULL) return null;
+ final Decodeable localdecoder = this.decoder;
+ final List result = new ArrayList();
+ if (len == Reader.SIGN_NOLENGTH) {
+ while (in.hasNext()) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ }
+ in.readArrayE();
+ T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size());
+ return result.toArray(rs);
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.decoder + "}";
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+}
diff --git a/src/org/redkale/convert/ArrayEncoder.java b/src/org/redkale/convert/ArrayEncoder.java
new file mode 100644
index 000000000..1cc5c4b18
--- /dev/null
+++ b/src/org/redkale/convert/ArrayEncoder.java
@@ -0,0 +1,78 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.*;
+
+/**
+ * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组.
+ * 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有必要采用int存储。
+ * 支持一定程度的泛型。
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param 序列化的数组元素类型
+ */
+@SuppressWarnings("unchecked")
+public final class ArrayEncoder implements Encodeable {
+
+ private final Type type;
+
+ private final Type componentType;
+
+ private final Encodeable anyEncoder;
+
+ private final Encodeable encoder;
+
+ public ArrayEncoder(final ConvertFactory factory, final Type type) {
+ this.type = type;
+ if (type instanceof GenericArrayType) {
+ Type t = ((GenericArrayType) type).getGenericComponentType();
+ this.componentType = t instanceof TypeVariable ? Object.class : t;
+ } else if ((type instanceof Class) && ((Class) type).isArray()) {
+ this.componentType = ((Class) type).getComponentType();
+ } else {
+ throw new ConvertException("(" + type + ") is not a array type");
+ }
+ factory.register(type, this);
+ this.encoder = factory.loadEncoder(this.componentType);
+ this.anyEncoder = factory.getAnyEncoder();
+ }
+
+ @Override
+ public void convertTo(Writer out, T[] value) {
+ if (value == null) {
+ out.writeNull();
+ return;
+ }
+ if (value.length == 0) {
+ out.writeArrayB(0);
+ out.writeArrayE();
+ return;
+ }
+ out.writeArrayB(value.length);
+ final Type comp = this.componentType;
+ boolean first = true;
+ for (Object v : value) {
+ if (!first) out.writeArrayMark();
+ ((v != null && v.getClass() == comp) ? encoder : anyEncoder).convertTo(out, v);
+ if (first) first = false;
+ }
+ out.writeArrayE();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.encoder + "}";
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/src/org/redkale/convert/CollectionDecoder.java b/src/org/redkale/convert/CollectionDecoder.java
new file mode 100644
index 000000000..8c9ed24b8
--- /dev/null
+++ b/src/org/redkale/convert/CollectionDecoder.java
@@ -0,0 +1,72 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import org.redkale.util.Creator;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * 对象集合的反序列化.
+ * 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param 反解析的集合元素类型
+ */
+@SuppressWarnings("unchecked")
+public final class CollectionDecoder implements Decodeable> {
+
+ private final Type type;
+
+ private final Type componentType;
+
+ protected Creator> creator;
+
+ private final Decodeable decoder;
+
+ public CollectionDecoder(final ConvertFactory factory, final Type type) {
+ this.type = type;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pt = (ParameterizedType) type;
+ this.componentType = pt.getActualTypeArguments()[0];
+ this.creator = factory.loadCreator((Class) pt.getRawType());
+ factory.register(type, this);
+ this.decoder = factory.loadDecoder(this.componentType);
+ } else {
+ throw new ConvertException("collectiondecoder not support the type (" + type + ")");
+ }
+ }
+
+ @Override
+ public Collection convertFrom(Reader in) {
+ final int len = in.readArrayB();
+ if (len == Reader.SIGN_NULL) return null;
+ final Decodeable localdecoder = this.decoder;
+ final Collection result = this.creator.create();
+ if (len == Reader.SIGN_NOLENGTH) {
+ while (in.hasNext()) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ }
+ in.readArrayE();
+ return result;
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+}
diff --git a/src/org/redkale/convert/CollectionEncoder.java b/src/org/redkale/convert/CollectionEncoder.java
new file mode 100644
index 000000000..ebf3b211b
--- /dev/null
+++ b/src/org/redkale/convert/CollectionEncoder.java
@@ -0,0 +1,66 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.*;
+import java.util.Collection;
+
+/**
+ * 对象集合的序列化.
+ * 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ * 详情见: http://www.redkale.org
+ * @author zhangjx
+ * @param 序列化的集合元素类型
+ */
+@SuppressWarnings("unchecked")
+public final class CollectionEncoder implements Encodeable> {
+
+ private final Type type;
+
+ private final Encodeable encoder;
+
+ public CollectionEncoder(final ConvertFactory factory, final Type type) {
+ this.type = type;
+ if (type instanceof ParameterizedType) {
+ Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
+ if (t instanceof TypeVariable) {
+ this.encoder = factory.getAnyEncoder();
+ } else {
+ this.encoder = factory.loadEncoder(t);
+ }
+ } else {
+ this.encoder = factory.getAnyEncoder();
+ }
+ }
+
+ @Override
+ public void convertTo(Writer out, Collection value) {
+ if (value == null) {
+ out.writeNull();
+ return;
+ }
+ if (value.isEmpty()) {
+ out.writeArrayB(0);
+ out.writeArrayE();
+ return;
+ }
+ out.writeArrayB(value.size());
+ boolean first = true;
+ for (Object v : value) {
+ if (!first) out.writeArrayMark();
+ encoder.convertTo(out, v);
+ if (first) first = false;
+ }
+ out.writeArrayE();
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/src/org/redkale/convert/Convert.java b/src/org/redkale/convert/Convert.java
new file mode 100644
index 000000000..19b7ab5bf
--- /dev/null
+++ b/src/org/redkale/convert/Convert.java
@@ -0,0 +1,29 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+/**
+ * 序列化操作类
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param Reader输入的子类
+ * @param Writer输出的子类
+ */
+public abstract class Convert {
+
+ protected final ConvertFactory factory;
+
+ protected Convert(ConvertFactory factory) {
+ this.factory = factory;
+ }
+
+ public ConvertFactory getFactory() {
+ return this.factory;
+ }
+}
diff --git a/src/org/redkale/convert/ConvertColumn.java b/src/org/redkale/convert/ConvertColumn.java
new file mode 100644
index 000000000..53d91f62c
--- /dev/null
+++ b/src/org/redkale/convert/ConvertColumn.java
@@ -0,0 +1,47 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * 依附在setter、getter方法、字段进行简单的配置
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ */
+@Inherited
+@Documented
+@Target({METHOD, FIELD})
+@Retention(RUNTIME)
+@Repeatable(ConvertColumns.class)
+public @interface ConvertColumn {
+
+ /**
+ * 给字段取个别名
+ *
+ * @return 字段别名
+ */
+ String name() default "";
+
+ /**
+ * 解析/序列化时是否屏蔽该字段
+ *
+ * @return 是否屏蔽该字段
+ */
+ boolean ignore() default false;
+
+ /**
+ * 解析/序列化定制化的TYPE
+ *
+ * @return JSON or BSON or ALL
+ */
+ ConvertType type() default ConvertType.ALL;
+}
diff --git a/src/org/redkale/convert/ConvertColumnEntry.java b/src/org/redkale/convert/ConvertColumnEntry.java
new file mode 100644
index 000000000..a4e341481
--- /dev/null
+++ b/src/org/redkale/convert/ConvertColumnEntry.java
@@ -0,0 +1,68 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+/**
+ * ConvertColumn 对应的实体类
+ *
+ *
详情见: http://www.redkale.org
+ * @author zhangjx
+ */
+public final class ConvertColumnEntry {
+
+ private String name = "";
+
+ private boolean ignore;
+
+ private ConvertType convertType;
+
+ public ConvertColumnEntry() {
+ }
+
+ public ConvertColumnEntry(ConvertColumn column) {
+ if (column == null) return;
+ this.name = column.name();
+ this.ignore = column.ignore();
+ this.convertType = column.type();
+ }
+
+ public ConvertColumnEntry(String name, boolean ignore) {
+ this.name = name;
+ this.ignore = ignore;
+ this.convertType = ConvertType.ALL;
+ }
+
+ public ConvertColumnEntry(String name, boolean ignore, ConvertType convertType) {
+ this.name = name;
+ this.ignore = ignore;
+ this.convertType = convertType;
+ }
+
+ public String name() {
+ return name == null ? "" : name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean ignore() {
+ return ignore;
+ }
+
+ public void setIgnore(boolean ignore) {
+ this.ignore = ignore;
+ }
+
+ public ConvertType type() {
+ return convertType == null ? ConvertType.ALL : convertType;
+ }
+
+ public void setConvertType(ConvertType convertType) {
+ this.convertType = convertType;
+ }
+
+}
diff --git a/src/org/redkale/convert/ConvertColumns.java b/src/org/redkale/convert/ConvertColumns.java
new file mode 100644
index 000000000..4173069a4
--- /dev/null
+++ b/src/org/redkale/convert/ConvertColumns.java
@@ -0,0 +1,25 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * ConvertColumn 的多用类
+ *
+ *
详情见: http://www.redkale.org
+ * @author zhangjx
+ */
+@Inherited
+@Documented
+@Target({METHOD, FIELD})
+@Retention(RUNTIME)
+public @interface ConvertColumns {
+
+ ConvertColumn[] value();
+}
diff --git a/src/org/redkale/convert/ConvertEntity.java b/src/org/redkale/convert/ConvertEntity.java
new file mode 100644
index 000000000..9adb71cd2
--- /dev/null
+++ b/src/org/redkale/convert/ConvertEntity.java
@@ -0,0 +1,27 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * 用于类名的别名, 类似javax.persistence.Table
+ * 该值必须是全局唯一
+ * 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.wirteClassName(String value) 。
+ *
+ *
详情见: http://www.redkale.org
+ * @author zhangjx
+ */
+@Inherited
+@Documented
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface ConvertEntity {
+
+ String value();
+}
diff --git a/src/org/redkale/convert/ConvertException.java b/src/org/redkale/convert/ConvertException.java
new file mode 100644
index 000000000..7898ad319
--- /dev/null
+++ b/src/org/redkale/convert/ConvertException.java
@@ -0,0 +1,29 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+/**
+ *
+ *
详情见: http://www.redkale.org
+ * @author zhangjx
+ */
+public class ConvertException extends RuntimeException {
+
+ public ConvertException() {
+ super();
+ }
+
+ public ConvertException(String s) {
+ super(s);
+ }
+
+ public ConvertException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConvertException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/org/redkale/convert/ConvertFactory.java b/src/org/redkale/convert/ConvertFactory.java
new file mode 100644
index 000000000..a9a568db2
--- /dev/null
+++ b/src/org/redkale/convert/ConvertFactory.java
@@ -0,0 +1,486 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map;
+import java.lang.reflect.*;
+import java.math.BigInteger;
+import java.net.*;
+import java.nio.channels.*;
+import static org.redkale.convert.ext.InetAddressSimpledCoder.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.regex.*;
+import org.redkale.convert.ext.*;
+import org.redkale.util.*;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param Reader输入的子类
+ * @param Writer输出的子类
+ */
+@SuppressWarnings("unchecked")
+public abstract class ConvertFactory {
+
+ private final ConvertFactory parent;
+
+ protected Convert convert;
+
+ protected boolean tiny;
+
+ private final Encodeable anyEncoder = new AnyEncoder(this);
+
+ //-----------------------------------------------------------------------------------
+ private final ConcurrentHashMap creators = new ConcurrentHashMap();
+
+ private final ConcurrentHashMap entitys = new ConcurrentHashMap();
+
+ private final ConcurrentHashMap> decoders = new ConcurrentHashMap();
+
+ private final ConcurrentHashMap> encoders = new ConcurrentHashMap();
+
+ private final ConcurrentHashMap columnEntrys = new ConcurrentHashMap();
+
+ private final Set skipIgnores = new HashSet();
+
+ private boolean skipAllIgnore = false;
+
+ protected ConvertFactory(ConvertFactory parent, boolean tiny) {
+ this.tiny = tiny;
+ this.parent = parent;
+ if (parent == null) {
+ //---------------------------------------------------------
+ this.register(boolean.class, BoolSimpledCoder.instance);
+ this.register(Boolean.class, BoolSimpledCoder.instance);
+
+ this.register(byte.class, ByteSimpledCoder.instance);
+ this.register(Byte.class, ByteSimpledCoder.instance);
+
+ this.register(short.class, ShortSimpledCoder.instance);
+ this.register(Short.class, ShortSimpledCoder.instance);
+
+ this.register(char.class, CharSimpledCoder.instance);
+ this.register(Character.class, CharSimpledCoder.instance);
+
+ this.register(int.class, IntSimpledCoder.instance);
+ this.register(Integer.class, IntSimpledCoder.instance);
+
+ this.register(long.class, LongSimpledCoder.instance);
+ this.register(Long.class, LongSimpledCoder.instance);
+
+ this.register(float.class, FloatSimpledCoder.instance);
+ this.register(Float.class, FloatSimpledCoder.instance);
+
+ this.register(double.class, DoubleSimpledCoder.instance);
+ this.register(Double.class, DoubleSimpledCoder.instance);
+
+ this.register(Number.class, NumberSimpledCoder.instance);
+ this.register(String.class, StringSimpledCoder.instance);
+ this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
+ this.register(java.util.Date.class, DateSimpledCoder.instance);
+ this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
+ this.register(InetAddress.class, InetAddressSimpledCoder.instance);
+ this.register(DLong.class, DLongSimpledCoder.instance);
+ this.register(Class.class, TypeSimpledCoder.instance);
+ this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
+ this.register(Pattern.class, PatternSimpledCoder.instance);
+ this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
+ this.register(URL.class, URLSimpledCoder.instance);
+ this.register(URI.class, URISimpledCoder.instance);
+ //---------------------------------------------------------
+ this.register(boolean[].class, BoolArraySimpledCoder.instance);
+ this.register(byte[].class, ByteArraySimpledCoder.instance);
+ this.register(short[].class, ShortArraySimpledCoder.instance);
+ this.register(char[].class, CharArraySimpledCoder.instance);
+ this.register(int[].class, IntArraySimpledCoder.instance);
+ this.register(long[].class, LongArraySimpledCoder.instance);
+ this.register(float[].class, FloatArraySimpledCoder.instance);
+ this.register(double[].class, DoubleArraySimpledCoder.instance);
+ this.register(String[].class, StringArraySimpledCoder.instance);
+ //---------------------------------------------------------
+ }
+ }
+
+ public ConvertFactory parent() {
+ return this.parent;
+ }
+
+ public abstract ConvertType getConvertType();
+
+ public abstract boolean isReversible();
+
+ public abstract ConvertFactory createChild();
+
+ public abstract ConvertFactory createChild(boolean tiny);
+
+ public Convert getConvert() {
+ return convert;
+ }
+
+ public ConvertFactory tiny(boolean tiny) {
+ this.tiny = tiny;
+ return this;
+ }
+
+ public ConvertColumnEntry findRef(AccessibleObject element) {
+ if (element == null) return null;
+ ConvertColumnEntry en = this.columnEntrys.get(element);
+ if (en != null) return en;
+ final ConvertType ct = this.getConvertType();
+ ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class);
+ if (ccs.length == 0 && element instanceof Method) {
+ final Method method = (Method) element;
+ String fieldName = readGetSetFieldName(method);
+ if (fieldName != null) {
+ try {
+ ccs = method.getDeclaringClass().getDeclaredField(fieldName).getAnnotationsByType(ConvertColumn.class);
+ } catch (Exception e) { //说明没有该字段,忽略异常
+ }
+ }
+ }
+ for (ConvertColumn ref : ccs) {
+ if (ref.type().contains(ct)) {
+ ConvertColumnEntry entry = new ConvertColumnEntry(ref);
+ if (skipAllIgnore) {
+ entry.setIgnore(false);
+ return entry;
+ }
+ if (skipIgnores.isEmpty()) return entry;
+ if (skipIgnores.contains(((Member) element).getDeclaringClass())) entry.setIgnore(false);
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ static String readGetSetFieldName(Method method) {
+ if (method == null) return null;
+ String fname = method.getName();
+ if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname;
+ fname = fname.substring(fname.startsWith("is") ? 2 : 3);
+ if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
+ fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
+ } else if (fname.length() == 1) {
+ fname = "" + Character.toLowerCase(fname.charAt(0));
+ }
+ return fname;
+ }
+
+ final String getEntity(Class clazz) {
+ ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class);
+ if (ce != null && findEntity(ce.value()) == null) entitys.put(ce.value(), clazz);
+ return ce == null ? clazz.getName() : ce.value();
+ }
+
+ private Class findEntity(String name) {
+ Class clazz = entitys.get(name);
+ return parent == null ? clazz : parent.findEntity(name);
+ }
+
+ final Class getEntity(String name) {
+ Class clazz = findEntity(name);
+ try {
+ return clazz == null ? Class.forName(name) : clazz;
+ } catch (Exception ex) {
+ throw new ConvertException("convert entity is " + name, ex);
+ }
+ }
+
+ /**
+ * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false
+ *
+ * @param skipIgnore 是否忽略Ignore注解
+ */
+ public final void registerSkipAllIgnore(final boolean skipIgnore) {
+ this.skipAllIgnore = skipIgnore;
+ }
+
+ /**
+ * 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false
+ *
+ * @param type 指定的类
+ */
+ public final void registerSkipIgnore(final Class type) {
+ skipIgnores.add(type);
+ }
+
+ public final void register(final Class type, boolean ignore, String... columns) {
+ for (String column : columns) {
+ register(type, column, new ConvertColumnEntry(column, ignore));
+ }
+ }
+
+ public final boolean register(final Class type, String column, ConvertColumnEntry entry) {
+ if (type == null || column == null || entry == null) return false;
+ try {
+ final Field field = type.getDeclaredField(column);
+ String get = "get";
+ if (field.getType() == boolean.class || field.getType() == Boolean.class) get = "is";
+ char[] cols = column.toCharArray();
+ cols[0] = Character.toUpperCase(cols[0]);
+ String col2 = new String(cols);
+ try {
+ register(type.getMethod(get + col2), entry);
+ } catch (Exception ex) {
+ }
+ try {
+ register(type.getMethod("set" + col2, field.getType()), entry);
+ } catch (Exception ex) {
+ }
+ return register(field, entry);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public final boolean register(final AccessibleObject field, final ConvertColumnEntry entry) {
+ if (field == null || entry == null) return false;
+ this.columnEntrys.put(field, entry);
+ return true;
+ }
+
+ public final void reloadCoder(final Type type) {
+ this.register(type, this.createDecoder(type));
+ this.register(type, this.createEncoder(type));
+ }
+
+ public final void reloadCoder(final Type type, final Class clazz) {
+ this.register(type, this.createDecoder(type, clazz));
+ this.register(type, this.createEncoder(type, clazz));
+ }
+
+ public final void register(final Class clazz, final Creator extends E> creator) {
+ creators.put(clazz, creator);
+ }
+
+ public final Creator findCreator(Class type) {
+ Creator creator = creators.get(type);
+ if (creator != null) return creator;
+ return this.parent == null ? null : this.parent.findCreator(type);
+ }
+
+ public final Creator loadCreator(Class type) {
+ Creator result = findCreator(type);
+ if (result == null) {
+ result = Creator.create(type);
+ if (result != null) creators.put(type, result);
+ }
+ return result;
+ }
+
+ //----------------------------------------------------------------------
+ public final Encodeable getAnyEncoder() {
+ return (Encodeable) anyEncoder;
+ }
+
+ public final void register(final Type clazz, final SimpledCoder coder) {
+ decoders.put(clazz, coder);
+ encoders.put(clazz, coder);
+ }
+
+ public final void register(final Type clazz, final Decodeable decoder) {
+ decoders.put(clazz, decoder);
+ }
+
+ public final void register(final Type clazz, final Encodeable printer) {
+ encoders.put(clazz, printer);
+ }
+
+ public final Decodeable findDecoder(final Type type) {
+ Decodeable rs = (Decodeable) decoders.get(type);
+ if (rs != null) return rs;
+ return this.parent == null ? null : this.parent.findDecoder(type);
+ }
+
+ public final Encodeable findEncoder(final Type type) {
+ Encodeable rs = (Encodeable) encoders.get(type);
+ if (rs != null) return rs;
+ return this.parent == null ? null : this.parent.findEncoder(type);
+ }
+
+ public final Decodeable loadDecoder(final Type type) {
+ Decodeable decoder = findDecoder(type);
+ if (decoder != null) return decoder;
+ if (type instanceof GenericArrayType) return new ArrayDecoder(this, type);
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof TypeVariable) { // e.g. extends E>
+ final TypeVariable tv = (TypeVariable) type;
+ Class cz = tv.getBounds().length == 0 ? Object.class : null;
+ for (Type f : tv.getBounds()) {
+ if (f instanceof Class) {
+ cz = (Class) f;
+ break;
+ }
+ }
+ clazz = cz;
+ if (cz == null) throw new ConvertException("not support the type (" + type + ")");
+ } else if (type instanceof WildcardType) { // e.g. extends Serializable>
+ final WildcardType wt = (WildcardType) type;
+ Class cz = null;
+ for (Type f : wt.getUpperBounds()) {
+ if (f instanceof Class) {
+ cz = (Class) f;
+ break;
+ }
+ }
+ clazz = cz;
+ if (cz == null) throw new ConvertException("not support the type (" + type + ")");
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ decoder = findDecoder(clazz);
+ if (decoder != null) return decoder;
+ return createDecoder(type, clazz);
+ }
+
+ public final Decodeable createDecoder(final Type type) {
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ return createDecoder(type, clazz);
+ }
+
+ private Decodeable createDecoder(final Type type, final Class clazz) {
+ Decodeable decoder = null;
+ ObjectDecoder od = null;
+ if (clazz.isEnum()) {
+ decoder = new EnumSimpledCoder(clazz);
+ } else if (clazz.isArray()) {
+ decoder = new ArrayDecoder(this, type);
+ } else if (Collection.class.isAssignableFrom(clazz)) {
+ decoder = new CollectionDecoder(this, type);
+ } else if (Map.class.isAssignableFrom(clazz)) {
+ decoder = new MapDecoder(this, type);
+ } else if (clazz == Object.class) {
+ od = new ObjectDecoder(type);
+ decoder = od;
+ } else if (!clazz.getName().startsWith("java.")) {
+ Decodeable simpleCoder = null;
+ for (final Method method : clazz.getDeclaredMethods()) {
+ if (!Modifier.isStatic(method.getModifiers())) continue;
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) continue;
+ if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue;
+ if (!Decodeable.class.isAssignableFrom(method.getReturnType())) continue;
+ try {
+ method.setAccessible(true);
+ simpleCoder = (Decodeable) method.invoke(null, this);
+ break;
+ } catch (Exception e) {
+ }
+ }
+ if (simpleCoder == null) {
+ od = new ObjectDecoder(type);
+ decoder = od;
+ } else {
+ decoder = simpleCoder;
+ }
+ }
+ if (decoder == null) throw new ConvertException("not support the type (" + type + ")");
+ register(type, decoder);
+ if (od != null) od.init(this);
+ return decoder;
+ }
+
+ public final Encodeable loadEncoder(final Type type) {
+ Encodeable encoder = findEncoder(type);
+ if (encoder != null) return encoder;
+ if (type instanceof GenericArrayType) return new ArrayEncoder(this, type);
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof TypeVariable) {
+ TypeVariable tv = (TypeVariable) type;
+ Type t = Object.class;
+ if (tv.getBounds().length == 1) {
+ t = tv.getBounds()[0];
+ }
+ if (!(t instanceof Class)) t = Object.class;
+ clazz = (Class) t;
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ encoder = findEncoder(clazz);
+ if (encoder != null) return encoder;
+ return createEncoder(type, clazz);
+ }
+
+ public final Encodeable createEncoder(final Type type) {
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ return createEncoder(type, clazz);
+ }
+
+ private Encodeable createEncoder(final Type type, final Class clazz) {
+ Encodeable encoder = null;
+ ObjectEncoder oe = null;
+ if (clazz.isEnum()) {
+ encoder = new EnumSimpledCoder(clazz);
+ } else if (clazz.isArray()) {
+ encoder = new ArrayEncoder(this, type);
+ } else if (Collection.class.isAssignableFrom(clazz)) {
+ encoder = new CollectionEncoder(this, type);
+ } else if (Map.class.isAssignableFrom(clazz)) {
+ encoder = new MapEncoder(this, type);
+ } else if (clazz == Object.class) {
+ return (Encodeable) this.anyEncoder;
+ } else if (!clazz.getName().startsWith("java.")) {
+ Encodeable simpleCoder = null;
+ for (final Method method : clazz.getDeclaredMethods()) {
+ if (!Modifier.isStatic(method.getModifiers())) continue;
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) continue;
+ if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue;
+ if (!Encodeable.class.isAssignableFrom(method.getReturnType())) continue;
+ try {
+ method.setAccessible(true);
+ simpleCoder = (Encodeable) method.invoke(null, this);
+ break;
+ } catch (Exception e) {
+ }
+ }
+ if (simpleCoder == null) {
+ oe = new ObjectEncoder(type);
+ encoder = oe;
+ } else {
+ encoder = simpleCoder;
+ }
+ }
+ if (encoder == null) throw new ConvertException("not support the type (" + type + ")");
+ register(type, encoder);
+ if (oe != null) oe.init(this);
+ return encoder;
+
+ }
+
+}
diff --git a/src/org/redkale/convert/ConvertType.java b/src/org/redkale/convert/ConvertType.java
new file mode 100644
index 000000000..070ac5a86
--- /dev/null
+++ b/src/org/redkale/convert/ConvertType.java
@@ -0,0 +1,29 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+/**
+ *
+ * 详情见: http://www.redkale.org
+ * @author zhangjx
+ */
+public enum ConvertType {
+
+ JSON(1),
+ BSON(2),
+ ALL(127);
+
+ private final int value;
+
+ private ConvertType(int v) {
+ this.value = v;
+ }
+
+ public boolean contains(ConvertType type) {
+ if (type == null) return false;
+ return this.value >= type.value && (this.value & type.value) > 0;
+ }
+}
diff --git a/src/org/redkale/convert/DeMember.java b/src/org/redkale/convert/DeMember.java
new file mode 100644
index 000000000..3278c5618
--- /dev/null
+++ b/src/org/redkale/convert/DeMember.java
@@ -0,0 +1,85 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.*;
+import org.redkale.util.Attribute;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param Reader输入的子类
+ * @param 字段依附的类
+ * @param 字段的数据类型
+ */
+@SuppressWarnings("unchecked")
+public final class DeMember implements Comparable> {
+
+ protected final Attribute attribute;
+
+ protected Decodeable decoder;
+
+ public DeMember(final Attribute attribute) {
+ this.attribute = attribute;
+ }
+
+ public DeMember(Attribute attribute, Decodeable decoder) {
+ this(attribute);
+ this.decoder = decoder;
+ }
+
+ public static DeMember create(final ConvertFactory factory, final Class clazz, final String fieldname) {
+ try {
+ Field field = clazz.getDeclaredField(fieldname);
+ return new DeMember<>(Attribute.create(field), factory.loadDecoder(field.getGenericType()));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public final boolean match(String name) {
+ return attribute.field().equals(name);
+ }
+
+ public final void read(R in, T obj) {
+ this.attribute.set(obj, decoder.convertFrom(in));
+ }
+
+ public final F read(R in) {
+ return decoder.convertFrom(in);
+ }
+
+ public Attribute getAttribute() {
+ return this.attribute;
+ }
+
+ @Override
+ public final int compareTo(DeMember o) {
+ if (o == null) return 1;
+ return this.attribute.field().compareTo(o.attribute.field());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof DeMember)) return false;
+ DeMember other = (DeMember) obj;
+ return compareTo(other) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.attribute.field().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "DeMember{" + "attribute=" + attribute.field() + ", decoder=" + decoder + '}';
+ }
+}
diff --git a/src/org/redkale/convert/Decodeable.java b/src/org/redkale/convert/Decodeable.java
new file mode 100644
index 000000000..c4f1a6767
--- /dev/null
+++ b/src/org/redkale/convert/Decodeable.java
@@ -0,0 +1,30 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.Type;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param Reader输入的子类
+ * @param 反解析的数据类型
+ */
+public interface Decodeable {
+
+ public T convertFrom(final R in);
+
+ /**
+ * 泛型映射接口
+ *
+ * @return 反解析的数据类型
+ */
+ public Type getType();
+
+}
diff --git a/src/org/redkale/convert/EnMember.java b/src/org/redkale/convert/EnMember.java
new file mode 100644
index 000000000..e31dcf2bb
--- /dev/null
+++ b/src/org/redkale/convert/EnMember.java
@@ -0,0 +1,78 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.*;
+import org.redkale.util.Attribute;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param Writer输出的子类
+ * @param 字段依附的类
+ * @param 字段的数据类型
+ */
+@SuppressWarnings("unchecked")
+public final class EnMember implements Comparable> {
+
+ final Attribute attribute;
+
+ final Encodeable encoder;
+
+ final boolean istring;
+
+ //final boolean isnumber;
+ final boolean isbool;
+
+ public EnMember(Attribute attribute, Encodeable encoder) {
+ this.attribute = attribute;
+ this.encoder = encoder;
+ Class t = attribute.type();
+ this.istring = CharSequence.class.isAssignableFrom(t);
+ this.isbool = t == Boolean.class || t == boolean.class;
+ //this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive());
+ }
+
+ public static EnMember create(final ConvertFactory factory, final Class clazz, final String fieldname) {
+ try {
+ Field field = clazz.getDeclaredField(fieldname);
+ return new EnMember<>(Attribute.create(field), factory.loadEncoder(field.getGenericType()));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public final boolean match(String name) {
+ return attribute.field().equals(name);
+ }
+
+ @Override
+ public final int compareTo(EnMember o) {
+ if (o == null) return 1;
+ return this.attribute.field().compareTo(o.attribute.field());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof EnMember)) return false;
+ EnMember other = (EnMember) obj;
+ return compareTo(other) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.attribute.field().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "EnMember{" + "attribute=" + attribute.field() + ", encoder=" + encoder + '}';
+ }
+}
diff --git a/src/org/redkale/convert/Encodeable.java b/src/org/redkale/convert/Encodeable.java
new file mode 100644
index 000000000..9a2a96191
--- /dev/null
+++ b/src/org/redkale/convert/Encodeable.java
@@ -0,0 +1,30 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import java.lang.reflect.Type;
+
+/**
+ *
+ *
+ * 详情见: http://www.redkale.org
+ *
+ * @author zhangjx
+ * @param Writer输出的子类
+ * @param 序列化的数据类型
+ */
+public interface Encodeable {
+
+ public void convertTo(final W out, T value);
+
+ /**
+ * 泛型映射接口
+ *
+ * @return 返回序列化对象类的数据类型
+ */
+ public Type getType();
+
+}
diff --git a/src/org/redkale/convert/MapDecoder.java b/src/org/redkale/convert/MapDecoder.java
new file mode 100644
index 000000000..22fb3a3c8
--- /dev/null
+++ b/src/org/redkale/convert/MapDecoder.java
@@ -0,0 +1,79 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.redkale.convert;
+
+import org.redkale.util.Creator;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ *
+ * 详情见: http://www.redkale.org
+ * @author zhangjx
+ * @param Map key的数据类型
+ * @param Map value的数据类型
+ */
+@SuppressWarnings("unchecked")
+public final class MapDecoder implements Decodeable> {
+
+ private final Type type;
+
+ private final Type keyType;
+
+ private final Type valueType;
+
+ protected Creator