The Java 2 Platform, Standard Edition (J2SE technology) v1.3 for Linux means that Linux users and developers can take advantage of thousands of Java technology-based applications, from enterprise e-commerce infrastructure to client-side applications. It also opens up a huge emerging market for companies that already develop Java products.
The Java 2 Platform port was developed with the assistance of the blackdown.org porting group. Although Linux is a UNIX-based operating system, it's evolved at a different pace and direction than other UNIX platforms, making a port of the Java platform a sizable challenge. Many important bug fixes have gone into the Linux operating system and associated GNU libraries as a result of the work initiated by Blackdown, not just on Intel-based distributions but also Power PC and Sparc platforms. In particular, the linuxthreads library has improved dramatically over the past year on multiprocessor machines.
In an ideal world, the Java 2 Platform, combined with Java development tools such as Forté Community Edition, should be all you need to develop your projects. However, if the Linux operating system is a new development environment for you and you've previously developed with the Java 2 Platform for Windows or Solaris, you might find Linux a little different. This article provides tips to bring you up to speed as quickly as possible developing on the Linux platform.
SDK and JRE Installation Tips
Installation is straightforward, but be aware that the Java Runtime Environment (JRE) and Java Software Developer Toolkit (SDK) Red hat Package Management (RPM) files are installed by default to /usr/java. If you want these files in a different location, install with the rpm command:
rpm -i --badreloc --relocate /usr/java/=/usr/local/home j2sdk-1_3_0-linux.rpm
After relocating the files as shown above, start developing by adding the bin directory from the SDK or JRE installation to your $PATH. For example, using the bash shell, the $PATH is updated:
To verify that you've configured your environment, run the java -version command, and you should see a version string confirming that you've installed the Java 2 Runtime Environment:
java version "1.3.0"
Java 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot Client VM (build 1.3.0, mixed mode)
Java Plug-in Installation Tips
The J2SE plug-in is embedded inside the Java Runtime directory. As long as the Netscape browser can find the plug-in library from within the Java Runtime directory, it can also find the additional runtime files it needs.
To let your Netscape browser know where the Java plug-in library is, set the NPX_PLUGIN_PATH environment variable to point to the directory where the javaplugin.so shared library is located:
To configure the Java plug-in properties, use the ControlPanel program located in the jre/bin directory. In the example above, the program is /usr/java/jdk1.3/jre/bin/ControlPanel.
Java HotSpot Virtual Machine
This is the first release on Linux that includes the Java HotSpot Virtual Machine. Both the client and server Java HotSpot compilers are included by default in the Java Runtime Environment. These two solutions share the unique Java HotSpot runtime environment, but have different compilers optimized for the specific performance requirements of client- and server-side-based applications. The bundling of these virtual machines in one package provides developers with increased flexibility and convenience in deploying their client- or server-based Java technology applications.
By default the client compiler is enabled, but for intense server-side applications, the server compiler can be run using the -server runtime option. The Java HotSpot Virtual machine normally runs in a mixed mode, as seen in the version output. This means HotSpot will dynamically compile Java bytecodes into native code when a number of criteria have been met, including how many times the method has been run through the interpreter. The mixed runtime mode normally results in the best performance.
About Linux Threads
One major difference between developing on Linux and other UNIX operating systems is the system threads library. In Java 2 releases prior to 1.3, the JVM uses its own threads library, known as greenThreads, to implement threads in the Java platform. The GreenThreads implementation minimizes the JVM's exposure to differences in the LinuxThreads library and makes the port easier to complete. The downside to GreenThreads is that system threads on Linux are not taken advantage of, so the JVM doesn't scale well when additional CPUs are added.
In J2SE Release 1.3, the HotSpot virtual machine uses Linux system threads to implement JavaThreads. Because LinuxThreads are implemented as a cloned process, each JavaThread shows up in the process table if you run the ps command. Although this may look different, it's normal behavior for threaded programs on Linux:
java -jar Notepad.jar
ps -eo pid,ppid,command
In Table 1, the process id 11712 is the invoked JVM. The other processes with id 11712 as the parent process (listed under the PPID column) are JavaThreads implemented by the Linux system threads library. Each LinuxThread is created as a process clone operation that leaves the task of scheduling threads to the process scheduler.
On Solaris, however, JavaThreads are mapped onto user threads, which in turn are run on lightweight processes (LWP). On Windows the threads are created inside the process itself. Today, creating a large number of JavaThreads on Solaris and Windows is faster than on Linux. Thus, you might need to adjust programs that rely on platform-specific timing to take a little longer on startup when they run on Linux.
Linux Debugging Tools
As you learned in the section on threads, LinuxThreads are implemented as Linux processes. This makes debugging Java programs running on Linux a little tricky, but not impossible. This section explains how to attach the gdb GNU debugger to the JVM, and how to start the JVM with gdb to run debugging commands.
Going back to the example in the threads section, if the JVM is already running, you can attach the system gdb tool as follows:
gdb /usr/java/jdk1.3/bin/i386/native_threads/java 11712
The output from using the gdb tool should look similar to Listing 1. At this point the gdb tool has connected to the running Java process and is waiting for user input.
With gdb attached to the JVM, you can run any gdb commands. If you've used gdb before, these commands will look familiar. The info threads command lists the LinuxThreads, the t 12 command selects thread number 12 as the current thread, and the where command lists the stack frames in that thread (thread number 12, in this example). The output from an example session is shown in Listing 2.
Starting a Java Virtual Machine using gdb
Attaching a gdb trace to an existing program is usually the easiest way to debug a deadlock JVM; however, you might want to start the JVM from within the gdb debug tool from the outset, especially if you suspect the failure happens shortly after your application starts.
To enable the JVM to run from inside gdb on Linux, the following setup needs to be in place:
- Set the APPHOME and LD_LIBRARY_PATH environment variables before you run the gdb command. This step is required because the Java program is a shell wrapper that configures the environment for the real Java program to run, and gdb needs to run with the real binary Java program, not the shell wrapper version.
- Instruct gdb to ignore the signals that the Hotspot JVM uses to maintain its own state. An example of such a signal is SIGUSR1.
These next lines start the Java 2D demo inside a gdb session. First, the LD_LIBRARY_PATH and APPHOME environment variables are set. In this example, the setup assumes the SDK is installed in the default /usr/java directory:
export LD_LIBRARY_PATH=/usr/java/jdk1.3/jre/lib/i386/native_threads:/usr/ java/jdk1.3/jre/lib/i386:/usr/java/jdk1.3/jre/lib/i386/hotspot
- Start gdb. The following gdb commands will load the JVM, instruct gdb to stop at the main method, and ignore the signals used by the Hotspot Virtual Machine. The commands can be entered either after gdb has started, or put in a .gdbinit file in your home directory. Once the gdb tool has reached the breakpoint, other breakpoints can be added, or just enter the gdb cont command to continue:
run -jar Java2Demo.jar
handle SIGUSR1 nostop noprint pass
handle SIGSEGV nostop noprint pass
handle SIGILL nostop noprint pass
handle SIGQUIT nostop noprint pass
Generating a Java stack trace on Linux
As LinuxThreads are implemented as Linux processes, this makes debugging Java programs running on Linux a little different. To generate a stack trace from the terminal window that started your application, type the sequence <CTRL>/ to send the QUIT signal to generate a stack trace. However, if you need to generate a stack trace from a Java application already running in the background, send the QUIT or signal number 3 to the launcher process id. The launcher process in this example is process id 11667. The stack trace can be generated by sending the signal via the kill command as follows:
kill -3 11667
The resulting stack trace is a snapshot of the JavaThreads and details the state of each thread. In each thread trace, a value called nid - the hex number of the cloned process it came from - can be used to track down deadlocks or busy sections in your application. For more details on analyzing Java stack traces, refer to the "Debugging Applets, Applications, and Servlets" chapter in Advanced Programming for the Java 2 Platform (Addison-Wesley, 2000).
Integrating Native JNI Code on Linux
If you've used native JNI code in your Java application, it probably needs to be recompiled on Linux. Linux distributions come complete with a wealth of GNU tools for compiling C, C++, Fortran, and other languages. Some of the system header files and system calls may be named slightly differently on linux, and the size of structures or global variables may be smaller than on other Unix platforms. A quick run-through of the header files can avoid complex issues later on.
To recompile your native C or C++ code and generate the native library file, use the GNU tool gcc for C programs and g++ for C++ programs, and supply the options as shown in the next example. Finally, set the LD_LIBRARY_PATH environment variable to point to the directory where the final native library is located. In the following examples the native library is called libnativelib.so and is loaded from within a Java program by the using System.loadLibrary("nativelib").
gcc -o libnativelib.so -D_REENTRANT -shared -Wl,-soname,libnative.so -I/usr/java/jdk1.3/include -I/usr/java/jdk1.3/include/linux nativelib.c -static -lc
g++ -o libnativelib.so -D_REENTRANT -shared -Wl,-soname,libdbmap.so -I/usr/java/jdk1.3/include -I/usr/java/jdk1.3/include/linux nativelib.cc -static -lc
Desktop Differences (Copy and Paste)
If you've been using Windows or a keyboard with a copy and paste key, you may be wondering how to copy and paste text between Java programs and other desktop programs and terminals.
Linux uses a mouse-driven copy-and-paste mechanism in which mouse button one selects and copies text, and mouse button two pastes the text. This technique works for Abstract Window Toolkit (AWT) components because they use the primary selection to achieve copy and paste. Project Swing components, however, use the system clipboard for copy and paste, and most tools on the desktop, apart from the Netscape browser, don't use the clipboard.
A workaround to this limitation is to map a key or mouse button to access the system clipboard:
*VT100.Translations: #override \
<Btn3Up>: select-end(CLIPBOARD) \n\
<Btn2Up>: insert-selection(CLIPBOARD) \n
The lines above can be passed as a value to an X tool using the -xrm option. Alternatively, the mapping can be made accessible to the entire desktop by including it in the .Xdefaults file in the user's home directory. The command xrdb -merge $HOME/.Xdefaults will reload updates in the .Xdefaults file.
Using Linux to develop and deploy applications written in Java has the same benefits as developing on any Java platform. Thousands of Java applications are available, and because they're cross-platform, those compiled on Linux will work right out of the box on Windows or any other Java-enabled platform.
In addition, the amount of Linux knowledge needed to develop or deploy Java applications is relatively small, making Linux an attractive choice as a development platform.
Java technologies on linux: http://java.sun.com/linux
Blackdown Java porting team: http://blackdown.org
Calvin Austin is the lead staff engineer for the Java 2 Standard Edition Linux project at Sun Microsystems, Inc., and works with the Blackdown.org Java porting group. He's cofounder of the Java Developer Connection and coauthor of Advanced Programming for the Java 2 Platform (Addison-Wesley, 2000). He can be reached at:
[[email protected] bin]# gdb /usr/java/jdk1.3/bin/i386/native_threads/java 11712
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License,
and you're welcome to change and/or distribute copies of it
under certain conditions. Type "show copying" to see the conditions.
There's absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
/usr/X11R6/bin/11712: No such file or directory.
Attaching to program: /usr/java/jdk1.3/bin/i386/native_threads/java, Pid 11712
Reading symbols from /lib/libpthread.so.0...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/native_threads/libhpi.so...
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/client/libjvm.so...done.
Reading symbols from /lib/libdl.so.2...done.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /usr/X11R6/lib/libX11.so.6...done.
Reading symbols from /lib/libnsl.so.1...done.
Reading symbols from /lib/libm.so.6...done.
Reading symbols from /usr/lib/libstdc++-libc6.1-1.so.2...done.
Reading symbols from /lib/ld-linux.so.2...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/libverify.so...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/libjava.so...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/libzip.so...done.
Reading symbols from /lib/libnss_files.so.2...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/libawt.so...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/libmlib_image.so...done.
Reading symbols from /usr/X11R6/lib/libXp.so.6...done.
Reading symbols from /usr/X11R6/lib/libXt.so.6...done.
Reading symbols from /usr/X11R6/lib/libXext.so.6...done.
Reading symbols from /usr/X11R6/lib/libXtst.so.6...done.
Reading symbols from /usr/X11R6/lib/libSM.so.6...done.
Reading symbols from /usr/X11R6/lib/libICE.so.6...done.
Reading symbols from /usr/java/jdk1.3/jre/lib/i386/libfontmanager.so...done.
Reading symbols from /usr/lib/gconv/ISO8859-1.so...done.
0x404fa320 in __poll (fds=0x805ac50, nfds=1, timeout=2000)
45 ../sysdeps/unix/sysv/linux/poll.c: No such file or directory
(gdb) info threads
12 Thread 11726 0x4046e58b in __sigsuspend (set=0xbe3ff808)
11 Thread 11724 0x404fa320 in __poll (fds=0x48cf8d60, nfds=2, timeout=250)
10 Thread 11723 0x4046e58b in __sigsuspend (set=0xbe9ff82c)
9 Thread 11722 0x4046e58b in __sigsuspend (set=0xbebff7bc)
8 Thread 11718 0x4046e58b in __sigsuspend (set=0xbedffae0)
7 Thread 11717 0x4046e58b in __sigsuspend (set=0xbefffaf8)
6 Thread 11716 0x404e07f1 in __libc_nanosleep () from /lib/libc.so.6
5 Thread 11715 0x4046e58b in __sigsuspend (set=0xbf3ff7f8)
4 Thread 11714 0x4046e58b in __sigsuspend (set=0xbf5ff830)
3 Thread 11713 0x404e07f1 in __libc_nanosleep () from /lib/libc.so.6
2 Thread 11667 (initial thread) 0x4046e58b in __sigsuspend (set=0xbfffd9e0)
* 1 Thread 11712 (manager thread) 0x404fa320 in __poll (fds=0x805ac50,
nfds=1, timeout=2000) at ../sysdeps/unix/sysv/linux/poll.c:45
(gdb) t 12
[Switching to thread 12 (Thread 11726)]
#0 0x4046e58b in __sigsuspend (set=0xbe3ff808)
48 ../sysdeps/unix/sysv/linux/sigsuspend.c: No such file or directory.
#0 0x4046e58b in __sigsuspend (set=0xbe3ff808)
#1 0x4001d1db in pthread_cond_wait (cond=0x81b8efc, mutex=0x81b8ee4)
#2 0x401cc7bd in ObjectMonitor::wait ()
#3 0x401ea68f in ObjectSynchronizer::wait ()
#4 0x4017c462 in JVM_MonitorWait ()
#5 0x8069c95 in ?? ()
#6 0x8067579 in ?? ()
#7 0x8067579 in ?? ()
#8 0x8067525 in ?? ()
#9 0x404439b0 in StubRoutines::_code1 ()
#10 0x40154342 in JavaCalls::call_helper ()
#11 0x401d1801 in os::os_exception_wrapper ()
#12 0x40154690 in JavaCalls::call ()
#13 0x40153e8b in JavaCalls::call_virtual ()
#14 0x40154b1b in JavaCalls::call_virtual ()
#15 0x4018880f in thread_entry ()
#16 0x401fef1f in JavaThread::thread_main_inner ()
#17 0x40202a87 in JavaThread::run ()
#18 0x401d05c3 in _start () from /usr/java/jdk1.3/jre/lib/i386/client/libjvm.so
#19 0x4001dea5 in pthread_start_thread (arg=0xbe3ffe60) at manager.c:213
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: /usr/java/jdk1.3/bin/i386/native_threads/java, Thread 11712