parent
b272b54fbd
commit
822b1fbe16
44 changed files with 1351 additions and 265 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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<AdminClient> _logger; |
||||
|
||||
private List<AddressEntry> _addresses; |
||||
|
||||
private int _currentIndex; |
||||
|
||||
public AdminClient(IOptions<XxlJobExecutorOptions> optionsAccessor |
||||
,IHttpClientFactory clientFactory |
||||
,ILogger<AdminClient> logger) |
||||
{ |
||||
this._options = optionsAccessor.Value; |
||||
_clientFactory = clientFactory; |
||||
this._logger = logger; |
||||
InitAddress(); |
||||
} |
||||
|
||||
private void InitAddress() |
||||
{ |
||||
this._addresses = new List<AddressEntry>(); |
||||
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<ReturnT> Callback(List<HandleCallbackParam> callbackParamList) |
||||
{ |
||||
return InvokeRpcService("callback", new List<string> {"java.util.List"}, callbackParamList); |
||||
} |
||||
|
||||
|
||||
public Task<ReturnT> Registry(RegistryParam registryParam) |
||||
{ |
||||
return InvokeRpcService("callback", new List<string> {"java.lang.Class"}, registryParam); |
||||
} |
||||
|
||||
public Task<ReturnT> RegistryRemove(RegistryParam registryParam) |
||||
{ |
||||
return InvokeRpcService("callback", new List<string> {"java.lang.Class"}, registryParam); |
||||
} |
||||
|
||||
private async Task<ReturnT> InvokeRpcService(string methodName, List<string> 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<object>(), |
||||
Parameters = new List<object> {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<Stream> 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(); |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
||||
@ -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<string, PropertyInfo> requestProperties = |
||||
new Dictionary<string, PropertyInfo>(); |
||||
|
||||
private static readonly Dictionary<string, PropertyInfo> triggerProperties = |
||||
new Dictionary<string, PropertyInfo>(); |
||||
|
||||
static HessianSerializer() |
||||
{ |
||||
var typeInfo = typeof(RpcRequest).GetTypeInfo(); |
||||
foreach (var property in typeInfo.DeclaredProperties) |
||||
{ |
||||
var attribute = property.GetCustomAttribute<DataMemberAttribute>(); |
||||
|
||||
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<DataMemberAttribute>(); |
||||
|
||||
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<object>(); |
||||
|
||||
var defList = deserializer.ReadValue() as List<object>; |
||||
|
||||
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<object> 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) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -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++; |
||||
} |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
@ -1,11 +0,0 @@ |
||||
using System.Collections.Generic; |
||||
using System.Runtime.Serialization; |
||||
|
||||
namespace DotXxlJob.Core.Model |
||||
{ |
||||
[DataContract(Name = "hessianArrayList")] |
||||
public class HessianArrayList:List<object> |
||||
{ |
||||
|
||||
} |
||||
} |
||||
@ -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; } |
||||
} |
||||
} |
||||
@ -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; } |
||||
} |
||||
} |
||||
@ -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; } |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Hessian |
||||
{ |
||||
public class ClassElement |
||||
{ |
||||
public string ClassName { get; set; } |
||||
|
||||
public List<PropertyElement> Fields { get; set; } |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -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; |
||||
|
||||
|
||||
} |
||||
} |
||||
@ -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<PropertyElement> |
||||
{ |
||||
private readonly IComparer<int> comparer; |
||||
|
||||
public PropertyComparer() |
||||
{ |
||||
comparer = Comparer<int>.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; |
||||
} |
||||
} |
||||
} |
||||
@ -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<Type,ClassElement> ClassDefCache =new ConcurrentDictionary<Type, ClassElement>(); |
||||
|
||||
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<DataContractAttribute>(); |
||||
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<DataContractAttribute>(); |
||||
if (classAttr == null) |
||||
{ |
||||
throw new HessianException("DataContract must be set"); |
||||
} |
||||
|
||||
ClassElement ce = new ClassElement {ClassName = classAttr.Name, Fields = new List<PropertyElement>()}; |
||||
|
||||
//ClassDef def = new ClassDef(classAttr.Name); |
||||
foreach (var property in typeInfo.DeclaredProperties) |
||||
{ |
||||
var attribute = property.GetCustomAttribute<DataMemberAttribute>(); |
||||
|
||||
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; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Writes NULL token into stream |
||||
/// </summary> |
||||
public void WriteNull() |
||||
{ |
||||
this._stream.WriteByte(Marker.Null); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Writes <see cref="System.Boolean" /> value into output stream. |
||||
/// </summary> |
||||
/// <param name="value">The value.</param> |
||||
public void WriteBoolean(bool value) |
||||
{ |
||||
this._stream.WriteByte(value ? Marker.True : Marker.False); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Writes array of <see cref="System.Byte" /> into output stream. |
||||
/// </summary> |
||||
/// <param name="buffer">The value.</param> |
||||
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); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue