Статьи

Экспорт конечных точек Camel в качестве сервисов OSGi

Доступ к сервисам OSGi через шину

Часто полезно использовать прокси-вызовы для служб OSGi через служебную шину.

Шина может использоваться для регистрации входящих вызовов, адаптации / фильтрации аргументов вызова и результата, прозрачной пересылки запросов в удаленный контейнер OSGi, широковещательной передачи в несколько удаленных контейнеров или пересылки на более мощные конечные точки Fabric8.

Экспорт обычного интерфейса в конечную точку Camel является ключом к этой функциональности.

Рисунок 1: Прямая привязка сервиса


Рисунок 2: Привязка к верблюду прокси

Заклинание верблюжья фасоль

Верблюд позволяет обернуть вызовы к интерфейсам в объектах BeanInvocation внутри бирж, а затем отправить их через конечные точки, такие как JMS, HTTP и т. Д.

Верблюд поддерживает 3 подхода к вызову бина:

  • Боб компонент , который вызывает компонент в реестре фасоли (например , Blueprint)
  • Создание POJO, когда прокси, которые автоматизируют вызов компонента, вводятся посредством аннотаций.
  • Spring remoting, где прокси, используемые при создании POJO, создаются явно.

Пружина Remoting

Мы будем использовать Spring remoting для явного создания прокси служб, связанных с конечными точками Camel, и экспортировать эти прокси как сервисы OSGi в контейнере Blueprint.

Учитывая следующий интерфейс:

interface TaskManager {
    Task save(Task task);
    void delete(String id);
    Task find(String id);
}

The Blueprint container implementing the TaskManager service makes it available through Camel endpoint activemq:queue:io.modio.blog.osgi.camel.proxy:

<blueprint default-activation="eager"
    xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     
    <bean id="taskManager"
        class="io.modio.blog.osgi.camel.proxy.impl.TaskManagerImpl"/>
 
    <camelContext id="camelContext"
        xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="activemq:queue:io.modio.blog.osgi.camel.proxy"/>
            <to uri="bean:taskManager"/>
        </route>
    </camelContext>
 
</blueprint>

The proxy to the TaskManager service can be in the same or a different Blueprint container:

<blueprint default-activation="eager"
    xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
    <camelContext id="camelContext"
        xmlns="http://camel.apache.org/schema/blueprint">
        <proxy id="taskManagerProxy"
            serviceUrl="activemq:queue:io.modio.blog.osgi.camel.proxy"
            serviceInterface="io.modio.blog.osgi.camel.proxy.TaskManager"/>
    </camelContext>
     
    <service interface="io.modio.blog.osgi.camel.proxy.TaskManager"
        ref="taskManagerProxy"/>
 
</blueprint>

Alternatively if the proxy is defined outside the camel context then the blueprint container and camel context ID need to be set explicitly and afterPropertiesSet() needs to be called:

<blueprint default-activation="eager"
    xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
    <camelContext id="camelContext"
        xmlns="http://camel.apache.org/schema/blueprint">
    </camelContext>
     
    <bean id="taskManagerProxyFactory" init-method="afterPropertiesSet"
        class="org.apache.camel.blueprint.CamelProxyFactoryBean">
        <property name="serviceUrl"
             value="activemq:queue:io.modio.blog.osgi.camel.proxy"/>
        <property name="serviceInterface"
            value="io.modio.blog.osgi.camel.proxy.TaskManager"/>
        <property name="camelContextId" value="camelContext"/>
        <property name="blueprintContainer" ref="blueprintContainer"/>
    </bean>
    
    <service interface="io.modio.blog.osgi.camel.proxy.TaskManager">
        <bean factory-ref="taskManagerProxyFactory" factory-method="getObject"/>
    </service>
 
</blueprint>

The Blueprint container on the consuming side is:

<blueprint default-activation="eager"
    xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
    <reference id="taskManager"
        interface="io.modio.blog.osgi.camel.proxy.TaskManager"/>
     
    <bean id="driver" class="io.modio.blog.osgi.camel.proxy.Driver">
        <property name="taskManager" ref="taskManager"/>
    </bean>
         
</blueprint>

Blueprint service reference semantics

As powerful as it is, this technique also has a notable side effect. Placing the proxy in a different Blueprint container than the service implementation breaks the semantics of Blueprint service references: the proxy service may be available while the real service may be not.

This will affect both the bootstrapping of the service consumer Blueprint container and the semantics of the calls to the service when the underlying service is not available:

   Direct Service Binding Binding to a Camel Proxy 
 Service is unavailable during initialization phase.

The blueprint container initialization will block until the required service becomes available. The blueprint container will complete initialization and bind to the proxy.
 Service becomes unavailable after initialization phase. Call to the service reference will block until the service becomes available again or a timeout occurs in which case a ServiceUnavailableException exception is thrown. The blueprint container will call the proxy. The behavior of the call will depend on the Camel routing setup. E.g.

  • An asynchronous call that posts to a queue will complete (however the message will be left in the queue until the backend service is available to consume it).
  • A synchronous call that posts to a queue expecting a reply will block.
  • A call that delegates to a REST API that is not available, will result in an exception.

Blueprint’s approach to service references has received a lot of criticism recently in favor of declarative services (DS). E.g. check the discussions regarding the migration of Fabric8 from Blueprint to DS or the (expected?) migration of Apache Karaf core 4.x from Blueprint to DS. 
However, in this particular case, DS is also affected because communication through the bus bypasses the OSGi service lookup.