Fix FinalModifierException due to BindingToNormalizedNodeCodec final 74/44274/2
authorTom Pantelis <tpanteli@brocade.com>
Thu, 18 Aug 2016 14:31:00 +0000 (10:31 -0400)
committerTom Pantelis <tpanteli@brocade.com>
Thu, 18 Aug 2016 19:22:07 +0000 (19:22 +0000)
The BindingToNormalizedNodeCodec class is final which prevents blueprint from
being able to create a proxy instance when advertising as a service. This is
is OK as it will use the actual instance but it logs an exception:

2016-08-11 23:03:18,215 | INFO  | rint Extender: 2 | ServiceRecipe
| 15 - org.apache.aries.blueprint.core - 1.6.1 | Unable to create a
proxy object for the service .component-2 defined in bundle
org.opendaylight.controller.sal-binding-broker-impl/1.5.0.SNAPSHOT with
id. Returning the original object instead.
org.apache.aries.proxy.FinalModifierException: The class
org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec
is final.
at
org.apache.aries.proxy.impl.gen.ProxySubclassGenerator.scanForFinalModifiers(ProxySubclassGenerator.java:330)[12:org.apache.aries.proxy.impl:1.0.5]

Although it's logged at INFO level we should avoid it.

Changing BindingToNormalizedNodeCodec to non-final fixes the exception however it causes
an error when it tries to create the proxy b/c some methods are final. To avoid this, weneed to avoid the proxy creation altogether so I added a method to
BindingToNormalizedNodeCodecFactory to advertise the OSGi service instead of using
the blueprint <service> element.

Note: it's not good practice to advertise a service with the actual class but it's
needed with BindingToNormalizedNodeCodec for backwards compatibility with CSS modules
that inject the BindingToNormalizedNodeCodec instance and not its interfaces. The
BindingToNormalizedNodeCodec CSS service identity is deprecated and will/should go away in the future.

Change-Id: I96b6cb8b030de39808de17142d79f8bbd09bf735
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodecFactory.java
opendaylight/md-sal/sal-binding-broker/src/main/resources/org/opendaylight/blueprint/binding-broker.xml

index bb123a15f1cb6461d8f739b3ccea5e8417c183d8..f1ae5cb1c58eb86f8aa384cbfa4232cf580b70f4 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.md.sal.binding.impl;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
 import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
@@ -14,6 +16,8 @@ import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeC
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 
 /**
  * Factory class for creating and initializing the BindingToNormalizedNodeCodec instances.
@@ -62,4 +66,27 @@ public class BindingToNormalizedNodeCodecFactory {
             SchemaService schemaService) {
         return schemaService.registerSchemaContextListener(instance);
     }
+
+    /**
+     * This method is called via blueprint to register a BindingToNormalizedNodeCodec instance with the OSGI
+     * service registry. This is done in code instead of directly via blueprint because the BindingToNormalizedNodeCodec
+     * instance must be advertised with the actual class for backwards compatibility with CSS modules and blueprint
+     * will try to create a proxy wrapper which is problematic with BindingToNormalizedNodeCodec because it's final
+     * and has final methods which can't be proxied.
+     *
+     * @param instance the BindingToNormalizedNodeCodec instance
+     * @param bundleContext the BundleContext
+     * @return ServiceRegistration instance
+     */
+    public static ServiceRegistration<BindingToNormalizedNodeCodec> registerOSGiService(BindingToNormalizedNodeCodec instance,
+            BundleContext bundleContext) {
+        Dictionary<String, String> props = new Hashtable<>();
+
+        // Set the appropriate service properties so the corresponding CSS module is restarted if this
+        // blueprint container is restarted
+        props.put("config-module-namespace", "urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl");
+        props.put("config-module-name", "runtime-generated-mapping");
+        props.put("config-instance-name", "runtime-mapping-singleton");
+        return bundleContext.registerService(BindingToNormalizedNodeCodec.class, instance, props );
+    }
 }
index 79d0868d0d445117816f20774a9b62f032574e8a..f1438c650e6b3bdffa841873dfcef9ac158c2ab4 100644 (file)
@@ -13,7 +13,7 @@
   </bean>
 
   <!-- Register the BindingToNormalizedNodeCodec with the SchemaService as a SchemaContextListener -->
-  <bean id="mappingCodecReg" class="org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodecFactory"
+  <bean id="mappingCodecListenerReg" class="org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodecFactory"
          factory-method="registerInstance" destroy-method="close">
     <argument ref="mappingCodec"/>
     <argument ref="schemaService"/>
 
   <!-- We also register the BindingToNormalizedNodeCodec with its actual class name for
        backwards compatibility for CSS users that inject the binding-dom-mapping-service -->
-  <service ref="mappingCodec" interface="org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec"
-        odl:type="default">
-    <!-- Set the appropriate service properties so the corresponding CSS module is restarted if this
-         blueprint container is restarted -->
-    <service-properties>
-      <entry key="config-module-namespace" value="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl"/>
-      <entry key="config-module-name" value="runtime-generated-mapping"/>
-      <entry key="config-instance-name" value="runtime-mapping-singleton"/>
-    </service-properties>
-  </service>
+  <bean id="mappingCodecServiceReg" class="org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodecFactory"
+         factory-method="registerOSGiService" destroy-method="unregister">
+    <argument ref="mappingCodec"/>
+    <argument ref="blueprintBundleContext"/>
+  </bean>
 
   <!-- Binding RPC Registry Service -->