13.3.4. NAPI 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.
13.3.4.1. 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.
13.3.4.2. Installation¶
13.3.4.2.1. 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.
13.3.4.2.2. Installation under Windows¶
Copy the HDF DLL’s and the file
jnexus.dll
to a directory in your path. For instanceC:\\Windows\\system32
.Copy the
jnexus.jar
to the place where you usually keep library jar files.
Note that the location or the naming of these files in the binary Nexus distributions
have changed over the years.
In the Nexus 4.3.0 Windows 64-bit distribution (see Assets in https://github.com/nexusformat/code/releases/tag/4.3.0),
By default, the DLL is at: C:\Program Files\NeXus Data Format\bin\libjnexus-0.dll
.
Please rename this file to jnexus.dll
before making it available in your path.
This is important, otherwise, JVM runtime will not be able to locate this file.
For the same distribution, the location of jnexus.jar
is at: C:\Program Files\NeXus Data Format\share\java
.
13.3.4.2.3. 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
.
13.3.4.3. 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:
The shared library implementing the native methods.
The
nexus.jar
file in order to find the Java classes.
13.3.4.3.2. 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.
UNIX example shell script to start jnexus.jar
1#!/sbin/sh
2java -classpath /usr/lib/classes.zip:../jnexus.jar:. \
3 -Dorg.nexusformat.JNEXUSLIB=../libjnexus.so TestJapi
Windows 32 example batch file to start jnexus.jar
1set JL=-Dorg.nexusformat.JNEXUSLIB=..\jnexus\bin\win32\jnexus.dll
2java -classpath C:\jdk1.5\lib\classes.zip;..\jnexus.jar;. %JL% TestJapi
13.3.4.4. 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:
fragment for opening and closing
1 try{
2 NexusFile nf = new NexusFile(filename, NexusFile.NXACC_READ);
3 nf.opengroup("entry1","NXentry");
4 nf.finalize();
5 }catch(NexusException ne) {
6 // Something was wrong!
7 }
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 thefinalize()
method. In practice it seems not to be guaranteed that the garbage collector calls thefinalize()
method. It is safer to callfinalize()
yourself in order to properly close a file. Multiple calls to thefinalize()
method for the same object are safe and do no harm.
13.3.4.5. Data Writing and Reading¶
Again a code sample which shows how this looks like:
fragment for writing and reading
1 int idata[][] = new idata[10][20];
2 int iDim[] = new int[2];
3
4 // put some data into idata.......
5
6 // write idata
7 iDim[0] = 10;
8 iDim[1] = 20;
9 nf.makedata("idata",NexusFile.NX_INT32,2,iDim);
10 nf.opendata("idata");
11 nf.putdata(idata);
12
13 // read idata
14 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 writing
1 String ame = "Alle meine Entchen";
2 nf.makedata("string_data",NexusFile.NX_CHAR,
3 1,ame.length()+2);
4 nf.opendata("string_data");
5 nf.putdata(ame.getBytes());
And reading:
String reading
1 byte bData[] = new byte[132];
2 nf.opendata("string_data");
3 nf.getdata(bData);
4 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.
13.3.4.6. Inquiry Routines¶
Let us compare the C-API and Java-API signatures of the
getinfo()
routine (C) or method (Java):
C API signature of getinfo()
1 /* C -API */
2 NXstatus NXgetinfo(NXhandle handle, int *rank, int iDim[],
3 int *datatype);
Java API signature of getinfo()
1 // Java
2 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:
vGroup search
1 nf.opengroup(group,nxclass);
2 h = nf.groupdir();
3 e = h.keys();
4 System.out.println("Found in vGroup entry:");
5 while(e.hasMoreElements())
6 {
7 vname = (String)e.nextElement();
8 vclass = (String)h.get(vname);
9 System.out.println(" Item: " + vname + " class: " + vclass);
10 }
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:
attribute search
1 Hashtable h = nf.attrdir();
2 Enumeration e = h.keys();
3 while(e.hasMoreElements())
4 {
5 attname = (String)e.nextElement();
6 atten = (AttributeEntry)h.get(attname);
7 System.out.println("Found global attribute: " + attname +
8 " type: "+ atten.type + " ,length: " + atten.length);
9 }
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.
13.3.4.7. 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 innative/handle.h
and recompile everything.
13.3.4.8. On-line Documentation¶
The following documentation is browsable online:
A verbose tutorial for the NeXus for Java API.
The API Reference.
Finally, the source code for the test driver for the API which also serves as a documented usage example.