Browse user interfaces for Jini services

Build a JFC-based application for browsing and launching Jini services

1 2 3 Page 2
Page 2 of 3

The Factory design pattern is used here because with many UI toolkits, component instantiation is closely tied to the local JVM. For instance, when creating an AWT frame, a native peer is created that builds the actual window on the hosting OS. For that reason, you cannot simply create the window within the service JVM and serialize it across the network to the client. Instead, you create an object on the service side that knows how to instantiate the UI. You then serialize that Factory object and attach it to the service. The client can then download that Factory, and instantiate the UI within the local JVM's context. That is a fundamental feature of the ServiceUI framework.

Our Factory is a JFrameFactory type, meaning that the object we return implements the JFrameFactory interface and by contract implements the getJFrame() method. When getJFrame() is called, the Factory object calls the constructor of the TimeJFrame class and returns it to the client. The code for our Factory is listed below:

   private static class Factory implements JFrameFactory{
      public JFrame getJFrame(Object roleObject){
         try {
            return new TimeJFrame((ServiceItem)roleObject);
         }catch (ClassCastException e) {
            throw new IllegalArgumentException("ServiceItem for TimeService required");

Returning to our initialize_Service(...) method, all the core components of our ServiceUI example are now built. The next few steps are common to the registration of all Jini services. We'll be using some utility classes provided by Sun in the Jini 1.1 release:

         LookupDiscoveryManager jini_Lookup_Discovery = 
            new LookupDiscoveryManager( null,
               new LookupLocator[0],
         LeaseRenewalManager jini_Lease_Manager = new LeaseRenewalManager();
         JoinManager jini_Join_Manager = new JoinManager(proxy,
                new ServiceIDListener(){
                    public void serviceIDNotify(ServiceID id) {
                        System.out.println("Got ServiceID: "+id);
         System.out.println("Registered service");
      }catch (Exception e){
            System.out.println("Problem registering service");

The LookupDiscoveryManager class locates Jini lookup services for us. We have configured it to find lookup services on the local area network, in any group. The next class needed in service registration is the LeaseRenewalManager, which will maintain the service's lease with the lookup service. Finally, the JoinManager works in conjunction with those two classes to register and maintain the registration with all lookup services.

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:

  • jini_: Jini utilities, entries, and other classes relating to services
  • comp_: Swing components
  • util_: Utility classes
  • hybrid_: Classes that combine Swing and Jini functionality

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() {

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:

  • comp_Start_Button: This is the entry point for users to interact with our application.
  • comp_Start_Menu: This JPopupMenu will provide a menu of functions from which users can select, as well as the submenu for launching our ServiceUI services.
  • comp_Active_Service_ToolBar: While we won't be using this component, I've included a JToolBar so that in the future you can manage the windows associated with active services.

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();
      JMenuItem comp_Exit_Menu_Item = new JMenuItem("Shutdown");
      comp_Start_Menu.add(new JMenuItem("Settings"));
      comp_Start_Menu.add(new JMenuItem("Search"));
      setLayout(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      //Configure layout constraints
      //Configure layout constraints

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

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){
      protected synchronized void reload(){
         ServiceItem[] items = jini_Lookup_Cache.lookup(jini_ServiceUI_Filter, 
         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]);
      public void serviceAdded(net.jini.lookup.ServiceDiscoveryEvent evt) {
         SwingUtilities.invokeLater(new Runnable(){
            public void run(){
      public void serviceChanged(net.jini.lookup.ServiceDiscoveryEvent evt) {
         SwingUtilities.invokeLater(new Runnable(){
            public void run(){
      public void serviceRemoved(net.jini.lookup.ServiceDiscoveryEvent evt) {
         SwingUtilities.invokeLater(new Runnable(){
            public void run(){
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];
            }else if(entries[i] instanceof UIDescriptor){
      public void actionPerformed(ActionEvent evt){
            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);
               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.

1 2 3 Page 2
Page 2 of 3