Split STATIC_CODECS into per-type caches
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / EnumerationCodec.java
index 2e16b0c616c0144b0ceee188b6ae47f0d4b90388..076ff1935f135b9a014c5c7ff1347ec14cd17393 100644 (file)
@@ -10,13 +10,16 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
 import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.Maps;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.impl.ValueTypeCodec.SchemaUnawareCodec;
 import org.opendaylight.yangtools.yang.binding.Enumeration;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
@@ -26,19 +29,31 @@ import org.slf4j.LoggerFactory;
 
 final class EnumerationCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
     private static final Logger LOG = LoggerFactory.getLogger(EnumerationCodec.class);
+    /*
+     * Use identity comparison for keys and allow classes to be GCd themselves.
+     *
+     * Since codecs can (and typically do) hold a direct or indirect strong reference to the class, they need to be also
+     * accessed via reference. Using a weak reference could be problematic, because the codec would quite often be only
+     * weakly reachable. We therefore use a soft reference, whose implementation guidance is suitable to our use case:
+     *
+     *     "Virtual machine implementations are, however, encouraged to bias against clearing recently-created or
+     *      recently-used soft references."
+     */
+    private static final Cache<Class<?>, @NonNull EnumerationCodec> CACHE = CacheBuilder.newBuilder().weakKeys()
+        .softValues().build();
 
     private final ImmutableBiMap<String, Enum<?>> nameToEnum;
 
-    EnumerationCodec(final Class<? extends Enum<?>> enumeration, final Map<String, Enum<?>> nameToEnum) {
+    private EnumerationCodec(final Class<? extends Enum<?>> enumeration, final Map<String, Enum<?>> nameToEnum) {
         super(enumeration);
         this.nameToEnum = ImmutableBiMap.copyOf(nameToEnum);
     }
 
-    static Callable<EnumerationCodec> loader(final Class<?> returnType, final EnumTypeDefinition def) {
-        checkArgument(Enum.class.isAssignableFrom(returnType));
-        @SuppressWarnings("unchecked")
-        final Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) returnType;
-        return () -> {
+    static @NonNull EnumerationCodec of(final Class<?> returnType, final EnumTypeDefinition def)
+            throws ExecutionException {
+        return CACHE.get(returnType, () -> {
+            final Class<? extends Enum<?>> enumType = castType(returnType);
+
             final Map<String, Enum<?>> mapping = Maps.uniqueIndex(Arrays.asList(enumType.getEnumConstants()),
                 value -> {
                     checkArgument(value instanceof Enumeration,
@@ -61,7 +76,13 @@ final class EnumerationCodec extends ReflectionBasedCodec implements SchemaUnawa
             }
 
             return new EnumerationCodec(enumType, mapping);
-        };
+        });
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Class<? extends Enum<?>> castType(final Class<?> returnType) {
+        checkArgument(Enum.class.isAssignableFrom(returnType));
+        return (Class<? extends Enum<?>>) returnType;
     }
 
     @Override