Snapping Your NDS Application into ConsoleOne
Articles and Tips: article
Senior Research Engineer
Developer Information
01 Mar 1998
- Introduction
- ConsoleOne's NDS Namespace Snap-in
- The API_Info ConsoleOne Snap-inProject
- Developing APIInfoObjImageSnapin
- Developing APIInfoToolBarMenuSnapin
- Developing APIInfoAddObjSnapin
- Developing APIInfoEditPageSnapin
- Developing APIInfoPopupMenuSnapin
Introduction
If you read the preceding article "More on OSA's New Tools Platform, ConsoleOne," you learned that ConsoleOne, (to be released with NetWare 5) is more than just an NDS administration tool, it is actually a highly extensible namespace tools environment. By developing to ConsoleOne, your snap-ins could be written to administer your objects in multiple namespaces from multiple platforms.
Additionally, Novell plans to make ConsoleOne functionality available through applets thereby enabling administrators to manage your application from anywhere using a web-browser. So, if you are looking for a cross-platform, highly extensible, multi-namespace environment for your administration tools, ConsoleOne is the clear choice.
This article will describe how to develop a series of ConsoleOne snap-ins to add basic support for an NDS enabled application called API_Info to the ConsoleOne environment. In the near future, Java visual components will become available for those that wish to use a 5th generation development environment. These components will significantly simplify ConsoleOne snap-in development for those that wish to create generic snap-ins.
API_Info is a C-based network application that we have used many times in the past few months to explain different aspects of NetWare programming because it is about as simple as an application can be and yet be fully NDS enabled.
The original API_Info had a C-based snap-in written for the NetWare Administrator application. The snap-ins referred to in this article will accomplish about the same functionality for API_Info in ConsoleOne.
ConsoleOne's NDS Namespace Snap-in
As you learned in "More on OSA's New Tools Platform, ConsoleOne," ConsoleOne has no direct knowledge of any particular namespace. All of the useful functionality that enables ConsoleOne to browse, manage, and so on comes from the snap-ins installed in its snap-ins directory. For example, the sources discussed in this article rely on a special NDS namespace snap-in that provides the functionality to read and write NDS objects, extend the NDS schema, and so on.
Notes:
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 "Snapping Your NDS App in to ConsoleOne" 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.
The API_Info ConsoleOne Snap-in Project
In order to provide full administrative support for the API_Info NDS enabled application, this article describes how to develop five snap-ins:
Snap-in Name |
Description |
APIInfoObjImageSnapin |
To display your object type's icon in the NDS namespace view. |
APIInfoToolBarMenuSnapin |
To add tool items to ConsoleOne's tool bar and menu items to its menus which enable the administrator to extend or unextend an NDS tree's schema. |
APIInfoAddObjSnapin |
To create your application's object in an NDS tree. |
APIInfoEditPageSnapin |
To display your object's attribute edit pages in the ConsoleOne multi-page edit window and manage the resultant attribute modifications. |
APIInfoPopupMenuSnapin |
To configure your object's popup menu with the items you choose. |
Developing APIInfoObjImageSnapin
We will start with the APIInfoObjImageSnapin because it is the simplest of the five and yet it is a full snap-in. The purpose of APIInfoObjImageSnapin is to supply ConsoleOne with an image for API_Info objects, as shown in Figure 1.
Figure 1: APIInfoObjImageSnapin provides the image for API_Info Objects when an NDS namespace is selected.
APIInfoObjImageSnapin Source Description
As was mentioned in "More on OSA's New Tools Platform, ConsoleOne" all snap-ins are extended from ConsoleOne's snap-in interface. Shown below is the definition of Snapin.
public interface Snapin { /* getRegistration( ) is called by the shell to find what points of participation for which the snap-in(s) provided by this class should be registered. */ public SnapinItemInfo[] getRegistration() public String getSnapinDescription()/* Return the description of the snap-in. */ public String getSnapinName() /* Return the localized name of the snap-in. */ public boolean initSnapin(Shell) /* Initializes the participating snap-in. */ public void shutdownSnapin(Shell) /* Shuts down the participating snap-in. */ }
DisplayImageSnapin is another interface defined by ConsoleOne. The purpose of DisplayImageSnapin is to provide a java.lang.awt.Image object that can be drawn for its object type. To accomplish this purpose DisplayImageSnapin extends Snapin with two simple methods.
public interface DisplayImageSnapin extends Snapin { /*Return the image for the specified ObjectEntry in a namespace tree. */ public Image getDisplayImage(ObjectEntry) /*Return the image for the specified ObjectType in a namespace tree. */ public Image getDisplayImage(ObjectType) }
In order for API_Info to implement DisplayImageSnapin we must implement all of its abstract methods and those of its ancestor, Snapin. Shown below is the complete listing for APIInfoObjImageSnapin.
1 package com.novell.admin.ndssnapins.classes.shared.apiinfo.displayimage; 2 3 import java.awt.*; 4 import com.novell.application.console.snapin.*; 5 import com.novell.admin.ns.nds.*; 6 import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; 7 8 public final class ApiInfoObjImageSnapin implements DisplayImageSnapin 9 { 10 private final String IMAGE_FILE = "ApiObj.gif"; 11 private final String SNAPIN_NAME = "APIINFO:ObjectImage"; 12 private final String SNAPIN_DESCRIPTION = "gets image of API_Info object"; 13 private Image img = null; 14 protected Shell shell = null; 15 protected APIInfoCommon apiCom; 16 17 public SnapinItemInfo[] getRegistration() 18 { 19 SnapinItemInfo[] siis = new SnapinItemInfo[1]; 20 siis[0] = new SnapinItemInfo(this, 21 NDSNamespace.name, 22 APIInfoCommon.API_INFO_CLASSNAME ); 23 return siis; 24 } 25 26 public boolean initSnapin( Shell s) 27 { 28 shell = s; 29 apiCom = new APIInfoCommon(shell); 30 apiCom.getImage(this,APIInfoCommon.IMAGE_PATH+IMAGE_FILE); 31 return true; 32 } 33 34 public Image getDisplayImage(ObjectEntry oe) 35 { return img ; } 36 37 public Image getDisplayImage(ObjectType ot) 38 { return img ; } 39 40 public String getSnapinDescription() 41 { return SNAPIN_DESCRIPTION; } 42 43 public String getSnapinName() 44 { return SNAPIN_NAME; } 45 46 public void shutdownSnapin( Shell s) 47 { } 48 }
Line 1 begins our code with a package statement. In non-serious java projects, a package statement is not required because, if one is not present, your build environment will put your compiled classes into the default package (generally the directory containing your sources). However, with ConsoleOne snap-in development, package statements are required to provide an absolute path through all of the package directories to the directory containing your snap-in's executable classes. ConsoleOne's snap-in package directories will generally be in JAR files located in ConsoleOne's snap-ins directory.
All packages used in ConsoleOne begin with the "com" directory name. This is followed by the name of the company, (e.g., "java" for core Java packages [free!], "sun" for packages from Sun, or "novell" for packages from Novell). You should put a directory with your company name at this context level in the package name and install your Java package directories inside of it. This way your classes can be easily found during installs and uninstalls. The package directory tree for ConsoleOne will undoubtably get quite large.
Line 3 begins the import statements required by the code in this file. Each statement describe the location of a specific directory in the package directory tree that contains classes (executables) used by the code in this file. A specific class can be specified in the directory targeted by the import statement, but generally programmers like to use the wildcard "*" to make all of the classes in the directory available. Using import statements with wildcards significantly abbreviates your code.
Notes:
Without import statements, you would have to fully qualify each public reference to methods, variables or types in classes outside of your own package. For example, without the import statement: import com.novell.application.console.snapin.*; the class declaration: public class ApiInfoObjImageSnapin implements DisplayImageSnapin would need to become: public class ApiInfoObjImageSnapin implements com.novell.application.console.snapin. DisplayImageSnapin
Methods, variables or types in the Java.lang package (e.g., strings and other basic types) require no import statement.
The first import statement in the code enables us to reference methods variables or types from the core Java awt package, (GUI things like Images).
Line 4 enables us to reference methods variables or types from the basic package within Novell's ConsoleOne code.
Line 5 enables us to reference methods variables or types from the special NDS namespace classes used as a basis for this article.
Line 6 enables us to reference a special class defined for API_Info containing common methods available to all API_Info snap-ins (more on this later).
Line 8 tells the compiler that we intend to implement the abstract classes found in the interface com.novell.application.console.snapin.DisplayImageSnapin(this of course, would include the abstract methods from its ancestor interface Snapin).
Lines 10-12 essentially define some constants for this class. Notice that this code uses hard-wired Strings. ConsoleOne does provide localization functionality for resources like strings. However, our code examples use hard-wired Strings just to keep things a little simpler.
Lines 17-24 implement the abstract getRegistration( ) method defined in the Snapin interface. This method is extremely important to all ConsoleOne snap-ins because it describes to ConsoleOne what the snap-in is all about.
As was mentioned in the "More on OSA's New Tools Platform, ConsoleOne" article found earlier in this issue, snap-ins are loaded on an as-needed basis. This means that a snap-in can be loaded multiple times during a ConsoleOne user session. During all of this loading and unloading, getRegistration( ) will only be called once during the initialization phase of ConsoleOne, when all snap-ins in its snap-in directory are loaded and registered.
As was also mentioned in the "More on OSA's New Tools Platform, ConsoleOne" article, ConsoleOne has several predefined snap-in types. ConsoleOne knows when to call a particular snap-in because it knows which type, namespace, and object type or view that snap-in was registered for. SnapinItemInfo objects are used to carry this information to ConsoleOne.
getRegistration( ) always returns an "array" of SnapinItemInfos because a class can implement more than one snap-in. For each snap-in type implemented in a class, there would need to be one SnapinItemInfo instantiated. To better illustrate what getRegistration( ) does, we will instantiate APIInfoObjImageSnapin's single SnapinItemInfo with a more descriptive constructor;
1 siis[0] = new SnapinItemInfo( Shell.SNAPIN_DISPLAYIMAGE, 2 getClass().getName(), 3 NDSNamespace.name, 4 APIInfoCommon.API_INFO_CLASSNAME );
The first parameter to the SnapinItemInfo constructor shown above is the snap-in type. All snap-in types are defined in ConsoleOne's Shell class (see "More on OSA's New Tools Platform, ConsoleOne" for more explanation of the Shell class.) The second parameter uses the getClass( ) method (inherited from java.lang.Object) to obtain the fully qualified class name of the APIInfoObjImageSnapin class. ConsoleOne uses this information to locate the snap-in's class in order to load it. The third parameter tells ConsoleOne what namespace this snap-in is intended to work with. The fourth parameter tells ConsoleOne what object type or view this snap-in is intended for within the specified namespace.
Line 20 This constructor works because its first parameter tells ConsoleOne both the snap-in type (the Snapin interface implemented by this snap-in implies its type) and the fully qualified name (ConsoleOne can call getClass( ).getName( ) on the "this" snap-in object just as well as the snap-in can.) This constructor is a little shorter than the full blown constructor that we used to explain the getRegistration( ) method.
Lines 26-31 Here, APIInfoObjImageSnapin implements another method declared in the basic Snapin interface, initSnapin( ). initSnapin( ) will be called by ConsoleOne each time it loads the snap-in to give it a chance to perform some initialization before being used.
APIInfoObjImageSnapin's initSnapin( ) first uses its own variable to remember the Shell object reference passed to it. Next it instantiates an APIInfoCommon object to obtain a reference to the common methods available to all APIInfo snap-ins (kind of like gaining access to your own library).
Instead of making all of APIInfoCommon's methods and state available staticly, a new instance of APIInfoCommon is created each time an API_Info snap-in is loaded. This is because the ConsoleOne environment may change between loads. Having each loaded instance of the snap-in create its own instance of APIInfoCommon, ensures that they both will have the same environment state, (e.g., namespace selection, etc.).
initSnapin( ) uses a method in the instantiation of APIInfoCommon to get the image that is to be displayed to represent the API_Info object in the NDS namespace.
A true is returned to ConsoleOne indicating that APIInfoObjImageSnapin initialized successfully and can be used by ConsoleOne.
Lines 34-38 These two methods implement the purpose of the whole snap-in. When called, they return the API_Info object image.
For a detailed description of the interactions needed between snap-ins and the ConsoleOne shell in order display each specific object type's image in a given namespace, refer to the "Snap-in to Shell to Snap-in Communication" topic in the "More on OSA's New Tools Platform, ConsoleOne" article.
Lines 40-44 Implement the obligatory getName( ) and getDescription( ) methods from the Snapin interface which help ConsoleOne further indentify the snap-in.
Lines 46-47 implement the shutdownSnapin( ) method which gives the snap-in a chance to undo things which might affect system state (e.g., closing connections, deallocating native memory, etc.) which would be unaffected by normal Java garbage collecting.)
Developing APIInfoToolBarMenuSnapin
Ultimately, APIInfoToolBarMenuSnapin, as a sample snap-in, had two purposes. First, it was to demonstrate how to add menu item and toolbar button combinations to the ConsoleOne GUI. Second, it was to demonstrate how to extend and unextend an NDS schema using the special NDS namespace snap-in we refer to so often in this article.
However, because this article is going to be very large, we decided to accomplish the second purpose, extending the NDS schema with ConsoleOne's NDS namespace, with another article in the May issue. In this issue we will only demonstrate how to install a snap-in specific menu item and tool button combination into ConsoleOne's GUI.
Figure 2: Each APIInfoToolBarMenuSnapin adds a menu item/toolbar button combination to ConsoleOne when an NDS namespace is selected.
In reality, API_Info implements two snap-ins for its tool button/ menu item combinations. Whenever the NDS namespace is selected, the classes for both of these snap-ins will be loaded. As shown in Figure 2, the first snap-in will install both a menu item saying, "Add API_Info Class" and a corresponding tool button. Both of these Components will ultimately allow a user to extend an NDS schema for API_Info. The second snap-in will install both a menu item saying, "Remove API_Info Class" and a corresponding tool button that will perform the same operation.
Below is a listing of the three files which make up the two snap-ins which implement APIInfoToolBarMenuSnapin. A brief explanation will follow the listing.
APIInfoToolBarMenuSnapin Source Description
// file APIInfoToolBarMenuSnapin.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.menutools; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.*; import com.novell.admin.ns.nds.*; import com.novell.application.console.snapin.*; public abstract class APIInfoToolBarMenuSnapin extends SimpleToolBarMenuSnapin { protected final String TOOLS_MENU_STR = "Tools"; private static boolean nDSTreeIsAPI_InfoExtended= false; protected APIInfoCommon apiCom; protected Shell shell; /******************************************************************** Below are the abstract methods that this abstract class's descendants must implement ********************************************************************/ // abstract public void execute(boolean state );//SimpleToolBarMenuSnapin defined // abstract public String getLabel(); // defined in SimpleToolBarMenuSnapin // abstract public String getSnapinName(); // defined in Snapin // abstract public String getSnapinDescription(); // defined in Snapin // abstract public String getToolTip(); // defined in SimpleToolBarMenuSnapin // abstract public String getMenuLocation();// defined in SimpleToolBarMenuSnapin abstract public String getSnapinToolImagePath();// defined for this classes use /******************************************************************** Below are implementations of some abstract methods from this class's ancestors ********************************************************************/ public SnapinItemInfo[] getRegistration() // implements Snapin method { SnapinItemInfo[] siis = new SnapinItemInfo[ 2 ]; siis[0] = new SnapinItemInfo( Shell.SNAPIN_TOOLBARITEM, getClass().getName(), NDSNamespace.name, ""); siis[1] = new SnapinItemInfo( Shell.SNAPIN_MENU, getClass().getName(), NDSNamespace.name, ""); return siis; } public int getCommandType() // implements SimpleToolBarMenuSnapin method { return SimpleToolBarMenuSnapin.TYPE_COMMAND; } public Image getImage() // implements SimpleToolBarMenuSnapin method { return apiCom.getImage(this,getSnapinToolImagePath()); } initSimpleSnapin(Shell theShell) // implements SimpleToolBarMenuSnapin method { boolean result = super.initSimpleSnapin(theShell); shell = theShell; apiCom = new APIInfoCommon(shell); return result; } public void shutdownSimpleSnapin(Shell sh)//implmnts SimpleToolBarMenuSnapin meth { } /******************************************************************** Below is this classes only method that is not an implementation of an abstract method ********************************************************************/ protected void setNDSTreeIsAPI_InfoExtended (boolean state ) { nDSTreeIsAPI_InfoExtended = state ; } } // file APIInfoClassAdder.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.menutools; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.*; import java.beans.*; import com.novell.utility.nmsgbox.*; import com.novell.admin.ns.nds.*; import com.novell.admin.ns.*; import com.novell.admin.common.exceptions.*; public class APIInfoClassAdder extends APIInfoToolBarMenuSnapin { protected final String IMAGE_FILE = "ApiAdd.gif"; protected final String SNAPIN_NAME = "APIINFO:ToolBarMenuAdd"; protected final String SNAPIN_DESCRIPTION ="Manages API_Info Schema xtnsn add menu"; protected final String TOOL_TIP="Extends NDS schema for API_Info classes and attrs"; protected final String LABEL = "Add API_Info Class"; // Executed on button click or menu selection. Example code will be in May issue. public void execute(boolean state ) // implements SimpleToolBarMenuSnapin method { } public String getSnapinToolImagePath()//implements APIInfoToolBarMenuSnapin method { return APIInfoCommon.IMAGE_PATH+IMAGE_FILE; } public String getLabel() // implements SimpleToolBarMenuSnapin method { return LABEL; } public String getSnapinName() // implements Snapin method { return SNAPIN_NAME; } public String getSnapinDescription() // implements Snapin method { return SNAPIN_DESCRIPTION; } public String getToolTip() // implements SimpleToolBarMenuSnapin method { return TOOL_TIP; } public String getMenuLocation() // implementation of MenuSnapin method { return TOOLS_MENU_STR; } } // file APIInfoClassRemover.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.menutools; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.*; import java.beans.*; import com.novell.utility.nmsgbox.*; import com.novell.admin.ns.nds.*; import com.novell.admin.ns.*; import com.novell.admin.common.exceptions.*; public class APIInfoClassRemover extends APIInfoToolBarMenuSnapin { protected final String IMAGE_FILE = "ApiRmv.gif"; protected final String SNAPIN_NAME = "APIINFO:ToolBarMenuRemove"; protected final String SNAPIN_DESCRIPTION ="Manages API_Info Schema xtnsn remv menu"; protected final String TOOL_TIP = "Removes API_Info class from NDS schema."; protected final String LABEL = "Remove API_Info Class"; // Executed on button click or menu selection. Example code will be in May issue. public void execute(boolean state ) // implements SimpleToolBarMenuSnapin method { } public String getSnapinToolImagePath()//implements APIInfoToolBarMenuSnapin method { return APIInfoCommon.IMAGE_PATH+IMAGE_FILE; } public String getLabel() // implements SimpleToolBarMenuSnapin method { return LABEL; } public String getSnapinName() // implements Snapin method { return SNAPIN_NAME; } public String getSnapinDescription() // implements Snapin method { return SNAPIN_DESCRIPTION; } public String getToolTip() // implements SimpleToolBarMenuSnapin method { return TOOL_TIP; } public String getMenuLocation() // implementation of MenuSnapin method { return TOOLS_MENU_STR; } }
Notice that APIInfoToolBarMenuSnapin extends SimpleToolBarMenuSnapin. SimpleToolBarMenuSnapin is an abstract class defined by ConsoleOne which implements both the MenuSnapin and ToolBarSnapin interfaces taking care of a lot of details in the process.
The abstract APIInfoToolBarMenuSnapin further simplifies the development of toolbar button/menu item combinations for APIInfo.
APIInfoClassAdder and APIInfoClassRemover are at the end of the inheritance line. They each separately implement APIInfoToolBarMenuSnapin to produce two different menu item/tool button snap-ins. One to add API_Info shema things and one to remove them. Notice that they are virtually identical with the only difference being their state variables.
Some other things should be pointed out as well.
There can be many classes in a package which are not snap-ins but contribute functionality to snap-ins. During its initialization phase, ConsoleOne needs to know specifically which classes are snap-ins so that they can be loaded and registered and then loaded again when required (once loaded, a snap-in will load its own classes and resources, as needed). So, the question is, how does ConsoleOne know which classes in a package are valid snap-ins?
There are two ways that ConsoleOne can be told about the snap-in classes in a package. First, the snap-ins could be specified in a JAR manifest as snap-ins. This is the most efficient way to tell ConsoleOne about a snap-in but it requires the developer to write a manifest. Second, even if there is no manifest in the JAR, ConsoleOne can still identify snap-ins by using low level reflection on the JAR (parsing the classes in the JAR for Snapin methods).
For example, both APIInfoClassAdder and APIInfoClassRemover extend APIInfoToolBarMenuSnapin which extends SimpleToolBarMenuSnapin which implements the ConsoleOne Snapin interfaces MenuSnapin and ToolBarSnapin which extend the Snapin interface.
So, although APIInfoClassAdder and APIInfoClassRemover don't have getRegistration( ) methods defined in them, ConsoleOne could still see them as valid snap-ins, because they ultimately inherit from Snapin, and will call the getRegistration( ) methods which they inherit from APIInfoToolBarMenuSnapin.
Next, notice that the third parameter in APIInfoToolBarMenuSnapin's SnapinItemInfo constructor is the name of the NDS namespace and that the fourth parameter (which would normally specify an object type or view) is an empty string. This combination registers the APIInfoClassAdder and APIInfoClassRemover snap-ins to be loaded whenever the NDS namespace is selected, without regard to particular objects or views.
Developing APIInfoAddObjSnapin
The purpose of APIInfoAddObjSnapin is to enable an administrator to create an API_Info object in an NDS tree with ConsoleOne.
Naturally, since the API_Info objects are defined in the NDS namespace, the APIInfoAddObjSnapin will only be loaded when that namespace is selected. As you can see in figure 3, user access to this functionality is provided through an "API_Info Object" menu item that is added by the snap-in to ConsoleOne's File menu when the NDS namespace is selected.
APIInfoAddObjSnapin consists of two files, one for the ConsoleOne and NDS namespace calls necessary to add an object and the other for the create object dialog GUI. Since the dialog is pretty standard Java GUI fare, we won't discuss its source in this article. It is enough to say that the purpose of the dialog is simply to allow the user to enter a name for the new object and select "define additional properties" or "create another object."
Figure 3: APIInfo AddObjSnapin adds a menu item and manages a dialog to add an API_Info object to an NDS tree.
This snap-in depends on both ConsoleOne objects and namespace objects. If you haven't yet read the "Snap-in to Shell to Snap-in Communication" topic in the "More on OSA's New Tools Platform, ConsoleOne" article, you may wish to do so now so that you can become more familiar with the ConsoleOne object model.
APIInfoAddObjSnapin Source Description
Since this is the first snap-in in this article that utilizes the NDS namespace, a class hierarchy and object diagram is provided for the namespace on the following pages. This namespace information is followed by the APIInfoAddObjSnapin source code and explanations.
NDS Namespace Class Hierarchy (incomplete)
class java.lang.Object interface com.novell.admin.ns.AdminNamespace class com.novell.admin.ns.AttributeDefinition (implements java.io.Serializable) class com.novell.admin.ns.nds.NDSAttributeDefinition class com.novell.admin.ns.ClassDefinition class com.novell.admin.ns.nds.NDSClassDefinition class java.util.EventObject (implements java.io.Serializable) class java.beans.PropertyChangeEvent class com.novell.admin.ns.ValueListPropertyChangeEvent class com.novell.application.console.snapin.SnapinEvent class com.novell.admin.ns.NSObjectEvent class com.novell.admin.ns.IntegerSyntaxValueStrategy (implements com.novell.admin.ns.SyntaxValueStrategy) class com.novell.admin.ns.JNDINameSpace class com.novell.admin.ns.nds.jndi.NDSNamespaceImpl (implements com.novell.admin.ns.nds.NDSNameSpace, com.novell.application.console.snapin.NamespaceSnapin) class com.novell.admin.ns.nw.NetWareNameSpace (implements com.novell.application.console.snapin.NamespaceSnapin) class com.novell.admin.ns.nds.NDSAttributeFlags (implements java.lang.Cloneable) class com.novell.admin.ns.nds.NDSClassFlags (implements java.lang.Cloneable) class com.novell.admin.ns.nds.NDSFilter (implements java.io.Serializable) interface com.novell.admin.ns.nds.NDSNameSpace (extends com.novell.admin.ns.AdminNamespace) class com.novell.admin.ns.nds.NDSPartitionInfo class com.novell.admin.ns.nds.jndi.NDSPartitionOperationStatus (implements com.novell.admin.ns.OperationStatus) interface com.novell.admin.ns.nds.NDSPartitionService (extends com.novell.admin.ns.PartitionService) class com.novell.admin.ns.nds.jndi.NDSPartitionServiceImpl (implements com.novell.admin.ns.PartitionService) class com.novell.admin.ns.nds.NDSReplicaOperationStatus (implements com.novell.admin.ns.OperationStatus) class com.novell.admin.ns.nds.NDSReplicaState class com.novell.admin.ns.nds.NDSReplicaType interface com.novell.admin.ns.nds.NDSSchema (extends class com.novell.admin.ns.nds.jndi.NDSSchemaImpl (implements com.novell.admin.ns.nds.NDSSchema) class com.novell.admin.ns.NSObject (implements java.lang.Cloneable, java.beans.PropertyChangeListener, java.io.Serializable) class com.novell.admin.ns.ObjectAttribute (implements java.lang.Cloneable, java.beans.PropertyChangeListener, java.io.Serializable) class com.novell.admin.ns.nds.NDSObjectAttribute class com.novell.admin.ns.nds.ObjectFlags interface com.novell.admin.ns.OperationStatus interface com.novell.admin.ns.nds.OperationThreadStatus interface com.novell.admin.ns.PartitionOperationCallBack interface com.novell.admin.ns.PartitionService class com.novell.admin.ns.Replica class com.novell.admin.ns.nds.NDSReplica class com.novell.admin.ns.nds.NDSReplicaInfo class java.util.ResourceBundle class java.util.ListResourceBundle class com.novell.admin.ns.nds.NDSRes class com.novell.admin.ns.nw.NWRes class com.novell.admin.ns.nds.ndsResourceBundle class com.novell.admin.ns.Rights class com.novell.admin.ns.nds.NDSObjectRights class com.novell.admin.ns.nds.NDSPropertyRights class com.novell.admin.ns.SchemaDefinition (implements java.lang.Cloneable, java.io.Serializable) class com.novell.admin.ns.nds.NDSSchemaDefinition interface com.novell.admin.ns.SchemaService class com.novell.admin.ns.StringSyntaxValueStrategy (implements com.novell.admin.ns.SyntaxValueStrategy) class com.novell.admin.ns.Syntax (implements java.io.Serializable) class com.novell.admin.ns.nds.NDSSyntax class com.novell.admin.ns.SyntaxListStrategy (implements com.novell.admin.ns.SyntaxStrategy) interface com.novell.admin.ns.SyntaxStrategy interface com.novell.admin.ns.SyntaxValueStrategy (extends com.novell.admin.ns.SyntaxStrategy) class java.lang.Throwable (implements java.io.Serializable) class java.lang.Exception class com.novell.admin.common.exceptions.AdminException class com.novell.admin.ns.ComponentCreationException class com.novell.admin.ns.IncompatibleComponentException class com.novell.admin.ns.NamespaceException class com.novell.admin.ns.nds.NDSNamespaceException class com.novell.admin.ns.OperationNotSupportedException class com.novell.admin.ns.SchemaDefinitionException class com.novell.admin.ns.ValueComponent class com.novell.admin.ns.Value class com.novell.admin.ns.BooleanValue class com.novell.admin.ns.HexValue class com.novell.admin.ns.LongValue class com.novell.admin.ns.StreamValue class com.novell.admin.ns.StringValue class com.novell.admin.ns.ValueList
Notes:
The entries in the class hierarchy listing are in the form of fully qualified names. For example, in the entry,
com.novell.application.console.snapin. NamespaceSnapin,
the prefix
com.novell.application.console.snapin
specifies the location of the Java class package directory that contains the class NamespaceSnapin.
The dots or "." are name context delimiters. So, the DOS equivalent of the entry would be:
com\novell\application\console.snapin\ NamespaceSnapin
and this is what you would see if you looked inside of the JAR file.
The "package" statement that you write for each source file tells the build environment in which directory to put the classes compiled from the file. Usually, programmers put classes of like purpose together into the same directory or package. Often all of the packages relating to a given purpose are zipped up into the same JAR file which is then put into the ConsoleOne snap-ins directory so that ConsoleOne can accesss it.
Figure 4: This simplified NDS namespace object model diagram is for discussion purposes only.
// file APIInfoAddObjSnapin.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.addobj; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*;// for APIInfoCommon import java.awt.*; // for the java GUI classes import java.awt.event.*; // for the listeners import com.sun.java.swing.*; // for the dialog window and controls import com.novell.admin.ns.nds.*; // for the NDS stuff import com.novell.admin.ns.*; import com.novell.admin.common.exceptions.*; // for the Novell exceptions import com.novell.application.console.snapin.*; // for ConsoleOne import com.novell.utility.nmsgbox.*; // for the message dialog public class APIInfoAddObjSnapin implements MenuSnapin, ActionListener, ShellListener {static protected final String LABEL = "Create New APIInfo"; private final String INVALID_CONTAINER_PROMPT = "Can't contain an APIInfo object!"; private final String IMAGE_FILE_NAME = "ApiObj.gif"; private final String SNAPIN_NAME = "APIINFO:AddObject"; private final String SNAPIN_DESCRIPTION = "Creates an APIInfo Object"; private final static String CN_ATTR_NAME = "CN"; private final static String OBJECT_CLASS_ATTR_NAME = "Object Class"; private Frame parentFrame; private JMenuItem apiInfoMenuItem; protected Shell shell; protected APIInfoCommon apiCom ; /*********************************************** the next 5 methods are from the Snapin interface ***********************************************/ public SnapinItemInfo[ ] getRegistration() { SnapinItemInfo[ ] siis = new SnapinItemInfo[1]; siis[0] = new SnapinItemInfo( this, NDSNamespace.name, ""); return siis; } public boolean initSnapin(Shell theShell) { shell = theShell; shell.addShellListener(this); apiCom = new APIInfoCommon(shell); return true; } public String getSnapinName() { return SNAPIN_NAME; } public String getSnapinDescription() { return SNAPIN_DESCRIPTION; } public void shutdownSnapin(Shell theShell) { } /*********************************************** the next 2 methods are from the MenuSnapin interface ***********************************************/ public JMenuItem[] getMenuItems() { apiInfoMenuItem = new JMenuItem("API_Info Object"); apiInfoMenuItem.addActionListener(this); JmenuItem[] items = new JMenuItem[1]; items[0] = apiInfoMenuItem; return items; } public String getMenuLocation() { return "File%New"; }// % is used to designate a hierarchical menu install /*********************************************** the next method implements the ActionListener interface ***********************************************/ public void actionPerformed(ActionEvent e) { 1 parentFrame = shell.getShellFrame(); 2 NDSSchema schema = null; 3 NDSSchemaDefinition sd = null; 4 ObjectEntry oe = shell.getTreeSelection(); 5 NDSNamespace ns = (NDSNamespace)shell.getTreeSelectionNamespace(); 6 String parentClass = oe.getObjectType().getUniqueID(); 7 String childClass = APIInfoCommon.API_INFO_CLASSNAME; 8 try 9 { schema = (NDSSchema)ns.getSchemaService(oe); 10 sd = (NDSSchemaDefinition)schema.getSchemaDefinition(); 11 } 12 catch( SPIException ex ) 13 { NmsgBox nbox = new NMsgBox( parentFrame, "SPIException",APIInfoAddObjSnapin" 14 +"ex.getLocalizedMessage(),NMsgBox.ERROR ); 15 return; 16 } 17 18 // Check to see if the selected container can contain the new object. 19 if ( sd.canContain(parentClass, childClass) == true ) 20 { 21 Vector dlgs = new Vector(1); 22 APIInfoAddObjDlg dlg; 23 do 24 { dlg = new APIInfoAddObjDlg( parentFrame,this ); 25 dlgs.addElement(dlg); 26 dlg.show(); 27 } 28 while ( dlg.isAnotherAPIInfoChecked() == true ); 29 30 for( int x = 0 ; x < dlgs.size() ; x++ ) 31 { 32 dlg = (APIInfoAddObjDlg)dlgs.elementAt( x ); 33 if((dlg.isCreateSuccessful()==true)&&(dlg.isAdditionalPropertiesChecked()==true)) 34 { 35 // Get the new Object Entry from the APIInfoAddObjDlg 36 ObjectEntry newOE =dlg.getCreatedObjectEntry(); 37 // Launch the MPEC 38 ObjectEntry[] entries = new ObjectEntry[1]; 39 entries[0] = newOE; 40 shell.showDetails( entries, false ); 41 } 42 } 43 } 44 else 45 { NmsgBox box = new NmsgBox( parentFrame, LABEL , 46 INVALID_CONTAINER_PROMPT, 47 NmsgBox.INFO, null, null); 48 box.show(); 49 } 50} /***************************************************************** APIInfoAddObjDlg calls createNewAPIInfo( ) when user clicks its Create button *****************************************************************/ protected synchronized void createNewAPIInfo(APIInfoAddObjDlg dlg ) { 1 String apiInfoName = dlg.getObjNameFromTextField(); 2 ObjectEntry objEntry = shell.getTreeSelection(); 3 NDSNamespace namespace = (NDSNamespace)shell.getTreeSelectionNamespace(); 4 5 // Get the FDN of the selected container 6 String objFDN = objEntry.getName(); 7 String apiInfoFDN = null; 8 String parentFDN = null; 9 10 try 11 { 12 NDSSchema schemaObj = (NDSSchema)namespace.getSchemaService( objEntry ); 13 Vector attrNDSAPIInfV = new Vector(); 14 addStringAttributeValue(schemaObj, attrNDSAPIInfV , NDSSyntax.SYN_CI_STRING, 15 CN_ATTR_NAME ,apiInfoName ); 16 addStringAttributeValue(schemaObj, attrNDSAPIInfV , NDSSyntax.SYN_CLASS_NAME , 17 OBJECT_CLASS_ATTR_NAME, apiCom.API_INFO_CLASSNAME); 18 addStringAttributeValue(schemaObj, attrNDSAPIInfV , NDSSyntax.SYN_DIST_NAME, 19 apiCom.API_INFO_SERVER_ATTRNAME ,"[Root]"); 20 addStringAttributeValue(schemaObj, attrNDSAPIInfV , NDSSyntax.SYN_DIST_NAME, 21 apiCom.API_INFO_OPRTR_ATTRNAME ,"[Root]"); 22 23 parentFDN = namespace.getObjectFDN(objFDN); 24 25 if(parentFDN.compareTo("") == 0) 26 apiInfoFDN = namespace.createFullObjectFDN( 27 namespace.getRootName(objFDN), apiInfoName); 28 else 29 apiInfoFDN = namespace.createFullObjectFDN(namespace.getRootName(objFDN), 30 apiInfoName + "." + namespace.getObjectFDN(objFDN)); 31 32 NDSClassDefinition apiInfoClassDef=(NDSClassDefinition)schemaObj. 33 getClassDefinition( apiCom.API_INFO_CLASSNAME ); 34 35 ObjectType objType = schemaObj.getObjectType(apiCom.API_INFO_CLASSNAME ); 36 ObjectEntry apiInfoObjectEntry = new ObjectEntry( apiInfoFDN, objType); 37 38 NSObject apiInfoObj = 39 new NSObject( apiInfoObjectEntry, apiInfoClassDef,attrNDSAPIInfV, 0 ); 40 41 // Create the API_Info object 42 namespace.create( apiInfoObj ); 43 shell.refreshCurrentTreeSelection(); 44 dlg.setSuccess(true,apiInfoObjectEntry); 45 } 46 catch( SPIException e) 47 { 48 NmsgBox nbox = new NMsgBox( parentFrame, "SPIException", 49 "createNewAPIInfo"+e.toString(),NMsgBox.ERROR ); 50 } 51 catch( SnapinVetoException sve ) 52 { 53 sve.printStackTrace(); 54 System.out.println(sve.toString()); 55 } 56} /***************************************************************** addStringAttributeValue is called by createNewAPIInfo( ) *****************************************************************/ private void addStringAttributeValue( NDSSchema schemaObj, Vector attrVector , NDSSyntax syntax, String attrName , String attrVal ) throws SPIException { 1 NDSObjectAttribute ndsAttrObj = new NDSObjectAttribute ( 2 (NDSAttributeDefinition) schemaObj.getAttributeDefinition( attrName )); 3 try 4 { 5 ValueComponent valComp = syntax.createValueComponent(attrVal); 6 ndsAttrObj.addComponent(valComp); 7 attrVector.addElement( ndsAttrObj ); 8 } 9 catch( ComponentCreationException ce ) 10 { 11 ce.printStackTrace(); 12 System.out.println("ComponentCreationException - " + ce); 13 } 14} /*********************************************** the next 4 methods implement the ShellListener interface ***********************************************/ public void treeSelectionChanged(ShellEvent event) { 1 try 2 { ObjectEntry oe = shell.getTreeSelection(); 3 NDSNamespace ns = (NDSNamespace)shell.getTreeSelectionNamespace(); 4 5 // Need to pass two object entries to the ns.getobjecteffectiverights call. 6 // The oe for the obj to check on and the oe for the logged in user. 7 8 // Get the logged in user 9 ObjectEntry userOe = ns.getLoginUser(oe); 10 11 // Get rights for trustee 12 NDSObjectRights rights=(NDSObjectRights)ns.getObjectEffectiveRights(oe, userOe); 13 14 if ( rights.hasAddRights() == true ) 15 apiInfoMenuItem.setEnabled(true); 16 else 17 apiInfoMenuItem.setEnabled(false); 18 } 19 catch(SPIException e) 20 { NmsgBox nbox = new NMsgBox( parentFrame, "SPIException", 21 "treeSelectionChanged"+e.toString(),NMsgBox.ERROR ); 22 } 23} public void viewSelectionChanged(ShellEvent event) { } public void pageHostInit(ShellEvent event) { } public void pageHostShutdown(ShellEvent event) { } }
This snap-in starts out like any other with implementation methods for the Snapin interface. You should be pretty familiar with that by now.
One thing that you haven't yet seen in a snap-in, is a listener interface. A Java listener object registers with a source object to be called when something particular happens. The methods to be called are defined in the appropriate listener interface. This snap-in has two listener interfaces, ShellListener and ActionListener.
The ShellListener interface is defined in ConsoleOne. A ShellListener snap-in wants to know if a new tree has been selected, a new view of the current namespace has been selected, or if a Page Host (property book) is being initialized or shutdown.
As you can see in the code above, a ShellListener must implement a method to handle each of these occurences. As you can also see, this snap-in only cares about the TreeSelectionChanged occurrence, which we will discuss in a minute. In any event, this snap-in registers as a listener for Shell events in its initSnapin( ) method.
The ActionListener interface is defined in java.awt.event. Any java.awt.MenuItem or one of its descendants can be a source for Action events (Buttons and other Components can as well.) An action event is generated by the source when a user selects it. The source then sends the event to all of the ActionListeners that have registered with it. Only one method is needed to become an ActionListener and that is actionPerformed( ).
APIInfoAddObjSnapin's actionPerformed( ) Method
The menu item instantiated in the snap-in's getMenuItems( ) method is an Action event source and the snap-in is registered as its listener.
When the user selects the "API_Info Object" menu item under File/New (shown in Figure 3), this snap-in's actionPerformed( ) method will be called to create a new object in the selected NDS tree. During our discussion of this method, you may wish to refer to the NDS namespace object model shown earlier to get a better idea of how the objects interact.
Line 4 An ObjectEntry describing the object currently selected in the namespace navigator view is obtained. This will be the new object's container.
Line 5 An object representing the currently selected namespace, in this case an NDS tree is obtained.
Line 6 The class name for the currently selected object is obtained.
Lines 8-19 As you can see in the object diagram for the NDS namespace, NDSSchema is descended from SchemaService and has a close working relationship with an object called NDSSchemaDefinition descended from SchemaDefinition. SchemaService and SchemaDefinition have lots of nifty methods. In this case, we are instantiating SchemaDefinition so that we can use its canContain( ) method to determine if the class of the currently selected object container is listed in the API_Info class as one of API_Info's potential containers.
Note: An SPIException stands for Service Provider Interface exception. An example of a service provider would be JNDI.
Lines 21-28 This code creates APIInfoAddObjDlg windows as long as the user has "Create another APIInfo Object" checked (The APIInfoAddObjDlg class implements the dialog shown in in Figure 3. Source for this class is not listed in this article). Each dialog object is added to a vector called dlgs (a vector is a kind of dynamic array in java.util). For every APIInfoAddObjDlg that is created, when the user clicks its "Create" button, it calls the snap-in's createNewAPIInfo( ) method to create an object.
Lines 30-40 Each dialog object is pulled out of the Vector and has its "Define additional properties" checkbox examined to see if the user wishes to edit the property book for this object. If the checkbox is checked, the ObjectEntry reference for the object created by the dialog is obtained and used as a parameter to the shell's showDetails( ) method. During the showDetails( ) method, the shell will locate the PageSnapins registered to handle the specified object's type and load it to display the appropriate property book.
APIInfoAddObjSnapin's createNewAPIInfo( ) Method
The createNewAPIInfo( ) method is called by an APIInfoAddObjDlg object when the user selects its "Create" button to create a new API_Info object (see Figure 3).
Note: Before we discuss this method, we need to talk about ConsoleOne namespace name contexts. They are probably different than what you are used to. Because ConsoleOne must manage selections in multiple namespaces, the FDN returned by this method in ConsoleOne includes a namespace identifier. For example, a fully distinguished name would be: Treename/user.orgUnit.org - where Treename is the namespace id and user.orgUnit.org is the initial context.
Line 1 Gets the name for the new object that was entered by the user into the dialog's text field. The assumption is made here that an appropriate container object for the object to be created is selected (see the discussion of the actionPerformed method).
Lines 2-3 References to the currently selected object and its namespace are obtained.
Line 6 A fully distinguished name is obtained for the selected container, (as per note above, this includes tree name).
Lines 12-21 A SchemaService for the namespace is obtained and passed to the addStringAttributeValue( ) method to instantiate the four mandatory API_Info attributes, initialize them with values, and add them to a Vector (the addStringAttributeValue( ) method is discussed below).
Line 23 The namespace.getObjectFDN( ) method recieves an FDN (full distinguished name including the treename's namespace id) and returns the FDN without the namespace id (e.g., recieves TreeName/OrgUnit.Org, returns OrgUnit.Org).
Lines 25-30 This block of code develops a name context string which uniquely describes the location (context) and name for the object to be created. First, it checks to see if the container is the root of the tree. If it is, getRootName( ) obtains the namespace id and concatenates it with the user entered name for the new object. If the object is being created in the context of a real container object, then the new name is added to that context and the namespace id (e.g., TreeName/NewObjName.Org).
Lines 33-36 The NDS namespace's SchemaService is passed API_Info's class name defined in APIInfoCommon to obtain an NDS ClassDefinition for the defined API_Info NDS class. The SchemaService is then used to obtain an ObjectType object describing the API_Info type of NDS object. Finally, the fully qualified distinguished name context string for the new object and the object describing its type are used to instantiate a new ObjectEntry for the new object. At this point all changes have been local. An ObjectEntry is strictly for the use of ConsoleOne it represents an entry that is currently listed in one of its views.
Lines 38-44 First, an NSObject, or namespace object, is instantiated. An NSObject contains the name context, attribute values, class definition, and schema object reference that the NDS namespace snap-in needs to create a real object in the namespace. However, it is still just a local representation of that information. In order to instantiate an object in the real namespace, the namespace's create( ) method must be called.
The namespace associates the attribute names in the ObjectAttributes it obtains from the attribute vector with the attribute names defined in the various categories of the class definition to determine which is mandatory, optional, etc.
After creation, ConsoleOne is told to redraw the namespace's view so that the new object will appear.
APIInfoAddObjSnapin's addStringAttributeValue( ) Method
As you can see in the NDS namespace's object diagram, namespace objects contain ObjectAttributes. An ObjectAttribute will have an AttributeDefinition and may have ValueComponents each of which has a Syntax (this model is pretty consistent with the old way of looking at NDS objects).
The addStringAttributeValue( ) method is called by the createNewAPIInfo( ) method each time createNewAPIInfo( ) wants to instantiate an ObjectAttribute and initialize it with ValueComponents. In API_Info's class each of its attributes is mandatory. Therefore, all of them must be initialized with values.
Line 1 The SchemaService's getAttributeDefinition( ) method is given the schema name for the desired attribute and returns an AttributeDefinition describing the attribute. The AttributeDefinition is then used to instantiate an empty ObjectAttribute (without values).
Lines 5-7 The desired syntax is used to create a ValueComponent containing the specified value. The ValueComponent is then added to the ObjectAttribute. The ObjectAttribute is then added to the vector of ObjectAttributes that will eventually be sent to the namespace to create a namespace object in the createNewAPIInfo( ) method.
APIInfoAddObjSnapin's treeSelectionChanged( ) Method
The treeSelectionChanged event comes from the Shell everytime a new object is selected in the tree, even if it is in the same namespace as the last selection. The viewSelectionChanged event, on the other hand, only comes when a new object in the current namespace's view is selected.
The purpose of the treeSelectionChanged( ) method in this snap-in is to check to see if the user has rights to create an API_Info object in the context of the new selection. If they do, the "API_Info Object" item that was added to "File/New" is enabled. If not, the menu item is disabled.
Developing APIInfoEditPageSnapin
Like the NetWare Administrator application, ConsoleOne can display a property book showing the attribute values in a selected object. This allows an administrator to edit values. The purpose of a page snap-in is to provide the shell's PropertyBook (hereafter referred to as PageHost) with the information it needs to display a page of object information.
When the user selects an object in some namespace and then selects "Details," ConsoleOne responds by instantiating a property book. Each object gets a Configuration and a Rights page in the property book for free from the NDS namespace, no snap-ins required. Beyond these two pages, the property book has no idea what else to display about the selected object. So, the PageHost calls upon all page snap-ins registered for the selected object in the current namespace.
The number of additional pages in an object type's property book is determined by the number of page snap-ins developed for the object's class. API_Info has two additional property pages and therefore requires two page snap-ins (see Figure 5). As in NetWare Administrator, pages may support any combination of attributes.
Figure 5: Two snap-ins implement the abstract APIInfoEditPagesSnapin class to support API_Info's two attributes, APIInfo:Server and APIInfo:Operator.
Like APIInfoToolBarMenuSnapin, APIInfoEditPageSnapin had two purposes. First, it was to demonstrate how to implement a PageSnapin interface that would interact with the PageHost. The second purpose was to add object specific GUI to a page tab in the Property Book and obtain and modify object information for the page using the NDS namespace.
However, like the APIInfoToolBarMenuSnapin example found earlier, there is not enough room in this article for everything. So, in this issue, we will only demonstrate how to implement a PageSnapin interface. We will describe the API_Info functionality to display a page in the PageHost and edit attributes in the April issue.
APIInfoEditPagesSnapin Source Description
API_Info implements two snap-ins from an abstract class to produce two pages for its PropertyBook. Since API_Info only has two attributes, this is really overkill. But, it does allow us to demonstrate how to implement multiple pages. APIInfoEditPageSnapin is the abstract class used as the basis for API_Info's two PageSnapin classes. Shown below is the complete listing for the APIInfoEditPageSnapin and the two PageSnapin classes which implement it.
What is not shown is the code for the APIInfoAttrEditPanel which provides the API_Info functionality to build a page view for the PropertyBook and the functionality to modify an object's attribute values APIInfoAttrEditPanel will be described in the April issue.
// file APIInfoEditPageSnapin.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.mpecpages; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import com.novell.application.ncc.snapin.*; import com.novell.admin.ns.*; import com.novell.admin.ns.nds.*; import com.novell.admin.common.exceptions.*; import com.novell.utility.nmsgbox.*; import java.awt.*; import java.util.*; abstract public class APIInfoEditPageSnapin implements PageSnapin { private final boolean HAS_HELP = true; private final String MENU_NAME = ""; private boolean isDirty = false; private PageHost pageHost; private Shell shell; protected APIInfoAttrEditPanel thePanel; private ObjectEntry oe; private NSObject nsObj; private NDSNamespace ns; /******************************************************************** Below are the methods that this abstract class's descendants must implement ********************************************************************/ abstract APIInfoAttrEditPanel // for use by this class getAPIInfoEditPanel( Shell theShell, Frame parent, NDSNamespace NDSNs, NSObject obj ) throws SPIException; // public String getSnapinName() // from Snapin interface // public String getSnapinDescription()// from Snapin interface // public String getTabName() // from PageSnapin interface // public String getTabID( ) // from PageSnapin interface /******************************************************************** Below are some Snapin abstract method implementations ********************************************************************/ public SnapinItemInfo[] getRegistration() // implements Snapin method {SnapinItemInfo[] info = new SnapinItemInfo[1]; info[0]=new SnapinItemInfo(this,NDSNamespace.name,"APIINFO:APIInfoClass",true); return info; } // called before initSnapin( ) to initialize the pageHost variable public void setPageHost (PageHost theHost)// implements PageSnapin method { pageHost = theHost; } public boolean initSnapin(Shell theShell) // implements Snapin method { 1 shell = theShell; 2 ObjectEntry[] oeArray = pageHost.getObjects(); 3 ns = (NDSNamespace)oeArray[0].getObjectType().getNamespace(); 4 oe = oeArray[0]; 5 nsObj = null; 6 try 7 { nsObj = ns.getDetails(oe, pageHost); } 8 9 catch(SPIException e) 10 { return false; } 11 12 try 13 { thePanel = getAPIInfoEditPanel(shell,pageHost.getFrame(),ns,nsObj);} 14 15 catch(SPIException e) 16 { return false; } 17 18 return true; 19 } public void shutdownSnapin(Shell theShell)// implements Snapin interface method { } /*********************************************** the next 8 methods partially implement the PageSnapin interface /***********************************************/ public boolean hasHelp() // implements PageSnapin interface method { return HAS_HELP; } public void showHelp() // implements PageSnapin interface method { String helppath = shell.getHelpPath("NovellAdminHelp") + "\\Basics.htm"; shell.launchHelp(helppath); } public boolean saveData() // implements PageSnapin interface method { thePanel.writeData(); try { ns.update(nsObj); } catch (SnapinVetoException e) { NmsgBox msg = new NmsgBox ( shell.getShellFrame(), "Changes were vetoed", "Another process running in your shell has vetoed these modifications!", NmsgBox.INFO); msg.show(); } catch (SPIException e) { System.out.println(e.toString()); } return true; } public Component getPage() // implements PageSnapin interface method { return thePanel; } public boolean isDirty() // implements PageSnapin interface method { return isDirty; } public String getMenuName() // implements PageSnapin interface method { return MENU_NAME; } public boolean canSave() // implements PageSnapin interface method { return true; } public boolean canChange() // implements PageSnapin interface method { return true; } } // file APIInfoServerPage.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.mpecpages; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.*; import com.novell.application.ncc.snapin.*; import com.novell.admin.ns.*; import com.novell.admin.ns.nds.*; import com.novell.admin.common.exceptions.*; public class APIInfoServerPage extends APIInfoEditPageSnapin { private final String TAB_NAME = "Server Distinguished Name"; private final String SNAPIN_NAME = "APIINFO:ServerPanel"; private final String SNAPIN_DESCRIPTION = "Allows editing of Server Page"; APIInfoAttrEditPanel getAPIInfoEditPanel( Shell theShell, Frame parent, NDSNamespace ndsNs,NSObject theNSObj) throws SPIException { return new APIInfoServerPanel(theShell,parent,ndsNs,theNSObj); } public String getTabName() // implements PageSnapin interface method { return TAB_NAME; } public String getTabID( ) // implements PageSnapin interface method { return TAB_NAME; } public String getSnapinName() // implements Snapin interface method { return SNAPIN_NAME; } public String getSnapinDescription() // implements Snapin interface method { return SNAPIN_DESCRIPTION; } } // file APIInfoOperatorPage.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.mpecpages; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.*; import com.novell.application.ncc.snapin.*; import com.novell.admin.ns.*; import com.novell.admin.ns.nds.*; import com.novell.admin.common.exceptions.*; public class APIInfoOperatorPage extends APIInfoEditPageSnapin { private final String TAB_NAME = "Operator Distinguished Name"; private final String SNAPIN_NAME = "APIINFO:OperatorPanel"; private final String SNAPIN_DESCRIPTION= "Allows editing of Operator Page"; APIInfoAttrEditPanel getAPIInfoEditPanel( Shell theShell, Frame parent, NDSNamespace ndsNs,NSObject theNSObj) throws SPIException { return new APIInfoOperatorPanel(theShell,parent,ndsNs,theNSObj); } public String getTabName() // implements PageSnapin interface method { return TAB_NAME; } public String getTabID( ) // implements PageSnapin interface method { return TAB_NAME; } public String getSnapinName() // implements Snapin interface method { return SNAPIN_NAME; } public String getSnapinDescription()// implements Snapin interface method { return SNAPIN_DESCRIPTION; } }
Refer to the NDS namespace object model shown in Figure 4 for the following discussion.
Most of the real work in this snap-in is done in the APIInfoAttrEditPanel class which will be explained in the April issue. However, we can briefly explain the purpose of each method in the PageSnapin interface.
First, as was said earlier, each page in a PropertyBook (hereafter called PageHost) has to have a PageSnapin to provide its GUI and interact with the object's namespace to edit attribute values.
When a user selects "Details" under ConsoleOne's "File" menu with an API_Info object selected, the Shell's showDetails( ) method will be called with the API_Info ObjectEntry as a parameter. The showDetails( ) method will get the type from the ObjectEntry and use it to look up all of the PageSnapin's registered for that type and then load them.
App developers can also provide access to their object's property pages by installing a "Properties" item in the object's pop-up menu using a PopUp menu snap-in. This is described in the next section of this article.
The setPageHost( ) method defined in PageSnapin will be called by ConsoleOne 'before' initSnapin( ) method. This way the initSnapin( ) method can refer to the PageHost.
APIInfoEditPageSnapin's initSnapin( ) Method
When either the APIInfoServerPage or APIInfoOperatorPage is loaded, its setPageHost( ) method is called first, followed by its initSnapin( ) method.
Line 2 ConsoleOne's PageHost can display pages for more than one object at a time. The code in this snap-in assumes that only an API_Info object is selected. The PageHost method getObjects( ) returns an array of the ObjectEntrys representing all of the objects in a selection.
Lines 7-13 An NSObject, or namespace object, contains the name context, attribute values, class definition, and schema reference that completely describes the object to the NDS namespace. The namespace method getDetails( ) is used to obtain an NSObject for the selected API_Info object.
Notice that the PageHost object reference is sent to the getDetails( ) method. The reason for this is to make sure that all of the PageSnapins installed with this PageHost use the same NSObject. If each of the pages edited their own instantiation of NSObject the edits would potentially get out of sync. Because PageHost maintains a single NSObject for use by all of its pages, all edits are synchronized.
An APIInfoAttrEditPanel is then instantiated passing the NSObject with all of its information in the constructor. The APIInfoAttrEditPanel will then read the attribute values from the NSObject and install them in its view as the current attribute values.
After all of the PageHost's PageSnapins are initialized, the PageHost will call each snap-in's getPage( ) method. Each APIInfoEditPageSnapin will then return a reference to its APIInfoAttrEditPanel as the view which the PageHost will then install as a page.
As the user edits the attribute values displayed in its page, the APIInfoAttrEditPanel will modify the corresponding values in the NSObject. However these changes are local to the ConsoleOne application and will not be committed to the distributed NDS database until the user clicks the "OK" button in the PageHost. When this happens, the PageHost will call each PageSnapin's saveData( ) method.
In the APIInfoEditPageSnapin's saveData( ) method (inherited by both APIInfo PageSnapins), the APIInfoAttrEditPanel's writeData( ) method is called to commit any recently changed values to the NSObject. Finally, the data in the NSObject is committed to the distributed NDS database with the namespace's update( ) method.
Below is some brief info on some of the other methods required by the PageSnapin interface.
Method |
Description |
isDirty() |
Called by the Shell when it wants to know if the page has been modified or edited. A true returned by this method, results in the enabling of the OK button in the PageHost. |
getMenuName() |
Provides a unique menu name in case more than one page has the same tab name. If this happens, a menu will appear with the names from the contending snap-in's getMenuName( ) methods. The snap-in selected in the menu selection will then provide the tab's GUI. |
hasHelp() |
Indicates whether or not the panel has help. |
canSave() |
Returns true if it is ok to save, false otherwise. |
canChange() |
Called when another tab is selected. You can check for the validity of your page data here. |
Developing APIInfoPopupMenuSnapin
Application developers can provide access to all sorts of object specific functionality by extending the object's default popup menu. APIInfoPopupMenuSnapin extends an API_Info object's popup menu by installing a "Properties" menu item into it. Selecting the "Properties" item will then cause APIInfoPopupMenuSnapin to use the Shell's showDetails( ) method to launch the PropertyBook (or PageHost), as shown in Figure 6.
Figure 6: APIInfoPopupMenuSnapin installs a "Properties" menu item in API_Info object's popup menus.
APIInfoPopupMenuSnapin Source Description
Below is a listing of the files which implement APIInfoPopupMenuSnapin. A brief explanation will follow the listing.
// file APIInfoPopupMenuSnapin.java package com.novell.admin.ndssnapins.classes.shared.apiinfo.popup; import com.novell.admin.ndssnapins.classes.shared.apiinfo.*; import java.awt.event.*; import com.sun.java.swing.*; import com.novell.admin.ns.nds.*; import com.novell.application.ncc.snapin.*; public class APIInfoPopupMenuSnapin implements MenuSnapin, ActionListener {protected final String SNAPIN_NAME = new String("APIINFO:PopupMenuSnapin"); protected final String SNAPIN_DESCRIPTION=new String("xtnds API_Infoobj popup menu"); private Shell shell; public SnapinItemInfo[] getRegistration() {SnapinItemInfo[] siis = new SnapinItemInfo[1]; siis[0] = new SnapinItemInfo(Shell.SNAPIN_POPUP_MENU, getClass().getName(), NDSNamespace.name, APIInfoCommon.API_INFO_CLASSNAME ); return siis; } public JMenuItem[] getMenuItems() { JmenuItem[] items = new JmenuItem[1]; items[0] = new JMenuItem("Properties..."); items[0].addActionListener( this ); return items; } public void actionPerformed(ActionEvent event) { ObjectEntry[] entries = shell.getCurrentSelections(); shell.showDetails(entries,false); } public String getMenuLocation()// Returns "" to be placed at top level of the {return "" ;}// current popup menu hierarchy public String getSnapinName() {return SNAPIN_NAME;} public String getSnapinDescription() {return SNAPIN_DESCRIPTION;} public boolean initSnapin(Shell theShell) { shell = theShell ; return true; } public void shutdownSnapin(Shell theShell) {} }
MenuSnapin can be used to extend any menu in ConsoleOne. APIInfoPopupMenuSnapin extends MenuSnapin to add an item to its object's popup menu.
In getRegistration( ), APIInfoPopupMenuSnapin is registered against its object's type.
getMenuItems( ) is a MenuSnapin interface method. When this snap-in's implementation of getMenuItems( ) is called it instantiates one menu item referring to itself as the item's ActionListener (meaning APIInfoPopupMenuSnapin implements the ActionListener's actionPerformed( ) method).
Since APIInfoPopupMenuSnapin is the ActionListener for the menu item, when the user selects it, APIInfoPopupMenuSnapin's actionPerformed( ) method is called. This actionPerformed( ) method first gets an ObjectEntry representing the currently selected object from the shell. This will be an API_Info object because that is what this snap-in is registered against. So, when the Shell's showDetails( ) method is called, the snap-ins for API_Info's property pages will be loaded and displayed in the PageHost (or PropertyBook) completing the intended function of this snap-in.
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.