The Artima Developer Community
Sponsored Link

Browse User Interfaces for Jini Services
Build a JFC-Based Application for Browsing and Launching Jini Services
by Jeffrey Peden
First Published in JavaWorld, March 2001

<<  Page 5 of 7  >>

Advertisement

The Jini TaskBar

The implementation of our TaskBar will use a combination of Swing components and Jini utilities to present the user with a list of Jini services. Our ServiceUI client application will be responsible for reacting to the user and instantiating the appropriate user interface for a selected Jini service.

Throughout the following code, all instances of objects are prefixed according to the type of class they represent:

Our client application has been designed as a Swing component. That lets us potentially reuse the component we have designed in another application. While we could directly subclass a JWindow or JFrame, we instead subclass JPanel and have the flexibility of adding our component to any container.

We will use a number of fields throughout our JiniTaskbar class, so we will define them as instance variables:


public class JiniTaskBar extends javax.swing.JPanel implements ActionListener{  
   protected JButton                   comp_Start_Button;
   protected JPopupMenu                comp_Start_Menu;
   protected JToolBar                  comp_Active_Service_ToolBar;
   protected LookupCache               jini_Lookup_Cache;
   protected ServiceDiscoveryManager   jini_Service_Discovery;
   protected LookupDiscoveryManager    jini_Lookup_Discovery;
   protected LeaseRenewalManager       jini_Lease_Manager;
   protected ServiceUIFilter           jini_ServiceUI_Filter;
   protected ServiceMenu               hybrid_Service_Menu;

Our constructor will delegate responsibility for building the service to other methods that we have defined. That lets you create a functional grouping of steps in creating our client application:


   public JiniTaskBar() {
      initialize_Security_Manager();
      initialize_Codebase_Server();
      initialize_Components();
      initialize_Hybrid_Components();
      initialize_Lookup();
   }

The first two method calls should look familiar. They are identical to the methods in TimeServiceImpl, with the exception that here they are not declared static.

Next, we begin the creation and layout of components that will form the majority of our user interface. We will create three main components here:

Here's the code:


   protected void initialize_Components(){
      comp_Start_Button = new JButton("Jini");
      comp_Start_Menu   = new JPopupMenu("Jini");
      comp_Active_Service_ToolBar=new JToolBar();
        
      comp_Start_Button.setActionCommand("START_BUTTON_ACTION");
      comp_Start_Button.addActionListener(this);
      comp_Active_Service_ToolBar.setFloatable(false);
        
      JMenuItem comp_Exit_Menu_Item = new JMenuItem("Shutdown");
      comp_Exit_Menu_Item.setActionCommand("EXIT_MENU_ITEM_ACTION");
      comp_Exit_Menu_Item.addActionListener(this);
        
      comp_Start_Menu.addSeparator();
      comp_Start_Menu.add(new JMenuItem("Settings"));
      comp_Start_Menu.add(new JMenuItem("Search"));
      comp_Start_Menu.addSeparator();
      comp_Start_Menu.add(comp_Exit_Menu_Item);
        
      setLayout(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      //Configure layout constraints
      ...
      add(comp_Start_Button,gbc);
      //Configure layout constraints
      ...
      add(comp_Active_Service_ToolBar,gbc);
        
      setBorder(BorderFactory.createRaisedBevelBorder());
   }

Our next step is to initialize the JMenu subclass that we have designed to display available Jini services. The method we call, initialize_Hybrid_Components(), is itself fairly simple. It creates a ServiceMenu, which it adds as the first item in comp_Start_Menu:


   protected void initialize_Hybrid_Components(){
      hybrid_Service_Menu = new ServiceMenu("Jini Services");
      comp_Start_Menu.add(hybrid_Service_Menu,0);
   }

The ServiceMenu class has a single method, reload(), which contains the bulk of its added functionality -- the rest is inherited from JMenu. That class also implements net.jini.lookup.ServiceDiscoveryListener to notify it when the set of available Jini services changes. The three methods (serviceAdded(), serviceRemoved(), and serviceChanged()) implemented from the ServiceDiscoveryListener interface call reload().

Notice that because those three methods will be called from threads other than the AWT event queue, we're using the javax.swing.SwingUtilities class to make sure that our operations on Swing components are performed on the proper thread.

I've designed that class as an inner class so that it has access to instance variables inside JiniTaskBar, but you can rewrite the class with minor effort to be used as a standalone class:


   protected class ServiceMenu extends JMenu
                               implements ServiceDiscoveryListener{
      public ServiceMenu(String name){
         super(name);
      }
      protected synchronized void reload(){
         ServiceItem[] items = jini_Lookup_Cache.lookup(jini_ServiceUI_Filter,
                                                        Integer.MAX_VALUE);
         this.removeAll();
         if(items.length==0) add(new JMenuItem("No Services Available"));
         for(int i = 0; i<items.length; i++){
            ServiceUIMenuItem hybrid_Service_Menu_Item = new ServiceUIMenuItem(items[i]);
            add(hybrid_Service_Menu_Item);
         }
      }
      public void serviceAdded(net.jini.lookup.ServiceDiscoveryEvent evt) {
         SwingUtilities.invokeLater(new Runnable(){
            public void run(){
               reload();
            }
         });
      }
      public void serviceChanged(net.jini.lookup.ServiceDiscoveryEvent evt) {
         SwingUtilities.invokeLater(new Runnable(){
            public void run(){
               reload();
            }
         });
      }
      public void serviceRemoved(net.jini.lookup.ServiceDiscoveryEvent evt) {
         SwingUtilities.invokeLater(new Runnable(){
            public void run(){
               reload();
            }
         });
      }
   }


JiniTaskBar showing a list of available
ServiceUI-based Jini services

When reload() is called, it removes all items from the menu and then adds any item in the jini_Lookup_Cache. The jini_Lookup_Cache is an instance of net.jini.lookup.LookupCache, which is a utility class used to maintain a local list of services discovered on the network. The method we call here, lookup(), takes an instance of net.jini.lookup.ServiceItemFilter and the maximum number of items to return.

Our filter checks to make sure that the service has at least one UIDescriptor. If the ServiceItem being checked does not have an associated UIDescriptor entry, our filter returns false, and the service will not be included in the list returned by lookup().


   protected class ServiceUIFilter implements ServiceItemFilter{
      public boolean check(net.jini.core.lookup.ServiceItem item) {
         Entry[] entries = item.attributeSets;
         for(int i = 0; i<entries.length; i++){
            if(entries[i] instanceof UIDescriptor) return true;
         }
         return false;
      }
   }

Once we have our list of services, we can construct the menu that will display them to the user. We'll be wrapping each ServiceItem with another hybrid component, the ServiceUIMenuItem. The ServiceUIMenuItem is a subclass of JMenuItem. Each instance is passed a ServiceItem into the constructor from which it gets the name and the UIDescriptor:


   protected class ServiceUIMenuItem extends JMenuItem
                                     implements ActionListener{
      protected ServiceItem   jini_Service_Item;
      protected String        jini_Service_Name="Unnamed Service";
      protected UIDescriptor  jini_Service_UI_Descriptor;
      public ServiceUIMenuItem(ServiceItem item){
         super("Unknown Service");
         jini_Service_Item = item;
         Entry[] entries = jini_Service_Item.attributeSets;
         for(int i = 0; i<entries.length; i++){
            if(entries[i] instanceof Name){
               net.jini.lookup.entry.Name name = (Name)entries[i];
               jini_Service_Name=name.name;
               setName(jini_Service_Name);
               setLabel(jini_Service_Name);
            }else if(entries[i] instanceof UIDescriptor){
               jini_Service_UI_Descriptor=(UIDescriptor)entries[i];
            }
         }
         addActionListener(this);
      }
      public void actionPerformed(ActionEvent evt){
         if(jini_Service_UI_Descriptor!=null)try{
            MarshalledObject marshalled_Factory = jini_Service_UI_Descriptor.factory;
            Object unmarshalled_Factory = marshalled_Factory.get();
            if(unmarshalled_Factory instanceof JFrameFactory){
               JFrameFactory frame_Factory = (JFrameFactory)unmarshalled_Factory;
               JFrame service_Frame = frame_Factory.getJFrame(jini_Service_Item);
               service_Frame.show();
            }else{
               System.out.println("Unsupported UI Type");
            }
         }catch(Exception e){
                
         }
      }
   }

The ServiceUIMenuItem class also implements java.awt.event.ActionListener and registers to receive events. When the user selects a ServiceUIMenuItem from the service menu, the actionPerformed() method is called. That method retrieves the Factory object from the UIDescriptor entry. The Factory is wrapped in a java.rmi.MarshalledObject, and it must be unwrapped by first calling the get() method.

Once we have the Factory object, we check to see what kind of factory it is. Currently, we are only dealing with JFrameFactory instances, but it should be a simple exercise to implement handling code for other types of ServiceUI factories.

With the Factory now available to us, we can call getJFrame(), passing in the ServiceItem as an argument. The ServiceUI 1.0 spec refers to that argument as the role object, and suggests that the ServiceItem for the service be passed in for MainUI and AdminUI roles. getJFrame() constructs and returns an instance of a JFrame or a subclass of JFrame. The only step left at that point is to call show() on the returned UI.

You should be aware that the process of unmarshalling the Factory and instantiating the UI could result in loading a number of classes from the service's codebase. Since we are reacting to an ActionEvent, the actionPerformed() method is being called on the AWT event thread. That means that while we are loading classes and instantiating them, the JVM will be unable to react to other requests. For that example system, that is acceptable, but for any production system, you should be performing complex tasks on a separate thread and then calling show() on the AWT event queue.

<<  Page 5 of 7  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2017 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us