简体   繁体   中英

Best Practice in an OSGi UI application

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.

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.

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.

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. In such case you would consume TreeProviderService directly from the listeners.

I propose to simply also use DS for the UI creation and wiring. If you use the annotations Peter mentioned you will not clutter your bundles with component descriptors in XML form.

So your listener is a @Component and you inject the UI elements it needs to update into it.

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

Btw. you may also want to look for more advanced frameworks than 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. So this made it really easy for me to code the 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

In the old version I still needed a bridge to run vaadin in OSGi but version 7 should be quite OSGi ready.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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