BUY THIS BOOK
Add to Cart

Print Book $39.95


Safari Books Online

What is this?

Add to UK Cart

Print Book £28.50

What is this?

Looking to Reprint this content?


Mac OS X for Java Geeks
Mac OS X for Java Geeks

By Will Iverson
Price: $39.95 USD
£28.50 GBP

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Getting Oriented
Mac OS X is, in many ways, a new paint job on a 30-year-old operating system. BSD (the Berkeley Software Distribution), the Unix root of Mac OS X, has been around since the 1960s. The Mach kernel was developed in the 1990s, and the underlying user interface was created in early 1980s along with Lisa (Apple's ill-fated precursor to the Macintosh). In other words, everything old is new again.
Mac OS X doesn't feel like a 30-year-old clunker, though, but the culmination of countless hours of experimentation and refinement in desktop and workstation operating systems. To a Unix expert, Mac OS X is much like a solid distribution of a classic BSD system with the most egregiously beautiful window manager you've ever seen. For the Windows veteran, it is a simplified beast—a pure workhorse of modern productivity stripped of decades of anachronisms and distilled until it has an almost Zen-like simplicity. For the Mac OS 9 user, it represents an even more significant change. Nasty crashes and ridiculous extension conflicts are now a thing of the past, while Aqua, Mac OS X's new user interface, is clearly the look of the future.
Most importantly, though, Mac OS X is finally a developer's platform. With the melding of BSD, a killer user interface, and unprecedented stability, code can finally be written on the Mac OS X platform and deployed to Windows, Linux, Unix, or other Mac OS X servers. This book was written with the Java developer in mind. It assumes some degree of Java experience and familiarity with basic Unix commands such as cd, ls, and pwd. Maybe you are interested in porting an existing Java application to Mac OS X (perhaps because your customers asked for a Mac OS X version). Or maybe Linux is your development platform, but you are interested in moving to Mac OS X to access powerful graphics applications such as Adobe Photoshop. Maybe you're a bored Windows user, or are philosophically opposed to the Microsoft hegemony.
Your degree of experience really doesn't matter; Mac OS X is a great Java development platform for people of all programming and operating system backgrounds.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
All Those Confusing Names
Mac OS X has, at different times, been associated with several different names. At one point it was called Rhapsody. Prior to that, it was NeXT's OpenStep and NeXTStep platform. The underlying Unix guts were also released as an open source project, Darwin, which includes BSD and the Mach kernel. With that in mind, explaining where Mac OS X started and where it is now will contextualize Mac OS X in its current incarnation.
Mac OS X 10.0 was the first commercial release of Mac OS X. That release, however, wasn't particularly usable.
Convincing a large body of developers to embrace a new platform is not easy. You can release developer seeds, betas, and prereleases all you want, but at the end of the day, major operating system vendors have to release something that can be called a 1.0 product (or, in the case of Mac OS X, a 10.0 product). Releasing this product lets users know that you're transitioning from testing to "prime time."
The commercial release of Mac OS X 10.0 was just that: it was Apple's way of telling developers that the system was ready to go and that they should get on board. At this point, Apple began shipping Mac OS X 10.0 with their hardware, but didn't make it the default operating system. The release was lacking in quality, features, and supported applications, and everyone knew that the product needed more work.
Mac OS X 10.1 marks what most people consider the first usable version of Mac OS X. Developers fixed a lot of important bugs, addressed performance issues, and added missing features.
Even more significant, however, was an Apple announcement at Macworld in January 2002. During one of the conference's keynote addresses, Steve Jobs announced that Apple would begin shipping Mac OS X as the default operating system. Users could still switch back to Mac OS 9 if they wanted, but when someone took that shiny new iMac out of the box, Mac OS X's Aqua greeted them. Apple's commitment to Mac OS X as their default platform was a clear message—developers and users both were assured of Apple's commitment to Mac OS X as an operating system for mainstream use.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Why Now?
Apple has shipped Macintosh computers since 1984, but my tale of the Mac OS begins with Mac OS X. Describing all of the prior releases and their features is beyond the scope of this book, but all "Classic Mac" operating systems share a common set of weaknesses, including:
  • A lack of memory protection
  • Explicit shared memory for important structures
  • An overburdened, fragile, and inadequate system-extending mechanism
  • A lack of true multiprocessing
  • An amazing legacy of cruft, including the Motorola 680x0 emulator and various other obsolete or cancelled technologies
The list could go on. Every Mac OS X installation includes a complete working copy of this Classic Mac OS, and when you launch an old-style Classic Mac OS application, you actually launch a complete working copy of this environment as a process for Mac OS X.
However, with the release of Mac OS X, Java development is finally a reality, rather than a marketing ploy or an Apple employee's pipe dream. So let's dive right into Java development on the Mac.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Apple's Java Platform
With a basic understanding of the Mac OS X platform, you're ready to get down to some bits and bytes . . . well, almost. First, you need to make sure you've got your Java compiler running properly, your environment variables set, and all program directories in the right place. We'll deal with all of that in this chapter.
First, make sure you have a Java Virtual Machine (JVM). Open up the Terminal application, type java -version, and you'll see the following message (or something similar):
java version "1.3.1"
Java(TM) 2 Runtime Environment, 
    Standard Edition (build 1.3.1-root-020219-20:07)
Java HotSpot(TM) Client VM (build 1.3.1, mixed mode)
This message indicates that your JVM is set up and working, and that may seem like all you need to know. However, there is much more to a JVM than the ability to fire up a Java process. For starters, Apple preinstalls the JVM in a specific location, automatically including a number of additional classes. These classes number in the hundreds and add Apple-specific functionality to the core Java distributions.
In this section we'll look at Apple's JDK 1.3.1 installation, which is included with all Mac OS X 10.1 systems and beyond. The JDK 1.4.1 release is available for download via Apple's Software Update feature or http://www.apple.com/macosx/downloads. It will install only on Mac OS X Version 10.2.3 or later.
The Swing settings are also unique to the Apple JVM: the default look and feel corresponds to the Aqua user interface, which has quite an effect on your graphical applications. Apple also added hardware acceleration (for JDK 1.3) and implemented a shared memory model for reducing the overhead of running multiple Java applications. Although you've got the same basic JVM as on a Unix, Linux, or Windows platform, you should pay attention to some additions and differences.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Apple JVM Basics
First, make sure you have a Java Virtual Machine (JVM). Open up the Terminal application, type java -version, and you'll see the following message (or something similar):
java version "1.3.1"
Java(TM) 2 Runtime Environment, 
    Standard Edition (build 1.3.1-root-020219-20:07)
Java HotSpot(TM) Client VM (build 1.3.1, mixed mode)
This message indicates that your JVM is set up and working, and that may seem like all you need to know. However, there is much more to a JVM than the ability to fire up a Java process. For starters, Apple preinstalls the JVM in a specific location, automatically including a number of additional classes. These classes number in the hundreds and add Apple-specific functionality to the core Java distributions.
In this section we'll look at Apple's JDK 1.3.1 installation, which is included with all Mac OS X 10.1 systems and beyond. The JDK 1.4.1 release is available for download via Apple's Software Update feature or http://www.apple.com/macosx/downloads. It will install only on Mac OS X Version 10.2.3 or later.
The Swing settings are also unique to the Apple JVM: the default look and feel corresponds to the Aqua user interface, which has quite an effect on your graphical applications. Apple also added hardware acceleration (for JDK 1.3) and implemented a shared memory model for reducing the overhead of running multiple Java applications. Although you've got the same basic JVM as on a Unix, Linux, or Windows platform, you should pay attention to some additions and differences.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Apple's JVM Directory Layout
Even if you're an experienced Java developer, it can be a bit difficult to understand exactly where and how the Apple JVM is installed and configured. Apple has carefully hidden some of its files and libraries to keep users from accidentally wiping out their data and to manage the complexity of upgrading the Mac OS X operating system.
Although its philosophy is unlike that of Windows, Mac OS X tries to maintain a high level of integration between the OS and the programs that run on it. Java is no exception, and the Apple JVM was created with integration and ease of upgrade in mind.
Begin your system tour by opening the Terminal application and going to your hard drive's root directory (or folder). The quickest way to get there is to open a new Finder window and click Computer in the toolbar, and then double-click on the Hard Drive icon. This is the Mac OS X "root" location, which is what you would see from the terminal by typing cd / and then ls:
[Wills-Laptop:/] wiverson% ls
AppleShare PDS            SimpleClass.java          etc
Applications              System                    mach
Desktop DB                TheVolumeSettingsFolder   mach.sym
Desktop DF                Trash                     mach_kernel
Desktop Folder            Users                     private
Developer                 Volumes                   sbin
IE Install Log File       automount                 tmp
Library                   bin                       usr
Network                   cores                     var
Office X SR1 Updater Log  dev
[Wills-Laptop:/] wiverson%
You'll find a folder called Library immediately inside the root directory. This folder contains several default directories. When you install Mac OS X, these directories are created automatically so that the applications will have appropriate default directories available to them.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Additional APIs and Services
The Mac OS X Java installation includes several additional APIs and services besides the default JDK installations available from Sun for Unix and Windows. In theory, you could build applications for the Mac OS X Java platform that won't run on other platforms. Purists may argue that Apple's JVM is therefore no different from the infamous Microsoft JVM and its incompatibilities. It is very different, however.
Apple hasn't removed anything from their JVM implementation. All the expected services, including the politically contentious RMI, are available on the Mac OS X Java platform. If you build your application against normal J2SE APIs, you will have no problem porting your applications to other platforms. For more information on cross-platform compatibility, check out Chapter 9.
However, Apple provides many features in addition to the core Java APIs. Spelling, integration with QuickTime, and the Apple look and feel, for example, are specific to the Macintosh, and you will have trouble porting applications that use them to another platform unless you're willing to change some of your application's code. However, this book will help you recognize what is Mac-specific, and you'll soon avoid these APIs or adjust them for use on other platforms when cross-platform compatibility is a concern.
Later chapters cover many additional services, such as the Java Speech and Spelling Frameworks and application bundling. For now, though, here's a summary of what the Mac OS X JVM adds to the standard Java bundle:
  • Support for Aqua, the translucent, swooshing, pulsing user interface Apple introduced with Mac OS X (as well as Metal, the default Java look and feel)
  • External BSD tool support (significant if you need man, ifconfig, or other unusual BSD tools)
  • Java interfaces to QuickTime, for multimedia support
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Going Forward
Now you've explored the basic foundations of the Mac OS X Java platform. The next few chapters cover different aspects of Java development, from IDEs and tools, rich desktop application GUIs, and application packaging to Apple-specific technologies such as QuickTime and the Speech Framework.
If you are interested in desktop (also known as client-side) development, start with Chapter 4 and Chapter 5, which cover GUI applications and Apple extensions. From there, you may want to move on to standalone applications in Chapter 7. Then you might benefit from exploring other Apple technologies, such as Speech, QuickTime, and Spelling.
There is also a lot to be said about Mac OS X's enterprise support for Java. If you are primarily interested in web-delivered client applications, look at Chapter 8. If you want to learn more about web application development, concentrate on Chapter 12 through Chapter 15.
No matter what sort of application you're building, if maintaining cross-platform compatibility is important, be sure to review the material in Chapter 6.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Java Tools
One of the nicest things about Mac OS X is its very broad range of tools. The Classic Mac OS platform had many software development tools, of which the most popular and flexible was Metrowerks CodeWarrior. The release of Mac OS X, however, has broadened the range of available tools tremendously, and a large set of Java- and Unix-based tools is now available. Mac OS X also ships with Project Builder, an integrated desktop environment for programming in several languages, including Java.
It's worth taking the time to review the various tools. If you're an old emacs or vi hand, you'll be able to access those tools (just fire up the Terminal). Even if you're an emacs or vi addict, you still might want to browse through the tools just to get an idea of what folks are talking about.
The revolutionary thing about Mac OS X for a developer is a single, boring window with a blinking cursor. Double-click on the main icon for your computer on the desktop, navigate into the Applications directory, and then into Utilities. Inside this folder, you'll see a Terminal icon. Double-click on this icon to open the application.
You'll then see a "Welcome to Darwin!" message and a localhost prompt. The shell is the Unix standard tcsh, which stands for "Tenex csh." csh was the default shell for BSD Unix through the 1980s (and Mac OS X is based on BSD Unix). tcsh is an upward-compatible enhancement of csh, which includes "command completion" borrowed from an early 1970s experimental operating system called Tenex.
If you're an emacs user, turn on emulation of the Meta (alt) key in the "Terminal Preferences... Emulation" screen (or by selecting "Shell Inspector... Emulation" for a per-window modification).
Learning to use the Terminal is far beyond the scope of this text, but a few basic commands are required for basic system navigation. These common commands are listed here:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Terminal
The revolutionary thing about Mac OS X for a developer is a single, boring window with a blinking cursor. Double-click on the main icon for your computer on the desktop, navigate into the Applications directory, and then into Utilities. Inside this folder, you'll see a Terminal icon. Double-click on this icon to open the application.
You'll then see a "Welcome to Darwin!" message and a localhost prompt. The shell is the Unix standard tcsh, which stands for "Tenex csh." csh was the default shell for BSD Unix through the 1980s (and Mac OS X is based on BSD Unix). tcsh is an upward-compatible enhancement of csh, which includes "command completion" borrowed from an early 1970s experimental operating system called Tenex.
If you're an emacs user, turn on emulation of the Meta (alt) key in the "Terminal Preferences... Emulation" screen (or by selecting "Shell Inspector... Emulation" for a per-window modification).
Learning to use the Terminal is far beyond the scope of this text, but a few basic commands are required for basic system navigation. These common commands are listed here:
cd
Change directory.
ls
List the contents of the current working directory.
pwd
Print the current working directory.
man
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Code Editors
More than a few code editors are available for Mac OS X, and no work on Java would be complete without at least mentioning these integrated development environments (IDEs). I've broken them up into several categories: those that are open source, those that are free, and those that are sold commercially. I'm a big fan of open source tools, but all the tools mentioned here get the job done, so pick your own poison.
Many available open source tools have been ported to or run under Mac OS X. These tools are all free (as are the tools in the next section), but also make their source code available.

Section 3.2.1.1: NetBeans

NetBeans™ is a full-featured, commercial-grade IDE that was acquired and open-sourced by Sun Microsystems. Written in Java, it's easily configured to run on Mac OS X. You can download it for free from http://www.netbeans.com/.
To install and configure NetBeans, pull down a current version of the software. I'm currently using NetBeans 3.4.1. Go to the NetBeans web site, click on the download link, and agree to the NetBeans license. You can then download a release for Mac OS X in disk image format. On my system, the downloaded file was called NetBeansIDE-release341-MacOSX.dmg. Mac OS X will mount it, and you can then launch NetBeans from the disk image.
Before starting up NetBeans, though, you should copy the contents of the disk image into a folder on your hard drive, such as /Applications/netbeans. I created this folder in the Finder and then copied the contents of the NetBeans disk image into the new folder. You can then drag the disk image to the trash to "eject" it.
NetBeans comes in a Mac OS X package called NetBeans Launcher. However, since we're all geeks here, let's look more closely inside this package. Control-click the launcher icon and select Show Package Contents. Navigate inside the revealed
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Jakarta Ant
As you work with Java, you'll often encounter references to Ant, an open source tool for managing build processes and other tasks. Ant is a great companion tool to IDEs and text editors, as it can manage complex build tasks, compilation, and even those nasty classpath issues discussed earlier. This section will describe Ant in some detail.
Ant is part of Apache's Jakarta project. Originally created to provide a cross-platform, portable replacement for the Unix make command, it has become a powerful development, deployment, and installation tool. To use Ant, run scripts from the command line, passing in an XML build file. Inside the build file, you can define variables and tasks to be performed at build time.
You can download Ant from http://ant.apache.org/. The latest version as of this writing is 1.5.1, and you can select a ZIP file or a gzipped version. I downloaded jakarta-ant-1.5.1-bin.tar.gz. Expand the file to jakarta-ant-1.5.1 and copy the resultant directory somewhere easily accessible (I used ~/dev for a user-specific installation). Then put the bin subdirectory in your path:
[Wills-Laptop:~] wiverson% setenv PATH ~/dev/jakarta-ant-1.5.1/bin:$PATH
To run Ant, use the ant script (ant.bat on Windows, and just plain ant on Unix-based systems such as Mac OS X):
[Wills-Laptop:~] wiverson% ant
Buildfile: build.xml does not exist!
Build failed
As the message here indicates, Ant expects a build file, generally called build.xml, to give it instructions.
Let's look at writing a simple Ant project and building a file. Suppose you want to write a single Ant file that handles the drudgery of compiling an application, bundles the results into a JAR file, and then copies the resulting files into a new distribution directory.
First, create a new file and call it
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Additional Tools
People get into bar fights over their choice of tools and IDEs. Here are some additional popular tools:
  • IDEA IntelliJ (http://www.intellij.com/idea/), a commercial IDE that includes support for easily refactoring your application code.
  • Eclipse (http://www.eclipse.org/), an open source IDE that also includes support for code refactoring.
  • TogetherSoft Control Center (http://www.togethersoft.com/), a commercial development environment with support for Unified Modeling Language (UML).
  • Bare Bones Software's BBEdit (http://www.barebones.com/), an excellent text editor.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: GUI Applications
Question: How many lines of code does it take to display a window?
Answer: One, but you'll spend the rest of your life rewriting it.
OK, so it's not a particularly funny joke, but it does get to the heart of why so many people originally embraced the Java platform's promise of "write once, run anywhere." Here's a graphical user interface (GUI) development conundrum: each platform has a set of specific guidelines for what is considered the proper look and feel, yet users often want to access an application across multiple platforms. Just look at the variety of development tools described earlier; the installation for many IDEs, such as NetBeans, involves selecting which user interface you'd like to work with.
The long and the short of it is that there is no one right answer for GUI construction. From overt issues, such as the menu structure defaults (where is the placement of the Preferences menu item: the File, Edit, or Application menu?), to subtle ones, such as the default layout for dialog buttons, to paradigm decisions, such as requiring the use of the second mouse button—it's hard to imagine a single approach to GUI application programming that would satisfy all application development needs.
Instead of focusing on the theoretical debate, it is often more useful to consider two key factors: the intended audience and the available resources. If you know that you will develop a consumer application on a large budget, you may wish to build a multimedia-style interface, with an emphasis on graphics, single-click actions, and lots of mouse-rollover responses. If you're building a developer tool in your spare time, you'll probably want to rely on standard Java Metal user interface objects. If you're developing a general productivity application or an in-house application for a corporate environment, you may want to build and test for both Metal on Windows and Unix and Aqua on Mac OS X.
That said, it's often easiest for GUI programmers to begin with the Mac OS X Aqua interface rather than the standard Metal look and feel. Perhaps the best reason to start with Aqua is its sheer number of default components. Aqua has one of the largest sets of defaults for spacing and fonts of any platform. If you start with Aqua, you're less likely to have problems with other platforms (including both Motif and Windows) when you switch to Metal, because your defaults will all be set correctly. In addition, the graphics-intensive nature of the Aqua platform tends to push the limits of a graphics card; if your application responds well under Aqua, less sophisticated user interfaces should be at least as responsive, if not more so.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Swing and Aqua
Swing is the user interface toolkit of Java Foundation Classes (JFC). When Sun developed the original version of Java, it introduced the Abstract Windowing Toolkit (AWT), which drew user interfaces based on an abstract layer that sat on top of the native windowing toolkit. This caused many problems, as the abstraction tended to blur when faced with the peculiarities of many windowing platforms. To resolve these issues, JFC and Swing were introduced as a more sophisticated toolkit with much better cross-platform support. JFC and Swing are based on AWT, so the core AWT is still part of Java. You could even write a pure AWT application, although there's really no good reason to: if you're developing a rich user interface you'll want to stick to Swing APIs.
One of Swing's most interesting aspects is its notion of a "pluggable" look and feel. The entire Unix world has a high degree of customizability, at the cost of a staggering variety of different approaches to user interface design. At first, these custom behaviors and functionality seem ideal, but they soon become a headache for developers and users. To deal with this issue, Swing introduced a standard look and feel called "Metal" that provides a reasonably attractive user interface for all platforms. Metal looks the same, more or less pixel-for-pixel, on all supported platforms.
However, it is still possible to override Metal and use a custom look and feel instead. Windows users may choose to add a Windows-specific look and feel to their application instead of going with the standard Metal. On Mac OS X, the obvious choice is the native Aqua look and feel. Apple has done an excellent job with their implementation of the Java-based Aqua look and feel, with many graphical operations featuring native hardware acceleration.
Therefore, when developing applications in Java, it is useful to determine what your supported look and feel options are going to be. While it's possible to say that you intend to support Metal, Aqua, Motif, and Windows look and feel selections, you'll wind up having to test your application's appearance (including the length of localized strings—you were planning on making your application localizable, right?) on each supported look and feel. This is largely a matter of budget and resources, but in this section we will focus on comparisons between the standard Metal look and feel and the Mac OS X Aqua look and feel.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
An Example Swing Application
This section introduces a fairly comprehensive Swing application and details its various components. This application is used throughout the rest of this book as the foundation application (so you can run each sample without having to create new windows, set up the menu bar, and perform other mundane tasks). For that reason, be sure to code and compile the application and have the source code handy before diving into future chapters.
For those of you who don't like large code blocks, especially when it's mostly GUI setup code, understand that this section is a necessary evil; it's the basis for several future applications, all of which are made up of more interesting code fragments and concepts.
This application also demonstrates techniques that factor out and partition portions of your application to increase team development, as well as an introduction to concepts (such as dynamic class loading) used in later chapters. In this example, these modules are referred to as plug-ins. If you've used an application such as Adobe Photoshop or the NetBeans development environment, you've been exposed to applications that serve as a framework and then load other modules. Example 4-1 shows the entire class listing.
Example 4-1. The sample Swing application
package com.wiverson.macosbook;

import javax.swing.JMenuItem;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import java.awt.Cursor;
import java.awt.BorderLayout;
import java.util.Hashtable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;

public class SimpleEdit extends javax.swing.JFrame
{
    // Used to set the number for new untitled windows
    private static int newWindows = -1;

    // Used to track all of the currently installed plugins
    private static Hashtable plugins = null;
    
    // The initial plugin configuration
    private static String[] argsconfig;
    
    /** Creates a new instance of SimpleEdit */
    public SimpleEdit(  )
    {
        init(  );
    }

/* --------------------- Application APIs ------------------------------------ */

    /** Used by tools to get the text actual text area.
     * This wouldn't generally be recommended, but in this
     * case it's ok.
     *
     * In general, you'd want to use something to make the
     * interface more opaque (thereby freeing up options to
     * switch to a different underlying toolkit), but in this
     * case it would cost readability (since everyone can look
     * up a JTextArea).
     */
    public javax.swing.JTextArea getJTextArea(  )
    {
        return this.mainTextArea;
    }
    
    /** Used by tools to get the current text */
    public String getDocumentText(  )
    {
        return this.mainTextArea.getText(  );
    }
    
    /** Used by tools to set the current text */
    public void setDocumentText(String in)
    {
        mainTextArea.setText(in);
        mainTextArea.setCaretPosition(mainTextArea.getText().length(  ));
        mainTextArea.requestFocus(  );
    }
    
    /** Used by tools to add to the current text */
    public void appendDocumentText(String in)
    {
        setDocumentText(mainTextArea.getText(  ) + in);
    }
    
    /** Used by tools to set the status text at the bottom
     * of a frame.
     */
    public void setStatusText(String in)
    {
        this.mainStatusText.setText(in);
    }

/* --------------------- Initialization ------------------------------------ */

    // Sets up and creates a "pristine" window environment
    private void init(  )
    {
        if(newWindows++ < 0)
            setTitle("Untitled");
        else
            setTitle("Untitled-" + newWindows);
                
        initPlugins(  );
        initComponents(  );
        initMenuBar(  );
    }

/* --------------------- Initialization: Plugins  ---------------------------- */
    
    // Installs all plugins as currently defined by the
    // private argsconfig.
    private void initPlugins(  )
    {
        if(plugins != null)
            return;
        if(argsconfig == null)
            return;
        if(argsconfig.length == 0)
            return;
        plugins = new Hashtable(  );
        
        for(int i = 0; i < argsconfig.length; i++)
        {
            // This may very well fail, as we are going
            // to be loading classes by name, which is
            // prone to errors (e.g. typos, etc.)
            try
            {
                // This requests the classloader to find a
                // given class by name.  We are using this to
                // implement a plugin architecture, based on
                // expecting classes to implement a specific
                // interface (SimpleEditPlugin).  If the class
                // can be loaded and cast without failure,
                // we are good to go.
                Class myClass = Class.forName(argsconfig[i]);
                SimpleEditPlugin myPlugin = 
                 (SimpleEditPlugin)myClass.getConstructor(null).newInstance(null);
                
                // Don't add the plugin if already installed. Allows for 
                // eventual support for dynamically adding new plugins later.  
                // Calls the Plugin init if this is the first time 
                // it's being loaded.
                if(plugins.containsKey(myPlugin.getAction(  )))
                {
                    return;
                } else
                {
                    myPlugin.init(this);
                }
                
                // If we made it this far, the plugin has been loaded
                // and initialized, so it's ok to add to the list of
                // valid plugins.
                plugins.put(myPlugin.getAction(  ), myPlugin);
            }
            catch(Exception e)
            {
                // This is not really adequate for a quality client 
                // application, but it's acceptable for our purposes.
                System.out.println("Couldn't load Plugin: " + argsconfig[i]);
                System.out.println(e.getMessage(  ));
                e.printStackTrace(  );
            }
        }
    }

/* --------------------- Initialization: GUI Components---------------------- */

    // The main visual components
    private javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane(  );
    private javax.swing.JTextArea mainTextArea = new javax.swing.JTextArea(  );
    private javax.swing.JToolBar mainToolBar = new javax.swing.JToolBar(  );
    private javax.swing.JTextField mainStatusText= new javax.swing.JTextField(  );
    
    private void initComponents(  )
    {
        this.getContentPane(  ).setBackground(java.awt.Color.white);
        this.getContentPane().setLayout(new BorderLayout(  ));
        this.getContentPane(  ).add(mainScrollPane, BorderLayout.CENTER);
        this.getContentPane(  ).add(mainToolBar, BorderLayout.NORTH);
        this.getContentPane(  ).add(mainStatusText, BorderLayout.SOUTH);
        
        // This text field serves two purposes. It provides useful information
        // to the user, and also serves as a graceful "bump" for the Mac OS
        // grow box on the Mac OS platform.
        mainStatusText.setText("Ready.");
           
        mainStatusText.setCursor(
             Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        
        mainScrollPane.setViewportView(mainTextArea);
        
        mainTextArea.setEditable(true);
        mainTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
        mainTextArea.setFont(
            new java.awt.Font("serif", java.awt.Font.PLAIN, 12)
            );

        // Perhaps a tool might be added later to control this dynamically?
        mainTextArea.setLineWrap(true);
        
        // Generally looks terrible on all platforms, and requires
        // a fair amount of work to get it to work right.
        mainToolBar.setFloatable(false);
        initToolBar(mainToolBar, this);
        
        // Determine the offset value and stagger new windows
        // (with a reset every ten windows). A somewhat
        // unscientific mechanism, but it works well enough.
        int top_offset = 0;
        if((newWindows % 10) > 0)
        {
            top_offset =((this.newWindows) % 10) * 20 + 20;
            
            this.setLocation(
                 new Double(getLocation().getX() + top_offset - 20).intValue(  ),
                 new Double(getLocation().getY() + top_offset).intValue(  )
            );
        }
        int bottom_offset = 0;
        if (top_offset > 0)
            bottom_offset = top_offset - 20;
        
        // In a later chapter, we can use the JDirect and the
        // Carbon API GetAvailableWindowPositioningBounds(  )
        // to properly position this.
        java.awt.Dimension screensize =
             java.awt.Toolkit.getDefaultToolkit().getScreenSize(  );
        screensize = 
             new java.awt.Dimension(640, screensize.height -128 - bottom_offset);
        this.setSize(screensize);
    }

    // Default items that always appear on the toolbar.
    // null items are treated as separators.
    String[] toolbarItems = {"New", "Open", null, "Timestamp"};

    private void initToolBar(javax.swing.JToolBar myToolBar, SimpleEdit myFrame)
    {
        JButton newButton;
        for(int i = 0; i < toolbarItems.length; i++)
        {
            if(toolbarItems[i] != null)
            {
                // It would be nice to provide icons
                // instead of just text labels.
                newButton = new JButton(toolbarItems[i]);
                
                // Used to track the targets more easily
                newButton.putClientProperty("window", myFrame);
                newButton.addActionListener(actionListenerHandler);
                myToolBar.add(newButton);
            } else
            {
                myToolBar.add(new javax.swing.JToolBar.Separator(  ));
            }
        }
        
        // Load all plugins into the toolbar
        if(plugins != null)
            if(plugins.size(  ) > 0)
            {
                java.util.Enumeration e = plugins.elements(  );
                SimpleEditPlugin currentPlugin;
                while(e.hasMoreElements(  ))
                {
                    currentPlugin = (SimpleEditPlugin)e.nextElement(  );
                    newButton = new JButton(currentPlugin.getAction(  ));
                    // We are using Swing client properties to 
                    // track additional information without having
                    // to subclass - in effect, using the
                    // client properties mechanism as a form of
                    // delegation.
                    newButton.putClientProperty("window", myFrame);
                    newButton.putClientProperty("plugin", currentPlugin);
                    newButton.addActionListener(actionListenerHandler);
                    myToolBar.add(newButton);
                }
            }
    }

/* --------------------- Initialization: Menu Bar  ---------------------------- */

    // The menu bar for the window
    private javax.swing.JMenuBar mainMenuBar = new javax.swing.JMenuBar(  );
    
    // The menus attached to the menu bar
    private JMenu menuFile = new JMenu(  );
    private JMenu menuEdit = new JMenu(  );
    private JMenu menuTools = new JMenu(  );
    
    // A Hashtable holding all of the default menu items, keyed by title
    protected static Hashtable menuItemsHashtable = new Hashtable(  );
    
    /*
     * The items to be installed into the menus.
     * Each item consists of an identification string and
     * a corresponding virtual key.
     *
     * For a "real" application, the default item titles
     * and virtual keys would be loaded from resource bundles,
     * and ideally the user would be able to configure their
     * own toolbar and menu structure.
     *
     * For this demonstration, however, this is adequate.
     */
    private Object[][] fileItems =
    {
        {"New", new Integer(KeyEvent.VK_N)},
        {"Open", new Integer(KeyEvent.VK_O)},
        {"Close", new Integer(KeyEvent.VK_W)},
        {null, null},
        {"Save", new Integer(KeyEvent.VK_S)},
        {"Revert to Saved", null},
        {null, null},
        {"Print...", new Integer(KeyEvent.VK_P)},
        {"Print Setup...", null}
    };
    private Object[][] editItems =
    {
        {"Undo", new Integer(KeyEvent.VK_Z)},
        {"Redo", new Integer(KeyEvent.VK_Y)},
        {null, null},
        {"Cut", new Integer(KeyEvent.VK_X)},
        {"Copy", new Integer(KeyEvent.VK_C)},
        {"Paste", new Integer(KeyEvent.VK_V)},
        {"Delete", null},
        {"Select All", new Integer(KeyEvent.VK_A)}
    };
    private Object[][] toolItems =
    {
        {"Timestamp", null}
    };

    private void dispatchEvent(ActionEvent evt, String tag)
    {
        SimpleEdit myFrame = null;
        SimpleEditPlugin myPlugin = null;
        if(evt.getSource(  ) instanceof JComponent)
        {
            myFrame = (SimpleEdit)
                        (((JComponent)evt.getSource(  )).getClientProperty("window"));
            myPlugin =  (SimpleEditPlugin)
                        (((JComponent)evt.getSource(  )).getClientProperty("plugin"));
        }
        
        // If it's a plugin, hand off to the plugin to handle
        if(myPlugin != null)
        {
            myPlugin.doAction(myFrame, evt);
            return;
        }
        
        // Handle minimal required functionality.
        // It could legitimately be argued that even this
        // functionality should be split off into an
        // overarching set of plugin functionality...
        // but this is adequate for now, and reinforces
        // the notion of certain "default" services.
        if(tag.compareTo("New") == 0)
            doNew(  );
        if(tag.compareTo("Close") == 0)
            doClose(myFrame);
        if(tag.compareTo("Timestamp") == 0)
            doTimestamp(myFrame);
    }

    /*
     * Default event processing.
     */
    private void doNew(  )
    {
        (new SimpleEdit()).show(  );
    }
    
    private void doTimestamp(SimpleEdit myFrame)
    {
        if(myFrame != null)
           myFrame.mainTextArea.setText(myFrame.mainTextArea.getText(  ) +
                 System.getProperty("line.separator")  + new java.util.Date(  ) + " : ");
        
             myFrame.mainTextArea.setCaretPosition(
              myFrame.mainTextArea.getText().length(  ));
        myFrame.mainTextArea.requestFocus(  );
    }

    // Used to track the number of open windows, and
    // automatically quit when they are all closed.
    private static int openWindows = 0;
    
    // Overrides the default hide to see how many windows are currently
    // showing. If none are visible, quit the app.
    /** Hides the window. If no windows are visible, terminates quietly. */
    public void hide(  )
    {
        super.hide(  );
        openWindows--;
        if(openWindows == 0)
            System.exit(0);
    }
    
    public void show(  )
    {
        super.show(  );
        openWindows++;
        // All ready to go, go ahead and get ready for input.
        this.appendDocumentText("");
    }
    
    private void doClose(SimpleEdit myFrame)
    {
        myFrame.hide(  );
    }

    /* This variable is used to track the default accelerator 
     * key for this platform.
     */
    private int preferredMetaKey =
        Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(  );

    private void setupMenu(JMenu myMenu, Object[][] menuconfig, 
        SimpleEdit thisFrame)
    {
        JMenuItem currentMenuItem;
        for(int i = 0; i < menuconfig.length; i++)
        {
            if(menuconfig[i][0] != null)
            {
                currentMenuItem = new JMenuItem(  );
                currentMenuItem.setLabel((String)menuconfig[i][0]);
                
                if(menuconfig[i][1] != null)
                {
                    int keyCode = ((Integer)menuconfig[i][1]).intValue(  );
                    KeyStroke key = 
                 KeyStroke.getKeyStroke(keyCode, preferredMetaKey);
                    currentMenuItem.setAccelerator(key);
                }
                
                currentMenuItem.setEnabled(false);
                currentMenuItem.setActionCommand((String)menuconfig[i][0]);
                currentMenuItem.putClientProperty("window", thisFrame);
                
                currentMenuItem.addActionListener(actionListenerHandler);
                
                // Put the menu item into the menu hash to add handlers later
                menuItemsHashtable.put((String)menuconfig[i][0], currentMenuItem);
                myMenu.add(currentMenuItem);
            } else
            {
                javax.swing.JSeparator sep = new javax.swing.JSeparator(  );
                myMenu.add(sep);
            }
        }
    }

    // A single default ActionListener that punts to dispatchEvent(  ).
    private ActionListener actionListenerHandler = new ActionListener(  )
    {
        public void actionPerformed(ActionEvent evt)
        {
            Object src = evt.getSource(  );
            if(src instanceof JMenuItem)
            {
                String input = ((JMenuItem)src).getLabel(  );
                dispatchEvent(evt, input);
            }
            if(src instanceof JButton)
            {
                String input = ((JButton)src).getLabel(  );
                dispatchEvent(evt, input);
            }
        }
    };

    private void initMenuBar(  )
    {
        mainMenuBar = new javax.swing.JMenuBar(  );
        
        menuFile = new JMenu("File");
        setupMenu(menuFile, fileItems, this);
        mainMenuBar.add(menuFile);
        
        menuEdit = new JMenu("Edit");
        setupMenu(menuEdit, editItems, this);
        mainMenuBar.add(menuEdit);
        
        menuTools = new JMenu("Tools");
        setupMenu(menuTools, toolItems, this);
        mainMenuBar.add(menuTools);
        
        JMenuItem newMenuItem;
        if(plugins != null)
            if(plugins.size(  ) > 0)
            {
                java.util.Enumeration e = plugins.elements(  );
                SimpleEditPlugin currentPlugin;
                while(e.hasMoreElements(  ))
                {
                    currentPlugin = (SimpleEditPlugin)e.nextElement(  );
                    newMenuItem = new JMenuItem(  );
                    newMenuItem.setLabel(currentPlugin.getAction(  ));
                    newMenuItem.setEnabled(true);
                    newMenuItem.setActionCommand(currentPlugin.getAction(  ));
                    newMenuItem.putClientProperty("window", this);
                    newMenuItem.putClientProperty("plugin", currentPlugin);
                    newMenuItem.addActionListener(actionListenerHandler);
                    menuTools.add(newMenuItem);
                }
            }
        
        ((JMenuItem)menuItemsHashtable.get("New")).setEnabled(true);
        ((JMenuItem)menuItemsHashtable.get("Timestamp")).setEnabled(true);
        ((JMenuItem)menuItemsHashtable.get("Close")).setEnabled(true);
        
        setJMenuBar(mainMenuBar);
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 5: Apple Extensions
Apple ships a nonstandard JVM with proprietary extensions. There—I've said it, and the cat is out of the bag. So why isn't Apple lumped into the same category as other vendors that ship proprietary JVMs? Why has Apple not been accused of trying to co-opt Java for sinister purposes? Put simply, Apple's JVM extensions are just that—extensions. They don't change what Java is, but add additional functionality on top of and around a normal Java environment. Apple ships a complete implementation of not just the Java Runtime Environment (JRE), but a full Java Development Kit (JDK). The extensions just include some icing on the standard Java cake.
Apple's Java implementation is fully compliant with any Java 2/JDK 1.3/4-based "pure" Java application. Certain vendors ship incomplete JVM implementations for strategically competitive reasons, in opposition to technologies such as RMI and CORBA. Apple's extensions to Java, however, principally address weaknesses in the Java platform. Careful application development lets you support these extensions while still maintaining excellent cross-platform compatibility. This chapter explores these extensions and shows how they can add to standard Java programs (like the editor from the last chapter).
When comparing Apple's extensions to the efforts of other vendors (Microsoft in particular), keep the following in mind:
  • Apple's extensions are (to the developer) just classes that allow you to more easily access Mac OS X functionality.
  • The extensions do not add to the language model itself (e.g., additional keywords).
  • The full Java 2 Standard Edition stack is included.
  • Apple's JVM is compliant with all relevant Java specifications.
Virtually every desktop client platform provides a standard that specifies application icons, associates documents with the application, and notifies the application of user requests generated by the desktop shell, be it the Finder, Explorer, or some X Windows-based system. However, the standard Java environment lacks mechanisms and APIs to deal with many of these conventions. Instead, you are given a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Mac OS X Finder
Virtually every desktop client platform provides a standard that specifies application icons, associates documents with the application, and notifies the application of user requests generated by the desktop shell, be it the Finder, Explorer, or some X Windows-based system. However, the standard Java environment lacks mechanisms and APIs to deal with many of these conventions. Instead, you are given a main( ) method and the assumption that users can figure out what to do on their own. The result is a Java program that looks like Java, instead of a seamlessly integrated part of the user's desktop experience.
This section examines Apple's extensions that provide APIs for this desktop shell integration and describes how to support them while maintaining cross-platform compatibility.
Apple provides new interfaces for these interactions under JDK 1.4.1 using a different set of packages (com.apple.eio and com.apple.eawt). However, the existing interfaces, described below, work under both JDK 1.3.1 and JDK 1.4.1. In addition, users are required to download JDK 1.4.1 separately, as it is not available for redistribution. Therefore, this section will focus on the JDK 1.3.1 extension mechanisms.
When a Java application runs on Mac OS X, the system creates the default application menu shown in Figure 5-1. The default application name is the fully qualified class name of the launching main( ) class (which is really only acceptable during development, if at all).
Figure 5-1: Default application menu
Each menu item needs to be accessible and integrated into the program so that code can respond to user actions. Apple provides hooks for integrating with these menu items via callbacks (or handlers). To implement these callbacks for the SimpleEdit application developed in Chapter 4, you will create a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Native Access
The preferred mechanism for accessing native functionality on Mac OS X is the standard Java Native Interface (JNI). This section builds a simple JNI library using Apple's Project Builder tool, found in /Developer/Applications/.
In the past, Apple supplied a technology known as JDirect, a set of bindings between native code and Java that is much simpler than JNI. Specifically, JDirect allows access to native libraries without the cumbersome header generation of JNI. Apple has deprecated JDirect, however, and strongly encourages the use of JNI. In fact, the latest versions of the JDK (1.4+) remove JDirect altogether.
To begin, launch Project Builder and select "File New Project." Select the "Java Java JNI Application" option, as shown in Figure 5-3. On the next panel, name your project and give it a location (here, we'll name it "JNIExample"). Then save it in ~/JNIExample/.
Figure 5-3: JNI's new project
The assistant will generate several files for you automatically, as shown in Figure 5-4. Before looking at the files, however, consider the build process and the targets, as shown in Figure 5-5. When building applications with JNI, you should usually first write Java application code, and then flag methods that will have a native implementation using the native keyword:
            native boolean loginAsRoot(String username, String password);
Figure 5-4: JNI files
Figure 5-5: JNI targets
This Java source file is then read by the javah tool (a standard JDK command-line tool), and an appropriate C header file is generated. You then write a native implementation in C, build a library appropriate for the target platform, and ship both the original Java source file and the native library.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 6: Cross-Platform Programming
In the last chapter, you learned how to provide users with Mac OS X features "on the fly," depending on what platform an application runs on. While that chapter focused on menuing options, you can apply the same techniques to any of the other Mac OS X features (such as QuickTime or Spelling, both discussed in future chapters). However, you need to consider a lot of other issues when writing a cross-platform application. While some techniques require code to determine what platform is used, others are simply good programming practices that make any application run better, on any platform.
This chapter, then, is a mixed bag of suggestions and tricks. It's organized by problem area: each section deals with one particular aspect of cross-platform programming. Generally, these are areas of concern where things can go wrong if you aren't careful. Occasionally, you may see some general programming tips mixed in with them, as good development practices often take care of many of these issues implicitly.
Chapter 4 covered this topic in detail, but it's worth reviewing. Generically, for maximum cross-platform compatibility, stick with the Metal look and feel, and perform sanity checking to ensure that the user interface operates correctly on both platforms. For the best performance (and user experience) on Mac OS X, however, be sure to let users run the application with the Aqua look and feel. Doing so involves using appropriate fonts and spacing so that Metal and Aqua interfaces look good on every platform.
While Apple's Aqua GUI is excellent and the implementation allows first-class application appearances, the same cannot be said for the standard Windows look and feel. Determining whether you want to support one or more native look and feel targets is largely a matter of budget and resources (mostly consumed by the testing personnel). Whatever you decide, though, you need to test your GUI applications on every platform they will run on. This might mean buying some extra hardware (or better yet, salvaging those old 486 and Pentium II machines), installing Windows and Linux, and actually seeing what your application looks like on each platform. Despite the best advice from this book, things can go wrong when running an application on a platform it wasn't designed for or developed on. Your own eyes are always the best verification.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
GUI Construction
Chapter 4 covered this topic in detail, but it's worth reviewing. Generically, for maximum cross-platform compatibility, stick with the Metal look and feel, and perform sanity checking to ensure that the user interface operates correctly on both platforms. For the best performance (and user experience) on Mac OS X, however, be sure to let users run the application with the Aqua look and feel. Doing so involves using appropriate fonts and spacing so that Metal and Aqua interfaces look good on every platform.
While Apple's Aqua GUI is excellent and the implementation allows first-class application appearances, the same cannot be said for the standard Windows look and feel. Determining whether you want to support one or more native look and feel targets is largely a matter of budget and resources (mostly consumed by the testing personnel). Whatever you decide, though, you need to test your GUI applications on every platform they will run on. This might mean buying some extra hardware (or better yet, salvaging those old 486 and Pentium II machines), installing Windows and Linux, and actually seeing what your application looks like on each platform. Despite the best advice from this book, things can go wrong when running an application on a platform it wasn't designed for or developed on. Your own eyes are always the best verification.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
New Line
In the old days, developers built applications for terminal and simple daisy-wheel feed printers. They had agreed on the ASCII standard for 7-bit text encoding, with the eighth bit reserved for system specific uses (such as character-based graphics). These developers neglected, however, to specify the precise encoding for generating a new line. Some systems used a carriage return (CR) to return the printer head to the start of a new line, and then a line feed (LF) to tell the printer to roll up the paper a line.
However, many developers decided that using two characters for a line feed was wasteful and redundant. This led to the use of either a CR or LF code (but not both) to indicate the end of a line. For these developers, the single character was sufficient to tell the printer or terminal character generator that a new line should be generated. Of course, fragmentation occurred and applications didn't always use the same line feed character, or didn't correctly interpret documents and applications that used a different character than they were programmed to interpret.
Since then, we've moved to a world of WYSIWYG and GUI, where users typically associate the return key with a new paragraph break, not a new line. Today, the Windows environment is standardized on the CR/LF value (the original double-character line feed), the Classic Mac OS is standardized on the CR value, and the Unix world on LF. As you can see, this is the worst possible scenario—three major platforms with three different line feed standards. Therefore, a Java developer doesn't know which of these bits actually renders the proper logical result. Since Java is intended to be a multiplatform language, this situation can be quite a problem.
Fortunately, Java developers have a standard mechanism th