Novell Home

How To Port Java Applications To SUSE Linux

Novell Cool Solutions: Feature
By Simon Nattrass

Digg This - Slashdot This

Posted: 14 Jul 2004
 

Overview
With Java's "Write Once, Run Anywhere" promise, migrating Java applications between platforms is a relatively painless process. Developers can take advantage of the wide spectrum of Java applications currently available and couple these with the power of SUSE Linux. This document highlights those areas where special consideration might be required in porting Java application to the Linux platform.

Contents

1. Java on the Linux Platform
2. Java on SUSE
3. GUI Applications
4. Threading
5. Generating a Java Stack Trace on Linux

Java on the Linux Platform

To most people "Java" is the official distribution from Sun Microsystems. But it is important to note that there are implementations of both Java Virtual Machine (JVM) and Java Development Kit (JDK) from other vendors available for Linux. Some alternatives are:

  • IBM developer kit for Linux


  • Blackdown -- a Java environment specifically for Linux


  • Kaffe - a clean room implementation of the Java virtual machine, plus the associated class libraries needed to provide a Java runtime environment

For a fuller list see "Marco Schmidt: List of Java virtual machines, Java development kits and Java runtime environments". This document focuses on the Sun distribution.

Java on SUSE

The SUSE distributions offer both the user and developer a choice of Java Runtime Environments (JRE) and Java Development Kits, with a vanilla installation providing the following environments by default:

  • SUSE Professional 9.0 - Sun JRE 1.4.2


  • SUSE Professional 9.1 - Sun JRE 1.4.2


  • SUSE Linux Enterprise Server 8.0 - Sun JRE 1.3.1, and IBM JDK 1.3.1

Where a JDK is not installed by default, one (or more) are available on the distribution as are a variety of other tools to aid development: jikes (the IBM Java compiler), gcc-java (the GNU java compiler) and associated libraries, Apache Ant, jserv (Java Servlet engine for Apache) etc.

SUSE supports the installation of multiple Java environments via a framework which prioritizes the installed JREs and JDKs, allowing one of the Java Virtual Machines to be the default for the system environment (defined by symbolic link /usr/lib/java), with the others available to applications when specifically requested. Of course this system-wide default is easily switchable.

The directory /etc/java contains configuration files for all installed JDKs and JREs, with one file for each installed JRE or JDK. For example, the contents of SUSE Linux Enterprise Server 8.0 appear as follows:

Each configuration file contains the following parameters:

  • Priority - the JDK or JRE with higher priority (lower value) is selected to be the system-wide setting.


  • Vendor - defines vendor name and allows selection of Java component based on this name.


  • Version - defines JDK or JRE version. Allows selection based on version.


  • Devel -- either "True" or "False" with "True" defining a JDK component.


  • JAVA_BINDIR - defines the path to Java executables and will be used for PATH and JAVA_BINDIR environment variables.


  • JAVA_ROOT - defines the path to the Java root directory and will be the same for JDK and JRE of the same version from the same vendor. This defines their JAVA_ROOTDIR environment variable.


  • JAVA_HOME -- similar to JAVA_ROOT and used for applications which use this value to find Java executables in $JAVA_HOME/bin. Will be used for JAVA_HOME environment variable.


  • JRE_HOME -- some applications use this value to search for Java runtime executables and libraries in $JRE_HOME/bin and $JRE_HOME /lib. Will be used for JRE_HOME environment variable.


  • JDK_HOME - added as supplement to JRE_HOME and is defined for all JDKs. Will be used for JDK_HOME environment variable.


  • SDK_HOME - defined for JDKs with version 1.2 and higher. Will be used for SDK_HOME environment variable.


  • JAVA_LINK - used by scripts setDefaultJava and SUSEconfig, the link /usr/lib/java and other compatibility links will point to this directory name or to this path.

Example:

# Configuration for IBMJava2-SDK package
Priority : 50
Vendor   : IBM
Version  : 1.3.1
Devel    : True

JAVA_BINDIR = /usr/lib/IBMJava2-1.3/bin
JAVA_ROOT   = /usr/lib/IBMJava2-1.3
JAVA_HOME   = /usr/lib/IBMJava2-1.3
JRE_HOME    = /usr/lib/IBMJava2-1.3/jre
JDK_HOME    = /usr/lib/IBMJava2-1.3
SDK_HOME    = /usr/lib/IBMJava2-1.3
	JAVA_LINK   = IBMJava2-1.3

These configuration files are read by the /usr/sbin/setDefaultJava and /usr/bin/setJava scripts. setDefaultJava changes the system-wide default Java environment via updating the link /usr/lib/java while setJava sets updates to the current shell only. Both are wrappers for setJava.pl Perl script.

The scripts may be used to accept the following options:

  • devel
  • Example: Change link in /usr/lib from JRE to highest priority JDK
    setDefaultJava ---devel


  • strict
  • Example: Set any Java with version 1.3.x
    setDefaultJava ---strict ---version 1.3


    Example: Set any Java version 1.3.1
    setDefaultJava ---strict ---version 1.3.1


  • linkname (setDefaultJava only)
  • Example: add link "foo" in /usr/lib pointing to current Java environment
    setDefaultJava ---linkname foo


  • vendor
  • Example: Set any Java from Sun Microsystems
    setDefaultJava ---vendor Sun


  • version
  • Example: Set any Java version 1.3 or above
    setDefaultJava ---version 1.3

    The setJava script is required to be run with the source shell command since the result of the script must be sourced to change the settings in the current shells.

    Example: Set any Java version 1.3 or above
    source setJava ---version 1.3

    Upon installing an additional JRE or JDK, the corresponding configuration files in /etc/java will need to be written before this new environment can take advantage of the setDefaultJava and setJava scripts.

    In addition to the default java link (/usr/lib/java), the SUSEconfig script maintains some links which can also help you to find valid JDK or JRE:

    • /usr/lib/java1 - points to any JDK or JRE with version 1.1.x
    • /usr/lib/java2 - points to any JDK or JRE with version 1.2 or higher
    • /usr/lib/SunJava1 - points to any JDK or JRE from Sun Microsystems with version 1.1.x
    • /usr/lib/SunJava2 - points to any JDK or JRE from Sun Microsystems with version 1.2 or higher
    • /usr/lib/IBMJava2 - points to any JDK or JRE from IBM with version 1.2 or higher
    • /usr/lib/BlackdownJava2 - points to any JDK or JRE from Blackdown with version 1.2 or higher

    Not all the links have to exist at a time. SUSEconfig uses the following rules when creating these links:

    • Create the link when a related JDK / JRE exists
    • Do not change the link which points to a valid JDK / JRE
    • Fix the link that points to a non-existing target or a directory containing no JDK / JRE, yet a related JDK / JRE exists
    • Remove the link that points to a non-existing target or a directory containing no JDK / JRE, and a related JDK / JRE does not exist

    At your own risk, you may disable this SUSEconfig behavior by setting the variable CREATE_JAVALINK="no" in /etc/sysconfig/java or indirectly via YaST (System -> Editor for /etc/sysconfig -> Applications -> Java). Take into account that Java applications distributed within SUSE may use the above links.

    By default SUSEconfig ensures the validity of the link /usr/lib/java and the link will be corrected if it points to an invalid directory.

    GUI Applications

    Java GUI development is largely platform agnostic with a few issues to be aware of when developing for Linux.

  • Events


  • For pop-up menus, the trigger event on Linux is a mouse press, while the same event on the Windows platform is a mouse release. Thus both events require checking to see if MouseEvent.isPopupTrigger() returns true.

  • Cut and Paste
  • Cut and Paste under the X Windows environment is provided by two mechanisms:

    • Selection: text is selected for copying by dragging the cursor, then is copied to other applications by clicking the middle mouse button. The data is stored in the primary buffer. Most X applications support primary selection.


    • Clipboard: applications can cut/copy/paste text using menu selections and common key shortcuts -- Control-C for copy, Control-X for cut, and Control-V for paste. The data is stored in the clipboard buffer. Many applications do not support this method.

    The AWT only supports the clipboard buffer. For primary selection functionality, developers will need to use JNI to make the required calls to the X server.

  • Fonts
  • Java defines five logical font names: Serif, SansSerif, Monospaced, Dialog, and DialogInput which are mapped to physical fonts in implementation-dependent ways via the font.properties files. Since the same logical fonts do not always map into the same physical fonts across all platforms, GUI applications may differ in the appearance of their fonts.

    Users can edit or create their own font.properties files to adjust the mappings to their particular system setup. Note, however, that this is a modification of the JRE which Sun does not support.

  • AWT and X11
  • The AWT uses the native graphics resources of the system on which the Java Virtual Machine runs to implement drawing operations of primitives such as drawLine (), fillOval (), drawString () etc. Although this technique ensures the best performance, it can cause problems when no X11 Display is available (headless environment), for example a J2EE application running returning dynamically generated images like pies, charts, etc.

    There is a popular misconception that the system property java.awt.headless=true fixes this issue, however running J2EE code similar to the above without any X11 installation results in a java.lang.UnsatisfiedLinkError to libawt.so. If we look deeper at libawt.so we see that there is still a dependency on the X11 libraries.

    ldd libawt.so
    
    libmlib_image.so => not found
    libjvm.so => not found
    libXp.so.6 => not found
    libXt.so.6 => not found
    libXext.so.6 => not found
    libXtst.so.6 => not found
    libX11.so.6 => not found
    libm.so.6 => /lib/i686/libm.so.6 (0x40306000)
    libdl.so.2 => /lib/libdl.so.2 (0x40329000)
    libjava.so => not found
    libc.so.6 => /lib/i686/libc.so.6 (0x4032d000)
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)

    This can be easily remedied by installing the X11 libraries (Xfree86-libs).

    Threading

    Both green and native threads are mechanisms to support multi-threaded execution of Java programs. Some JDK distributions include the option to run with either type of threading.

    Native threads use the operating system's native ability to manage multi-threaded processes - in particular, they use the pthread library. When you run with native threads, the kernel schedules and manages the various threads that make up the process.

    Green threads emulate multi-threaded environments without relying on any native OS capabilities. They run code in user space that manages and schedules threads; Sun wrote green threads to enable Java to work in environments that do not have native thread support.

    Sun Java version 1.3 supports both threading models, native by default and green via the -classic flag to the JVM. Java version 1.4 has since dropped green thread functionality.

    There are some important differences between using the two in a Linux environment:

    • Native threads can switch between threads pre-emptively, switching control from a running thread to a non-running thread at any time. Green threads only switch when control is explicitly given up by a thread (Thread.yield(), Object.wait(), etc.) or a thread performs a blocking operation (read(), etc.).


    • On multi-CPU machines, native threads can run more than one thread simultaneously by assigning different threads to different CPUs. Green threads run on only one CPU.


    • Using native threads provides an implementation which is simpler and stable, but creates the appearance that many Java processes are running, since each thread takes up its own entry in the process table. One clue that these are all threads of the same process is that the memory size is identical for all the threads - they are all using the same memory.

    Unfortunately, this behavior limits the scalability of Java on Linux. The process table is not infinitely large, and processes can only create a limited number of threads before running out of system resources or hitting configured limits.

    The confusing ps command display issue is fixed in the 2.0.7 version of the procps package and with the advent of the new 2.6 kernel, which provides proper thread grouping and reporting. The 2.6 kernel introduces a new, improved threading model that is implemented through the Native Posix Thread Library (NPTL). The NPTL approach keeps the 1:1 thread mapping, 1 user or Java thread to 1 kernel thread, but optimizes the kernel for thread related operations, including signal handling, synchronization and thread creation speed. The forthcoming SUSE Linux Enterprise Server 9.0 and Novell Open Enterprise Server contain the 2.6 kernel.

    We can see the dependence of the JVM on the pthreads library below:

    ldd java
            libpthread.so.0 => /lib/i686/libpthread.so.0 (0x40029000)
            libdl.so.2 => /lib/libdl.so.2 (0x4007a000)
            libc.so.6 => /lib/i686/libc.so.6 (0x4007d000)
            /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

    Generating a Java stack trace on Linux

    As Java threads are implemented as cloned processes, the task of debugging Java programs running on Linux differs from other platforms. To generate a stack trace from the terminal window in which the application was started, the sequence <CTRL>\ is typed to send the QUIT signal which generates a stack trace.

    If <CTRL>\ fails to give the expected result, try checking the mapping of key sequence to signal via typing: stty -a. The entry to look for being quit = <key sequence> where ^ typically denotes CTRL.

    To generate a stack trace from a Java application already running in the background, the QUIT (signal number 3) needs to be explicitly sent to the launcher process via the kill command: kill -3 <process id>. For example kill -3 1234, where 1234 is the launcher process id.

    The resulting stack trace will be a snapshot of the Java program execution detailing the state of each thread. This information can be used to track down deadlocks or busy sections in your application.

    Summary

    With its security and stability, the minimal amount of Linux knowledge required to develop or deploy Java applications, and the ease of migration, Linux is becoming an attractive choice for development and application hosting. Today there exists a broad range of Java applications which can be ported to SUSE Linux with nominal effort.

    References


    Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

    © 2014 Novell