diff --git a/DotXxlJob.sln b/DotXxlJob.sln index dee34fe..9fe021a 100644 --- a/DotXxlJob.sln +++ b/DotXxlJob.sln @@ -5,21 +5,17 @@ VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{97756BA5-1E7C-4536-A49E-AE2190C0E6A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotXxlJob.Core", "src\DotXxlJob.Core\DotXxlJob.Core.csproj", "{FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hessian.NET", "src\Hessian.NET\Hessian.NET.csproj", "{86456232-19D5-48DD-AC39-A0526517E0AD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotXxlJob.Core", "src\DotXxlJob.Core\DotXxlJob.Core.csproj", "{FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{352EC932-F112-4A2F-9DC3-F0761C85E068}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hessian.NET.Tests", "tests\Hessian.NET.Tests\Hessian.NET.Tests.csproj", "{187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E959F9B5-F3EB-48B1-B842-2CDDFDB01900}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASPNetCoreExecutor", "samples\ASPNetCoreExecutor\ASPNetCoreExecutor.csproj", "{DC9E5AF3-18FF-4713-BDB4-672E47ADA4E5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASPNetCoreExecutor", "samples\ASPNetCoreExecutor\ASPNetCoreExecutor.csproj", "{DC9E5AF3-18FF-4713-BDB4-672E47ADA4E5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HessianReader", "samples\HessianReader\HessianReader.csproj", "{F822B528-95FD-40B4-9EE0-3AE8878075AC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HessianReader", "samples\HessianReader\HessianReader.csproj", "{F822B528-95FD-40B4-9EE0-3AE8878075AC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hessian", "src\Hessian\Hessian.csproj", "{BD9B8108-6528-430F-AD28-6F8434A29F55}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hessian", "src\Hessian\Hessian.csproj", "{BD9B8108-6528-430F-AD28-6F8434A29F55}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -30,17 +26,6 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65} = {97756BA5-1E7C-4536-A49E-AE2190C0E6A5} - {86456232-19D5-48DD-AC39-A0526517E0AD} = {97756BA5-1E7C-4536-A49E-AE2190C0E6A5} - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9} = {352EC932-F112-4A2F-9DC3-F0761C85E068} - {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} - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -54,30 +39,6 @@ Global {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}.Release|x64.Build.0 = Release|Any CPU {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}.Release|x86.ActiveCfg = Release|Any CPU {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65}.Release|x86.Build.0 = Release|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Debug|x64.ActiveCfg = Debug|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Debug|x64.Build.0 = Debug|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Debug|x86.ActiveCfg = Debug|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Debug|x86.Build.0 = Debug|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Release|Any CPU.Build.0 = Release|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Release|x64.ActiveCfg = Release|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Release|x64.Build.0 = Release|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Release|x86.ActiveCfg = Release|Any CPU - {86456232-19D5-48DD-AC39-A0526517E0AD}.Release|x86.Build.0 = Release|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Debug|x64.ActiveCfg = Debug|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Debug|x64.Build.0 = Debug|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Debug|x86.ActiveCfg = Debug|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Debug|x86.Build.0 = Debug|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Release|Any CPU.Build.0 = Release|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Release|x64.ActiveCfg = Release|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Release|x64.Build.0 = Release|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Release|x86.ActiveCfg = Release|Any CPU - {187B28C7-C3D7-4E0A-A84B-98B7C1C758F9}.Release|x86.Build.0 = Release|Any CPU {DC9E5AF3-18FF-4713-BDB4-672E47ADA4E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC9E5AF3-18FF-4713-BDB4-672E47ADA4E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC9E5AF3-18FF-4713-BDB4-672E47ADA4E5}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -115,4 +76,16 @@ Global {BD9B8108-6528-430F-AD28-6F8434A29F55}.Release|x86.ActiveCfg = Release|Any CPU {BD9B8108-6528-430F-AD28-6F8434A29F55}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FFFEEA78-CB09-4BFB-89B7-E9A46EC3ED65} = {97756BA5-1E7C-4536-A49E-AE2190C0E6A5} + {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} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F4A8B63E-6284-4D00-9719-BAB1D955DACF} + EndGlobalSection EndGlobal diff --git a/samples/ASPNetCoreExecutor/1547621183.dat b/samples/ASPNetCoreExecutor/1547621183.dat deleted file mode 100644 index d8c48ad..0000000 Binary files a/samples/ASPNetCoreExecutor/1547621183.dat and /dev/null differ diff --git a/samples/ASPNetCoreExecutor/ASPNetCoreExecutor.csproj b/samples/ASPNetCoreExecutor/ASPNetCoreExecutor.csproj index 181e783..99a0127 100644 --- a/samples/ASPNetCoreExecutor/ASPNetCoreExecutor.csproj +++ b/samples/ASPNetCoreExecutor/ASPNetCoreExecutor.csproj @@ -1,8 +1,7 @@  - netcoreapp2.2 - InProcess + netcoreapp2.2 @@ -13,4 +12,8 @@ + + <_ContentIncludedByDefault Remove="Properties\launchSettings.json" /> + + diff --git a/samples/ASPNetCoreExecutor/Properties/launchSettings.json b/samples/ASPNetCoreExecutor/Properties/launchSettings.json deleted file mode 100644 index 663adfb..0000000 --- a/samples/ASPNetCoreExecutor/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:22613", - "sslPort": 44333 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "ASPNetCoreExecutor": { - "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/samples/ASPNetCoreExecutor/XxlJobExecutorMiddleware.cs b/samples/ASPNetCoreExecutor/XxlJobExecutorMiddleware.cs index 882891f..8c4c527 100644 --- a/samples/ASPNetCoreExecutor/XxlJobExecutorMiddleware.cs +++ b/samples/ASPNetCoreExecutor/XxlJobExecutorMiddleware.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Net; using System.Threading.Tasks; using DotXxlJob.Core; @@ -28,11 +29,21 @@ namespace ASPNetCoreExecutor if ("POST".Equals(context.Request.Method, StringComparison.OrdinalIgnoreCase) && "application/octet-stream".Equals(context.Request.ContentType, StringComparison.OrdinalIgnoreCase)) { + /* + using (Stream file = File.Create("./"+DateTime.Now.ToUnixTimeSeconds()+".data")) + { + context.Request.Body.CopyTo(file); + } + + return; + */ + 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); diff --git a/samples/HessianReader/1547620263.dat b/samples/HessianReader/1547620263.dat deleted file mode 100644 index 3f69aab..0000000 Binary files a/samples/HessianReader/1547620263.dat and /dev/null differ diff --git a/samples/HessianReader/1547621183.dat b/samples/HessianReader/1547621183.dat deleted file mode 100644 index d8c48ad..0000000 Binary files a/samples/HessianReader/1547621183.dat and /dev/null differ diff --git a/samples/HessianReader/HessianReader.csproj b/samples/HessianReader/HessianReader.csproj index 5f6fbcb..37178a2 100644 --- a/samples/HessianReader/HessianReader.csproj +++ b/samples/HessianReader/HessianReader.csproj @@ -6,21 +6,16 @@ - - + - - C:\Program Files\dotnet\sdk\NuGetFallbackFolder\newtonsoft.json\11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll - + + - - PreserveNewest - - + PreserveNewest diff --git a/samples/HessianReader/Program.cs b/samples/HessianReader/Program.cs index ebade53..f41dbd6 100644 --- a/samples/HessianReader/Program.cs +++ b/samples/HessianReader/Program.cs @@ -1,8 +1,9 @@ using System; -using System.Collections; +using System.Collections.Generic; using System.IO; +using DotXxlJob.Core; using DotXxlJob.Core.Model; -using Hessian.Net; +using Hessian; using Newtonsoft.Json; namespace HessianReader @@ -11,7 +12,7 @@ namespace HessianReader { static void Main(string[] args) { - byte[] myBinary = File.ReadAllBytes("1547621183.dat"); + byte[] myBinary = File.ReadAllBytes("log.dat"); foreach (var i in myBinary) { @@ -21,39 +22,32 @@ namespace HessianReader } Console.WriteLine(Environment.NewLine); - Console.WriteLine("---------------------------------------------------------------"); + Console.WriteLine("---------------------{0}------------------------------------------",myBinary.Length); + - var serializer = new DataContractHessianSerializer(typeof (RpcRequest)); - using (var stream1 = new MemoryStream(myBinary)) { - - var s = new Hessian.Deserializer(stream1); - var a = s.ReadValue(); - Console.WriteLine(a); - a = s.ReadValue(); - Console.WriteLine(a); - Console.WriteLine(JsonConvert.SerializeObject(a)); - a = s.ReadValue(); - Console.WriteLine(a); - Console.WriteLine(JsonConvert.SerializeObject(a)); - a = s.ReadValue(); - Console.WriteLine(a); - Console.WriteLine(JsonConvert.SerializeObject(a)); + var s1 = HessianSerializer.DeserializeRequest(stream1); + Console.WriteLine(JsonConvert.SerializeObject(s1)); } + + Console.WriteLine("------------------------------------------------------------"); + Console.ReadKey(); - return; + /** + * + * Console.WriteLine("---------------------------------------------------------------"); RpcRequest req = new RpcRequest { RequestId = "71565f61-94e8-4dcf-9760-f2fb73a6886a", CreateMillisTime = 1547621183585, AccessToken = "", ClassName = "com.xxl.job.core.biz.ExecutorBiz", MethodName = "run", - ParameterTypes = new HessianArrayList {"class com.xxl.job.core.biz.model.TriggerParam"}, - Version = "null", - Parameters = new HessianArrayList() + ParameterTypes = new List {new JavaClass{ Name = "com.xxl.job.core.biz.model.TriggerParam"}}, + Version = null, + Parameters = new List() }; var p =new TriggerParam { @@ -71,37 +65,25 @@ namespace HessianReader BroadcastTotal=1 }; req.Parameters.Add(p); - - byte[] distArray; - using (MemoryStream stream = new MemoryStream()) - { - - - serializer.WriteObject(stream,req); - //Console.WriteLine(Environment.NewLine); - //Console.WriteLine("---------------------------"+ stream.Length+"------------------------------------"); - stream.Flush(); - distArray =stream.ToArray(); - } - foreach (var j in distArray) + using (var stream2 = new MemoryStream()) { - Console.Write("0x"); - Console.Write(j.ToString("x2")); - Console.Write(","); + var serializer = new Serializer(stream2); + serializer.WriteObject(req); + Console.WriteLine("-----------------------------序列化成功---{0}-------------------------------",stream2.Length); + stream2.Position = 0; + + var s2 = HessianSerializer.DeserializeRequest(stream2); + Console.WriteLine(JsonConvert.SerializeObject(s2)); } - - Console.WriteLine(Environment.NewLine); - Console.WriteLine("---------------------------------------------------------------"); - - using (var stream2 = new MemoryStream(distArray)) - { - - var instance = serializer.ReadObject(stream2) as RpcRequest; + * [{"Item1":"requestId","Item2":"71565f61-94e8-4dcf-9760-f2fb73a6886a"},{"Item1":"createMillisTime","Item2":1432957289},{"Item1":"accessToken","Item2":""},{"Item1":"className","Item2":"com.xxl.job.core.biz.ExecutorBiz"},{"Item1":"methodName","Item2":"run"},{"Item1":"version","Item2":null},{"Item1":"parameterT +ypes","Item2":[{"Name":"java.lang.Class","Fields":["name"]}]},{"Item1":"parameters","Item2":[{"Item1":"name","Item2":"com.xxl.job.core.biz.model.TriggerParam"}]}] +System.Collections.Generic.List`1[System.Object] +[{"Name":"com.xxl.job.core.biz.model.TriggerParam","Fields":["jobId","executorHandler","executorParams","executorBlockStrategy","executorTimeout","logId","logDateTim","glueType","glueSource","glueUpdatetime","broadcastIndex","broadcastTotal"]}] + Hessian.HessianObject +[{"Item1":"jobId","Item2":1},{"Item1":"executorHandler","Item2":"demoJobHandler"},{"Item1":"executorParams","Item2":"111"},{"Item1":"executorBlockStrategy","Item2":"SERIAL_EXECUTION"},{"Item1":"executorTimeout","Item2":0},{"Item1":"logId","Item2":5},{"Item1":"logDateTim","Item2":1432956926},{"Item1":"glueTy +pe","Item2":"BEAN"},{"Item1":"glueSource","Item2":""},{"Item1":"glueUpdatetime","Item2":-638368258},{"Item1":"broadcastIndex","Item2":0},{"Item1":"broadcastTotal","Item2":1}] - Console.WriteLine(JsonConvert.SerializeObject(instance)); - } - /** * requestId='71565f61-94e8-4dcf-9760-f2fb73a6886a', * createMillisTime=1547621183585, * accessToken='', diff --git a/samples/HessianReader/kill.dat b/samples/HessianReader/kill.dat new file mode 100644 index 0000000..54767e0 Binary files /dev/null and b/samples/HessianReader/kill.dat differ diff --git a/samples/HessianReader/log.dat b/samples/HessianReader/log.dat new file mode 100644 index 0000000..a1ad998 Binary files /dev/null and b/samples/HessianReader/log.dat differ diff --git a/samples/ASPNetCoreExecutor/1547620263.dat b/samples/HessianReader/run.dat similarity index 100% rename from samples/ASPNetCoreExecutor/1547620263.dat rename to samples/HessianReader/run.dat diff --git a/src/DotXxlJob.Core/AdminClient.cs b/src/DotXxlJob.Core/AdminClient.cs new file mode 100644 index 0000000..52dc0fe --- /dev/null +++ b/src/DotXxlJob.Core/AdminClient.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using DotXxlJob.Core.Config; +using DotXxlJob.Core.Model; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace DotXxlJob.Core +{ + public class AdminClient + { + static readonly string MAPPING = "/api"; + private readonly XxlJobExecutorOptions _options; + private readonly IHttpClientFactory _clientFactory; + private readonly ILogger _logger; + + private List _addresses; + + private int _currentIndex; + + public AdminClient(IOptions optionsAccessor + ,IHttpClientFactory clientFactory + ,ILogger logger) + { + this._options = optionsAccessor.Value; + _clientFactory = clientFactory; + this._logger = logger; + InitAddress(); + } + + private void InitAddress() + { + this._addresses = new List(); + foreach (var item in this._options.AdminAddresses) + { + try + { + var uri = new Uri(item + MAPPING); + var entry = new AddressEntry { RequestUri = uri }; + this._addresses.Add(entry); + } + catch (Exception ex) + { + this._logger.LogError(ex, "init admin address error."); + } + } + } + + + public Task Callback(List callbackParamList) + { + return InvokeRpcService("callback", new List {"java.util.List"}, callbackParamList); + } + + + public Task Registry(RegistryParam registryParam) + { + return InvokeRpcService("callback", new List {"java.lang.Class"}, registryParam); + } + + public Task RegistryRemove(RegistryParam registryParam) + { + return InvokeRpcService("callback", new List {"java.lang.Class"}, registryParam); + } + + private async Task InvokeRpcService(string methodName, List parameterTypes, + object parameters) + { + var request = new RpcRequest { + CreateMillisTime = DateTime.Now.ToUnixTimeSeconds(), + AccessToken = this._options.AccessToken, + ClassName = "com.xxl.job.core.biz.AdminBiz", + MethodName = methodName, + ParameterTypes = parameterTypes.ToList(), + Parameters = new List {parameters} + }; + byte[] postBuf; + using (var stream = new MemoryStream()) + { + HessianSerializer.SerializeRequest(stream,request); + + postBuf =stream.ToArray(); + } + + int triedTimes = 0; + + using (var client = this._clientFactory.CreateClient()) + { + + while (triedTimes++ < _addresses.Count) + { + var address = _addresses[_currentIndex]; + _currentIndex = (_currentIndex + 1) % _addresses.Count; + if (!address.CheckAccessable()) + continue; + + Stream resStream; + try + { + resStream =await DoPost(client, address, postBuf); + address.Reset(); + + } + catch (Exception ex) + { + _logger.LogError(ex, "request admin error."); + address.SetFail(); + continue; + } + + RpcResponse res; + try + { + res = HessianSerializer.DeserializeResponse(resStream); + } + catch (Exception ex) + { + _logger.LogError("des"); + } + + + if (res.IsError) + { + throw new Exception(res.error); + } + else + { + return rpcResponse.result as ReturnT; + } + } + } + throw new Exception("xxl-rpc server address not accessable."); + + + } + + private async Task DoPost(HttpClient client,AddressEntry address, byte[] postBuf) + { + var content = new ByteArrayContent(postBuf); + content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + var responseMessage = await client.PostAsync(address.RequestUri, content); + responseMessage.EnsureSuccessStatusCode(); + return await responseMessage.Content.ReadAsStreamAsync(); + } + + + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/CallbackTaskQueue.cs b/src/DotXxlJob.Core/CallbackTaskQueue.cs index 4df750c..ef50149 100644 --- a/src/DotXxlJob.Core/CallbackTaskQueue.cs +++ b/src/DotXxlJob.Core/CallbackTaskQueue.cs @@ -6,7 +6,8 @@ namespace DotXxlJob.Core { public void Push(CallbackParam callbackParam) { - throw new System.NotImplementedException(); + + //throw new System.NotImplementedException(); } } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/Constants.cs b/src/DotXxlJob.Core/Constants.cs index ec5092a..0ecb03e 100644 --- a/src/DotXxlJob.Core/Constants.cs +++ b/src/DotXxlJob.Core/Constants.cs @@ -4,6 +4,8 @@ namespace DotXxlJob.Core { internal static class Constants { + public const string RpcRequestJavaFullName = "com.xxl.rpc.remoting.net.params.XxlRpcRequest"; + public const string XxlLogsDefaultRootDirectory = "xxl-job-logs"; public const string HandleLogsDirectory = "HandlerLogs"; public const string LogFileNameCallContextKey = "XxlJob.LogFileName"; diff --git a/src/DotXxlJob.Core/DotXxlJob.Core.csproj b/src/DotXxlJob.Core/DotXxlJob.Core.csproj index fc5fc47..cb6e128 100644 --- a/src/DotXxlJob.Core/DotXxlJob.Core.csproj +++ b/src/DotXxlJob.Core/DotXxlJob.Core.csproj @@ -6,10 +6,11 @@ + - + diff --git a/src/DotXxlJob.Core/HessianSerializer.cs b/src/DotXxlJob.Core/HessianSerializer.cs new file mode 100644 index 0000000..e0abe71 --- /dev/null +++ b/src/DotXxlJob.Core/HessianSerializer.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net.NetworkInformation; +using System.Reflection; +using System.Runtime.Serialization; +using DotXxlJob.Core.Model; +using Hessian; + +namespace DotXxlJob.Core +{ + public static class HessianSerializer + { + private static readonly Dictionary requestProperties = + new Dictionary(); + + private static readonly Dictionary triggerProperties = + new Dictionary(); + + static HessianSerializer() + { + var typeInfo = typeof(RpcRequest).GetTypeInfo(); + foreach (var property in typeInfo.DeclaredProperties) + { + var attribute = property.GetCustomAttribute(); + + if (null == attribute) + { + continue; + } + + if (!property.CanRead || !property.CanWrite) + { + continue; + } + + requestProperties.Add(attribute.Name,property); + } + + var triggerTypeInfo = typeof(TriggerParam).GetTypeInfo(); + foreach (var property in triggerTypeInfo.DeclaredProperties) + { + var attribute = property.GetCustomAttribute(); + + if (null == attribute) + { + continue; + } + + if (!property.CanRead || !property.CanWrite) + { + continue; + } + + triggerProperties.Add(attribute.Name,property); + } + } + + public static RpcRequest DeserializeRequest(Stream stream) + { + RpcRequest request = new RpcRequest(); + + try + { + var deserializer = new Deserializer(stream); + var classDef = deserializer.ReadValue() as ClassDef; + if (!Constants.RpcRequestJavaFullName.Equals(classDef.Name)) + { + throw new HessianException($"unknown class :{classDef.Name}"); + } + if (requestProperties.Count != classDef.Fields.Length) + { + throw new HessianException($"unknown class :{classDef.Name}, field count not match ${requestProperties.Count} !={classDef.Fields.Length}"); + } + + //obj serialize + if (deserializer.ReadValue() is HessianObject hessianObject) + { + foreach (var item in hessianObject) + { + if (requestProperties.TryGetValue(item.Item1, out var p)) + { + if (IsSimpleType(p.PropertyType.GetTypeInfo())) + { + p.SetValue(request,item.Item2); + } + else + { + if (item.Item2 is HessianObject ) + { + request.Parameters = new List(); + + var defList = deserializer.ReadValue() as List; + + foreach (var li in defList) + { + ReadParameters(deserializer,request.Parameters, li); + } + } + else + { + throw new HessianException($"unknown item :{item.Item1}"); + } + } + } + + } + } + + + } + catch (EndOfStreamException) + { + //没有数据可读了 + } + + return request; + + } + + private static void ReadParameters(Deserializer deserializer,IList list, object item) + { + var itemType = item.GetType(); + if (IsSimpleType(itemType.GetTypeInfo())) + { + list.Add(item); + return; + } + + if (itemType == typeof(ClassDef)) + { + var triggerClass = item as ClassDef; + //TODO:这里要做成动态的话 ,可以注册所有的实体到对应的字典中,不过这里只有这个类型哦 + if (triggerClass.Name != "com.xxl.job.core.biz.model.TriggerParam") + { + throw new HessianException($"not expected parameter type [{triggerClass.Name}]"); + } + + if (!(deserializer.ReadValue() is HessianObject triggerData)) + { + throw new HessianException("not expected parameter type ,data is null"); + } + TriggerParam param = new TriggerParam(); + foreach (var field in triggerData) + { + if (triggerProperties.TryGetValue(field.Item1, out var tgPropertyInfo)) + { + tgPropertyInfo.SetValue(param,field.Item2); + } + } + } + else + { + throw new HessianException($"unsupported list item type =[{itemType}]"); + } + + } + public static void SerializeRequest(Stream stream,RpcRequest req) + { + var serializer = new Serializer(stream); + serializer.WriteObject(req); + } + + public static void SerializeResponse(Stream stream,RpcResponse res) + { + var serializer = new Serializer(stream); + serializer.WriteObject(res); + } + + private static bool IsSimpleType(TypeInfo typeInfo) + { + if (typeInfo.IsValueType || typeInfo.IsEnum || typeInfo.IsPrimitive) + { + return true; + } + + if (typeof (String) == typeInfo.AsType()) + { + return true; + } + + return false; + } + + public static RpcResponse DeserializeResponse(Stream resStream) + { + + } + } + + +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/JobDispatcher.cs b/src/DotXxlJob.Core/JobDispatcher.cs index de95df8..3cad564 100644 --- a/src/DotXxlJob.Core/JobDispatcher.cs +++ b/src/DotXxlJob.Core/JobDispatcher.cs @@ -110,7 +110,7 @@ namespace DotXxlJob.Core JobQueue jobQueue = new JobQueue ( executor, this._callbackTaskQueue,this._jobQueueLogger); if (RUNNING_QUEUE.TryAdd(triggerParam.JobId, jobQueue)) { - jobQueue.Push(triggerParam); + return jobQueue.Push(triggerParam); } return ReturnT.Failed("add running queue executor error"); } diff --git a/src/DotXxlJob.Core/Model/AddressEntity.cs b/src/DotXxlJob.Core/Model/AddressEntity.cs new file mode 100644 index 0000000..aa2399d --- /dev/null +++ b/src/DotXxlJob.Core/Model/AddressEntity.cs @@ -0,0 +1,39 @@ +using System; + +namespace DotXxlJob.Core.Model +{ + public class AddressEntry + { + public Uri RequestUri { get; set; } + + private DateTime? LastFailedTime { get; set; } + + private int FailedTimes { get; set; } + + public bool CheckAccessable() + { + if (LastFailedTime == null) + return true; + + if (DateTime.UtcNow.Subtract(LastFailedTime.Value) > Constants.AdminServerReconnectInterval) + return true; + + if (FailedTimes < Constants.AdminServerCircuitFaildTimes) + return true; + + return false; + } + + public void Reset() + { + LastFailedTime = null; + FailedTimes = 0; + } + + public void SetFail() + { + LastFailedTime = DateTime.UtcNow; + FailedTimes++; + } + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Model/HandleCallbackParam.cs b/src/DotXxlJob.Core/Model/HandleCallbackParam.cs new file mode 100644 index 0000000..865ef60 --- /dev/null +++ b/src/DotXxlJob.Core/Model/HandleCallbackParam.cs @@ -0,0 +1,17 @@ +using System.Runtime.Serialization; + +namespace DotXxlJob.Core.Model +{ + [DataContract(Name = "com.xxl.job.core.biz.model.HandleCallbackParam")] + public class HandleCallbackParam + { + [DataMember(Name = "callbackRetryTimes",Order = 1)] + public int CallbackRetryTimes; + [DataMember(Name = "logId",Order = 2)] + public int LogId; + [DataMember(Name = "logDateTim",Order = 3)] + public long LogDateTim; + [DataMember(Name = "executeResult",Order = 4)] + public ReturnT ExecuteResult; + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Model/HessianArrayList.cs b/src/DotXxlJob.Core/Model/HessianArrayList.cs deleted file mode 100644 index a3c3ba7..0000000 --- a/src/DotXxlJob.Core/Model/HessianArrayList.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace DotXxlJob.Core.Model -{ - [DataContract(Name = "hessianArrayList")] - public class HessianArrayList:List - { - - } -} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Model/JavaClass.cs b/src/DotXxlJob.Core/Model/JavaClass.cs new file mode 100644 index 0000000..6a15256 --- /dev/null +++ b/src/DotXxlJob.Core/Model/JavaClass.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace DotXxlJob.Core.Model +{ + [DataContract(Name = "java.lang.Class")] + public class JavaClass + { + [DataMember(Name = "name",Order = 1)] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Model/LogResult.cs b/src/DotXxlJob.Core/Model/LogResult.cs new file mode 100644 index 0000000..a4c855c --- /dev/null +++ b/src/DotXxlJob.Core/Model/LogResult.cs @@ -0,0 +1,17 @@ +using System.Runtime.Serialization; + +namespace DotXxlJob.Core.Model +{ + [DataContract(Name = "com.xxl.job.core.biz.model.LogResult")] + public class LogResult + { + [DataMember(Name = "fromLineNum",Order = 1)] + public int FromLineNum { get; set; } + [DataMember(Name = "toLineNum",Order = 2)] + public int ToLineNum { get; set; } + [DataMember(Name = "logContent",Order = 3)] + public string LogContent { get; set; } + [DataMember(Name = "isEnd",Order = 4)] + public bool IsEnd { get; set; } + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Model/RegistryParam.cs b/src/DotXxlJob.Core/Model/RegistryParam.cs new file mode 100644 index 0000000..0bc4fad --- /dev/null +++ b/src/DotXxlJob.Core/Model/RegistryParam.cs @@ -0,0 +1,16 @@ +using System.Runtime.Serialization; + +namespace DotXxlJob.Core.Model +{ + [DataContract(Name = "")] + 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; } + + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Model/ReturnT.cs b/src/DotXxlJob.Core/Model/ReturnT.cs index d28a69a..da07b93 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] + [DataContract(Name = "com.xxl.job.core.biz.model.ReturnT")] public class ReturnT { public const int SUCCESS_CODE = 200; @@ -12,11 +12,6 @@ namespace DotXxlJob.Core public static readonly ReturnT FAIL = new ReturnT(FAIL_CODE, null); public static readonly ReturnT FAIL_TIMEOUT = new ReturnT(502, null); - [DataMember(Name = "code",Order = 1)] - public int Code { get; set; } - [DataMember(Name = "msg",Order = 2)] - public string Msg { get; set; } - public ReturnT() { } public ReturnT(int code, string msg) @@ -24,6 +19,17 @@ namespace DotXxlJob.Core this.Code = code; this.Msg = msg; } + + + [DataMember(Name = "code",Order = 1)] + public int Code { get; set; } + [DataMember(Name = "msg",Order = 2)] + public string Msg { get; set; } + + [DataMember(Name = "content",Order = 3)] + public object Content { get; set; } + + public static ReturnT Failed(string msg) { @@ -33,8 +39,7 @@ namespace DotXxlJob.Core { return new ReturnT(SUCCESS_CODE, msg); } - [DataMember(Name = "content",Order = 3)] - public object Content { get; set; } + } diff --git a/src/DotXxlJob.Core/Model/RpcRequest.cs b/src/DotXxlJob.Core/Model/RpcRequest.cs index f862092..18637f7 100644 --- a/src/DotXxlJob.Core/Model/RpcRequest.cs +++ b/src/DotXxlJob.Core/Model/RpcRequest.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Specialized; +using System.Collections.Generic; using System.Runtime.Serialization; namespace DotXxlJob.Core.Model @@ -40,11 +39,11 @@ namespace DotXxlJob.Core.Model public string Version{ get; set; } [DataMember(Name = "parameterTypes",Order = 7)] - public HessianArrayList ParameterTypes{ get; set; } + public IList ParameterTypes{ get; set; } [DataMember(Name = "parameters",Order = 8)] - public HessianArrayList Parameters{ get; set; } + public IList Parameters{ get; set; } } diff --git a/src/DotXxlJob.Core/Model/TriggerParam.cs b/src/DotXxlJob.Core/Model/TriggerParam.cs index 0e6425d..b918e81 100644 --- a/src/DotXxlJob.Core/Model/TriggerParam.cs +++ b/src/DotXxlJob.Core/Model/TriggerParam.cs @@ -3,43 +3,43 @@ using System.Runtime.Serialization; namespace DotXxlJob.Core.Model { - [DataContract(Name = "triggerParam")] + [DataContract(Name = "com.xxl.job.core.biz.model.TriggerParam")] public class TriggerParam { static readonly long SerialVersionUID = 42L; - [DataMember(Name = "jobId")] + [DataMember(Name = "jobId",Order = 1)] public int JobId { get; set; } - [DataMember(Name = "executorHandler")] + [DataMember(Name = "executorHandler",Order = 2)] public string ExecutorHandler { get; set; } - [DataMember(Name = "executorParams")] + [DataMember(Name = "executorParams",Order = 3)] public string ExecutorParams{ get; set; } - [DataMember(Name = "executorBlockStrategy")] + [DataMember(Name = "executorBlockStrategy",Order = 4)] public string ExecutorBlockStrategy{ get; set; } - [DataMember(Name = "executorTimeout")] + [DataMember(Name = "executorTimeout",Order = 5)] public int ExecutorTimeout{ get; set; } - [DataMember(Name = "logId")] + [DataMember(Name = "logId",Order = 5)] public int LogId{ get; set; } - [DataMember(Name = "logDateTim")] + [DataMember(Name = "logDateTim",Order = 6)] public long LogDateTime{ get; set; } - [DataMember(Name = "glueType")] + [DataMember(Name = "glueType",Order = 7)] public string GlueType{ get; set; } - [DataMember(Name = "glueSource")] + [DataMember(Name = "glueSource",Order = 8)] public string GlueSource{ get; set; } - [DataMember(Name = "glueUpdatetime")] + [DataMember(Name = "glueUpdatetime",Order = 9)] public long GlueUpdateTime{ get; set; } - [DataMember(Name = "broadcastIndex")] + [DataMember(Name = "broadcastIndex",Order = 10)] public int BroadcastIndex{ get; set; } - [DataMember(Name = "broadcastTotal")] + [DataMember(Name = "broadcastTotal",Order = 11)] public int BroadcastTotal{ get; set; } } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/ServiceCollectionExtensions.cs b/src/DotXxlJob.Core/ServiceCollectionExtensions.cs index dcb8c4d..5f35362 100644 --- a/src/DotXxlJob.Core/ServiceCollectionExtensions.cs +++ b/src/DotXxlJob.Core/ServiceCollectionExtensions.cs @@ -12,6 +12,7 @@ namespace DotXxlJob.Core services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); return services; diff --git a/src/DotXxlJob.Core/TaskQueue.cs b/src/DotXxlJob.Core/TaskQueue.cs index 87fc4d3..9275bfa 100644 --- a/src/DotXxlJob.Core/TaskQueue.cs +++ b/src/DotXxlJob.Core/TaskQueue.cs @@ -53,7 +53,6 @@ namespace DotXxlJob.Core this._cancellationTokenSource?.Cancel(); this._cancellationTokenSource?.Dispose(); this._cancellationTokenSource = null; - } diff --git a/src/DotXxlJob.Core/XxlRpcServiceHandler.cs b/src/DotXxlJob.Core/XxlRpcServiceHandler.cs index 53abdc5..fb0a6f2 100644 --- a/src/DotXxlJob.Core/XxlRpcServiceHandler.cs +++ b/src/DotXxlJob.Core/XxlRpcServiceHandler.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Concurrent; using System.IO; +using System.Linq; using System.Reflection; using System.Threading.Tasks; using DotXxlJob.Core.Config; using DotXxlJob.Core.Model; -using Hessian.Net; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -20,8 +20,6 @@ namespace DotXxlJob.Core private readonly JobDispatcher _jobDispatcher; private readonly ILogger _logger; - private readonly DataContractHessianSerializer _reqSerializer; - private readonly DataContractHessianSerializer _resSerializer; private readonly XxlJobExecutorOptions _options; private readonly ConcurrentDictionary METHOD_CACHE = @@ -34,8 +32,7 @@ namespace DotXxlJob.Core _jobDispatcher = jobDispatcher; this._logger = logger; - this._reqSerializer = new DataContractHessianSerializer(typeof (RpcRequest)); - this._resSerializer = new DataContractHessianSerializer(typeof (RpcResponse)); + this._options = optionsAccessor.Value; if (this._options == null) { @@ -52,13 +49,10 @@ namespace DotXxlJob.Core /// public async Task HandlerAsync(Stream reqStream) { - using (Stream output = File.OpenWrite(DateTime.Now.ToUnixTimeSeconds()+".dat")) - { - reqStream.CopyTo(output); - } - - var req = _reqSerializer.ReadObject(reqStream) as RpcRequest; - var res = new RpcResponse(); + var req = HessianSerializer.DeserializeRequest(reqStream); + + + var res = new RpcResponse { RequestId = req.RequestId}; if (!ValidRequest(req, out var error)) { res.ErrorMsg = error; @@ -70,7 +64,7 @@ namespace DotXxlJob.Core using (var outputStream = new MemoryStream()) { - _resSerializer.WriteObject(outputStream,res); + HessianSerializer.SerializeResponse(outputStream,res); return outputStream.GetBuffer(); } @@ -192,6 +186,7 @@ namespace DotXxlJob.Core private ReturnT Log(long logDateTime, int logId, int fromLineNum) { //var logResult = JobLogger.ReadLog(logDateTime, logId, fromLineNum); + Console.WriteLine("{0} ---{1} --{2}",logDateTime,logId,fromLineNum); return ReturnT.Success(null); } diff --git a/src/Hessian.NET/HessianInputReader.cs b/src/Hessian.NET/HessianInputReader.cs index 55ff1ac..f42bbb5 100644 --- a/src/Hessian.NET/HessianInputReader.cs +++ b/src/Hessian.NET/HessianInputReader.cs @@ -13,7 +13,7 @@ namespace Hessian.Net public bool IsInstanceReference => ObjectPreamble.InstanceReference == preamble; - protected LeadingByte LeadingByte + public LeadingByte LeadingByte { get; } @@ -355,11 +355,16 @@ namespace Hessian.Net return ReadInt32(); } + public byte Peek() + { + byte value= (byte)this.Stream.ReadByte(); + this.Stream.Position--; + return value; + } public byte ReadByte() { - return (byte)Stream.ReadByte(); + return (byte)this.Stream.ReadByte(); } - protected void ReadLeadingByte() { var data = Stream.ReadByte(); diff --git a/src/Hessian.NET/HessianSerializationScheme.cs b/src/Hessian.NET/HessianSerializationScheme.cs index f8caf1b..d143e08 100644 --- a/src/Hessian.NET/HessianSerializationScheme.cs +++ b/src/Hessian.NET/HessianSerializationScheme.cs @@ -62,9 +62,7 @@ namespace Hessian.Net private static ISerializationElement BuildSerializationObject(Type type, IDictionary catalog, IObjectSerializerFactory factory) { - ISerializationElement existing; - - if (catalog.TryGetValue(type, out existing)) + if (catalog.TryGetValue(type, out var existing)) { return existing; } @@ -121,7 +119,7 @@ namespace Hessian.Net return false; } - public static bool IsListType(TypeInfo typeInfo) + private static bool IsListType(TypeInfo typeInfo) { return typeof(IEnumerable).IsAssignableFrom(typeInfo); } diff --git a/src/Hessian.NET/ListElement.cs b/src/Hessian.NET/ListElement.cs index 474d75f..7bfc89f 100644 --- a/src/Hessian.NET/ListElement.cs +++ b/src/Hessian.NET/ListElement.cs @@ -59,9 +59,7 @@ namespace Hessian.Net private string ReadTypeName(HessianInputReader reader) { - var tag = reader.ReadByte(); - - + var tag = reader.Peek(); // A type name is either a string, or an integer reference to a // string already read and stored in the type-name ref map. @@ -74,22 +72,21 @@ namespace Hessian.Net return typeName; } - throw new HessianSerializerException(); - + reader.ReadInt32(); + return ""; + } #region List private IList ReadVarList(HessianInputReader reader, HessianSerializationContext context) { - reader.ReadByte(); var type = ReadTypeName(reader); return ReadListCore(reader, context, type: type); } private IList ReadFixList(HessianInputReader reader, HessianSerializationContext context) { - reader.ReadByte(); var type = ReadTypeName(reader); var length = reader.ReadInt32(); return ReadListCore(reader, context, length, type); @@ -97,20 +94,18 @@ namespace Hessian.Net private IList ReadVarListUntyped(HessianInputReader reader, HessianSerializationContext context) { - reader.ReadByte(); return ReadListCore(reader, context); } private IList ReadFixListUntyped(HessianInputReader reader, HessianSerializationContext context) { - reader.ReadByte(); var length = reader.ReadInt32(); return ReadListCore(reader, context, length); } private IList ReadCompactFixList(HessianInputReader reader, HessianSerializationContext context) { - var tag = reader.ReadByte(); + var tag = reader.LeadingByte.Data; var length = tag - 0x70; var type = ReadTypeName(reader); return ReadListCore(reader, context, length, type); @@ -118,14 +113,14 @@ namespace Hessian.Net private IList ReadCompactFixListUntyped(HessianInputReader reader, HessianSerializationContext context) { - var tag = reader.ReadByte(); + var tag = reader.LeadingByte.Data; var length = tag - 0x70; return ReadListCore(reader, context, length); } private IList ReadListCore(HessianInputReader reader, HessianSerializationContext context, int? length = null, string type = null) { - var list = GetListIntance(type, length); + var list = GetListInstance(type, length); //objectRefs.Add(list); @@ -137,7 +132,7 @@ namespace Hessian.Net return list; } - private IList GetListIntance(string type, int? length = null) + private IList GetListInstance(string type, int? length = null) { IList list; @@ -156,9 +151,14 @@ namespace Hessian.Net private void PopulateFixLengthList(HessianInputReader reader, HessianSerializationContext context, IList list, int length) { - for (var i = 0; i < length; ++i) { - //ObjectElement objectElement = new ObjectElement() - list.Add(this.ChildSerializationElement.Deserialize(reader,context)); + var tag = reader.ReadByte(); //0x16 + + for (var i = 0; i < length; ++i) + { + ObjectElement objectElement = new ObjectElement(); + var scheme = HessianSerializationScheme.CreateFromType(this.GetType(), this._factory); + var obj = scheme.Deserialize(reader, context); + list.Add(obj); } } diff --git a/src/Hessian.NET/ObjectElement.cs b/src/Hessian.NET/ObjectElement.cs index bc7de2f..3c51fd9 100644 --- a/src/Hessian.NET/ObjectElement.cs +++ b/src/Hessian.NET/ObjectElement.cs @@ -101,12 +101,12 @@ namespace Hessian.Net - if (!String.Equals(ClassName, className)) + if (!String.Equals(this.ClassName, className)) { throw new HessianSerializerException(); } - if (ObjectProperties.Count != propertiesCount) + if (this.ObjectProperties.Count != propertiesCount) { throw new HessianSerializerException(); } @@ -114,8 +114,8 @@ namespace Hessian.Net for (var index = 0; index < propertiesCount; index++) { var propertyName = reader.ReadString(); - Console.WriteLine(propertyName); - var exists = ObjectProperties.Any(property => String.Equals(property.PropertyName, propertyName)); + + var exists = this.ObjectProperties.Any(property => String.Equals(property.PropertyName, propertyName)); if (!exists) { @@ -123,7 +123,7 @@ namespace Hessian.Net } } - context.Classes.Add(ObjectType); + context.Classes.Add(this.ObjectType); reader.EndClassDefinition(); } @@ -134,11 +134,10 @@ namespace Hessian.Net } var number = reader.ReadObjectReference(); - var instance = Activator.CreateInstance(ObjectType); + var instance = Activator.CreateInstance(this.ObjectType); context.Instances.Add(instance); - Console.WriteLine("==========================================="); - foreach (var item in ObjectProperties) + foreach (var item in this.ObjectProperties) { Console.WriteLine(item.PropertyName); var value = item.Deserialize(reader, context); diff --git a/src/Hessian/ClassElement.cs b/src/Hessian/ClassElement.cs new file mode 100644 index 0000000..9bac0b4 --- /dev/null +++ b/src/Hessian/ClassElement.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Hessian +{ + public class ClassElement + { + public string ClassName { get; set; } + + public List Fields { get; set; } + } +} \ No newline at end of file diff --git a/src/Hessian/DateTimeExtension.cs b/src/Hessian/DateTimeExtension.cs new file mode 100644 index 0000000..03e952b --- /dev/null +++ b/src/Hessian/DateTimeExtension.cs @@ -0,0 +1,56 @@ +using System; + +namespace Hessian +{ + /// + /// + /// + internal static class DateTimeExtension + { + private const long Era = 62135596800000L; + private const long Millis = 60000; + + /// + /// + /// + /// + /// + public static long GetTotalMilliseconds(this DateTime dt) + { + return dt.ToUniversalTime().Ticks / 10000 - Era; + } + + /// + /// + /// + /// + /// + public static int GetTotalMinutes(this DateTime dt) + { + var val = GetTotalMilliseconds(dt); + return (int)(val / Millis); + } + + /// + /// + /// + /// + /// + public static DateTime FromMinutes(int value) + { + var ticks = (value * Millis + Era) * 10000; + return new DateTime(ticks, DateTimeKind.Utc); + } + + /// + /// + /// + /// + /// + public static DateTime FromMilliseconds(long value) + { + var ticks = (value + Era) * 10000; + return new DateTime(ticks, DateTimeKind.Utc); + } + } +} diff --git a/src/Hessian/Deserializer.cs b/src/Hessian/Deserializer.cs index 5de1161..fb73d7c 100644 --- a/src/Hessian/Deserializer.cs +++ b/src/Hessian/Deserializer.cs @@ -22,7 +22,7 @@ namespace Hessian public Deserializer (Stream stream) { if (stream == null) { - throw new ArgumentNullException("stream"); + throw new ArgumentNullException(nameof(stream)); } reader = new ValueReader(stream); @@ -40,97 +40,127 @@ namespace Hessian if (!tag.HasValue) { throw new EndOfStreamException(); } - + + switch (tag.Value) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: + return ReadShortString(); case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: + return ReadShortBinary(); case 0x30: case 0x31: case 0x32: case 0x33: + return ReadMediumString(); case 0x34: case 0x35: case 0x36: case 0x37: + return ReadMediumBinary(); case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: + return ReadLongThreeBytes(); case 0x40: + return Reserved(); case 0x41: case 0x42: + return ReadChunkedBinary(); case 0x43: + return ReadClassDefinition(); case 0x44: + return ReadFullDouble(); case 0x45: + return Reserved(); case 0x46: + return ReadBoolean(); case 0x47: + return Reserved(); case 0x48: + return ReadUntypedMap(); case 0x49: + return ReadInteger(); case 0x4A: + return ReadDateInMillis(); case 0x4B: + return ReadDateInMinutes(); case 0x4C: + return ReadLongFull(); case 0x4D: + return ReadTypedMap(); case 0x4E: + return ReadNull(); case 0x4F: + return ReadObject(); case 0x50: + return Reserved(); case 0x51: + return ReadRef(); case 0x52: case 0x53: + return ReadChunkedString(); case 0x54: + return ReadBoolean(); case 0x55: + return ReadVarList(); case 0x56: + return ReadFixList(); case 0x57: + return ReadVarListUntyped(); case 0x58: + return ReadFixListUntyped(); case 0x59: + return ReadLongFourBytes(); case 0x5A: @@ -138,19 +168,24 @@ namespace Hessian throw new UnexpectedTagException(0x5A, "value"); case 0x5B: case 0x5C: + return ReadDoubleOneByte(); case 0x5D: + return ReadDoubleOneByte(); case 0x5E: + return ReadDoubleTwoBytes(); case 0x5F: + return ReadDoubleFourBytes(); case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: + return ReadObjectCompact(); case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: @@ -171,9 +206,11 @@ namespace Hessian case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: + return ReadIntegerTwoBytes(); case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7: + return ReadIntegerThreeBytes(); case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF: @@ -261,7 +298,7 @@ namespace Hessian private IList ReadListCore(int? length = null, string type = null) { - var list = GetListIntance(type, length); + var list = GetListInstance(type, length); objectRefs.Add(list); @@ -273,7 +310,7 @@ namespace Hessian return list; } - private IList GetListIntance(string type, int? length = null) + private IList GetListInstance(string type, int? length = null) { IList list; @@ -700,10 +737,15 @@ namespace Hessian private long ReadLongFull() { var data = new byte[9]; + + reader.Read(data, data.Length); return LongFromBytes(data, 1); + } - + + + private long ReadLongOneByte() { return reader.ReadByte() - 0xE0; @@ -865,6 +907,9 @@ namespace Hessian if (tag != 0x51) { throw new UnexpectedTagException(tag.Value, "ref"); } + + reader.ReadByte();//过滤tag + return objectRefs.Get(ReadInteger()); } @@ -878,14 +923,29 @@ namespace Hessian private static long LongFromBytes(byte[] buffer, int offset) { - return (buffer[offset + 0] << 0x38) - | (buffer[offset + 1] << 0x30) - | (buffer[offset + 2] << 0x28) - | (buffer[offset + 3] << 0x20) - | (buffer[offset + 4] << 0x18) - | (buffer[offset + 5] << 0x10) - | (buffer[offset + 6] << 0x08) - | (buffer[offset + 7] << 0x00); + /* + var value = (long) reader.ReadByte() << 56; + + value |= ((long) reader.ReadByte() << 48); + value |= ((long) reader.ReadByte() << 40); + value |= ((long) reader.ReadByte() << 32); + value |= ((long) reader.ReadByte() << 24); + value |= ((long) reader.ReadByte() << 16); + value |= ((long) reader.ReadByte() << 8); + value |= (uint)reader.ReadByte(); + + return value; + */ + return ((long)buffer[offset + 0] << 0x38) + + ((long)buffer[offset + 1] << 0x30) + | ((long)buffer[offset + 2] << 0x28) + | ((long)buffer[offset + 3] << 0x20) + | ((long)buffer[offset + 4] << 0x18) + | ((long)buffer[offset + 5] << 0x10) + | ((long)buffer[offset + 6] << 0x08) + | ((uint)buffer[offset + 7] << 0x00); + + } } } diff --git a/src/Hessian/Hessian.csproj b/src/Hessian/Hessian.csproj index ec0b0b9..d2a210c 100644 --- a/src/Hessian/Hessian.csproj +++ b/src/Hessian/Hessian.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netstandard2.0 diff --git a/src/Hessian/HessianObject.cs b/src/Hessian/HessianObject.cs index 1e87a61..f23a2ef 100644 --- a/src/Hessian/HessianObject.cs +++ b/src/Hessian/HessianObject.cs @@ -10,20 +10,11 @@ namespace Hessian private readonly string typeName; private readonly IDictionary fields; - public string TypeName - { - get { return typeName; } - } + public string TypeName => typeName; - public object this[string key] - { - get { return fields[key]; } - } + public object this[string key] => fields[key]; - public int Count - { - get { return fields.Count; } - } + public int Count => fields.Count; private HessianObject(string typeName) { diff --git a/src/Hessian/HessianSerializationContext.cs b/src/Hessian/HessianSerializationContext.cs new file mode 100644 index 0000000..d7322d6 --- /dev/null +++ b/src/Hessian/HessianSerializationContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Hessian +{ + public class HessianSerializationContext + { + public IList Classes + { + get; + private set; + } + + public IList Instances + { + get; + private set; + } + + public HessianSerializationContext() + { + Classes = new List(); + Instances = new List(); + } + } +} \ No newline at end of file diff --git a/src/Hessian/Main.cs b/src/Hessian/Main.cs deleted file mode 100644 index 59bfcfc..0000000 --- a/src/Hessian/Main.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace Hessian -{ - class MainClass - { - public static void Main (string[] args) - { - Console.WriteLine ("Hello World!"); - var bytes = Encoding.UTF8.GetBytes("Hessian"); - var ms = new MemoryStream(); - ms.WriteByte((byte)"Hessian".Length); - ms.Write (bytes, 0, bytes.Length); - ms.Position = 0; - - var ds = new Deserializer(ms); - - var actual = ds.ReadValue(); - Console.WriteLine(actual); - } - } -} diff --git a/src/Hessian/Marker.cs b/src/Hessian/Marker.cs new file mode 100644 index 0000000..ef153be --- /dev/null +++ b/src/Hessian/Marker.cs @@ -0,0 +1,39 @@ +namespace Hessian +{ + internal static class Marker + { + public const byte True = (byte) 'T';//0x54; + public const byte False = (byte) 'F';// 0x46; + public const byte Null = (byte) 'N';//0x4E; + public const byte BinaryNonfinalChunk = (byte) 'b';//0x41; + public const byte BinaryFinalChunk = (byte) 'B';//0x42; + public const byte ClassDefinition = (byte) 'C';//0x43; + public const byte DateTimeLong = 0x4A; + public const byte DateTimeCompact = 0x4B; + public const byte Double = 0x5A; + public const byte DoubleZero = 0x5B; + public const byte DoubleOne = 0x5C; + public const byte DoubleOctet = 0x5D; + public const byte DoubleShort = 0x5E; + public const byte DoubleFloat = 0x5F; + public const byte UnpackedInteger = (byte) 'I';// 0x49; + public const byte PackedLong = (byte) 'Y';// 0x59; + public const byte UnpackedLong = (byte) 'L';// 0x4C; + public const byte StringNonfinalChunk = 0x52; + public const byte StringFinalChunk = 0x53; + public const byte VarList = 0x55; + public const byte FixedList = 0x56; + public const byte VarListUntyped = 0x57; + public const byte FixListUntyped = 0x58; + + public const byte CompactFixListStart = 0x70; + public const byte CompactFixListEnd = 0x77; + public const byte CompactFixListUntypedStart = 0x78; + public const byte CompactFixListUntypedEnd = 0x7F; + + public const byte ClassReference = (byte) 'O';//0x4F + public const byte InstanceReference = (byte) 'Q'; //0x51; + + + } +} \ No newline at end of file diff --git a/src/Hessian/PropertyElement.cs b/src/Hessian/PropertyElement.cs new file mode 100644 index 0000000..3cb8686 --- /dev/null +++ b/src/Hessian/PropertyElement.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Hessian +{ + public class PropertyElement + { + public string Name { get; set; } + public int Order { get; set; } + public PropertyInfo PropertyInfo { get; set; } + } + + internal class PropertyComparer : IComparer + { + private readonly IComparer comparer; + + public PropertyComparer() + { + comparer = Comparer.Default; + } + + public int Compare(PropertyElement x, PropertyElement y) + { + var eq = comparer.Compare(x.Order, y.Order); + return 0 == eq + ? String.Compare(x.Name, y.Name, StringComparison.Ordinal) + : eq; + } + } +} \ No newline at end of file diff --git a/src/Hessian/Serializer.cs b/src/Hessian/Serializer.cs new file mode 100644 index 0000000..5b65305 --- /dev/null +++ b/src/Hessian/Serializer.cs @@ -0,0 +1,517 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; + +namespace Hessian +{ + public class Serializer + { + static readonly ConcurrentDictionary ClassDefCache =new ConcurrentDictionary(); + + private readonly Stream _stream; + private readonly HessianSerializationContext _context; + + public Serializer (Stream stream) + { + this._stream = stream ?? throw new ArgumentNullException(nameof(stream)); + _context = new HessianSerializationContext(); + } + + public void WriteObject(object graph) + { + var objectType = graph.GetType(); + if (!ClassDefCache.TryGetValue(objectType, out var classDef)) + { + classDef = GetClassDef(objectType.GetTypeInfo()); + } + + var index = this._context.Instances.IndexOf(graph); + + if (index > -1) + { + WriteInstanceReference(index); + return; + } + + this._context.Instances.Add(graph); + + index = this._context.Classes.IndexOf(objectType); + + if (index < 0) + { + BeginClassDefinition(); + WriteString(classDef.ClassName); + WriteInt32(classDef.Fields.Count); + + foreach (var property in classDef.Fields) + { + WriteString(property.Name); + } + EndClassDefinition(); + + index = this._context.Classes.Count; + + this._context.Classes.Add(objectType); + } + WriteObjectReference(index); + foreach (var item in classDef.Fields) + { + var value = item.PropertyInfo.GetValue(graph); + WriteValue(value); + } + } + + public void WriteValue(object val) + { + if (val == null) + { + WriteNull(); + return; + } + var valType = val.GetType(); + var typeInfo = valType.GetTypeInfo(); + if (IsSimpleType(typeInfo)) + { + WriteSimpleValue(val); + return; + } + + if (IsListType(typeInfo)) + { + WriteListValue(val); + return; + } + + WriteObject(val); + } + + private void WriteListValue(object val) + { + var tag = (int)Marker.CompactFixListStart; + if (!(val is ICollection eVal)) + { + throw new HessianException("write list data error"); + } + tag += eVal.Count; + if (tag > Marker.CompactFixListEnd) + { + throw new HessianException("write list data error,tag too large"); + } + this._stream.WriteByte((byte)tag); + int index = 1; + foreach (var item in eVal) + { + if (index == 1) + { + WriteString("["+GetItemTypeName(item.GetType())); + } + WriteValue(item); + } + } + + private string GetItemTypeName(Type type) + { + var classType = type.GetCustomAttribute(); + if (classType != null) + { + return classType.Name; + } + + return type.Name; + } + private void WriteSimpleValue(object val) + { + var valType = val.GetType(); + if (valType == typeof(int)) + { + WriteInt32((int)val); + } + else if (valType == typeof(bool)) + { + WriteBoolean((bool)val); + } + else if (valType == typeof(long)) + { + WriteInt64((long)val); + } + else if (valType == typeof(double)) + { + WriteDouble((double)val); + } + else if (valType == typeof(string)) + { + WriteString((string)val); + } + else if (valType == typeof(DateTime)) + { + WriteDateTime((DateTime)val); + } + } + + private ClassElement GetClassDef(TypeInfo typeInfo) + { + + var classAttr = typeInfo.GetCustomAttribute(); + if (classAttr == null) + { + throw new HessianException("DataContract must be set"); + } + + ClassElement ce = new ClassElement {ClassName = classAttr.Name, Fields = new List()}; + + //ClassDef def = new ClassDef(classAttr.Name); + foreach (var property in typeInfo.DeclaredProperties) + { + var attribute = property.GetCustomAttribute(); + + if (null == attribute) + { + continue; + } + + if (!property.CanRead || !property.CanWrite) + { + continue; + } + PropertyElement p = new PropertyElement { + Name = attribute.Name, + Order = attribute.Order, + PropertyInfo = property + }; + ce.Fields .Add( p ); + } + ce.Fields.Sort(new PropertyComparer()); + return ce; + } + + /// + /// Writes NULL token into stream + /// + public void WriteNull() + { + this._stream.WriteByte(Marker.Null); + } + + /// + /// Writes value into output stream. + /// + /// The value. + public void WriteBoolean(bool value) + { + this._stream.WriteByte(value ? Marker.True : Marker.False); + } + + /// + /// Writes array of into output stream. + /// + /// The value. + public void WriteBytes(byte[] buffer) + { + if (null == buffer) + { + WriteNull(); + return; + } + + WriteBytes(buffer, 0, buffer.Length); + } + + public void WriteBytes(byte[] buffer, int offset, int count) + { + if (offset < 0) + { + throw new ArgumentException("", nameof(offset)); + } + + if (null == buffer) + { + WriteNull(); + return; + } + + if (count < 0x10) + { + this._stream.WriteByte((byte)(0x20 + (count & 0x0F))); + this._stream.Write(buffer, offset, count); + return; + } + + const int chunkSize = 0x8000; + + while (count > chunkSize) + { + this._stream.WriteByte(Marker.BinaryNonfinalChunk); + this._stream.WriteByte(chunkSize >> 8); + this._stream.WriteByte(chunkSize & 0xFF); + this._stream.Write(buffer, offset, chunkSize); + + count -= chunkSize; + offset += chunkSize; + } + + this._stream.WriteByte(Marker.BinaryFinalChunk); + this._stream.WriteByte((byte)(count >> 8)); + this._stream.WriteByte((byte)(count & 0xFF)); + this._stream.Write(buffer, offset, count); + } + + public void WriteDateTime(DateTime value) + { + if (value.Second == 0) + { + var s = value.GetTotalMinutes(); + + this._stream.WriteByte(Marker.DateTimeCompact); + this._stream.WriteByte((byte)(s >> 24)); + this._stream.WriteByte((byte)(s >> 16)); + this._stream.WriteByte((byte)(s >> 8)); + this._stream.WriteByte((byte)s); + + return; + } + + var dt = value.GetTotalMilliseconds(); + + this._stream.WriteByte(Marker.DateTimeLong); + this._stream.WriteByte((byte)(dt >> 56)); + this._stream.WriteByte((byte)(dt >> 48)); + this._stream.WriteByte((byte)(dt >> 40)); + this._stream.WriteByte((byte)(dt >> 32)); + this._stream.WriteByte((byte)(dt >> 24)); + this._stream.WriteByte((byte)(dt >> 16)); + this._stream.WriteByte((byte)(dt >> 8)); + this._stream.WriteByte((byte)dt); + } + + public void WriteDouble(double value) + { + if (value.Equals(0.0d)) + { + this._stream.WriteByte(Marker.DoubleZero); + return; + } + + if (value.Equals(1.0d)) + { + this._stream.WriteByte(Marker.DoubleOne); + return; + } + + var fraction = Math.Abs(value - Math.Truncate(value)); + + if (Double.Epsilon >= fraction) + { + if (Byte.MinValue <= value && value <= Byte.MaxValue) + { + this._stream.WriteByte(Marker.DoubleOctet); + this._stream.WriteByte(Convert.ToByte(value)); + + return; + } + + if (Int16.MinValue <= value && value <= Int16.MaxValue) + { + var val = Convert.ToInt16(value); + + this._stream.WriteByte(Marker.DoubleShort); + this._stream.WriteByte((byte)(val >> 8)); + this._stream.WriteByte((byte)val); + + return; + } + } + + if (Single.MinValue <= value && value <= Single.MaxValue) + { + var bytes = BitConverter.GetBytes((float) value); + + this._stream.WriteByte(Marker.DoubleFloat); + + for (var index = bytes.Length - 1; index >= 0; index--) + { + this._stream.WriteByte(bytes[index]); + } + + return; + } + + var temp = BitConverter.DoubleToInt64Bits(value); + + this._stream.WriteByte(Marker.Double); + + for (var index = 56; index >= 0; index -= 8) + { + this._stream.WriteByte((byte) (temp >> index)); + } + } + + public void WriteInt32(int value) + { + if (-16 <= value && value < 48) + { + this._stream.WriteByte((byte)(0x90 + value)); + } + else if (-2048 <= value && value < 2048) + { + this._stream.WriteByte((byte)(0xC8 + (byte)(value >> 8))); + this._stream.WriteByte((byte)value); + } + else if (-262144 <= value && value < 262144) + { + this._stream.WriteByte((byte)(0xD4 + (byte)(value >> 16))); + this._stream.WriteByte((byte)(value >> 8)); + this._stream.WriteByte((byte)value); + } + else + { + this._stream.WriteByte(Marker.UnpackedInteger); + this._stream.WriteByte((byte)(value >> 24)); + this._stream.WriteByte((byte)(value >> 16)); + this._stream.WriteByte((byte)(value >> 8)); + this._stream.WriteByte((byte)value); + } + } + + public void WriteInt64(long value) + { + if (-8 <= value && value < 16) + { + this._stream.WriteByte((byte)(0xE0 + value)); + } + else if (-2048 <= value && value < 2048) + { + this._stream.WriteByte((byte)(0xF8 + (byte)(value >> 8))); + this._stream.WriteByte((byte)value); + } + else if (-262144 <= value && value < 262144) + { + this._stream.WriteByte((byte)(0x3C + (byte)(value >> 16))); + this._stream.WriteByte((byte)(value >> 8)); + this._stream.WriteByte((byte)value); + } + else if (Int32.MinValue <= value && value <= Int32.MaxValue) + { + this._stream.WriteByte(Marker.PackedLong); + this._stream.WriteByte((byte)(value >> 24)); + this._stream.WriteByte((byte)(value >> 16)); + this._stream.WriteByte((byte)(value >> 8)); + this._stream.WriteByte((byte)value); + } + else + { + this._stream.WriteByte(Marker.UnpackedLong); + this._stream.WriteByte((byte)(value >> 56)); + this._stream.WriteByte((byte)(value >> 48)); + this._stream.WriteByte((byte)(value >> 40)); + this._stream.WriteByte((byte)(value >> 32)); + this._stream.WriteByte((byte)(value >> 24)); + this._stream.WriteByte((byte)(value >> 16)); + this._stream.WriteByte((byte)(value >> 8)); + this._stream.WriteByte((byte)value); + } + } + + public void WriteString(string value) + { + if (string.IsNullOrEmpty(value)) + { + this._stream.WriteByte(0x00); + return; + } + + var length = value.Length; + + if (1024 > length) + { + var bytes = Encoding.UTF8.GetBytes(value.ToCharArray()); + + if (32 > length) + { + this._stream.WriteByte((byte) length); + } + else + { + this._stream.WriteByte((byte) (0x30 + (byte) (length >> 8))); + this._stream.WriteByte((byte) length); + } + + this._stream.Write(bytes, 0, bytes.Length); + + return; + } + + const int maxChunkLength = 1024; + var position = 0; + + while (position < length) + { + var count = Math.Min(length - position, maxChunkLength); + var final = length == (position + count); + var chunk = value.Substring(position, count); + var bytes = Encoding.UTF8.GetBytes(chunk.ToCharArray()); + + this._stream.WriteByte(final ? Marker.StringFinalChunk : Marker.StringNonfinalChunk); + this._stream.WriteByte((byte)(count >> 8)); + this._stream.WriteByte((byte)count); + this._stream.Write(bytes, 0, bytes.Length); + + position += count; + } + } + + public void BeginClassDefinition() + { + this._stream.WriteByte(Marker.ClassDefinition); + } + + public void EndClassDefinition() + { + } + + public void WriteObjectReference(int index) + { + if (index < 0x10) + { + this._stream.WriteByte((byte)(0x60 + index)); + } + else + { + this._stream.WriteByte(Marker.ClassReference); + WriteInt32(index); + } + } + + public void WriteInstanceReference(int index) + { + this._stream.WriteByte(Marker.InstanceReference); + WriteInt32(index); + } + + + private static bool IsSimpleType(TypeInfo typeInfo) + { + if (typeInfo.IsValueType || typeInfo.IsEnum || typeInfo.IsPrimitive) + { + return true; + } + + if (typeof (String) == typeInfo.AsType()) + { + return true; + } + + return false; + } + + private static bool IsListType(TypeInfo typeInfo) + { + return typeof(ICollection).IsAssignableFrom(typeInfo); + } + } +} \ No newline at end of file