NeXus is a proposal for a common file format for neutron and X-ray scattering. NeXus uses HDF as its physical file format. At the last NOBUGS conference a strong need for a Java-API to NeXus files was expressed. Version 1.0 of this Java API has now (October 2000) become available. As recoding the HDF library in Java was no option the Java API for NeXus (jnexus) was implemented through the Java Native Methods Interface (JNI). This has the consequence that the Java API for NeXus cannot be used in applets as the security restrictions for applets prohibit downloading of shared libraries and local file access. Applets can use a NeXus Data Server in order to access NeXus files in readonly mode.
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.
For the binary distribution only a JDK1.1 compatible Java runtime is required. Suitable runtime environments for Solaris, Linux and Windows32 can be downloaded from Sun's Java homepage. This website also holds pointers to Java runtime systems for other platforms. Jnexus has not been tested with Java 2 but should work with it.
In order to compile the Java API for NeXus the following components are required:
Two files are needed: the jnexus.so shared library and the jnexus.jar file holding the required Java class. Copy them wherever you like and see below for instructions how to run programs using jnexus.
In order to successfully run a program with jnexus the Java runtime systems needs to locate two items:
Of course the method for locating a shared library differ between systems. Under Windows32 systems the best method is to copy the jnexus.dll and the HDF-libarary DLL's into a directory in your path. The HDF DLL's have to go there anyway.
On a unix system the problem can be solved in three different ways:
This is easier: just add the the full pathname to jnexus.jar to the classpath when starting java.
A unix example shell script:
#!/sbin/sh java -classpath /usr/lib/classes.zip:../jnexus.jar:. \ -Dneutron.nexus.JNEXUSLIB=../bin/du40/libjnexus.so TestJapiA Windows 32 example batch file:
set JL=-Dneutron.nexus.JNEXUSLIB=..\jnexus\bin\win32\jnexus.dll java -classpath C:\jdk1.1.5\lib\classes.zip;..\jnexus.jar;. %JL% TestJapi
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:
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.
try{ NexusFile nf = new NexusFile(filename, NexusFile.NXACC_READ); nf.opengroup("entry1","NXentry"); nf.finalize(); }catch(NexusException ne) { // oh shit! something was wrong! }Some notes on this little example:
Again a code sample which shows how this looks like:
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:
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:
byte bData[] = new byte[132]; nf.opendata("string_data"); nf.getdata(bData); String string_data = new String(bData);The aforementioned holds for all strings written as SDS content or as an attribute. SDS or vGroup names do not need this treatment.
Let us compare the C-API and Java-API signatures of the getinfo routine or method:
/* C -API */ NXstatus NXgetinfo(NXhandle handle, int *rank, int iDim[], int *datatype); // 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 Hastable returned by groupdir() holds the name of the item as a key and the classname or the string SDS as ths stored object for the key. Thus the code for a vGroup search looks like this:
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:
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.
These are a couple of known problems which you might run into:
You will need
For Windows32 a Microsoft Visual C++ 6.0 project file is supplied in the jnexus/jnexus directory. Use this project file. You will need to adapt the directory settings under Tools/Options/Directories for both include and library directories in order to reflect the placement of the HDF libraries and the jnexus source code in your directory hierarchy. Hitting F7 after that should build the shared library. Hint: Only a release build is possible with the HDF library binaries. If a debug build is needed you have to recompile the HDF libraries yourself. For a recompilation of the Java classes use the compilejava batch file in the jnexus main directory.
For DigitalUnix4.0D and Redhat Linux 6.2 Makefiles are provided (Makefile and Make.tux repectively). For these systems everything can be build with make du40 of make -f make.tux respectively. If the Makefiles do not work edit the directory paths in the configuration section to match your installation. If you wish to compile on another unix system, create a copy of one of the above mentioned Makefiles and edit the configuration section in your copy to match your installation of java and the HDF libraries. If you succeed in building the NeXus API for Java on a new system, please put back modified sources into the CVS repository and make your Makefile and the compiled shared library available to the NAPI team in order to provide a new binary distribution.
I'am sure this software contains swarms of bugs. If you manage to find one you may send requests either to the NAPI developer mailing list or to Mark Könnecke who wrote the Java API for NeXus.
Author:
Mark Könnecke
Laboratory for Neutron Scattering
Paul Scherrer Institut
CH-5232-Villigen-PSI
Switzerland
and the NeXus Design team.
Last Update: October, 19, 2000