The most reliable way to ensure that your application runs on another platform is to test it out?run your code on as many platforms as possible before releasing it or considering the development complete. Testing compatibility will reveal some of your most obvious problems, such as GUI elements that don't display correctly. That said, some specific areas of your application should be tested rigorously.
Make sure your application can read and write files correctly. If the platform supports path or file names with spaces (such as on Mac OS X), see if your application handles this task correctly. Also, find out how the application handles high-bit characters (for example, ™, ä, ê, and ó).
You'd be surprised how often you'll see a hardcoded reference to a path in a Java application. Try opening the preferences dialog, changing and saving preferences, and quitting and reopening the application. If you use JDK 1.4's Preferences API, be aware that preferences are not guaranteed to migrate across platforms (or even different systems running the same platform). If migration is a requirement, you might want to consider a different preferences mechanism. If you're using ordinary Java property files, remember that they are saved to disk as 7-bit text files, and users who try to edit them manually might encounter difficulties with high-bit characters.
Instead, rely on JAR files to contain resources and values returned from standard file dialogs. For more information on packaging applications with relative links and storing resources in JAR files, consult Chapter 7 and Chapter 8.
When building cross-platform applications, anything that interacts with native or platform-specific code is obviously at risk. If you've written native code, you're probably breaking portability, but if your application should still function on a pure Java environment, make sure it does. Add checks to your code to verify that classes that rely on native code can actually be loaded (fortunately, you can use the same dynamic class loading techniques described above).
If at all possible, try to build a version of your application that still runs (perhaps with limited functionality) in a so-called "pure" Java environment. To test this application, remove the native library from the JVM search path and try to launch the application. Depending on the application, you may wish to display a dialog to the user indicating that some functionality is not available on this platform, or simply note the reduced functionality in a log. You might also want to include a mechanism for users to report their desired platform so you can get a sense of the demand for your application on that environment.
Besides the guidelines and suggestions described in Chapter 4 and Chapter 5, test the various "Minimize" and "Maximize" controls and other native user interface elements. On Mac OS X, this will require testing the application's behavior for the red, yellow, and green buttons in various combinations. You'll also want to test the application's response to messages sent from the Dock and various system events (for example, if a user tries to shut down). You should duplicate these tests on other supported platforms as well.
If your application uses threads extensively, stress-test it on different platforms to see if you can force deadlocks or other issues to appear. For example, if your application is a multithreaded GUI FTP client, try to initiate as many downloads as possible. If it's a web application, use a web testing tool such as Apache Jakarta's JMeter (http://jakarta.apache.org/jmeter).
If your application allows user-controlled thread generation, you may wish to provide more graceful handling of thread generation control than merely allowing the user to continue creating threads until the JVM fails. For example, you may wish to limit the number of simultaneous downloads in your FTP client to a maximum of ten. Unfortunately, it's almost impossible to determine in advance the specific number of threads that will result in failure. Some applications, such as web servers, let users control the maximum number of threads that may be created via a preference (defaulting to a fairly conservative value), and if your system administrator intends to make your application highly tunable, you may wish to offer a similar option.