Rework DataObjectSerializer implementations
[yangtools.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / gen / impl / AbstractStreamWriterGenerator.java
index 1b1be4125e065798b6da45a7f8b310cda245e7dc..d1c501f5a8ece023b3e122b9ebec7508c097c0b8 100644 (file)
@@ -7,13 +7,16 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.gen.impl;
 
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Map.Entry;
 import javassist.CannotCompileException;
 import javassist.CtClass;
@@ -27,13 +30,13 @@ import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.util.Types;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
-import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -43,44 +46,61 @@ import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Deprecated
 abstract class AbstractStreamWriterGenerator extends AbstractGenerator implements DataObjectSerializerGenerator {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractStreamWriterGenerator.class);
 
     protected static final String SERIALIZE_METHOD_NAME = "serialize";
     protected static final AugmentableDispatchSerializer AUGMENTABLE = new AugmentableDispatchSerializer();
-    private static final Field FIELD_MODIFIERS;
+    private static final Field FIELD_MODIFIERS = getModifiersField();
 
     private final LoadingCache<Class<?>, DataObjectSerializerImplementation> implementations;
     private final CtClass[] serializeArguments;
     private final JavassistUtils javassist;
     private BindingRuntimeContext context;
 
-    static {
+    private static Field getModifiersField() {
         /*
          * Cache reflection access to field modifiers field. We need this to set
          * fix the static declared fields to final once we initialize them. If we
          * cannot get access, that's fine, too.
          */
-        Field field = null;
+        final Field field;
         try {
             field = Field.class.getDeclaredField("modifiers");
-            field.setAccessible(true);
         } catch (NoSuchFieldException | SecurityException e) {
-            LOG.warn("Could not get Field modifiers field, serializers run at decreased efficiency", e);
+            LOG.warn("Could not get modifiers field, serializers run at decreased efficiency", e);
+            return null;
+        }
+
+        try {
+            AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+                field.setAccessible(true);
+                return null;
+            });
+        } catch (SecurityException e) {
+            LOG.warn("Could not get access to modifiers field, serializers run at decreased efficiency", e);
+            return null;
         }
 
-        FIELD_MODIFIERS = field;
+        return field;
     }
 
     protected AbstractStreamWriterGenerator(final JavassistUtils utils) {
-        this.javassist = Preconditions.checkNotNull(utils,"JavassistUtils instance is required.");
-        this.serializeArguments = new CtClass[] {
-                javassist.asCtClass(DataObjectSerializerRegistry.class),
-                javassist.asCtClass(DataObject.class),
-                javassist.asCtClass(BindingStreamEventWriter.class),
-        };
-        javassist.appendClassLoaderIfMissing(DataObjectSerializerPrototype.class.getClassLoader());
-        this.implementations = CacheBuilder.newBuilder().weakKeys().build(new SerializerImplementationLoader());
+        this.javassist = requireNonNull(utils, "JavassistUtils instance is required.");
+        synchronized (javassist) {
+            this.serializeArguments = new CtClass[] {
+                    javassist.asCtClass(DataObjectSerializerRegistry.class),
+                    javassist.asCtClass(DataObject.class),
+                    javassist.asCtClass(BindingStreamEventWriter.class),
+            };
+            javassist.appendClassLoaderIfMissing(DataObjectSerializerPrototype.class.getClassLoader());
+        }
+        this.implementations = CacheBuilder.newBuilder()
+                .removalListener(notification -> LOG.debug("onRemoval: cause={}, wasEvicted={}",
+                        notification.getCause(), notification.wasEvicted()))
+                .weakKeys().build(new SerializerImplementationLoader());
+        LOG.debug("AbstractStreamWriterGenerator constructor, new instance: {}", this);
     }
 
     @Override
@@ -91,11 +111,12 @@ abstract class AbstractStreamWriterGenerator extends AbstractGenerator implement
     @Override
     public final void onBindingRuntimeContextUpdated(final BindingRuntimeContext runtime) {
         this.context = runtime;
+        LOG.debug("onBindingRuntimeContextUpdated() : {}", runtime);
     }
 
     @Override
     protected final String loadSerializerFor(final Class<?> cls) {
-        return implementations.getUnchecked(cls).getClass().getName();
+        return getSerializer(cls).getClass().getName();
     }
 
     private final class SerializerImplementationLoader
@@ -111,8 +132,8 @@ abstract class AbstractStreamWriterGenerator extends AbstractGenerator implement
         @Override
         @SuppressWarnings("unchecked")
         public DataObjectSerializerImplementation load(final Class<?> type) throws Exception {
-            Preconditions.checkArgument(BindingReflections.isBindingClass(type));
-            Preconditions.checkArgument(DataContainer.class.isAssignableFrom(type),
+            checkArgument(BindingReflections.isBindingClass(type));
+            checkArgument(DataContainer.class.isAssignableFrom(type),
                 "DataContainer is not assingnable from %s from classloader %s.", type, type.getClassLoader());
 
             final String serializerName = getSerializerName(type);
@@ -127,7 +148,7 @@ abstract class AbstractStreamWriterGenerator extends AbstractGenerator implement
 
             final DataObjectSerializerImplementation obj =
                     (DataObjectSerializerImplementation) cls.getDeclaredMethod(GETINSTANCE_METHOD_NAME).invoke(null);
-            LOG.debug("Loaded serializer {} for class {}", obj, type);
+            LOG.trace("Loaded serializer {} for class {}", obj, type);
             return obj;
         }
 
@@ -135,11 +156,13 @@ abstract class AbstractStreamWriterGenerator extends AbstractGenerator implement
                 final String serializerName) throws CannotCompileException, IllegalAccessException,
                 IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException,
                 NoSuchFieldException {
+            LOG.debug("generateSerializer() due to Cache miss: typeName={}, typeClassLoader={}, serializerName={}",
+                    type.getTypeName(), type.getClassLoader(), serializerName);
             final DataObjectSerializerSource source = generateEmitterSource(type, serializerName);
             final CtClass poolClass = generateEmitter0(type, source, serializerName);
-            @SuppressWarnings("unchecked")
-            final Class<? extends DataObjectSerializerImplementation> cls = poolClass.toClass(type.getClassLoader(),
-                type.getProtectionDomain());
+            final Class<? extends DataObjectSerializerImplementation> cls =
+                    poolClass.toClass(type.getClassLoader(), type.getProtectionDomain())
+                    .asSubclass(DataObjectSerializerImplementation.class);
 
             /*
              * Due to OSGi class loader rules we cannot initialize the fields during
@@ -202,8 +225,8 @@ abstract class AbstractStreamWriterGenerator extends AbstractGenerator implement
          * care of this before calling instantiatePrototype(), as that will call our customizer with the lock held,
          * hence any code generation will end up being blocked on the javassist lock.
          */
-        final String body = ClassLoaderUtils.withClassLoader(type.getClassLoader(),
-            (Supplier<String>) () -> source.getSerializerBody().toString());
+        final String body = ClassLoaderUtils.getWithClassLoader(type.getClassLoader(), source::getSerializerBody)
+                .toString();
 
         try {
             product = javassist.instantiatePrototype(DataObjectSerializerPrototype.class.getName(), serializerName,
@@ -222,7 +245,7 @@ abstract class AbstractStreamWriterGenerator extends AbstractGenerator implement
                     // The prototype is not visible, so we need to take care of that
                     cls.setModifiers(Modifier.setPublic(cls.getModifiers()));
                 });
-        } catch (final NotFoundException e) {
+        } catch (NotFoundException | CannotCompileException e) {
             LOG.error("Failed to instatiate serializer {}", source, e);
             throw new LinkageError("Unexpected instantation problem: serializer prototype not found", e);
         }