Package nesstar.api

The core classes of the Nesstar API.

See:
          Description

Interface Summary
ExecutorLog A log for an Executor.
HTMLViewer The Nesstar HTMLViewer
NewObjListener A listener for creation of new objects.
Type A Type, for example String or Integer, used to indicate
 

Class Summary
AType A Type, for example String or Integer, used to indicate
BasicExecutorLog A simple implementation of ExecutorLog.
BasicObj A basic remote object, useful as a default mapping object.
Bookmark A description in both human and machine understandable form of an operation.
BooleanType A Boolean.
Catalog A Catalog, a container for searchable metadata and processable data.
CatalogHome An API class for the CatalogHome Code automatically generated from ARGO/UML
CatalogTest A Test class for Catalog.java Automatically generated from ARGO/UML
CatalogTestBase A Base Test class for Catalog.java Automatically generated from ARGO/UML
CollectionType A Collection.
Dataset A dataset, whose metadata is defined according to the DDI standard and whose data is stored in one or more NSDSTAT files.
DatasetEx
An extension of Dataset, adds a few convenience methods.
DatasetHome An API class for the DatasetHome Code automatically generated from ARGO/UML
DatasetTest A Test class for Dataset.java Automatically generated from ARGO/UML
DatasetTestBase A Base Test class for Dataset.java Automatically generated from ARGO/UML
DBSearchEx Perform a search on a database.
DoubleType A double precision float number.
Executor A manager for the execution of an URL.
Explore
Explore a dataset.
ExploreCube  
ExploreCubeOp  
ExploreEx The state that has to be preserved to rebuild an Explorer Window.
Explorer A rapresentation of the Explorer, used by the Nesstar Explorer
ExplorerHome An API class for the ExplorerHome Code automatically generated from ARGO/UML
ExplorerOp An Explorer operation.
ExplorerTest A Test class for Explorer.java Automatically generated from ARGO/UML
ExplorerTestBase A Base Test class for Explorer.java Automatically generated from ARGO/UML
FileType A File.
FloatType A single precision floating number.
IntegerType An Integer.
Learn
Acquire new information by reading the contents of the given URL.
LearnOp Acquire new information by applying an operation on a remote object
Method
A Method

Code automatically generated from APIMaker on Mon Dec 18 14:33:33 GMT+00:00 2000
MethodInvocation A remote method invocation.
MIMEObject A MIME object (actually a MIME text object such as text/html).
NetDB A repository and cache for nesstar_rdf objects information from the internet.
ObjectType A generic Object.
Operation A generic operation.
Parameters A set of method parameters.
PasswordType A Password.
PathIterator An iterator on a path.
Protocol Client/Server protocol.
ProtocolTest  
Search
Perform a search in one or more catalogs.
SearchEx Perform a search.
Server A server used to publish data and metadata.
ServerEx A server used to publish data and metadata.
ServerHome An API class for the ServerHome Code automatically generated from ARGO/UML
ServerNewObjListener A listener for creation of new objects.
ServerTest A Test class for Server.java Automatically generated from ARGO/UML
ServerTestBase A Base Test class for Server.java Automatically generated from ARGO/UML
StringType A String.
TestExploreCubeOp  
TestTrendOp  
Trend  
TrendOp  
URLType A URL.
User
A User.
Version Description of the Class
VoidType A void (null) result.
 

Exception Summary
AccessException The exception that is returned when the user hasn't the right to execute an operation.
TargetException The exception that is returned when the user hasn't the right to execute an operation.
 

Package nesstar.api Description

The core classes of the Nesstar API.

If you are a Java programmers interested in developing client-side applications (Web based, Java applications, etc.) for the Nesstar system this document is the right place to start.

It describes the Nesstar API, an object-oriented interface to Nesstar that hides most of the complexity of the Nesstar HTTP/RDF network protocol.

A fuller description of the Nesstar Network Architecture can be found at http://www.nesstar.org/sdk/.

Check in particular:

NESSTAR: A Semantic Web Application for Statistical Data and Metadata
Brief introduction to the NESSTAR system and architecture.
NEOOM: A Web and Object Oriented Middleware System
A detailed technical description of the Nesstar architecture

What Does Nesstar Look Like Through the Java API?

As a set of Java objects. The API tries to mimic as closely as possible the way local Java objects are accessed hiding the details of the Nesstar network protocol used to access the remote Nesstar objects.

What Can I Do with the Nesstar Java API?

The API provides an object-oriented interface to Nesstar objects that supports:

  1. getting 'proxies' for remote objects
  2. reading remote object properties
  3. traversing object relationships
  4. execution of remote object methods
  5. bookmarks
  6. caching of remote objects
  7. (multiple) security challenges
  8. HTTP and HTTPS network protocols

Overall Operation

Nesstar is a distributed object-oriented system built on top of WWW technology.

Nesstar objects are a particular kind of Web resource. Just as any other Web resource they "live" at a given URL and can be accessed by entering their URL in a Web browser.

When you access the URL of a Nesstar object what is returned is a description of the current state of the object, that's to say the value of the object properties at the time the access was performed. The information regarding the object state is coded in XML/RDF (the XML serialisation of RDF).

There are currently a number of Nesstar servers on the Net, each one of them containing thousands of Nesstar objects. To show what a Nesstar object looks like we will pick one object from the demonstration Nesstar server at http://nesstar.essex.ac.uk.

The object we are interested has the URL: http://nesstar.essex.ac.uk/obj/cServer/DEMO. If you click on this link your browser, if XML compatible, will display a short XML document that should look more or less like this:

<r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:s="http://www.w3.org/TR/1999/PR-rdf-schema-19990303#" xmlns:n="http://www.nesstar.org/rdf/" xmlns:on="http://www.nesstar.org/rdf/common/" xmlns:op="http://www.nesstar.org/rdf/common/Server#" xmlns:ncp="http://www.nesstar.org/rdf/Server#">

<on:Server r:about="http://nesstar.essex.ac.uk:80/obj/cServer/Demo">
<s:label>Demo</s:label>
<s:comment>The Demo Server.</s:comment>
<op:homes r:resource="http://nesstar.essex.ac.uk:80/obj/cServer/Demo@homes" />
<op:securePort>443</op:securePort>
<ncp:debug>on</ncp:debug>
<ncp:port>80</ncp:port>
<ncp:revision>Fri Mar 14 16:47:14 GMT 2003</ncp:revision>
<ncp:version>2.16</ncp:version>
<ncp:services r:resource="http://nesstar.essex.ac.uk:80/obj/cServer/Demo@services" />
<ncp:catalogs r:resource="http://nesstar.essex.ac.uk:80/obj/cServer/Demo@catalogs" />
</on:Server>

</r:RDF>

The top level element of the returned XML document is <http://www.w3.org/1999/02/22-rdf-syntax-ns#RDF/> (in the display it appears as <r:RDF/>, with r being the XML namespace identified by the URL http://www.w3.org/1999/02/22-rdf-syntax-ns#). This element states that the document is coded according to the RDF standard.

Inside there is a <http://www.nesstar.org/rdf/common/Server/> (<on:Server/>) element that is the one the contains the actual description of the object we are accessing.

The name of the element indicates the type or class of the returned object. It tells us that we are looking at an element of type <http://www.nesstar.org/rdf/common/Server/> (from now on we will refer to it as just Server for short).

This is the first important thing to know about Nesstar objects. Just as Java objects, Nesstar objects are typed. They are always instances of a specific class that corresponds to a specific concept.

Note:
A Nesstar class is just another Nesstar object (just like a Java Class is also a Java object). As any Nesstar object its description can be found at its URL. If you point your browser to http://www.nesstar.org/rdf/common/Server you will find a description of the Server class plus a set of other objects that describe the properties and methods (the operations) of the objects that are instances of the class.

Note:
Nesstar objects are normally hosted in specialised Nesstar servers but, if they have properties but no methods, they can also be stored in files and published using a normal Web server.

Most Nesstar classes represent statistical concepts such as datafiles, variables, groups of variables, etc. but there are also classes, just like Server, that correspond to non-statistical concepts.

Server objects are actually a bit special and well worth knowing about. They play in Nesstar servers a role similar to that of a home page in a Web server. A Web home page contains key information about a Web site and it is the starting point from which, usually, all other Web pages on that particular site can be reached. Similarly Server objects, of which there is only one per Nesstar server, contains key information regarding the server. They are also the starting point from which it is possible to reach any other object stored in the server.

In addition to its type an object has, usually, other properties as well. Each property is coded as an XML element inside the object class element.

For example in our Server object there are a couple of properties that state the version and revision date of the Nesstar server:

<ncp:version>2.16</ncp:version>

<ncp:revision>Fri Mar 14 16:47:14 GMT 2003</ncp:revision>

Another couple of interesting properties are label and comment. They respectively contain the human readable name and a description of the object:

<s:label>Demo</s:label>

<s:comment>The Demo Server.</s:comment>

While version and revision properties are specific to Server objects label and comment are generic properties. All Nesstar objects should have a label and, whenever possible, they should also have a comment.

Retrieving Nesstar Objects the Easy Way

As we have seen the way Nesstar objects are published on the Internet is very simple: they are XML documents accessible through an HTTP GET call. Therefore they can be easily accessed and processed using standard XML and HTTP libraries that are nowadays available for any programming language.

As a Java programmer you have an even easier option. You can use the API to access Nesstar objects through an object oriented interface.

The API makes Nesstar objects easily accessible by providing, for every type of object, a corresponding 'proxy' class. All accesses to remote Nesstar objects take place through the proxy classes. The developer is therefore isolated from all the XML/HTTP protocol low level details.

So, for example, Nesstar Server objects are mapped by the API to instances of nesstar.api.common.Server objects.

Here is an example of how we can create and use a Server proxy object using the API:

 // Initialise the API
   Init.testInit();
 // The URL of a Server object
   URL serverURL = new URL("http://nesstar.essex.ac.uk:80/obj/cServer/Demo");
 // Retrieve the object
   nesstar.api.common.Server server = (nesstar.api.common.Server) Protocol.getDB().retrieve(serverURL);
 // Read the object label/name 
 // Note: out() is equivalent to System.out.println()
   String label = server.getLabel();
   out("label=" + label);
 // Read the server version
   String version = server.getVersion();
   out("version=" + version);
 

The output of the program should be:

label=Demo
version=2.16

Initialising the API and the Default Proxies Database

The example program starts by initialising the API. To simplify this process Init contains a set of predefined init methods.

As part of the initialisation the API creates an in-memory default proxy object cache. This is used to store the proxies for the Nesstar objects. The default object cache is an instance of NetDB and is accessed with Protocol.getDB() (Protocol is one of the API's central classes. It provides access to the basic API settings and components).

Retrieving Objects

Just like any other Web resource a Nesstar object can be easily accessed through its URL.

In the example a Server object is retrieved by URL through the object cache using RDFDB.retrieve(java.net.URL).

RDFDB.retrieve(java.net.URL) first checks if the object is already cached. If this is not the case it performs an HTTP GET operation to the object URL. If a correct RDF description of the required object is found at the provided URL the corresponding proxy instance is automatically created, cached and then returned to the calling application.

If you know in advance the class of the object that you want to retrieve you can also use the retrieve method of the proxy class and avoid an explicit class cast:


Server server = Server.retrieve(serverURL);.

Refreshing Objects

One thing to remember is that proxy objects are not immediatly synchronized with the remote objects they represents. If a property of a remote object changes, or if the remote object disappears completely, this is not immediatly reflected in its proxies.

Proxies do become 'stale' though, but only after a while. If they are 'stale', the next time that they are accessed with a getX operation they will automatically refresh their contents by reloading their RDF description from the Net. By default a proxy object becomes stale if it has not been explicitly refreshed for 1 hour. You can change the defaul timeout calling Protocol.setTimeoutInSecs(long).

It is also possible to explicitly make all the objects in a cache 'stale' (check RDFDB.markAllAsStale() and RDFDB.markAsStale(String). For example to make stale all the objects in the default cache:

 Protocol.getDB().markAllAsStale();

Note:
Object proxies reload themselves automatically in 2 cases, when they are stale and when they are 'partial' and a property that is set to null is being requested. When an operation that returns a high number of objects is performed, e.g. a query, the Nesstar servers have the option of returning 'partial objects'. Say for example that you are performing a query that will return 1000 objects of type Study and say that a Study properties are either numerous or long or both (say for example that it has an 'abstract' property whose value is 10K bytes long). The server can decide to return only the basic properties of the requested objects (URL, type, label and comment) and leave out the rest. Now say that you are trying to get the value of the abstract property of a Study by calling a getAbstract() method. The proxy object will notice that the value of the abstract property is currently null, as it was not returned from the server. It will then reload itself (when a server is asked to return a single object it will always return all its properties) before returning the value of the property .

To reload a proxy immediately, without waiting for a timeout, you can use the RDFObject.reload() method.

If you already have the object you can ask it to reload itself:

// Reload the server object to refresh its properties
server.reload() 

Otherwise you can ask the cache to load it in:

 // Reload the object
 nesstar.api.common.Server server = (nesstar.api.common.Server) Protocol.getDB().reload(serverURL);

 

Accessing Object Properties

Once an application has got the proxy corresponding to the desired object it can access its properties via normal getX accessor methods such as RDFObject.getLabel() or Server.getVersion().

All proxies classes implement the RDFObj interface and extend RDFObject. A couple of useful methods that all proxies share are:

Properties do not have to be Strings. They can also be Integers, Floats, Doubles or Booleans, e.g.:

// Read the HTTPS port that the server is listening on (if any)
Integer securePort = server.getSecurePort();
out("HTTPS port=" + securePort);

This program should display:

HTTPS port=443

Note that the API returns an Integer, not an int. The API always returns objects rather than literals. This makes it easy to detect when a property has no value. In our example, if the server is not configured to accept HTTPS connection the value of the securePort property would be null. In your code you should always check if a property value is null and treat it accordingly.

Traversing Object Relationships

Till now we have only seen examples of properties, such as label or version, that state a particular attribute of an object.

As you would expect there is also another kind of properties: those who are used to represent relationships among objects.

These properties have as their value either another object (1 to 1 relationship) or a List of objects (1 to n relationship).

Server objects have both 1-1 (e.g. nesstar.api.Server#getStatEngine that returns an object of type nesstar.api.NSDStatEngine) and 1-N relationships (e.g. Server.getCatalogs()that returns a List of Catalog objects) as we can see in the following example:

  // Traverse the 1-1 relationship from Server to NSDStatEngine
  NSDStatEngine engine = server.getStatEngine();
  
  // Traverse the 1-N relationship from Server to Catalogs
  List catalogs = server.getCatalogs();
  Iterator c = catalogs.iterator();
  while (c.hasNext()) { 
    nesstar.api.faster.Catalog catalog = (nesstar.api.faster.Catalog)c.next();
    String catalogLabel = catalog.getLabel();
  }

It is therefore very simple, starting from a known object, to access all related objects. Indeed starting from the a Server object you can easily access all the objects stored in a given server. These accessor functions will automatically create proxies for the objects they link to if they are not already in the default DB.

Performing Method Calls

Properties are not all there is to Nesstar Objects. Again: think of the Web. The Web is not composed only by static entities, it also offers dynamic services. We can use the Web to perform operations such as buying a flight ticket or searching a library catalogue. Similarly Nesstar objects can have methods/operations.

You can perform method calls on a remote object by using the corresponding operation on the proxy object.

For example Server objects have a Server.GetReport() operation that will return an HTML document containing a technical report on the current server activity. The operation can be invoked as:

// Apply a method to get a technical report on the current state of the server
String report = server.GetReport(null);
out("Report:\n" + report);

The output of the program will be something like:

 <pre>Report:
Started On=Tue Apr 15 08:06:32 BST 2003
Started Ops=6062
Completed Ops=6061
MaxConcurrent Ops=2
Threads: .....

Now, the GetReport doesn't need any parameter so why do we see a null in the parameter list?

To understand that we have to take a step back and consider that remote operations are quite different from local ones. For a start they can take quite longer (as you have to add the time needed to establish a connection and transfer the parameters and the operation result on the network) so an application might need to provide some feedback to the user on the development of the operation (think of the "Connecting/Downloading" messages displayed by WWW browsers) as well as allowing him to cancel the ongoing operation (think of the Stop button in WWW browsers). They can also fail for reasons totally unrelated to the operation itself (e.g. a network failure). Finally remote operation are executed on a remote server that is probably controlled by someone different from the person who owns the client that is asking for the operation to be performed. For this reason the server will probably enforce some kind of access control before allowing an operation to be carried out. The client might be required to provide some proof of identity, such as an user id and password, or maybe asked to pay a certain amount of money

For all these reasons remote calls need more 'management' than plain local calls. The API provides an interface, ExecutorLog that can be used to provide such management. When you execute a remote operation the execution is performed by an instance of Executor. The Executor is fed with a BasicExecutorLog object (that implements the ExecutorLog interface) and will call this object to notify it of any major event that it is taking place while the operation execute (e.g. the operation failed or we are being challenged to provide some authentication information to the server).

So, if you want to control the execution of a call, you can create your own class that extends BasicExecutorLog and provide an instance of it as the last parameter of the call. If on the contrary you cannot be bothered to explicitly manage the operation you can simply pass a null.

You might wonder how the API actually performs the operation on the remote object. To understand it again think of how the Web works: user interacts with Web applications by filling in a form and clicking on a Submit button. The Web browser then code this information and forwards it to the remote server by using the HTML Forms submit protocol. This protocol is very well proven and it is being supported by Web browser and Web applications for a decade. The Nesstar API uses the same protocol to perform remote calls. It maps object method calls to HTML forms calls.

This approach has a number of advantages: method calls can be performed using a normal web browser (and in general using programs written in any language that comes with an HTTP library) and it is easy to determine an URL that corresponds to the operation. The URL can then be used by an application to store and replay the operation.

Bookmarking Operations

For each operation the API provides a corresponding URL. The application can store the operation URL so that the operation can be replayed at a later time.

To see how it works let's look at another way of executing the GetReport operation. This time we start by getting a Bookmark representing the operation and then we execute the bookmark:

// Get a bookmark corresponding to an operation
Bookmark reportOp = server.GetReport();
out("The URL of the report operation is:\n" + reportOp.getURL());
out("The human readable description of the report operation is:\n" + reportOp.getDescription());

// Execute the operation
MIMEObject result = reportOp.execute();
out("Report:\n" + result.getContent());

The output of the program will be something like:

The URL of the report operation is:
http://nesstar.essex.ac.uk:80/obj/cServer/Demo?http%3A%2F%2Fwww.nesstar.org%2Frdf%2Fmethod=http%3A%2F%2Fwww.nesstar.org%2Frdf%2FServer%2FGetReport
The human readable description of the report operation is:
GetReport
Report:
<pre>Report:
Started On=Tue Apr 15 08:06:32 BST 2003
...

The URL of the operation is just a normal Web URL. You can execute the same operation directly in your browser by clicking on it:

http://nesstar.essex.ac.uk:80/obj/cServer/Demo?http%3A%2F%2Fwww.nesstar.org%2Frdf%2Fmethod=http%3A%2F%2Fwww.nesstar.org%2Frdf%2FServer%2FGetReport

Custom Proxy Classes

In order to convert the Nesstar objects to the appropriate proxy objects the cache consults an internal list of mappings. A default set of mappings is created when the API is initialised. The default settings are set in the addDefaultMappings method of nesstar/client/InitMappings.java

In some cases you might want to map Nesstar objects to your own custom proxy classes. Say for example that you want to display your objects in a tree view using a different icon for each type. You might define an interface that captures these requirements as follows:

interface TreeViewObj {
  Icon getIcon();
  Collection getChildren();
}

You can then create new classes that extend the proxy classes and implement the new interface.

For this to work you have to tell the object cache to use your new proxy classes rather then the standard ones. Say for example if you have defined a new mypackage.Server proxy class, you can register it with:

Protocol.getDB().addMapping("http://www.nesstar.org/rdf/Server","mypackage.Server");.

What this line says is: map any object of type Server to my custom proxy class.

Remember that the mapping has to be in place before you load any object in the DB.

Handling Security Challenges

Whenever a client tries to access a remote object or perform on it some operation the server that manages the object will check if the user that the client is operating on behalf of has the right to access the object or perform the operation. If the user is authorised the operation is performed and the result is returned to the client. If the user is not authorised the API will return an exception.

There is also a third case: the server might not know yet if the user is authorised or not. Say for example that the requested operation can be carried out only by the server administrator. If the user has not logged in yet the server doesn't know his identity and therefore cannot decide if the user can be authorised to carry out the required operation. In this case the server will return a challenge, that's to say an HTML form that the user will have to fill in to provide more information. The most common challenge is a login form but other challenges are possible. An user might for example be asked to agree to some condition or to pay some money.

If any time it performs an API operation the client application had to check if a challenge had been returned and perform some appropriate action the application code would become extremely complex. Luckily this is not needed. Any API operation always returns either a result or throws an exception if something went wrong (and this include the case that the user was not authorised to perform the operation).

Challenges are dealt with separately. The application provides, at setup time, the API with a class that implements the HTMLViewer interface. Every time a challenge is received the API will create an instance of this class and use the HTMLViewer.setPage(URL) or HTMLViewer.setPage(MIMEObject) to pass it the challenge for display. A standalone Java application might use this mechanism to display the challenge in an embedded HTML browser or pass it to an external browser. An example of this kind of setup can be seen in Init.testInit().

Handling Security Challenges and Managing User Identities in Multi User Applications

There are cases when more than one Nesstar application execute in the same Java VM, and therefore share the same Nesstar API, and/or when one Nesstar application operates on behalf of different users. An example of the first case is the Nesstar Server that contains many clients (object browser, study deployer, etc) all running in the same VM. An example of the second case is a web client such as the Nesstar WebView that can serve many concurrent web users.

Multi-user applications face two additional problems:

  1. how to specify on behalf of what user the application is operating
  2. how to send the security challenges to the correct user

User identities are represented in the API by Protocol objects. Multi-user applications need to create a separate Protocol object for each user and make sure that whenever they execute an operation on behalf of that user the API uses the correct Protocol object.

Protocols are created with Protocol.newProtocol() and are associated with threads. Newly created protocols are automatically associated with the thread on which they are created. Additional threads can be associated with a given protocol by executing Protocol.setAsCurrent(). Once a Protocol has been associated with a thread, all API operations executed thereafter on that thread will operate under the same user identity.

A good example of a Nesstar multi-user application is Nesstar WebView. When WebView receives a request from an user browser for the first time it will create a corresponding HTTPSession object and associate to it a newly created Protocol object (e.g. using HttpSession.html.setAttribute(..)). At the beginning of the thread(s) that will be used to service the user request WebView retrieves the Protocol object associated with the user session and sets it as the current Protocol using Protocol.setAsCurrent().

Now to the second problem. As we have seen in the previous section an application need to provide the API with HTMLViewers that are used to display security challenges. In the case of a multi user application the HTMLViewers need to be correctly initialised to return the challenge to the correct user. For example a web client will need to initialise its HTMLViewers so that the challenge is sent back to the correct HTTP Response object.

Web applications need to customise other aspects as well. If a challenge takes place and is send to the user the web application cannot do anything else and is interrupted. Once the user has completed the challenge sequence there must be a way for the server to redirect the user browser to the web application so that the original user operation can be reapplied. This can be done by passing additional parameters to the API.

API calls are controlled by instances of classes that implement the ExecutorLog interface. By default the API uses instances of BasicExecutorLog but this can be changed using Protocol.setExecutorLog(BasicExecutorLog). The logic needed to drive API calls for Web application is in nesstar.server.WebExecutor.

A Web client should therefore initialise the protocol, at the beginning of each thread that serves an user request, as follows:

WebHTMLViewer viewer = new WebHTMLViewer(response,...);
Protocol.setExecutorLog(new WebExecutorLog(request, response, viewer));

where WebHTMLViewer is an application specific viewer.

A Short Summary

Some of the main things to keep in mind about Nesstar objects:

Properties are attributes of an object, there value can be either:

Methods are operation that can be applied to an object, they can take 0 to N parameters. They also have an unique URL that can be used to store and replay them.

What Object Models Are Supported?

The API supports (contains proxy classes) for the following object models:

Model
Packages
Nesstar Servers 1.1.X object model
nesstar.api
Nesstar Servers 2.X object model
nesstar.api.common and nesstar.api.faster
Health Canada model nesstar.api.hc
EU Cosmos project object model (described in the Cosmos Architecture Document)
nesstar.api.cosmos
EU Madiera project model nesstar.api.madiera

A model is composed by one of more packages of Nesstar classes. A model defines all the classes needed for a particular Nesstar application. Classes, packages and models are formally specified in UML class diagrams.

The API proxy classes (as well as the RDF description of the classes, the server skeletons, etc.) are automatically generated from the UML class diagrams.


Nesstar SDK

Copyright©2003 NSD - All Rights Reserved