简体   繁体   English

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

[英]Best Practice in an OSGi UI application

I am somewhat new to the OSGi world. 我对OSGi世界有些新意。 And some concepts still elude me. 还有一些概念让我望而却步。

I'm trying to create a graphical OSGi application using Swing, Equinox and Declarative Services. 我正在尝试使用Swing,Equinox和Declarative Services创建一个图形OSGi应用程序。 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. 我有一个包含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> {
}

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). 假设我有一个bundle( Core ),它提供的服务是某些类型对象的管理器(它基本上包含一个内部List并添加,删除和更新它)。

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. 我还有第二个包( UI )来处理主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. 它不应该“关心”管理自身的对象,但是在添加,删除或更改对象以更新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. 为此,我使用了一个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.
     }

}

My problem here is the following: 我的问题如下:

The MainWindow is a DS component. MainWindow是一个DS组件。 I am using its activator to initiate the whole UI. 我正在使用它的激活器来启动整个UI。 The instance creation is handled by OSGi. 实例创建由OSGi处理。

In order to get the updates from the manager, I am exposing the SomeClassUIListener as a Declarative Service. 为了从管理器获取更新,我将SomeClassUIListener公开为声明式服务。 Its instance is also handled by OSGi. 它的实例也由OSGi处理。

How should I access the instance of the JTree model from the SomeClassUIListener? 我应该如何从SomeClassUIListener访问JTree模型的实例?

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. 选项1:为UI包(如Guice或Pico)使用某种内部DI系统,并将其放在具有静态方法的类中以获取它并在整个包中使用它。

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. 选项2:通过OSGi在SomeClassUIListener中注入对MainWindow的引用(通过将其转换为服务)并从那里开始。 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? 但是,另一方面,随着UI变得越来越复杂,这不会使组件配置文件变得混乱吗?

Option 3: Create a separate bundle only for listeners and use OSGi to update the MainWindow. 选项3:仅为侦听器创建单独的包,并使用OSGi更新MainWindow。 This seems to me a bit extreme, as I would have to create an enormous amount of bundles as the UI complexity grows. 这在我看来有点极端,因为随着UI复杂性的增长,我将不得不创建大量的bundle。

Option 4: Use the MainWindow class to implement the Listener. 选项4:使用MainWindow类实现监听器。 But, the more services in the main UI bundle, the bigger the MainWindow class would be. 但是,主UI包中的服务越多,MainWindow类就越大。 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. 只是为了澄清Peter Kriens对这个问题有些怀疑。

My goal here is to decouple the user interface from the Manager. 我的目标是将用户界面与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). 通过管理器我的意思是一种存储库,我在其中存储某种类型的对象(例如,如果您在http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html上考虑Oracle的JTree教程,经理将包含图书的实例)。

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. 侦听器可以是主UI包,但也可以是选择侦听更新的任何其他包。

I am not sure I completely grasp your proposal, and it feels like you are on your way to create a whole load of infrastructure. 我不确定我是否完全掌握了您的建议,感觉您正在创建一整套基础架构。 In OSGi this is generally not necessary, so why not start small and simple. 在OSGi中,这通常不是必需的,所以为什么不从小而简单开始。

Your basic model is a manager and an extension. 您的基本模型是经理和扩展。 This is the domain model and I would try to flow things around here: 这是域模型,我会尝试在这里流动:

@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); }
 }

Isn't this what you're looking for? 这不是你想要的吗? Be very careful not to create all kinds of listeners, in general you find the events already in the OSGi registry. 要非常小心不要创建各种类型的侦听器,通常你会发现OSGi注册表中已有的事件。

Some option here would be to pass the tree model instance as the argument in the listeners methods. 这里的一些选项是将树模型实例作为侦听器方法中的参数传递。

public void added(JTree tree, SomeClass obj)

This way listeners manager would be responsible only for listeners logic, not for the tree state. 这种方式,侦听器管理器只负责侦听器逻辑,而不是树状态。

Another nice option would be to create a separated TreeProviderService , responsible for holding and serving singleton JTree instance for the application. 另一个不错的选择是创建一个单独的TreeProviderService ,负责为应用程序保存和提供单例JTree实例。 In such case you would consume TreeProviderService directly from the listeners. 在这种情况下,您将直接从侦听器使用TreeProviderService

I propose to simply also use DS for the UI creation and wiring. 我建议简单地使用DS来创建和连接UI。 If you use the annotations Peter mentioned you will not clutter your bundles with component descriptors in XML form. 如果您使用Peter提到的注释,您将不会使用XML格式的组件描述符来混乱您的bundle。

So your listener is a @Component and you inject the UI elements it needs to update into it. 因此,您的侦听器是@Component,并且您需要将更新所需的UI元素注入其中。

Btw. 顺便说一句。 what you plan to do sounds a bit like databinding to me so you should also investigate what these offer already. 你打算做什么听起来有点像数据绑定给我,所以你也应该调查这些提供的内容。 See: Swing data binding frameworks 请参阅: Swing数据绑定框架

Btw. 顺便说一句。 you may also want to look for more advanced frameworks than swing. 您可能还想寻找比swing更高级的框架。 For example some time ago I did a small tutorial for vaadin: https://github.com/cschneider/Karaf-Tutorial/tree/master/vaadin It already has a databinding for java beans. 例如前段时间我为vaadin做了一个小教程: https//github.com/cschneider/Karaf-Tutorial/tree/master/vaadin它已经有了java bean的数据绑定。 So this made it really easy for me to code the UI. 所以这使我很容易编写UI代码。 The full UI is just this little class: https://github.com/cschneider/Karaf-Tutorial/blob/master/vaadin/tasklist-ui-vaadin/src/main/java/net/lr/tutorial/karaf/vaadin/ExampleApplication.java 完整的UI就是这个小类: https//github.com/cschneider/Karaf-Tutorial/blob/master/vaadin/tasklist-ui-vaadin/src/main/java/net/lr/tutorial/karaf/vaadin /ExampleApplication.java

In the old version I still needed a bridge to run vaadin in OSGi but version 7 should be quite OSGi ready. 在旧版本中,我仍然需要一个桥接来在OSGi中运行vaadin,但版本7应该是OSGi准备就绪。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM