Core API

The core API provides the basic routines for reading, writing and navigating NeXus files. Operations are performed using a handle that keeps a record of its current position in the file hierarchy. All are read or write requests are then implicitly performed on the currently 'open' entity. This limits number of parameters that need to be passed to API calls, at the cost of forcing a certain mode of operation. It is very similar to navigating a directory hierarchy; NeXus groups are the directories, which can contain data sets and/or other directories.

The core API comprises the following functional groups:

C and C++ Interface

Doxygen documentation is provided for C[31] and C++[32] routines.

Fortran 77 Interface

Doxygen documentation is provided for the f77 NAPI.[33]

Fortran 90 Interface

The Fortran 90 interface is a wrapper to the C interface with nearly identical routine definitions. As with the Fortran 77 interface, it is necessary to reverse the order of indices in multidimensional arrays, compared to an equivalent C program, so that data are stored in the same order in the NeXus file.

Any program using the F90 API needs to put the following line at the top (after the PROGRAM statement) :

Example 1.1. line required for use with F90 API

use NXmodule


Use the Table 1.1, “ Conversion of data types from C to F90 ” to convert from the C data types listed with each routine to the Fortran 90 data types.

Table 1.1.  Conversion of data types from C to F90

CFORTRAN 90
int, int integer
char* character(len=*)
NXhandle, NXhandle* type(NXhandle)
NXstatus integer
int[] integer(:)
void* real(:) or integer(:) or character(len=*)
NXlink a, NXlink* a type(NXlink)


The parameters in Table 1.2, “ F90 parameters from NXmodule used in defining variables ”, defined in NXmodule, may be used in defining variables.

Table 1.2.  F90 parameters from NXmodule used in defining variables

NameDescriptionValue
NX_MAXRANK Maximum number of dimensions32
NX_MAXNAMELEN Maximum length of NeXus name64
NXi1 Kind parameter for a 1-byte integerselected_int_kind(2)
NXi2 Kind parameter for a 2-byte integerselected_int_kind(4)
NXi4 Kind parameter for a 4-byte integerselected_int_kind(8)
NXr4 Kind parameter for a 4-byte realkind(1.0)
NXr8 Kind parameter for an 8-byte realkind(1.0D0)


Also see the doxygen documentation.[34]

Java Interface

This section includes installation notes, instructions for running NeXus for Java programs and a brief introduction to the API.

The Java API for NeXus (jnexus) was implemented through the Java Native Interface (JNI) to call on to the native C library. This has a number of disadvantages over using pure Java, however the most popular file backend HDF5 is only available using a JNI wrapper anyway.

Acknowledgement

This implementation uses classes and native methods from NCSA's Java HDF Interface project. Basically all conversions from native types to Java types is done through code from the NCSA HDF group. Without this code the implementation of this API would have taken much longer. See NCSA's copyright for more information.

Installation

Requirements

Caution

Documentation is old and may need revision.

For running an application with jnexus an recent Java runtime environment (JRE) will do.

In order to compile the Java API for NeXus a Java Development Kit is required on top of the build requirements for the C API.

Installation under Windows

  1. Copy the HDF DLL's and the file jnexus.dll to a directory in your path. For instance C:\Windows\system32.

  2. Copy the jnexus.jar to the place where you usually keep library jar files.

Installation under Unix

The jnexus.so shared library as well as all required file backend .so libraries are required as well as the jnexus.jar file holding the required Java classes. Copy them wherever you like and see below for instructions how to run programs using jnexus.

Running Programs with the NeXus API for Java

In order to successfully run a program with jnexus, the Java runtime systems needs to locate two items:

  1. The shared library implementing the native methods.

  2. The nexus.jar file in order to find the Java classes.

Locating the shared libraries

The methods for locating a shared library differ between systems. Under Windows32 systems the best method is to copy the jnexus.dll and the HDF4, HDF5 and/or XML-library DLL files into a directory in your path.

On a UNIX system, the problem can be solved in three different ways:

  1. Make your system administrator copy the jnexus.so file into the systems default shared library directory (usually /usr/lib or /usr/local/lib).

  2. Put the jnexus.so file wherever you see fit and set the LD_LIBRARY_PATH environment variable to point to the directory of your choice.

  3. Specify the full pathname of the jnexus shared library on the java command line with the -Dorg.nexusformat.JNEXUSLIB=full-path-2-shared-library option.

Locating jnexus.jar

This is easier, just add the the full pathname to jnexus.jar to the classpath when starting java. Here are examples for a UNIX shell and the Windows shell.

Example 1.2. UNIX example shell script to start jnexus.jar

#!/sbin/sh
java -classpath /usr/lib/classes.zip:../jnexus.jar:. \
	-Dorg.nexusformat.JNEXUSLIB=../libjnexus.so TestJapi


Example 1.3. Windows 32 example batch file to start jnexus.jar

set JL=-Dorg.nexusformat.JNEXUSLIB=..\jnexus\bin\win32\jnexus.dll
java -classpath C:\jdk1.5\lib\classes.zip;..\jnexus.jar;. %JL% TestJapi


Programming with the NeXus API for Java

The NeXus C-API is good enough but for Java a few adaptions of the API have been made in order to match the API better to the idioms used by Java programmers. In order to understand the Java-API, it is useful to study the NeXus C-API because many methods work in the same way as their C equivalents. A full API documentation is available in Java documentation format. For full reference look especially at:

  • The interface NeXusFileInterface first. It gives an uncluttered view of the API.

  • The implementation NexusFile which gives more details about constructors and constants. However this documentation is interspersed with information about native methods which should not be called by an application programmer as they are not part of the standard and might change in future.

See the following code example for opening a file, opening a vGroup and closing the file again in order to get a feeling for the API:

Example 1.4. fragment for opening and closing

	// $Id: napi-java-prog1.java 554 2010-04-25 01:50:38Z Pete Jemian $
	try{
		NexusFile nf = new NexusFile(filename, NexusFile.NXACC_READ);
		nf.opengroup("entry1","NXentry");
		nf.finalize();
	}catch(NexusException ne) {
		// Something was wrong!
	}


Some notes on this little example:

  • Each NeXus file is represented by a NexusFile object which is created through the constructor.

  • The NexusFile object takes care of all file handles for you. So there is no need to pass in a handle anymore to each method as in the C language API.

  • All error handling is done through the Java exception handling mechanism. This saves all the code checking return values in the C language API. Most API functions return void.

  • Closing files is tricky. The Java garbage collector is supposed to call the finalize method for each object it decides to delete. In order to enable this mechanism, the NXclose() function was replaced by the finalize() method. In practice it seems not to be guaranteed that the garbage collector calls the finalize() method. It is safer to call finalize() yourself in order to properly close a file. Multiple calls to the finalize() method for the same object are safe and do no harm.

Data Writing and Reading

Again a code sample which shows how this looks like:

Example 1.5. fragment for writing and reading

	// $Id: napi-java-datarw1.java 554 2010-04-25 01:50:38Z Pete Jemian $
	int idata[][] = new idata[10][20];
	int iDim[] = new int[2];

	// put some data into idata.......

	// write idata
	iDim[0] = 10;
	iDim[1] = 20;
	nf.makedata("idata",NexusFile.NX_INT32,2,iDim);
	nf.opendata("idata");
	nf.putdata(idata);

	// read idata
	nf.getdata(idata);


The dataset is created as usual with makedata() and opened with putdata(). The trick is in putdata(). Java is meant to be type safe. One would think then that a putdata() method would be required for each Java data type. In order to avoid this, the data to write() is passed into putdata() as type Object. Then the API proceeds to analyze this object through the Java introspection API and convert the data to a byte stream for writing through the native method call. This is an elegant solution with one drawback: An array is needed at all times. Even if only a single data value is written (or read) an array of length one and an appropriate type is the required argument.

Another issue are strings. Strings are first class objects in Java. HDF (and NeXus) sees them as dumb arrays of bytes. Thus strings have to be converted to and from bytes when reading string data. See a writing example:

Example 1.6. String writing

	// $Id: napi-java-datarw2.java 554 2010-04-25 01:50:38Z Pete Jemian $
	String ame = "Alle meine Entchen";
	nf.makedata("string_data",NexusFile.NX_CHAR,
			1,ame.length()+2);
	nf.opendata("string_data");
	nf.putdata(ame.getBytes());


And reading:

Example 1.7. String reading

	// $Id: napi-java-datarw2.java 554 2010-04-25 01:50:38Z Pete Jemian $
	String ame = "Alle meine Entchen";
	nf.makedata("string_data",NexusFile.NX_CHAR,
			1,ame.length()+2);
	nf.opendata("string_data");
	nf.putdata(ame.getBytes());


The aforementioned holds for all strings written as SDS content or as an attribute. SDS or vGroup names do not need this treatment.

Inquiry Routines

Let us compare the C-API and Java-API signatures of the getinfo() routine (C) or method (Java):

Example 1.8. C API signature of getinfo()

	/* $Id: frag-c-api-sig-getinfo.c 554 2010-04-25 01:50:38Z Pete Jemian $ */
	/* C -API */
	NXstatus NXgetinfo(NXhandle handle, int *rank, int iDim[], 
					 int *datatype);


Example 1.9. Java API signature of getinfo()

	// $Id: frag-c-api-sig-getinfo.java 554 2010-04-25 01:50:38Z Pete Jemian $
	// Java 
	void getinfo(int iDim[], int args[]);


The problem is that Java passes arguments only by value, which means they cannot be modified by the method. Only array arguments can be modified. Thus args in the getinfo() method holds the rank and datatype information passed in separate items in the C-API version. For resolving which one is which, consult a debugger or the API-reference.

The attribute and vGroup search routines have been simplified using Hashtables. The Hashtable returned by groupdir() holds the name of the item as a key and the classname or the string SDS as the stored object for the key. Thus the code for a vGroup search looks like this:

Example 1.10. vGroup search

	// $Id: napi-java-inquiry1.java 554 2010-04-25 01:50:38Z Pete Jemian $
	nf.opengroup(group,nxclass);
	h = nf.groupdir();
	e = h.keys();
	System.out.println("Found in vGroup entry:");
	while(e.hasMoreElements())
	{
		vname = (String)e.nextElement();
		vclass = (String)h.get(vname);
		System.out.println("     Item: " + vname + " class: " + vclass);
	}


For an attribute search both at global or SDS level the returned Hashtable will hold the name as the key and a little class holding the type and size information as value. Thus an attribute search looks like this in the Java-API:

Example 1.11. attribute search

	// $Id: napi-java-inquiry2.java 554 2010-04-25 01:50:38Z Pete Jemian $
	Hashtable h = nf.attrdir();
	Enumeration e = h.keys();
	while(e.hasMoreElements())
	{
		attname = (String)e.nextElement();
		atten = (AttributeEntry)h.get(attname);
		System.out.println("Found global attribute: " + attname +
			" type: "+ atten.type + " ,length: " + atten.length); 
	}


For more information about the usage of the API routines see the reference or the NeXus C-API reference pages. Another good source of information is the source code of the test program which exercises each API routine.

Known Problems

These are a couple of known problems which you might run into:

Memory

As the Java API for NeXus has to convert between native and Java number types a copy of the data must be made in the process. This means that if you want to read or write 200MB of data your memory requirement will be 400MB! This can be reduced by using multiple getslab()/putslab() to perform data transfers in smaller chunks.

Java.lang.OutOfMemoryException

By default the Java runtime has a low default value for the maximum amount of memory it will use. This ceiling can be increased through the -mxXXm option to the Java runtime. An example: java -mx512m ... starts the Java runtime with a memory ceiling of 512MB.

Maximum 8192 files open

The NeXus API for Java has a fixed buffer for file handles which allows only 8192 NeXus files to be open at the same time. If you ever hit this limit, increase the MAXHANDLE define in native/handle.h and recompile everything.

On-line Documentation

The following documentation is browsable online:

  1. The Doxygen API documentation[35]

  2. A verbose tutorial for the NeXus for Java API.

  3. The API Reference.

  4. Finally, the source code for the test driver for the API which also serves as a documented usage example.

Python Interface

Documentation available in pydoc and doxygen.[36]

IDL Interface

IDL is an interactive data evaluation environment developed by Research Systems - it is an interpreted language for data manipulation and visualization. The NeXus IDL bindings allow access to the NeXus API from within IDL - they are installed when NeXus is compiled from source after being configured with the following options

Example 1.12. configure NeXus source to build IDL bindings

./configure --with-idlroot=/path/to/idl/installation --with-idldlm=/path/to/install/dlm/files/to


For further details see the README[37] for the NeXus IDL binding.