Writing Client Plug-ins

In This Section:

Access Point for Plug-ins

Class Packages

How the Client Locates Plug-ins

Creating the Miscellaneous Handler Class

Adding Functionality

Standard Controls

Administration Services Console Services

Internationalization

Packaging the Plug-in

This chapter explains how to write a plug-in for Administration Services Console. Plug-ins are the mechanism for extending the functionality of Administration Services Console.

Access Point for Plug-ins

The implementation of the Administration Services client is contained in the eas_client.jar and framework_client.jar files that are installed with Administration Services. Additional classes are found in the eas_common.jar and framework_common.jar files. The Essbase plug-in to Administration Services Console is contained in the essbase_common.jar and essbase_client.jar files.

Class Packages

Administration Services Console consists of several packages. The public classes in these packages are available to the implementor of plug-ins. In particular, the user interface, print, and mail-related classes. For detailed information about the packages and classes described in Table 1, see the Administration Services Java API Reference.

Table 1. Administration Services Console Class Packages  

Package or Class Name

Description

com.essbase.eas.client.intf

The classes and interfaces that provide an interface to the console

com.essbase.eas.client. manager

The classes that provide “management” services for parts of the console; such as, LoginManager, CommandManager, ConsoleManager, and so on

com.essbase.eas.client. plugins

The classes that the client framework uses to install plug-ins, track plug-ins, and so on

com.essbase.eas.framework.client.defs.command

The client-specific classes related to sending commands to the mid-tier. As of Release 7.1, this consists only of the UICommandManager class.

com.essbase.eas.framework.client.defs.login

This is the default login dialog box provided by the console. It displays if no plug-in has registered a different login dialog or if any command is sent to the Administration Services mid-tier and a mid-tier server name has not been provided.

com.essbase.eas.framework.client.ui.filedlgs

Implements dialog boxes associated with a file menu. For example, New, Open, Save As

com.essbase.eas.ui

Another package with several user interface components used by the console and by the Essbase plug-in

com.essbase.eas.ui.ctable

An implementation of a standard extension to the JTable control

com.essbase.eas.ui.ctree

An implementation of an extension to the JTree control. This is the control that is used in the Enterprise Tree and in the custom views of the console.

com.essbase.eas.ui.editor

An implementation of a standard text editor with syntax highlighting. This control is used as the base class for the calculation script editor, MaxL editor, and report script editor in the Essbase plug-in.

com.essbase.eas.ui.email

An implementation of some e-mail related classes. The framework provides a service for sending e-mail; this package contains the implementation of the service.

com.essbase.eas.ui.font

The classes that provide the font-related utility

com.essbase.eas.ui.print

The classes that provide the print-related utility

com.essbase.eas.ui.ptable

An extension to the JTable control for editing properties. This table provides extensive editing, sorting capabilities, and is used by many windows and dialogs in the Essbase plug-in.

com.essbase.eas.ui.ptree

An extension to the JTree control for editing tree-oriented properties. This tree provides extensive editing capabilities and is used by many windows and dialogs in the Essbase plug-in.

com.essbase.eas.ui.tree

The generic utility routines for working with JTree-based controls

com.essbase.eas.framework.defs

This package and the packages under it provide services for transferring commands from the mid-tier to the client, packaging/unpackaging data to be transferred, a logging mechanism, and so on

com.essbase.eas.i18n

The internationalization utility classes

com.essbase.eas.utils

Various utility classes spanning a range of uses: file utilities, compression, encryption, array utilities, and so on

com.essbase.eas.utils.print

Utility classes dealing with printing

How the Client Locates Plug-ins

The client tracks plug-ins by maintaining a list of jar files that the user has selected using the Configure Plugin Components dialog box. To display this dialog box, from Administration Services Console, select Tools, and then Configure components.

When a jar file is selected, the dialog scans through each package in the jar file looking for a class called MiscellaneousHandler.class. When a class with this name is found, the jar file name and the package name containing that class file are retained by the plug-in manager. Therefore, each jar file must contain exactly one package with a MiscellaneousHandler class in it.

When Administration Services Console starts, the plug-in manager scans each jar file in its stored list, looking for the MiscellaneousHandler.class file in the specified package. If this class is found, the plug-in manager adds this plug-in to its list of plug-ins. Other parts of the application, or any other plug-in can then call the plug-in manager to get a list of all plug-ins.

Basically, each plug-in consists of the following:

A jar file containing a package with a

MiscellaneousHandler class

For the rest of this document, we will use the term “plug-in root” to refer to the package containing the MiscellaneousHandler class.

For example, the rest of this document uses a plug-in with a class named com.MyPlugin.MiscellaneousHandler; the plug-in root refers to the package com.MyPlugin.

Creating the Miscellaneous Handler Class

In order for Administration Services to recognize your client plugin, you must create a MiscellaneousHandler.java class and include it in the plugin jar file. See the following example, which implements a single API getDescription(). The framework_client.jar file is required to compile this example.

package com.mycompany.client.plugins;
 
import com.essbase.eas.client.plugins.Description;
 
public class MiscellaneousHandler {
    public MiscellaneousHandler() {
    }
    /**
     * Return a description object for this plugin
     */
    public static Object getDescription() {
        Description d = new Description();
        d.setText("Give a short description");
        d.setVersion("1.0.0");
        d.setVendor("My Company Inc.");
        d.setCopyright("Copyright 2006, My Company Inc.");
        return d;
    }
}

Adding Functionality

There are many ways to add functionality to Administration Services Console. The following sections describe how this is currently implemented:

Semantic Rules

Many of the following sections have a description of semantic rules. In most cases, Administration Services Console does not enforce these rules. We expect that developers writing plug-ins for Administration Services will be “well-behaved citizens”; philosophically, this means that a lot of the console is open, accessible, and plug-ins can have an adverse effect on the application by taking actions that break these semantic rules.

Adding a Branch to the Enterprise Tree

When Administration Services Console starts, a panel is created called the “Enterprise View”. This panel contains an instance of the CTree class. The text for the root node is called “Enterprise View”. Each plug-in gets the opportunity to add children to the root node. This permits each plug-in to have its own branch in the Enterprise Tree view.

In the plug-in root, add a class called ConsoleTreeHandler. In our example, this would be com.MyPlugin.ConsoleTreeHandler. Add a method called “populateTree()” to this class. The source code should look something like the following example:

public class Console TreeHandler {
  //a no-argument constructor is required by the framework.
  public ConsoleTreeHandler() {
  }
  
  public void populateTree(CTreeModel model) {
    Object root=model.getRoot();
    
    //strictly speaking, this next check should not be
    //necessary; however, we do this to make sure some other
    //plug-in hasn’t replaced the root node with something
    //unexpected.
    if ((root!=null) && (root instanceof CTreeNode))
      //create any CTreeNode-derived objects, adding them
      //as children of the root node.
    }
  }
}

There are some unenforced semantic rules associated with CTree objects:

  • The only action a plug-in should perform on the CTreeModel is to get the root. The plug-in should never replace the root node, traverse the tree model, or make changes to any other descendants of the root node.

  • Every object added as child of the root node must be derived from a CTreeNode. Theoretically, any object can be added as a child of the root; however, other parts of the framework will not respond to those objects in any meaningful way.

    Note:

    A plug-in can be called more than once if the console disconnects from the current server. The code needs to check that the node has already been added and only append nodes that have not been added previously. The source code should look something like the following Essbase ConsoleTreeHandler code:

    /**
      * populates the model with information required.
      */
      public void populateTree(CTreeModel model) {
        Object root=model.getRoot();
        CTreeNode rootNode=null;
        boolean firstTime=true;
        if (root instanceof CTreeNode) {
          rootNode=(CTreeNode) root;
          if (rootNode.getChildCount()!=0) {
            CTreeNode node=(CTreeNode) rootNode.getFirstChild();
            while (node !=null) {
              if (node instanceof ServersContainerNode) {
                firstTime=false;
                UIFactory.refreshServerList();
                break;
             }
              node=(CTreeNode rootNode.getChildAfter(node);
            }
          }
        }
        if (firstTime) {
          CTreeNode essnode=new ServersContainerNode(null);
          rootNode.add(essnode);
          final CTreeNode containerNode=essnode;
          
          ConsoleManager.getConsoleInstance().addFrameListener(new WindowAdapter() {
            public void windowClosed(WindowEvent e) {
              //signal that we are simply disconnecting instead of
              //closing
              if (e.getNewState() == WindowEvent.WINDOW_OPENED &&		
                e.getOldState() == WindowEvent.WINDOW_OPENED) {
                Server[] servers = UIFactory.getServers();
                for (int ii=0; ii<servers.length; ii++) {
                  UIFactory.removeServerInstance(servers[ii]);
                }
              }
               UIFactory.disconnectAll();
            }
          })
        }
      }

Adding Children to Other Tree Nodes

When a CTreeNode object is expanded for the first time, each plug-in gets the opportunity to add child nodes to the CTreeNode being expanded.

In the plug-in root, add a class called ConsoleTreeHandler. In our example, this would be com.MyPlugin.ConsoleTreeHandler. Add a method called “getTreeNodeChildren()” to this class. The source code should look something like the following example:

public static CTreeNode[] getTreeNodeChildren(CTreeNode node) {	
  // strictly speaking, this check for null should never be	
  // necessary
  if (node == null)
    return new CTreeNode[0];
  if (node instanceof SomeSpecificTreeNode) {
    CTreeNode[] theChildren = new CTreeNode[5];
    theChildren[0] = new ChildNode();
    theChildren[1] = new AnotherChildNode();
    // and so on...
    return theChildren;
  }
  else if (node instanceof SomeOtherTreeNode) {
    // different set of children here.
  }
  // and if we're not interested in any other types.
  return new CTreeNode[0].
}

Item of interest for this operation:

  • This method could be declared public Object[] getTreeNodeChildren(CTreeNode node) and it would still get called. The CTreeNode method that handles this checks the return value for null and also checks each item returned in the array to ensure that it is an instance of a CTreeNode object. Declaring the method as in the example enforces to the implementer of the plug-in that the items returned must be items derived from the CTreeNode class.

  • The only arrangement that currently is done is that child nodes that cannot have children are placed before the child nodes that can have children. Nodes from plug-ins are placed after the nodes that the parent node already knows about.

Permitting Plug-ins To Add Children To Your Tree Nodes

By default, all CTreeNode based objects that can have children have this feature enabled. Currently, there is no way to prevent plug-ins from adding children to a tree node if that tree node can have children.

Adding Context Menu Items To Tree Nodes

When the CTree control detects that a popup menu needs to be displayed, it calls the instance of the CTreeNode and asks it for a list of items to display in the context menu. The following are rules or guidelines for how CTreeNode objects should build this array:

  • The signature for the CTreeNode method is:

    public Component[] getContextMenuItems();

    Even though this method is declared to return an array of Component objects, it is highly recommended that the objects returned all be instances of the JMenuItem class (or classes derived from JMenuItem).

  • The state of any menu items returned from the getContextMenuItems() method must be properly initialized; that is, enabled/disabled, checked.

  • The JMenuItem objects (or whatever objects) must be properly linked to the specific CTreeNode object that is being called. The event passed in the actionPerformed() call will contain none of this contextual information.

The CTree then calls each plug-in, retrieving any additional menu items for the specified CTreeNode object. If there are additional items, the CTree places a separator after the original menu items, then places all of the plug-in items in the popup menu, and then, if the CTreeNode can be put on custom views, puts another separator and the menu items related to custom views.

For a plug-in to respond to the CTree properly in this case, add a class called ConsoleTreeHandler to the plug-in root package. In our example, this would be com.MyPlugin.ConsoleTreeHandler. Add a method called “getContextMenuItemsFor()” to this class. The source code could look something like the following example:

public static Component[] getContextMenuItmsFor(CTreeNode node) {
  // strictly speaking, this check for null should never be
  // necessary
  if (node == null)
    return new Component[0];
  if (node instanceof SomeSpecificTreeNode) {
    JMenuItem theItem = new JMenuItem("Walk");
    JMenuItem anotherItem = new JMenuItem("Don't walk");
    theItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        // take action here.
      }
    }
    return new Component[] { theItem, anotherItem };
  }
  else if (node instanceof SomeOtherTreeNode) {
    // different set of menu items here.
  }
  // and if we're not interested in any other types.
  return new Component[0].
}

Items of interest for this operation:

  • This method can be declared to return anything. For instance, for better type safety within your own code, you could declare the method to be “public static JMenuItem[] getContextMenuItemsFor(CTreeNode node)”; however, the CTree object making the call will only use items that are derived from the Component class.

  • This example is very bare bones; for instance, the returned JMenuItem object does not know which CTreeNode object it should be working with; even worse, one of the items does not have an action listener associated with it. For a complete example of this, please see the sample plug-ins developed by the Administration Services development team.

  • CTreeNode (being derived from DefaultMutableTreeNode) objects have a user object. This is available through the getUserObject() method. The intent is that the user object for a node represents that data that the node has been created for and this is the data that would need to be associated with the menu item. For instance, a node might have an object representing an Essbase application. In the above example, we would then perform a node.getUserObject() call to obtain this Essbase application object

  • Because plug-ins are called in the order that the user has arranged them in the Component Manager dialog box, there currently is no way to force the menu items from one plug-in to appear before the menu items of another plug-in.

Adding Options to the New Menu

The Administration Services development framework provides one dialog box for the New menu. When New is selected, the framework creates and displays an instance of the com.essbase.eas.framework.client.ui.filedlgs.NewDialog.java class. The Essbase plug-in and Administration Services plug-in add the following tabs:

  • Essbase

  • Scripts

  • Wizards

The dialog box class includes the following items:

  • The OK, Cancel, and Help buttons

  • An instance of a JTabbedPane to act as a container for each of the other panels

  • Actions for the OK, Cancel, and Help buttons that make the appropriate calls into the plug-in that provided the active panel

To add a panel and tab to the New dialog box, add a class called NewDialogHandler to the plug-in root package. In our example, this would be com.MyPlugin.ConsoleTreeHandler. Add a method called “populatePanel()” to this class. Example source code:

public void populatePanel(JTabbedPane panel) {
  // create an instance of the right kind of panel
  CNewDialogScrollPanel s = new CNewDialogScrollPane();
  s.setHorizontalScrollBarPolicy(JscrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
  s.setVerticalScrollBarPolicy(JscrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
  
  // create a list model that has some items in it.
  DefaultListModel model = new DefaultListModel();
  model.addElement(new JLabel("XTD Connection");
  model.addElement(new Jlabel("SQL Connection");
  
  // make sure the list box has a selected item
  list.setSelectedIndex(0);
  
  // toss the list into the scroll pane and ensure that the new
   // dialog box will call this instance when the OK button is
  // clicked.
  s.getViewport().add(list);
  s.setOkHandler(this);
  
  // add this panel to the tabbed panel we were given
  panel.add("My Objects", s);
  
}

For this to work correctly, you must add the following method to the class:

public void handleOk(Component component) {
  if (component instanceof CNewDialogScrollPane) {
    CNewDialogScrollPane scroller = (CNewDialogScrollPane) component;
    Component control = scroller.getViewport().getComponent(0);
    if (control != null) && (control instanceof JList)) {
      // extract the selected item in the JList.
      // ensure that it is one of the ones we added.
      // take the appropriate action.
    }
  }
}

Items of interest for this operation:

  • Items added to the JTabbedPane must be derived from the CNewDialogScrollPane class.

  • Because CNewDialogScrollPane is derived from JScrollPane, components derived from JTable, JTree, and JList display best in the New dialog box.

  • Components added to the scroller can have custom renderers, event handlers, and so on.

  • For the best behavior, this list would need a MouseListener added to listen for double-click events. This MouseListener should then call the enclosing dialog box’s handleOk() method.

  • A plug-in can add more than one panel to the JTabbedPane instance.

Adding Items To Menus

Menu items are typically displayed in three ways:

  • Static

  • From an internal frame

  • From a CTreeNode on the console tree

Static Menu Items

Static menu items are always displayed. The following example is for a static menu item:

public class XYZ {
  private CMenu editorsMenu = new CMenu("Scripts", Console.ID_ACTIONS_MENU - 1, this);
  private CMenuItem outline  = new CMenuItem("Outline", null, 0, this);
  private CMenuItem report  = new CMenuItem("Report", null, 1, this);
  private CMenuItem calc   = new CMenuItem("Calc", null, 2, this);
  private CMenuItem maxl   = new CMenuItem("Maxl", null, 3, this);
  private CMenuItem mdx    = new CMenuItem("Mdx", null, 4, this);
  private CMenuItem dataprep  = new CMenuItem("DataPrep", null, 5, this);

    void createMenu() {
report.addActionListener(new AbstractAction("createReport") {
  public void actionPerformed(ActionEvent e) {
  }
});

calc.addActionListener(new AbstractAction("createCalc") {
  public void actionPerformed(ActionEvent e) {
  }
});

maxl.addActionListener(new AbstractAction("createMaxl") {
  public void actionPerformed(ActionEvent e) {
  }
});

mdx.addActionListener(new AbstractAction("createMdx") {
  public void actionPerformed(ActionEvent e) {
  }
});

outline.addActionListener(new AbstractAction("createOutline") {
  public void actionPerformed(ActionEvent e) {
  }
});

dataprep.addActionListener(new AbstractAction("createDataPrep") {
  public void actionPerformed(ActionEvent e) {
  }
});

editorsMenu.add(outline);
editorsMenu.add(dataprep);
editorsMenu.add(calc);
editorsMenu.add(report);
editorsMenu.add(maxl);
editorsMenu.add(mdx);

LocalizeUtils.localizeMenu(resources, editorsMenu);
ConsoleManager.getConsoleInstance().mergeMenus(new Component[] { editorsMenu});
    }
}

Internal Frame Menu Items

Menu items from an internal frame only display when the internal frame is active. If the internal frame is deactivated or closed, then these menu items no longer are displayed. The following example is for an internal frame menu item:

public class XYZ extends CInternalFrame {
  public Component[] getFrameMenus() {
// Like the example above
    return (new Component[] { editorsMenu});
  }
}

Console Tree Menu Items

These menu items only display when a node is selected. The following example is for a console tree menu item:

public XYZ extends CTreeNode {
  public Component[] getActionMenuItems() {
    return (new Component[] { editorsMenu});
  }
}

In general, there are predefined menu positions defined in the Console interface:

  public static final int ID_FILE_MENU = 0;
  public static final int ID_EDIT_MENU = 1;
  public static final int ID_VIEW_MENU = 2;
  public static final int ID_ACTIONS_MENU = 10;
  public static final int ID_TOOLS_MENU = 20;
  public static final int ID_WIZARD_MENU = 30;
  public static final int ID_WINDOW_MENU = 90;
  public static final int ID_HELP_MENU = 99;

If the CMenu item’s (that is returned from the above example) position matches with one of the predefined ones, then that CMenu item’s submenus are merged in else that CMenu is inserted based on the position. So if the CMenu has a position of ID_ACTIONS_MENU, then the items are merged in to the action menu item that is already on the main menubar. If the CMenu has a position (ID_ACTIONS_MENU - 1), then the CMenu is inserted before the action menu.

Handling Save As

Save As requires the plug-in to implement the interface SaveAsRequestor. The following example uses an inner class:

  if (saveAsAdapter == null) {
  saveAsAdapter = new SaveAsAdapter();
  }
  SaveAsDialog.showDialog(resources.getString("exportTitle"), (SaveAsRequestor) saveAsAdapter);
  
}

The initSaveAsDialog is called to allow the dialog/frame to initialize the SaveAsDialog as it needs to. By default a file system chooser is added to mainPanel at index 0. A plug-in can add other panels to save to other places in this method.

When an object is selected from any panel, then the saveAsObject method is called with the selected object. If the file system panel is selected the object will be a File if the plug-in adds a panel of their own it they will have to perform the steps to save the object.

private class SaveAsAdapter implements SaveAsRequestor {
  public void initSaveAsDialogComponents(JTabbedPane mainPanel) {
    String xmlString = ResourceUtilities.getStringSafely(resources, XML_FILES);
    DefaultFileFilter xmlFilter =  new DefaultFileFilter(xmlString, "xml", resultAction);
    JFileChooser jfc = (JFileChooser) mainPanel.getComponentAt(0);
    jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
    if (jfc.isAcceptAllFileFilterUsed() == true)
      jfc.setAcceptAllFileFilterUsed(false);
    jfc.setFileFilter(xmlFilter);
  }
  
  public void initExtraComponents(JPanel extraPanel) {
  }
  
   public boolean saveAsObject(Object saveObject) {
    boolean saved = false;
    if (saveObject instanceof File) {
      File file = (File) saveObject;
      String exportFile = file.getPath();
      if (exportFile != null) {
        String msg = "";
        if (AdminServerPropertiesHelper.requestExportDB(exportFile))
        {
          msg = resources.getString("sucEXDBMsg");
          StandardMessages.showMessage(resources, "exportTitle", msg,           JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE);
          saved = true;
        }
        else
        {
          msg = resources.getString("failEXDBMsg");
          StandardMessages.showMessage(resources, "exportTitle", msg,           JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE);
        }
      }
    }
    return saved;
  }
  public void setFocusComponent() {
  }
}

Handling Server Connection and Disconnection

In this release, Administration Services Console initially opens up disconnected from any Essbase Administration Server. Your code can be notified when Essbase Administration Server is connected, is disconnecting, or has already disconnected. Your code can implement the EASServerListener interface to be notified of the change in the Essbase Administration Server state.

import com.essbase.eas.client.intf.EASServerListener; 

private EASServerListener listener = new EASServerListener() { 

    public void ServerDisconnecting(String server) { 
        // server is about to disconnect. 
    } 

    public void ServerDisconnected(String name) {
        // server is disconnected 
        // disable menu items or Enterprise tree nodes when disconnected             from the EAS server
    } 

    public void ServerConnected(String name) { 
        // enable menus 
        // add Enterprise tree nodes
    }
}; 

To add the EASServerListener to the console use the following code snippet.

Console console = ConsoleManager.getConsoleInstance().
console.addEASServerListener(listener);

Standard Controls

While it is not required that plug-ins use the standard controls provided by the framework classes, there are some benefits to using them. Namely, some consistency of look and feel is provided, some housekeeping tasks are performed by the standard controls, there is support for internationalization, accessibility, and so on.

The StandardDialog Class

The StandardDialog class is an extension of the JDialog class and was introduced for the following reasons:

  1. Standardize the mechanism for internationalization and localization handling

  2. Standardize the position, location, and behavior of dialog “action” buttons

  3. Standardize some of the accessibility handling for modal dialogs

  4. Standardize the handling of results

The StandardDialog class contains the following protected (or private) fields:

Table 2. Fields in the StandardDialog Class  

Field

Description

okBtn

An instance of an OK button. This is one of the standard controls described in Dialog Initialization.

cancelBtn

An instance of a Cancel button. This is one of the standard controls described in Dialog Initialization.

helpBtn

An instance of a Help button. This is one of the standard controls described in Dialog Initialization.

buttons

An instance of a ButtonPanel. The ButtonPanel is one of the standard controls described in Dialog Initialization.

resources

An instance of a ResourceBundle object. This resource bundle is used for internationalization purposes.

adapter

An instance of a StandardDialogAdapter.

dialogResult

An instance of a DialogResult object.

saveDialogBounds

A boolean value indicating whether the bounds (location and size) of this dialog should be saved when it is closed.

Name of Standard Dialog Class

The name of the Standard Dialog class is StandardDialog. It is in com.essbase.easui.StandardDialog.class.

Dialog Creation

There are at least 11 constructors for the StandardDialog class; most of these chain to another constructor. The two constructors that should be invoked by derived classes are the ones with the following signatures:

  • StandardDialog(Frame owner, String title, boolean modal, DialogResult result);

  • StandardDialog(Dialog owner, String title, boolean modal);

Most of the other constructors exist only to match constructor names of the JDialog class.

Dialog Initialization

During the call to the StandardDialog constructor, the following initialization steps will occur:

  • An OK button, a Cancel button, and a Help button are created

    These are the standard buttons used by most dialogs. If the dialog being implemented uses a different set of buttons (for instance, Close, Apply, Next, and so on) the derived class should implement instances of those buttons.

  • A ButtonPanel containing the OK, Cancel, and Help buttons is created

    If the dialog being implemented wants the button panel to contain a different set of buttons, it should call buttonPanel.changeButtons(new Jbutton[] { closeBtn, helpBtn }); // as an example.

  • A ResourceBundle instance is created

    This resource bundle is used to perform localization work within the dialog. It is important to know where the standard dialog looks for the instance of the resource bundle. For example, if the dialog class is MyFunnyDialog, then the resource bundle must be in a file called resources/MyFunnyDialog.properties.

  • A StandardDialogAdapter is created and is added as a window listener to the dialog

    Caution!

    Because of the implementation of the StandardDialogAdapter class, there should never be a reason for a descendant class of StandardDialog to attach a WindowListener to itself. Routing of all window events should be handled by the StandardDialogAdapter. If the descendant class needs to take action when a window close, window open, and so on, event occurs then override the methods in StandardDialog that the StandardDialogAdapter calls.

  • Sets the instance of the dialog result to the value passed in, if any

    To understand how this works, see Dialog Results.

  • Sets the dialog’s default close operation to DISPOSE_ON_CLOSE

    In most cases, this is the desired behavior; for a dialog that needs a different behavior, this can be changed by the constructor in the descendant class.

  • Sets the dialog’s content pane layout to be a BoxLayout oriented vertically

    If necessary, this can be changed by the derived class.

  • Adds entries to the action and input maps of the dialog’s root pane to take a “default action” when the Enter key is pressed by the user

    For more information on what this default action is, and why this step is necessary, see the section of this document titled “Dialog default action”.

Dialog Default Action

The Microsoft Windows operating environment has the concept of a default button when modal dialog windows are open. The default button is painted in a way that makes it stand out visually to the user. Normally, that is the OK button; however, it can be any action button on the dialog. To handle this concept, the StandardDialog adds entries to the action and input maps of its root pane for handling the enter keystroke.

If your dialog box does not have an OK button or, if at any time, the default button should be some other button, then a call like the following needs to be performed:

dlg.getRootPane().setDefaultButton(closeBtn);

Dialog Keyboard Handling, Focus Order, Action Maps, and So On

Depending on which buttons are inserted into a dialog, certain keystrokes will be mapped automatically:

  • The Enter key

  • The Esc key

  • The F1 key (for help)

These are the primary keystrokes that are mapped by the standard dialog and the standard buttons.

To add handling when these keystrokes are pressed, do the following:

  • For the Enter key, override the handleOk() method. If everything finishes correctly and the dialog needs to be released, then call super.handleOk(). This will ensure that the dialog shuts down properly.

  • For the Esc key, override the handleCancel() method. The standard dialog behavior closes the dialog, releases all the controls, disposes of contained components, and so on. In most cases, this method will not need to be overridden.

  • For the F1 key, override the handleHelp() method. If the dialog has been connected via the Administration Services help system via the normal manner, this step should not be necessary.

By default, the Java Swing implementation sets the focus order of controls to correspond to the order in which they were added to their container, and then those container’s to their container, and so on. This can be overridden by making a call to the method DialogUtils.setFocusOrder(). This mechanism should be used in all dialogs to ensure the focus order of controls is correct and doesn’t rely on how the code for building the containment models was written.

Dialog Results

In many cases, a dialog needs to return a significant amount of information to the calling mechanism. Unfortunately, the method Dialog.show() is declared as void and does not return any data.

If, when implementing a dialog, results from the dialog are needed, the recommended way to get those is by doing the following tasks:

  • Extend the DialogResult class to contain references and additional data needed by the dialog and/or returned by the dialog.

  • Before creating the dialog, create an instance of the DialogResult class.

  • Ensure that the dialog has at least one constructor that accepts an instance of a DialogResult object.

  • In the constructor for the dialog class derived from StandardDialog, pass the DialogResult object to the correct StandardDialog constructor.

  • During the handling of the OK button, set the results back into this instance.

Methods to Override

The StandardDialog class has a set of methods that can be overridden. Whether each of these methods are overridden will depend on the needs of each derived class. See the Administration Services Java API Reference for detailed information about each of the following methods:

  • dispose()

  • handleCancel()

  • handleOk()

  • handleWindowClosed()

  • handleWindowClosing()

  • handleWindowOpened()

Standard Buttons and Other Controls

There are a large number of standard controls provided by the client framework. The following is a representative list; for more complete information, see the Administration Services Java API Reference for the com.essbase.eas.ui package and descendant packages.

Note:

This is not a complete list of controls. The plug-in developer should browse the Java API Reference for the com.essbase.eas.ui package and other packages under this one for additional standard components.

  • ActivateButton

  • ApplyButton

  • BackButton

  • BooleanComboBox

  • ButtonPanel

  • CancelButton

  • CloseButton

  • DoneButton

  • FinishButton

  • HelpButton

  • ListMoverPanel

  • NextButton

  • NumericTextField

  • OkButton

  • ReadOnlyTextFrame

  • RefreshButton

  • ResetButton

  • SimpleWizardPanel

  • VerticalPairPanel

  • WizardPanel

Administration Services Console Services

The client framework provides the following Administration Services Console services:

Retrieving the CSS Token from the Console

The CSS token is retrieved from the FrameworkUser object which is returned on successful login to Essbase Administration Server.

import com.essbase.eas.client.intf.Login;
import com.essbase.eas.client.manager.LoginManager;
import com.essbase.eas.admin.defs.*;
import com.essbase.eas.admin.client.*;
import com.essbase.eas.framework.defs.FrameworkUser;

private String getToken() {
    String loginToken = null;
    Login login = LoginManager.getLoginInstance();
    if (login != null) {
        FrameworkUser u = (FrameworkUser)
login.getProperty("FrameworkUser");
        if (u != null) {
            loginToken = u.getToken();
        }
    }
    return loginToken;
}

Sending E-mail

Administration Services Console has integrated support for sending e-mail using the JavaMail API. We have wrapped the classes and provide a dialog for sending e-mail. There is also support in the InternalFrame class to send from any class derived from the CInternalFrame class.

The following is a simple example of how to send the contents of a text area in an e-mail from a dialog.

Import com.essbase.eas.ui.email.*;

public void email() {
    JFrame fr = ConsoleManager.getConsoleFrame();
    
    SendEmail email = new SendEmail(fr, fr.getTitle(), new Object[] {
getTextArea().getText()} );
    email.send();
  }

The following example is for a window derived from CInternalFrame. The methods, isEmailable() and getObjectsToEmail, are methods in the CInternalFrame class.

  public boolean isEmailable() {
    return true;
  }
  
  public Object[] getObjectsToEmail() {
    HTMLDoc doc = new HTMLDoc();
    
    doc.setTitle(getTitle());
    doc.addObject(doc.getHeading(2, doc.getStyleText(getTitle(),
doc.BOLD | doc.UNDERLINE), doc.CENTER));

    doc.addObject(doc.BR);

doc.addObject(TableUtilities.getHTML((DefaultTableModel)locksTable.getModel()));

    return (new Object[] { new EmailAttachment(doc.toString(),
"Locks.htm", EmailAttachment.HTMLTEXT, "", EmailAttachment.ATTACHMENT)});
  }

Note:

Sending an e-mail puts an entry in the background process table showing the outcome of the e-mail.

Internationalization

The framework provides a set of internationalization and localization utilities in the package com.essbase.eas.i18n. These classes provide a mechanism for locating resources associated with a window or dialog box, loading resource bundles based on the locale, localizing collections, arrays of components, or containers. There is also an i18n-friendly string collator class.

Packaging the Plug-in

The only packaging requirement is that all classes and resources necessary for a client plug-in must be contained in the same jar file. You must include an entry in the jar file which defines the other jar files it depends on. For example, lets say the plug-in jar file xyz.jar depends on abc.jar and cde.jar, include the following entry in the manifest file for the plug-in jar file:

Class-Path: xyz.jar cde.jar