This is an automated email from the ASF dual-hosted git repository.
fokko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/master by this push:
new e72aae6 AVRO-2396: Cache types in ObjectCreator (#538)
e72aae6 is described below
commit e72aae63be4d5ceb2ad05822e85894e868c7b2d6
Author: Brian Lachniet <[email protected]>
AuthorDate: Mon Jun 10 14:25:32 2019 -0400
AVRO-2396: Cache types in ObjectCreator (#538)
(cherry picked from commit 675e6f017d3758da1f79c2407df5c0c6d2b83e99)
---
.../src/apache/main/Specific/ObjectCreator.cs | 196 ++++++++++++---------
lang/csharp/src/apache/test/File/FileTests.cs | 4 +-
.../src/apache/test/Specific/ObjectCreatorTests.cs | 82 +++++++++
3 files changed, 192 insertions(+), 90 deletions(-)
diff --git a/lang/csharp/src/apache/main/Specific/ObjectCreator.cs
b/lang/csharp/src/apache/main/Specific/ObjectCreator.cs
index 1164053..a307b37 100644
--- a/lang/csharp/src/apache/main/Specific/ObjectCreator.cs
+++ b/lang/csharp/src/apache/main/Specific/ObjectCreator.cs
@@ -16,51 +16,60 @@
* limitations under the License.
*/
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Text;
using System.Reflection;
-using System.Reflection.Emit;
namespace Avro.Specific
{
-
+ /// <summary>
+ /// Resolves and creates types associated with a schema and/or name. You
should generally use
+ /// the shared <see cref="Instance"/> to take advantage caching.
+ /// </summary>
public sealed class ObjectCreator
{
- private static readonly ObjectCreator instance = new ObjectCreator();
- public static ObjectCreator Instance { get { return instance; } }
+ /// <summary>
+ /// Shareable instance of the <see cref="ObjectCreator"/>.
+ /// </summary>
+ public static ObjectCreator Instance { get; } = new ObjectCreator();
/// <summary>
/// Static generic dictionary type used for creating new dictionary
instances
/// </summary>
- private Type GenericMapType = typeof(Dictionary<,>);
+ private readonly Type GenericMapType = typeof(Dictionary<,>);
/// <summary>
/// Static generic list type used for creating new array instances
/// </summary>
- private Type GenericListType = typeof(List<>);
+ private readonly Type GenericListType = typeof(List<>);
/// <summary>
/// Static generic nullable type used for creating new nullable
instances
/// </summary>
- private Type GenericNullableType = typeof(Nullable<>);
+ private readonly Type GenericNullableType = typeof(Nullable<>);
+ private readonly ConcurrentDictionary<string, Type> typeCacheByName;
private readonly Assembly execAssembly;
private readonly Assembly entryAssembly;
private readonly bool diffAssembly;
+ [Obsolete("This will be removed from the public API in a future
version.")]
public delegate object CtorDelegate();
- private ObjectCreator()
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ObjectCreator"/>
class.
+ /// </summary>
+ public ObjectCreator()
{
- execAssembly = System.Reflection.Assembly.GetExecutingAssembly();
- entryAssembly = System.Reflection.Assembly.GetEntryAssembly();
- if (entryAssembly != null && execAssembly != entryAssembly) //
entryAssembly returns null when running from NUnit
- diffAssembly = true;
+ typeCacheByName = new ConcurrentDictionary<string, Type>();
+ execAssembly = Assembly.GetExecutingAssembly();
+ entryAssembly = Assembly.GetEntryAssembly();
- GenericMapType = typeof(Dictionary<,>);
- GenericListType = typeof(List<>);
+ // entryAssembly returns null when running from NUnit
+ diffAssembly = entryAssembly != null && execAssembly !=
entryAssembly;
}
+ [Obsolete("This will be removed from the public API in a future
version.")]
public struct NameCtorKey : IEquatable<NameCtorKey>
{
public string name { get; private set; }
@@ -104,76 +113,75 @@ namespace Avro.Specific
/// Find the type with the given name
/// </summary>
/// <param name="name">the object type to locate</param>
- /// <param name="throwError">whether or not to throw an error if the
type wasn't found</param>
/// <returns>the object type, or <c>null</c> if not found</returns>
- private Type FindType(string name,bool throwError)
+ /// <exception cref="AvroException">
+ /// No type found matching the given name.
+ /// </exception>
+ private Type FindType(string name)
{
- Type type;
-
- // Modify provided type to ensure it can be discovered.
- // This is mainly for Generics, and Nullables.
- name = name.Replace("Nullable", "Nullable`1");
- name = name.Replace("IList", "System.Collections.Generic.IList`1");
- name = name.Replace("<", "[");
- name = name.Replace(">", "]");
-
- if (diffAssembly)
+ return typeCacheByName.GetOrAdd(name, (_) =>
{
- // entry assembly different from current assembly, try entry
assembly first
- type = entryAssembly.GetType(name);
- if (type == null) // now try current assembly and mscorlib
- type = Type.GetType(name);
- }
- else
- type = Type.GetType(name);
+ Type type = null;
- Type[] types;
+ // Modify provided type to ensure it can be discovered.
+ // This is mainly for Generics, and Nullables.
+ name = name.Replace("Nullable", "Nullable`1");
+ name = name.Replace("IList",
"System.Collections.Generic.IList`1");
+ name = name.Replace("<", "[");
+ name = name.Replace(">", "]");
- if (type == null) // type is still not found, need to loop through
all loaded assemblies
- {
- Assembly[] assemblies =
AppDomain.CurrentDomain.GetAssemblies();
- foreach (Assembly assembly in assemblies)
+ // if entry assembly different from current assembly, try
entry assembly first
+ if (diffAssembly)
{
- // Fix for Mono 3.0.10
- if (assembly.FullName.StartsWith("MonoDevelop.NUnit"))
- continue;
+ type = entryAssembly.GetType(name);
+ }
- // Loading all types from all assemblies could fail for a
variety of non-fatal
- // reasons. If we fail to load types from an assembly,
continue.
- try
- {
- types = assembly.GetTypes();
- }
- catch
- {
- continue;
- }
+ // try current assembly and mscorlib
+ if (type == null)
+ {
+ type = Type.GetType(name);
+ }
- // Change the search to look for Types by both NAME and
FULLNAME
- foreach (Type t in types)
+ // type is still not found, need to loop through all loaded
assemblies
+ if (type == null)
+ {
+ foreach (Assembly assembly in
AppDomain.CurrentDomain.GetAssemblies())
{
- if (name == t.Name || name == t.FullName) type = t;
+ // Loading all types from all assemblies could fail
for a variety of
+ // non -fatal reasons. If we fail to load types from
an assembly, continue.
+ try
+ {
+ // Change the search to look for Types by both
NAME and FULLNAME
+ foreach (Type t in assembly.GetTypes())
+ {
+ if (name == t.Name || name == t.FullName)
+ {
+ type = t;
+ break;
+ }
+ }
+ }
+ catch
+ {
+ continue;
+ }
}
-
- if (type != null)
- break;
}
- }
-
- if (null == type && throwError)
- {
- throw new AvroException("Unable to find type " + name + " in
all loaded assemblies");
- }
- return type;
+ return type
+ ?? throw new AvroException($"Unable to find type '{name}'
in all loaded " +
+ $"assemblies");
+ });
}
-
/// <summary>
/// Gets the type for the specified schema
/// </summary>
/// <param name="schema"></param>
/// <returns></returns>
+ /// <exception cref="AvroException">
+ /// No type found matching the given name.
+ /// </exception>
public Type GetType(Schema schema)
{
switch(schema.Tag) {
@@ -195,8 +203,7 @@ namespace Avro.Specific
return typeof(string);
case Schema.Type.Union:
{
- UnionSchema unSchema = schema as UnionSchema;
- if (null != unSchema && unSchema.Count==2)
+ if (schema is UnionSchema unSchema && unSchema.Count == 2)
{
Schema s1 = unSchema.Schemas[0];
Schema s2 = unSchema.Schemas[1];
@@ -212,13 +219,13 @@ namespace Avro.Specific
itemType = GetType(s1);
}
- if (null != itemType )
+ if (itemType != null)
{
if (itemType.IsValueType && !itemType.IsEnum)
{
try
{
- return
GenericNullableType.MakeGenericType(new [] {itemType});
+ return
GenericNullableType.MakeGenericType(itemType);
}
catch (Exception) { }
}
@@ -229,30 +236,37 @@ namespace Avro.Specific
return typeof(object);
}
- case Schema.Type.Array: {
- ArraySchema arrSchema = schema as ArraySchema;
- Type itemSchema = GetType(arrSchema.ItemSchema);
+ case Schema.Type.Array:
+ {
+ ArraySchema arrSchema = schema as ArraySchema;
+ Type itemSchema = GetType(arrSchema.ItemSchema);
- return GenericListType.MakeGenericType(new [] {itemSchema}); }
- case Schema.Type.Map: {
- MapSchema mapSchema = schema as MapSchema;
- Type itemSchema = GetType(mapSchema.ValueSchema);
+ return GenericListType.MakeGenericType(itemSchema);
+ }
+ case Schema.Type.Map:
+ {
+ MapSchema mapSchema = schema as MapSchema;
+ Type itemSchema = GetType(mapSchema.ValueSchema);
- return GenericMapType.MakeGenericType(new [] { typeof(string),
itemSchema }); }
+ return GenericMapType.MakeGenericType(typeof(string),
itemSchema );
+ }
case Schema.Type.Enumeration:
case Schema.Type.Record:
case Schema.Type.Fixed:
- case Schema.Type.Error: {
- // Should all be named types
- var named = schema as NamedSchema;
- if(null!=named) {
- return FindType(named.Fullname,true);
+ case Schema.Type.Error:
+ {
+ // Should all be named types
+ if (schema is NamedSchema named)
+ {
+ return FindType(named.Fullname);
+ }
+
+ break;
}
- break; }
}
// Fallback
- return FindType(schema.Name,true);
+ return FindType(schema.Name);
}
/// <summary>
@@ -261,17 +275,20 @@ namespace Avro.Specific
/// <param name="name">name of the object to get type of</param>
/// <param name="schemaType">schema type for the object</param>
/// <returns>Type</returns>
+ /// <exception cref="AvroException">
+ /// No type found matching the given name.
+ /// </exception>
public Type GetType(string name, Schema.Type schemaType)
{
- Type type = FindType(name, true);
+ Type type = FindType(name);
if (schemaType == Schema.Type.Map)
{
- type = GenericMapType.MakeGenericType(new[] { typeof(string),
type });
+ type = GenericMapType.MakeGenericType(typeof(string), type);
}
else if (schemaType == Schema.Type.Array)
{
- type = GenericListType.MakeGenericType(new [] {type});
+ type = GenericListType.MakeGenericType(type);
}
return type;
@@ -283,6 +300,9 @@ namespace Avro.Specific
/// <param name="name">fully qualified name of the type</param>
/// <param name="schemaType">type of schema</param>
/// <returns>new object of the given type</returns>
+ /// <exception cref="AvroException">
+ /// No type found matching the given name.
+ /// </exception>
public object New(string name, Schema.Type schemaType)
{
return Activator.CreateInstance(GetType(name, schemaType));
diff --git a/lang/csharp/src/apache/test/File/FileTests.cs
b/lang/csharp/src/apache/test/File/FileTests.cs
index 9d7f1d7..0d7433a 100644
--- a/lang/csharp/src/apache/test/File/FileTests.cs
+++ b/lang/csharp/src/apache/test/File/FileTests.cs
@@ -471,11 +471,11 @@ namespace Avro.Test.File
[TestCase(specificSchema, Codec.Type.Null, 2, 0, 1)]
[TestCase(specificSchema, Codec.Type.Null, 10, 1, 4)]
[TestCase(specificSchema, Codec.Type.Null, 200, 111, 15)]
- // Disabled due to long runtime [TestCase(specificSchema,
Codec.Type.Null, 1000, 588, 998)]
+ [TestCase(specificSchema, Codec.Type.Null, 1000, 588, 998)]
[TestCase(specificSchema, Codec.Type.Deflate, 2, 0, 1)]
[TestCase(specificSchema, Codec.Type.Deflate, 10, 1, 4)]
[TestCase(specificSchema, Codec.Type.Deflate, 200, 111, 15)]
- // Disabled due to long runtime [TestCase(specificSchema,
Codec.Type.Deflate, 1000, 588, 998)]
+ [TestCase(specificSchema, Codec.Type.Deflate, 1000, 588, 998)]
public void TestSyncAndSeekPositions(string schemaStr, Codec.Type
codecType, int iterations, int firstSyncPosition, int secondSyncPosition)
{
// create and write out
diff --git a/lang/csharp/src/apache/test/Specific/ObjectCreatorTests.cs
b/lang/csharp/src/apache/test/Specific/ObjectCreatorTests.cs
new file mode 100644
index 0000000..5abda69
--- /dev/null
+++ b/lang/csharp/src/apache/test/Specific/ObjectCreatorTests.cs
@@ -0,0 +1,82 @@
+/**
+ * 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.
+ */
+using Avro.Specific;
+using Avro.Test.File;
+using NUnit.Framework;
+using System.Collections.Generic;
+
+namespace Avro.test.Specific
+{
+ [TestFixture()]
+ public class ObjectCreatorTests
+ {
+ [Test]
+ public void TestNewTypeDoesNotExist()
+ {
+ var objectCreator = new ObjectCreator();
+
+ Assert.Throws<AvroException>(() =>
+ objectCreator.New("ThisTypeDoesNotExist", Schema.Type.Record));
+ }
+
+ [Test]
+ public void TestNew()
+ {
+ var objectCreator = new ObjectCreator();
+
+ // Single Foo
+ Assert.IsInstanceOf(typeof(Foo),
+ objectCreator.New("Foo", Schema.Type.Record));
+
+ // Array of Foo
+ Assert.IsInstanceOf(typeof(IList<Foo>),
+ objectCreator.New("Foo", Schema.Type.Array));
+
+ // Map of Foo
+ Assert.IsInstanceOf(typeof(IDictionary<string, Foo>),
+ objectCreator.New("Foo", Schema.Type.Map));
+ }
+
+ [Test]
+ public void TestGetTypeTypeDoesNotExist()
+ {
+ var objectCreator = new ObjectCreator();
+
+ Assert.Throws<AvroException>(() =>
+ objectCreator.GetType("ThisTypeDoesNotExist",
Schema.Type.Record));
+ }
+
+ [Test]
+ public void TestGetType()
+ {
+ var objectCreator = new ObjectCreator();
+
+ // Single Foo
+ Assert.AreEqual(typeof(Foo),
+ objectCreator.GetType("Foo", Schema.Type.Record));
+
+ // Array of Foo
+ Assert.True(typeof(IList<Foo>).IsAssignableFrom(
+ objectCreator.GetType("Foo", Schema.Type.Array)));
+
+ // Map of Foo
+ Assert.True(typeof(IDictionary<string, Foo>).IsAssignableFrom(
+ objectCreator.GetType("Foo", Schema.Type.Map)));
+ }
+ }
+}