Chapter 21
Applets versus Applications

by Joseph Weber

Although Java became famous for its ability to create Applets, it is also an equally powerful language to develop full fledged Applications. In fact, the ability to use Java to create Applications may be the more powerful attribute. Applications written in Java do not suffer from the numerous pitfalls that traditional programming paradigms present.

Applications Explored

It's almost ironic that the most overlooked portion of Java is the ability to create applications. When programming in other languages, such as C, C++, or any other traditional language, what you always create are standard programs. Oddly enough the hype surrounding applets has created an environment where most people interested in Java completely overlook the possibility of using Java to create applications in addition to applets.

The difference between applications and applets is at once very subtle and at the same time very profound. In fact, as you will learn later in the chapter, applications can at the same time be applets and vice versa. The most fundamental difference between applets and applications is their operating environment. Applets must "live" within a browser such as Netscape Navigator, Microsoft Internet Explorer, or Appletviewer. Applications can be run directly from the command prompt with the use of the Java interpreter (if you're using the JDK, that would be java.exe).


NOTE: In the future you will be able to run java applications directly from the your operating system without having to invoke the Java interpreter. Microsoft, IBM, and Apple have all signed a letter of intent to embed the Java virtual machine into upcoming versions of their operating systems. In addition, Sun has a project currently code-named Kona which will be an entirely Java-based OS. When the VM becomes part of the OS, Java Applications will become even more crucial.


Advantages of Applications

There are a number of advantages of the application model over the Applet. For one thing applications can be faster. This is caused by a couple of things. First, an application does not have the overhead of the browser to deal with. In addition, when run as an applet, the browser generally has control of the amount of memory an applet may utilize. As an application you have complete control over the entire environment the program is running in. These items combine to result in slightly faster execution of Java applications, which are free of some of the burdens of their applet counterparts.


NOTE: Recent changes and upgrades to the JITs included in most browsers may result in faster execution in the browser than using java.exe. To realize the performance boost from Applications you must also use a JIT, such as SuperCede's sc10java.exe.


The Sandbox

The more substantial difference between applications and applets is the lack of what is known as a sandbox. The sandbox restricts the operation of an applet. For instance, under ordinary circumstances an applet is forbidden from trying to write or read from your local file system, and the applet can not open an URL to any host on the Internet that it pleases, only to the host from which the HTML and Class files came. In contrast, an application is under no such restrictions. When a Java program is run as an application it has all the rights and abilities that any program written in, say C++, would have.

This means that applications are able to run what are known as trusted methods. You can find a number of these methods in the java.lang.RunTime class. However, they also include all native methods, and a host of others.

See Chapter 41, "Extending the Reach of Java--The Java Native Interface," to learn more about creating native methods and classes.

So, assuming that you don't care about the minor performance boost, and you don't need access to elements outside of the sandbox, why not just bundle Appletviewer with your applet? Applications have four additional advantages:

Developing Java Applications

When you learned about writing Java applets in Chapter 14, one of the first things you learned is that any applet must extend the java.applet.Applet class. Unlike applets, applications do not need to extend any other class in order to be usable. In fact, the reason that applets need to extend the java.applet.Applet class is so that an application (known as a browser) can use the class through polymorphism.

See "Extending Objects Through Inheritance." Chapter 5

Any Java class can be run as an application. There is really only one restriction to this: In order to run a class as an application it must have a main method with the following prototype:

public static void main (String args[])

So an application can be thought of as just a normal class that has one unique feature: a static public main method. In Java the main() method has the same purpose as the main() function in C and C++--it's where the application starts.

HelloWorldthe Application

As you have done in previous chapters, and will continue to do through out this book, take a look at the infamous "Hello World" program as it would be written as a Java application as shown in Listing 21.1, which follows.

Listing 21.1The Simplest Application Is HelloWorld


public class Hello{
    public static void main(String args[]){
         System.out.println("Hello World!");
    }
}

You can compile the Hello class just as you have the others in this book. From a command prompt type:

javac Hello.java

Alternatively, on a Macintosh drag the Hello.java file over the javac icon.


NOTE: As with any standard public class, Hello must be defined within a file that carries its name .java, so in this case Hello must be in a file called Hello.java.


To invoke a Java application you will use the syntax [java ClassName]. Note that you use the ClassName only, not the ClassName.class or the ClassName.java. java will search the existing classpath (which includes your current directory [.]) to try to locate the class that you've indicated. So, to run your Hello application from the command prompt, type the following:
java Hello

What you should see is the message "Hello World" appear on the screen. Note that you did not type Hello.class, only Hello. The java virtual machine implicitly knows that the Hello class is located within the file Hello.class, and that it should start off right away with the main() method.


NOTE: On the Macintosh things work a bit differently, as you have already learned when you learned to compile Java programs. In the case of running a Java application, double click the java icon and enter the class name you wish to run. Alternatively, you can drag the class file for the application over the Java icon. Also, for users of Windows, in order to get a command prompt you need to start the program MS-DOS Prompt.


Passing Parameters to an Application

As you saw with the "HelloWorld" application, applications, unlike applets, do not rely on an HTML file to be loaded. This can be a benefit, since it decreases the complexity of the system, but how then do you pass parameters into the application?

In C/C++ you will typically utilize the values in the arrays of argv and argc. The argv/argc system tends to be one that is a bit obtuse, and many programmers look up how to utilize the variables each time they need them. In Java the parameter set is much simpler.

You will recall from laying out the prototype for the main() method, that main has a parameter--an array of Strings. This array of strings contains the values of the additional parameters left on the command line. For instance, if you are a DOS user, you're probably familiar with the /? option. For instance:

dir /?

The /? is an additional parameter to the dir program. Now, take a look at how to do this with the HelloWorld program. Instead of having the program say hello to the whole world, change it so that it only says hello to you. Listing 21.2 shows just how to do this.

Listing 21.2HelloWorld Using a Command Line Parameter


public class Hello{
    public static void main(String args[]){
         System.out.println("Hello "+args[0]+"!");
    }
}

To compile the program, type:

javac Hello.java

But to run this version of Hello is slightly different since you need to use the additional parameter:

java Hello Weber

Now, what you should see is:

Hello Weber!

Preventing Null Pointer Exceptions

If you accidentally did not type the additional parameter at the end of the command line, what you saw was:

java.lang.ArrayIndexOutOfBoundsException:
    at Hello.main(Hello.java):3

To prevent this message, if a user happens to forget to add their name at the end of the line, you need to put in some error checking. Probably the best way to do this is to add in an if statement. Make one more change to the HelloWorld program, as shown in Listing 21.3.

Listing 21.3HelloWorld with a Parameter and Some Error Checking


public class Hello{
    public static void main(String args[]){
         if (args.length <1){
              System.out.println("Syntax for running Hello is:");
              System.out.println("        java  Hello  <Name>");
              System.out.println("\n\nWhere <Name> is the person to greet");
         } else
              System.out.println("Hello "+args[0]+"!");
    }

}

Now, if you happen to run the HelloWorld program without an parameters, what you will see should look like this:

Syntax for running Hello is:
       java  Hello  <Name>
Where <Name> is the person to greet

Limitations Imposed Due to the Static Nature of main()

The main method of a class has very similar characteristics of the main() function in C or C++. However, unlike C and C++, since the main method must be static it can not utilize nonstatic methods or variables of its class directly. For instance the following code would not compile.

public class fooBar {
    int foo;
    public static void main(String args[]){
         foo = 50;
    }

}

The problem of course is that the foo variable is not static, and the compiler will refuse to allow the static method main() to modify it. To understand why this occurs, review what it means for any method or variable to be static. When a static method is loaded into memory, the virtual machine looks at it and essentially says: "Ok, well there is only going to be one of these, regardless of how many instances of the class the user creates, so I'm going to assign it to a special place in memory. I might as well do that now." This happens not when the class is first instantiated, but as soon as the class is loaded. Later, when the fooBar class is actually instantiated, if the main method was allowed to access the foo variable what would happen?

When the fooBar class is instantiated the machine allocates space for the foo variable, and then calls the main method. But wait, the main method was already placed into memory with a reference to the foo variable. . . but which foo variable? Of course this is assuming that you had actually been able to compile this class, so there is no real answer, but you can see why the compiler won't let you perform this type of activity.

You can solve this problem in one of two ways. First, you can declare the foo variable to be static as shown in Listing 21.4.

Listing 21.4fooBar Written so that foo Is Static


public class fooBar {
    static int foo;
    
    public init(){
         System.out.println("Init method");
    }

    public static void main(String args[]){
         foo = 50;
    }

}

The fooBar class will now compile, but what about calling methods, such as the init method in the above example? Since the init() method is not itself static, the compiler would again refuse to compile the fooBar class. Of course you could declare the init method to be static as well, but this can quickly become quite cumbersome and it would be difficult, if not impossible, to actually perform many of the useful tasks you want to do as a programmer. Instead, it's probably a good idea to have the fooBar's main method instantiate another copy of fooBar as shown in the next example (Listing 21.5).

Listing 21.5fooBar Creates an Instance of Itself in the main Method


public class fooBar {
    int foo;

    public init(){
         System.out.println("Init method");
    }

    public static void main(String args[]){
         fooBar f = new fooBar();
         f.foo = 50;
         f.init();
    }

}

Now, since the f variable is actually created within the main method, you can perform operations on the f instance. The major difference here is that you are performing operations not on the this variables, but on the f. this variables, and this distinction helps the compiler understand how to deal with such methods.

Converting an Applet to an Application

Now that you have briefly looked at how to create an application, consider another very important aspect of Application programming--converting an applet to an application. You see, there is really no reason why a program you have already written as an applet can't also be run as an application. In this next section you will walk step-by-step through converting the clock applet (shown in Figure 21.1) into an application.

FIG. 21.1
The Clock Applet running in Netscape. As an applet, it cannot be run from the command prompt without a browser.

Why Convert an Applet to an Application?

So why convert an applet to anapplication? Well for one thing, believe it or not, not every one in the world has realized that the Internet exists. Some people and companies do, but do not yet have access to the Internet, and many companies have access to the Internet but do not allow their users to surf the World Wide Web. More important though, many people do not have access to the World Wide Web 100 percent of the time. For those people who don't have access to the World Wide Web all the time, applications on the Web aren't as useful.

As a result, before long you probably will want to present your applets to people and companies that are not yet familiar with the Net, or you may want to present your applets to people without forcing them to be connected to the Internet. One perfect example of this is when you want to deliver your applets on a CD-ROM. With Java, there is no reason why the application you deliver on the CD should be any different from what you display out on the Net. Imagine being able to develop a single application that will run on every platform and that can work over the World Wide Web, Enterprise Network, and CD-ROM, all without changing a single line of code or performing a single recompilation.

Changing the Applet Code to an Application

For this chapter you will change the simple clock applet into an application. The source code for the applet is shown here in Listing 21.6.

Listing 21.6A Simple Application Which Displays a Clock


/*
*
* Clock
*
*/
import java.applet.Applet;
import java.awt.*;
import java.util.*;

public class Clock extends Applet implements Runnable{
    Thread thisThread;

    Color faceColor,borderColor,minuteColor,hourColor,secondColor;

    public void init(){
         //read in the colors for each of the hands and for the face/border
         faceColor = readColor (getParameter("faceCol"));
         borderColor = readColor (getParameter("borderCol"));
         minuteColor = readColor (getParameter("minuteCol"));
         hourColor = readColor(getParameter("hourCol"));
         secondColor = readColor(getParameter("secondCol"));
    }

    // This method creates a color based on a  string.
    // The string is assumed to be "red,green,blue"  where each
    // of the colors is represented by its integer equivalant.
    public Color readColor(String aColor) {
     if (aColor == null) { 
           return Color.black; 
      }
     
     int r;
     int g;
     int b;
     
      //break the string apart into each number
      StringTokenizer st = new StringTokenizer(aColor, ",");
     
     try {
         r = Integer.valueOf(st.nextToken()).intValue();
         g = Integer.valueOf(st.nextToken()).intValue();
         b = Integer.valueOf(st.nextToken()).intValue();          
         return new Color(r,g,b);
     }
     catch (Exception e) {
         System.out.println("An exception occured trying to conver a parameter           to a color:"+e);
         return Color.black;
     }
   }
 

    public void start(){
         thisThread = new Thread(this);
         thisThread.start();
    }

    public void run(){
         while(true){
              repaint();
              try{
                   thisThread.sleep(1000);
              }catch (Exception e){}
         }
    }

    public void update(Graphics g){
         paint(g);
    }

    public void paint(Graphics g){
         //fill clock face
         g.setColor(faceColor);
         g.fillOval(0,0,100,100);
         g.setColor(borderColor);
         g.drawOval(0,0,100,100);
              
         //get the current time
         Date d = new Date();
         //draw the minute hand
         g.setColor(minuteColor);     
         double angle = (((double)(90 - d.getMinutes()))/60)*2 * Math.PI;     
         g.drawLine(50,50,50+(int)(Math.sin(angle)*50),50 +           (int)(Math.cos(angle)*50));
         //draw the hour hand
         g.setColor(hourColor);
         angle = ((((double)18 - d.getHours()+(double)d.getMinutes()/60))/12)*2           * Math.PI;
         g.drawLine(50,50,50+(int)(Math.sin(angle)*40),50 +           (int)(Math.cos(angle)*40));
         //draw the second hand
         g.setColor(secondColor);
         angle = (((double)(90 - d.getSeconds()))/60)*2 * Math.PI;          
         g.drawLine(50,50,50+(int)(Math.sin(angle)*50),50 +           (int)(Math.cos(angle)*50));
    }
}

The first task is to add a main() method to the Clock class in order to make it into an application. To do so, open Clock.java in your favorite text editor. Page all the way down until you reach the closing brace (}). Directly before that brace, add the code shown in Listing 21.7.

Listing 21.7New main Method for Clock.java


static boolean inApplet =true;
public static void main(String args[]){
         /*set a boolean flag to show if you are in an applet or not */
         inApplet=false;

         /*Create a Frame to place our application in. */
         /*You can change the string value to show your desired label*/
         /*for the frame */
         Frame myFrame = new Frame ("Clock as an Application");

         /*Create a clock instance. */
         Clock myApp = new Clock();         /*Add the current application to the Frame */
         myFrame.add ("Center",myApp);

         /*Resize the Frame to the desired size, and make it visible */
         myFrame.resize(100,130);
         myFrame.show();

         /*Run the methods the browser normally would */
         myApp.init();
         myApp.start();

}

Here is a breakdown of this code fragment line-by-line:

inApplet=false;

The first statement in this code creates a status variable so you can tell if the program is being run as an applet or as an application. As you will learn later, you often must do a few things differently when you have an applet that is not actually running in a browser, such as AppletViewer or Netscape. As a result, a Boolean variable (inApplet) has been added to the class. Technically, for good programming structure, the declaration for this variable should be placed at the top with the rest of your variables, but it's easier to see it here. Notice that the variable is declared to be static. If you miss this keyword, the compiler growls at you about referencing a nonstatic variable in a static method. main() must be static and public for you to run the method as an application.

Frame myFrame = new Frame ("Clock as an Application");

Next, you create a frame in which to put your new clock. The parameter "Clock as an Application" is placed in the title bar of the Frame. Indicating that the program is being run as an application is good practice; this indication helps eliminate confusion on the part of the user. If you don't want to set the title in the Constructor for some reason, you can create an untitled frame and change the title later, using setTitle(String), if you prefer.

Clock myApp = new Clock();

The next line indicates that you want to create a new instance of the class Clock. A perfectly legitimate question at this point is, why not use this? After all, this is an instantiation of the class Clock already, right? The primary reason to create a new instance of Clock is to avoid rewriting any of the applet methods to make them static. Just as it is not legitimate to change the variable inApplet if it is nonstatic, it is not legitimate to try to access a nonstatic method. It is however legitimate to access the nonstatic methods of a variable. Bearing that in mind, create a new instance variable of the class Clock called myApp and add it to the frame.

myFrame.add ("Center",myApp);

The next line adds the new Clock variable to the Frame. This is important because before you attach the Clock to something, it can't be displayed.

See "Layout Managers" Chapter 30

Next, you add the lines myFrame.resize(100,130) and myFrame.show() to the Clock. java file. myFrame.resize(100,130) tells the application to make the frame's size 100x100, but you also need to account for a 30 pixel titlebar that the Frame has vertically. Normally, when you convert an applet to an application, you know the ideal size for your applet. When in doubt, go ahead and copy the WIDTH and HEIGHT values from your most commonly used HTML file. On those rare occasions when you want the size to be adjustable, use the techniques covered later in this chapter when you learn how to account for parameter data, to read in the size from the command line.

myFrame.resize(100,100);

myFrame.show();


CAUTION:
Technically, when the applet has been added to the frame you could go through the normal applet methods init() and start() right there. Contrary to popular belief, however, this procedure is not a good idea. If your applet uses double buffering, or requires any other image that is built with the createImage(x,y) method, the procedure will not work until the frame has been shown(). The drawback is that you will see a flicker as the frame comes up with nothing in it. Keep this fact in the back of your mind, even if you're not using createImage(x,y) now, because this minor fact is not documented anywhere and has caused this author hours of headaches because it's easy to forget.


Finally, you add the lines myApp.init() and myApp.start() to your function. Because your application is not running in the browser, the init() and start() methods are not called automatically, as they would be if the program was running as an applet. As a result, you must simulate the effect by calling the methods explicitly. It should be pointed out that if your application does not appear, you may want to add the line myApp.repaint() to the end of the main() method.
myApp.init();

myApp.start();

Before you save your new copy of Clock.java, you need to make one more change. Go to the top of the file in which you are performing your imports, and make sure that you are importing java.awt.Frame. Then go ahead and save the file.

import java.awt.Frame

Accounting for Other Applet Peculiarities

The most difficult problem to deal with when you convert applets to applications has to do with duplicating the effect of a parameter tag and other applet-specific tasks. You can handle this situation in many ways; the following sections discuss two of the most common solutions.

Defaulting
The first solution is defaulting. In defaulting, the idea is to provide the application with all the information that it would be getting anyway from the HTML file. In a sense, this solution is exactly what you did when you told the frame what size you wanted to use with resize(x,y). To do this for the <param> items requires rewriting the getParameter method. Clock has several parameters it receives in <param> tags. Take a look at the number of <param> tags from the Clocks HTML file in Listing 21.8.

Listing 21.8Clock.html


<TITLE>Clock</TITLE>
<H1>Clock </H1>
<hr>
<applet code="Clock.class" width=100 height=100>
<param name=hourCol value=255,00,00 >
<param name=minuteCol value=00,255,00>
<param name=secondCol value=00,00,255>
<param name=borderCol value=255,255,255>
<param name=faceCol value=125,125,125>
</applet>

<hr>

To mimic these effects in your new application, add the method shown in Listing 21.9 to your current Clock.java file.

Listing 21.9getParameter() Method for Clock.java


public String getParameter (String name){
    String ST;
    if (inApplet)
         return super.getParameter(name);
    //If you are not in an applet you default all of the values.
    if (name == "hourCol")
         return "255,00,00" ;
    if (name == "minuteCol")
         return "00,255,00";
    if (name == "secondCol")
         return "00,00,255";
    if (name == "borderCol")
         return "255,255,255";
    if (name == "faceCol")
         return "125,125,125"; 
    return null;

}


CAUTION:
If you are going to have several parameters, you should use a switch statement. A switch however requires an integer, which you can get by using the hashCode() of the String. However, since multiple Strings can have the same hashCode() you must then make sure you really have the correct String. This makes the solution much more involved. However, if you are working with several <param> tags, consider using this alternative method.


This method replaces the duties that are normally performed by the java.applet.Applet class with your own default values.

Notice that the first thing you do is check to see whether you are in an applet (if (inApplet)). If so, you use the getParameter(String) method from your super class (java.applet.Applet). Doing this maintains your normal pattern of operation when you go back and use Clock as an applet again. The idea is to have one program that can run as both an application and an applet.


NOTE: A better way to do handle the getParameter() is to implement appletStub, but without a complete explanation of interfaces, explaining how to do this would be purely academic. If you plan to implement several aspects of java.applet.Applet, refer to Chapter 12 for more information.


Recompiling the Application

The next step is recompiling the application. Recompiling an application is no different from compiling an applet. In this case, type the following:

javac Clock.java

Testing the Application

Now you can test your application (see Figure 21.2). To do so, you need to invoke the Java Virtual Machine, followed by the class name, as follows:

java Clock

FIG. 21.2
The Clock running as an application.


TIP: Be sure to maintain proper capitalization at all times.


Second Way to Add <param> Information Defaulting is a quick and easy way to get the extraneous information into an application that you normally leave in an HTML file. Odds are, however, that if you took the time to include a parameter tag in the first place, you don't want the values to be fixed. After all, you could have hard-coded the values to start with, and then you never would have this problem in the first place. How do you get information into your application from the outside world? The easiest answer is to get it from the command line. As you recall, the main method takes an array of Strings as an argument. You can use this array to deliver information to an application at run-time. This section addresses one of the simplest cases: sending the WIDTH and HEIGHT information to the application from the command line. While, the section doesn't also explain how to insert the information for a <param>; hopefully, you will be able to deduce from this example how to do it for <param> tags on your own.

To use the information from the command line, you need to make a few modifications in the main() method. Listing 21.10 shows the new version.

Listing 21.10New main Method


static boolean inApplet =true;
public static void main(String args[]){
    /*set a boolean flag to show if you are in an applet or not */
    inApplet=false;

    /*Create a Frame to place your application in. */
    /*You can change the string value to show your desired label*/
    /*for the frame */
    Frame myFrame = new Frame ("Clock as an Application");

    /*Create a clock instance. */
    Clock myApp = new Clock();    /*Add the current application to the Frame */
    myFrame.add ("Center",myApp);
    
    /*Resize the Frame to the desired size, and make it visible */
    /*Resize the Frame to the desired size, and make it visible */
    if (args.length>=2)
/*resize the Frame based on command line inputs */
          myFrame.resize(Integer.parseInt(args[0]),Integer.parseInt(args[1]));
    else
         myFrame.resize(100,130);
    myFrame.show();

    myFrame.show();

    /*Run the methods the browser normally would */
    myApp.init();
    myApp.start();

}

Make the necessary changes, and recompile the program. Now you can run the Clock at any size you want. Try the following:

java Clock 100 100

At first glance, your new main method is almost identical to the one in Listing 21.3. The main difference is a group of six lines:

/*Resize the Frame to the desired size, and make it visible */
if (argv.length>=2)
/*resize the Frame based on command line inputs */
     myFrame.resize(Integer.parseInt(args[0]),Integer.parseInt(args[1]));
else

myFrame.resize(100,130);

The first line of actual code checks to see whether the user put enough information in the command line. This check prevents null pointer exceptions caused by accessing information that isn't really there. Besides, you probably want the user to be able to run Clock at its normal size without specifying the actual size.

The next line is the one that does most of the work. It should be fairly obvious to you what is happening in this code, but you should know why you need to use Integer.parseInt on the array values. At run-time, the Java machine isn't aware of what is coming in from the command line; it simply sees a string. To convert a String to an int, you need to use the class Integer's parseInt(String) method. (Note, use the Integer class, not int. If you're confused, refer to Chapter 7.)


CAUTION:
To be complete, the parseInt method should be surrounded by a try{} catch{} block, in case something other than an integer is typed in the command line.


Making the Window Close Work

By now, you probably have noticed that to close your new Clock application you have to press Ctrl+C or in some other way cause the operating system to close your application. This section reveals to you the method by which all MagnaStar, Inc. applications and applets close their frames with a normal method.

The answer is to not use the frame from java.awt.Frame. This frame ignores the Event.WINDOW_DESTROY event and doesn't bother to pass it along to its container objects. To get around this situation, use the goodFrame class shown in Listing 21.11.

Listing 21.11goodFrame.java


import java.awt.Frame;

/*
*
* GoodFrame
*
*/
/* -----------------------------------------------------------------
* goodFrame , Copyright  1995 MagnaStar Inc, All Rights Reserved.
* Permission to use, copy, modify, and distribute this software and its
* documentation for NON-COMMERCIAL purposes and without fee is hereby
* granted, provided that this copyright notice and appropriate documentation
* appears in all software that contains this code, or is derived from it.
*
* MagnaStar Inc. MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. MagnaStar Inc. SHALL NOT BE LIABLE
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* You may reach MagnaStar Inc. at info@magnastar.com
*
* For more information see:
* http://www.magnastar.com */

import java.awt.Event;
import java.awt.Component;
/** GoodFrame extends java.awt.Frame and acknowledges standard window
* Close and minimize commands */

public class GoodFrame extends java.awt.Frame{

//Constructors duplicate those from java.awt.Frame
public GoodFrame (){
     super();
}

public GoodFrame(String title){
     super(title);
}

public boolean handleEvent(Event evt){
     //Acknowledge minimize requests
     if (evt.id== Event.WINDOW_ICONIFY){
          hide();
          return false;
     }

     //Acknowledge window close requests
     if (evt.id == Event.WINDOW_DESTROY){
          //Pass the destroy event along to the components.
          //This should be used to stop all Threads
          Component c[] = getComponents();
          for (int x=0;x<c.length;x++)
               c[x].handleEvent(evt);
          //Destroy the current Frame
          dispose();
          return false;
     }
     //Default to the normal handleEvent
     return super.handleEvent(evt);
}

}

To implement this class, copy goodFrame.java to the directory in which the Clock.java class is located. Next, edit Clock.java. Replace all the instances of Frame with goodFrame, and make one more change: rather than import java.awt. Frame at the top of the program, import goodFrame. Now recompile the program. Everything should work exactly as it did before, with one change: if you click the Close icon, the application disappears.

Now you need to make one more change to Clock to get it to quit completely. Add the following method to your Clock applet:

public boolean handleEvent(Event evt){
if (evt.id == Event.WINDOW_DESTROY){
      if(inApplet){
    stop();
    destroy();
    return true;
      } else {
    System.exit(0);
      }
        }
return super.handleEvent(evt);

}

This method causes Clock to run through the standard exit procedures under an applet (if (inApplet)). The real key, however, is that when you run it as an application, Clock calls System.exit(0). Before you do one more recompilation of Clock.java, you will need to add one more line to the top of Clock.java to import the Event class:

import java.awt.Event;

Finally, the complete Clock applet should look like Listing 21.12 below.

Listing 21.12The Final Clock Application with Everything in Place


/*
*
* Clock
*
*/
import java.applet.Applet;
import java.awt.*;
import java.util.*;

public class Clock extends Applet implements Runnable{
    Thread thisThread;

    Color faceColor ,borderColor,minuteColor,hourColor,secondColor;

    public void init(){
         //read in the colors for each of the hands and for the face/border
         faceColor = readColor (getParameter("faceCol"));
         borderColor = readColor (getParameter("borderCol"));
         minuteColor = readColor (getParameter("minuteCol"));
         hourColor = readColor(getParameter("hourCol"));
         secondColor = readColor(getParameter("secondCol"));
    }

    // This method creates a color based on a  string.
    // The string is assumed to be "red,green,blue"  where each
    // of the colors is represented by it's integer equivalant.
    public Color readColor(String aColor) {
     if (aColor == null) { 
           return Color.black; 
      }
     
     int r;
     int g;
     int b;
     
      //break the string apart into each number
      StringTokenizer st = new StringTokenizer(aColor, ",");
     
     try {
         r = Integer.valueOf(st.nextToken()).intValue();
         g = Integer.valueOf(st.nextToken()).intValue();
         b = Integer.valueOf(st.nextToken()).intValue();          
         return new Color(r,g,b);
     }
     catch (Exception e) {
         System.out.println("An exception occured trying to convert a parameter           to a color:"+e);
         return Color.black;
     }
   }
 

    public void start(){
         thisThread = new Thread(this);
         thisThread.start();
    }

    public void run(){
         while(true){
              repaint();
              try{
                   thisThread.sleep(1000);
              }catch (Exception e){}
         }
    }

    public void update(Graphics g){
         paint(g);
    }

    public void paint(Graphics g){
         //fill clock face
         g.setColor(faceColor);
         g.fillOval(0,0,100,100);
         g.setColor(borderColor);
         g.drawOval(0,0,100,100);
              
         //get the current time
         Date d = new Date();
         //draw the minute hand
         g.setColor(minuteColor);     
         double angle = (((double)(90 - d.getMinutes()))/60)*2 * Math.PI;     
         g.drawLine(50,50,50+(int)(Math.sin(angle)*50),50 +           (int)(Math.cos(angle)*50));
         //draw the hour hand
         g.setColor(hourColor);
         angle = ((((double)18 - d.getHours()+(double)d.getMinutes()/60))/12)*2           * Math.PI;
         g.drawLine(50,50,50+(int)(Math.sin(angle)*40),50 +                     (int)(Math.cos(angle)*40));
         //draw the second hand
         g.setColor(secondColor);
         angle = (((double)(90 - d.getSeconds()))/60)*2 * Math.PI;          
         g.drawLine(50,50,50+(int)(Math.sin(angle)*50),50 +                     (int)(Math.cos(angle)*50));
    }static boolean inApplet =true;
public static void main(String args[]){
    /*set a boolean flag to show if you are in an applet or not */
    inApplet=false;

    /*Create a Frame to place your application in. */
    /*You can change the string value to show your desired label*/
    /*for the frame */
    GoodFrame myFrame = new GoodFrame ("Clock as an Application");

    /*Create a clock instance. */
    Clock myApp = new Clock();    /*Add the current application to the Frame */
    myFrame.add ("Center",myApp);
    
    /*Resize the Frame to the desired size, and make it visible */
    /*Resize the Frame to the desired size, and make it visible */
    if (args.length>=2)
    /*resize the Frame based on command line inputs */
          myFrame.resize(Integer.parseInt(args[0]),Integer.parseInt(args[1]));
    else
         myFrame.resize(100,130);
    myFrame.show();

    myFrame.show();

    /*Run the methods the browser normally would */
    myApp.init();
    myApp.start();
}

public String getParameter (String name){
    String ST;
    if (inApplet)
         return super.getParameter(name);
    //If you are not in an applet you default all of the values.
    if (name == "hourCol")
         return "255,00,00" ;
    if (name == "minuteCol")
         return "00,255,00";
    if (name == "secondCol")
         return "00,00,255";
    if (name == "borderCol")
         return "0,0,0";
    if (name == "faceCol")
         return "125,125,125"; 
    return null;
}

public boolean handleEvent(Event evt){
    if (evt.id == Event.WINDOW_DESTROY){
         if(inApplet){
              stop();
              destroy();
              return true;
          } else {
              System.exit(0);
          }
     }
     return super.handleEvent(evt);
}}

Now, recompile and run Clock one last time. If you click the Window Close icon, Clock exits like a normal program.

Checking All the Applet Methods

When you convert your own applets to applications, you need to perform one last step. You need to search for all methods in java.applet.Applet that are valid only with respect to a browser. Most commonly, you need to search for the methods described in the following sections.

getAppletContext()
Fortunately, most of the things you will do with getAppletContext() you can ignore with applications. showDocument(), for example, has no meaning without a browser. Attempting to execute getAppletContext().showDocument() produces an error on System.out, but the application shouldn't crash because of it.

Similarly, showStatus() usually is not relevant with applications. In applets that use the applet context to display information, the easiest thing to do usually is to surround the specific code with an if (inApplet){} block, and ignore it if you're not in an applet.

What do you do if you really have to see that information? You can select the top and bottom 21 lines of the frame and write into the paint method a section that displays the applet-context information there. Why do you select the top and the bottom? Due to a strange quirk between the UNIX version of Frame peer and the Windows 95 version of Frame peer, each system chops out a 21-line area in which it can display its "warning applet" message. On Windows machines, this area is the top 21 lines; on UNIX machines, it is the bottom 21 lines.

If you're not convinced, go to the following URL:

http://www.magnastar.com/ultra_nav

UltraNav is a program by MagnaStar Inc. which aids in the navigation of Web pages. Notice the yellow "information" line. Its location moves based on your platform.

If you are on a Windows machine, you should see an information bar at the top of the frame. If you're on a UNIX machine, that bar is at the bottom. The bar is being drawn at both the top and the bottom; you are just seeing only one.

getCodeBase()
and getDocumentBase()
getCodeBase() and getDocumentBase() are a bit trickier to deal with. Both of these methods return an URL, and you don't want to limit yourself to having the user connected to the Internet. After all, if the user can access your Web site, you probably have him or her downloading the applet directly from you, so you would have no need to turn the applet into an application. You'll usually deal with getCodeBase() and getDocumentBase() on a case-by-case basis. If you can get away without the information, ignore it. If you really need the information from getCodeBase() or getDocumentBase(), you may have to give it a hard-coded URL or one that you read from the command line.

Paying Attention to Your Constructor
Frequently when converting applets, you will find yourself creating a constructor for your class other than the null constructor. Creating a custom constructor is a perfectly desirable thing to do to pass information from the command line or other information. If you do this, however, make sure you add the null constructor back in manually (the null constructor is the constructor that does not take any parameters on input). If you create another constructor, Java doesn't automatically generate a null one for you. You won't even notice that you need one until you are working on a project and another class needs to create an instance of your applet, for a thread or something. When this situation occurs, the class attempts to access the null constructor. Now, even though you didn't actually delete the null constructor from the class, it is no longer there. The error message that you get will look something like this:

java.lang.NoSuchMethodError
      at sun.applet.AppletPanel.run(AppletPanel.java:210)

      at java.lang.Thread(Thread.java)

Notice that nothing in the error message tells you anything about your classes. The error doesn't even look like one that involves your class; it looks like a bug in AppletPanel. If you encounter this situation, the first thing to do is delete *.class and recompile the whole program. Then the compiler will be able to catch the missing Constructor call.

createImage
If you are using createImage, and the Image variable is being returned as null when you covert your applet to an application, make sure you have made the frame visible first. See the caution under "Changing the Applet Code to an Application," earlier in this chapter.

Packaging Your Applications in Zip Format

Now that you have converted your applets to applications, you can send them to your clients. The best way to deliver the applications is in a single zip file.

When you package your own applications in zip format, make sure you don't use compression. Due to the way the Java interpreter uses the zip files, the files can only be stored in the zip file not compressed.


CAUTION:
Be aware that you cannot deliver applets in the zip format. Sun and Netscape may see the wisdom of this in the future but, at least in JDK version 1.0, you cannot package applets in zip format.


Under Windows

Currently, PKZIP, the most popular zip program, does not have support for long file names, so you have to use an alternative zip compression program. When PKZIP does support long file names, however, the command should be:

pkzip -e0 classes.zip *.class

Under UNIX

On UNIX machines, you can use Zip. Zip 2.0.1, from Info-ZIP, is available at a variety of FTP sites. The command for Zip to zip up .class files is:

zip -0 classes.zip *.class


NOTE: When you deliver applications, be sure to include directions for your users on how to get the Java JDK (if you are not including it with your application). In general, it is also a good idea is to include both a batch file and a script file, to allow both UNIX and Windows users to access your applications.