From 76564675e0e68ce07a9b7f388c7b503ff778c865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=87=E6=AD=A3=E7=BB=8F=E5=93=A5=E5=93=A5?= Date: Sat, 19 Jan 2019 18:39:06 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=96=20=E5=8F=91=E5=B8=83=E7=89=88?= =?UTF-8?q?=E6=9C=AC1.0.0=E5=B9=B6=E7=BC=96=E5=86=99readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 46 +++ .vscode/solution-explorer/class.cs-template | 8 + .vscode/solution-explorer/class.ts-template | 3 + .vscode/solution-explorer/class.vb-template | 9 + .vscode/solution-explorer/default.ts-template | 3 + .vscode/solution-explorer/enum.cs-template | 8 + .../solution-explorer/interface.cs-template | 8 + .../solution-explorer/interface.ts-template | 3 + .vscode/solution-explorer/template-list.json | 46 +++ .../solution-explorer/template-parameters.js | 16 + .vscode/tasks.json | 15 + DotXxlJob.sln | 30 ++ README.md | 170 ++++++++- RELEASE_NOTES.md | 6 +- build.cake | 26 +- .../Extensions/XxlJobExecutorMiddleware.cs | 7 +- src/DotXxlJob.Core/AdminClient.cs | 69 ++-- .../Config/XxlJobExecutorOptions.cs | 28 +- .../DefaultHandlers/SimpleHttpJobHandler.cs | 38 +- src/DotXxlJob.Core/DotXxlJob.Core.csproj | 25 +- src/DotXxlJob.Core/ExecutorRegistry.cs | 67 +++- .../Extensions/ServiceCollectionExtensions.cs | 25 +- .../Hosted/JobExecuteHostedService.cs | 21 +- src/DotXxlJob.Core/IExecutorRegistry.cs | 8 +- src/DotXxlJob.Core/IJobHandlerFactory.cs | 1 - src/DotXxlJob.Core/Internal/Constants.cs | 21 +- .../Internal/HessianObjectConvert.cs | 12 +- .../Internal/HessianSerializer.cs | 16 +- src/DotXxlJob.Core/Internal/IPUtility.cs | 132 ++++++- src/DotXxlJob.Core/Internal/Preconditions.cs | 84 ++++- src/DotXxlJob.Core/JobDispatcher.cs | 1 + src/DotXxlJob.Core/Logger/JobLogger.cs | 14 +- .../Model/HandleCallbackParam.cs | 2 +- src/DotXxlJob.Core/Model/JavaClass.cs | 2 +- src/DotXxlJob.Core/Model/LogResult.cs | 2 +- src/DotXxlJob.Core/Model/RegistryParam.cs | 5 +- src/DotXxlJob.Core/Model/ReturnT.cs | 2 +- src/DotXxlJob.Core/Model/RpcRequest.cs | 2 +- src/DotXxlJob.Core/Model/RpcResponse.cs | 2 +- src/DotXxlJob.Core/Model/TriggerParam.cs | 2 +- src/DotXxlJob.Core/Queue/CallbackTaskQueue.cs | 10 +- src/DotXxlJob.Core/Queue/JobTaskQueue.cs | 15 +- .../Queue/RetryCallbackTaskQueue.cs | 27 +- src/DotXxlJob.Core/TaskExecutorFactory.cs | 16 +- src/Hessian/Hessian.csproj | 24 +- .../DotXxlJob.Core.Tests.csproj | 19 + tests/DotXxlJob.Core.Tests/UnitTest1.cs | 13 + tests/Hessian.NET.Tests/ByteArray.cs | 33 -- .../Hessian.NET.Tests.csproj | 24 -- .../HessianInputReaderTests.cs | 246 ------------- .../HessianOutputWriterTests.cs | 326 ------------------ .../HessianSerializerTests.cs | 167 --------- tests/Hessian.Tests/Hessian.Tests.csproj | 19 + tests/Hessian.Tests/UnitTest1.cs | 13 + tools/packages.config | 4 + 55 files changed, 1006 insertions(+), 935 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/solution-explorer/class.cs-template create mode 100644 .vscode/solution-explorer/class.ts-template create mode 100644 .vscode/solution-explorer/class.vb-template create mode 100644 .vscode/solution-explorer/default.ts-template create mode 100644 .vscode/solution-explorer/enum.cs-template create mode 100644 .vscode/solution-explorer/interface.cs-template create mode 100644 .vscode/solution-explorer/interface.ts-template create mode 100644 .vscode/solution-explorer/template-list.json create mode 100644 .vscode/solution-explorer/template-parameters.js create mode 100644 .vscode/tasks.json create mode 100644 tests/DotXxlJob.Core.Tests/DotXxlJob.Core.Tests.csproj create mode 100644 tests/DotXxlJob.Core.Tests/UnitTest1.cs delete mode 100644 tests/Hessian.NET.Tests/ByteArray.cs delete mode 100644 tests/Hessian.NET.Tests/Hessian.NET.Tests.csproj delete mode 100644 tests/Hessian.NET.Tests/HessianInputReaderTests.cs delete mode 100644 tests/Hessian.NET.Tests/HessianOutputWriterTests.cs delete mode 100644 tests/Hessian.NET.Tests/HessianSerializerTests.cs create mode 100644 tests/Hessian.Tests/Hessian.Tests.csproj create mode 100644 tests/Hessian.Tests/UnitTest1.cs create mode 100644 tools/packages.config diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..29b256d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,46 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/samples/ASPNetCoreExecutor/bin/Debug/netcoreapp2.2/ASPNetCoreExecutor.dll", + "args": [], + "cwd": "${workspaceFolder}/samples/ASPNetCoreExecutor", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart", + "launchBrowser": { + "enabled": true, + "args": "${auto-detect-url}", + "windows": { + "command": "cmd.exe", + "args": "/C start ${auto-detect-url}" + }, + "osx": { + "command": "open" + }, + "linux": { + "command": "xdg-open" + } + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ,] +} \ No newline at end of file diff --git a/.vscode/solution-explorer/class.cs-template b/.vscode/solution-explorer/class.cs-template new file mode 100644 index 0000000..015da46 --- /dev/null +++ b/.vscode/solution-explorer/class.cs-template @@ -0,0 +1,8 @@ +using System; + +namespace {{namespace}} +{ + public class {{name}} + { + } +} diff --git a/.vscode/solution-explorer/class.ts-template b/.vscode/solution-explorer/class.ts-template new file mode 100644 index 0000000..ff2edef --- /dev/null +++ b/.vscode/solution-explorer/class.ts-template @@ -0,0 +1,3 @@ +export class {{name}} { + +} \ No newline at end of file diff --git a/.vscode/solution-explorer/class.vb-template b/.vscode/solution-explorer/class.vb-template new file mode 100644 index 0000000..38ef67f --- /dev/null +++ b/.vscode/solution-explorer/class.vb-template @@ -0,0 +1,9 @@ +Imports System + +Namespace {{namespace}} + + Public Class {{name}} + + End Class + +End Namespace diff --git a/.vscode/solution-explorer/default.ts-template b/.vscode/solution-explorer/default.ts-template new file mode 100644 index 0000000..04af870 --- /dev/null +++ b/.vscode/solution-explorer/default.ts-template @@ -0,0 +1,3 @@ +export default {{name}} { + +} \ No newline at end of file diff --git a/.vscode/solution-explorer/enum.cs-template b/.vscode/solution-explorer/enum.cs-template new file mode 100644 index 0000000..7d4cdee --- /dev/null +++ b/.vscode/solution-explorer/enum.cs-template @@ -0,0 +1,8 @@ +using System; + +namespace {{namespace}} +{ + public enum {{name}} + { + } +} diff --git a/.vscode/solution-explorer/interface.cs-template b/.vscode/solution-explorer/interface.cs-template new file mode 100644 index 0000000..6b5dec1 --- /dev/null +++ b/.vscode/solution-explorer/interface.cs-template @@ -0,0 +1,8 @@ +using System; + +namespace {{namespace}} +{ + public interface {{name}} + { + } +} diff --git a/.vscode/solution-explorer/interface.ts-template b/.vscode/solution-explorer/interface.ts-template new file mode 100644 index 0000000..3ea404b --- /dev/null +++ b/.vscode/solution-explorer/interface.ts-template @@ -0,0 +1,3 @@ +export interface {{name}} { + +} \ No newline at end of file diff --git a/.vscode/solution-explorer/template-list.json b/.vscode/solution-explorer/template-list.json new file mode 100644 index 0000000..2849622 --- /dev/null +++ b/.vscode/solution-explorer/template-list.json @@ -0,0 +1,46 @@ +{ + "templates": [ + { + "name": "Class", + "extension": "cs", + "file": "./class.cs-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Interface", + "extension": "cs", + "file": "./interface.cs-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Enum", + "extension": "cs", + "file": "./enum.cs-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Class", + "extension": "ts", + "file": "./class.ts-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Interface", + "extension": "ts", + "file": "./interface.ts-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Default", + "extension": "ts", + "file": "./default.ts-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Class", + "extension": "vb", + "file": "./class.vb-template", + "parameters": "./template-parameters.js" + } + ] +} \ No newline at end of file diff --git a/.vscode/solution-explorer/template-parameters.js b/.vscode/solution-explorer/template-parameters.js new file mode 100644 index 0000000..58fa432 --- /dev/null +++ b/.vscode/solution-explorer/template-parameters.js @@ -0,0 +1,16 @@ +var path = require("path"); + +module.exports = function(filename, projectPath, folderPath) { + var namespace = "Unknown"; + if (projectPath) { + namespace = path.basename(projectPath, path.extname(projectPath)); + if (folderPath) { + namespace += "." + folderPath.replace(path.dirname(projectPath), "").substring(1).replace(/[\\\/]/g, "."); + } + } + + return { + namespace: namespace, + name: path.basename(filename, path.extname(filename)) + } +}; \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..cb5b8b5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/samples/ASPNetCoreExecutor/ASPNetCoreExecutor.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/DotXxlJob.sln b/DotXxlJob.sln index 9fe021a..f527a9c 100644 --- a/DotXxlJob.sln +++ b/DotXxlJob.sln @@ -17,6 +17,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HessianReader", "samples\He EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hessian", "src\Hessian\Hessian.csproj", "{BD9B8108-6528-430F-AD28-6F8434A29F55}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hessian.Tests", "tests\Hessian.Tests\Hessian.Tests.csproj", "{A51FACF0-C90F-43D1-898D-CD171977C1A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotXxlJob.Core.Tests", "tests\DotXxlJob.Core.Tests\DotXxlJob.Core.Tests.csproj", "{81C60471-7C1C-48CE-98C0-F252C267AC9F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +79,30 @@ Global {BD9B8108-6528-430F-AD28-6F8434A29F55}.Release|x64.Build.0 = Release|Any CPU {BD9B8108-6528-430F-AD28-6F8434A29F55}.Release|x86.ActiveCfg = Release|Any CPU {BD9B8108-6528-430F-AD28-6F8434A29F55}.Release|x86.Build.0 = Release|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Debug|x64.Build.0 = Debug|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Debug|x86.ActiveCfg = Debug|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Debug|x86.Build.0 = Debug|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Release|Any CPU.Build.0 = Release|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Release|x64.ActiveCfg = Release|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Release|x64.Build.0 = Release|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Release|x86.ActiveCfg = Release|Any CPU + {A51FACF0-C90F-43D1-898D-CD171977C1A1}.Release|x86.Build.0 = Release|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Debug|x64.ActiveCfg = Debug|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Debug|x64.Build.0 = Debug|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Debug|x86.ActiveCfg = Debug|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Debug|x86.Build.0 = Debug|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Release|Any CPU.Build.0 = Release|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Release|x64.ActiveCfg = Release|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Release|x64.Build.0 = Release|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Release|x86.ActiveCfg = Release|Any CPU + {81C60471-7C1C-48CE-98C0-F252C267AC9F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -84,6 +112,8 @@ Global {DC9E5AF3-18FF-4713-BDB4-672E47ADA4E5} = {E959F9B5-F3EB-48B1-B842-2CDDFDB01900} {F822B528-95FD-40B4-9EE0-3AE8878075AC} = {E959F9B5-F3EB-48B1-B842-2CDDFDB01900} {BD9B8108-6528-430F-AD28-6F8434A29F55} = {97756BA5-1E7C-4536-A49E-AE2190C0E6A5} + {A51FACF0-C90F-43D1-898D-CD171977C1A1} = {352EC932-F112-4A2F-9DC3-F0761C85E068} + {81C60471-7C1C-48CE-98C0-F252C267AC9F} = {352EC932-F112-4A2F-9DC3-F0761C85E068} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4A8B63E-6284-4D00-9719-BAB1D955DACF} diff --git a/README.md b/README.md index 4c790b4..6ad382f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,168 @@ -# DotXxlJob -xxl-job的dotnet core 执行器实现 +# DotXxlJob +xxl-job的dotnet core 执行器实现,支持XXL-JOB 2.0+ + +## 1 XXL-JOB概述 +[XXL-JOB][1]是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。以下是它的架构图 +![架构图](https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/img_Qohm.png) + + + +## 2. 关于DotXxlJob产生 +在工作中调研过多个任务调度平台,如Hangfire、基于Quatz.NET的第三方扩展,都与实际的需求有一点差距。 之前一直使用Hangfire,Hangfire的执行器在同步调用业务服务时,如果即时业务服务正在重新部署或者重启,有一定概率会出现死锁,导致CPU100%,后来全部调整为异步,但是这样就无法获得执行结果,这样的设计有蛮大问题,XxlJob的回调机制很好的解决了这个问题。本身如果通过http的方式调用,只要部署springbootd的一个执行器就可以解决问题,但是扩展性较差。所以萌生了实现DotNet版本的执行器的想法,为避免重复造轮子,开始之前也进行过调研,以下仓库[https://github.com/yuniansheng/xxl-job-dotnet][2]给了较大的启发,但是该库只支持1.9版本的xxljob,还有一些其他小问题,所以还是自力更生。 + +## 3. 如何使用 + +目前只实现了BEAN的方式,即直接实现IJobHandler调用的方式,Glue源码的方式实际上实现起来也并不复杂(有需求再说把),或者各位有需求Fork 实现一下 + +可参考sample + +安装: + +> dotnet add package DotXxlJob.Core + +### 3.1 在AspNetCore中使用 + +1. 生命一个AspNet的Middleware中间件,并扩展ApplicationBuilder,本质是拦截Post请求,解析Body中的流信息 + +``` +public class XxlJobExecutorMiddleware +{ + private readonly IServiceProvider _provider; + private readonly RequestDelegate _next; + + private readonly XxlRpcServiceHandler _rpcService; + public XxlJobExecutorMiddleware(IServiceProvider provider, RequestDelegate next) + { + this._provider = provider; + this._next = next; + this._rpcService = _provider.GetRequiredService(); + } + + public async Task Invoke(HttpContext context) + { + + if ("POST".Equals(context.Request.Method, StringComparison.OrdinalIgnoreCase) && + "application/octet-stream".Equals(context.Request.ContentType, StringComparison.OrdinalIgnoreCase)) + { + var rsp = await _rpcService.HandlerAsync(context.Request.Body); + context.Response.StatusCode = (int) HttpStatusCode.OK; + context.Response.ContentType = "text/plain;utf-8"; + await context.Response.Body.WriteAsync(rsp,0,rsp.Length); + return; + } + await _next.Invoke(context); + } +} +``` + +扩展ApplicationBuilderExtensions,可根据实际情况绑定在特殊的Url Path上 + +``` +public static class ApplicationBuilderExtensions +{ + public static IApplicationBuilder UseXxlJobExecutor(this IApplicationBuilder @this) + { + return @this.UseMiddleware(); + } +} +``` + +在Startup中添加必要的引用,其中自动注册。 + +``` +public class Startup +{ + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + private IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + + services.AddXxlJobExecutor(Configuration); + services.AddSingleton(); // 添加自定义的jobHandler + services.AddAutoRegistry(); // 自动注册 + } + + + public void Configure(IApplicationBuilder app,IHostingEnvironment env) + { + //启用XxlExecutor + app.UseXxlJobExecutor(); + } +} +``` + +编写JobHandler,继承AbstractJobHandler或者直接实现接口IJobHandler,通过context.JobLogger 记录执行过程和结果,在AdminWeb上可查看的哦 +``` +[JobHandler("demoJobHandler")] +public class DemoJobHandler:AbstractJobHandler +{ + public override Task Execute(JobExecuteContext context) + { + context.JobLogger.Log("receive demo job handler,parameter:{0}",context.JobParameter); + + return Task.FromResult(ReturnT.SUCCESS); + } +} +``` + +## 3.2 配置信息 +管理端地址和端口是必填信息,其他根据实际情况,选择配置,配置项说明见下代码中的注释 + +``` + public class XxlJobExecutorOptions +{ + + /// + /// 管理端地址,多个以;分隔 + /// + public string AdminAddresses { get; set; } + /// + /// appName自动注册时要去管理端配置一致 + /// + public string AppName { get; set; } = "xxl-job-executor-dotnet"; + /// + /// 自动注册时提交的地址,为空会自动获取内网地址 + /// + public string SpecialBindAddress { get; set; } + /// + /// 绑定端口 + /// + public int Port { get; set; } + /// + /// 是否自动注册 + /// + public bool AutoRegistry { get; set; } + /// + /// 认证票据 + /// + public string AccessToken { get; set; } + /// + /// 日志目录,默认为执行目录的logs子目录下,请配置绝对路径 + /// + public string LogPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "./logs"); + /// + /// 日志保留天数 + /// + public int LogRetentionDays { get; set; } = 30; +} +``` +## 在其他Http服务中使用 + +只需要实现Http请求的拦截,并判断post请求中content-Type="application/octet-stream",并使用XxlRpcServiceHandler来处理流 即可。 + +## 其他说明 +XXL-JOB内置的RPC是使用Hessian协议,这个有点坑。很多都是java特有的属性和标识,比如类名什么的。在本项目中,并没有实现完整的Hessian2协议,只实现了使用到的类型,当然扩展起来也非常方便。如果有人要单独使用Hessian 这个类库的话,要特别注意这个问题。 + +有任何问题,可Issue反馈 ,最后感谢 xxl-job + + +如果觉得对你有帮助,可以请我吃包肥仔快乐薯片 +![](http://ww1.sinaimg.cn/large/697065c1gy1fzc2pfj071j207s09swfg.jpg) ![](http://ww1.sinaimg.cn/large/697065c1gy1fzc2popzirj208h0audgp.jpg) + + [1]: http://www.xuxueli.com/xxl-job + [2]: https://github.com/yuniansheng/xxl-job-dotnet \ No newline at end of file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index efcf08d..50a485a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,2 +1,4 @@ -#### 0.1.0 Jan 10, 2019 -- 首次发布,实现xxl-job-execute的实现 +#### 1.0.0 Jan 19, 2019 +- 首次发布,实现xxl-job-execute的实现 +- 实现基本的Bean JobHandler的功能 +- 实现自动注册 \ No newline at end of file diff --git a/build.cake b/build.cake index 28118b1..eb90f32 100644 --- a/build.cake +++ b/build.cake @@ -36,8 +36,8 @@ Information($"Running target {target} in {configuration} configuration, version var artifactsDirectory = Directory("./artifacts"); -var projName = "./src/Peach/Peach.csproj"; - +var projName = "./src/DotXxlJob.Core/DotXxlJob.Core.csproj"; +var hessionProjName = "./src/Hessian/Hessian.csproj"; ////////////////////////////////////////////////////////////////////// // TASKS ////////////////////////////////////////////////////////////////////// @@ -55,7 +55,7 @@ Task("Restore") .IsDependentOn("Clean") .Does(() => { - DotNetCoreRestore("./Peach.sln"); + DotNetCoreRestore("./DotXxlJob.sln"); }); @@ -105,27 +105,39 @@ Task("Package") NoBuild = true, ArgumentCustomization = args => args.Append($"/p:PackageVersion={packageVersion}"), }); + + DotNetCorePack( + hessionProjName, + new DotNetCorePackSettings() + { + Configuration = configuration, + OutputDirectory = artifactsDirectory, + NoBuild = true, + ArgumentCustomization = args => args.Append($"/p:PackageVersion={packageVersion}"), + }); }); - + + Task("Publish")   .IsDependentOn("Package")   .Does(()=> {    var settings = new DotNetCoreNuGetPushSettings { - Source = "https://www.nuget.org", - ApiKey = EnvironmentVariable("NUGET_KEY") + Source = "https://www.nuget.org", + ApiKey = EnvironmentVariable("NUGET_KEY") }; foreach(var file in GetFiles($"{artifactsDirectory}/*.nupkg")) { - DotNetCoreNuGetPush(file.FullPath, settings); + DotNetCoreNuGetPush(file.FullPath, settings); } }); + ////////////////////////////////////////////////////////////////////// // TASK TARGETS ////////////////////////////////////////////////////////////////////// diff --git a/samples/ASPNetCoreExecutor/Extensions/XxlJobExecutorMiddleware.cs b/samples/ASPNetCoreExecutor/Extensions/XxlJobExecutorMiddleware.cs index 8c4c527..c1d9182 100644 --- a/samples/ASPNetCoreExecutor/Extensions/XxlJobExecutorMiddleware.cs +++ b/samples/ASPNetCoreExecutor/Extensions/XxlJobExecutorMiddleware.cs @@ -16,10 +16,9 @@ namespace ASPNetCoreExecutor private readonly XxlRpcServiceHandler _rpcService; public XxlJobExecutorMiddleware(IServiceProvider provider, RequestDelegate next) { - _provider = provider; - _next = next; - - _rpcService = _provider.GetRequiredService(); + this._provider = provider; + this._next = next; + this._rpcService = _provider.GetRequiredService(); } diff --git a/src/DotXxlJob.Core/AdminClient.cs b/src/DotXxlJob.Core/AdminClient.cs index 2d2b48d..50a0d02 100644 --- a/src/DotXxlJob.Core/AdminClient.cs +++ b/src/DotXxlJob.Core/AdminClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.SymbolStore; using System.IO; using System.Linq; using System.Net.Http; @@ -28,7 +29,10 @@ namespace DotXxlJob.Core ,IHttpClientFactory clientFactory ,ILogger logger) { - this._options = optionsAccessor.Value; + + Preconditions.CheckNotNull(optionsAccessor?.Value, "XxlJobExecutorOptions"); + + this._options = optionsAccessor?.Value; this._clientFactory = clientFactory; this._logger = logger; InitAddress(); @@ -61,16 +65,16 @@ namespace DotXxlJob.Core public Task Registry(RegistryParam registryParam) { - return InvokeRpcService("callback", new List {new JavaClass {Name = Constants.JavaClassFulName}}, registryParam); + return InvokeRpcService("registry", new List {new JavaClass {Name = "com.xxl.job.core.biz.model.RegistryParam"}}, registryParam,true); } public Task RegistryRemove(RegistryParam registryParam) { - return InvokeRpcService("callback", new List {new JavaClass {Name = Constants.JavaClassFulName}}, registryParam); + return InvokeRpcService("registryRemove", new List {new JavaClass {Name = "com.xxl.job.core.biz.model.RegistryParam"}}, registryParam,true); } private async Task InvokeRpcService(string methodName, List parameterTypes, - object parameters) + object parameters,bool polling=false) { var request = new RpcRequest { RequestId = Guid.NewGuid().ToString("N"), @@ -86,14 +90,14 @@ namespace DotXxlJob.Core { HessianSerializer.SerializeRequest(stream,request); - postBuf =stream.ToArray(); + postBuf = stream.ToArray(); } - int triedTimes = 0; + var triedTimes = 0; + var retList = new List(); - using (var client = this._clientFactory.CreateClient("DotXxlJobClient")) + using (var client = this._clientFactory.CreateClient(Constants.DefaultHttpClientName)) { - while (triedTimes++ < this._addresses.Count) { var address = this._addresses[this._currentIndex]; @@ -106,43 +110,66 @@ namespace DotXxlJob.Core { resStream = await DoPost(client, address, postBuf); address.Reset(); - } catch (Exception ex) { - this._logger.LogError(ex, "request admin error."); + this._logger.LogError(ex, "request admin error.{0}",ex.Message); address.SetFail(); continue; } RpcResponse res = null; try - { - res = HessianSerializer.DeserializeResponse(resStream); + { + /* + using (StreamReader reader = new StreamReader(resStream)) + { + string content = await reader.ReadToEndAsync(); + + this._logger.LogWarning(content); + } + */ + res = HessianSerializer.DeserializeResponse(resStream); + } catch (Exception ex) { - this._logger.LogError(ex,"DeserializeResponse error:"+ex.Message); + this._logger.LogError(ex,"DeserializeResponse error:{errorMessage}",ex.Message); + + } if (res == null) { - - return ReturnT.Failed("response is null"); + retList.Add(ReturnT.Failed("response is null")); } - - - if (res.IsError) + else if (res.IsError) + { + retList.Add(ReturnT.Failed(res.ErrorMsg)); + } + else if(res.Result is ReturnT ret) + { + retList.Add(ret); + } + else { - return ReturnT.Failed(res.ErrorMsg); + retList.Add(ReturnT.Failed("response is null")); } - return res.Result as ReturnT; + if (!polling) + { + return retList[0]; + } + + } + + if (retList.Count > 0) + { + return retList.Last(); } } throw new Exception("xxl-rpc server address not accessible."); - } private async Task DoPost(HttpClient client,AddressEntry address, byte[] postBuf) diff --git a/src/DotXxlJob.Core/Config/XxlJobExecutorOptions.cs b/src/DotXxlJob.Core/Config/XxlJobExecutorOptions.cs index f17e4d0..d5be764 100644 --- a/src/DotXxlJob.Core/Config/XxlJobExecutorOptions.cs +++ b/src/DotXxlJob.Core/Config/XxlJobExecutorOptions.cs @@ -6,24 +6,50 @@ namespace DotXxlJob.Core.Config public class XxlJobExecutorOptions { + /// + /// 管理端地址,多个以;分隔 + /// public string AdminAddresses { get; set; } - public string AppName { get; set; } = "DotXxlJob"; + /// + /// appName自动注册时要去管理端配置一致 + /// + public string AppName { get; set; } = "xxl-job-executor-dotnet"; + /// + /// 自动注册时提交的地址,为空会自动获取内网地址 + /// public string SpecialBindAddress { get; set; } + /// + /// 绑定端口 + /// public int Port { get; set; } + + /// + /// 是否自动注册 + /// + public bool AutoRegistry { get; set; } + /// + /// 认证票据 + /// public string AccessToken { get; set; } + /// + /// 日志目录,默认为执行目录的logs子目录下,请配置绝对路径 + /// public string LogPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "./logs"); + /// + /// 日志保留天数 + /// public int LogRetentionDays { get; set; } = 30; } diff --git a/src/DotXxlJob.Core/DefaultHandlers/SimpleHttpJobHandler.cs b/src/DotXxlJob.Core/DefaultHandlers/SimpleHttpJobHandler.cs index 01eaa9f..8ecb2ca 100644 --- a/src/DotXxlJob.Core/DefaultHandlers/SimpleHttpJobHandler.cs +++ b/src/DotXxlJob.Core/DefaultHandlers/SimpleHttpJobHandler.cs @@ -1,3 +1,5 @@ +using System; +using System.Net; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -16,7 +18,7 @@ namespace DotXxlJob.Core.DefaultHandlers { this._httpClientFactory = httpClientFactory; } - public async override Task Execute(JobExecuteContext context) + public override async Task Execute(JobExecuteContext context) { if (string.IsNullOrEmpty(context.JobParameter)) { @@ -29,15 +31,35 @@ namespace DotXxlJob.Core.DefaultHandlers { return ReturnT.Failed("url format is not valid"); } - - using (var client = _httpClientFactory.CreateClient(Constants.DefaultHttpClientName)) + context.JobLogger.Log("Get Request Data:{0}",context.JobParameter); + using (var client = this._httpClientFactory.CreateClient(Constants.DefaultHttpClientName)) { - var responseMessage = await client.GetAsync(url); + try + { + var response = await client.GetAsync(url); + if (response == null) + { + context.JobLogger.Log("call remote error,response is null"); + return ReturnT.Failed("call remote error,response is null"); + } + + if (response.StatusCode != HttpStatusCode.OK) + { + context.JobLogger.Log("call remote error,response statusCode ={0}",response.StatusCode); + return ReturnT.Failed("call remote error,response statusCode ="+response.StatusCode); + } + + string body = await response.Content.ReadAsStringAsync(); + context.JobLogger.Log("
call remote success ,response is :
{0}",body); + return ReturnT.SUCCESS; + } + catch (Exception ex) + { + context.JobLogger.LogError(ex); + return ReturnT.Failed(ex.Message); + } + } - - //判断是否为单URL - context.JobLogger.Log("Get Request Data:{0}",context.JobParameter); - return ReturnT.SUCCESS; } } diff --git a/src/DotXxlJob.Core/DotXxlJob.Core.csproj b/src/DotXxlJob.Core/DotXxlJob.Core.csproj index 6a74dc3..ec0a90d 100644 --- a/src/DotXxlJob.Core/DotXxlJob.Core.csproj +++ b/src/DotXxlJob.Core/DotXxlJob.Core.csproj @@ -1,13 +1,30 @@  - netstandard2.0 + netstandard2.0 + $(DefineConstants);DOTNETCORE + XxlJobExecutor DotNet port + Xuanye @ 2019 + Xuanye + XxlJobExecutor DotNet port + DotXxlJob.Core + DotXxlJob.Core + 1.0.0 + 1.0.0.0 + 1.0.0.0 + Hession,xxl-job,DotXxlJob + XxlJobExecutor的 DOTNETCORE 实现 + https://github.com/xuanye/DotXxlJob + https://github.com/xuanye/DotXxlJob/blob/master/LICENSE + false + git + https://github.com/xuanye/DotXxlJob - - - + + + diff --git a/src/DotXxlJob.Core/ExecutorRegistry.cs b/src/DotXxlJob.Core/ExecutorRegistry.cs index d32f530..dc2c749 100644 --- a/src/DotXxlJob.Core/ExecutorRegistry.cs +++ b/src/DotXxlJob.Core/ExecutorRegistry.cs @@ -1,3 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using DotXxlJob.Core.Config; +using DotXxlJob.Core.Model; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + namespace DotXxlJob.Core { /// @@ -5,9 +13,64 @@ namespace DotXxlJob.Core /// public class ExecutorRegistry:IExecutorRegistry { - public void Start() + private readonly AdminClient _adminClient; + private readonly XxlJobExecutorOptions _options; + private readonly ILogger _logger; + + public ExecutorRegistry(AdminClient adminClient,IOptions optionsAccessor,ILogger logger) { - throw new System.NotImplementedException(); + Preconditions.CheckNotNull(optionsAccessor, "XxlJobExecutorOptions"); + Preconditions.CheckNotNull(optionsAccessor.Value, "XxlJobExecutorOptions"); + this._adminClient = adminClient; + this._options = optionsAccessor.Value; + if (string.IsNullOrEmpty(this._options.SpecialBindAddress)) + { + this._options.SpecialBindAddress = IPUtility.GetLocalIntranetIP().MapToIPv4().ToString(); + } + this._logger = logger; } + + public async Task RegistryAsync(CancellationToken cancellationToken) + { + var registryParam = new RegistryParam { + RegistryGroup = "EXECUTOR", + RegistryKey = this._options.AppName, + RegistryValue = $"{this._options.SpecialBindAddress}:{this._options.Port}" + }; + + this._logger.LogInformation(">>>>>>>> start registry to admin <<<<<<<<"); + + var errorTimes = 0; + + while (!cancellationToken.IsCancellationRequested) + { + try + { + var ret = await this._adminClient.Registry(registryParam); + this._logger.LogInformation("registry last result:{0}", ret?.Code); + errorTimes = 0; + await Task.Delay(Constants.RegistryInterval, cancellationToken); + } + catch (TaskCanceledException) + { + this._logger.LogInformation(">>>>> Application Stopping....<<<<<"); + } + catch (Exception ex) + { + errorTimes++; + this._logger.LogError(ex,"registry error:{0},{1} Times",ex.Message,errorTimes); + } + } + + this._logger.LogInformation(">>>>>>>> end registry to admin <<<<<<<<"); + + this._logger.LogInformation(">>>>>>>> start remove registry to admin <<<<<<<<"); + + var removeRet = await this._adminClient.RegistryRemove(registryParam); + this._logger.LogInformation("remove registry last result:{0}",removeRet?.Code); + this._logger.LogInformation(">>>>>>>> end remove registry to admin <<<<<<<<"); + } + + } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/Extensions/ServiceCollectionExtensions.cs b/src/DotXxlJob.Core/Extensions/ServiceCollectionExtensions.cs index 8ab62ef..fc1ab7e 100644 --- a/src/DotXxlJob.Core/Extensions/ServiceCollectionExtensions.cs +++ b/src/DotXxlJob.Core/Extensions/ServiceCollectionExtensions.cs @@ -1,8 +1,11 @@ using System; using DotXxlJob.Core.Config; using DotXxlJob.Core.DefaultHandlers; +using DotXxlJob.Core.Queue; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; namespace DotXxlJob.Core { @@ -27,23 +30,31 @@ namespace DotXxlJob.Core public static IServiceCollection AddDefaultXxlJobHandlers(this IServiceCollection services) { - services.AddSingleton(); + services.AddSingleton(); + return services; + } + public static IServiceCollection AddAutoRegistry(this IServiceCollection services) + { + services.AddSingleton() + .AddSingleton(); return services; } private static IServiceCollection AddXxlJobExecutorServiceDependency(this IServiceCollection services) - { - + { + + //可在外部提前注册对应实现,并替换默认实现 + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.AddHttpClient("DotXxlJobClient"); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); return services; } diff --git a/src/DotXxlJob.Core/Hosted/JobExecuteHostedService.cs b/src/DotXxlJob.Core/Hosted/JobExecuteHostedService.cs index b708f3d..102144f 100644 --- a/src/DotXxlJob.Core/Hosted/JobExecuteHostedService.cs +++ b/src/DotXxlJob.Core/Hosted/JobExecuteHostedService.cs @@ -7,24 +7,19 @@ namespace DotXxlJob.Core /// /// NOTE: 负责启动Executor服务,和进行服务注册的宿主服务 /// - public class JobsExecuteHostedService:IHostedService + public class JobsExecuteHostedService:BackgroundService { - public Task StartAsync(CancellationToken cancellationToken) + private readonly IExecutorRegistry _registry; + + public JobsExecuteHostedService(IExecutorRegistry registry) { - //1 初始化服务注册 - //2 初始化adminClient - - //3 初始化执行线程 - - //4 初始化XXL_RPC服务端口,HTTP服务 - - throw new System.NotImplementedException(); + this._registry = registry; } - public Task StopAsync(CancellationToken cancellationToken) + protected override Task ExecuteAsync(CancellationToken stoppingToken) { - //清理Start中启动的资源 - throw new System.NotImplementedException(); + return this._registry.RegistryAsync(stoppingToken); } + } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/IExecutorRegistry.cs b/src/DotXxlJob.Core/IExecutorRegistry.cs index e6bb502..8a6119e 100644 --- a/src/DotXxlJob.Core/IExecutorRegistry.cs +++ b/src/DotXxlJob.Core/IExecutorRegistry.cs @@ -1,7 +1,13 @@ +using System.Threading; +using System.Threading.Tasks; + namespace DotXxlJob.Core { public interface IExecutorRegistry { - void Start(); + + Task RegistryAsync(CancellationToken cancellationToken); + + } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/IJobHandlerFactory.cs b/src/DotXxlJob.Core/IJobHandlerFactory.cs index ba986ab..d09be4c 100644 --- a/src/DotXxlJob.Core/IJobHandlerFactory.cs +++ b/src/DotXxlJob.Core/IJobHandlerFactory.cs @@ -2,7 +2,6 @@ namespace DotXxlJob.Core { public interface IJobHandlerFactory { - //TODO: 获取实际执行的JobHandler IJobHandler GetJobHandler(string handlerName); } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/Internal/Constants.cs b/src/DotXxlJob.Core/Internal/Constants.cs index ce1673d..46ed32b 100644 --- a/src/DotXxlJob.Core/Internal/Constants.cs +++ b/src/DotXxlJob.Core/Internal/Constants.cs @@ -6,15 +6,28 @@ namespace DotXxlJob.Core { public const string RpcRequestJavaFullName = "com.xxl.rpc.remoting.net.params.XxlRpcRequest"; public const string RpcResponseJavaFullName = "com.xxl.rpc.remoting.net.params.XxlRpcResponse"; - public const string JavaClassFulName = "java.lang.Clas"; + + public const string RegistryParamJavaFullName = "com.xxl.job.core.biz.model.RegistryParam"; + public const string ReturnTJavaFullName = "com.xxl.job.core.biz.model.ReturnT"; + + public const string TriggerParamJavaFullName = "com.xxl.job.core.biz.model.TriggerParam"; + public const string HandleCallbackParamJavaFullName = "com.xxl.job.core.biz.model.HandleCallbackParam"; + public const string LogResultJavaFullName = "com.xxl.job.core.biz.model.LogResult"; + + + public const string JavaClassFulName = "java.lang.Class"; public const string JavaListFulName = "java.util.List"; - public const string XxlLogsDefaultRootDirectory = "xxl-job-logs"; + public const string XxlJobRetryLogsFile = "xxl-job-retry.log"; public const string HandleLogsDirectory = "HandlerLogs"; - public const string LogFileNameCallContextKey = "XxlJob.LogFileName"; + + public const string DefaultHttpClientName = "DotXxlJobHttpClient"; + public const int DefaultLogRetentionDays = 30; public static TimeSpan RpcRequestExpireTimeSpan = TimeSpan.FromMinutes(3); + + public static TimeSpan RegistryInterval = TimeSpan.FromSeconds(60); public const int MaxCallbackRetryTimes = 10; //每次回调最多发送几条记录 @@ -22,7 +35,7 @@ namespace DotXxlJob.Core public static TimeSpan CallbackRetryInterval = TimeSpan.FromSeconds(600); //Admin集群机器请求默认超时时间 - public static TimeSpan AdminServerDefaultTimeout = TimeSpan.FromSeconds(15); + //public static TimeSpan AdminServerDefaultTimeout = TimeSpan.FromSeconds(15); //Admin集群中的某台机器熔断后间隔多长时间再重试 public static TimeSpan AdminServerReconnectInterval = TimeSpan.FromMinutes(3); //Admin集群中的某台机器请求失败多少次后熔断 diff --git a/src/DotXxlJob.Core/Internal/HessianObjectConvert.cs b/src/DotXxlJob.Core/Internal/HessianObjectConvert.cs index 2adf473..cb3712c 100644 --- a/src/DotXxlJob.Core/Internal/HessianObjectConvert.cs +++ b/src/DotXxlJob.Core/Internal/HessianObjectConvert.cs @@ -64,7 +64,7 @@ namespace DotXxlJob.Core TransferObjCache.Add(classAttr.Name,propertyInfos); } - public static object GetRealObjectValue(object value) + public static object GetRealObjectValue(Deserializer deserializer,object value) { if (value == null || IsSimpleType(value.GetType())) { @@ -80,7 +80,7 @@ namespace DotXxlJob.Core { if (properties.TryGetValue(k, out var p)) { - p.SetValue(instance,GetRealObjectValue(v)); + p.SetValue(instance,GetRealObjectValue(deserializer,v)); } } @@ -88,6 +88,11 @@ namespace DotXxlJob.Core } } + if (value is ClassDef) + { + return GetRealObjectValue(deserializer, deserializer.ReadValue()); + } + if (IsListType(value.GetType())) { var listData = new List(); @@ -95,11 +100,12 @@ namespace DotXxlJob.Core var cList = value as List; foreach (var cItem in cList) { - listData.Add(GetRealObjectValue(cItem)); + listData.Add(GetRealObjectValue(deserializer,cItem)); } return listData; } + throw new HessianException($"unknown item:{value.GetType()}"); } diff --git a/src/DotXxlJob.Core/Internal/HessianSerializer.cs b/src/DotXxlJob.Core/Internal/HessianSerializer.cs index 97dddcd..a502851 100644 --- a/src/DotXxlJob.Core/Internal/HessianSerializer.cs +++ b/src/DotXxlJob.Core/Internal/HessianSerializer.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using DotXxlJob.Core.Model; using Hessian; @@ -19,7 +20,7 @@ namespace DotXxlJob.Core { throw new HessianException($"unknown class :{classDef.Name}"); } - request = HessianObjectHelper.GetRealObjectValue(deserializer.ReadValue()) as RpcRequest; + request = HessianObjectHelper.GetRealObjectValue(deserializer,deserializer.ReadValue()) as RpcRequest; } catch (EndOfStreamException) { @@ -50,18 +51,23 @@ namespace DotXxlJob.Core try { var deserializer = new Deserializer(resStream); - var classDef = deserializer.ReadValue() as ClassDef; + var classDef = deserializer.ReadValue() as ClassDef; if (!Constants.RpcResponseJavaFullName.Equals(classDef.Name)) { - throw new HessianException($"unknown class :{classDef.Name}"); + throw new HessianException($"unknown class :{classDef.Name}"); } - rsp = HessianObjectHelper.GetRealObjectValue(deserializer.ReadValue()) as RpcResponse; - + + rsp = HessianObjectHelper.GetRealObjectValue(deserializer,deserializer.ReadValue()) as RpcResponse; + } catch (EndOfStreamException) { //没有数据可读了 } + catch (Exception) + { + //TODO: do something? + } return rsp; } diff --git a/src/DotXxlJob.Core/Internal/IPUtility.cs b/src/DotXxlJob.Core/Internal/IPUtility.cs index 7a4a7e4..3b640e1 100644 --- a/src/DotXxlJob.Core/Internal/IPUtility.cs +++ b/src/DotXxlJob.Core/Internal/IPUtility.cs @@ -1,7 +1,135 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + namespace DotXxlJob.Core { - public class IPUtility + /// + /// ip utility + /// + internal static class IPUtility { - + #region Private Members + /// + /// A类: 10.0.0.0-10.255.255.255 + /// + private static long ipABegin, ipAEnd; + /// + /// B类: 172.16.0.0-172.31.255.255 + /// + private static long ipBBegin, ipBEnd; + /// + /// C类: 192.168.0.0-192.168.255.255 + /// + private static long ipCBegin, ipCEnd; + #endregion + + #region Constructors + /// + /// static new + /// + static IPUtility() + { + ipABegin = ConvertToNumber("10.0.0.0"); + ipAEnd = ConvertToNumber("10.255.255.255"); + + ipBBegin = ConvertToNumber("172.16.0.0"); + ipBEnd = ConvertToNumber("172.31.255.255"); + + ipCBegin = ConvertToNumber("192.168.0.0"); + ipCEnd = ConvertToNumber("192.168.255.255"); + } + #endregion + + #region Public Methods + /// + /// ip address convert to long + /// + /// + /// + private static long ConvertToNumber(string ipAddress) + { + return ConvertToNumber(IPAddress.Parse(ipAddress)); + } + /// + /// ip address convert to long + /// + /// + /// + private static long ConvertToNumber(IPAddress ipAddress) + { + var bytes = ipAddress.GetAddressBytes(); + return bytes[0] * 256 * 256 * 256 + bytes[1] * 256 * 256 + bytes[2] * 256 + bytes[3]; + } + /// + /// true表示为内网IP + /// + /// + /// + public static bool IsIntranet(string ipAddress) + { + return IsIntranet(ConvertToNumber(ipAddress)); + } + /// + /// true表示为内网IP + /// + /// + /// + private static bool IsIntranet(IPAddress ipAddress) + { + return IsIntranet(ConvertToNumber(ipAddress)); + } + /// + /// true表示为内网IP + /// + /// + /// + private static bool IsIntranet(long longIP) + { + return ((longIP >= ipABegin) && (longIP <= ipAEnd) || + (longIP >= ipBBegin) && (longIP <= ipBEnd) || + (longIP >= ipCBegin) && (longIP <= ipCEnd)); + } + /// + /// 获取本机内网IP + /// + /// + public static IPAddress GetLocalIntranetIP() + { + return NetworkInterface + .GetAllNetworkInterfaces() + .Select(p => p.GetIPProperties()) + .SelectMany(p => + p.UnicastAddresses + ).FirstOrDefault(p => p.Address.AddressFamily == AddressFamily.InterNetwork + && !IPAddress.IsLoopback(p.Address) + && IsIntranet(p.Address))?.Address; + } + /// + /// 获取本机内网IP列表 + /// + /// + public static List GetLocalIntranetIPList() + { + var infList =NetworkInterface.GetAllNetworkInterfaces() + .Select(p => p.GetIPProperties()) + .SelectMany(p => p.UnicastAddresses) + .Where(p => + p.Address.AddressFamily == AddressFamily.InterNetwork + && !IPAddress.IsLoopback(p.Address) + && IsIntranet(p.Address) + ); + + var result = new List(); + foreach (var child in infList) + { + result.Add(child.Address); + } + + return result; + } + #endregion } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/Internal/Preconditions.cs b/src/DotXxlJob.Core/Internal/Preconditions.cs index 52c32f2..c6131ea 100644 --- a/src/DotXxlJob.Core/Internal/Preconditions.cs +++ b/src/DotXxlJob.Core/Internal/Preconditions.cs @@ -1,7 +1,87 @@ +using System; + namespace DotXxlJob.Core { - public class Preconditions + /// + /// Utility methods to simplify checking preconditions in the code. + /// + internal static class Preconditions { - + /// + /// Throws if condition is false. + /// + /// The condition. + public static void CheckArgument(bool condition) + { + if (!condition) + { + throw new ArgumentException(); + } + } + + /// + /// Throws with given message if condition is false. + /// + /// The condition. + /// The error message. + public static void CheckArgument(bool condition, string errorMessage) + { + if (!condition) + { + throw new ArgumentException(errorMessage); + } + } + + /// + /// Throws if reference is null. + /// + /// The reference. + public static T CheckNotNull(T reference) + { + if (reference == null) + { + throw new ArgumentNullException(); + } + return reference; + } + + /// + /// Throws if reference is null. + /// + /// The reference. + /// The parameter name. + public static T CheckNotNull(T reference, string paramName) + { + if (reference == null) + { + throw new ArgumentNullException(paramName); + } + return reference; + } + + /// + /// Throws if condition is false. + /// + /// The condition. + public static void CheckState(bool condition) + { + if (!condition) + { + throw new InvalidOperationException(); + } + } + + /// + /// Throws with given message if condition is false. + /// + /// The condition. + /// The error message. + public static void CheckState(bool condition, string errorMessage) + { + if (!condition) + { + throw new InvalidOperationException(errorMessage); + } + } } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/JobDispatcher.cs b/src/DotXxlJob.Core/JobDispatcher.cs index bee51a7..00e19bf 100644 --- a/src/DotXxlJob.Core/JobDispatcher.cs +++ b/src/DotXxlJob.Core/JobDispatcher.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using DotXxlJob.Core.Model; +using DotXxlJob.Core.Queue; using Microsoft.Extensions.Logging; namespace DotXxlJob.Core diff --git a/src/DotXxlJob.Core/Logger/JobLogger.cs b/src/DotXxlJob.Core/Logger/JobLogger.cs index 43d88f7..8001104 100644 --- a/src/DotXxlJob.Core/Logger/JobLogger.cs +++ b/src/DotXxlJob.Core/Logger/JobLogger.cs @@ -90,7 +90,7 @@ namespace DotXxlJob.Core } catch (Exception ex) { - _logger.LogError(ex, "ReadLog error."); + this._logger.LogError(ex, "ReadLog error."); } // result @@ -114,7 +114,7 @@ namespace DotXxlJob.Core private string MakeLogFileName(long logDateTime, int logId) { //log fileName like: logPath/HandlerLogs/yyyy-MM-dd/9999.log - return Path.Combine(_options.LogPath, Constants.HandleLogsDirectory, + return Path.Combine(this._options.LogPath, Constants.HandleLogsDirectory, logDateTime.FromMilliseconds().ToString("yyyy-MM-dd"), $"{logId}.log"); } private void LogDetail(string logFileName, StackFrame callInfo, string appendLog) @@ -147,16 +147,16 @@ namespace DotXxlJob.Core private void CleanOldLogs() { - if (_options.LogRetentionDays <= 0) + if (this._options.LogRetentionDays <= 0) { - return; + this._options.LogRetentionDays = Constants.DefaultLogRetentionDays; } Task.Run(() => { try { - var handlerLogsDir = new DirectoryInfo(Path.Combine(_options.LogPath, Constants.HandleLogsDirectory)); + var handlerLogsDir = new DirectoryInfo(Path.Combine(this._options.LogPath, Constants.HandleLogsDirectory)); if (!handlerLogsDir.Exists) { return; @@ -167,7 +167,7 @@ namespace DotXxlJob.Core { if (DateTime.TryParse(dir.Name, out var dirDate)) { - if (today.Subtract(dirDate.Date).Days > _options.LogRetentionDays) + if (today.Subtract(dirDate.Date).Days > this._options.LogRetentionDays) { dir.Delete(true); } @@ -176,7 +176,7 @@ namespace DotXxlJob.Core } catch (Exception ex) { - _logger.LogError(ex, "CleanOldLogs error."); + this._logger.LogError(ex, "CleanOldLogs error."); } }); } diff --git a/src/DotXxlJob.Core/Model/HandleCallbackParam.cs b/src/DotXxlJob.Core/Model/HandleCallbackParam.cs index b4b6243..864efe5 100644 --- a/src/DotXxlJob.Core/Model/HandleCallbackParam.cs +++ b/src/DotXxlJob.Core/Model/HandleCallbackParam.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "com.xxl.job.core.biz.model.HandleCallbackParam")] + [DataContract(Name = Constants.HandleCallbackParamJavaFullName)] public class HandleCallbackParam { public HandleCallbackParam() diff --git a/src/DotXxlJob.Core/Model/JavaClass.cs b/src/DotXxlJob.Core/Model/JavaClass.cs index 6a15256..64ec819 100644 --- a/src/DotXxlJob.Core/Model/JavaClass.cs +++ b/src/DotXxlJob.Core/Model/JavaClass.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "java.lang.Class")] + [DataContract(Name = Constants.JavaClassFulName)] public class JavaClass { [DataMember(Name = "name",Order = 1)] diff --git a/src/DotXxlJob.Core/Model/LogResult.cs b/src/DotXxlJob.Core/Model/LogResult.cs index c181dfb..a63f3d0 100644 --- a/src/DotXxlJob.Core/Model/LogResult.cs +++ b/src/DotXxlJob.Core/Model/LogResult.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "com.xxl.job.core.biz.model.LogResult")] + [DataContract(Name = Constants.LogResultJavaFullName)] public class LogResult { diff --git a/src/DotXxlJob.Core/Model/RegistryParam.cs b/src/DotXxlJob.Core/Model/RegistryParam.cs index 05fadb6..93925cd 100644 --- a/src/DotXxlJob.Core/Model/RegistryParam.cs +++ b/src/DotXxlJob.Core/Model/RegistryParam.cs @@ -2,13 +2,16 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "com.xxl.job.core.biz.model.RegistryParam")] + [DataContract(Name = Constants.RegistryParamJavaFullName)] public class RegistryParam { [DataMember(Name = "registGroup",Order = 1)] public string RegistryGroup { get; set; } + [DataMember(Name = "registryKey",Order = 2)] public string RegistryKey { get; set; } + + [DataMember(Name = "registryValue",Order = 3)] public string RegistryValue { get; set; } diff --git a/src/DotXxlJob.Core/Model/ReturnT.cs b/src/DotXxlJob.Core/Model/ReturnT.cs index b2021b6..ef50105 100644 --- a/src/DotXxlJob.Core/Model/ReturnT.cs +++ b/src/DotXxlJob.Core/Model/ReturnT.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core { - [DataContract(Name = "com.xxl.job.core.biz.model.ReturnT")] + [DataContract(Name = Constants.ReturnTJavaFullName)] public class ReturnT { public const int SUCCESS_CODE = 200; diff --git a/src/DotXxlJob.Core/Model/RpcRequest.cs b/src/DotXxlJob.Core/Model/RpcRequest.cs index 18637f7..27d04e3 100644 --- a/src/DotXxlJob.Core/Model/RpcRequest.cs +++ b/src/DotXxlJob.Core/Model/RpcRequest.cs @@ -3,7 +3,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "com.xxl.rpc.remoting.net.params.XxlRpcRequest")] + [DataContract(Name = Constants.RpcRequestJavaFullName)] public class RpcRequest { /* diff --git a/src/DotXxlJob.Core/Model/RpcResponse.cs b/src/DotXxlJob.Core/Model/RpcResponse.cs index 5a172f2..9b52ddb 100644 --- a/src/DotXxlJob.Core/Model/RpcResponse.cs +++ b/src/DotXxlJob.Core/Model/RpcResponse.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "com.xxl.rpc.remoting.net.params.XxlRpcResponse")] + [DataContract(Name = Constants.RpcResponseJavaFullName)] public class RpcResponse { [DataMember(Name = "requestId",Order = 1)] diff --git a/src/DotXxlJob.Core/Model/TriggerParam.cs b/src/DotXxlJob.Core/Model/TriggerParam.cs index 06d84bf..fe571f9 100644 --- a/src/DotXxlJob.Core/Model/TriggerParam.cs +++ b/src/DotXxlJob.Core/Model/TriggerParam.cs @@ -3,7 +3,7 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "com.xxl.job.core.biz.model.TriggerParam")] + [DataContract(Name = Constants.TriggerParamJavaFullName)] public class TriggerParam { //static readonly long SerialVersionUID = 42L; diff --git a/src/DotXxlJob.Core/Queue/CallbackTaskQueue.cs b/src/DotXxlJob.Core/Queue/CallbackTaskQueue.cs index f996cf0..93129dd 100644 --- a/src/DotXxlJob.Core/Queue/CallbackTaskQueue.cs +++ b/src/DotXxlJob.Core/Queue/CallbackTaskQueue.cs @@ -8,7 +8,7 @@ using DotXxlJob.Core.Model; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace DotXxlJob.Core +namespace DotXxlJob.Core.Queue { public class CallbackTaskQueue:IDisposable { @@ -16,7 +16,7 @@ namespace DotXxlJob.Core private readonly IJobLogger _jobLogger; private readonly RetryCallbackTaskQueue _retryQueue; private readonly ILogger _logger; - private readonly ConcurrentQueue TASK_QUEUE = new ConcurrentQueue(); + private readonly ConcurrentQueue taskQueue = new ConcurrentQueue(); private bool _stop; @@ -38,7 +38,7 @@ namespace DotXxlJob.Core public void Push(HandleCallbackParam callbackParam) { - TASK_QUEUE.Enqueue(callbackParam); + this.taskQueue.Enqueue(callbackParam); StartCallBack(); } @@ -76,7 +76,7 @@ namespace DotXxlJob.Core { List list = new List(); - while (list.Count < Constants.MaxCallbackRecordsPerRequest && this.TASK_QUEUE.TryDequeue(out var item)) + while (list.Count < Constants.MaxCallbackRecordsPerRequest && this.taskQueue.TryDequeue(out var item)) { list.Add(item); } @@ -104,7 +104,7 @@ namespace DotXxlJob.Core { foreach (var param in list) { - this._jobLogger.LogSpecialFile(param.LogDateTime, param.LogId, result.Msg??"Empty"); + this._jobLogger.LogSpecialFile(param.LogDateTime, param.LogId, result.Msg??"Success"); } } diff --git a/src/DotXxlJob.Core/Queue/JobTaskQueue.cs b/src/DotXxlJob.Core/Queue/JobTaskQueue.cs index d115c30..8395f9a 100644 --- a/src/DotXxlJob.Core/Queue/JobTaskQueue.cs +++ b/src/DotXxlJob.Core/Queue/JobTaskQueue.cs @@ -52,9 +52,12 @@ namespace DotXxlJob.Core { if(!ID_IN_QUEUE.TryAdd(triggerParam.LogId,0)) { - _logger.LogWarning("repeat job task,logId={logId},jobId={jobId}",triggerParam.LogId,triggerParam.JobId); + this._logger.LogWarning("repeat job task,logId={logId},jobId={jobId}",triggerParam.LogId,triggerParam.JobId); return ReturnT.Failed("repeat job task!"); } + + //this._logger.LogWarning("add job with logId={logId},jobId={jobId}",triggerParam.LogId,triggerParam.JobId); + this.TASK_QUEUE.Enqueue(triggerParam); StartTask(); return ReturnT.SUCCESS; @@ -86,8 +89,8 @@ namespace DotXxlJob.Core { return; //running } - this._cancellationTokenSource =new CancellationTokenSource(); - CancellationToken ct = _cancellationTokenSource.Token; + this._cancellationTokenSource = new CancellationTokenSource(); + var ct = this._cancellationTokenSource.Token; this._runTask = Task.Factory.StartNew(async () => { @@ -108,10 +111,10 @@ namespace DotXxlJob.Core if (TASK_QUEUE.TryDequeue(out triggerParam)) { - if (ID_IN_QUEUE.TryRemove(triggerParam.LogId,out _)) + if (!ID_IN_QUEUE.TryRemove(triggerParam.LogId,out _)) { - this._logger.LogWarning("remove id in queue failed,logId={logId},jobId={jobId}" - ,triggerParam.LogId,triggerParam.JobId); + this._logger.LogWarning("remove queue failed,logId={logId},jobId={jobId},exists={exists}" + ,triggerParam.LogId,triggerParam.JobId,ID_IN_QUEUE.ContainsKey(triggerParam.LogId)); } //set log file; this._jobLogger.SetLogFile(triggerParam.LogDateTime,triggerParam.LogId); diff --git a/src/DotXxlJob.Core/Queue/RetryCallbackTaskQueue.cs b/src/DotXxlJob.Core/Queue/RetryCallbackTaskQueue.cs index ecdcdf5..40a68bc 100644 --- a/src/DotXxlJob.Core/Queue/RetryCallbackTaskQueue.cs +++ b/src/DotXxlJob.Core/Queue/RetryCallbackTaskQueue.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using DotXxlJob.Core.Json; using DotXxlJob.Core.Model; using Microsoft.Extensions.Logging; -namespace DotXxlJob.Core +namespace DotXxlJob.Core.Queue { public class RetryCallbackTaskQueue:IDisposable { @@ -16,7 +17,7 @@ namespace DotXxlJob.Core private readonly Action _actionDoCallback; private readonly ILogger _logger; - private bool _stop; + private CancellationTokenSource _cancellation; private Task _runTask; private readonly string _backupFile; public RetryCallbackTaskQueue(string backupPath,Action actionDoCallback,ILogger logger) @@ -24,11 +25,11 @@ namespace DotXxlJob.Core this._actionDoCallback = actionDoCallback; this._logger = logger; - this._backupFile = Path.Combine(backupPath, "xxl-job-callback.log"); + this._backupFile = Path.Combine(backupPath, Constants.XxlJobRetryLogsFile); var dir = Path.GetDirectoryName(backupPath); if (!Directory.Exists(dir)) { - Directory.CreateDirectory(dir); + Directory.CreateDirectory(dir ?? throw new Exception("logs path is empty")); } StartQueue(); @@ -36,12 +37,14 @@ namespace DotXxlJob.Core private void StartQueue() { + this._cancellation = new CancellationTokenSource(); + var stopToken = this._cancellation.Token; this._runTask = Task.Factory.StartNew(async () => { - while (!this._stop) + while (!stopToken.IsCancellationRequested) { await LoadFromFile(); - await Task.Delay(Constants.CallbackRetryInterval); + await Task.Delay(Constants.CallbackRetryInterval,stopToken); } }, TaskCreationOptions.LongRunning); @@ -51,14 +54,14 @@ namespace DotXxlJob.Core { var list = new List(); - if (!File.Exists(_backupFile)) + if (!File.Exists(this._backupFile)) { return; } - var nextLine = string.Empty; using (StreamReader reader = new StreamReader(this._backupFile)) { + string nextLine; while ((nextLine = await reader.ReadLineAsync()) != null) { try @@ -67,7 +70,7 @@ namespace DotXxlJob.Core } catch(Exception ex) { - this._logger.LogError(ex,"de error:{error}",ex.Message); + this._logger.LogError(ex,"read backup file error:{error}",ex.Message); } } @@ -99,7 +102,7 @@ namespace DotXxlJob.Core { if (item.CallbackRetryTimes >= Constants.MaxCallbackRetryTimes) { - _logger.LogInformation("callback too many times and will be abandon,logId {logId}", item.LogId); + this._logger.LogInformation("callback too many times and will be abandon,logId {logId}", item.LogId); } else { @@ -112,13 +115,13 @@ namespace DotXxlJob.Core } catch (Exception ex) { - _logger.LogError(ex, "SaveCallbackParams error."); + this._logger.LogError(ex, "SaveCallbackParams error."); } } public void Dispose() { - this._stop = true; + this._cancellation.Cancel(); this._runTask?.GetAwaiter().GetResult(); } } diff --git a/src/DotXxlJob.Core/TaskExecutorFactory.cs b/src/DotXxlJob.Core/TaskExecutorFactory.cs index ad9f6d3..9f47d91 100644 --- a/src/DotXxlJob.Core/TaskExecutorFactory.cs +++ b/src/DotXxlJob.Core/TaskExecutorFactory.cs @@ -15,26 +15,26 @@ namespace DotXxlJob.Core private readonly Dictionary _cache = new Dictionary(); public TaskExecutorFactory(IServiceProvider provider) { - _provider = provider; + this._provider = provider; Initialize(); } private void Initialize() { - var executors = _provider.GetServices(); + var executors = this._provider.GetServices(); - if (executors != null && executors.Any()) + var taskExecutors = executors as ITaskExecutor[] ?? executors.ToArray(); + if (executors == null || !taskExecutors.Any()) return; + + foreach (var item in taskExecutors) { - foreach (var item in executors) - { - _cache.Add(item.GlueType,item); - } + this._cache.Add(item.GlueType,item); } } public ITaskExecutor GetTaskExecutor(string glueType) { - return _cache.TryGetValue(glueType, out var executor) ? executor : null; + return this._cache.TryGetValue(glueType, out var executor) ? executor : null; } } } \ No newline at end of file diff --git a/src/Hessian/Hessian.csproj b/src/Hessian/Hessian.csproj index d2a210c..5560452 100644 --- a/src/Hessian/Hessian.csproj +++ b/src/Hessian/Hessian.csproj @@ -1,7 +1,23 @@  - - netstandard2.0 - - + + netstandard2.0 + $(DefineConstants);DOTNETCORE + Hession的编解码实现,并不完全哦 + Xuanye @ 2018 + Xuanye + Hession DotNet port + Hession + Hession + 1.0.0 + 1.0.0.0 + 1.0.0.0 + Hession,xxl-job,protocol + Hession的编解码实现,并不完全哦 + https://github.com/xuanye/DotXxlJob + https://github.com/xuanye/DotXxlJob/blob/master/LICENSE + false + git + https://github.com/xuanye/DotXxlJob + diff --git a/tests/DotXxlJob.Core.Tests/DotXxlJob.Core.Tests.csproj b/tests/DotXxlJob.Core.Tests/DotXxlJob.Core.Tests.csproj new file mode 100644 index 0000000..a8d75a1 --- /dev/null +++ b/tests/DotXxlJob.Core.Tests/DotXxlJob.Core.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/tests/DotXxlJob.Core.Tests/UnitTest1.cs b/tests/DotXxlJob.Core.Tests/UnitTest1.cs new file mode 100644 index 0000000..dd1c350 --- /dev/null +++ b/tests/DotXxlJob.Core.Tests/UnitTest1.cs @@ -0,0 +1,13 @@ +using System; +using Xunit; + +namespace DotXxlJob.Core.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + } + } +} \ No newline at end of file diff --git a/tests/Hessian.NET.Tests/ByteArray.cs b/tests/Hessian.NET.Tests/ByteArray.cs deleted file mode 100644 index 131ab3b..0000000 --- a/tests/Hessian.NET.Tests/ByteArray.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Hessian.NET.Tests -{ - internal static class ByteArray - { - public static bool Equals(byte[] expect, byte[] value) - { - if (null == expect) - { - return null == value; - } - - if (null == value) - { - return false; - } - - if (expect.Length != value.Length) - { - return false; - } - - for (var index = 0; index < expect.Length; index++) - { - if (!expect[index].Equals(value[index])) - { - return false; - } - } - - return true; - } - } -} diff --git a/tests/Hessian.NET.Tests/Hessian.NET.Tests.csproj b/tests/Hessian.NET.Tests/Hessian.NET.Tests.csproj deleted file mode 100644 index b3dbb8e..0000000 --- a/tests/Hessian.NET.Tests/Hessian.NET.Tests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - netcoreapp2.2 - - false - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - - diff --git a/tests/Hessian.NET.Tests/HessianInputReaderTests.cs b/tests/Hessian.NET.Tests/HessianInputReaderTests.cs deleted file mode 100644 index 44883eb..0000000 --- a/tests/Hessian.NET.Tests/HessianInputReaderTests.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System; -using System.IO; -using Hessian.Net; -using Xunit; - - -namespace Hessian.NET.Tests -{ - public class HessianInputReaderTests - { - [Fact] - public void BooleanTrue() - { - AssertInput(true, new[] { (byte)'T' }, reader => reader.ReadBoolean()); - } - - [Fact] - public void BooleanFalse() - { - AssertInput(false, new[] { (byte)'F' }, reader => reader.ReadBoolean()); - } - - [Fact] - public void BytesCompactEmpty() - { - AssertInput(null, new[] { (byte)'N' }, reader => reader.ReadBytes()); - } - - [Fact] - public void BytesCompactOne() - { - AssertInput(new[] { (byte)0x01 }, new[] { (byte)0x21, (byte)0x01 }, reader => reader.ReadBytes()); - } - - [Fact] - public void BytesCompact15() - { - AssertInput( - new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, - new byte[] {0x2A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, - reader => reader.ReadBytes() - ); - } - - [Fact] - public void BytesSingleChunk() - { - AssertInput( - new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x012, 0x13, 0x14, 0x15 }, - new byte[] {(byte)'B', 0x00, 0x15, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x012, 0x13, 0x14, 0x15 }, - reader => reader.ReadBytes() - ); - } - - [Fact] - public void BytesMultiChunk() - { - AssertInput( - new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x012, 0x13, 0x14, 0x15 }, - new byte[] { (byte)'b', 0x00, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, (byte)'B', 0x00, 0x05, 0x11, 0x012, 0x13, 0x14, 0x15 }, - reader => reader.ReadBytes() - ); - } - - [Fact] - public void Int32SingleOctet() - { - AssertInput(0, new[] { (byte)0x90 }, reader => reader.ReadInt32()); - AssertInput(-16, new[] { (byte)0x80 }, reader => reader.ReadInt32()); - AssertInput(47, new[] { (byte)0xBF }, reader => reader.ReadInt32()); - } - - [Fact] - public void Int32TwoOctets() - { - AssertInput(-2048,new byte[] { 0xC0, 0x00 }, reader => reader.ReadInt32()); - AssertInput(-256, new byte[] { 0xC7, 0x00 }, reader => reader.ReadInt32()); - AssertInput(2047, new byte[] { 0xCF, 0xFF }, reader => reader.ReadInt32()); - } - - [Fact] - public void Int32ThreeOctets() - { - AssertInput(-262144, new byte[] { 0xD0, 0x00, 0x00 }, reader => reader.ReadInt32()); - AssertInput(262143, new byte[] { 0xD7, 0xFF, 0xFF }, reader => reader.ReadInt32()); - } - - [Fact] - public void Int32Full() - { - AssertInput(262144, new byte[] { 0x49, 0x00, 0x04, 0x00, 0x00 }, reader => reader.ReadInt32()); - } - - [Fact] - public void Int64SingleOctet() - { - AssertInput(0L, new byte[] { 0xE0 }, reader => reader.ReadInt64()); - AssertInput(-8L, new byte[] { 0xD8 }, reader => reader.ReadInt64()); - AssertInput(15L, new byte[] { 0xEF }, reader => reader.ReadInt64()); - } - - [Fact] - public void Int64TwoOctet() - { - AssertInput(-256L, new byte[] { 0xF7, 0x00 }, reader => reader.ReadInt64()); - AssertInput(-2048L, new byte[] { 0xF0, 0x00 }, reader => reader.ReadInt64()); - AssertInput(2047L, new byte[] { 0xFF, 0xFF }, reader => reader.ReadInt64()); - } - - [Fact] - public void Int64ThreeOctet() - { - AssertInput(-262144L, new byte[] { 0x38, 0x00, 0x00 }, reader => reader.ReadInt64()); - AssertInput(262143L, new byte[] { 0x3F, 0xFF, 0xFF }, reader => reader.ReadInt64()); - } - - [Fact] - public void Int64Full() - { - AssertInput(0x80000000L, new byte[] { 0x4C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }, reader => reader.ReadInt64()); - } - - [Fact] - public void DoubleZero() - { - AssertInput(0.0d, new byte[] { 0x5B }, reader => reader.ReadDouble()); - } - - [Fact] - public void DoubleOne() - { - AssertInput(1.0d, new byte[] { 0x5C }, reader => reader.ReadDouble()); - } - - [Fact] - public void DoubleOctet() - { - AssertInput(123.0d, new byte[] { 0x5D, 0x7B }, reader => reader.ReadDouble()); - } - - [Fact] - public void DoubleShort() - { - AssertInput(-32768.0d, new byte[] { 0x5E, 0x80, 0x00 }, reader => reader.ReadDouble()); - AssertInput(32767.0d, new byte[] { 0x5E, 0x7F, 0xFF }, reader => reader.ReadDouble()); - } - - [Fact] - public void DoubleFloat() - { - AssertInput(value => Assert.Equal(Single.MinValue, Convert.ToDouble(value)), new byte[] {0x5F, 0xFF, 0x7F, 0xFF, 0xFF}, reader => reader.ReadDouble()); - AssertInput(value => Assert.Equal(Single.MaxValue, Convert.ToDouble(value)), new byte[] {0x5F, 0x7F, 0x7F, 0xFF, 0xFF}, reader => reader.ReadDouble()); - } - - [Fact] - public void DoubleFull() - { - AssertInput(Double.MaxValue - 1, new byte[] { 0x5A, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, reader => reader.ReadDouble()); - } - - [Fact] - public void StringEmpty() - { - AssertInput(String.Empty, new byte[] {0x00}, reader => reader.ReadString()); - } - - [Fact] - public void StringShort() - { - AssertInput("\u00c3", new byte[] {0x01, 0xC3, 0x83}, reader => reader.ReadString()); - AssertInput("hello", new byte[] {0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F}, reader => reader.ReadString()); - } - - [Fact] - public void StringCompact() - { - const string expected = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat"; - var data = new byte[] - { - 0x30, 0x8f, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, - 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x65, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, - 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, - 0x64, 0x69, 0x61, 0x6d, 0x20, 0x6e, 0x6f, 0x6e, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x6e, 0x69, 0x62, - 0x68, 0x20, 0x65, 0x75, 0x69, 0x73, 0x6d, 0x6f, 0x64, 0x20, 0x74, 0x69, 0x6e, 0x63, 0x69, 0x64, - 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, 0x6c, 0x61, 0x6f, 0x72, 0x65, 0x65, 0x74, 0x20, 0x64, - 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x20, 0x61, 0x6c, 0x69, 0x71, - 0x75, 0x61, 0x6d, 0x20, 0x65, 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x74, 0x70, 0x61, - 0x74 - }; - - AssertInput(expected, data, reader => reader.ReadString()); - } - - [Fact] - public void DateTimeShort() - { - var data = new byte[] { 0x4B, 0x00, 0xE3, 0x83, 0x8F }; - AssertInput(new DateTime(1998, 5, 8, 9, 51, 00, DateTimeKind.Utc), data, reader => reader.ReadDateTime()); - } - - [Fact] - public void DateTimeFull() - { - var data = new byte[] { 0x4A, 0x00, 0x00, 0x00, 0xD0, 0x4B, 0x92, 0x84, 0xB8 }; - AssertInput(new DateTime(1998, 5, 8, 9, 51, 31, DateTimeKind.Utc), data, reader => reader.ReadDateTime()); - } - - private static void AssertInput(Action validator, byte[] input, Func action) - { - var memoryStream = new MemoryStream(input); - var reader = new HessianInputReader(memoryStream); - - validator(action(reader)); - } - - private static void AssertInput(object expected, byte[] input, Func action) - { - var memoryStream = new MemoryStream(input); - var reader = new HessianInputReader(memoryStream); - - Assert.Equal(expected, action(reader)); - } - - private static void AssertInput(byte[] expected, byte[] input, Func action) - { - var memoryStream = new MemoryStream(input); - var reader = new HessianInputReader(memoryStream); - var temp = action(reader); - - if (!ReferenceEquals(expected, temp)) - { - - Assert.Equal(expected.Length,temp.Length); - - - for (var index = 0; index < expected.Length; index++) - { - - Assert.Equal(expected[index], temp[index]); - - } - } - } - } -} \ No newline at end of file diff --git a/tests/Hessian.NET.Tests/HessianOutputWriterTests.cs b/tests/Hessian.NET.Tests/HessianOutputWriterTests.cs deleted file mode 100644 index fcfd018..0000000 --- a/tests/Hessian.NET.Tests/HessianOutputWriterTests.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Text; -using Hessian.Net; -using Xunit; - -namespace Hessian.NET.Tests -{ - // - // http://hessian.caucho.com/doc/hessian-serialization.html##date - // - public class HessianOutputWriterTests - { - [Fact] - public void BooleanTrue() - { - AssertOutput(new[] { (byte)'T' }, writer => writer.WriteBoolean(true)); - } - - [Fact] - public void BooleanFalse() - { - AssertOutput(new[] { (byte)'F' }, writer => writer.WriteBoolean(false)); - } - - [Fact] - public void BytesCompactEmpty() - { - AssertOutput(new[] { (byte)0x20 }, writer => writer.WriteBytes(new byte[0])); - } - - [Fact] - public void BytesCompactOne() - { - AssertOutput(new[] { (byte)0x21, (byte)0x01 }, writer => writer.WriteBytes(new byte[] { 1 })); - } - - [Fact] - public void BytesCompact15() - { - var value = new byte[15]; - - for (var index = 0; index < value.Length; index++) - { - value[index] = (byte)index; - } - - var expected = new byte[16]; - - expected[0] = (byte)(0x20 + value.Length); - value.CopyTo(expected, 1); - - AssertOutput(expected, writer => writer.WriteBytes(value)); - } - - [Fact] - public void BytesSingleChunk() - { - var data = new byte[255]; - - for (var index = 0; index < data.Length; index++) - { - data[index] = (byte)index; - } - - var expected = new byte[3 + data.Length]; - - WriteBytesLengthPrefix(expected, 0, data.Length, true); - - data.CopyTo(expected, 3); - - AssertOutput(expected, writer => writer.WriteBytes(data)); - } - - [Fact] - public void BytesMultiChunk() - { - const int sigLength = 3; - const int chunkSize = 0x8000; - const int appendixSize = 3; - var buffer = new byte[chunkSize + appendixSize]; - - for (var index = 0; index < buffer.Length; index++) - { - buffer[index] = (byte)(index + 1); - } - - var expected = new byte[sigLength + chunkSize + sigLength + appendixSize]; - - WriteBytesLengthPrefix(expected, 0, chunkSize, false); - CopyBytes(buffer, 0, expected, sigLength, chunkSize); - WriteBytesLengthPrefix(expected, sigLength + chunkSize, appendixSize, true); - CopyBytes(buffer, chunkSize, expected, sigLength + chunkSize + sigLength, appendixSize); - - AssertOutput(expected, writer => writer.WriteBytes(buffer)); - } - - [Fact] - public void DateTimeShort() - { - var expected = new byte[] { 0x4B, 0x00, 0xE3, 0x83, 0x8F }; - AssertOutput(expected, writer => writer.WriteDateTime(new DateTime(1998, 5, 8, 9, 51, 00, DateTimeKind.Utc))); - } - - [Fact] - public void DateTimeFull() - { - var expected = new byte[] { 0x4A, 0x00, 0x00, 0x00, 0xD0, 0x4B, 0x92, 0x84, 0xB8 }; - AssertOutput(expected, writer => writer.WriteDateTime(new DateTime(1998, 5, 8, 9, 51, 31, DateTimeKind.Utc))); - } - - [Fact] - public void DoubleZero() - { - AssertOutput(new byte[] { 0x5B }, writer => writer.WriteDouble(0.0d)); - } - - [Fact] - public void DoubleOne() - { - AssertOutput(new byte[] { 0x5C }, writer => writer.WriteDouble(1.0d)); - } - - [Fact] - public void DoubleOctet() - { - var expected = new byte[] { 0x5D, 0x7B }; - AssertOutput(expected, writer => writer.WriteDouble(123.0d)); - } - - [Fact] - public void DoubleShort() - { - AssertOutput(new byte[] { 0x5E, 0x80, 0x00 }, writer => writer.WriteDouble(-32768d)); - AssertOutput(new byte[] { 0x5E, 0x7F, 0xFF }, writer => writer.WriteDouble(32767d)); - } - - [Fact] - public void DoubleFloat() - { - AssertOutput(new byte[] { 0x5F, 0xFF, 0x7F, 0xFF, 0xFF }, writer => writer.WriteDouble(Single.MinValue)); - AssertOutput(new byte[] { 0x5F, 0x7F, 0x7F, 0xFF, 0xFF }, writer => writer.WriteDouble(Single.MaxValue)); - } - - [Fact] - public void DoubleFull() - { -// var expected = new byte[] { 0x5A, 0x40, 0x28, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var expected = new byte[] { 0x5A, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - AssertOutput(expected, writer => writer.WriteDouble(Double.MaxValue - 1)); - } - - [Fact] - public void Int32SingleOctet() - { - AssertOutput(new byte[] { 0x80 }, writer => writer.WriteInt32(-16)); - AssertOutput(new byte[] { 0x90 }, writer => writer.WriteInt32(0)); - AssertOutput(new byte[] { 0xBF }, writer => writer.WriteInt32(47)); - } - - [Fact] - public void Int32TwoOctets() - { - AssertOutput(new byte[] { 0xC0, 0x00 }, writer => writer.WriteInt32(-2048)); - AssertOutput(new byte[] { 0xC7, 0x00 }, writer => writer.WriteInt32(-256)); - AssertOutput(new byte[] { 0xCF, 0xFF }, writer => writer.WriteInt32(2047)); - } - - [Fact] - public void Int32ThreeOctets() - { - AssertOutput(new byte[] { 0xD0, 0x00, 0x00 }, writer => writer.WriteInt32(-262144)); - AssertOutput(new byte[] { 0xD7, 0xFF, 0xFF }, writer => writer.WriteInt32(262143)); - } - - [Fact] - public void Int32Full() - { - AssertOutput(new byte[] { 0x49, 0x00, 0x04, 0x00, 0x00 }, writer => writer.WriteInt32(262144)); - } - - [Fact] - public void Int64SingleOctet() - { - AssertOutput(new byte[] { 0xD8 }, writer => writer.WriteInt64(-8)); - AssertOutput(new byte[] { 0xE0 }, writer => writer.WriteInt64(0)); - AssertOutput(new byte[] { 0xEF }, writer => writer.WriteInt64(15)); - } - - [Fact] - public void Int64TwoOctets() - { - AssertOutput(new byte[] { 0xF0, 0x00 }, writer => writer.WriteInt64(-2048)); - AssertOutput(new byte[] { 0xF7, 0x00 }, writer => writer.WriteInt64(-256)); - AssertOutput(new byte[] { 0xFF, 0xFF }, writer => writer.WriteInt64(2047)); - } - - [Fact] - public void Int64ThreeOctets() - { - AssertOutput(new byte[] { 0x38, 0x00, 0x00 }, writer => writer.WriteInt64(-262144)); - AssertOutput(new byte[] { 0x3F, 0xFF, 0xFF }, writer => writer.WriteInt64(262143)); - } - - [Fact] - public void Int64Full() - { - var expected = new byte[] { 0x4C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }; - AssertOutput(expected, writer => writer.WriteInt64(0x80000000L)); - } - - [Fact] - public void StringEmpty() - { - AssertOutput(new byte[] {0x00}, writer => writer.WriteString(null)); - AssertOutput(new byte[] {0x00}, writer => writer.WriteString(String.Empty)); - } - - [Fact] - public void StringShort() - { - AssertOutput(new byte[] { 0x01, 0xC3, 0x83 }, writer => writer.WriteString("\u00c3")); - AssertOutput(new byte[] { 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F }, writer => writer.WriteString("hello")); - } - - [Fact] - public void StringCompact() - { - const string text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat"; - var expected = new byte[] - { - 0x30, 0x8f, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, - 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x65, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, - 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, - 0x64, 0x69, 0x61, 0x6d, 0x20, 0x6e, 0x6f, 0x6e, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x6e, 0x69, 0x62, - 0x68, 0x20, 0x65, 0x75, 0x69, 0x73, 0x6d, 0x6f, 0x64, 0x20, 0x74, 0x69, 0x6e, 0x63, 0x69, 0x64, - 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, 0x6c, 0x61, 0x6f, 0x72, 0x65, 0x65, 0x74, 0x20, 0x64, - 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x20, 0x61, 0x6c, 0x69, 0x71, - 0x75, 0x61, 0x6d, 0x20, 0x65, 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x74, 0x70, 0x61, - 0x74 - }; - - AssertOutput(expected, writer => writer.WriteString(text)); - } - - private static void AssertOutput(byte[] expected, Action action) - { - var stream = new MemoryStream(); - byte[] bytes; - - using (var writer = new HessianOutputWriter(stream)) - { - action(writer); - bytes = stream.ToArray(); - } - - //WriteOutput(bytes); - - Assert.True(ByteArray.Equals(expected, bytes)); - } - - private static void WriteOutput(byte[] bytes) - { - for (var offset = 0; offset < bytes.Length;) - { - var count = Math.Min(bytes.Length - offset, 16); - var line = new StringBuilder(); - - line.AppendFormat("{0:X08}: ", offset); - - for (var position = 0; position < count; position++) - { - line.AppendFormat("{0:x02} ", bytes[offset]); - offset++; - } - - Debug.WriteLine(line.ToString()); - } - } - - private static void WriteBytesLengthPrefix(byte[] buffer, int offset, int length, bool final) - { - buffer[offset] = final ? (byte)'B' : (byte)'b'; - buffer[offset + 1] = (byte) (length >> 8); - buffer[offset + 2] = (byte) length; - } - - private static void CopyBytes(byte[] source, int srcoffset, byte[] dest, int destoffset, int count) - { - for (var index = 0; index < count; index++) - { - dest[destoffset + index] = source[srcoffset + index]; - } - } - - /*private static void AssertOutput(bool value, byte[] expected) - { - var stream = new MemoryStream(); - byte[] bytes; - - using (var writer = new HessianOutputWriter(stream)) - { - writer.WriteBoolean(value); - bytes = stream.ToArray(); - } - - Assert.IsTrue(ByteArray.Equals(expected, bytes)); - }*/ - - /*private static void AssertOutput(byte[] value, byte[] expected) - { - var stream = new MemoryStream(); - byte[] bytes; - - using (var writer = new HessianOutputWriter(stream)) - { - writer.WriteBytes(value); - bytes = stream.ToArray(); - } - - Assert.IsTrue(ByteArray.Equals(expected, bytes)); - }*/ - } -} diff --git a/tests/Hessian.NET.Tests/HessianSerializerTests.cs b/tests/Hessian.NET.Tests/HessianSerializerTests.cs deleted file mode 100644 index 409c2fb..0000000 --- a/tests/Hessian.NET.Tests/HessianSerializerTests.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System.IO; -using System.Runtime.Serialization; -using Hessian.Net; -using Xunit; - -namespace Hessian.NET.Tests -{ - - public class HessianSerializerTests - { - [Fact] - public void SimpleSerialize() - { - byte[] expected = - { - 0x43, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x92, 0x06, 0x43, 0x6c, - 0x61, 0x73, 0x73, 0x32, 0x04, 0x4e, 0x65, 0x78, 0x74, 0x60, 0x43, 0x0a, 0x54, 0x65, 0x73, 0x74, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x32, 0x93, 0x03, 0x49, 0x6e, 0x74, 0x06, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x61, 0x91, 0x0b, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, - 0x49, 0x70, 0x73, 0x75, 0x6d, 0x51, 0x90, 0x51, 0x90 - }; - var graph = new TestClass1 - { - Class2 = new TestClass2 - { - IntValue = 1, - StringValue = "Lorem Ipsum" - } - }; - - graph.Next = graph; - graph.Class2.Parent = graph; - - using (var stream = new MemoryStream()) - { - var serializer = new DataContractHessianSerializer(typeof (TestClass1)); - - serializer.WriteObject(stream, graph); - stream.Flush(); - - Assert.True(ByteArray.Equals(expected, stream.ToArray())); - } - } - - [Fact] - public void SimpleDeserialize() - { - byte[] expected = - { - 0x43, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x92, 0x06, 0x43, 0x6c, - 0x61, 0x73, 0x73, 0x32, 0x04, 0x4e, 0x65, 0x78, 0x74, 0x60, 0x43, 0x0a, 0x54, 0x65, 0x73, 0x74, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x32, 0x93, 0x03, 0x49, 0x6e, 0x74, 0x06, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x61, 0x91, 0x0b, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, - 0x49, 0x70, 0x73, 0x75, 0x6d, 0x51, 0x90, 0x51, 0x90 - }; - var graph = new TestClass1 - { - Class2 = new TestClass2 - { - IntValue = 1, - StringValue = "Lorem Ipsum" - } - }; - - graph.Next = graph; - graph.Class2.Parent = graph; - - using (var stream = new MemoryStream(expected)) - { - var serializer = new DataContractHessianSerializer(typeof (TestClass1)); - var instance = serializer.ReadObject(stream) as TestClass1; - - Assert.NotNull(instance); - Assert.Equal(graph.GetType(), instance.GetType()); - Assert.Equal(instance, instance.Next); - Assert.Equal(typeof (TestClass2), instance.Class2.GetType()); - Assert.Equal(instance, instance.Class2.Parent); - Assert.Equal(graph.Class2.IntValue, instance.Class2.IntValue); - Assert.Equal(graph.Class2.StringValue, instance.Class2.StringValue); - } - } - -/* - private static void WriteOutput(IReadOnlyList bytes) - { - const int size = 16; - - var pattern = new String(' ', 3); - - for (var offset = 0; offset < bytes.Count; ) - { - var count = Math.Min(bytes.Count - offset, size); - var line = new StringBuilder(); - - line.AppendFormat("{0:X08}: ", offset); - - for (var position = 0; position < size; position++) - { - if (position < count) - { - line.AppendFormat("{0:x02} ", bytes[position + offset]); - } - else - { - line.Append(pattern); - } - } - - line.Append(new string(' ', 2)); - - for (var position = 0; position < count; position++) - { - var ch = (char) bytes[position + offset]; - line.Append(Char.IsLetterOrDigit(ch) ? ch : '.'); - } - - Debug.WriteLine(line.ToString()); - - offset += count; - } - } -*/ - - [DataContract(Name = "TestClass1")] - public class TestClass1 - { - [DataMember(Name = "Class2")] - public TestClass2 Class2 - { - get; - set; - } - - [DataMember(Name = "Next")] - public TestClass1 Next - { - get; - set; - } - } - - [DataContract(Name = "TestClass2")] - public class TestClass2 - { - [DataMember(Name = "Int", Order = 1)] - public int IntValue - { - get; - set; - } - - [DataMember(Name = "String", Order = 2)] - public string StringValue - { - get; - set; - } - - [DataMember(Name = "Link", Order = 3)] - public TestClass1 Parent - { - get; - set; - } - } - } -} \ No newline at end of file diff --git a/tests/Hessian.Tests/Hessian.Tests.csproj b/tests/Hessian.Tests/Hessian.Tests.csproj new file mode 100644 index 0000000..314a58e --- /dev/null +++ b/tests/Hessian.Tests/Hessian.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/tests/Hessian.Tests/UnitTest1.cs b/tests/Hessian.Tests/UnitTest1.cs new file mode 100644 index 0000000..4b82831 --- /dev/null +++ b/tests/Hessian.Tests/UnitTest1.cs @@ -0,0 +1,13 @@ +using System; +using Xunit; + +namespace Hessian.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + } + } +} \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config new file mode 100644 index 0000000..f27ab48 --- /dev/null +++ b/tools/packages.config @@ -0,0 +1,4 @@ + + + +