This issue presents tips, techniques, and sample code for the following topics:
ERROR CHECKING WITH JNI
The JavaTM Native Interface (JNI) is a
mechanism that enables Java to call functions written in other languages such
as C++. JNI supports the use of existing code libraries.
One important aspect of JNI is error handling. Consider the following scenario.
Suppose you have a Java application that passes a string to a C++ function,
and the string is further processed in some way. The Java code looks like this:
public class err {
static {
System.loadLibrary("clib");
}
public static native void print(String s);
public static void main(String args[]) {
print("testing\n");
print(null);
}
}
In this example, print is a native method, that is, a method coded in some
other language. It accepts a string argument. The implementation of this
method is in a shared library, clib, that is loaded using
System.loadLibrary
when the Java program starts.
If you haven't used JNI before, you might wonder how to actually
build a JNI-based application. The first step is to compile the
Java program:
$ javac err.java
Then you specify:
$ javah -jni -o clib.cpp err
javah is a utility program that comes with the
JDKTM software.
It generates a C++ function prototype for the native method
(function) found in the example above. In other words, if you have
a Java program that calls a native function, you need to create
a shared library containing a C++ implementation of that function.
The function in the library needs to have a specific name or
signature.
The implementation of the native function looks like this:
// clib.cpp
extern "C" {
JNIEXPORT void JNICALL
Java_err_print(JNIEnv* env, jclass c, jstring s) {
if (s == NULL) {
jclass exc =
env->FindClass(
"java/lang/NullPointerException");
if (exc != NULL)
env->ThrowNew(exc, "(in C++ code)");
return;
}
const jbyte* str = env->GetStringUTFChars(s, NULL);
if (str == NULL)
return;
printf("%s", str);
env->ReleaseStringUTFChars(s, str);
}
}
The 'extern "C"' is a C++ notation saying that the function should
have a C external name instead of a C++ one. C++ compilers vary in
the way they generate function names, due to differences in encoding
of overloaded argument lists and so on. The function needs to have
a C external name to ensure name consistency.
The sequence for building a shared library varies. For example,
using Borland C++Builder 4, you specify:
$ bcc32 -c -Ic:/java/include
-Ic:/java/include/win32 clib.cpp
$ bcc32 -tWD clib.obj
where c:/java is the directory where the JavaTM 2 SDK is installed.
Once you've built the shared library, the program is run as usual:
$ java err
There are several interesting aspects of the C++ function worth
mentioning. One is that C++ doesn't automatically check for
null pointers (references) in the way that the Java language does.
If you pass a null string to the function and don't check for it,
you'll probably get a "Segmentation violation" error.
So you must check whether the passed-in pointer is null.
If it is, an exception is raised using the JNI ThrowNew
function.
This doesn't immediately trigger the exception, but propagates it
to the calling Java program. The Java program treats it as a normal
exception originating in Java code.
Once the pointer is checked, the program retrieves the
passed-in string. Low-level C/C++ strings and Java strings are
represented differently. Because of this, a JNI call is used to
retrieve a pointer to the string characters. The string is in UTF-8
format, which is equivalent to 7-bit ASCII if (and only if) the
string is composed entirely of 7-bit characters. Once the pointer
is retrieved, the string can be printed using printf.
Here's another point about error handling. GetStringUTFChars
returns NULL if it cannot allocate space to copy the string. If this JNI function does return NULL, then it has already raised a java.lang.OutOfMemoryError
exception which will
be propagated back to the Java program. In this case, there's no
need to detect a NULL return value and raise an exception yourself.
Let's run the program. The output is:
testing
Exception in thread "main"
java.lang.NullPointerException:
(in C++ code)
at err.print(Native Method)
at err.main(err.java:8)
The book "The Java Native Interface" by Sheng Liang covers the JNI
in depth. For information about this book, see
http://java.sun.com/docs/books/jni/index.html
USING JTEXTPANE
The JavaTM Foundation Classes (JFC) Project
Swing classes JTextArea and JTextField are used as GUI components for text
entry and text display. JEditorPane and JTextPane are also used for plain
text, but they go further. You can use them for styled text, HTML,
and RTF, or to insert arbitrary images and components into the pane.
JEditorPane is a subclass of JTextComponent, and you can use it
to edit various kinds of content based on an "editor kit".
An editor kit is a class that contains functions for text handling.
For example, you can use it to perform standard text editor actions
(such as, cut/copy/paste), or read text documents from a file.
JTextPane is a subclass of JEditorPane, combined with
StyledEditorKit. It allows you to edit styled text. Examples of
styled text include underlining, italicized text, and inclusion of
images and components in the text.
One application of JTextPane might be in a customized word
processor, in particular, where you want special symbols to mark
the text. Here is an example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class MyComponent extends JComponent {
public Dimension getPreferredSize() {
return new Dimension(10, 15);
}
public Dimension getMaximumSize() {
return getPreferredSize();
}
public void paint(Graphics g) {
Dimension d = getSize();
g.setColor(Color.red);
g.fillRect(0, 0, d.width, d.height);
}
}
public class panedemo {
public static void main(String args[]) {
JFrame frame = new JFrame("JTextPane demo");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
final JTextPane pane = new JTextPane();
pane.setPreferredSize(new Dimension(600, 400));
JButton insbutton = new JButton("Insert Component");
insbutton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
pane.insertComponent(new MyComponent());
pane.requestFocus();
}
});
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add("North", insbutton);
panel.add("Center", pane);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
This example sets up a JTextPane area, where you can enter text in a normal
way. If you click on "Insert Component", an instance of a custom component is
inserted into the text. In this example, the component is an instance of
MyComponent
. The component consists of a 10 x 15 red filled box,
representing markup that you'd like to insert into the text. In other words,
you can insert arbitrary components interspersed with text.
You can also use the InsertIcon
method to insert images into the
text pane.
Note: JDKTM 1.2.2 fixes a number of bugs,
some of them associated with the use of JFC Swing. If you have trouble running
the JTextPane example (or any other code that includes JFC Swing
components), try running it with JDK 1.2.2.
Note
The names on the JDCSM mailing list
are used for internal Sun MicrosystemsTM
purposes only. To remove your name from the list, see Subscribe/Unsubscribe below.
Feedback
Comments? Send your feedback on the JDC Tech Tips to: jdc-webmaster
Subscribe/Unsubscribe
The JDC Tech Tips are sent to you because you elected to subscribe
when you registered as a JDC member. To unsubscribe from
JDC email, go to the following address and enter the email
address you wish to remove from the mailing list:
http://developer.java.sun.com/unsubscribe.html
To become a JDC member and subscribe to this newsletter go to:
http://java.sun.com/jdc/
This issue of the JDC Tech Tips is written by Patrick Chan,
the author of the publication
"The JavaTM Developers Almanac".