blob: a7c5411f2ff91514b0d40ac62ce5a8ef498e8648 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache.Ignite.Core.Impl.Binary
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using Apache.Ignite.Core.Binary;
using Apache.Ignite.Core.Cache.Affinity;
using Apache.Ignite.Core.Common;
using Apache.Ignite.Core.Impl.Binary.IO;
using Apache.Ignite.Core.Impl.Binary.Metadata;
using Apache.Ignite.Core.Impl.Cache;
using Apache.Ignite.Core.Impl.Cache.Query.Continuous;
using Apache.Ignite.Core.Impl.Common;
using Apache.Ignite.Core.Impl.Compute;
using Apache.Ignite.Core.Impl.Compute.Closure;
using Apache.Ignite.Core.Impl.Datastream;
using Apache.Ignite.Core.Impl.Deployment;
using Apache.Ignite.Core.Impl.Messaging;
using Apache.Ignite.Core.Log;
/// <summary>
/// Marshaller implementation.
/// </summary>
internal class Marshaller
{
/** Register same java type flag. */
public static readonly ThreadLocal<Boolean> RegisterSameJavaTypeTl = new ThreadLocal<Boolean>(() => false);
/** Binary configuration. */
private readonly BinaryConfiguration _cfg;
/** Type to descriptor map. */
private readonly CopyOnWriteConcurrentDictionary<Type, BinaryFullTypeDescriptor> _typeToDesc =
new CopyOnWriteConcurrentDictionary<Type, BinaryFullTypeDescriptor>();
/** Type name to descriptor map. */
private readonly CopyOnWriteConcurrentDictionary<string, BinaryFullTypeDescriptor> _typeNameToDesc =
new CopyOnWriteConcurrentDictionary<string, BinaryFullTypeDescriptor>();
/** ID to descriptor map. */
private readonly CopyOnWriteConcurrentDictionary<long, BinaryFullTypeDescriptor> _idToDesc =
new CopyOnWriteConcurrentDictionary<long, BinaryFullTypeDescriptor>();
/** Cached binary types. */
private volatile IDictionary<int, BinaryTypeHolder> _metas = new Dictionary<int, BinaryTypeHolder>();
/** */
private volatile IIgniteInternal _ignite;
/** */
private readonly ILogger _log;
/** */
private readonly bool _registerSameJavaType;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="cfg">Configuration.</param>
/// <param name="log"></param>
public Marshaller(BinaryConfiguration cfg, ILogger log = null)
{
_cfg = cfg ?? new BinaryConfiguration();
_log = log;
CompactFooter = _cfg.CompactFooter;
if (_cfg.TypeConfigurations == null)
_cfg.TypeConfigurations = new List<BinaryTypeConfiguration>();
foreach (BinaryTypeConfiguration typeCfg in _cfg.TypeConfigurations)
{
if (string.IsNullOrEmpty(typeCfg.TypeName))
throw new BinaryObjectException("Type name cannot be null or empty: " + typeCfg);
}
// Define system types. They use internal reflective stuff, so configuration doesn't affect them.
AddSystemTypes();
// 2. Define user types.
var typeResolver = new TypeResolver();
ICollection<BinaryTypeConfiguration> typeCfgs = _cfg.TypeConfigurations;
if (typeCfgs != null)
foreach (BinaryTypeConfiguration typeCfg in typeCfgs)
AddUserType(typeCfg, typeResolver);
var typeNames = _cfg.Types;
if (typeNames != null)
foreach (string typeName in typeNames)
AddUserType(new BinaryTypeConfiguration(typeName), typeResolver);
_registerSameJavaType = _cfg.NameMapper == null ||
_cfg.NameMapper is BinaryBasicNameMapper && !((BinaryBasicNameMapper)_cfg.NameMapper).IsSimpleName &&
_cfg.IdMapper == null;
}
/// <summary>
/// Gets or sets the backing grid.
/// </summary>
public IIgniteInternal Ignite
{
get { return _ignite; }
set
{
Debug.Assert(value != null);
_ignite = value;
}
}
/// <summary>
/// Gets the compact footer flag.
/// </summary>
public bool CompactFooter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether type registration is disabled.
/// This may be desirable for static system marshallers where everything is written in unregistered mode.
/// </summary>
public bool RegistrationDisabled { get; set; }
/// <summary>
/// Gets force timestamp flag value.
/// </summary>
public bool ForceTimestamp
{
get { return _cfg.ForceTimestamp; }
}
/// <summary>
/// Gets date time converter.
/// </summary>
public ITimestampConverter TimestampConverter
{
get { return _cfg.TimestampConverter; }
}
/// <summary>
/// Returns register same java type flag value.
/// </summary>
public bool RegisterSameJavaType
{
get { return _registerSameJavaType && RegisterSameJavaTypeTl.Value; }
}
/// <summary>
/// Marshal object.
/// </summary>
/// <param name="val">Value.</param>
/// <returns>Serialized data as byte array.</returns>
public byte[] Marshal<T>(T val)
{
using (var stream = new BinaryHeapStream(128))
{
Marshal(val, stream);
return stream.GetArrayCopy();
}
}
/// <summary>
/// Marshal data.
/// </summary>
/// <param name="stream"></param>
/// <param name="action"></param>
public void Marshal(IBinaryStream stream, Action<BinaryWriter> action)
{
BinaryWriter writer = StartMarshal(stream);
action(writer);
FinishMarshal(writer);
}
/// <summary>
/// Marshals an object.
/// </summary>
/// <param name="val">Value.</param>
/// <param name="stream">Output stream.</param>
private void Marshal<T>(T val, IBinaryStream stream)
{
BinaryWriter writer = StartMarshal(stream);
writer.Write(val);
FinishMarshal(writer);
}
/// <summary>
/// Start marshal session.
/// </summary>
/// <param name="stream">Stream.</param>
/// <returns>Writer.</returns>
public BinaryWriter StartMarshal(IBinaryStream stream)
{
return new BinaryWriter(this, stream);
}
/// <summary>
/// Finish marshal session.
/// </summary>
/// <param name="writer">Writer.</param>
/// <returns>Dictionary with metadata.</returns>
public void FinishMarshal(BinaryWriter writer)
{
var metas = writer.GetBinaryTypes();
var ignite = Ignite;
if (ignite != null && metas != null && metas.Count > 0)
{
ignite.BinaryProcessor.PutBinaryTypes(metas);
OnBinaryTypesSent(metas);
}
}
/// <summary>
/// Unmarshal object.
/// </summary>
/// <param name="data">Data array.</param>
/// <param name="mode">The mode.</param>
/// <returns>
/// Object.
/// </returns>
public T Unmarshal<T>(byte[] data, BinaryMode mode = BinaryMode.Deserialize)
{
using (var stream = new BinaryHeapStream(data))
{
return Unmarshal<T>(stream, mode);
}
}
/// <summary>
/// Unmarshal object.
/// </summary>
/// <param name="stream">Stream over underlying byte array with correct position.</param>
/// <param name="keepBinary">Whether to keep binary objects in binary form.</param>
/// <returns>
/// Object.
/// </returns>
public T Unmarshal<T>(IBinaryStream stream, bool keepBinary)
{
return Unmarshal<T>(stream, keepBinary ? BinaryMode.KeepBinary : BinaryMode.Deserialize, null);
}
/// <summary>
/// Unmarshal object.
/// </summary>
/// <param name="stream">Stream over underlying byte array with correct position.</param>
/// <param name="mode">The mode.</param>
/// <returns>
/// Object.
/// </returns>
public T Unmarshal<T>(IBinaryStream stream, BinaryMode mode = BinaryMode.Deserialize)
{
return Unmarshal<T>(stream, mode, null);
}
/// <summary>
/// Unmarshal object.
/// </summary>
/// <param name="stream">Stream over underlying byte array with correct position.</param>
/// <param name="mode">The mode.</param>
/// <param name="builder">Builder.</param>
/// <returns>
/// Object.
/// </returns>
public T Unmarshal<T>(IBinaryStream stream, BinaryMode mode, BinaryObjectBuilder builder)
{
return new BinaryReader(this, stream, mode, builder).Deserialize<T>();
}
/// <summary>
/// Start unmarshal session.
/// </summary>
/// <param name="stream">Stream.</param>
/// <param name="keepBinary">Whether to keep binarizable as binary.</param>
/// <returns>
/// Reader.
/// </returns>
public BinaryReader StartUnmarshal(IBinaryStream stream, bool keepBinary)
{
return new BinaryReader(this, stream, keepBinary ? BinaryMode.KeepBinary : BinaryMode.Deserialize, null);
}
/// <summary>
/// Start unmarshal session.
/// </summary>
/// <param name="stream">Stream.</param>
/// <param name="mode">The mode.</param>
/// <returns>Reader.</returns>
public BinaryReader StartUnmarshal(IBinaryStream stream, BinaryMode mode = BinaryMode.Deserialize)
{
return new BinaryReader(this, stream, mode, null);
}
/// <summary>
/// Gets metadata for the given type ID.
/// </summary>
/// <param name="typeId">Type ID.</param>
/// <returns>Metadata or null.</returns>
public BinaryType GetBinaryType(int typeId)
{
// NOTE: This method results can't (easily) be cached because binary metadata is changing on the fly:
// New fields and enum values can be added.
if (Ignite != null)
{
var meta = Ignite.BinaryProcessor.GetBinaryType(typeId);
if (meta != null)
{
UpdateOrCreateBinaryTypeHolder(meta);
return meta;
}
}
return BinaryType.Empty;
}
/// <summary>
/// Gets cached metadata for the given type ID.
/// NOTE: Returned value is potentially stale.
/// Caller is responsible for refreshing the value as needed by invoking <see cref="GetBinaryType"/>.
/// </summary>
/// <param name="typeId">Type ID.</param>
/// <returns>Metadata or null.</returns>
public BinaryTypeHolder GetCachedBinaryTypeHolder(int typeId)
{
BinaryTypeHolder holder;
_metas.TryGetValue(typeId, out holder);
return holder;
}
/// <summary>
/// Puts the binary type metadata to Ignite.
/// </summary>
/// <param name="desc">Descriptor.</param>
public void PutBinaryType(IBinaryTypeDescriptor desc)
{
Debug.Assert(desc != null);
GetBinaryTypeHandler(desc); // ensure that handler exists
if (Ignite != null)
{
var metas = new[] {new BinaryType(desc, this)};
Ignite.BinaryProcessor.PutBinaryTypes(metas);
OnBinaryTypesSent(metas);
}
}
/// <summary>
/// Gets binary type handler for the given type ID.
/// </summary>
/// <param name="desc">Type descriptor.</param>
/// <returns>Binary type handler.</returns>
public IBinaryTypeHandler GetBinaryTypeHandler(IBinaryTypeDescriptor desc)
{
var holder = GetBinaryTypeHolder(desc);
if (holder != null)
{
ICollection<int> ids = holder.GetFieldIds();
bool newType = ids.Count == 0 && !holder.IsSaved;
return new BinaryTypeHashsetHandler(ids, newType);
}
return null;
}
/// <summary>
/// Gets the binary type holder.
/// </summary>
/// <param name="desc">Descriptor.</param>
/// <returns>Holder</returns>
private BinaryTypeHolder GetBinaryTypeHolder(IBinaryTypeDescriptor desc)
{
BinaryTypeHolder holder;
if (_metas.TryGetValue(desc.TypeId, out holder))
{
return holder;
}
lock (this)
{
if (!_metas.TryGetValue(desc.TypeId, out holder))
{
var metas0 = new Dictionary<int, BinaryTypeHolder>(_metas);
holder = new BinaryTypeHolder(desc.TypeId, desc.TypeName, desc.AffinityKeyFieldName,
desc.IsEnum, this);
metas0[desc.TypeId] = holder;
_metas = metas0;
}
}
return holder;
}
/// <summary>
/// Updates or creates cached binary type holder.
/// </summary>
private void UpdateOrCreateBinaryTypeHolder(BinaryType meta)
{
BinaryTypeHolder holder;
if (_metas.TryGetValue(meta.TypeId, out holder))
{
holder.Merge(meta);
return;
}
lock (this)
{
if (_metas.TryGetValue(meta.TypeId, out holder))
{
holder.Merge(meta);
return;
}
var metas0 = new Dictionary<int, BinaryTypeHolder>(_metas);
holder = new BinaryTypeHolder(meta.TypeId, meta.TypeName, meta.AffinityKeyFieldName, meta.IsEnum, this);
holder.Merge(meta);
metas0[meta.TypeId] = holder;
_metas = metas0;
}
}
/// <summary>
/// Callback invoked when metadata has been sent to the server and acknowledged by it.
/// </summary>
/// <param name="newMetas">Binary types.</param>
private void OnBinaryTypesSent(IEnumerable<BinaryType> newMetas)
{
foreach (var meta in newMetas)
{
_metas[meta.TypeId].Merge(meta);
}
}
/// <summary>
/// Gets descriptor for type.
/// </summary>
/// <param name="type">Type.</param>
/// <returns>
/// Descriptor.
/// </returns>
public IBinaryTypeDescriptor GetDescriptor(Type type)
{
BinaryFullTypeDescriptor desc;
if (!_typeToDesc.TryGetValue(type, out desc) || !desc.IsRegistered)
{
desc = RegisterType(type, desc);
}
return desc;
}
/// <summary>
/// Gets descriptor for type name.
/// </summary>
/// <param name="typeName">Type name.</param>
/// <param name="requiresType">If set to true, resulting descriptor must have Type property populated.
/// <para />
/// When working in binary mode, we don't need Type. And there is no Type at all in some cases.
/// So we should not attempt to call BinaryProcessor right away.
/// Only when we really deserialize the value, requiresType is set to true
/// and we attempt to resolve the type by all means.</param>
/// <returns>Descriptor.</returns>
public IBinaryTypeDescriptor GetDescriptor(string typeName, bool requiresType = false)
{
BinaryFullTypeDescriptor desc;
if (_typeNameToDesc.TryGetValue(typeName, out desc))
{
return desc;
}
var typeId = GetTypeId(typeName, _cfg.IdMapper);
return GetDescriptor(true, typeId, typeName: typeName, requiresType: requiresType);
}
/// <summary>
/// Gets descriptor for a type id.
/// </summary>
/// <param name="userType">User type flag.</param>
/// <param name="typeId">Type id.</param>
/// <param name="requiresType">If set to true, resulting descriptor must have Type property populated.
/// <para />
/// When working in binary mode, we don't need Type. And there is no Type at all in some cases.
/// So we should not attempt to call BinaryProcessor right away.
/// Only when we really deserialize the value, requiresType is set to true
/// and we attempt to resolve the type by all means.</param>
/// <param name="typeName">Known type name.</param>
/// <param name="knownType">Optional known type.</param>
/// <returns>
/// Descriptor.
/// </returns>
public IBinaryTypeDescriptor GetDescriptor(bool userType, int typeId, bool requiresType = false,
string typeName = null, Type knownType = null)
{
BinaryFullTypeDescriptor desc;
var typeKey = BinaryUtils.TypeKey(userType, typeId);
if (_idToDesc.TryGetValue(typeKey, out desc) && (!requiresType || desc.Type != null))
return desc;
if (!userType)
return null;
if (requiresType && _ignite != null)
{
// Check marshaller context for dynamically registered type.
var type = knownType;
if (type == null && _ignite != null)
{
typeName = typeName ?? GetTypeName(typeId);
if (typeName != null)
{
type = ResolveType(typeName);
if (type == null)
{
// Type is registered, but assembly is not present.
return new BinarySurrogateTypeDescriptor(_cfg, typeId, typeName);
}
}
}
if (type != null)
{
if (_typeToDesc.TryGetValue(type, out desc))
{
return desc;
}
return AddUserType(type, typeId, GetTypeName(type), true, desc);
}
}
var meta = GetBinaryType(typeId);
if (meta != BinaryType.Empty)
{
var typeCfg = new BinaryTypeConfiguration(meta.TypeName)
{
IsEnum = meta.IsEnum,
AffinityKeyFieldName = meta.AffinityKeyFieldName
};
return AddUserType(typeCfg, new TypeResolver());
}
return new BinarySurrogateTypeDescriptor(_cfg, typeId, typeName);
}
/// <summary>
/// Gets the type name by id.
/// </summary>
private string GetTypeName(int typeId)
{
var errorAction = RegisterSameJavaType
? ex =>
{
// Try to get java type name and register corresponding DotNet type.
var javaTypeName =
_ignite.BinaryProcessor.GetTypeName(typeId, BinaryProcessor.JavaPlatformId);
_ignite.BinaryProcessor.RegisterType(typeId, javaTypeName, false);
return javaTypeName;
}
: (Func<Exception, string>) null;
return _ignite.BinaryProcessor.GetTypeName(typeId, BinaryProcessor.DotNetPlatformId, errorAction);
}
/// <summary>
/// Registers the type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="desc">Existing descriptor.</param>
private BinaryFullTypeDescriptor RegisterType(Type type, BinaryFullTypeDescriptor desc)
{
Debug.Assert(type != null);
var typeName = GetTypeName(type);
var typeId = GetTypeId(typeName, _cfg.IdMapper);
var registered = _ignite != null && _ignite.BinaryProcessor.RegisterType(typeId, typeName, RegisterSameJavaType);
return AddUserType(type, typeId, typeName, registered, desc);
}
/// <summary>
/// Add user type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="typeId">The type id.</param>
/// <param name="typeName">Name of the type.</param>
/// <param name="registered">Registered flag.</param>
/// <param name="desc">Existing descriptor.</param>
/// <returns>Descriptor.</returns>
private BinaryFullTypeDescriptor AddUserType(Type type, int typeId, string typeName, bool registered,
BinaryFullTypeDescriptor desc)
{
Debug.Assert(type != null);
Debug.Assert(typeName != null);
var ser = GetSerializer(_cfg, null, type, typeId, null, null, _log);
desc = desc == null
? new BinaryFullTypeDescriptor(type, typeId, typeName, true, _cfg.NameMapper,
_cfg.IdMapper, ser, false, AffinityKeyMappedAttribute.GetFieldNameFromAttribute(type),
BinaryUtils.IsIgniteEnum(type), registered)
: new BinaryFullTypeDescriptor(desc, type, ser, registered);
if (RegistrationDisabled)
{
return desc;
}
var typeKey = BinaryUtils.TypeKey(true, typeId);
var desc0 = _idToDesc.GetOrAdd(typeKey, x => desc);
if (desc0.Type != null && desc0.TypeName != typeName)
{
ThrowConflictingTypeError(type, desc0.Type, typeId);
}
desc0 = _typeNameToDesc.GetOrAdd(typeName, x => desc);
if (desc0.Type != null && desc0.TypeName != typeName)
{
ThrowConflictingTypeError(type, desc0.Type, typeId);
}
ValidateRegistration(type);
_typeToDesc.Set(type, desc);
return desc;
}
/// <summary>
/// Validates type registration.
/// </summary>
[ExcludeFromCodeCoverage]
private void ValidateRegistration(Type type)
{
BinaryFullTypeDescriptor desc;
if (_typeToDesc.TryGetValue(type, out desc) && !desc.UserType)
{
throw new BinaryObjectException("Invalid attempt to overwrite system type registration: " + type);
}
}
/// <summary>
/// Throws the conflicting type error.
/// </summary>
private static void ThrowConflictingTypeError(object type1, object type2, int typeId)
{
throw new BinaryObjectException(string.Format("Conflicting type IDs [type1='{0}', " +
"type2='{1}', typeId={2}]", type1, type2, typeId));
}
/// <summary>
/// Add user type.
/// </summary>
/// <param name="typeCfg">Type configuration.</param>
/// <param name="typeResolver">The type resolver.</param>
/// <exception cref="BinaryObjectException"></exception>
private BinaryFullTypeDescriptor AddUserType(BinaryTypeConfiguration typeCfg, TypeResolver typeResolver)
{
// Get converter/mapper/serializer.
IBinaryNameMapper nameMapper = typeCfg.NameMapper ?? _cfg.NameMapper ?? GetDefaultNameMapper();
IBinaryIdMapper idMapper = typeCfg.IdMapper ?? _cfg.IdMapper;
bool keepDeserialized = typeCfg.KeepDeserialized ?? _cfg.KeepDeserialized;
// Try resolving type.
Type type = typeResolver.ResolveType(typeCfg.TypeName);
if (type != null)
{
ValidateUserType(type);
if (typeCfg.IsEnum != BinaryUtils.IsIgniteEnum(type))
{
throw new BinaryObjectException(
string.Format(
"Invalid IsEnum flag in binary type configuration. " +
"Configuration value: IsEnum={0}, actual type: IsEnum={1}, type={2}",
typeCfg.IsEnum, type.IsEnum, type));
}
// Type is found.
var typeName = GetTypeName(type, nameMapper);
int typeId = GetTypeId(typeName, idMapper);
var affKeyFld = typeCfg.AffinityKeyFieldName
?? AffinityKeyMappedAttribute.GetFieldNameFromAttribute(type);
var serializer = GetSerializer(_cfg, typeCfg, type, typeId, nameMapper, idMapper, _log);
return AddType(type, typeId, typeName, true, keepDeserialized, nameMapper, idMapper, serializer,
affKeyFld, BinaryUtils.IsIgniteEnum(type));
}
else
{
// Type is not found.
string typeName = GetTypeName(typeCfg.TypeName, nameMapper);
int typeId = GetTypeId(typeName, idMapper);
return AddType(null, typeId, typeName, true, keepDeserialized, nameMapper, idMapper, null,
typeCfg.AffinityKeyFieldName, typeCfg.IsEnum);
}
}
/// <summary>
/// Gets the serializer.
/// </summary>
private static IBinarySerializerInternal GetSerializer(BinaryConfiguration cfg,
BinaryTypeConfiguration typeCfg, Type type, int typeId, IBinaryNameMapper nameMapper,
IBinaryIdMapper idMapper, ILogger log)
{
var serializer = (typeCfg != null ? typeCfg.Serializer : null) ??
(cfg != null ? cfg.Serializer : null);
if (serializer == null)
{
if (type.GetInterfaces().Contains(typeof(IBinarizable)))
return BinarizableSerializer.Instance;
if (type.GetInterfaces().Contains(typeof(ISerializable)))
{
LogSerializableWarning(type, log);
return new SerializableSerializer(type);
}
serializer = new BinaryReflectiveSerializer
{
ForceTimestamp = cfg != null && cfg.ForceTimestamp
};
}
var refSerializer = serializer as BinaryReflectiveSerializer;
return refSerializer != null
? refSerializer.Register(type, typeId, nameMapper, idMapper)
: new UserSerializerProxy(serializer);
}
/// <summary>
/// Add type.
/// </summary>
/// <param name="type">Type.</param>
/// <param name="typeId">Type ID.</param>
/// <param name="typeName">Type name.</param>
/// <param name="userType">User type flag.</param>
/// <param name="keepDeserialized">Whether to cache deserialized value in IBinaryObject</param>
/// <param name="nameMapper">Name mapper.</param>
/// <param name="idMapper">ID mapper.</param>
/// <param name="serializer">Serializer.</param>
/// <param name="affKeyFieldName">Affinity key field name.</param>
/// <param name="isEnum">Enum flag.</param>
private BinaryFullTypeDescriptor AddType(Type type, int typeId, string typeName, bool userType,
bool keepDeserialized, IBinaryNameMapper nameMapper, IBinaryIdMapper idMapper,
IBinarySerializerInternal serializer, string affKeyFieldName, bool isEnum)
{
Debug.Assert(!string.IsNullOrEmpty(typeName));
long typeKey = BinaryUtils.TypeKey(userType, typeId);
BinaryFullTypeDescriptor conflictingType;
if (_idToDesc.TryGetValue(typeKey, out conflictingType) && conflictingType.TypeName != typeName)
{
ThrowConflictingTypeError(typeName, conflictingType.TypeName, typeId);
}
var descriptor = new BinaryFullTypeDescriptor(type, typeId, typeName, userType, nameMapper, idMapper,
serializer, keepDeserialized, affKeyFieldName, isEnum);
if (RegistrationDisabled)
{
return descriptor;
}
if (type != null)
{
ValidateRegistration(type);
_typeToDesc.Set(type, descriptor);
}
if (userType)
{
_typeNameToDesc.Set(typeName, descriptor);
}
_idToDesc.Set(typeKey, descriptor);
return descriptor;
}
/// <summary>
/// Adds a predefined system type.
/// </summary>
private void AddSystemType<T>(int typeId, Func<BinaryReader, T> ctor, string affKeyFldName = null,
IBinarySerializerInternal serializer = null)
where T : IBinaryWriteAware
{
var type = typeof(T);
serializer = serializer ?? new BinarySystemTypeSerializer<T>(ctor);
// System types always use simple name mapper.
var typeName = type.Name;
if (typeId == 0)
{
typeId = BinaryUtils.GetStringHashCodeLowerCase(typeName);
}
AddType(type, typeId, typeName, false, false, null, null, serializer, affKeyFldName, false);
}
/// <summary>
/// Adds predefined system types.
/// </summary>
private void AddSystemTypes()
{
AddSystemType(BinaryTypeId.NativeJobHolder, r => new ComputeJobHolder(r));
AddSystemType(BinaryTypeId.ComputeJobWrapper, r => new ComputeJobWrapper(r));
AddSystemType(BinaryTypeId.ComputeOutFuncJob, r => new ComputeOutFuncJob(r));
AddSystemType(BinaryTypeId.ComputeOutFuncWrapper, r => new ComputeOutFuncWrapper(r));
AddSystemType(BinaryTypeId.ComputeFuncWrapper, r => new ComputeFuncWrapper(r));
AddSystemType(BinaryTypeId.ComputeFuncJob, r => new ComputeFuncJob(r));
AddSystemType(BinaryTypeId.ComputeActionJob, r => new ComputeActionJob(r));
AddSystemType(BinaryTypeId.ContinuousQueryRemoteFilterHolder, r => new ContinuousQueryFilterHolder(r));
AddSystemType(BinaryTypeId.CacheEntryProcessorHolder, r => new CacheEntryProcessorHolder(r));
AddSystemType(BinaryTypeId.CacheEntryPredicateHolder, r => new CacheEntryFilterHolder(r));
AddSystemType(BinaryTypeId.MessageListenerHolder, r => new MessageListenerHolder(r));
AddSystemType(BinaryTypeId.StreamReceiverHolder, r => new StreamReceiverHolder(r));
AddSystemType(0, r => new AffinityKey(r), "affKey");
AddSystemType(BinaryTypeId.PlatformJavaObjectFactoryProxy, r => new PlatformJavaObjectFactoryProxy());
AddSystemType(0, r => new ObjectInfoHolder(r));
AddSystemType(BinaryTypeId.IgniteUuid, r => new IgniteGuid(r));
AddSystemType(0, r => new GetAssemblyFunc());
AddSystemType(0, r => new AssemblyRequest(r));
AddSystemType(0, r => new AssemblyRequestResult(r));
AddSystemType<PeerLoadingObjectHolder>(0, null, serializer: new PeerLoadingObjectHolderSerializer());
AddSystemType<MultidimensionalArrayHolder>(0, null, serializer: new MultidimensionalArraySerializer());
AddSystemType(BinaryTypeId.IgniteBiTuple, r => new IgniteBiTuple(r));
}
/// <summary>
/// Logs the warning about ISerializable pitfalls.
/// </summary>
private static void LogSerializableWarning(Type type, ILogger log)
{
if (log == null)
return;
log.GetLogger(typeof(Marshaller).Name)
.Warn("Type '{0}' implements '{1}'. It will be written in Ignite binary format, however, " +
"the following limitations apply: " +
"DateTime fields would not work in SQL; " +
"sbyte, ushort, uint, ulong fields would not work in DML.", type, typeof(ISerializable));
}
/// <summary>
/// Validates binary type.
/// </summary>
// ReSharper disable once UnusedParameter.Local
private static void ValidateUserType(Type type)
{
Debug.Assert(type != null);
if (type.IsGenericTypeDefinition)
{
throw new BinaryObjectException(
"Open generic types (Type.IsGenericTypeDefinition == true) are not allowed " +
"in BinaryConfiguration: " + type.AssemblyQualifiedName);
}
if (type.IsAbstract)
{
throw new BinaryObjectException(
"Abstract types and interfaces are not allowed in BinaryConfiguration: " +
type.AssemblyQualifiedName);
}
}
/// <summary>
/// Resolves the type (opposite of <see cref="GetTypeName(Type, IBinaryNameMapper)"/>).
/// </summary>
public Type ResolveType(string typeName)
{
return new TypeResolver().ResolveType(typeName, nameMapper: _cfg.NameMapper ?? GetDefaultNameMapper());
}
/// <summary>
/// Gets the name of the type according to current name mapper.
/// See also <see cref="ResolveType"/>.
/// </summary>
public string GetTypeName(Type type, IBinaryNameMapper mapper = null)
{
return GetTypeName(type.AssemblyQualifiedName, mapper);
}
/// <summary>
/// Called when local client node has been reconnected to the cluster.
/// </summary>
/// <param name="clusterRestarted">Cluster restarted flag.</param>
public void OnClientReconnected(bool clusterRestarted)
{
if (!clusterRestarted)
return;
// Reset all binary structures. Metadata must be sent again.
// _idToDesc enumerator is thread-safe (returns a snapshot).
// If there are new descriptors added concurrently, they are fine (we are already connected).
// Race is possible when serialization is started before reconnect (or even before disconnect)
// and finished after reconnect, meta won't be sent to cluster because it is assumed to be known,
// but operation will succeed.
// We don't support this use case. Users should handle reconnect events properly when cluster is restarted.
// Supporting this very rare use case will complicate the code a lot with little benefit.
foreach (var desc in _idToDesc)
{
desc.Value.ResetWriteStructure();
}
}
/// <summary>
/// Gets the name of the type.
/// </summary>
public string GetTypeName(string fullTypeName, IBinaryNameMapper mapper = null)
{
mapper = mapper ?? _cfg.NameMapper ?? GetDefaultNameMapper();
var typeName = mapper.GetTypeName(fullTypeName);
if (typeName == null)
{
throw new BinaryObjectException("IBinaryNameMapper returned null name for type [typeName=" +
fullTypeName + ", mapper=" + mapper + "]");
}
return typeName;
}
/// <summary>
/// Resolve type ID.
/// </summary>
/// <param name="typeName">Type name.</param>
/// <param name="idMapper">ID mapper.</param>
private static int GetTypeId(string typeName, IBinaryIdMapper idMapper)
{
Debug.Assert(typeName != null);
int id = 0;
if (idMapper != null)
{
try
{
id = idMapper.GetTypeId(typeName);
}
catch (Exception e)
{
throw new BinaryObjectException("Failed to resolve type ID due to ID mapper exception " +
"[typeName=" + typeName + ", idMapper=" + idMapper + ']', e);
}
}
if (id == 0)
{
id = BinaryUtils.GetStringHashCodeLowerCase(typeName);
}
return id;
}
/// <summary>
/// Gets the default name mapper.
/// </summary>
private static IBinaryNameMapper GetDefaultNameMapper()
{
return BinaryBasicNameMapper.FullNameInstance;
}
}
}