5

I am somewhat new to the OSGi world. And some concepts still elude me.

I'm trying to create a graphical OSGi application using Swing, Equinox and Declarative Services. The goal is to ease the creation of plugins and extensions for the application.

I have stumbled with a design problem and, since I am doing this from the ground up, I want to use all the best practices I can.

I do have a bundle that contains the API and only exposes interfaces to be implemented as services.

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> {
}

Let's say I have a bundle (Core) that provides a service that is a manager of certain types of objects (It basically contains an internal List and adds, removes and updates it).

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)"
      }

}

I also have a second bundle (UI) that takes care of the main UI. It should not "care" for the object managing itself, but should be notified when an object is added, removed or changed in order to update a JTree. For that purpose I used a Whiteboard pattern: The UI bundle implements a service that is used by the Core bundle to fire object change events.

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.
     }

}

My problem here is the following:

The MainWindow is a DS component. I am using its activator to initiate the whole UI. The instance creation is handled by OSGi.

In order to get the updates from the manager, I am exposing the SomeClassUIListener as a Declarative Service. Its instance is also handled by OSGi.

How should I access the instance of the JTree model from the SomeClassUIListener?

I have come up with several options but I am not sure which to use:

Option 1: Use some kind of internal DI system for the UI bundle (like Guice or Pico) and put it in a class with a static method to get it and use it all over the bundle.

This approach seems to be frowned upon by some.

Option 2: Inject a reference to the MainWindow (by turning it into a service) in the SomeClassUIListener through OSGi and go from there. Is this possible or advisable? Seems to me that it is the simpler solution. But, on the other hand, wouldn't this clutter the bundle with component config files as the UI got more and more complex?

Option 3: Create a separate bundle only for listeners and use OSGi to update the MainWindow. This seems to me a bit extreme, as I would have to create an enormous amount of bundles as the UI complexity grows.

Option 4: Use the MainWindow class to implement the Listener. But, the more services in the main UI bundle, the bigger the MainWindow class would be. I think this would not be a good option.

I cannot think of more options. Is any of the above the way to go? Or is there another option?

Thank you in advance.

Edit:

Just to clarify as Peter Kriens had some doubts about this question.

My goal here is to decouple the user interface from the Manager. By Manager I mean a kind of repository in which I store a certain type of objects (For instance, if you consider the Oracle's JTree tutorial at http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html, the manager would contain instances of Books).

The Manager may be used by any other bundle but, according to my current plan, it would notify any listener registered in it. The listener may be the main UI bundle but may also be any other bundle that chooses to listen for updates.

4

3 回答 3

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 注册表中。

于 2013-11-26T16:54:18.407 回答
0

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

public void added(JTree tree, SomeClass obj)

这样,监听器管理器将只负责监听器逻辑,而不是树状态。

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

于 2013-11-26T14:54:29.870 回答
0

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

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

顺便提一句。你打算做什么对我来说听起来有点像数据绑定,所以你还应该调查这些已经提供了什么。请参阅:Swing 数据绑定框架

顺便提一句。您可能还想寻找比 swing 更高级的框架。例如前段时间我为 vaadin 做了一个小教程:https ://github.com/cschneider/Karaf-Tutorial/tree/master/vaadin 它已经有一个 java beans 的数据绑定。所以这让我很容易编写 UI 代码。完整的 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 做好了准备。

于 2013-11-26T23:04:40.140 回答