Uploaded image for project: 'Camel'
  1. Camel
  2. CAMEL-11810

Lifecycle problems for services retrieved from Blueprint container

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Minor
    • Resolution: Won't Fix
    • None
    • None
    • camel-blueprint
    • None
    • Unknown

    Description

      I have a case where one bundle/blueprint-container defines:

          <bean id="messageIdRepositoryImpl" class="org.apache.camel.processor.idempotent.MemoryIdempotentRepository"/>
      
          <service id="messageIdRepository" interface="org.apache.camel.spi.IdempotentRepository" ref="messageIdRepositoryImpl" />
      

      and another one defines Camel context with:

          <reference id="messageIdRepository" interface="org.apache.camel.spi.IdempotentRepository"/>
          <camelContext xmlns="http://camel.apache.org/schema/blueprint">
      
              <route id="xx">
      
                  <from uri="file://xxx?y=z&amp;readLock=idempotent&amp;idempotent=true&amp;idempotentRepository=#messageIdRepository&amp;other.properties[...]" />
      

      The problem is that when bundle defining messageIdRepositoryImpl is stopped, stopping bundle/blueprint-container with camel context that references messageIdRepositoryImpl leads to wait on Proxy/ReferenceRecipe:

      org.osgi.service.blueprint.container.ServiceUnavailableException: Timeout expired when waiting for mandatory OSGi service reference: (objectClass=org.apache.camel.spi.IdempotentRepository)
      	at org.apache.aries.blueprint.container.ReferenceRecipe.getService(ReferenceRecipe.java:234)
      	at org.apache.aries.blueprint.container.ReferenceRecipe.access$000(ReferenceRecipe.java:56)
      	at org.apache.aries.blueprint.container.ReferenceRecipe$ServiceDispatcher.call(ReferenceRecipe.java:306)
      	at Proxy28f0d520_9465_4682_9ec1_02ae44e9fa4a.toString(Unknown Source)
      	at java.lang.String.valueOf(String.java:2994)[:1.8.0_144]
      	at java.lang.StringBuilder.append(StringBuilder.java:131)[:1.8.0_144]
      	at org.apache.camel.impl.DefaultCamelContext.shutdownServices(DefaultCamelContext.java:3214)
      	at org.apache.camel.impl.DefaultCamelContext.shutdownServices(DefaultCamelContext.java:3234)
      	at org.apache.camel.impl.DefaultCamelContext.shutdownServices(DefaultCamelContext.java:3222)
      	at org.apache.camel.impl.DefaultCamelContext.doStop(DefaultCamelContext.java:3101)
      	at org.apache.camel.support.ServiceSupport.stop(ServiceSupport.java:102)
      	at org.apache.camel.blueprint.BlueprintCamelContext.destroy(BlueprintCamelContext.java:129)
      ...
      

      There are few problems here. First - GenericFileEndpoint.doStart() does this:

      if (idempotentRepository != null) {
          getCamelContext().addService(idempotentRepository, true);
      }
      

      which adds the blueprint recipe (proxy) to org.apache.camel.impl.DefaultCamelContext#servicesToStop without a way to specify stopOnShutdown=false.

      IMO services obtained from OSGi registry should not be tied to lifecycle of single context with route having #referenceToOsgiOrBlueprintService in endpoint URI.

      Also - the above stack trace is not retrieved when stopping an osgi service, but when log.warning an exception!:

      log.warn("Error occurred while shutting down service: " + service + ". This exception will be ignored.", e);
      

      In the above code we have implicit toString() call on service which leads to another proxy call which calls org.apache.aries.blueprint.container.ReferenceRecipe.ServiceDispatcher#call()org.apache.aries.blueprint.container.ReferenceRecipe#getService()org.apache.aries.blueprint.container.ReferenceRecipe#monitor.wait(metadata.getTimeout().

      The most obvious fix is to move idempotent repository to a blueprint file where file endpoint is used.

      But as OSGi devil's advocate, I suggest more generic solution: when looking up objects inside org.apache.camel.blueprint.BlueprintContainerRegistry, in addition to calling org.osgi.service.blueprint.container.BlueprintContainer#getComponentInstance() we may check if blueprintContainer.getComponentMetadata(name) instanceof ReferenceMetadata and in such case return a proxied proxy that would prevent waiting for blueprint reference when we're stopping the context - we'd have to check if the target service is available anyway (why ReferenceRecipe doesn't have a method like "tryGetService()"?)...

      Less generic fix could be to add a flag used instead of default:

      // idempotent repository may be used by others, so add it as a service so its stopped when CamelContext stops
      if (idempotentRepository != null) {
          getCamelContext().addService(idempotentRepository, true);
      }
      

      so user could decide whether idempotent repository is or isn't "external" ("shared").

      Attachments

        Issue Links

          Activity

            People

              gnodet Guillaume Nodet
              ggrzybek Grzegorz Grzybek
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: