2016-08-10 4 views
0

Я работаю над приложением, которое предназначено для расширения пользователем. Он основан на OSGi (Equinox) и активно использует декларативные услуги (DS). Пакеты, установленные клиентами, предоставляют свои собственные сервисные реализации, которые затем использует мое приложение. Нет ограничений на количество реализаций услуг, которые могут предоставить пакеты, специфичные для клиента.OSGi: убедитесь, что все расширения загружены в приложении декларативных услуг

Есть ли способ гарантировать, что при выполнении основной функции приложения были зарегистрированы все службы, предоставляемые клиентом?

Для уточнения, предположим, что мое приложение состоит из одного компонента DS RunnableRunner:

public class RunnableRunner 
{ 
    private final List<Runnable> runnables = new ArrayList<Runnable>(); 

    public void bindRunnable(Runnable runnable) 
    { 
     runnables.add(runnable); 
    } 

    public void activate() 
    { 
     System.out.println("Running runnables:"); 
     for (Runnable runnable : runnables) { 
      runnable.run(); 
     } 
     System.out.println("Done running runnables."); 
    } 
} 

Этот компонент регистрируется с помощью component.xml DS, таких как следующие:

<?xml version="1.0" encoding="UTF-8"?> 
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="RunnableRunner" activate="activate"> 
    <implementation class="RunnableRunner"/> 
    <reference bind="bindRunnable" interface="java.lang.Runnable" name="Runnable" 
       cardinality="0..n" policy="dynamic"/> 
</scr:component> 

I понимаем, что нет гарантии, что в момент времени вызывается activate(), все Управляемые были связаны. Фактически, эксперименты, выполненные с помощью Eclipse/Equinox, указывают на то, что время выполнения DS не сможет связывать Runnables, внесенные другим пакетом, если этот пул запускает после основного пакета (что является вероятностью 50/50, если только явное начало уровни используются).

Итак, какие альтернативы мне доступны? Как я могу убедиться, что контейнеры OSGi стараются изо всех сил решить все зависимости до активации RunnableRunner?

Альтернативы я уже думал о том:

  • Bundle начинают уровни: слишком грубые (они работают на уровне связки, а не на уровне компонентов), а также ненадежных (они только приняты как намек по OSGi)
  • Исправление к точкам расширения Eclipse: слишком специфичное для Eclipse, трудно сочетающееся с Declarative Services.
  • Выполнение RunnableRunner динамически перенастраивается при регистрации нового Runnable: невозможно, в какой-то момент я должен выполнить все Runnables в последовательности.

Любые советы по обеспечению того, чтобы какое-либо расширяемое обслуживание было «готовым» до его использования?

ответ

0

Если вы не знаете, какие расширения вам нужно запустить, вы можете сделать только динамический компонент. Затем вы реагируете на каждое расширение, когда оно добавлено.

Если вам нужно убедиться, что ваши расширения были собраны до того, как может произойти какой-то дополнительный шаг, вы можете использовать имена для своих необходимых расширений и называть их в конфиге.

Так, например, у вас может быть свойство конфигурации «расширения», в котором перечислены все имена расширений, пропущенные пробелами. Каждое расширение должно иметь свойство службы, например «имя». В своем компоненте вы затем сравниваете расширения, которые вы нашли, с необходимыми расширениями по имени. Затем вы выполняете свою «активацию» только тогда, когда присутствуют все необходимые расширения.

Это, например, используется в CXF DOSGi для применения намерений на службе, как указано в спецификации администратора удаленной службы.

+0

Спасибо за предложение листинга необходимых расширений в свойстве конфигурации. Проблема, которую я вижу при таком подходе, заключается в том, что клиенту требуется не только установка нового пакета, но и редактирование центрального файла конфигурации. Я бы предпочел, чтобы необходимая информация была предоставлена ​​отдельными пакетами расширения децентрализованным способом. –

+0

Как и Питер, также предлагает, хотите ли вы гарантировать, что все ваши расширения присутствуют, вам нужно указать какое-то условие для него. Это невозможно решить полностью. –

2

На сегодняшний день лучшим способом является не забота и дизайн вашей системы, чтобы она протекала правильно. Есть много причин, по которым служба появляется и исчезает, поэтому любой мираж стабильности именно это. Неисполнение реальных условий создает хрупкие системы.

В вашем примере, почему нельзя использовать RunnableRunner для каждого сервиса Runnable по мере его поступления? Следующий код полностью OSGi динамический известно:

@Component 
public class RunnableRunner { 

    @Reference Executor executor; 

    @Reference(policy=ReferencePolicy.DYNAMIC) 
    void addRunnable(Runnable r) { 
    executor.execute(r); 
    } 
} 

Я ожидаю, что вы нашли это неправильно по причине вы не указали. Эта причина заключается в том, что вы должны пытаться выразить как регистрацию услуг.

Если у вас есть (редкие) варианты использования, где вам абсолютно необходимо знать, что доступны все (что бы это ни значило) услуги, вы могли бы подсчитать количество экземпляров или использовать какое-либо другое условие. В OSGi с DS подход заключается в том, чтобы превратить это условие в сервис, чтобы другие могли зависеть от него, и вы получаете все гарантии, предоставляемые службами.

В этом случае просто создайте компонент, учитывающий количество экземпляров. Используя конфигурацию, вы регистрируете услугу Ready, как только достигнете определенного счета.

public interface Ready {} 

@Component 
@Designate(Config.class) 
public class RunnableGuard { 
    @ObjectClass 
    @interface Config { 
     int count(); 
    } 
    int count = Integer.MAX_VALUE; 
    int current; 
    ServiceRegistration<Ready> registration; 

    @Activate 
    void activate(Config c, BundleContext context) { 
     this.context = context; 
     this.count = c.count(); 
     count(); 
    } 
    @Deactivate void deactivate() { 
     if (registration != null) 
     registration.unregister(); 
    } 

    @Reference 
    void addRunnable(Runnable r) { 
    count(1); 
    } 
    void removeRunnable(Runnable r) { 
     count(-1); 
    } 

    synchronized void count(int n) { 
     this.current += n; 
     if (this.current >= count && registration==null) 
     registration = context.registerService( 
      Ready.class, new Ready() {}, null 
     ); 
     if (this.current < count && registration != null) { 
     registration.unregister(); 
     registration = null; 
     } 
    } 
} 

Ваш RunnableRunner будет выглядеть:

@Component 
public class RunnableRunner { 

    @Reference volatile List<Runnable> runnables; 

    @Reference Ready ready; 

    @Activate void activate() { 
    System.out.println("Running runnables:"); 
    runnables.forEach(Runnable::run); 
    System.out.println("Done running runnables."); 
    } 
} 

Довольно хрупкий код, но иногда это единственный вариант.

я не знаю, что было до сих пор люди пишут XML ... мое сердце кровоточит для вас :-)

+0

Спасибо за совет, Питер! Вы правы, я четко не указал, почему я не могу сделать динамический RunnableRunner, как вы предложили. Причина в том, что в реальном программном обеспечении запускается процесс с (реальными) побочными эффектами. Это одноразовый, и это важно для правильного. Механизм подсчета хорош, но, к сожалению, я не знаю фактического количества зависимостей заранее. –

+0

Счет был всего лишь примером состояния ... Вы должны точно определить, каково ваше фактическое состояние, и затем превратить это условие в сервис. Это то, что я пытался показать. Это обычная модель в OSGi: найдите свое состояние, превратите его в сервис, а затем зависните от него. Делает вещи явными. –

+0

Почему вы синхронизировали 'deactivate' и' count'? дезактивация и инъекция ссылок могут происходить одновременно? –

Смежные вопросы