Ejemplo n.º 1
0
        /// <summary>
        /// Builds a serializer for the Confluent wire format.
        /// </summary>
        /// <param name="id">
        /// A schema ID to include in each serialized payload.
        /// </param>
        /// <param name="json">
        /// The schema to build the Avro serializer from.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        protected virtual ISerializer <T> Build <T>(
            int id,
            string json,
            TombstoneBehavior tombstoneBehavior
            )
        {
            var schema = SchemaReader.Read(json);

            if (tombstoneBehavior != TombstoneBehavior.None)
            {
                if (default(T) != null)
                {
                    throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
                }

                var hasNull = schema is Abstract.NullSchema ||
                              (schema is Abstract.UnionSchema union && union.Schemas.Any(s => s is Abstract.NullSchema));

                if (tombstoneBehavior == TombstoneBehavior.Strict && hasNull)
                {
                    throw new UnsupportedSchemaException(schema, "Tombstone serialization is not supported for schemas that can represent null values.");
                }
            }

            var bytes = BitConverter.GetBytes(id);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }

            var serialize = SerializerBuilder.BuildDelegate <T>(schema);

            return(new DelegateSerializer <T>((data, context) =>
            {
                if (data == null && tombstoneBehavior != TombstoneBehavior.None)
                {
                    if (context.Component == MessageComponentType.Value || tombstoneBehavior != TombstoneBehavior.Strict)
                    {
                        return null;
                    }
                }

                var stream = new MemoryStream();

                using (stream)
                {
                    stream.WriteByte(0x00);
                    stream.Write(bytes, 0, bytes.Length);

                    serialize(data, stream);
                }

                return stream.ToArray();
            }));
        }
        /// <summary>
        /// Serialize a message. (See <see cref="IAsyncSerializer{T}.SerializeAsync(T, SerializationContext)" />.)
        /// </summary>
        public virtual async Task <byte[]> SerializeAsync(T data, SerializationContext context)
        {
            var serialize = await(_cache.GetOrAdd(SubjectNameBuilder(context), async subject =>
            {
                int id;
                Action <T, Stream> @delegate;

                try
                {
                    var existing = await _resolve(subject).ConfigureAwait(false);
                    var schema   = SchemaReader.Read(existing.SchemaString);

                    @delegate = SerializerBuilder.BuildDelegate <T>(schema);
                    id        = existing.Id;
                }
                catch (Exception e) when(RegisterAutomatically && (
                                             (e is SchemaRegistryException sre && sre.ErrorCode == 40401) ||
                                             (e is AggregateException a && a.InnerExceptions.All(i =>
                                                                                                 i is UnsupportedSchemaException ||
                                                                                                 i is UnsupportedTypeException
                                                                                                 ))
                                             ))
                {
                    var schema = SchemaBuilder.BuildSchema <T>();
                    var json   = SchemaWriter.Write(schema);

                    @delegate = SerializerBuilder.BuildDelegate <T>(schema);
                    id        = await _register(subject, json).ConfigureAwait(false);
                }

                var bytes = BitConverter.GetBytes(id);

                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(bytes);
                }

                return(value =>
                {
                    var stream = new MemoryStream();

                    using (stream)
                    {
                        stream.WriteByte(0x00);
                        stream.Write(bytes, 0, bytes.Length);

                        @delegate(value, stream);
                    }

                    return stream.ToArray();
                });
            })).ConfigureAwait(false);

            return(serialize(data));
        }
Ejemplo n.º 3
0
        /// <inheritdoc />
        public virtual async Task <byte[]> SerializeAsync(T data, SerializationContext context)
        {
            var subject = SubjectNameBuilder(context);

            Task <Func <T, byte[]> > task;

            lock (cache)
            {
                if (!cache.TryGetValue(subject, out task) || task.IsFaulted)
                {
                    cache[subject] = task = ((Func <string, Task <Func <T, byte[]> > >)(async subject =>
                    {
                        switch (RegisterAutomatically)
                        {
                        case AutomaticRegistrationBehavior.Always:
                            var schema = SchemaBuilder.BuildSchema <T>();
                            var id = await RegistryClient.RegisterSchemaAsync(subject, new Schema(SchemaWriter.Write(schema), SchemaType.Avro)).ConfigureAwait(false);

                            return(Build(id, schema));

                        case AutomaticRegistrationBehavior.Never:
                            var registration = await RegistryClient.GetLatestSchemaAsync(subject).ConfigureAwait(false);

                            if (registration.SchemaType != SchemaType.Avro)
                            {
                                throw new UnsupportedSchemaException(null, $"The latest schema with subject {subject} is not an Avro schema.");
                            }

                            return(Build(registration.Id, SchemaReader.Read(registration.SchemaString)));

                        default:
                            throw new ArgumentOutOfRangeException(nameof(RegisterAutomatically));
                        }
                    }))(subject);
                }
            }

            var serialize = await task.ConfigureAwait(false);

            if (data == null && TombstoneBehavior != TombstoneBehavior.None)
            {
                if (context.Component == MessageComponentType.Value || TombstoneBehavior != TombstoneBehavior.Strict)
                {
                    return(null);
                }
            }

            return(serialize(data));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Builds a serializer for the Confluent wire format.
        /// </summary>
        /// <typeparam name="T">
        /// The type to be deserialized.
        /// </typeparam>
        /// <param name="id">
        /// A schema ID to include in each serialized payload.
        /// </param>
        /// <param name="json">
        /// The schema to build the Avro serializer from.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        /// <returns>
        /// A <see cref="ISerializer{T}" /> based on <paramref name="json" />.
        /// </returns>
        protected virtual ISerializer <T> Build <T>(
            int id,
            string json,
            TombstoneBehavior tombstoneBehavior)
        {
            var schema = SchemaReader.Read(json);

            if (tombstoneBehavior != TombstoneBehavior.None)
            {
                if (default(T) != null)
                {
                    throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
                }

                var hasNull = schema is Abstract.NullSchema ||
                              (schema is Abstract.UnionSchema union && union.Schemas.Any(s => s is Abstract.NullSchema));

                if (tombstoneBehavior == TombstoneBehavior.Strict && hasNull)
                {
                    throw new UnsupportedSchemaException(schema, "Tombstone serialization is not supported for schemas that can represent null values.");
                }
            }

            var inner  = SerializerBuilder.BuildDelegateExpression <T>(schema);
            var stream = Expression.Parameter(typeof(Stream));
            var value  = inner.Parameters[0];

            var writerConstructor = inner.Parameters[1].Type
                                    .GetConstructor(new[] { stream.Type });

            if (schema is Abstract.BytesSchema)
            {
                inner = new WireFormatBytesSerializerRewriter(stream)
                        .VisitAndConvert(inner, GetType().Name);
            }

            return(new DelegateSerializer <T>(
                       Expression
                       .Lambda <DelegateSerializer <T> .Implementation>(
                           Expression.Invoke(
                               inner,
                               value,
                               Expression.New(writerConstructor, stream)),
                           new[] { value, stream })
                       .Compile(),
                       id,
                       tombstoneBehavior));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Serialize a message. (See <see cref="IAsyncSerializer{T}.SerializeAsync(T, SerializationContext)" />.)
        /// </summary>
        public virtual async Task <byte[]> SerializeAsync(T data, SerializationContext context)
        {
            var subject = SubjectNameBuilder(context);

            Task <Func <T, byte[]> > task;

            lock (_cache)
            {
                if (!_cache.TryGetValue(subject, out task) || task.IsFaulted)
                {
                    _cache[subject] = task = ((Func <string, Task <Func <T, byte[]> > >)(async subject =>
                    {
                        switch (RegisterAutomatically)
                        {
                        case AutomaticRegistrationBehavior.Always:
                            var schema = SchemaBuilder.BuildSchema <T>();
                            var id = await _register(subject, SchemaWriter.Write(schema)).ConfigureAwait(false);

                            return(Build(id, schema));

                        case AutomaticRegistrationBehavior.Never:
                            var existing = await _resolve(subject).ConfigureAwait(false);

                            return(Build(existing.Id, SchemaReader.Read(existing.SchemaString)));

                        default:
                            throw new ArgumentOutOfRangeException(nameof(RegisterAutomatically));
                        }
                    }))(subject);
                }
            }

            var serialize = await task.ConfigureAwait(false);

            if (data == null && TombstoneBehavior != TombstoneBehavior.None)
            {
                if (context.Component == MessageComponentType.Value || TombstoneBehavior != TombstoneBehavior.Strict)
                {
                    return(null);
                }
            }

            return(serialize(data));
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Builds a serializer for the Confluent wire format.
        /// </summary>
        /// <param name="id">
        /// A schema ID to include in each serialized payload.
        /// </param>
        /// <param name="schema">
        /// The schema to build the Avro serializer from.
        /// </param>
        protected virtual ISerializer <T> Build <T>(int id, string schema)
        {
            var bytes = BitConverter.GetBytes(id);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }

            var serialize = SerializerBuilder.BuildDelegate <T>(SchemaReader.Read(schema));

            return(new DelegateSerializer <T>((data, stream) =>
            {
                stream.WriteByte(0x00);
                stream.Write(bytes, 0, bytes.Length);

                serialize(data, stream);
            }));
        }