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)));
+        }
+    }
+}

Reply via email to