Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2004-431
$eventId
ID of event: wwdc2004
$eventContentId
ID of session without event part: 431
$eventShortId
Shortened ID of event: wwdc04
$year
Year of session: 2004
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC04 • Session 431

Ten Steps to Building a Great Java Application on Mac OS X

Application • 1:04:28

This session will discuss important considerations when bringing an existing Java application to Mac OS X. We cover user interface, file I/O, drag and drop, image performance, reflection techniques, and Mac OS X user experience enhancements that can be made to your Java application, without sacrificing portability or simplicity.

Speaker: Matt Drance

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Okay, good afternoon. Thanks for waiting. I think the last talk got off a little early. Everybody ready to build some great Java applications? That's not what I was hoping for, but okay. Let's see if I can do better at the end. I've been on stage every day, so in case you don't already know me, my name is Matt Drance, and I work in developer tech support for Java.

And I'm going to talk to you about making the most of the features that are provided to you both by the Java 2 Standard Edition and provided by us on Mac OS X. We've got tips today for developers of all shapes and sizes and backgrounds, people who are new to the Mac. How many people are Java developers new to Mac OS X? Very few. That's great. That's really exciting. How many people are Mac developers that are new to Java? Wow, that's really interesting.

Great. Well, welcome to everybody. So we've got tips for all kinds of people, new to the Mac, new to Java, and people who are experienced in both. And I guess that's the majority of our crowd, since I didn't see a lot of hands in the first two questions.

So what are we going to learn? We're going to learn about making a standard Java application a real Mac OS X application, something that really doesn't feel like it was written in Java and your users would never know the difference. We're going to do step-by-step analysis of a real Java app. I was working on demos for this app over the last couple of weeks, and I was doing--it's 10 steps, so I was writing 10 tiny little demos, proof of concepts of each of these things.

And, you know, I thought that I could show these things, and you guys would say, "Yeah, well, great, Matt. You wrote 10 lines, and you showed us how this works in theory, but I have a real application." So what I did was I searched around on SourceForge for something that would be a good nominee--nominee. And I found this app called Pooka.

I don't know where the name came from. Don't ask me. It's an open-source email client, so it checks email, does IMAP and POP. It's built with Swing using Ant, and it also uses Java--it uses Java Mail and Java Help. It does, you know, all the things you would expect a mail client to do.

And it supports multiple look and feels. It has multiple GUI formats. And it's got a really nice, dynamic, customizable application. So I'm going to show you what it's like. You can change just about everything--labels, menu shortcuts, which actually will make one of our steps very easy later on. But there's the URL, sourceforge.net. I would encourage you to check it out after this talk just to see how far we'll have come at the end of this--at the end of the talk.

So we're going to do start to finish. So what's the start? The start is downloading an application to the desktop. And so what's the use case here? A user either installs your application using an installer. In the case of a lot of Java applications, you download them off the web or off an FTP site.

And usually it's just a JAR file or a number of JAR files. And from a Mac perspective, JAR files really aren't applications. I mean, they're files that you can double-click and launch, and that's great. But from a Mac user's perspective, somebody who doesn't know what Java is or Cocoa or Carbon, they just want an app.

A JAR file is going to be very foreign to them. It's got a very generic icon, as I'm sure you've all seen. It's usually got an unfriendly file name. We'll show you what Puka looked like when we downloaded it in a second. And usually, or often, there's more than one JAR file, and that becomes a real problem.

Which one do I double-click? What do I do? What's the user supposed to do? Now, of course, they could use the terminal. This is a Unix operating system. But, you know, we like to think that we've done a lot of work so that our end users don't ever have to know that it's Unix.

So asking your user to download something, go to the terminal and type some strings, really not the optimal thing. You could run a shell script. It's a little easier. Depending on how you format it, you can name it .command, and they could double-click that. Still gonna bring up a terminal window.

You know, it might freak your users out. Somebody like my grandmother would probably call me if I emailed her an application, told her to double-click something, and then she saw something with some really foreign-looking text on it. And, of course, there is the finder. If you've done the right thing with your JAR, you should be able to double-click it and launch the app. And if you haven't done the right thing with your JAR, you might try to double-click it, and nothing happens.

So what's step one? Step one is package your application for Mac OS X. The easiest thing to do-- you can do this no matter what platform you're actually building on-- is just write a manifest file with a main class attribute. This is a screenshot from Xcode of... will be presenting the manifest file that was already in PUCA. So they actually did step one, or half of step one already. So I applaud them for that. This is a double-clickable jar in Mac OS X, and it comes up without any problems.

But what we'd really like you to do, of course, is use our tool, Jar Bundler, which takes all of your jars and any of your other files and creates a real Mac OS X executable that's double clickable on the desktop. It gives you a friendly application name that shows up in the dock and up in the menu bar. You can set Mac and Java properties.

You can set an icon to make it look even more friendly. And we talked about this in a couple talks, specifically the deployment talk yesterday. But you can also ask for a Java version. And when you do that, using the JVM version key, we'd like you to use the star and plus modifiers instead of explicitly saying something like 131 or 141. And particularly if you have JNI libraries and you're adding JNI libraries to a package, those will automatically get detected by the application. You don't have to worry about setting the java.library.path variable like you might have to do on other platforms.

So, how many of you have used Jarbundler in the past? Okay, well, about half. Okay, so then this demo won't be totally pointless. Can we go to demo one, please? Alright. So, step one. So here's Puka, and it's one file, which is great. Downloaded from SourceForge, it's got a jar icon, it's got I'm guessing this is a version number and I'm guessing this is when it was built. I have no idea. Let's try to double click it and see what happens.

The app will come up eventually. And when it does, we see we have our dock icon, which is a generic Java icon. And it's got some interesting name, which we all know is just the main class, which is what the Java launcher takes by default. And well, it looks like we have some work to do. Let's not configure an email account right now. Let's see what we can do about this. Let's launch Jarbundler.

And let's just drag this jar right in there. And it's going to add it to the class path for me. Set a couple of very simple properties. The bundle name is how this will show up in the desktop and in the doc, so we'll call it...

[Transcript missing]

So now we have this icon here, and what can we do about that? And this is actually such a trivial step that I really want to show people how to do it.

I pulled this icon out of the jar file. This is the logo for Pucca. And so what I'm going to do is launch Icon Composer and just drag.

[Transcript missing]

Call it Pooka icon. Go out of here. I'm going to drag this guy right in, and there's our icon. So I think we're done. So let's create our app, call it Pooka.

And there's our app. And already we have a dock icon with a proper name, proper name up here, and obviously we still have some work to do. But there's nine steps to go. So, can we go back to the slides, please? So now I want to talk to you a little bit about GUI themes and the concept of look and feels and colors and standards, shapes and sizes. They talked a little bit about this yesterday, Barry and Tom. They did a great job with it. So I'm going to run through this as quickly as I can.

So the message is that Java is cross-platform. Write once, run everywhere. But there's also a caveat to that. It's cross-platform. You never know which platform this application's going to be run on. You don't know what the standard sizes are for buttons and scroll panes and all other kinds of widgets.

Closed boxes, background colors, font sizes are probably going to be different, not only across platforms, but across look and feels. And if you make assumptions about these things, if you're only building and testing on one platform, you could bring it over to another platform, building on Windows, coming over to the Mac, and you can be in a lot of trouble.

So this brings us to step two, which is make no assumptions. Don't force the look and feel. Trust the defaults. Use layout managers. Try not to calculate explicit sizes for your controls. Try to get your colors, your icons, your fonts, your borders from the system. Ask the UI manager. Ask the toolkit.

Things like the text field background color, the margin for the editor panes, close icons for your internal frames if you're using a JDesktop pane. All those things can be gotten from the look and feel, and you'll always get the right one instead of assuming you know where you're running.

And the other thing, which was a point from yesterday, is if you want or don't want anti-aliasing, Make sure you say you do or don't. Don't ever assume that it's going to be on or off from one place to another. Now I'd like to go back to demo one and just show you what I mean.

So this is the double clickable we just built with Jarb on there. I'm so used to the Macified version, I can't remember where the preferences are. Let's go to the Themes Options, which are right here. And we have all our look and feels here. And this is kind of a nice feature of Puca. It lets you select one. So let's go select Aqua.

And it's in Aqua. No, it's not. Well, it kind of is. I mean, I have an Aqua control here, and this button seems to have changed, and I see my Aqua scroll bars, but it looks half and half. So, Let's see what went on here. And unfortunately, I had to get the source out. It is SourceForge, so that's not that big a deal. And what we can go to, this is the main class of the application.

[Transcript missing]

Is that okay? Yeah. All right.

So, once I scroll and adjust for the new size. And I smartly marked all these steps. So here we are. We are setting the background color of the frame to gray. And this is basically, well, I'm running on metal, right? So let me just be extra careful and make sure everything shows up gray in case the look and feel for some reason forgets to paint them.

And, uh, As we can see, when we switch to something that doesn't have a default gray background, that's not how it-- it doesn't really turn out so great. So in the interest of time, I'm not going to build these things in front of your eyes. But we basically comment out the gray thing, and with that one line change, now we're truly in Aqua.

Looks as you might expect it to. You probably can guess, looking at this window, what the next step is going to be. But I will leave you in suspense for a couple more seconds. Can we go back to the slides, please? The next step, of course, would be the menu bar.

So what's the problem with the application menu bar? Mac OS X, as you know, has a menu bar at the top of the screen. We have an application menu that gets popped up for every app that you launch. But JMenuBars and Swing appear in the window. They don't appear at the top of the frame.

The application menu that we try to conveniently create for you isn't completely responsive, because Java has no concept of it. The Quit Menu item will just quit. It's not intercepted by your Java code automatically, because how can it be? Preferences item doesn't appear by default. And the About Menu item isn't integrated into whatever informational thing you might have had on other platforms.

So this brings us to step three, making a Macintosh menu bar. Most of you probably know this. You set the apple.laf.usescreenmenubar property. It's a runtime property. And that immediately jumps your JMenu bars up to the top of the screen with no additional work. This is just something you can specify either in the properties item of your Info.plist. You can do it as a -d property at runtime from the terminal, or you can do a system set property with it before you initialize AWT.

And the other item would be the application menu. And unfortunately, you can't do this in a completely universal way. We did have to write some APIs that you guys can use. And that gives us to the extended AWT, the EAWT package. And the main item here is the com.apple.eawt application listener. And there's an adapter class that you can extend, as opposed to implementing the interface. And this will allow you to handle all the items in the application menu.

About preferences quit gives you a couple of other methods that we probably won't go through today, because we've got a lot to cover. And once you implement those things, you can replace the things in your file or edit menus that you had on other platforms. You know, a lot of people have preferences under edit.

They have options or preferences under help, or they have their about stuff under help. So you can detect whether or not you're on a Mac. And if you know you're doing that, use the application listener. If you're on another platform, go ahead and put that menu where it was before.

And we have a great way of doing this with the OS X adapter sample code, which basically uses reflection to load a class that implements all these interfaces. So basically, you have that code, it's built, and it runs. And you only load it when you're on Mac OS X. And because you're loading it reflectively, the class load never occurs on Windows or Linux. So you don't have to worry about forking your code and building a Mac version of this.

So, I was talking about detecting a Mac, and how do we do that? Well, this is really the most recommended way by Apple to do it. Go ahead and get the OS.name property, send it to lower case just to be thorough, and then see if it starts with Mac OS X. It's pretty intuitive. Barry and Tom talked about this yesterday. I just wanted to go over it one more time. And this is covered in Tech Note 2110.

So if we can go back to demo one, I want to show you real quick what a Macintosh menu bar is supposed to look like. So as you can see, we do have the application menu, and it does have the proper name now, because we set that bundle name item in Jar Bundler when we built it.

When I click about here, I get this default about thing that Apple pulls up if you haven't implemented the EWT interfaces. Notice there's no preferences here, and it gets really ugly when I do something like this. I start typing an email, and then I go to quit. And it's gone.

Now, Pooka probably had some kind of "Are you sure you want to save?" stuff when you go to file exit or when you go to close the window manually. But you need to intercept that quit item. And so how do we do that? Well, let's go to step three.

And basically, I just inserted some quick code here. I have a Mac integration class, so I localized all my Mac code in one place. And I basically told it to do an application menu registration call. It's a static method, very simple. Let's see what it does. How many of you have seen the OS X adapter sample code in the past? Okay, all right, so we need to go over this.

So here we have my Mac integration class. And when I call the menu registration, there's a lot of verbiage here. But basically what I'm doing is I'm reflectively loading another class that I'll show you in a second, my application adapter class.

[Transcript missing]

So what this basically does is it creates an Apple EAWT application. And you notice that we're no longer using reflection here. This class is completely contained.

It's now calling the Apple APIs directly. And the reason it can do that is because we're not even loading this class unless we're on the Mac. So now we can be safe. We're creating an application, adding the application listener. This is the standard EAWT code. And then we have all our implementations down here. Handle Quit, Handle About, Handle Preferences. Let's see how we did.

Now you can see the menu bars at the top for starters. We create a new message here. And now when I do Apple Q, sorry, Command Q, I do get the standard PUCA close code. I didn't write this dialog. This was part of the standard app, but now we've hooked into it properly with a very minimal amount of effort. Can we go back to slides, please? So now I want to talk about user interaction. And a bunch of these things I'm not going to demo because Puka actually does them very well. But I want to talk about them anyway.

So a lot of GUI apps are often written and tested only on a single platform. I deal with this all the time in developer support. Things like hard-coded menu modifiers, people explicitly saying "control." I'm writing a menu, I'm on Windows, all the apps seem to use control, so I'll do control-C for copy. I'm on Windows, I have a two-button mouse, so to get a contextual menu, I'll check for the button-to mask on a mouse click or a mouse press, and it'll magically work, right? Wrong, unfortunately.

Much in the vein of the GUI theme step earlier, there are things that you can ask Java for, and Java will just tell you what the right thing is for the right platform. And among those things are the menu modifiers and the context menu trigger, what action is supposed to trigger a contextual menu. It's very abstract. You don't have to do any event parsing or worry about what platform you're on. It'll just do the right thing for you, no matter where you're running.

And, you know, the idea here is hard code as little as possible. Use Java to its fullest extent. And this is the menu example. There's a toolkit, get menu shortcut key mask method that gives you control on Windows and meta, which is the equivalent of command, on the Mac.

And with that, it will give you the right thing wherever you go, and this is all the code you need to do to create a JMenu item. I've seen people do halfway, and, you know, I appreciate their effort, where they do if on Windows, use control, if on Mac, use meta. It's way too much work. This is all you have to do, I promise. Amen.

And context menus are the same thing. There's a simple method called "IsPopupTrigger" on a mouse event, and that's the only thing you need to ask. Don't worry about button two, button three. Don't worry about whether or not the control key has been held down if it's a one-button Mac mouse. Just ask Java, "Is this a popup menu?" Say yes, then I go ahead and proceed as I can.

Now, there is one caveat here, and that's there is something of a difference between Windows and Mac specifically. On the Mac, a context menu is activated on the pressing of the mouse. So if you do a control click, it's gonna--as soon as you press the mouse button down, the menu will--the popup trigger will occur. It's not gonna be on the release like on Windows. And the other thing is that you never know what kind of mouse is running on a Mac. We ship a one-button mouse.

You can easily buy a two, three, five-button mouse. So somebody could have a right mouse, or they could have configured their mouse for it to be, you know, the thumb mouse or something like that. What's the mask for that? I don't even know. So ask the toolkit. The caveat of the mouse-pressed issue, though, is that right-dragging as well as control-dragging is not gonna work on a Mac. So if you have Java code that does a right drag, that's simply not gonna work because as soon as that right mouse button is pressed, Java is gonna think it's time for a context menu.

And that's something to be aware of. And Puka, actually, as you can see, has done a good job of this itself. So I don't need to do the context menu demo. Let's move right along to documents. A lot of Java applications have documents that they read and write to.

The trick is, how do I open these from outside Java code? Meaning, it would be great if I could double click this document, drag it over my app, whatever, and have my application launch and open it. Because Java is not an integral part of the system, at least other systems, This is kind of an issue.

The finder should really know how to do this. It knows for Cocoa, it knows for Carbon. How do we do it for Java? Well, this is also part of the EWT, and it's step five, which is register your documents. And this is really remarkably easy. If you've already done the work to do the OS X adapter style stuff, loading up the EWT implementation reflectively and safely, all you have to do is implement one other method, which is handle open file.

And then you need to go into your Info.plist, either in Jarbundler or in the Finder or in Xcode, if you're building with Xcode, and just set one very simple key, which is CFBundleDocumentTypes. It has some sub-items, which we have documented, but this is the same thing you would do for Cocoa and Carbon. There's no Java special stuff.

So just look up the standard Apple documentation, do this, implement this one method, and basically all you want to do, this is going to pass you a Java.io file, and you would just hand that file to whatever logic you already have for opening your documents. And as a little aside, this is supported in Java Web Start for 1.5, so if you're doing Web Start deployment, that you'll get without even needing to do any of this work up top.

This is a screenshot of the Info.plist keys. Obviously, you can't read them, but it's basically a blow-up of the CFBundleDocumentTypes property. It has things like the icon that you might want to specify for the document, file name extensions that you want to own, MIME types if you have them, so on and so forth. We'll have a link to that documentation later. Okay, so this is the big winner for me. I get this a lot. This isn't Windows.

When we're dealing with files, I download a lot of applications now and then, and I start it up and nothing happens. I go into console and sure enough, Java could not find the file C:\ something. And this is so easy to avoid. So really, don't ever assume--I've said assume nothing before, but that also applies to the file system, to what--you know, what system you are on. Things like drive letters, backslashes, or even colons, because this isn't Mac OS 9 either. Those can be potentially fatal. If you have some critical file that you need to load and you've got a specific path, you may be doomed.

And this brings us to step six. There are Java methods for this, too. There are constants for things like the rights file separator on the right platform. There is a path separator if there's some kind of path that you need to construct. There's a user dir and user home system properties just using system get property.

And those will give you the user home directory or the current running directory of the application. So if you want to do things like do relative--

[Transcript missing]

And if you have things on the class path, whether they be in your jars or just on somewhere else in the class path that you've set, whether they be images or something else, you can just call your class loader and ask for the system resource.

and you'll get a URL to that file, and it's all ready. You don't even need to worry about path names. And you can also use the macros that we make available to you in the Info.plist to do some other path access. I'm going to talk about that right now. This is a picture of the Info.plist.

Oh, I'm sorry, this is a picture of Jarbutler. And I know you can't read these, so I have the text on the right. There are two big macros that we have. One of them is app package. The other one is java root, and app package points to the absolute path to your double-clickable application.

And the java root is the contents, resources, java subdirectory of that package. Now, these macros are for use by the java launcher, meaning you'll never see them at runtime in your java code. But what you can do is, because you already have a plist and you have properties, as you can see up here in Jarbutler, you can set a java system property and set the value to app package or java root or some extension thereof. And the launcher stub up. will build these and set them as system properties, so at runtime you can do get system property for whatever I set, and you will have that path available to you.

So we talked about documents already. I know it sounds a little confusing. But now we're talking about other people's documents, other people's applications. How do I launch a website? What's the default browser on the system? Do I really want to call the system configuration framework? Probably not. How do I know if there's a browser installed? How do I know which one I can launch? If I have another application's document, especially with this, it's an email program, it's going to get attachments. How do I open that attachment? How do I find it, launch it? How do I feed the doc to the application? So this is the other one that we really haven't talked about at the conference before. It gets a lot of play on the mailing list.

The runtime exec method can be your friend. A lot of people are afraid of it, but particularly the open command because it's hooked directly into launch services. And you can test this out from the terminal. You just type open and some file name, and it will go to whatever the currently registered application is for that document. And it will go ahead and open it up.

You feed it a URL, it will launch Safari. Or you can override that behavior by using dash A. If you know specifically that there's an application installed somewhere, you can tell it, open this document with this app. And we recommend that when you're calling runtime exec, you use a separated string array with your spaces.

Because if you use a single space separated string, that could work. But if you have a single argument with spaces in it, that's going to fall apart on you. But the real key here is I'm telling you this, but I don't want you to overuse Runtime Exec. Use the Java APIs where you can.

Just like the previous steps, there are ways of doing this. If you want to get a list of files, don't do a runtime exec on LS. Just ask the--get a file object for the directory that you're in. Ask it to list the files. You can do a make--you can do a makeder with the file class. You can rename a file with the file class.

And this is just a code example, it looks pretty legible, of how we would do this. Constructing a string array here. Obviously the first item is open, because that's the command we want to send to the terminal. And then look, I'm using user.home. So this is going to open an MS Word doc called mydoc in the user's home directory.

And basically, Runtime Exec returns a process object. So you can get that process object, and if you need to do I/O with it or just take care of any error reporting, anything like that, you can do that. You can do that through the process object. And I would like to show you a demo of that right now.

Just to make sure I'm on schedule here. I think so. So here we are with our application menu bar, and we have our correct menu shortcuts with the command key as we'd expect to see them. So we're making good progress. But then I was playing around with Pooka some more, and there's this address book item. So let me open up the address book. And it brings me to this address book editor. Obviously, it's Pooka's address book. And I go over the addresses, and they're empty.

I really don't want to enter all my friends' addresses again. So what can we possibly do about this? Well, this is where the beauty of Runtime Exec comes in. I know I have an address book application here on the system, and it would be a shame if I had to do cutting and pasting, anything like that. Where are we? Step seven? I think. OK. And that brings us here.

I wrote some really simple methods in my Mac integration class to open the address book, the real address book. And again, I know where address book is installed. Everything system standard is installed in /applications. So we have a path here. And here's the same runtime exec code, or roughly the same code, that I showed you in the slide. And I'm calling this over here.

This is the code that responds to the menu item up in the menu bar. This is from the PUGA code. This is nothing that I wrote. And basically all I did was insert something right here to open the address book. So let's see how that works. And again, the app shouldn't look any different, because all we're doing is changing the menu item.

If I go ahead and I click the address book menu item, and-- oh, right. So that's what I'm used to seeing. Now I don't have to enter all this stuff again. If I need to edit addresses, I can do it the way I'm used to doing. This was six lines of code. Shouldn't be any problem at all. So let's go back to the slides, please.

Here's the next item, help. And I'm not asking for help. I'm just talking about help. There are plenty of concepts of user assistance, and Java provides those as well. Obviously, the most popular one is Java Help. And Java Help's fine. It works on the Mac just fine. You saw a screenshot of it up there.

The trick is that it's not a part of the standard edition. And part of our idea behind distributing the whole JDK as part of our system is that you don't have to worry about redistribution. And then you go into these additional features, and I have to do Java Help, I have to go find some third-party tool to generate a Java Help help set, and that's not the kind of work we want you to do.

And furthermore, after you do all that work, you have this Java Help window come up, and it really doesn't look that Mac-like. There's nothing wrong with it, but it's just not familiar. Mac users in particular are used to using the system help viewer. And now some people might say, whoa, hold on, I'm not rewriting my entire help.

But the Java help sets are already written in HTML, which is the same thing that the help viewer uses. So we have a very portable situation here. So that brings us to step eight: use the help viewer. You can create a help book to support your application very, very easily. You can use the existing HTML from your Java help set. Java help will still work if you did things correctly. You shouldn't have to interfere with anything you've already done.

And the great thing about integration with the Mac OS X help viewer is that after you load up your help, Mac OS X Help will now search your application's help content, even when the app isn't running. If you're using Java Help, you have to launch the app and then go to Help. And then only within the context of your application can your user get assistance. And I'll show you why that may be a good thing later.

So here's what you have to do to actually use the help viewer from your application. Here's another InfoP-List screenshot. There's two big InfoP-List keys. One of them is CFBundle Help Book Folder. The other one is CFBundle Help Book Name. And that's it. Basically, you obviously put the name of your folder. And the help book name is something a little more specific, and I'll go into that in a second.

And I will show you a demo now. Can we go to demo one? So let me first show you what I'm talking about. This is our Step 7 application. So I go up here to the Help menu. And here's our Java help. Now, it's not that bad. It's in Aqua. Everything's cool.

And here's our Java help. Now, it's not that bad. It's in Aqua. Everything's cool. And here's our Java help. Now, it's not that bad. It's in Aqua. Everything's cool. So here's our Step 8 project. And what I've written here is-- yes, don't gasp. It's JNI code. It's a very simple native void here with a show help method, and I'll explain what the bad show help is when we go back to the slides.

And here's our JNI function. We go to Show Help, and all we have to do is get the standard NS application, because again, we're running on top of Cocoa. And there's a very simple method called Show Help. And once we do that, the Cocoa system goes in, looks at the Info.plist, finds out what the helpbook folder is, finds out the helpbook name, and that's how it knows to load up the help folder and what the front page is. And the only remaining step-- was for me to call this new method from the help action, which was already written in PUCA. Now there's something missing here, and that's the help bundle itself.

So what you're looking at right here is the Java Help help set from Pooka. I took it out of the jar. This is the folder that it always was. It's got the same content, everything else. I did a couple simple things. You can see this Mac Help index item. This is a very simple front page that I wrote. It's got a frame set. I wrote another simple page because I wanted this to be a frame set thing with all the table of contents in the left looking just like the Java help did.

And the real thing here is this meta tag that I added to the front. This is the Apple title meta tag. This is what Help Viewer looks for when it goes to load up a help set. So this needs to match the content here. It needs to match what you put in your CFBundle help book name property.

And that's really all I did. I didn't change any of the other content. The other thing I did was I dropped this guy on top of the Apple Help indexer, which is part of the develop-- which is under Developer/Applications in your developer tools. And what that does is it makes it searchable. I added a few keywords, meta tags in a few places, and I created a search index for Apple Help. So let's see where that gets us.

brings up our Step 8 Pucca app. And now when I go to Help, we're launching Help Viewer. And it's going to go ahead and parse the help content. This is inside the .app package. This is the same content that was in the Java help. And if I did my HTML right, which I can't guarantee, these links should work. Well, that's great. OK. I mean, really.

This doesn't seem like a big win for a Java developer, especially somebody that's new to the platform. So great, I did this work, and I have the same help I had before. But where this really gets interesting is you'll notice I'm not running in PUCA anymore. Let's go to the Finder.

I'll go to Mac Help. And this is just the standard Macintosh help. Where is the finder? And I get a bunch of hits here. But what if I searched all hell and asked, how do I encrypt email messages? And now, if I sort this by relevance, you see that puka appears at the top because of the keywords I entered in the meta tag. And the key is that puka is not running anymore.

And so your user may use your application all the time, and if it's not running, and they're running in the fire, and they want to know how to do something, they can search the Macintosh help, and your application will pop up and maybe show them something that they didn't even know how to do before.

If you're using Java help, they have to be using your application to learn about it. If you're using the Macintosh help, they don't have to be. And that's the big win there. And again, this was five minutes of HTML work and about 30 seconds of info P list work. And obviously, I'm going to make this, that help code available to you so you don't have to do anything but call a Java method.

Can we go back to the slides, please? So this is where the bad show help method comes in. Once we start working with JNI, there are some things to be careful about. Whether you're calling to or from native code, there are threading issues. The AWT and the AppKit, the Cocoa, GUI, operate on separate threads. And they expect their events to appear in certain places.

If you're calling from Java into AppKit, you need to be careful because, typically, if you're coming from Java, you're coming from a Java thread. You may even be coming from the AWT thread. And Cocoa has a mechanism for this. It's called performSelectorOnMainThread. And that basically sends an asynchronous message to the event queue so that when you're doing some AppKit operations, you don't have to worry about a deadlock.

And likewise, if you're calling from Cocoa into Java, if you're going to do things like update the UI, you want to make sure you get on the event queue, especially in the case of Swing, which is single-threaded. And you can do that using event queue invoke later or Swing utilities invoke later, which is just a wrapper around that same call. You want to let Java return as soon as possible. And that's what this does.

It will drop you on a new thread-- on a new runnable, rather, and put yourself on the AWT thread so you can return. I mean, I think you can probably see the situation here where I have a Java event. And in response to that Java event, I make a call to AppKit. And then AppKit makes a call back into Java to do something like a repaint. And we're done.

So, and that's where the Badge Show Help came in. I'm not going to demo that, but what the Badge Show Help did was it made the help request off of the main AppKit thread. And that's really not, you know, you might say, what's the big deal? All we did was launch a separate application. And that's true.

It's not that big a deal in this case. But what happens is if there's no help book folder specified, or it can't find the help book folder, a dialog comes up, and that dialog comes up from Cocoa. So if we did this directly on the AWT thread, and an AppKit dialog came up, we would have been in big trouble.

And again, it's my job to make sure you guys don't have to worry about this. But I need to say it. It was mentioned earlier in Ted's talk in Xcode for Java Development, because he was talking about some JNI things too. And this is very, very important. If any of you are going to do any JNI stuff, you really need to be aware of this.

So with that, I'd like to talk some more about the really cool stuff. What can we really do? So far we've been talking about compatibility and cute little bells and whistles, but what can we do to really, really make this app cool? And, you know, it's a sad fact, but Java can't do everything.

It just can't. It's cross-platform. It doesn't know which platform it's running on, which is, you know, a good thing for you if you want to deploy as many places as you can. But it doesn't know its host that deeply. We've already seen that with the screen menu bar, with the application menu, and to an extent with the help viewer.

There are plenty of system APIs that you've learned about all week. There are plenty of components. And you may think that they're unavailable to you. Or are they? And so this is step nine. We have another piece of the extended AWT called Cocoa Component. And what Cocoa Component lets you do is, it's a Java AWT component, but what it lets you do is it creates a hook that allows you to write a JNI library to extend any NSView and implant that in a Java hierarchy.

So you can do anything. You can get a WebView, NSOpenGLView, an AB People Picker View, even the QT Movie View from the new QT kit that they've been talking about this week. You can drop any of those into a Java application. You can bring these features right to Java. And I'm going to show you how to do that right now. Can we go to demo one, please? I should have done that earlier.

OK, so let's run step eight again. And we got halfway there with step seven, when we launched the address book to do our editing. And now what I want to do is I want to write an email. So here's this little address icon that Puka has right here. And great, I'm back to that address book that I didn't want to enter all my email addresses in yet again. So what can we do about this? Well, we can do a little bit of work.

See if we can get a Cocoa component in here. And of course, we already did, but I'll show you how. So here's my Java People Picker view. It extends the Cocoa component. And it's actually pretty simple. Obviously we load a library. It uses JNI. There are a couple of things you have to implement that I'll leave to you after the talk. We have a pretty extensive Java documentation on this. You need to have something that creates the NSView.

[Transcript missing]

And we have the JNI callbacks shown right here. Let me open this up for you a little better.

I know it's scary, it's C code. I bet you didn't think you were going to see that in this talk, huh? So this is our standard Cocoa initialization code. We create a new People Picker view, and of course this extends the AB People Picker view from Address Book. And I think you can see where we're going with this. Let's just go ahead and run it before I explain anything else.

create another new message, click my button. And this time, we have the real address book. And this is really what I wanted to see. I didn't want to enter this stuff all again. I didn't want to look around and figure out who I wanted to email it, copy it, paste it into the other thing. And once we have this in the app, and we got the whole Java team in here, we can email whoever we want. Email Scott, email Caroline.

And so what's happening here is I'm double clicking into this Cocoa view, and I'm intercepting the event, taking the address book information, sending it back to Java. Because remember, this is a Java frame. So I'm taking these strings and putting them in the to field. I admit it's slightly complicated. But let me show you how I'm doing that. Here's our people picker view. And as soon as I remember how I did it.

So Cocoa is pretty dynamic. And with this view, this is specifically an address book method, which is the set name double action, as in I have a double click on this view. What do I do with it? And I told it to do the mail to selection method. And this is it right here. And basically, you can see a lot of AB methods here. This is address book API. I really don't want to teach you the address book. That's not the point here. What I'm doing is I'm getting the person that was just double clicked.

And then down here, we call back to the Java object that instantiated us. Now, how did we do that? How did we know where our Java peer is? Well, I do that up here. First thing we need to do is have the Java VM on hand at all times.

And there's a handy JNI function called JNI onload. And basically, as soon as you call system load library, This method gets called, this function gets called. And then you can just have the JVM sitting there and just grab the JVM on load and then you have it available to you at any point.

And what we do then is when the view is initialized, And here's our create NSView method. And whenever you get a JNI callback from Java, there's two possibilities. One of them is you have, if it's an object method, you get a reference to the J object that was calling you. If it's a static method, you get a reference to the J class.

So basically, I just Basically, we just cast that object and make sure we have a hold of it. Here's our init. We pass the J object. And so how you hold onto this is you create something called the global ref. And this is something you do in JNI to hold onto a Java object in native code.

And there are some caveats to that, namely the fact that this is a real Java reference. So if you know out all your references in your Java code, you still have this one. So garbage collection may not occur. Now there's also an equivalent to this, which is a weak global reference, which works like a weak reference. And that's probably the way you want to go. But in the interest of thread safety and all those things, I wanted to create a global ref here.

And that's really where we are. We have the cache VM. We go ahead and we get the JNI environment from it. We get the class from our Java peer. We go find a method called new email to that I wrote in Java. And we pass the string that we got from address book back to our Java object. And that's exactly what's happening in the demo. We double click the address book view. It gets an AB person. I ask for the email address. I send that back to Java. Java gets the string and puts it in the to field.

All right. I think I've beaten that to death. So can we go back to the slides? So what's the last thing? The last thing, of course, would be the dock. It's a really nice, innovative thing in Mac OS X, and It really doesn't work with Java, or maybe it does. You do get a lot of things for free, as you've seen already. You get the name in the dock, you get your dock icon if you set it, and a new feature in Java 1.4.2 Update 1 is window tracking.

Any of your Java frames appear in the dock menu, and you can go ahead and select those. There's no work from you, and I think that's a nice little addition. That's all you get, though. You're not able to daz the dock or change the icon. You can't bounce the dock, and you can't set any custom menu items, either.

Or can you? So here's step 10. Step 10 would be to really use the doc. And I've written a sample code called javadoc, not to be confused with the javadoc without the K. And I'll be releasing that after the show once I get rid of the lewd comments in it while I was cramming for this show. And basically it opens up the doc to Java developers.

Anything you want to do, you can do from Java code now. You can change the icon at runtime, you can add items to the menu, and you can alert the user if you're not in the front. I'd like to show you a demo of that. So this is our step 9 application, still open.

And so here's our dock menu. Can everybody see that? You don't need to read it, but you can see it. This item up here is that free window tracking menu that I mentioned earlier. Any Java frame that comes up is going to be selectable in the dock. But wouldn't it be great if I could do more than that? And so what I'm going to do first is, I've got some of the Java team sitting in the front row here, trying to figure out who I want to send an email to. I'll send an email to my fiance. Just give her a little free advertisement for her company while I'm at this conference here.

And, you know, the real convenience here is that Kyanne works for a wireless company called Good Technology, and she basically carries an email device everywhere with her. And we have a minor problem here, which is that I haven't logged into my email account. Let's take care of that. Okay. Let's try that one more time. Okay. We'll do it again.

And as you can see, I've been using either the file menu or this button at the top of the frame to create a new message. That's really not the best way to go. Let's see if we can come up with something better. Let me go ahead and run Step 10.

Let me log into my email one more time. And here we go. So, looks like the same app, obviously. The difference now is, of course, I have some doc menu items here. And, you know, I only created two. I didn't want to get too creative, and I didn't want to make the doc menu take up everything else. But, so, I click New Message.

And I get a new message. Basically what I did here was--I'll show you in a minute, but this is Java code that is calling, you know, a utility function to create a doc menu item. I passed it an action and a string, so it knew what the label should be and it knew how to respond when it was clicked.

And there's our response. And let's see if-- the other cool thing about the doc is if something special happens when it's not in the front, You get some kind of notification. You do the bounces, or maybe it badges if you're on mail. And so this is as penance for what went wrong on Monday night. I did something at the last minute.

I went to the library yesterday to see if I could

[Transcript missing]

As you can see, I've been testing this pretty extensively. Let's create a new message. The other thing I need to be careful of. Let's edit that. And this will be another neat demo. Notice I just changed that address.

So in a sense, we'll try to check the email one more time, see what happens. And if nothing seems to happen, I'll go ahead and just-- Maybe she's in a meeting. But I swear her technology works very well. All right, so we go ahead and send an email, and let's see if this one shows up. Ah, look, she did respond. And the dock is bouncing. Not only is it bouncing, but it's changed.

So what happened here? What happened was, because Kyanne is in my address book, we got a new email from her. We got the email address from the message, because we're in Java. Went back into address book, did a search on her address, returned a person from address book, got the picture, and put it in the doc, and then bounced it.

So basically, I could probably check mail again. Let's see what happens, because my message should be in there somewhere. Oh, there's my email. Look at that neck. That's an old picture. See, it's funny, you know, before I started working at Apple, I had time to do things like lift weights, and it was kind of cool. So let's look at how I did this.

So here's our Doc Utilities class, and this is in a separate project that I didn't have the time to build in here. This is not a COCA component, this is regular JNI code, and there's two things here. There's an action from doc menu item, and that's going to be our callback, that's going to be what C sends us.

And then we have all these native functions. So this is the one to bounce the doc if we need to, this is the one to set the image back. And I don't know if you noticed, but when I clicked on the doc icon, the standard one came back.

And that was part of the EAWT Reopen Application event. So when I notice that we've been focused back to the front, we go ahead and get the standard icon, put it back, because we're no longer alerting the user, and we want to go back to normal. So this will bounce us, and here's our API to add the doc menu item. You can do it two ways. You can do a J menu item, or you can do it with an action, which is really the more politically correct way of doing it in swing.

And now we take our dive into native code. And hopefully, if I played my cards right, when I release this, you guys won't ever have to look at this code. The idea is for you guys to be able to do this from Java. But I'm going to show it to you anyway.

So here's our addDocMenuItem function. We had a bunch of stuff. This is the class that called us because it's a static function. This is the object which could either be an action or a JMenuItem. and this is the name that we pass. So these are our two arguments from the Java world. We basically get these guys and this is a simple call to our Cocoa DocuTools is my Objective-C class. Let's go see what that does.

[Transcript missing]

And there's a delegate method called application doc menu, which is basically every time I do this, every time I click on the doc icon, the application calls its delegate and asks for a menu. So we respond by giving it the menu that we've created. What happens now when we-- and you can see the problem here.

So it's not really a Java menu, it's a Cocoa menu. So how do we get back to Java when this is clicked? How do we know? And as soon as I remember which one it is, there are a lot of functions here. Ah, yes, action from doc menu item. That seems pretty standard.

We basically get our callback. I set this as the action responder on the doc menu. And we cast our peer when we initialize the object, again, using the same technique as we did in the AB-- the AB people picker example. We do the same thing, get the JNI end, find the class, and find our action from doc menu item, the Java action from doc menu item option.

And we pass back the peer, the originator, because Java's going to need to-- you have more than one menu item, and you need to know which one got clicked. So we go back to the dock utilities. Here we go. This is what C is going to call to us. It's going to pass us some kind of object, and like I said earlier, it could be either an action or a JMenuItem.

So if it's a JMenuItem, we just get that and do a do click on it. And the reason this is, you know, like I said, the real politically correct way to do it is with an action. But the idea here is the human interface guidelines say that you should only do, you should only put things in the dock that are already in the menu bar.

So if you already have something, you already have a J menu there, just go ahead and pass it to this API, and we'll take that and we'll do it as if the user had clicked it in the menu bar, which is supposed to be how it operates. Of course, if you have an action, that would be the same idea. And, you know, as a result, we respond to it. We either go to that new message action that we had earlier or the check mail action.

And I believe that's all I got. Can we go back to the slides? Where have we gone in the last hour? We started with this anonymous, random application off of source boards that had an obscure file name, didn't look anything like it belonged on the Macintosh, and we ended up with this.

We have an application with an application icon, with a proper name, with a dock icon, a dock menu. It has integration with the address book, has integration with other system applications, and it has integration with the system help. This is a Macintosh application. This isn't just a Java app that runs on the Mac.

So we did this in an hour. It didn't really take me an hour. But I downloaded this program randomly. And I wanted to do that to demonstrate to you that this is not precooked stuff. I never saw the source code to this app before. And if I was able to do this, you guys, who know the code to your own app, should be able to do it without a problem.

And it's still cross-platform. Because of those reflective techniques, even the JNI stuff, if we don't load those classes that call the JNI code, they'll never load the native libraries. So you can dump all these files in your standard distribution and never worry about link errors or anything like that. There's no need to be afraid of the fact that we're using native code here.

So, if nothing else, make an application bundle, or at least a manifest file. Make your application double clickable, as Mac-like as possible. Give yourself a nice desktop icon. It usually takes just a couple seconds. Watch for your portability pitfalls. I didn't beat those to death because I've beat them to death over the last two years, and Tom and Barry talked about them to a pretty good length yesterday.

Use your Info.plist and use the EWT API to do quite a bit already. Get your application menu stuff. You can set your properties, get the menu bar at the top, do things like banish the grow box, so on and so forth. Use the Apple Help. That's probably the easiest of the steps that I've showed you. All I had to do was change a little bit of HTML code and drag the folder over the Apple Help indexer, which isn't even necessary. That was just to make the search more robust. I hate that word.

And, you know, Runtime Exec, Jay and I, you can use them. You don't need to be afraid. You just need to be careful. And once you know the rules, it's just like anything else. All of you have been new to some kind of programming technique in the past. You just need to know what you're doing. And that's why I'm here, to tell you.

If you want to learn some more, obviously we have a disk image, and I'm sorry, but I don't have the finished Pooka product on the disk image, and that's because I was cramming it well after the disk image was due. The reference library, which has a great document called Java 1.4 Development for Mac OS X, talks a lot about the portability pitfalls, the EAWT stuff that I was talking about earlier. We have a Java doc reference for the Apple extensions for the EAWT.

There's also an EIO class, extended I/O class, that will let you do some Mac-specific file interaction, but that's really for legacy development, people who are used to using MRJ on Mac OS 9. So unless you're one of those people, that's probably something you can skip over. And I'd really like you to read the human interface guidelines also, if you're really interested in making your Java apps as Mac-like as possible.

There's a lot of stuff that speaks within the context of, you know, assuming you're programming with C or Objective-C, but there are a lot of high-level design aesthetic things that can easily apply to your Java application. And a particularly useful thing is that the human interface guidelines have a table with all of our reserved system keyboard shortcuts.

And that can be really useful, where if you're using a Java application, or if you're worrying about your keyboard shortcuts on the Mac, and, you know, you say, "I did F12 on my app, and it works on Windows, and when I do it on the Mac, some kind of crazy stuff drops down and makes a splash on the screen." So if you read the human interface guidelines, you would not have to worry about that.

[Transcript missing]

and providing user assistance for Apple Help. This is both design guidelines for building a help book, but it also talks about setting those info P list keys in case you didn't write them down fast enough earlier, as well as what you need to do. There's going to be some stuff talking about calling Carbon functions and stuff like that, but again, you won't need to worry about that because we're going to get you the code to do it from Java.