OSGi UI应用程序中的最佳实践

时间:2013-11-26 13:53:10

标签: java swing osgi equinox declarative-services

我对OSGi世界有些新意。还有一些概念让我望而却步。

我正在尝试使用Swing,Equinox和Declarative Services创建图形OSGi应用程序。目标是简化应用程序的插件和扩展的创建。

我遇到了设计问题,因为我从头开始这样做,所以我想尽可能使用所有最佳实践。

我有一个包含API的包,只公开要实现为服务的接口。

public class SomeClass {
}

public interface Manager<T> {
     void add(T obj);
     void update(T obj);
     void remove(T obj);
}

public interface SomeClassManager extends Manager<SomeClass> {
}

public interface Listener<T> {
    void added(T obj);
    void updated(T obj);
    void removed(T obj);
}

public interface SomeClassListener extends Listener<SomeClass> {
}

假设我有一个捆绑包(核心),它提供的服务是某些类型对象的管理器(它基本上包含一个内部列表并添加,删除和更新它)。

public class SomeClassCoreManager implements SomeClassManager {

      private ArrayList<SomeClass> list = new ArrayList<SomeClass>();
      private ArrayList<SomeListener> listeners = new ArrayList<SomeListener>();

      protected void bindListener(SomeListener listener) {
            listeners.add(listener); 
      }

      protected void undindListener(SomeListener listener) {
            listeners.remove(listener);
      }

      public void add(SomeClass obj) {
          // Adds the object to the list
          // Fires all the listeners with "added(obj)"
      }


      public void update(SomeClass obj) {
          // Updates the object in the list.
          // Fires all the listeners with "updated(obj)"
      }

      public void remove(SomeClass obj) {
          // Removes the object from the list.
          // Fires all the listeners with "removed(obj)"
      }

}

我还有第二个包( UI )来处理主UI。它不应该“关心”管理自身的对象,但是在添加,删除或更改对象以更新JTree时应该通知它。为此,我使用了一个Whiteboard模式:UI包实现了一个服务,Core bundle使用该服务来触发对象更改事件。

public class MainWindow extends JFrame {

     private JTree tree = new JTree();
     private SomeClassManager manager;

     protected void activate() {
          // Adds the tree and sets its model and creates the rest of the UI.
     }

     protected void bindManager(SomeClassManager manager) {
          this.manager = manager;
     }

     protected unbindManager(SomeClassManager manager) {
          this.manager = null;
     }
}

public class SomeClassUIListener implements SomeClassListener {
     public void added(SomeClass obj) {
          // Should add the object to the JTree.
     }

     public void updated(SomeClass obj) {
          // Should update the existing object in the JTree.
     }

     public void removed(SomeClass obj) {
          // Should remove the existing object from the JTree.
     }

}

我的问题如下:

MainWindow是一个DS组件。我正在使用它的激活器来启动整个UI。实例创建由OSGi处理。

为了从管理器获取更新,我将SomeClassUIListener公开为声明式服务。它的实例也由OSGi处理。

我应该如何从SomeClassUIListener访问JTree模型的实例?

我提出了几个选项,但我不确定使用哪个选项:

选项1: 使用某种内部DI系统作为UI包(如Guice或Pico),并将其放在一个静态方法的类中,以获取它并在整个包中使用它。

这种方法似乎被一些人不赞成。

选项2: 通过OSGi在SomeClassUIListener中注入对MainWindow的引用(通过将其转换为服务)并从那里开始。这是可行的还是可取的?在我看来,这是更简单的解决方案。但是,另一方面,随着UI变得越来越复杂,这会不会使组件配置文件变得混乱?

选项3: 仅为侦听器创建单独的包,并使用OSGi更新MainWindow。这在我看来有点极端,因为随着UI复杂性的增加,我将不得不创建大量的包。

选项4: 使用MainWindow类实现Listener。但是,主UI包中的服务越多,MainWindow类就越大。我认为这不是一个好的选择。

我想不出更多选择。以上任何一种方式都可以吗?或者还有其他选择吗?

提前谢谢。

修改

只是澄清彼得克里恩斯对这个问题有些怀疑。

我的目标是将用户界面与Manager分离。通过 Manager 我的意思是一种存储库,我在其中存储某种类型的对象(例如,如果你在http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html考虑​​Oracle的JTree教程,那么管理器将包含Books的实例)

管理员可能被任何其他捆绑使用,但根据我目前的计划,它会通知在其中注册的任何听众。侦听器可以是主UI包,但也可以是选择侦听更新的任何其他包。

3 个答案:

答案 0 :(得分:1)

我不确定我是否完全掌握了您的建议,感觉您正在创建一整套基础架构。在OSGi中,这通常没有必要,所以为什么不从小而简单开始。

您的基本型号是经理和扩展。这是领域模型,我会尝试在这里流动:

@Component(immediate)
public class ManagerImpl { // No API == immediate
   List<Extension>  extensions  = new CopyOnWriteArrayList<Extension>();
   JFrame frame = new JFrame();

   @Reference(cardinality=MULTIPLE) 
   void addExtension( Extension e ) {
       addComponent(frame, e.getName(), e.getComponent());
       extensions.add(e);
   }

   void removeExtension( Extension e) {
     if ( extensions.remove(e) ) {
        removeComponent(frame, e.getName());
   }
 }

 @Component 
 public class MyFirstExtension implements Extension {
    public String getName() { return "My First Extension";}
    public Component getComponent() { return new MyFirstExtensionComponent(this); }
 }

这不是你要找的吗?要非常小心不要创建各种类型的侦听器,通常你会发现OSGi注册表中已有的事件。

答案 1 :(得分:0)

这里的一些选项是将树模型实例作为侦听器方法中的参数传递。

public void added(JTree tree, SomeClass obj)

这种方式,侦听器管理器只负责侦听器逻辑,而不是树状态。

另一个不错的选择是创建一个独立的TreeProviderService,负责为应用程序保存和提供单例JTree实例。在这种情况下,您将直接从侦听器中使用TreeProviderService

答案 2 :(得分:0)

我建议简单地使用DS来创建和连接UI。如果您使用Peter提到的注释,您将不会使用XML格式的组件描述符来混乱您的bundle。

因此,您的侦听器是@Component,并且您需要将更新所需的UI元素注入其中。

顺便说一下。你打算做什么听起来有点像数据绑定给我,所以你也应该调查这些提供的内容。 请参阅:Swing data binding frameworks

顺便说一下。您可能还想寻找比swing更高级的框架。例如,前段时间我为vaadin做了一个小教程:https://github.com/cschneider/Karaf-Tutorial/tree/master/vaadin 它已经有了java bean的数据绑定。所以这使我很容易编写UI代码。完整的用户界面只是这个小课程:https://github.com/cschneider/Karaf-Tutorial/blob/master/vaadin/tasklist-ui-vaadin/src/main/java/net/lr/tutorial/karaf/vaadin/ExampleApplication.java

在旧版本中,我仍然需要一个桥接器来在OSGi中运行vaadin,但版本7应该是OSGi准备就绪。