Adding a Property Page to ConsoleOne
Articles and Tips: article
Senior Research Engineer
Developer Information
01 Apr 1998
ConsoleOne can display a PropertyBook allowing administrators to edit the attribute values in a selected object. When the administrator selects an object in some namespace and then selects "Properties," ConsoleOne instantiates a PageHost window. This article discusses the changed PageSnapin interface and offers in-depth explanations, along with code samples, for APIInfoAttrEditPanel.
Introduction
Like the NetWare Administrator application, ConsoleOne can display a PropertyBook (hereafter referred to as PageHost), allowing administrators to edit the attribute values in a selected object. As shown in Figure 1, when the administrator selects an object in some namespace and then selects "Properties," ConsoleOne responds by instantiating a PageHost window.
When all of the snap-ins for the NDS Namespace are installed, the PageHost displayed for an NDS object gets a Configuration and a Rights page from the NDS namespace for free, no programming required. Beyond these two pages, the property book has no idea what else to display about the selected object.
In order to allow developers to furnish property pages for their own object types, ConsoleOne provides an interface called a PageSnapin. An implemented PageSnapin interface provides the ConsoleOne shell's PageHost with everything it needs to display a page of object type specific information.
Figure 1: A PageSnapin is responsible for the "look and feel" of its page's GUI area.
The number of additional pages displayed for an object type in the PageHost is determined by the number of PageSnapins developed for the object's type. So, after instantiating the PageHost, the Shell loads all PageSnapin's registered for the type of the selected object in the current namespace and then calls upon each to provide a GUI for their specific page tab.
For example, in the API_Info application's snap-in set, the code examined in this article series, there are two PageSnapin implementations. Therefore, in addition to the standard Configuration and Rights pages, an APIInfo object gets two extra property pages. As in NetWare Administrator, pages may support more than one attribute.
Changed PageSnapin Interface
In the March issue of this publication, an article titled "Snapping Your NDS App into ConsoleOne" described how to develop a series of ConsoleOne snap-ins to add basic support for an NDS enabled application called API_Info.
The "Developing APIInfo EditPageSnapin" section of that article explained how to implement the PageSnapin interface methods required by ConsoleOne to manage a property page.
The interface for a PageSnapin has changed between the publication of that article and this one. Notice in the code below that the isDirty( )and canChanged( )methods have been removed while the methods setPageHost( ), setModified( ), setActive( ), and killActive( )have been added.
//This is the old PageSnapin interface definition: public abstract interface PageSnapin extends Snapin { public abstract void showHelp(); public abstract Component getPage(); public abstract boolean hasHelp(); public abstract String getTabName(); public abstract String getTabID(); public abstract String getMenuName(); public abstract boolean canChange(); public abstract boolean isDirty(); public abstract boolean canSave(); public abstract boolean saveData(); } //This is the new PageSnapin interface definition: public abstract interface PageSnapin extends Snapin { public abstract void showHelp(); public abstract Component getPage(); public abstract boolean hasHelp(); public abstract String getTabName(); public abstract String getTabID(); public abstract String getMenuName(); public abstract boolean canSave(); public abstract boolean saveData(); public abstract void setPageHost( PageHost ph ); public abstract void setActive( boolean firstTime ); public abstract boolean killActive( ); public abstract setModified( boolean state, PageSnapin page ); }
The following discussion explains how the new PageSnapin methods are used.
ConsoleOne's PageHost/PageSnapin Protocol
The protocol between the PageHost and a PageSnapin can be divided into four main sequences:
Page load sequence
Set Focus sequence
Data Modify sequence
Save Data sequence
Refer to Figure 2 for the following discussion.
Figure 2: The PageHost/PageSnapin data management protocol.
1. Page load sequence
After the PageSnapin is loaded and before its initSnapin( ) method is called, the PageHost will pass itself as a parameter in the setPageHost( PageHost ph ) method of all of its pages. This gives them each a reference to the PageHost and to its associated data including the ObjectEntry that was selected in the namespace view when the PageHost was initialized. This ObjectEntry will be shared by all of the PageHost's PageSnapins.
The PageHost calls initSnapin( ) in all of its pages. This allows the NDS PageSnapins to use the PageHost's ObjectEntry to get a reference to the associated NDS NSObject that they will all share. How this is done was described in the previous issue of this publication.
After the PageHost has loaded and initialized all PageSnapins registered for the type of the selected object in the current namespace, it calls each of their getPage( ) methods to obtain a GUI for their specific page tab.The java.awt.Component sent to the PageHost by the getPage( ) method will usually be descended from java.awt.Container so it can contain any combination of java.awt Components that the developer wants to use in the GUI (e.g., buttons, text fields, lists, etc.). This is described in more detail later in the Code Description section of this article.
2. Set focus sequence
Before the PageHost sets the focus to a different page tab (brings a different page tab to the front), the PageHost will give the PageSnapin currently owning the focus a chance to veto the change. This is done so that the PageSnapin with the current focus can check the validity (e.g., format) of its data. To give the current focus holder an opportunity to veto, the PageHost calls the page's killActive( ) method. If the current owning PageSnapin feels that its data is OK, it will return a true telling the PageHost to go ahead with the focus change. If the PageSnapin checks its data and finds it invalid, it can return false, effectively vetoing the change in focus. For example, if one of the focus holding page's fields is associated with a date and the entered date is in an unacceptable format, then when the PageHost calls the page's killActive( ) method, the page could then display a dialog asking the user to enter a date with an appropriate format and return false. In response to the false returned by the focus owner, the PageHost would cancel the change in focus.
If the focus change is not vetoed by the current focus holder, the PageHost will call the focus recieving PageSnapin's setActive( ) method. If this is the first time that PageSnapin's tab will receive the focus, the PageHost will set the firstTime parameter in the method call to true. A true firstTime parameter is a signal to the PageSnapin that it should read its attribute data from the target object. The firstTime parameter in the setActive( ) method is an efficiency measure. Reading from a namespace can be a relatively time consuming operation. So, having all of the PageHost's PageSnapins read, at the same time, when they are loaded might cause a noticeable delay. By having the PageSnapin read only the first time that its tab is selected, the number of reads is reduced to only those pages that the user wishes to view and only once for each of them.
3. Data modified sequence
When the user modifies a field on a PageSnapin, it should call its setModified( ) method to let the PageHost know that a commitable change has occured.
The PageHost will respond to the setModified( ) call by enabling its OK and Apply buttons, thereby giving the user the opportunity to commit the change.
4. Data commit sequence If the user clicks either the OK button or the Apply button in a PageHost, the PageHost will:
Call the canSave( ) method in each of its PageSnapins, giving them a chance to do one last validity check before they commit their data to their namespace. If they do find some reason to veto the save, they can stop the save operation by returning false. If a PageSnapin returns false, the PageHost will terminate the save operation, award the vetoing PageSnapin the focus, bringing it forward and giving it an opportunity to display a dialog explaining why the operation was stopped.
If all of the PageHost's PageSnapins return a true in their canSave( ) method's, that tells the PageHost that the save operation is still on. So, the PageHost calls each of its PageSnapin's saveData( ) methods. If during the process of saving its data the PageSnapin has a problem, it can return false, thereby stopping the operation for the other pages as well and gaining the focus so that it can explain its error to the user.
APIInfoAttrEditPanel Code Description
The code description below explains how the PageHost/PageSnapin protocol is implemented by API_Info's PageSnapins.
Figure 3: Web server performance, before and after caching is applied.
The rest of this article explains how to do the other half of the PageSnapin development task, which is to program a PageSnapin's GUI (refer to Figure 3 for this discussion).
As explained in the March issue, API_Info implements two PageSnapins which inherit from an abstract class called APIInfoEditPageSnapin to produce two pages for the PageHost (see Figures 3 and 4). Since API_Info has only two attributes, this is overkill. But, it does allow us to demonstrate how to implement multiple pages.
After ConsoleOne loads the two APIInfoEditPageSnapins, the PageHost will create a blank page tab for each of them. To provide a GUI for the blank area in each page tab, each APIInfo PageSnapin instantiates a unique APIInfoAttrEditPanel. Each of these GUI panels, which must ultimately be derived from java.awt.container, are then sent to the PageHost in the PageSnapin's getPage( ) method. The PageHost then installs the GUI into the page's page tab area (see Figure 1).
The following pages list the sources for APIInfoAttrEditPanel and its related classes. The pages after the listing contain a line by line source code description (see notes 1 and 2 below).
Note 1: Refer to the previous issue for an object model diagram of ConsoleOne. That issue also contains a complete source listing for the APIInfoEditPageSnapin class and the two PageSnapin classes which implement it. Please note that the PageSnapin interface implemented by these classes has been superceded as explained earlier in this article.
Note 2: This ConsoleOne snap-in article series uses a unique NDS namespace which simplifies NDS transactions. Refer to the previous issue for an object model diagram and class hierarchy for this unique NDS namespace.
Note 3: For more info on API_Info, see Sept/Oct/Nov 97 issues of this publication. Also, we have developed a Java applet API_Info client developed to work with the API_Info service NLM, see the Dec. 97 and Jan.98 issues of this publication for more information. The ConsoleOne snap-in sources used in 'Snapping Your NDS App in to ConsoleOne" will be available at our download site on the web along with prerelease documentation and other build environment necessities. The ConsoleOne snap- in project and sources used in the "Snapping Your NDS App in to ConsoleOne" article series are for programming practice purposes only. The NDS namespace snap-in used in the project, may or may not be included with the first release of ConsoleOne.
package com.novell.admin.ndssnapins.classes.shared.apiinfo.mpecpages; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.*; import java.util.*; import java.awt.event.*; import com.sun.java.swing.*; import com.novell.application.console.snapin.*; import com.novell.application.console.util.objectentryselector.*; import com.novell.admin.common.exceptions.*; import com.novell.admin.ns.nds.*; import com.novell.admin.ns.*; // inherited by Operator & Server page GUIs abstract public class APIInfoAttrEditPanel extends Panel {private static final int FIELD_WIDTH = 250; private static final int FIELD_HEIGHT = 18; protected Shell shell; privateFrame parentFrame; protectedNSObject nsObj; protectedNDSNamespace ns; private JLabelcurrentAttrVal = new JLabel(); private APIEditFieldeditAttrVal = null; private booleanhasRights = false; public APIInfoEditPageSnapinpageSnap = null; private NDSObjectAttributethisObjAttrib = null; abstract protected String getThisAttrID(); abstract protected String getFilterTypeName(); public APIInfoAttrEditPanel( Shell theShell, Frame parent, APIInfoEditPageSnapin thePageSnapin, NDSNamespace NDSNs,NSObject obj) throws SPIException 1 {super(new BorderLayout(10,10)); 2 parentFrame = parent; 3 shell = theShell; 4 nsObj = obj; 5 ns = NDSNs; 6 pageSnap = thePageSnapin ; 7 checkRights(); // does user have rights to edit attrib values? 8 layoutControls();//create GUI.If user has rights,create fields, butns, etc. 9 } private void checkRights() throws SPIException 1 { ObjectEntry oe = nsObj.getObjectEntry(); // ObjectEntry for selected NSObject 2 ObjectEntry user = ns.getLoginUser(oe);//get authenticated usr connection oe 3 NDSPropertyRights rights = 4 (NDSPropertyRights)ns.getPropertyEffectiveRights(oe, user,getThisAttrID()); 5 if (rights.hasWriteRights() == true) 6 hasRights = true; 7 } private void layoutControls() 1 {setLayout ( new GridLayout( 3, 1 )); 2 Jpanel curValInfoPanel = new JPanel(new FlowLayout(FlowLayout.LEFT,5, 5)); 3curValInfoPanel.add(new Label("Cached Value")); 4currentAttrVal.resize( FIELD_WIDTH, FIELD_HEIGHT ); 5curValInfoPanel.add(currentAttrVal); 6add(curValInfoPanel); 7if( hasRights )// if hasRights can edit values, so, create editValPanel for user 8{Panel editValPanel = new Panel( new FlowLayout(FlowLayout.LEFT, 5, 5 )); 9editValPanel.add(new Label("Edit Value")); 10editAttrVal = new APIEditField( this ); 11 editAttrVal.resize( FIELD_WIDTH, FIELD_HEIGHT ); 12 editValPanel.add(editAttrVal); 13add(editValPanel); 14 15Panel valBtnPanel = new Panel(new FlowLayout(FlowLayout.LEFT,5, 5)); 16SetNewAttributeValueButton newValueBtn= new SetNewAttributeValueButton(this); 17valBtnPanel.add(newValueBtn); 18newValueBtn.resize(100,15); 19 20FlatBrowserButton browserBtn = new FlatBrowserButton(this); 21valBtnPanel.add(browserBtn); 22browserBtn.resize(100,15); 23add(valBtnPanel); 24 } 25 } public void readData() 1 { thisObjAttrib = (NDSObjectAttribute)nsObj.getAttribute(getThisAttrID()); 2if (thisObjAttrib != null) 3{Enumeration valueSet = thisObjAttrib.getComponents(); 4String value = ((StringValue)(valueSet.nextElement())).getValue(); 5currentAttrVal.setText(value); 6editAttrVal.setText( value ); 7} 8 } public void writeData() 1 { if ( editAttrVal.isModified() == true) 2{String newValue = editAttrVal.getText(); 3ValueComponent vc = null; 4thisObjAttrib.removeAllComponents(); 5try 6{NDSSyntax syntax = NDSSyntax.SYN_DIST_NAME ; 7vc = syntax.createValueComponent( newValue ); 8thisObjAttrib.addComponent( vc ); 9} 10catch (ComponentCreationException e) 11{System.out.println("Threw a componentCreationException"); } 12currentAttrVal.setText(newValue); 13editAttrVal.setModified(false); 14} 15} public void setEditValue(String newVal ){ editAttrVal.setText(newVal); } public NamespaceSnapin getNamespace(){ return((NamespaceSnapin)ns) } public Insets getInsets(){ return new Insets(10,10,10,10); } } /**************************************************************************** APIInfoServerPanel displays GUI for Server Name Attrib Info for user mods ****************************************************************************/ class APIInfoServerPanel extends APIInfoAttrEditPanel { final private static String SRVR_FILTER_TYPE_NAME = "NCP Server"; public APIInfoServerPanel( Shell theShell,Frame parent, APIInfoEditPageSnapin thePageSnapin, NDSNamespace NDSNs, NSObject obj) throws SPIException { super(theShell, parent, thePageSnapin, NDSNs, obj); } protected String getFilterTypeName(){ return SRVR_FILTER_TYPE_NAME; } protected String getThisAttrID(){ return APIInfoCommon.API_INFO_SERVER_ATTRNAME;} } /**************************************************************************** APIInfoOperatorPanel displays GUI for Operator Name Attrib Info for user mods ****************************************************************************/ class APIInfoOperatorPanel extends APIInfoAttrEditPanel { final private static String OPRTR_FILTER_TYPE_NAME = "User"; public APIInfoOperatorPanel(Shell theShell, Frame parent, APIInfoEditPageSnapin thePageSnapin, NDSNamespace NDSNs, NSObject obj) throws SPIException { super(theShell, parent, thePageSnapin, NDSNs, obj); } protected String getFilterTypeName(){ return OPRTR_FILTER_TYPE_NAME; } protected String getThisAttrID(){ return APIInfoCommon.API_INFO_OPRTR_ATTRNAME;} } class APIEditField extends JLabel {boolean isModified = false; APIInfoAttrEditPanel panel = null; public APIEditField( APIInfoAttrEditPanel thePanel ) {super( ); panel = thePanel; } public void setText( String newText ) { if( newText != null && !newText.equals(getText())) { super.setText( newText ); setModified( true ); } } public void setModified(boolean modified ) { isModified =modified; if (modified ) panel.pageSnap.pageHost.setModified( true , panel.pageSnap ); } public boolean isModified(){ return(isModified);} } abstract class AttributeEditPanelButton extends Button implements ActionListener {APIInfoAttrEditPanel panel = null; public AttributeEditPanelButton(String name,APIInfoAttrEditPanel thePanel ) {super(name); panel = thePanel; addActionListener(this); } } class SetNewAttributeValueButton extends AttributeEditPanelButton {public SetNewAttributeValueButton(APIInfoAttrEditPanel thePanel ) {super("Set Value",thePanel );} public void actionPerformed(ActionEvent e){ panel.writeData(); } } class FlatBrowserButton extends AttributeEditPanelButton {public FlatBrowserButton(APIInfoAttrEditPanel thePanel ) {super( "Browse", thePanel);} public void actionPerformed(ActionEvent e) {ObjectEntry oe = panel.nsObj.getObjectEntry(); APIInfoFlatBrwsrDlg oes = new APIInfoFlatBrwsrDlg(panel.shell.getShellFrame(),oe,panel); } } class APIInfoFlatBrwsrDlg extends ObjectEntrySelector implements WindowListener {APIInfoAttrEditPanel panel = null; ObjectEntry oe; public APIInfoFlatBrwsrDlg( Frame frame, ObjectEntry theOE, APIInfoAttrEditPanel thePanel ) 1{super(frame, theOE); 2panel = thePanel; 3oe = theOE; 4ObjectType[] filterTypes = new ObjectType[1]; 5filterTypes[0] = 6new ObjectType(panel.getFilterTypeName(),panel.getNamespace(), false); 7setFilter(panel.getFilterTypeName(), filterTypes); 8ObjectEntry parent = null; 9parent = oe.getParent(); 10setContext(parent); 11setVisible(true); 12addWindowListener(this); 13} // WindowListener interface methods public void windowClosed(WindowEvent e) {ObjectEntry[] selected = getSelectedObjectEntries(); if(selected != null && selected.length >0) {String value = ((NDSNamespace)panel.getNamespace()).getUnrootedName(selected[0]); panel.setEditValue(value ); } } public void windowOpened(WindowEvent e){ } public void windowClosing(WindowEvent e){ } public void windowDeactivated(WindowEvent e){ } public void windowDeiconified(WindowEvent e){ } public void windowIconified(WindowEvent e){ } public void windowActivated(WindowEvent e){ } }
Figure 4: APIInfo's PageSnapin object model.
Refer to the "Snapping Your NDS App into ConsoleOne" article, the ConsoleOne object model diagram and the NDS namespace object model diagram in the previous issue for the following discussion.
As shown in Figure 4, APIInfoServerPage and APIInfoOperatorPage are both derived from the abstract APIInfoEditPageSnapin class, as described in a previous article.
The class APIInfoAttrEditPanel is also an abstract class providing functionality to the APIInfoOperatorPanel and APIInfoServerPanel classes. The APIInfoOperatorPanel and APIInfoServerPanel classes are relatively trivial implementing the two accessor methods, getThisAttrID( ) and getFilterTypeName( ), specified by APIInfoAttrEditPanel as abstract.
The APIInfoOperatorPanel is instantiated by the APIInfoOperatorPage PageSnapin and the APIInfoServerPanel class is instantiated by the APIInfoServer PageSnapin.
Once their PageSnapins have instantiated them, references to APIInfoOperatorPanel and APIInfoServerPanel are sent back to ConsoleOne which installs them into the appropriate page tab areas in the PageHost (see Figures 1 and 2).
Note: Since both APIInfoOperatorPanel and APIInfoServerPanel inherit from APIInfoAttrEditPanel, the descriptions for APIInfoAttrEditPanel below apply to them as well.
APIInfoAttrEditPanel's NSObject nsObj Instance Variable
The PageHost keeps its own copy of the ObjectEntry for the object that was selected in the namespace when the PageHost was instantiated. The APIInfoEditPageSnapin that owns this APIInfoAttrEditPanel, used this ObjectEntry to obtain the NSObject (or namespace object) from the NDSNamespace. When the NDSNamespace obtains the NSObject it caches it. After the PageSnapin obtains the NSObject associated with the target ObjectEntry in ConsoleOne, it passes the NSObject to APIInfoAttrEditPanel in its constructor.
Since the PageHost is common to all PageSnapins and because the PageHost maintains its own single copy of the selected ObjectEntry, when the other pages use it to get their NSObjects, they will get the same NSObject cached by the NDSNamespace. The result is that there will be a single NSObject referenced by all of the PageSnapins in the PageHost. Since the PageSnapins will all be making changes to the same NSObject, the changes will be kept in sync.
An NSObject contains the name context, attribute values, class definition, and schema object reference that the NDS namespace snap-in needs to modify an object in the namespace. However, it is still just a local representation of that information.
Changes made to the NSObject won't be made to the distributed NDS database. So, changes made to the NSObject by the PageHost's pages remain local until the PageSnapin's saveData( ) method is called which will happen when the user clicks either the "OK" or "Apply" buttons.
How and why the "OK" or "Apply" buttons are enabled is discussed later.
APIInfoAttrEditPanel's Constructor
Please note that the value for this page's editable attribute is not read in this constructor. PageSnapins should not read their values from the namespace until the PageHost calls their setActive( ) method with a true firstTime parameter (see "ConsoleOne's PageHost/PageSnapin Protocol" discussion earlier in this article.)
Line 7 The first meaningful thing that happens in APIInfoAttrEditPanel's constructor is that its checkRights( ) method is called to make sure that this user has been authorized to edit this attributes in the target object. The checkRights( ) method is explained in more detail later.
Line 8 Next, the constructor calls the layoutControls( ) method to instantiate the GUI for this PageSnapin. This GUI will consist of a series of java Panels containing buttons, labels, etc. and installed into itself. APIInfoAttrEditPanel is a panel itself and so can contain other panels.
The APIInfoEditPageSnapin which instantiates the APIInfoAttrEditPanel, has a method called getPage( ) which is another method required to implement the PageSnapin interface. The PageHost will call the getPage( ) method in all of its PageSnapins to obtain a GUI for each of them to install in their page tab areas (see Figure 1). When APIInfoEditPageSnapin's getPage( ) is called, it will return a reference to its instantiated APIInfoAttrEditPanel to be installed as its page GUI.
The layoutControls( ) method is explained in more detail later.
APIInfoAttrEditPanel's checkRights( ) Method
The checkRights( ) method is called to make sure that this user has been authorized to edit this object's attributes.
Line 1 The ObjectEntry corresponding to the target NSObject is obtained (see discussion of the PageHost's NSObject above). This target ObjectEntry represents the object that is to be edited in the PageHost.
Line 2 An ObjectEntry representing the user that obtained ConsoleOne's current authenticated connection to this NDS tree is obtained. This is the connection that will be used to implement changes to the tree.
Lines 3-6 The NSObject's getPropertyEffectiveRights( ) method is called to have the namespace read the target object's access control list to see if the ConsoleOne user is listed as having write rights to the target attribute in the target object. If the user is listed as having such rights, the boolean instance variable hasRights is set to true.
APIInfoAttrEditPanel's layoutControls( ) Method
APIInfoAttrEditPanel is a java.awt.Panel therefore it inherits from java.awt.Container. This means that APIInfoAttrEditPanel can contain java.awt components of all kinds. The layoutControls( ) method is called once from the constructor of this class to add appropriate components to the APIInfoAttrEditPanel.
Once it has instantiated the APIInfoAttrEditPanel, the APIInfoEditPageSnapin which owns the APIInfoAttrEditPanel will then send the APIInfoAttrEditPanel to the PageHost when the PageHost calls its getPage( ) method. The PageHost will then install the Panel to be displayed in the PageSnapin's page tab area.
Please note that the value for this page's attribute are not read in this method. PageSnapins should not read their values until the PageHost calls their setActive( ) method with a true firstTime parameter (see "PageHost/PageSnapin Protocol" discussion earlier in this article.)
Refer to Figure 5 for the following discussion.
As designed, the APIInfoAttrEditPanel GUI would not be a very efficient user interface for normal use. This is because, one of its purposes is to demonstrate for you when the cached NSObject held by the namespace is updated.
The currentAttrVal field holds the value initially read from the NSObject. The editAttrVal field can be changed by using the FlatBrowserButton (explained later) to browse for existing objects of the type needed for this page's attribute. In the case of Figure 5, this object would be an NCP Server type NDS object. After a FlatBrowser selection, the distinguished name of the object selected would appear in the editAttrVal field. This value could then be moved to the curAttrVal field and installed into the cached NSObject with the SetNewAttributeValueButton.
Neither field can be directly edited by typing. This prevents syntax and object context errors, ensuring that the currentAttrVal is valid. This means that the PageSnapin owning this GUI can always return false when its killActive( ) method is called and true when its canSave( ) methods is called.
Figure 5: The components installed into an APIInfo page.
Line 1 The Layout for the APIInfoAttrEditPanel is set to a grid with one column and three rows. Each row will have a panel installed. Each panel will contain components of various kinds.
Lines 2-6 A panel is instantiated with a FlowLayout (adds items to a horizontal row). Two labels are then added to the Panel. First a label to tell the user the title of the value next to it and then the value which is the JLabel instance variable currentAttrVal. currentAttrVal will be initialized in the readData( ) method when the PageHost calls this GUI's PageSnapin owner's setActive( ) method with a true firstTime parameter (see the "PageHost/PageSnapin Protocol" discussion earlier in this article.) Finally, the Panel is added to the first row of APIInfoAttrEditPanel's GridLayout.
Lines 7-23 If the checkRights( ) method determined that this user has the right to edit this attribute, a Panel is added to the second row of APIInfoAttrEditPanel's GridLayout to contain two more labels. The second of these labels, editAttrVal, will be used to contain interim values selected by the user with the FlatBrowserButton explained below.
Then another Panel is added to the third row of APIInfoAttrEditPanel's GridLayout to contain two buttons.
The first button is intended to write the contents of the edit label to the APIInfoAttrEditPanel's NSObject and also write the value to the currentAttrVal in the first Panel.
Both of the attributes managed by APIInfoEditPageSnapins are defined to have the distinguished name syntax. A distinguished name value is supposed to fully qualify the location of an object in the NDS namespace. API_Info's Server attribute specifies the NCP Server running the API_Info service NLM. API_Info's Operator attribute specifies the user responsible for administering the API_Info application.
The second button is intended to instantiate a flat browser window to allow the user to obtain a distinguished name for the appropriate attribute by browsing objects in the NDS namespace tree.
The code for these two buttons is explained later.
APIInfoAttrEditPanel's readData( ) Method
Please note that this method is not called from anywhere in this class. This method will be called by the APIInfo PageSnapins when the PageHost calls their setActive( ) method with a true firstTime parameter (see the "PageHost/PageSnapin Protocol" discussion earlier in this article.)
The readData( ) method is called to obtain the current target attribute's values from the object in the tree (in APIInfo's case, its attributes are single- valued and there is only one attribute per page so there will be only one attribute read in this method). The obtained value is installed in the instance variable JLabel currentAttrVal.
Line 1 The getAttrID( ) method is declared as abstract in the APIInfoAttrEditPanel class. Classes implementing this abstract class must implement this method to provide the name of the attribute so that it can be read, written to, etc. APIInfoServerPanel's getAttrID( ) method returns the name for APIInfo's Server attribute. Likewise, APIInfoOperatorPanel's getAttrID( ) method returns the name for APIInfo's Operator attribute.
In the first line of this method, the NSObject is used to read the NDSObjectAttribute that has the attribute name returned by the getAttrID( ) method. Among other things, the returned NDSObjectAttribute will contain references to ValueComponent objects. ValueComponents contain the actual values for the attribute (see the NDS namespace object model in the March issue for more information).
Lines 3-5 An Enumeration is used by the NSObject getComponents( ) method to return the ValueComponents contained in the NDSObjectAttribute because an attribute can contain many values. However, the APIInfo Server and Operator attributes which are the values sought in these property pages are both defined as single-valued. So, there will only be one item in the Enumeration after getComponents( ).
APIInfo's Server and Operator attributes are also defined to have the distinguished name syntax which is a string type. The String value returned by the ValueComponent is installed into currentAttrVal and editAttrVal for display (see Figure 5).
APIInfoAttrEditPanels writeData( ) Method
The purpose of APIInfoAttrEditPanel's writeData( ) method is to write the contents of the editAttrVal text field to the cached NSObject referenced by the PageHost's ObjectEntry and shared by all of its pages (see Figure 5).
Please note that the cached NSObject held by the PageHost is maintained local to ConsoleOne. Writing to the NSObject with this method has no effect on the target object's attribute values in the distributed NDS database. The data in the NSObject will be committed to the namespace's distributed database when the PageHost calls the saveData( ) method in the PageSnapin owner of this GUI (see "ConsoleOne's PageHost/PageSnapin Protocol" discussion earlier in this article.)
Lines 1-4 If the user has selected new data for the editAttrVal label, it is obtained with the getText( ) method.
The thisObjAttr instance variable is an NDSObjectAttribute that was initialized in the readData( ) method when this page got its first focus. All ValueComponents are removed from this ObjectAttribute so that we can have a clean start (ValueComponents are used to contain values, in this case, since APIInfo's attributes are defined as single-valued, this will be a single distinguished name.)
Lines 6-9 A distinguished name Syntax object is created. The Syntax is then used to create a ValueComponent initialized to the value obtained from the editAttrVal field. This new ValueComponent is then added to the NDSObjectAttribute to replace the one that was removed.
Lines 13-14 The currentAttrVal label is set to the new value and the editAttrVal's modified flag is cleared.
The APIInfoOperatorPanel and APIInfoServerPanel Classes
The APIInfoOperatorPanel and APIInfoServerPanel classes are relatively trivial implementing the two accessor methods, getThisAttrID( ) and getFilterTypeName( ), specified by APIInfoAttrEditPanel as abstract.
These are the classes instantiated by the APIInfoOperatorPage and APIInfoServerPage classes during their initSnapin( ) PageSnapin method implementations. References to the instantiated APIInfoOperatorPanel and APIInfoServerPanel objects are returned by their owning APIInfoEditPageSnapin class's getPage( ) PageSnapin method implementations.
The APIEditField Class
APIEditField is the class used for APIInfoEditPanel's editAttrVal field (see Figure 5).
Most of this class is pretty self-explanatory with the exception of one noteworthy item in its PageSnapin interface method setModified( ). The setModified( ) method lets the PageHost know that this page now has modified data. The PageHost responds to this information by enabling its OK and Apply buttons. If the user selects either of these buttons, the PageHost will call the canSave( ) and saveData( ) methods in all of its pages to commit their changes to the namespace's distributed database (see the "ConsoleOne's PageHost/PageSnapin Protocol" discussion earlier in this article.)
The AttributeEditPanelButton Classes
AttributeEditPanelButton is an abstract class that implements the ActionListener interface. The SetNewAttributeValueButton and FlatBrowserButton classes implement the ActionListener interface by providing actionPerformed( ) methods.
See Figure 5 for a graphical representation of SetNewAttributeValueButton and FlatBrowserButton.
The SetNewAttributeValueButton's actionPerformed( ) method simply calls the panel's writeData( ) method to write the value in the panel's editAttrVal field into the appropriate NDSObjectAttribute of the NSObject cached by the PageHost. The panel's writeData( ) method also copies the contents of the editAttrVal field into the currentAttrVal field.
The FlatBrowserButton's actionPerformed( ) method instantiates a FlatBrowser dialog (explained below) to allow the user to browse for a suitable object, whose name context will then be obtained and stored in the editAttrVal field.
The APIInfoFlatBrwsrDlg Class
APIInfoFlatBrwsrDlg extends ConsoleOne's ObjectEntrySelector class and implements the java.awt.event.WindowListener interface.
In its constructor APIInfoFlatBrwsrDlg builds an object filter for the appropriate object type and then sets the context for the browser display before allowing the dialog to be shown.
Lines 1-7 You could build an object display filter for the browser that would allow the display any combination of object types. However, each of the APIInfoAttrEditPanel types only care about one specific type of object. APIInfoServerPanel is looking for "NCP Server" types and APIInfoOperatorPanel is looking for "User" types.
The getFilterTypeName( ) methods implemented by these two classes obtain the name for their particular types allowing the APIInfoFlatBrwsrDlg to set its filter for that particular type.
Lines 8-16 ObjectEntrys are often used to communicate context. An ObjectEntry for the parent of the PageHost's target object is obtained. This ObjectEntry is used to set the APIInfoFlatBrwsrDlg context for the intial ObjectEntrySelector browser display.
With the appropriate object display filter and context for the browser set, the APIInfoFlatBrwsrDlg shows its dialog and adds itself as a WindowListener to the dialog.
APIInfoFlatBrwsrDlg's windowClosed( ) method
Only the windowClosed( ) WindowListener interface method contains any code. The rest are left as stubs.
The ObjectEntrySelector's getSelectedObjectEntries( ) method will obtain any number of object selections from its browser list. APIInfoFlatBrwsrDlg is only interested in one of them because the target attributes of the APIInfo PageSnapins are single-valued.
The getName( ) method
The namespace.getUnrootedName( ) method on the selected ObjectEntry will return its full distinguished name without the namespace root id (treename) as a prefix (e.g., in TreeName/OrgUnit.Org, OrgUnit.Org would be the unrooted name).
This distinguished name without the treename prefix is installed into the panel's editAttrVal field.
You can obtain the source for this project with its NDS namespace snap-ins at: http://www.novell.com/coolsolutions/tools/index.html
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.