Uploaded image for project: 'Felix'
  1. Felix
  2. FELIX-6353

Constructor injection: activation with optional reference fails if service not available

    XMLWordPrintableJSON

Details

    Description

      A service that uses constructor injection for an optional service will crash with an "activation failed" exception if the optional service is not available.
      According to the OSGi R7 specification a "null" value should be injected instead.

      Example:

      @Activate
      public SomeService(
        @Reference(cardinality = ReferenceCardinality.OPTIONAL)
        OptionalService optionalService)
      {}
      

      The above code works if an OptionalService instance is present but it fails and crashes if no OptionalService instance is present during activation time:

      Error message in log:

      ERROR : bundle optionalreferenceconstructor:0.1.0.. (1)[optionalreferenceconstructor.SomeService(0)] : Error during instantiation of the implementation object: Unable to get service for reference optionalService
      

      Stack trace shown with scr:info:

      State:        FAILED ACTIVATION
      References:   (total 1)
        - optionalService: optionalreferenceconstructor.OptionalService SATISFIED 0..1 static
          target=(*) scope=bundle (no active bindings)
      Failure:      java.lang.InstantiationException: Unable to get service for reference optionalService
      java.lang.InstantiationException: Unable to get service for reference additionalSettingsStorage at org.apache.felix.scr.impl.inject.internal.ComponentConstructorImpl.newInstance(ComponentConstructorImpl.java:301) at org.apache.felix.scr.impl.manager.SingleComponentManager.createImplementationObject(SingleComponentManager.java:286) at org.apache.felix.scr.impl.manager.SingleComponentManager.createComponent(SingleComponentManager.java:115) at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:1000) at org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:973) at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:785) at org.apache.felix.scr.impl.manager.DependencyManager$SingleStaticCustomizer.addedService(DependencyManager.java:1271) at org.apache.felix.scr.impl.manager.DependencyManager$SingleStaticCustomizer.addedService(DependencyManager.java:1222) at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1200) at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1121) at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.trackAdding(ServiceTracker.java:928) at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.track(ServiceTracker.java:864) at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:1152) at org.apache.felix.scr.impl.BundleComponentActivator$ListenerInfo.serviceChanged(BundleComponentActivator.java:114) at org.apache.felix.framework.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:990) at org.apache.felix.framework.EventDispatcher.fireEventImmediately(EventDispatcher.java:838) at org.apache.felix.framework.EventDispatcher.fireServiceEvent(EventDispatcher.java:545) at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:4833) at org.apache.felix.framework.Felix.registerService(Felix.java:3804) at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:328) at
      ...
      

      It seems that the ComponentConstructorImpl (line 301) does only check for multiple cardinality but not for optional cardinality.
      Propably ComponentConstructorImpl.java (line 299) should be:

      if ( !refMetadata.isMultiple())
      {
      	if ( !refMetadata.isOptional() && (ref == null) )
      	{
      		throw new InstantiationException("Unable to get service for reference " + refMetadata.getName());
      	}
      }
      

      Instead of:

      if ( !refMetadata.isMultiple())
      {
      	if ( ref == null )
      	{
      		throw new InstantiationException("Unable to get service for reference " + refMetadata.getName());
      	}
      }
      

      The change is also attached a patch file. Please note that I did not have the opportunity to test the change. It might not fix the problem or cause other crashes.

      Attachments

        1. constructorinjection.patch
          0.9 kB
          Tobias Gunkel
        2. optionalreferenceconstructor-example.zip
          2 kB
          Tobias Gunkel

        Activity

          People

            tjwatson Tom Watson
            tobgun Tobias Gunkel
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: