Developing DeveloperNet 2000 Components for IntranetWare
Articles and Tips: article
Senior Research Engineer
Developer Information
KEN LOWRIE
Vice President of Engineering
HiTecSoft Corporation
01 Dec 1996
Focuses on the part of Component Management Services that does component management for server-based applications.
- Introduction
- NetBasic/NMX Directory Structure
- Developing an NMX Component
- Installing an NMX Component
- Accessing Services of an NMX Component
Introduction
Last month's issue of DevNotes, outlined NetBasic's place in the Net2000 (now known as DeveloperNet 2000) architecture ("NetBasic: IntranetWare's Scripting Language," Novell Developer Notes, November 1996, p 14). In this DevNote, we'll focus on the part of Component Management Services (see Figure 1), that does component management for server-based applications.
Figure 1: The DeveloperNet 2000 application development architecture.
Although DeveloperNet 2000's Component Management Services is depicted as a single layer in the architecture diagram, it is actually a heterogeneous set of services, depending upon the platform and development environment. Depending upon whether you develop client applications or server applications, or develop in C/C++, Java, Visual Basic, or Delphi, Component Management Services may take the form of controls or class libraries. For server-based applications, Component Management Services are provided by Network Management Extensions (NMX). NMX is a NetWare Loadable Module (NLM) that is included with IntranetWare, and can be added to NetWare 3 or 4 servers, either by itself or with NetBasic from HiTecSoft. NMX has two primary purposes:
Manage Loading and Unloading of NLMs "NMX-compliant" NLMs can be loaded and unloaded as needed. Most NLMs remain in memory after they have been loaded until they are unloaded by the console operator. NMX unloads NMX-compliant NLMs when no client program accesses any of the NLM's services for a specified period of time, thus freeing cache memory that the server can use for other purposes.
Provide a Common Interface to Services NMX provides an interface that components (written as NLMs or Basic scripts) can use to expose services to other components. NMX provides a mechanism for an NLM or Basic script to discover what services are available, to call functions provided by NMX-compliant components, to pass data as parameters to registered functions, and to receive data returned by functions. For example, a Basic script running on an IntranetWare server can query Novell Directory Services (NDS) to get information about a user, then retrieve data pertaining to that user from an Oracle database.
Writing your server-based applications (NLMs) to be NMX-compliant not only makes it so that your application is more friendly in its use of memory (it can be unloaded when not in use), but also enables you to provide services to other components. Take, for example, a backup system. Most backup systems for NetWare are large, feature-rich applications. They usually have a job queue and a database containing tape catalogs and job histories. They use a lot of memory on a server, but generally only operate during off-hours. If a backup system were rewritten to be NMX-compliant, it could be loaded by a Basic script, then unloaded when the backup was complete. In addition, the backup system could provide access to the job queue and databases. A system integrator or value-added reseller could customize the interface to the backup system for each of his customers.
NetBasic/NMX Directory Structure
IntranetWare's INSTALL.NLM installs NMX and NetBasic along with the other server utilities. To get started developing NMX components on a NetWare 3.12 or 4.1 server, you can download the NMX SDK from HiTecSoft's web site (http://www.hitecsoft.com/). The directory structure used by NMX and NetBasic is as follows:
Directory
|
Description
|
SYS:/NETBASIC/INCLUDE |
Include files/inline procedures |
SYS:/NETBASIC/USER |
Put your console scripts here |
SYS:/NETBASIC/UTIL |
Scripts provided by HiTecSoft |
SYS:/SYSTEM |
NETBASIC.NLM and NMX.NLM |
SYS:/SYSTEM/NMX |
All other DeveloperNet 2000 components |
Developing an NMX Component
To illustrate what's involved in developing an NMX-compliant component, let's look at an example program, an NLM written in C, called NETNLM. We'll just look at the relevant sections of the program here; you can download the complete source file, NETNLM.ZIP, from Compuserve's NOVLIB forum, or from Novell Research's web page, http://www.novell.com/research.
NETNLM provides a single API, NET:NLM:UPDATE, that updates an NLM file. It takes two parameters: the path to an existing NLM file, and the path to a "new" file with which to update the existing file. If the new file has a later version number than the existing file, NET:NLM:UPDATE overwrites the existing file. If the version number is the same or older, the API reports that the existing file is up to date.
Required Structures For an NLM to be NMX-compliant, it must have a fairly well-defined structure. The NMX SDK includes example programs to illustrate the structure, and which you can use as templates for your code. The required data structures are:
_NMX Structure. A static structure that encapsulates all of the component's functions and data: the NLM ID, thread and thread group IDs, screen ID, version numbers, copyright information, console messages to be displayed when the component is loaded, the list of exported APIs, and the API dispatcher function.
_NMXParamCheck Structure. Defines the parameters of each API in the component. The first API in the list must be NMXLibProc, the library message handler (see below). The parameter list for each API must be terminated by two NULLs. NETNLM only exports one API, so there are only two entries in its _NMXParamCheck structure:
_NMXParamCheck NMXLibProcParams[] = { NMX_BOOLEAN_TYPE, "Success", // return value Lib status NMX_STRING_TYPE, "NLM_Name", // param number 1 NLM name NMX_STRING_TYPE, "CLASS_Name", // param number 2 Class Name for INIT function NMX_OPTIONAL_TYPE, "Option1", // optional params 3 from user if any NULL, NULL, }; _NMXParamCheck NetNlmUpdateParams[] = { NMX_INTEGER_TYPE, "Success", // return value Lib status NMX_STRING_TYPE, "Current_NLM", // the existing NLM NMX_STRING_TYPE, "New_NLM", // the new NLM NULL, NULL, };
An Enumerated List of API Handles. Associates an identifier with an integer for each API exported by the component. The APIs enumerated in this list must be in the same order as in the _NMXFunc structure, which is in alphabetical order.
enum { NET_NLM_UPDATE = 0, _NMXLIBPROC };
_NMXFunc Structure. Connects functions with the function names that will be registered with NMX, and the function parameter lists. Because NMX builds a binary tree to facilitate efficient searching for registered APIs, the functions in this list must be in alphabetical order. The list must be terminated by three NULLs.
_NMXFunc MyFuncList[] = { // function name function index function parameters // ---------------------------------------------------------------- "NLM:UPDATE", NET_NLM_UPDATE, NetNlmUpdateParams, "_NMXLIBPROC", _NMXLIBPROC, NMXLibProcParams, NULL, NULL, NULL, };
An Unloader Function. This function is called if the NMX component is unloaded from the command line, and there are no client components registered (that is, no one is using your component). Mainly just calls NMXLibraryDeregister to deregister the component with NMX.
A Library Message Handler. Handles all NMX events. Primarily, this is to search the list of APIs exported by your component and call the correct one when requested by a client. There are default handlers for most events, but NMX allows you to customize what takes place when one of these events occurs. For example, NMX generates an event when a new component registers as a client of the library. This allows your library to allocate memory if your library requires unique data to be stored for each client.
NETNLM's main function is quite simple, since all it does is register the NLM as an NMX component:
int main( int argc, char *argv[] ) { //Fill in the fields of the _NMX structure MyLib.LibThreadID = GetThreadID(); MyLib.LibHandle = -1; MyLib.LibNLMID = GetNLMID(); MyLib.LibThreadGroupID = GetThreadGroupID(); MyLib.MinVer = MIN_VER; MyLib.MajVer = MAJ_VER; MyLib.ClassName = "NET:NLM"; MyLib.ModuleName = "NETNLM"; MyLib.Message1 = "NMX Library to update NLMs."; MyLib.Message2 = "______"; MyLib.CopyRight = "Copyright 1996 HiTecSoft Corp."; MyLib.LibScreenID = GetCurrentScreen(); MyLib.NumberOfClients = -1; MyLib.FuncList = MyFuncList; MyLib.EventList = NULL; MyLib.NMXLibProc = (void *)_NMXLibProc; //Register the unloader function AtUnload(Unloader); // become a client of NMX if (!NMXLibraryRegister(NULL,ClientUnload,&MyLib)){& ConsolePrintf("\r%s: %s\r\n", MyLib.ClassName, "Unable to register as NMX Library."); return -1; } return(0); }
This is all that's required to develop a minimal NMX-compliant component. All that remains is to write the functions that implement the APIs that are exported by the component.
Installing an NMX Component
Once we have written and compiled NETNLM, all that is required to install it is to copy NETNLM.NLM to SYS:SYSTEM\NMX. When a Basic script (for example) calls NET:NLM:UPDATE, NMX uses the name of the function to locate the NLM exports the function. It can then load the function, verify that it exports NET:NLM:UPDATE, and call the API.
Accessing Services of an NMX Component
To test NETNLM, we wrote a simple Basic script that calls NET:NLM:UPDATE. It specifies the paths of two NLM: the existing NLM, which is to be updated; and the new NLM.
#include "neterr.h" Sub Main newfile = "sys:test/bscan.nlm" curfile = "sys:nlms/bscan.nlm" ccode = NET:Nlm:Update(curfile,newfile) If( ccode = NET_NLM_SUCCESS ) Print("NLM has been updated!") NewLine Else If( ccode = NET_NLM_CURRENT ) Print("The NLM is already up to date!") NewLine Else Print("Failed on update. Reason: ", ccode) NewLine Endif Endif End Sub
You can run this script from the NetBasic interpreter on the server console. If SYS:TEST\BSCAN.NLM has a more recent version number than SYS:NLMS\BSCAN.NLM, NET:Nlm:Update overwrites the existing NLM with the new version. Otherwise it reports that the NLM is up to date.
You can also call NET:NLM:UPDATE from an NLM written in C/C++, using the NMXC_EZCall function, as shown below:
#include <stdio.h> #include "conio.h" #include "neterr.h" #include "NMXLIB.H" void main (void) { void *cp,*rv; //context pointer and return value int ccode; cp=NMXC_ContextCreate(GetCurrentScreen()); // Create a context // call the function rv=NMXC_EZCall(cp,"NET:NLM:UPDATE", NMX_STRING_C_TYPE,"SYS:\\TEMP\\NETTEST.NLM", NMX_STRING_C_TYPE,"SYS:\\TEST\\NETTEST.NLM", NULL, NULL); // Use the NMXC_RV... functions to access the value returned ccode = NMXC_RVInteger(cp, rv); if (ccode == NET_NLM_SUCCESS) printf("NLM file updated successfully\n"); else printf("Unable to update NLM\n"); NMXC_RVFree(cp, rv); // free the value returned from the function NMXC_ContextDestroy(cp); }
For more information, visit the web site at http://hitecsoft.com/">http://hitecsoft.com/">http://hitecsoft.com/.
* Originally published in Novell AppNotes
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.