JavaDynamic Loading/Reloading of Classes and Dynamic Method Invocation, Part 1

Dynamic Loading/Reloading of Classes and Dynamic Method Invocation, Part 1

Java Programming Notes # 1494


Preface

In my opinion, one of the most complex topics in all of Java involves
dynamic loading, unloading, and reloading of classes.  Not far behind in
terms of syntactical complexity is the dynamic invocation of methods using
reflection.

In this lesson, I will teach you how to write a program that modifies its fundamental behavior at
runtime by dynamically modifying, compiling, loading, and reloading classes. 
I will also teach you how to use reflection for dynamic method invocation.

A practical example

Whenever possible, I like to demonstrate Java programming concepts by
writing a small but useful program that incorporates those concepts to advantage. 
I thought long and hard before finally coming up with an idea for a small but
useful program that
clearly demonstrates the benefits of dynamic class loading.

I settled on a program that writes, compiles, loads and executes new Java code
on the fly with no requirement to stop and restart the program to
incorporate the new code.  The new code is code that was completely unknown
to me when I wrote the program.  The new code may also be code that is
unknown to the user when she starts the program running.

A plotting program

Stated briefly, the program allows a user to enter chunks
(multiple statements)
of Java code into a text field during runtime. 
(The reason for doing this will be explained in detail
later.)
 
Having entered a new chunk of code, the user clicks a button, which causes the
new code to be compiled, loaded, and executed as the body of a method.  The
results produced by executing the method are then plotted in a Cartesian coordinate
system as shown in Figure 1.  The user can repeat
this process for as long as she has new chunks of code to evaluate with no
requirement to stop and restart the program along the way.

From very simple to very complex

The chunk of Java code can be as simple or as complex as may be needed to
satisfy the user’s needs.  For example, the chunk of code could be as
simple as the following equation of
a straight line.

y = 6*x+5;

On the other hand, the chunk of code could be as complex as or more complex
than the
following which includes the definition and use of a local inner class along
with the getTime instance method from the Date class and the
static sqrt method from the Math class.

final double z = 65;class LocalClass{public long p = 
new java.util.Date().getTime();public double method(
double data){return sqrt(p/(data*z*1.0E09));}}LocalClass 
q = new LocalClass();y = q.method(18.6)*x;

(Line
breaks were manually inserted into the example given above to force it to fit
into this narrow publication format.)

Viewing tip

You may find it useful to open another copy of this lesson in a
separate browser window.  That will make it easier for you to
scroll back
and forth among the different listings and figures while you are
reading
about them.

Supplementary material

I recommend that you also study the other lessons in my extensive
collection of online Java tutorials.  You will find those lessons
published
at Gamelan.com
However, as of the date of this writing, Gamelan doesn’t maintain a
consolidated index of my Java tutorial lessons, and sometimes they are
difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

I also encourage you to review the material in the lessons
listed in the References section of this document.

Preview

A useful program

I claim no special expertise in the complex areas of class loaders or dynamic method
invocation.  In fact, I find much of what I read on the web about these
topics to be confusing and often contradictory.  However, I have learned enough about these topics to
put them to work.  As an example, I will present and explain a very useful program that might be called an
equation solver
at the low end, and might be called an interactive code exerciser
at the high end.  I will share the knowledge that I have gained on these
topics with you in
this two-part lesson.

A screen shot of the program GUI

I will begin by showing you a screen shot that illustrates the program in
operation.  Then I will briefly explain the operation of the program. 
After that, I will explain the program code in detail.

A screen
shot of the program GUI is shown in Figure 1.


Figure 1

Dynamic class loading and method invocation

This program, which is named Reload02, demonstrates the use of dynamic
class loading and dynamic method invocation.  When the program executes, it
defines, writes, compiles, loads, instantiates, and invokes methods on an object of a new
class that is designed automatically according to input from the user.

A plotting program

As you can see from Figure 1, this is a plotting program.  It is
designed to automatically create and load a new class file named Reload02a.class,
to instantiate a new instance (object) of the class, and to
plot the behavior of three instance methods belonging to the object.  The
user can repeat this cycle indefinitely, entering specifications for the new
classes into the text fields at the top of the GUI, with no requirement to stop
and restart the program.

Three methods

The three methods are named f1, f2, and f3.  The
behavior of each of the three methods is specified in text form by the user who
enters the specifying text into three corresponding text fields at the top of
Figure 1.

Creating and loading a new class

By modifying the text, checking the check box near the top, and clicking the
button near the bottom, the user can cause a new class to be created, compiled,
and loaded.  The
new class contains three methods with behavior matching the text entered by the
user.

Plotting the behavior of the methods

The behavior of each of the three methods is then plotted on a common set of
Cartesian coordinates.  Once the methods are defined and plotted for the
first time, the user
can modify the plotting parameters in the text fields at the bottom of Figure 1
and cause the methods to be plotted again and again with different plotting
parameters.

Zooming in

For example, Figure 2 shows the result of adjusting the
plotting parameters so as to zoom in on the region near the origin of the graph
shown in Figure 1.


Figure 2

The user can replot the graph as many times as may be needed using different
plotting parameters for each new graph.

New method specifications

When the user is satisfied with the plots of those three methods, she can modify the text in the text fields near the top and start over with three new
methods.  It is important to note that, due to dynamic reloading of the
class, this does not require the termination and/or restarting of the program.

Modifying the text specification for one or more of the three methods,
checking the checkbox, and clicking the Graph button causes a
new class to be defined, written, compiled, loaded, and instantiated, and causes
the three methods belonging to the object of the new class to be executed and
plotted.  The new class is designed automatically according to the input
provided in the text fields by the user.

Plotting format

As you can see in Figure 1 and Figure 2, the three methods
named f1, f2, and f3 are plotted in black, red, and blue respectively.  A
coordinate system with axes, tic marks, and labels is plotted in green.

The
labels on the axes correspond to the values at the extreme edges of the plotting
surface.  As shown in Figure 1, the initial plotting range extends from -151 to +151 on each
dimension.  This causes points plotted within the range from -150 to +150
inclusive to be visible inside the plotting surface.

As shown in Figure 2, the plotting range on the
positive or negative side of either dimension can easily be changed by the
user.

The overall default size of the GUI is 475 pixels wide by 430 pixels tall.  This size
was chosen to satisfy the size requirements of my publisher.  You may want to
expand the overall size of the GUI in order to be able to view more detail.

User input, graphic output

As discussed above and as shown in Figure 1 and Figure 2, the program provides three text fields
near the top of the GUI into which the user can enter text expressions written in java syntax. 
These expressions are evaluated to produce the plots shown in the center of the
GUI.  For example, when the following equation
is entered into one of the text fields, the check box is checked, and the
Graph
button is clicked, the value of y is plotted on the vertical axis as a function of x on the
horizontal axis.  (Note that the semicolon is required.)

y = 100.0*(cos(x/10.0)*sin(x/11.0));

In
this case, the cos and sin methods of the Math class are used to evaluate the
equation.  (This produces the blue curve in Figure 1 and Figure 2.)

All computations are performed as type double.  Then the results are
converted to integer values for plotting.

From simple to complex

The text entered into the text fields can be as simple or as complex as
is required to satisfy the user’s needs.  For example, a beginning algebra student might be
interested in graphing the following three equations of a straight line and
comparing the different graphs:

y = 10*x + 15;
y = -5*x - 0;
y = 3*x -50;

DSP

On the other hand, someone interested in understanding Digital Signal Processing (DSP),
might be interested in the plotted form of the sinusoidal equation shown
above(As
mentioned earlier, this is the blue curve in Figure 1 and Figure 2.)

Java programming and DSP

Someone having Java
programming knowledge can enter more advanced text into the text fields, such as
the complex chunk of Java code shown earlier.

For example, the following text will plot as a series of
impulses, as
shown in Figure 3.

if(x%10 == 0){y = 50;}else{y = -50;}

(This is another functional form that might be of great interest to someone
interested in DSP because we frequently deal with impulse trains in
DSP.)


Figure 3

Eliminating two of the three graphs

Note that two of the three graphs were effectively eliminated from Figure 3
by entering equations that caused the value for y to be a constant which was
off the page.

Static import directive

The program uses a static import directive for the Math class
requiring the use of J2SE 5.0 or later.  Therefore, it is not necessary to qualify methods from
the Math class with the
name of the class as in Math.cos(…).  As shown in Figure 2, all that is required is
cos(…).  Thus, all
of the methods of the Math class are directly available to be included in the
text specifications for the three methods.

Using other classes

If it is desired to use other classes from the Java API, it is necessary to
fully qualify the references to those classes using their package name, as shown
in the following text specification.  (This example has no purpose other
than to illustrate the syntax involved.)

y = (x * ((new java.util.Date().getTime())/
(new java.util.Date().getTime())));

This is the text that is plotted in
red in Figure 1 and Figure 2(Note that a line break was manually inserted in the middle of the
above equation to
force it to fit into this narrow publication format.)

Design, write, compile, load, instantiate, evaluate,
and plot a new class

As shown in the above figures, the three
text fields used for entry of text specifications are located near the top of the GUI.  In
addition, a check box is also located near the top of the GUI.  There is a
Graph
button at the bottom of the GUI.

If the check box is checked when the Graph
button is clicked, a new class that matches the specifications that have been entered
into the text fields is automatically designed, written, compiled, loaded and
instantiated before
the methods are evaluated and plotted.

If the check box is not checked…

If the check box has not been checked when the Graph button is clicked,
a new class is not loaded. Rather, the methods
in the existing class are simply re-plotted, potentially with different plotting
parameters.  (You can change the plotting parameters by changing the values in
the text
fields at the bottom of the GUI.)

Method specifications at startup

As shown in Figure 1, the program plots the following three text expressions
by default at startup in order to provide a visual confirmation that the program is
working, and also to illustrate the syntax that should be used to enter
text specifications into the text fields:

y = (x*x*x + 40*x*x + 100*x -6000)/100;
y = (x * ((new java.util.Date().getTime())/
(new java.util.Date().getTime())));
y = 100.0*(cos(x/10.0)*sin(x/11.0));

(Note that a line break was manually entered
into the
second
equation
above to force it to fit into this narrow
publication format.)

A simple cubic function

The first equation shown above is a
simple cubic function with roots at x values of -20, -30, and +10.

Classes other than the Math class

The second
equation
illustrates how classes other than the Math class can be incorporated
into the text specifications.

Methods of the Math class

The third equation illustrates how methods of the Math class can
be incorporated into the text specifications.

Adjusting the plotting parameters

In addition to the text fields at the top of the
GUI, this program also provides the following text fields and a button labeled
Graph at the bottom of the GUI.  The combination of these text fields and the
button makes it possible for the user to adjust the plotting parameters and to
replot the graph as many times with as many plotting-parameter settings as may
be needed.

  • xMin = minimum x-axis value
  • xMax = maximum x-axis value
  • yMin = minimum
    y-axis value
  • yMax = maximum y-axis value
  • xTicInt = tic interval on x-axis
  • yTicInt =
    tic interval on y-axis
  • xCalcInc = calculation interval (should normally be set to
    1.0)

The user can modify any of these parameters and then click the Graph button
to cause the three methods to be re-plotted according to the new parameters.

Output on the command-line screen

In addition to the output shown in the GUI in Figure 1, the program also produces status
output on the command-line screen as shown in Figure 4.


Figure 4

In
order to plot new and different equations, it is only necessary to:

  • Enter the new specifications into the text fields at the top of the GUI
  • Check the check box at the
    top of the GUI, and
  • Click the Graph button.

Possible compilation problems

This will cause a new class containing three methods that represent the
text specifications in the three text fields to be compiled and loaded.  If the new class is compiled and
loaded successfully, the new graphs will appear on the GUI fairly quickly.  If the compilation process fails to
return within five seconds, the compilation process will be aborted and a
"Compilation timeout error" will be declared on the
command-line screen as shown in Figure 4.

It is also possible that the compilation
process could return within five seconds with a compilation error.  In either
case, a "Probable compile error" will be declared on the
command-line screen.

If at first you don’t succeed, try, try again

However, the program does not terminate
as a result of a probable compile error.  You can fix the problem in the
text specification and try again by checking the checkbox and clicking the
Graph
button.

How do you know how to fix the problem?

The source code for the new class file named Reload02a.java is
deposited in a subdirectory (of the current directory) named Temp.

In the event of a compiler error, the command-line screen shown in Figure 4
does not provide any diagnostic information.  Therefore, it isn’t of much
help in fixing the problem.  In that case, it might be a
good idea to manually recompile the source-code file named Reload02a.java
in the subdirectory named Temp using the javac.exe
program from Sun before attempting to repair the text specification in the text
field shown in Figure 1.

In that instance, the source code should not compile successfully, but useful
diagnostic information should be produced.  My approach at that point would
probably be to repair the source code file in order to get a good compilation. 
Having determined the nature of the problem with the specification, I would then
repair the text specification in the text field of Figure 1, check the check
box, and click the Graph button again.

Two programs in Part 1

I will present and explain two programs in Part 1 of this lesson.  The
first program is named Reload01
The purpose of this program is simply to illustrate the methodology for
dynamically compiling and loading a new class, instantiating an object of the new class, and
invoking a method belonging to that object.  This program has no particular
use other than to illustrate the concepts listed above.

The second program that I will present and explain in Part 1 of this lesson
is named Reload02.  This program will incorporate the three concepts
listed above into the plotting program described earlier.

Three ways to do the job

There are at least three ways to make use of a class that has been
dynamically loaded.  Both of the programs presented in Part 1 of this
lesson will use the invoke method of the Method class
(reflection)
to
dynamically invoke methods belonging to objects of the newly-loaded class.

Part 2 of this lesson will demonstrate the second approach for accomplishing
the same results using interfaces while avoiding the complexities of reflection.  Part 2 will also describe, but
probably will not demonstrate the
third approach.

General Background Information

This section will provide general background information on class loaders and
dynamic method invocation.

Class Loaders

What does it mean to load a class?  I can’t provide a complete
technical description, but I can tell you that once a class has been loaded, a
special object of the
class named Class will have been automatically instantiated.  The Class object
represents the newly-loaded class, and you can do lots of things with it. 
(See for example the introspection example in the earlier lesson entitled
Swing from A to
Z: Analyzing Swing Components, Part 2, GUI Setup
.)

Putting class loaders to work

As I explained earlier, I claim no special expertise in the complex area of
class loaders.  However, I have learned enough about them to put them to
work and I will share that knowledge with you.

Two kinds of class loaders

I am led to believe that there are two kinds of class loaders:

  • The primordial class loader
  • Class loader objects

There is only one primordial class loader in a running Java program, and it is part of the Java Virtual
Machine.

There may in addition be one or more class loader objects, which are created
by the program at runtime.

When can a class be reloaded?

I am led to believe that once a class is loaded, it can only be reloaded if
it was loaded by a class loader object.  Classes loaded by the primordial
class loader cannot be reloaded.  If a class cannot be reloaded, it also
cannot be replaced by a new class having the same name.

Class loading priority

After creating a class loader object, you can ask it to load classes
according to certain restrictions.  However, I believe that even if you ask a class
loader object to load a class, the primordial class loader is given an
opportunity to load the class first.  If the primordial class loader finds
the class on the classpath, it will load it.

Avoid putting your re-loadable classes on the
classpath

As mentioned above, once a
class is loaded by the primordial class loader, it cannot be reloaded. 
Therefore, classes that you intend to load and reload using class
loader objects must not be on the class path.  If they are, they will be
loaded by the primordial loader, and once loaded cannot be reloaded.

The URLClassLoader class

It is possible to define your own class loader objects to deal with special
class loading requirements.  However, that
is not demonstrated in this lesson.  Rather, this lesson instantiates an
object of the URLClassLoader class and uses it to load classes.  The
URLClassLoader class is contained in the standard Java class library.

Loading a class

Once you have a URLClassLoader object, you can ask it to load a class
by invoking the loadClass method on the object passing the name of the
class as a String.  If the class is successfully loaded, the
loadClass
method will return a reference to a Class object that
represents the newly-loaded class.  (The loadClass method throws
the ClassNotFoundException if it can’t find the class file.)

Instantiating an object of the newly-loaded class

Once you have a Class object that represents the newly-loaded class,
you can instantiate an object of the class by invoking the newInstance
method on the Class object.

What is the type of the new object’s reference?

However, the type of the reference
returned by the newInstance method is not the name of the class
represented by the Class object.  Rather, in J2SE 5.0 and later, it is the generic type
<T>.

Cannot cast to the true type

Furthermore, because the newly-loaded class is not on the classpath,
it is not known to the compiler at compile time, so you cannot compile a
statement that will cast the object’s reference to the type of the newly-loaded
class.

(However, to get on more familiar ground, you can save the
reference as type Object if you are uncomfortable with the concept of
the generic type <T>.  Prior to the release of J2SE 5.0 and its
concept of
Generics
, the
newInstance
method of the Class class actually returned a
reference of type Object .)

How do you invoke a method belonging to the new
object?

That brings us to the next topic.  If your reference to the new object
is not of the correct type, how do you invoke methods belonging to the new
object?

As I mentioned earlier, there are at least three ways to invoke the new object’s
methods.  Both of the programs that I will explain in Part 1 of this lesson will use the
invoke method of the Method class (reflection) to dynamically invoke methods
belonging to the object.

Part 2 of this lesson will demonstrate the second
approach, which uses a common interface while avoiding the
complexity of dynamic method invocation.  Part 2 will also describe, but
probably will not demonstrate the third approach.

Dynamic Method Invocation using
Reflection

The Class object that represents a class provides a large number of
methods that make it possible for you to get information pertaining to the class
represented by the object.  For example, the method named
getDeclaredMethods
(plural form):

"Returns an array of Method objects reflecting all the methods
declared by the class or interface represented by this Class object."

Similarly, the method named getDeclaredMethod (singular form):

"Returns a Method object that reflects the specified declared
method of the class or interface represented by this Class object."

Will use the singular form of the method

Since I already know the names, return types, and parameter types
for the methods of interest in the programs in this lesson, I won’t need a list
of all the methods belonging to the object.  Therefore, I can use the
singular form, getDeclaredMethod, described above.

The Method class

The getDeclaredMethod method returns a reference to an object of the
class Method.  According to the Sun documentation:

"A Method provides information about, and access to, a single
method on a class or interface. The reflected method may be a class method
or an instance method (including an abstract method)."

The invoke method of the Method class

Among the many methods provided by the Method class is the method
named invoke.  Again, according to the Sun documentation, this
method:

"Invokes the underlying method represented by this Method
object, on the specified object with the specified parameters."

Makes it possible to invoke a particular method…

Thus, once you have a Method object that represents a particular
method, it is possible to invoke the method
named invoke on the Method object to cause that particular method
belonging to a particular object to be executed.

Does not require a reference of the true type

Furthermore, that is possible even if the type of your object’s reference is not the true type of the object.  For example, your reference
could be the generic type Object.

(Normally, if you have a reference to an object as type Object,
you cannot directly invoke a method on the object unless the method is one
of the eleven methods defined in the class named Object
However, you can get around that restriction by using the dynamic method
invocation process described above.  I will demonstrate the process in
the two programs in this document.)

Someone once said, "The devil is in the details."

There are quite a few tedious details involved in the use of dynamic method
invocation.  I will explain some of those details in the explanation of the
code in the sections that follow.

Discussion
and Sample Code


I will provide a detailed explanation of the following two programs in Part 1 of this lesson:

  • Reload01
  • Reload02

The first program listed above is designed to illustrate how to use dynamic class loading and
dynamic method invocation.  This is a relatively simple, but not particularly useful program.

The second program listed above is designed to incorporate dynamic class loading and
dynamic method invocation into a useful but much more complex program.

The Program Named Reload01

The program named Reload01 illustrates writing, compiling,
loading, recompiling, and reloading a new class, and invoking a method on an
object instantiated from that newly-loaded class totally under program control
at runtime.

Write a simple source code file

The program begins by automatically writing the simple class definition shown
in Listing 1 into a source code file named Reload01a.java in a subdirectory (of the current
directory)
named temp.

public class Reload01a{
  public String theMethod(String str,double val){
    System.out.println("Executing theMethod");
    return str + " " + "Tom" + " " + val/3;
  }
}

Listing 1

The program GUI

The program displays the simple GUI shown in Figure 5.


Figure 5

This GUI has a single button labeled Recompile and Reload.  Each time you click the button, the program compiles and reloads the class
named Reload01a, which was originally written as shown in
Listing 1, but
which may have modified by you, external to the program after it was originally written.

Then the program instantiates an object of the newly-loaded class and
invokes the method named theMethod on the object.

If you modify the source code…

If you modify the source code
file for the class using a text editor outside the program, the next time you click the button to cause the file to be
recompiled and reloaded, the new behavior that matches your modifications will be
exhibited.

Compiler errors

When you modify the source code for the class, you may inadvertently insert a
syntax error.  If the compilation
process fails to return within five seconds, the compilation process is aborted
and a "Compilation timeout error" is declared.

It is also possible that the
compilation process could return within five seconds with a compilation error.  In
either case, a "Probable compile error" will be declared and
displayed on the command-line screen.

However, the program does not terminate as a result of a probable compile error, so you can fix the problem
in the source code and try again by clicking the button again.

The location of the class file

The location of the class
file is hard-coded as the subdirectory (of the current directory) named
temp, but could be anywhere (including being located at some URL on
the internet)
that is accessible by the program if you were to modify the
program accordingly.  In other words, classes can be dynamically loaded at
runtime from a variety of locations.

Watch out for the classpath

You must make certain that there is not a copy of a compiled
version of the class named Reload01a on the classpath.  If the program finds a
copy on the classpath, that version of the class will be loaded by the
primordial loader, and will not be reloaded when a reload is requested.

Program output

Figure 6 shows typical output produced by the
program on the command-line screen.

Writing the class file.

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compile complete
Executing theMethod
Hello Tom 3.3666666666666667

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compilation timeout error.
Probable compiler error.

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compile complete
Executing theMethod
Hello Bill 3.3666666666666667

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compile complete
Executing theMethod
Hello Bill 2.525
Figure 6

Modified source code

The source code for the class was modified between each click of the button
in Figure 5 producing the output shown in Figure 6.   Thus, the behavior of the recompiled and reloaded class
was different each time the button was clicked.  (Note in particular the names and the
numeric values.)
  In one case, a syntax error was purposely created in
such a way as to produce a
compiler error.

Program testing

This program was tested using J2SE 5.0 under Windows XP.

Will explain the code in fragments

A complete listing of the program is provided in Listing 35 near the end of
the lesson.  I will explain the code in fragments.

Event-driven programming code

Much of the code in this program involves event-driven programming and the
Delegation or JavaBeans event model.  If you have studied my
earlier lessons on these
topics, this material should be familiar to you.

(This event-driven programming code is included in
the program simply as a means of controlling the process and is not an inherent
part of dynamic class loading.)

I won’t bore you by explaining the event-driven programming code in detail, but will skip
over it fairly rapidly and mention it fairly briefly.  I will concentrate on the code
required to write, compile,
load, recompile, and reload the new class named Reload01a, and to invoke a method on an
object instantiated from that newly-loaded class totally under program control
at runtime.

The Reload01 class

The beginning of the class and the main method for the class named
Reload01
is shown in Listing 2.

class Reload01 extends JFrame{
  File targetDir;//location of the file named Reload01a
  
  public static void main(String[] args){
    new Reload01().writeTheClassFile();
  }//end main

Listing 2

The code in Listing 2 instantiates a new object of the class named
Reload01
and invokes the method named writeTheClassFile on that
object.  The purpose of the method named writeTheClassFile is to
write the source code file for the class named Reload01a in a
subdirectory (of the current directory) named temp.

The method named writeTheClassFile

The method named writeTheClassFile is shown in its entirety in
Listing
3
.

  //Write the source code for the file named Reload01a
  // into a subdirectory named temp. If the file
  // exists, it will be overwritten.  
  void writeTheClassFile(){
    try{
      //Create a File object that points to a directory
      // where the class file will reside.
      targetDir = new File(System.getProperty("user.dir")
               + File.separator + "temp" + File.separator);
        
      //If the directory doesn't exist, make it.
      if(!targetDir.exists()){
        targetDir.mkdir();
      }//end if
      
      //Get an output stream for writing the source code
      // for the new class file.
      DataOutputStream dataOut = new DataOutputStream(
             new FileOutputStream("temp" + File.separator +
                                        "Reload01a.java"));

      //Create the source code for the new class file.
      System.out.println("Writing the class file.");
      dataOut.writeBytes(
       
      "public class Reload01a{" +
        "public String theMethod(String str,double val){" +
          "System.out.println("Executing theMethod");" +
          "return str + " " + "Tom" + " " + val/3;" +
        "}" +
      "}"

      );//end writeBytes method
      
      dataOut.close();//Close the output file.

    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end writeTheClassFile

Listing 3

Most of the code in Listing 3 should be familiar to you, particularly if you
have studied my previous lesson entitled
Core Java Classes, Input
and Output Streams
.  Therefore, I won’t discuss the code in Listing 3
any further.

The constructor

The constructor for the class named Reload01 begins in
Listing 4.

  Reload01(){//constructor
    setTitle("Copyright 2006, R.G.Baldwin");
    JButton button = new JButton("Recompile and Reload");
    getContentPane().add(button);
    
    button.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          System.out.println("nIn actionPerformed");

Listing 4

An anonymous inner class

The boldface code in Listing 4 shows the beginning of the definition of an
anonymous listener class, and the instantiation of an anonymous listener object
of the anonymous class.

This code is something less than straightforward.  If you are unfamiliar
with the use of anonymous inner classes, I recommend that you go back and review
my earlier lesson entitled
The Essence of
OOP using Java, Anonymous Classes
along with other lessons that I have
published on this topic.  You can access those other lessons using the links at

http://www.dickbaldwin.com/tocmed.htm
.  You might also want to review
the lessons on
member classes
and
local classes
while you are at it.

The last two lines in Listing 4 show the beginning of the definition of the
actionPerformed method, which is executed each time the user clicks the
button shown in Figure 5.

Invoke the compile method

Continuing in the actionPerformed method, the code in Listing 5 invokes the method named compile to cause the
new source file named Reload01a to be compiled.

          try{
            boolean compileStatus = compile(
               "temp" + File.separator + "Reload01a.java");

Listing 5

The method named compile returns true on a successful compile and
false otherwise.  The return value is saved in the variable named
compileStatus
for later use.

(If you are unfamiliar with the use of File.separator, you can look it
up in the Java documentation from
Sun.)

The method named compile

The code for the method named compile begins in Listing 6.

  private static boolean compile(String file)
                                        throws IOException{
    System.out.println("Compiling " + file);

Listing 6

The code in Listing 6 simply displays a message on the command-line screen. 

Executing the Java compiler in a separate process

The really important code in the compile method is shown in the single statement in
Listing 7.

    Process p = Runtime.getRuntime().exec("javac " + file);

Listing 7

In case you have never done this before, Listing 7 shows how you can cause
Java programs to execute other programs written in other languages. 
According to Sun, the exec method of the Runtime class:

"Executes the specified string command in a separate process."

In
this case, the code in Listing 7 causes the Java compiler named javac.exe
to be executed to compile the file named Reload01a in the subdirectory
named temp.

(I explained this process (used for an entirely
different purpose) in an earlier lesson entitled
Introduction to the Java
Robot Class
.)

An object of the Process class

As you can see, the invocation of the exec method in Listing 7 returns
a reference to an object of the class Process.

Part of what Sun has to say about a Process object is shown in
Figure
7
.

"The ProcessBuilder.start() and Runtime.exec methods create a native process and return an instance of a subclass of
Process that can be used to control the process and obtain information about it. The class
Process provides methods for performing input from the process, performing output to the process,
waiting for the process to complete, checking the exit status of the process, and
destroying (killing) the process."

Figure 7

In this program, I will be particularly interested in the following two
capabilities listed in Figure 7:

  • Waiting for the process to complete
  • Destroying (killing) the process

Will invoke the waitFor method

Once I start the compilation process, I will invoke the waitFor method on
the Process object to cause the program to block until the compilation is
finished.  However, I have determined experimentally that sometimes the waitFor
method hangs up and fails to return when there is a compiler
error.

A five-second timer

Therefore, I designed some special code to deal with that problem.  The
code that I designed allows five seconds for the compilation to complete
successfully and for the waitFor method to return.  If the
waitFor
method fails to return within five seconds, the code

  • Declares a "Compilation timeout error"
  • Invokes the destroy method on the Process object
  • Returns false to indicate a compiler error

However, it doesn’t terminate the program and the user may correct the source
code for the class
and try again.

Complex flow of control

I will be trying to explain the program flow of
control
involving two threads that are executing in parallel in the
paragraphs that follow.  This can
become a little difficult to explain.

The compiling thread and the timer thread

For want of a better approach, I will refer to the thread that executes the
code in Listing 8 as the compiling thread.  I will refer to the
other thread as the timer thread.

Two browser windows would be useful

This is a place where it would definitely be useful for you to have this
lesson open in two or more browser windows so that you can conveniently view two
or more code listings at the same time.

Start a five-second timer

The code in Listing 8, which is executing in the compiling thread,
instantiates a new Thread object of the Timer class and starts it running
as a
different thread, which is the timer thread.  The timer is set to
run for 5000 milliseconds, or five seconds.

(If you are unfamiliar with multithreaded programming in Java, see my
earlier lesson entitled
Threads of Control.)

    Thread myTimer = new Thread(
                   new Timer(Thread.currentThread(),5000));
    //Start Timer thread
    myTimer.start();

Listing 8

The Timer class

The Timer class, which is instantiated in Listing 8, is shown in its
entirety in Listing 9.

class Timer implements Runnable{
  Thread theCompilingThread;
  int delay;//time interval to sleep
  //-----------------------------------------------------//
  
  Timer(Thread theCompilingThread, int delay){//constructor
    this.theCompilingThread = theCompilingThread;
    this.delay = delay;
  }//end constructor
  //-----------------------------------------------------//
  
  public void run(){
    try{
      Thread.currentThread().sleep(delay);
    }catch(InterruptedException e){
      //No action is required when this sleeping thread is
      // interrupted.
      return;
    }//end catch

    //Control is transferred to here when the sleeping
    // thread awakens naturally after the specified delay
    // period.  This means that the compilation process is
    // probably hung up.  Interrupt the compilation
    // process and terminate the run method.
    theCompilingThread.interrupt();
  }//end run method
}//end Timer class

Listing 9

The constructor

The constructor for the Timer class receives and saves two incoming
values:

  • A reference back to the compiling thread.
  • The amount of time to count down in milliseconds.

The run method

The essential functionality of a Thread object is written into its
run
method, so that is what I will concentrate on.

The run method is executed after the code in Listing 8 invokes the
start
method on the Timer object.

(Apparently the start method takes care of some necessary
tasks, and then invokes the run method on the Thread object.)

Go to sleep

The code in the run method begins by putting the timer thread to sleep
for the specified delay interval (5000 milliseconds or 5 seconds in this
case)
.  The thread will sleep for that amount of time and then wake up
and continue executing statements unless awakened prematurely.

Waking up prematurely

A sleeping thread is awakened prematurely when code that is executing in
some other thread invokes the interrupt method on the sleeping thread. 
You will see shortly how that can happen in this program.

An InterruptedException

When a sleeping thread is awakened prematurely, it will wake up and throw an
InterruptedException.  This, in turn, causes the thread to execute
the code in the matching catch block.

For now, just note that when the thread is awakened prematurely, no special
action is required.  The code in the matching catch block in Listing 9
simply executes a return statement, which causes the run
method to terminate and the thread to die.

When the thread wakes up naturally

The case that is of particular interest to us at this point in the program is
the case where the sleeping thread wakes up naturally after having slept for
five seconds.  When this happens, the code below the catch block in
Listing
9
is executed.

The compiling thread must be hung up

The code in the compiling thread is responsible for completing the
compilation process and invoking the interrupt method on the timer thread
to awaken the timer thread before the five seconds has elapsed.

Therefore, if control reaches this point in the program, the assumption is
that the waitFor method that was invoked on the Process object in
the compiling thread is hung up in the wait state for some reason. 
It is also assumed that this is probably due to a compiler error.

Interrupt the compiling thread

Having concluded that a compiler error has probably occurred, the last
statement in Listing 9 invokes the interrupt method on the
compiling
thread
on which the waitFor method was invoked.  This causes the
waitFor
method to terminate and to throw an InterruptedException,
which in turn causes the code in the catch block in
Listing 11
to be executed.

(I will explain the code in the catch block in
Listing 11
shortly.)

The sequence of events

Returning now to a review of Listing 8, note that the code in Listing 8
instantiates the object of the Timer class and causes the run
method to be invoked on that object, starting the five-second timeout interval to begin on
the timer thread, which is running in parallel with the compiling thread that
executes the code in Listing 8.

Then control moves to the code on the compiling thread shown in
Listing 10.

    System.out.println("Waiting for completion");
    
    try{
      p.waitFor();//wait for completion

      //The waitFor method has returned,
      if(myTimer.isAlive()){
        //Stop the timer.
        myTimer.interrupt();
      }//end if

Listing 10

Wait for the compilation to complete

After starting the five-second timer on the timer thread, the code in
Listing
10
displays a message and then immediately enters a try block where it
invokes the waitFor method on the Process object.  This
effectively puts the compiling thread to sleep also, awaiting completion of the
compilation of the class file that was started in Listing 7.

If all goes well…

If all goes well, the compilation process will have completed (either
successfully or unsuccessfully)
before the five-second timer wakes up, and
control will have passed to the if statement shown in Listing 10.

The if statement checks to see if the timer thread is still alive
(meaning that it is still sleeping in this case)
and if so, invokes the
interrupt
method on the timer thread to cause it to wake up prematurely. 
When the timer thread is awakened prematurely, it executes the code in the
catch
block in Listing 9.  As mentioned earlier, this causes the
timer
thread
to die quietly.

If things don’t go well…

However, if the waitFor method fails to return within the five-second
timeout interval of the timer, the timer thread in Listing 9 will awaken naturally and
will invoke the interrupt method on the compiling thread running in
Listing 10.

(Again recall that this is based on the assumption that the
compilation should easily complete within five seconds.  If five
seconds has elapsed, it is assumed that the waitFor method is hung
up, and it is also assumed that this is probably the result of a compiler
error.)

This causes the waitFor method to terminate and to throw an
InterruptedException
, transferring control to the catch block shown in
Listing 11.

    }catch(InterruptedException e){
      //The timer expired before the waitFor method
      // returned and interrupted the waitFor method.
      System.out.println("Compilation timeout error.");
      p.destroy();
      return false;
    }//end catch

Listing 11

Handling the InterruptedException

The code in Listing 11:

  • Displays a Compilation timeout error message,
  • Invokes the destroy method on the Process object for the
    purpose of terminating the compilation operation that began in Listing 7,
  • Terminates the compile method, returning a value of false
    to indicate a probable compiler error.

When the waitFor method returns within five seconds

The code in Listing 12 (at the end of the compile method) is
executed only if the waitFor method that was called in Listing 10
returned during the five-second timeout interval.

    //The waitFor method returned in five seconds or less.

    //p.exitValue() other than 0 indicates a compiler
    // error.
    return p.exitValue() == 0;
  }//end method compile

Listing 12

Not necessarily a successful compilation

The fact that the waitFor method returned within five seconds does not
necessarily mean that the compilation was successful.  For the amount of
code being compiled in this program, one would hope that the method would always
return within five seconds whether or not the compilation was successful.

(The fact that it frequently doesn’t return within five seconds
indicates some sort of an inter-process communication bug to me.)

The process exit value

In any event, there is an exitValue associated with the Process
object.  By convention, this value will be 0 for a successful completion of
the process.  The last statement in Listing 12 gets and compares the
exitValue
with 0, returning true for success and false for
failure.

Back to the actionPerformed method

That brings us back to the code in the actionPerformed method of
Listing 5 where the compile method was called with the return value of
true
or false being saved in a local boolean variable named
compileStatus
.

When the compilation is successful

Listing 13 shows the beginning of an if-else statement
in the actionPerformed method that deals with
the possibility that the compilation either was, or was not successful.

            if(compileStatus){//If compile was successful.
              System.out.println("Compile complete");

Listing 13

The println statement in Listing 13 is executed when the compile process was
successful.

(If you would like to do so, you can skip ahead to
Listing 24 to see what happens when the compile
process is not successful.)

Listing 14 continues with the behavior of the program as a result of a
successful compilation.

Load/reload the class

At this point, the class has been defined (once by the program and
possibly more than once by the user external to the program).
  The
class has also been successfully compiled.

(In fact, the file may have been compiled many times with
manual changes to the source code by the user in between compilations.)

In any event, a compiled class file is available at this point for loading or
reloading.

Listing 14 invokes the method named reloadTheClass to cause the class
file named Reload01a.class to be loaded.  The method named reloadTheClass
returns a reference to a
Class object that represents the newly-loaded version of the class.

              Class loadedClass = 
                                reloadTheClass("Reload01a",
                                                targetDir);

Listing 14

The code in Listing 14 passes the name of the class
along with the directory containing
the class file that is to be loaded to the method named reloadTheClass.

The method named reloadTheClass

The purpose of this method is to load a class that is specified by name as a
String from a subdirectory specified by a File parameter named
dir
.

As I mentioned earlier, the class will be loaded using a class loader object
of type URLClassLoader, which is a class in the standard Java class
library.

The method named reloadTheClass begins in Listing 15.

  static Class reloadTheClass(String reloadableClass,
                              File dir){
    URL[] theUrl = null;

Listing 15

An array reference variable of type URL[]

Listing 15 declares an array reference variable of type URL[] initializing
its value to null.  The code in the method will use the information about the directory
containing the class file to create a URL object and will use that URL
object to instantiate and populate the URL array object. 

Create the URL object

At this point, we need to get a URL object that points to the directory containing the class file for the class that is to be loaded.

Following the recommendations in the Sun documentation, the code in
Listing
16
converts the incoming File object to a URI and then converts the
URI to a URL.  The reference to the URL object is
deposited in a new one-element URL array object.  The
array object’s reference is stored in the reference variable named theUrl,
which was declared in Listing 15.

    try{
      URI uri = dir.toURI();                   
      URL url = uri.toURL();
      theUrl = new URL[]{url};
    }catch(Exception e){
      e.printStackTrace();
    }//end catch

Listing 16

Next step is to load the class

The next step in the process is to load the class, getting and returning a
Class
object that represents the class in the process.

Listing 17 declares a reference variable of type Class,
which will be
populated later with a reference to the Class object that represents the
newly-loaded class.

    Class theClass = null;

Listing 17

Get a URLClassLoader object

Listing 18 instantiates a new class loader object of type URLClassLoader associated with the directory containing the
class file that is to be loaded.

    try{
      ClassLoader classLoader = new URLClassLoader(theUrl);

Listing 18

An array of URL object references

Note that the constructor for the URLClassLoader class requires an
array of URL object references as an incoming parameter.  The class
loader object that is constructed is capable of loading classes only from the
URLs that are specified by the references to URL objects stored in the
array.

In this case, that list of URLs is restricted to the single
directory containing the class that is to be loaded.  However, for a larger
program, it may be necessary to load classes from a variety of different URLs. 
If so, each of those URLs would be specified by references to URL objects
stored in the array.

Load the class

Finally, the code in Listing 19 invokes the loadClass method on the
URLClassLoader object, passing the String name of the class that is to be
loaded as a parameter.  The class loader object will search the specified
URLs for which it was constructed, looking for a class file having the specified name and
will attempt to load it if
it is found.

      theClass = classLoader.loadClass(reloadableClass);
    }catch (Exception e){
      e.printStackTrace();
    }//end catch

Listing 19

Success or failure in loading the class

If the load is successful, the loadClass method returns a reference to
a Class object that represents the newly-loaded class.  That
reference is saved in the reference variable named theClass.

If the load is unsuccessful, the method throws a ClassNotFoundException
In this case, the exception handler simply prints a stack trace describing the
problem.

Return the Class object’s reference

Listing 20 returns the reference to the Class object that represents the newly-loaded
class and terminates the method named reloadTheClass.

    return theClass; 
  }//end reloadTheClass

Listing 20

Note that if the load was unsuccessful, Listing 20 returns null.

Returning to the actionPerformed method

Returning once more to the code in Listing 14, which is part of the
actionPerformed
method being registered on the button shown in Figure 5, this code saves
the reference to the Class object that represents the newly-loaded class in a reference variable named
loadedClass
.

Instantiate an object

Listing 21 invokes the newInstance method on the Class object
to instantiate an object of the newly-loaded class, and saves the object’s
reference in a reference variable of type Object named obj.

              Object obj = loadedClass.newInstance();

Listing 21

Need to invoke the method named theMethod

Our objective is to invoke the method named theMethod on the object
instantiated in Listing 21
However, as explained earlier, it is not possible to invoke that method directly
on the object’s reference while the reference is being treated as type Object
It is also not possible to cast the object’s reference to the class type Reload01a
because the compiler knows nothing about that class at compile time. 
Therefore, another approach must be found to invoke the method named
theMethod
.

Will use reflection

This program will invoke that method using reflection.  This
entails invoking the invoke method on an object of the Method
class that represents the method named theMethod belonging to the object
of the newly-loaded Reload01a class.

Get a Method object

Listing 22 does the following:

  • Invokes getDeclaredMethod of the Class
    class
  • To get a reference to an object of type Method
  • That represents the
    instance method named theMethod
  • That requires two parameters, one of type String and the other of
    type double
  • Defined in the class that is represented
    by the Class object
  • Whose reference is stored in the reference variable
    named loadedClass.
              Method methodObj = 
                 loadedClass.getDeclaredMethod(
                   "theMethod",
                   new Class[]{String.class,double.class});

Listing 22

(The method named theMethod belonging to the target object will be
invoked later by invoking the method named invoke on the object of
type Method, passing the target object’s reference as a parameter to
the invoke method.)

Note that nowhere does the code in Listing 22
mention the name of the target class, Reload01a.

The name of the target method

The first parameter passed to getDeclaredMethod specifies the name
of the target method that will be invoked later.  In this case, the name is
theMethod, and the name of the method is passed as a reference to an
object of type String.

Number and types of parameters

The second parameter specifies the number and types of parameters required by
the method named theMethod.  In this case, the method requires two
parameters, one of type String and the other of type double.

Specify parameters in an array of type Class

Note that the number and types of the parameters are specified by passing a
reference to an array object of type Class.  The number of elements
in the array specifies the number of parameters.  In this case, the number
of elements is two.  The class (or type)
represented by each of the Class objects referred to by the elements in
the array specifies the types of the parameters.  In this case, the types are
String and double.

The target method could be overloaded

Keep in mind that a class could define many different overloaded versions of
methods having the same name.  In that case, a different Method object could
be obtained for each overloaded version by invoking getDeclaredMethod more than once with the
first parameter being the same in each case, but the second parameter being
different in each case.

Save the reference to the Method object

The reference to the Method object returned by
getDeclaredMethod
is saved in the reference variable of type Method
named methodObj.

Cause theMethod to be invoked by reflection

Listing 23 causes the method named theMethod to be invoked on the
target object by invoking the method
named invoke on the Method object, passing the target object’s
reference and a reference to a parameter array as parameters to the invoke
method.

              String returnVal = (String)methodObj.invoke(
                   obj,
                   new Object[]{"Hello",new Double(10.1)});

              System.out.println(returnVal);

Listing 23

Display the returned value

The invoke method returns type ObjectListing 23 casts the return value from Object to String and stores it in a
String
variable named returnVal.  Then Listing 23 displays the
return value.

Passing parameters for the target method

Note that parameters for the target method are passed to the invoke
method in an array of type Object.  Each parameter occupies one
element in the array.  When the target method requires primitive
parameters, they must be wrapped in an object of the corresponding wrapper class
for passing to the invoke method.  In this case, the double
value 10.1 is wrapped in an object of type Double for passing to the
invoke
method.

The primitive parameters are automatically unwrapped by the system for
delivery to the target method when it is executed.

Returned values

Similarly, when the target method returns a value of a primitive type, the
invoke
method wraps that value in an object of the corresponding wrapper
class and returns it as type Object.

As a result, the code that calls the invoke method must cast the
returned reference to the appropriate wrapper type and extract the primitive
value from the wrapper object.  That wasn’t the case in this example
because the return type for the target method was String.

Wrapping things up

Listing 24 shows the closing portions of the if-else statement that
began in Listing 13.  If the compilation process fails, the value of
compileStatus
is false.  In that event, all of the code in the if
clause of the statement is skipped, and the code in the else clause shown
in Listing 24 is executed.

            }//end if on compileStatus
            else{
              System.out.println(
                               "Probable compiler error.");
            }//end else

Listing 24

The code in the else clause simply displays a message on the command-line screen announcing a
Probable compiler error.  At that point, the user can correct the
source code and click the button in Figure 5 to try again.

Complete the anonymous listener class definition

Listing 25 contains cleanup code for the actionPerformed method
and for the anonymous listener class definition that began in Listing 4.

          }catch(Exception ex){
            ex.printStackTrace();
          }//end catch

        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

Listing 25

Complete the constructor

Finally, Listing 26 contains the cleanup code for the constructor that began in
Listing 4.

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(250,100);
    setVisible(true);
  }//end constructor

Listing 26

Listing 26 also signals the end of the program named Reload01
And that wraps up the explanation of the program named Reload01
Next, I will explain the more substantive program named Reload02.

The Program Named Reload02

The program named Reload02 is the plotting program that I described in
some detail earlier.  Therefore, I won’t repeat that
description here.

Two versions of the same program

I will present and explain two versions of this program in the two parts of this
lesson.  The version that I will explain in Part 1 (this document)
uses reflection to access and execute the three methods (f1, f2,
and f3)
belonging to an object of a newly-loaded class named
Reload02a
.

In Part 2, I will present and explain a version of the program that uses a
common interface to access and execute those methods, thus avoiding the
complexity of reflection.  I will also describe, but probably won’t
demonstrate a third approach to dealing with the same issue.

Location of the class file

A new class file named Reload02a.class is created in a subdirectory of the current directory named
temp.  This class file defines three methods named f1, f2,
and f3.  The three methods are evaluated and plotted in the GUI
shown in Figure 1.

If the subdirectory doesn’t exist, it will be created.  If a class file having the same name already exists in that subdirectory, it will be overwritten.

Program testing

This program was tested using J2SE 5.0 under Windows XP.  Because this
program uses static import directives, it requires J2SE 5.0 or later to compile and run successfully.

Will discuss in fragments

I will discuss this program in fragments.  You will find a complete
listing of the program in Listing 36 near the end of the lesson.

Plotting code has been explained before

Those of you who are familiar with my earlier publications may have
recognized the plotting format shown in Figure 1 as being very similar to
plotting formats that I have used in earlier lessons.  In fact, the
plotting code, as well as the code that manages the bottom portion of the GUI
shown in Figure 1, is very similar to code that I have explained in earlier
lessons.

The explanations in the earlier lessons began with the lesson entitled

Plotting Engineering and Scientific Data using Java
.  Therefore, I won’t bore you by explaining that code again. 
Rather, I will concentrate on code that is new to this lesson.  If you are
unfamiliar with the plotting code, please refer back to those earlier lessons.

The class named Reload02

The class named Reload02 is shown in its entirety in
Listing 27.

class Reload02{
  public static void main(String[] args){
    new ScienceGraph();
  }//end main
}//end class Reload02

Listing 27

The main method in Listing 27 simply instantiates a GUI object of the
ScienceGraph class as shown in Figure 1.  Thus, most of the
substance of this program is written into the GUI class named ScienceGraph.

The ScienceGraph class

The definition of the ScienceGraph class begins in Listing 28
As explained above, I deleted the plotting code from Listing 28 for brevity. 
You can view that code in its entirety in Listing 36 near the end of the lesson.

class ScienceGraph extends JFrame 
                                 implements ActionListener{

//Plotting code deleted for brevity
  
  //Text fields for methods with default equations
  // inserted.
  JTextField f1Txt = new JTextField(
             "y = (x*x*x + 40*x*x + 100*x -6000)/100;",40);
  JTextField f2Txt = new JTextField(
            "y = (x * ((new java.util.Date().getTime())/" +
                 "(new java.util.Date().getTime())));",40);
  JTextField f3Txt = new JTextField(
                "y = 100.0*(cos(x/10.0)*sin(x/11.0));",40);
  
  //Check box used to force rewrite, recompile, and reload
  // of the class.
  JCheckBox reloadCkBox = new JCheckBox(
              "Rewrite, Recompile, and Reload the Class " +
              "using the Above Equations",false);

  //Panels to contain a label and a text field
  JPanel pan0 = new JPanel();
  JPanel pan1 = new JPanel();
  JPanel pan2 = new JPanel();
  JPanel pan3 = new JPanel();
  JPanel pan4 = new JPanel();
  JPanel pan5 = new JPanel();
  JPanel pan6 = new JPanel();
  JPanel pan7 = new JPanel();
  JPanel pan8 = new JPanel();
  JPanel pan9 = new JPanel();

  //Misc instance variables
  int frmWidth = 475;
  int frmHeight = 430;
  int width;
  int height;

  //A reference to a newly-loaded class.
  Class loadedClass = null;

  //Plot is drawn on this canvas
  MyCanvas canvas;

Listing 28

Although long and tedious, the code in Listing 28 simply declares a large number of instance variables
used later in the program.  The comments in Listing 28 should suffice and
no further explanation of the code in Listing 28 should be required.

Implements the ActionListener interface

It is worth noting, however, that the ScienceGraph class implements
the ActionListener interface.  Therefore, an object of the
ScienceGraph
class can and will be registered on the button in the bottom of
Figure 1 to listen for action events on that button.

The constructor

The constructor for the ScienceGraph class begins in
Listing 29

  ScienceGraph(){//constructor
    System.out.println(
           "Write, compile, and load initial class file.");

    boolean compileStatus = updateClass();
    
    if(!compileStatus){
      System.out.println(
                        "Unable to compile initial class");
    }//end if

Listing 29

The updateClass method is invoked in Listing 29 for the purpose of
writing, compiling, and loading an initial class file.  The updateClass
method returns true on success and false on a failure to compile.

The returned value from the updateClass method is stored in the
boolean
variable named compileStatus.  If the returned value is
false, the code in Listing 29 posts an Unable to compile initial class
message on the command-line screen.

The updateClass method

Setting the discussion of the constructor aside for awhile,
Listing 30
shows the beginning of the updateClass method.

  boolean updateClass(){
    boolean compileStatus = false;
  
    try{
      //Create a File object that points to a directory
      // where the class file will reside.
      File targetDir = new File(
                System.getProperty("user.dir")
                + File.separator +"temp" + File.separator);
        
      //If the directory doesn't exist, make it.
      if(!targetDir.exists()){
        targetDir.mkdir();
      }//end if
      
      //Get an output stream for writing the source code
      // for the new class file.
      DataOutputStream dataOut = new DataOutputStream(
             new FileOutputStream("temp" + File.separator +
                                        "Reload02a.java"));

Listing 30

Very similar code

Because this program uses the same methodology for compiling, loading, and
instantiating classes, and for invoking the methods belonging to the objects of
those newly-loaded classes, much of the code in this program is very similar to
code that was explained earlier in conjunction with the program named
Reload01
.

(The purpose of presenting and explaining the program named
Reload01
was to explain
the code necessary to support those concepts in a relatively simple
program.)

Won’t repeat the explanation

In those cases where the code in this program is very similar to the code in
the earlier program, I won’t bore you by repeating the explanation. 
Rather, I will simply refer you back to the earlier explanation.

For example, the code in Listing 30 is very similar to the code that was
explained in conjunction with Listing 3 earlier.  Please refer to that
explanation and mentally apply it to the code in Listing 30

Create the class file source code

This program creates the source code for three different class files whereas
the program named Reload01 created the source code for only one class
file.  In addition, the definitions of the class files in this program are
based on information provided by the user in the three text fields shown at the
top of the GUI in Figure 1.

The code in Listing 31 causes the source code files for the three class files
to be written.

      //Get the equations from the text fields.
      String eq1 = f1Txt.getText();
      String eq2 = f2Txt.getText();
      String eq3 = f3Txt.getText();

      //Create the source code for the new class file.
      dataOut.writeBytes(

        "import static java.lang.Math.*;" +
        "public class Reload02a{" +
          "public double f1(double x)"+
          "{" +
            "double y = 0.0;" + 
            eq1 + 
            "return y;" +
          "}" +
          
          "public double f2(double x)"+
          "{" +
            "double y = 0.0;" + 
            eq2 + 
            "return y;" +
          "}" +          
          
          "public double f3(double x)"+
          "{" +
            "double y = 0.0;" + 
            eq3 + 
            "return y;" +
          "}" +
        "}"
      );//end writeBytes method
      
      dataOut.close();//Close the output file.

Listing 31

Get the text from the text fields

Listing 31 begins by invoking the getText method to extract the
current text from each of the three text fields at the top of Figure 1
This text is saved in the String variables named eq1, eq2,
and eq3.

Insert the text into skeleton methods

Then those three strings are concatenated into strings that provide the
skeletons for three methods named f1, f2, and f3.  Those
strings are written into the output file named Reload02a.java in a manner
very similar to that explained for Listing 3 earlier
in the lesson.

Compile and load the class file

The remainder of the updateClass method shown in Listing 32
compiles and loads the class file in a manner very similar to that explained for Listing 5
through Listing 20 earlier.

      //Now compile the new class file
      compileStatus = compile(
               "temp" + File.separator + "Reload02a.java");

      if(compileStatus){
        System.out.println("Compile complete");

        //The class has been defined and compiled.  Now
        // force it to be loaded.
        //Get a URL object that points to the directory 
        // containing the class file.  A File object that
        // points to that directory was created earlier.
        //The compiled class file for the reloadable class 
        // is stored in a directory that is pointed to by a
        // reference variable of type File named targetDir.
        //Following the recommendations in the Sun docs, 
        // convert the File object to a URI and convert the
        // URI to a URL.  Deposit the reference to the URL
        // object into an one-element array of type URL.
        URI uri = targetDir.toURI();                   
        URL url = uri.toURL();
        URL[] theUrl = new URL[]{url};
  
        //Create a new class loader associated with the 
        // directory (URL) containing the reloadable class.
        ClassLoader classLoader = 
                                new URLClassLoader(theUrl);
    
        // Load the specified class, creating a Class
        // object that represents the class in the process.
        loadedClass = classLoader.loadClass("Reload02a");
    
      }else{
        System.out.println("Probable compile error");
      }//end else

    }catch (Exception e){
      e.printStackTrace();
      System.exit(0);
    }//end catch
    
    return compileStatus;
  }//end updateClass method

Listing 32

Returning to the constructor

Now, returning to the discussion of the constructor from Listing 29 above,
Listing 33 assembles the GUI shown in Figure 1 by creating and adding various
components to the JFrame object.  The boldface code in
Listing 33
registers the object of the Reload02 class as an action listener on the
Graph button shown in the bottom of the GUI in Figure 1.

    //Now build the GUI.  
    canvas = new MyCanvas();
    
    JPanel southPanel = new JPanel();
    //Set the layout manager to GridLayout with 
    // 2 rows x 4 cols
    southPanel.setLayout(new GridLayout(2,4));
                  
    JPanel northPanel = new JPanel();
    northPanel.setLayout(new GridLayout(4,1));

    //Button for replotting the graph
    JButton graphBtn = new JButton("Graph");
    graphBtn.addActionListener(this);

    //Populate each small panel with a label
    // and a text field
    pan0.add(new JLabel("xMin"));
    pan0.add(xMinTxt);

    pan1.add(new JLabel("xMax"));
    pan1.add(xMaxTxt);

    pan2.add(new JLabel("yMin"));
    pan2.add(yMinTxt);

    pan3.add(new JLabel("yMax"));
    pan3.add(yMaxTxt);

    pan4.add(new JLabel("xTicInt"));
    pan4.add(xTicIntTxt);

    pan5.add(new JLabel("yTicInt"));
    pan5.add(yTicIntTxt);

    pan6.add(new JLabel("xCalcInc"));
    pan6.add(xCalcIncTxt);
    
    pan7.add(new JLabel("f1"));
    pan7.add(f1Txt);
    
    //Make the color of the labels that identify the 
    // equations for f2 and f3 match the color that will
    // be used to plot those two equations.  f1 is 
    // already black so I didn't need to change its color.
    JLabel f2Label = new JLabel("f2");
    f2Label.setForeground(Color.RED);
    pan8.add(f2Label);
    pan8.add(f2Txt);

    JLabel f3Label = new JLabel("f3");
    f3Label.setForeground(Color.BLUE);
    pan9.add(f3Label);
    pan9.add(f3Txt);

    //Add the populated panels, the button, and the check
    // box to the control panels for the South and North
    // locations.
    southPanel.add(pan0);
    southPanel.add(pan1);
    southPanel.add(pan2);
    southPanel.add(pan3);
    southPanel.add(pan4);
    southPanel.add(pan5);
    southPanel.add(pan6);
    southPanel.add(graphBtn);
    
    northPanel.add(pan7);
    northPanel.add(pan8);
    northPanel.add(pan9);
    northPanel.add(reloadCkBox);

    //Add the sub-assemblies to the frame.  Set the frame's
    // location, size, and title, and make it visible.
    getContentPane().add(northPanel,BorderLayout.NORTH);
    getContentPane().add(southPanel,BorderLayout.SOUTH);
    getContentPane().add(canvas,BorderLayout.CENTER);
    canvas.setBackground(Color.WHITE);
    setBounds(0,0,frmWidth,frmHeight);
    setTitle(
       "ScienceGraph, Copyright 2006, Richard G. Baldwin");
                 
    setVisible(true);//Make the GUI visible
    
    //Set to exit on X-button
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    //Get and save the size of the plotting surface
    width = canvas.getWidth();
    height = canvas.getHeight();
    
    //Cycle visibility once to force the initial methods
    // to be displayed.  There must be a better way to
    // accomplish this.  Without this, the initial
    // methods are not displayed at startup.  This
    // appears to be the result of a timing problem
    // involving compilation, etc.
    setVisible(false);
    setVisible(true);

  }//end constructor

Listing 33

Although the code in Listing 33 is long and tedious, it is straightforward
and shouldn’t require any explanation beyond that provided in the comments. 
If you are unfamiliar with this kind of code, you may want to review my earlier
tutorial lessons on the topic that you will find
here and
here.

The actionPerformed method

As mentioned earlier, because the Reload02 class implements the
ActionListener
interface, an object of the class can be, and is registered
on the Graph button at the bottom of the GUI shown in Figure 1
(See the boldface code in Listing 33.)
  Also, because the Reload02
class implements the ActionListener interface, and the class is not
declared abstract, the class must define the interface method named
actionPerformed
.

The actionPerformed method, with some of the plotting code deleted for
brevity, is shown in Listing 34.

  public void actionPerformed(ActionEvent evt){
    System.out.println("nExecuting actionPerformed");

//Plotting code deleted for brevity.

    boolean compileStatus = true;
    
    if(reloadCkBox.isSelected()){
      //Clear the checkbox, recompile, and reload.
      reloadCkBox.setSelected(false);
      compileStatus = updateClass();
    }//end if on reloadCkBox.isSelected()
    
    if(compileStatus){
      //Repaint the plotting surface
      canvas.repaint();
    }else{
      System.out.println("Unable to compile new class");
    }//end else
  }//end actionPerformed

Listing 34

This event handler is registered on the button to cause the methods to be
re-plotted.  In addition, if the checkbox at the top of the GUI is checked,
the class is rewritten, recompiled, and reloaded before being evaluated to
produce the plots.

If the check box is checked…

If the check box at the top of the GUI is checked, the updateClass
method discussed earlier is invoked to recompile and reload the class.

If the recompilation is successful, the repaint method is invoked on
the Canvas object to cause the methods named f1, f2, and
f3
to be evaluated and plotted.  If the recompilation is unsuccessful,
an Unable to compile new class message is posted on the command-line
screen and no attempt is made to evaluate and plot the methods.

The remaining program code

All of the remaining code in the program, including the overridden paint
method, is either plotting code that is very similar to code that was explained
in earlier lessons, or code that is very similar to code that was explained
earlier in this lesson in conjunction with the program named Reload01
Therefore, I won’t explain the remaining code in the program.  You can view
the remaining code in Listing 36 near the end of the lesson.

Run the Program

I encourage you to copy the code from Listing 35 and Listing 36 into your text
editor, compile it, and execute it.  Experiment with it, making
changes, and observing the results of your changes.

Summary

In this lesson, I taught you how to write a program that modifies its
fundamental behavior at runtime by dynamically modifying, compiling, loading,
and reloading classes.

This involves writing code, which in turn writes new code in the form of
source code for new class definitions.

It also involves writing code that causes the source code for the new classes
to be compiled and loaded.

Finally, it involves writing code that uses reflection to invoke
instance methods belonging to objects instantiated from the newly-loaded
classes.

What’s Next?

In Part 2 of this lesson, I will show you two alternative ways to accomplish
the same thing using an approach that avoids the complexity of reflection.

References

The following online documents contain information that is relevant to the
material covered in this tutorial lesson:

Complete Program Listing


Complete listings of the programs discussed in this lesson are shown in
Listing
35
and Listing 36 below.
 

/*File Reload01.java
Copyright 2006, R.G.Baldwin
Revised 03/02/04

Illustrates writing, compiling, loading, reloading, and
instantiating an object of a class, and invoking a method
on that object totally under program control.

The program begins by automatically writing the following 
simple class into a source code file in a subdirectory (of 
the current directory) named temp:

public class Reload01a{
  public String theMethod(String str,double val){
    System.out.println("Executing theMethod");
    return str + " " + "Tom" + " " + val/3;
  }
}

The program displays a GUI having a single button.

Each time you click the button, the program compiles and 
reloads the class named Reload01a.  Then it instantiates 
an object of the newly-loaded class and invokes the method 
named theMethod on the object.

If you modify the source code file for the class, the next 
time you click the button to cause the file to be compiled 
and reloaded, the new behavior will be evident.

If the compilation process fails to return within five 
seconds, the compilation process is aborted and a 
"Compilation timeout error" is declared.  It is also 
possible that the compilation process could return within 
five seconds with a compilation error.  In either case, a 
"Probable compile error" will be declared.  The program is 
not terminated as a result of a probable compile error, so 
you can fix the problem in the source code and try again
by clicking the button.

The location of the class file is hard-coded as the
subdirectory named temp, but could be anywhere on the disk
that is accessible to the program if the program were
modified accordingly.

You need to make certain that there is not a copy of 
a compiled version of the class named Reload01a on the 
classpath.  If the program finds a copy on the classpath, 
the class will not be reloaded when a reload is requested.

Once this program is running, use an editor to modify the
source code for class and then click the button.

Here is a typical output produced by the program by
modifying the source code file for the class between each 
click of the button.  In one case, a syntax error was
purposely created so as to produce a compiler error.

Writing the class file.

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compile complete
Executing theMethod
Hello Tom 3.3666666666666667

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compilation timeout error.
Probable compiler error.

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compile complete
Executing theMethod
Hello Bill 3.3666666666666667

In actionPerformed
Compiling tempReload01a.java
Waiting for completion
Compile complete
Executing theMethod
Hello Bill 2.525

Tested using J2SE 5.0 under Windows XP.
**********************************************************/
import java.net.*;
import java.io.*;
import java.lang.reflect.*;
import java.awt.event.*;
import javax.swing.*;

class Reload01 extends JFrame{
  
  File targetDir;//location of the file named Reload01a
  
  public static void main(String[] args){
    new Reload01().writeTheClassFile();
  }//end main
  //-----------------------------------------------------//
  
  Reload01(){//constructor
    setTitle("Copyright 2006, R.G.Baldwin");
    JButton button = new JButton("Recompile and Reload");
    getContentPane().add(button);
    
    button.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          System.out.println("nIn actionPerformed");

          try{
            //Now compile the new class file.  The compile
            // method returns true on a successful compile,
            // and false otherwise.
            boolean compileStatus = compile(
               "temp" + File.separator + "Reload01a.java");
      
            if(compileStatus){//If compile was successful.
              System.out.println("Compile complete");
      
              //The class has been defined and compiled.
              //Force the class named Reload01a to be
              // reloaded and get a Class object that
              // represents the reloaded version of the
              // class in the process.
              //Specify the name of the class and the
              // directory containing the class file that
              // is to be reloaded.
              Class loadedClass = 
                                reloadTheClass("Reload01a",
                                                targetDir);
              
              //Instantiate a new object of the class.
              Object obj = loadedClass.newInstance();
              
              //Get a reference to an object of type Method
              // that represents the instance method
              // belonging to the object.  The method
              // belonging to the object will be invoked
              // later by invoking the method named invoke
              // on the object of type Method.
              //The first parameter specifies the name of
              // the target method that will be invoked 
              // later.  In this case, the name is
              // theMethod.
              //The second parameter specifies that the
              // method named theMethod requires two
              // parameters, the first of type String and
              // the second of type double.
              Method methodObj = 
                 loadedClass.getDeclaredMethod(
                   "theMethod",
                   new Class[]{String.class,double.class});
              
              //Invoke the method named theMethod by
              // invoking the method named invoke on the
              // Method object, passing the reference to
              // the object and a reference to a parameter
              // array as parameters to the invoke method.
              //Cast the return value from Object to
              // String. Then display the return values.
              //When the target method requires primitive
              // parameters, they must be wrapped in an
              // object of the corresponding wrapper class
              // for passing to the invoke method.  They
              // are automatically unwrapped by the system
              // for passing to the target method.
              //Similarly, when the target method returns a
              // value of a primitive type, the invoke
              // method will return that value wrapped in
              // an object of the corresponding wrapper
              // class.
              String returnVal = (String)methodObj.invoke(
                   obj,
                   new Object[]{"Hello",new Double(10.1)});
              System.out.println(returnVal);
            }//end if on compileStatus
            else{
              System.out.println(
                               "Probable compiler error.");
            }//end else
          
          }catch(Exception ex){
            ex.printStackTrace();
          }//end catch

        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener
    
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(250,100);
    setVisible(true);
  }//end constructor
  //-----------------------------------------------------//

  //Write the source code for the file named Reload01a
  // into a subdirectory named temp. If the file
  // exists, it will be overwritten.  
  void writeTheClassFile(){
    try{
      //Create a File object that points to a directory
      // where the class file will reside.
      targetDir = new File(System.getProperty("user.dir")
               + File.separator + "temp" + File.separator);
        
      //If the directory doesn't exist, make it.
      if(!targetDir.exists()){
        targetDir.mkdir();
      }//end if
      
      //Get an output stream for writing the source code
      // for the new class file.
      DataOutputStream dataOut = new DataOutputStream(
             new FileOutputStream("temp" + File.separator +
                                        "Reload01a.java"));

      //Create the source code for the new class file.
      System.out.println("Writing the class file.");
      dataOut.writeBytes(
       
      "public class Reload01a{" +
        "public String theMethod(String str,double val){" +
          "System.out.println("Executing theMethod");" +
          "return str + " " + "Tom" + " " + val/3;" +
        "}" +
      "}"

      );//end writeBytes method
      
      dataOut.close();//Close the output file.

    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end writeTheClassFile
  //-----------------------------------------------------//

  //The purpose of this method is to re-load a class that
  // is specified by name as a String from a subdirectory
  // specified by a File parameter named dir.
  static Class reloadTheClass(String reloadableClass,
                              File dir){
    //Get a URL object that points to the directory 
    // containing the class file for the class that is
    // to be loaded.
    URL[] theUrl = null;
    try{
      //The compiled class file for the reloadable class is
      // stored in a directory that was specified by an
      // incoming parameter named dir of type File.
      //Following the recommendations in the Sun docs, 
      // convert the File object to a URI and convert the
      // URI to a URL.  Deposit the reference to the URL
      // object into an one-element array of type URL.
      URI uri = dir.toURI();                   
      URL url = uri.toURL();
      theUrl = new URL[]{url};
    }catch(Exception e){
      e.printStackTrace();
    }//end catch

    Class theClass = null;
    try {
      //Create a new class loader associated with the 
      // directory containing the reloadable class.
      ClassLoader classLoader = new URLClassLoader(theUrl);
  
      // Load the specified class, creating a Class object
      // that represents the class in the process.
      theClass = classLoader.loadClass(reloadableClass);
    }catch (Exception e){
      e.printStackTrace();
    }//end catch
    
    //Return a reference to the Class object that
    // represents the newly-loaded class.
    return theClass; 
  }//end reloadTheClass
  //-----------------------------------------------------//

  //Method to compile the java source code file. Returns
  // true if successful, false otherwise.
  private static boolean compile(String file)
                                        throws IOException{
    System.out.println("Compiling " + file);
    Process p = Runtime.getRuntime().exec("javac " + file);
    
    //Note:  Sometimes the method named waitFor hangs up
    // and fails to return when there is a compiler error.
    // The following code is designed to deal with that
    // problem.  This code allows five seconds for the
    // compilation to complete successfully and the
    // waitFor method to return.  If the waitFor method
    // fails to return within five seconds, the code
    // declares a "Compilation timeout error" and
    // terminates the compilation process, returning false
    // from the method to indicate a compiler error.
    // However, it doesn't terminate the program and the
    // user may correct the program and try again.
    
    Thread myTimer = new Thread(
                   new Timer(Thread.currentThread(),5000));
    //Start  Timer thread
    myTimer.start();
    
    System.out.println("Waiting for completion");
    
    try{
      p.waitFor();//wait for completion

      //The waitFor method has returned,
      if(myTimer.isAlive()){
        //Stop the timer.
        myTimer.interrupt();
      }//end if
    }catch(InterruptedException e){
      //The timer expired before the waitFor method
      // returned and interrupted the waitFor method.
      System.out.println("Compilation timeout error.");
      p.destroy();
      return false;
    }//end catch

    //The waitFor method returned in five seconds or less.

    //p.exitValue() other than 0 indicates a compiler
    // error.
    return p.exitValue() == 0;
  }//end method compile
  //-----------------------------------------------------//
}//end class Reload01
//=======================================================//

class Timer implements Runnable{
  Thread theCompilingThread;
  int delay;//time interval to sleep
  //-----------------------------------------------------//
  
  Timer(Thread theCompilingThread, int delay){//constructor
    this.theCompilingThread = theCompilingThread;
    this.delay = delay;
  }//end constructor
  //-----------------------------------------------------//
  
  //The significant functionality of all thread objects is
  // written into  run() method for the object.
  public void run(){
    try{
      Thread.currentThread().sleep(delay);
    }catch(InterruptedException e){
      //No action is required when this sleeping thread is
      // interrupted.
      return;
    }//end catch

    //Control is transferred to here when the sleeping
    // thread awakens naturally after the specified delay
    // period.  This means that the compilation process is
    // probably hung up.  Interrupt the compilation
    // process and terminate the run method.
    theCompilingThread.interrupt();
  }//end run method
}//end Timer class
//=======================================================//

Listing 35

Listing 36

/**********************************************************
File Reload02.java
Copyright 2006, R.G.Baldwin
Revised 03/02/06

This program demonstrates the use of dynamic class loading.

This version uses the invoke method of the Method class to
access and execute the methods belonging to an object of
a newly-loaded class.  See Reload03 for a version that
uses an interface to access and reload those methods.

When the program executes, it writes, compiles, loads, 
instantiates, and invokes methods on an object of a new 
class that is designed automatically according to input 
from the user.

This is a plotting program.  It is designed to create and 
to access a new class file named Reload02a and to plot 
three methods that are defined in that class.  The 
methods are named f1, f2, and f3.

The behavior of each of the three methods is specified in
text form by the user who enters equations in three 
corresponding text fields.

By modifying the equations, checking a check box, and 
clicking a button, the user can cause a new class to be 
created.

The new class contains three methods with behavior 
matching the equations entered by the user.  The three
methods are then plotted on a common set of Cartesian 
coordinates.  Once the methods are defined and plotted,
the user can modify many plotting parameters and cause the
methods to be plotted over and over with different
plotting parameters.

Once the user is satisfied with the plots of those three 
methods, the user can modify the equations and start 
over with three new equations without terminating the 
program or manually defining any new code.

The three methods named f1, f2, and f3 are plotted in 
black, red, and blue respectively.

A coordinate system with axes, tic marks, and labels is 
plotted in green.  The labels on the axes correspond to
the values at the extreme edges of the plotting surface.
The initial plotting range extends from -151 to +151 on
each dimension.  This causes points plotted within the 
range from -150 to +150 inclusive to be visible on the
plotting surface.

The plotting range on the positive or negative side of 
either dimension can easily be changed by the user.

The default size of the GUI is 475 pixels wide by 430 
pixels tall.  This size was chosen to satisfy the size
requirements of my publisher.  You may want to expand the
size in order to be able to view more detail.

The new class file named Reload02a.class is created in a 
subdirectory of the current directory named temp.  If the 
subdirectory doesn't exist, it will be created.  

CAUTION:  If a class file having the same name already 
exists in that subdirectory, it will be overwritten.

The program provides three text fields into which the user 
enters equations expressed in java syntax.  For example, 
the following equation causes the value of y to be plotted 
on the vertical as a function of x on the horizontal axis.
(Note that the semicolon is required.)

y = 100.0*(cos(x/10.0)*sin(x/11.0));

In this case, the cos and sin methods of the Math class are
used to evaluate the equation.  All computations are 
performed as type double.  Then the results are converted 
to integer values for plotting.

For the student with more advanced Java programming 
knowledge, the text entered into the text field can be
more advanced.  For example, the following will plot as 
a series of spikes.

if(x%10 == 0){y = 50;}else{y = -50;}

The program uses a static import directive for the Math 
class.  Therefore, it is not necessary to qualify methods 
from that class with the name of the class as in 
Math.cos(...).  All that is required is cos(...).  Thus, 
all of the methods of the Math class are directly available
to be included in the equations.

If it is desired to make use of other classes, it is 
necessary to fully qualify the references to those classes
using their package name.  Here is an example of such 
qualification, which has no purpose other than to 
illustrate the syntax involved:

y =  x * ((new java.util.Date().getTime())/
(new java.util.Date().getTime()));

(Note that line break was manually inserted in the middle
of the above equation.)

As it turns out, this is a very complicated way to express
the equation for a straight line, so the plot is a straight
line.

The three text fields described above are located in the 
NORTH portion of the GUI.  In addition, a check box is 
also located in the NORTH of the GUI.  There is a Graph 
button at the bottom of the GUI.  If the check box is 
checked when the Graph button is clicked, a new class that 
matches the equations that have been entered into the text
fields described above will be created, compiled, and 
loaded before the equations are plotted.  If the check box 
has not been checked, the equations in the existing class 
will be replotted, potentially with different plotting 
parameters.  (You can change the plotting parameters by
changing the values in text fields at the bottom of the 
GUI.)

The program plots the following three equations by default 
at startup to provide a visual confirmation that the 
program is working, and also to illustrate the syntax that 
should be used to enter equations into the text fields:

y = (x*x*x + 40*x*x + 100*x -6000)/100;

y = x * ((new java.util.Date().getTime())/
  (new java.util.Date().getTime()));
  
y = 100.0*(cos(x/10.0)*sin(x/11.0));

(Note that a line break was manually entered into the
middle equation in the above list.)

The first equation shown above is a cubic function with 
roots at values for x of -20, -30, and +10.

The second equation illustrates how classes other than the 
Math class can be incorporated into equations.

The third equation illustrates how methods of the Math 
class can be incorporated into equations.

In addition to the text fields at the top of the GUI as 
described above, this program also provides the following 
text fields at the bottom of the GUI for user input.  The 
program also provides a button labeled Graph at the bottom 
of the GUI.  The combination of these text fields and the 
button make it possible for the user to adjust the 
plotting parameters and to replot the graph as many times 
with as many plotting-parameter settings as may be needed.

xMin = minimum x-axis value
xMax = maximum x-axis value
yMin = minimum y-axis value
yMax = maximum y-axis value
xTicInt = tic interval on x-axis
yTicInt = tic interval on y-axis
xCalcInc = calculation interval (should normally be set
 to 1.0)

The user can modify any of these parameters and then click 
the Graph button to cause the three methods to be 
re-plotted according to the new parameters.

In order to plot new and different equations, it is only 
necessary to enter the new equations into the text fields
at the top of the GUI, check the check box at the top of 
the GUI, and click the Graph button.

If the compilation process fails to return within five 
seconds, the compilation process is aborted and a 
"Compilation timeout error" is declared.  It is also 
possible that the compilation process could return within 
five seconds with a compilation error.  In either case, a 
"Probable compile error" will be declared.  The program is 
not terminated as a result of a probable compile error, so 
you can fix the problem in the source code and try again
by clicking the button.

The program was tested using J2SE 5.0 under Windows XP.  
This program uses static import directives.  Therefore, it 
requires J2SE 5.0 or later to compile and run successfully.
**********************************************************/
import java.net.*;
import java.io.*;
import java.lang.reflect.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

class Reload02{
  public static void main(String[] args){
    //Instantiate a GUI object.
    new ScienceGraph();
  }//end main
}//end class Reload02
//=======================================================//

class ScienceGraph extends JFrame 
                                 implements ActionListener{

  //Define plotting parameters and their default values.
  double xMin = -151.0;
  double xMax = 151.0;
  double yMin = -151.0;
  double yMax = 151.0;

  double xTicInt = 10.0;//Tic interval
  double yTicInt = 10.0;//Tic interval

  //Calculation interval along x-axis.  This should
  // normally be left at 1.0 unless you have a special need
  // to use more coarse sampling.
  double xCalcInc = 1.0;

  //Tic lengths
  double xTicLen = (yMax-yMin)/50;
  double yTicLen = (xMax-xMin)/50;

  //Text fields for plotting parameters
  JTextField xMinTxt = new JTextField("" + xMin,6);
  JTextField xMaxTxt = new JTextField("" + xMax,6);
  JTextField yMinTxt = new JTextField("" + yMin,6);
  JTextField yMaxTxt = new JTextField("" + yMax,6);
  JTextField xTicIntTxt = new JTextField("" + xTicInt,5);
  JTextField yTicIntTxt = new JTextField("" + yTicInt,5);
  JTextField xCalcIncTxt = new JTextField("" + xCalcInc,4);
  
  //Text fields for methods with default equations
  // inserted.
  JTextField f1Txt = new JTextField(
             "y = (x*x*x + 40*x*x + 100*x -6000)/100;",40);
  JTextField f2Txt = new JTextField(
            "y = (x * ((new java.util.Date().getTime())/" +
                 "(new java.util.Date().getTime())));",40);
  JTextField f3Txt = new JTextField(
                "y = 100.0*(cos(x/10.0)*sin(x/11.0));",40);
  
  //Check box used to force rewrite, recompile, and reload
  // of the class.
  JCheckBox reloadCkBox = new JCheckBox(
              "Rewrite, Recompile, and Reload the Class " +
              "using the Above Equations",false);

  //Panels to contain a label and a text field
  JPanel pan0 = new JPanel();
  JPanel pan1 = new JPanel();
  JPanel pan2 = new JPanel();
  JPanel pan3 = new JPanel();
  JPanel pan4 = new JPanel();
  JPanel pan5 = new JPanel();
  JPanel pan6 = new JPanel();
  JPanel pan7 = new JPanel();
  JPanel pan8 = new JPanel();
  JPanel pan9 = new JPanel();

  //Misc instance variables
  int frmWidth = 475;
  int frmHeight = 430;
  int width;
  int height;

  //A reference to a newly-loaded class.
  Class loadedClass = null;

  //Plot is drawn on this canvas
  MyCanvas canvas;
  //-----------------------------------------------------//
  
  ScienceGraph(){//constructor
    System.out.println(
           "Write, compile, and load initial class file.");

    //Write, compile, and load the new class based on the
    // default equations in the text fields.  Returns 
    // true on success, false on failure to compile.
    boolean compileStatus = updateClass();
    
    if(!compileStatus){
      System.out.println(
                        "Unable to compile initial class");
    }//end if

    //Now build the GUI.  
    canvas = new MyCanvas();
    
    JPanel southPanel = new JPanel();
    //Set the layout manager to GridLayout with 
    // 2 rows x 4 cols
    southPanel.setLayout(new GridLayout(2,4));
                  
    JPanel northPanel = new JPanel();
    northPanel.setLayout(new GridLayout(4,1));

    //Button for replotting the graph
    JButton graphBtn = new JButton("Graph");
    graphBtn.addActionListener(this);

    //Populate each small panel with a label
    // and a text field
    pan0.add(new JLabel("xMin"));
    pan0.add(xMinTxt);

    pan1.add(new JLabel("xMax"));
    pan1.add(xMaxTxt);

    pan2.add(new JLabel("yMin"));
    pan2.add(yMinTxt);

    pan3.add(new JLabel("yMax"));
    pan3.add(yMaxTxt);

    pan4.add(new JLabel("xTicInt"));
    pan4.add(xTicIntTxt);

    pan5.add(new JLabel("yTicInt"));
    pan5.add(yTicIntTxt);

    pan6.add(new JLabel("xCalcInc"));
    pan6.add(xCalcIncTxt);
    
    pan7.add(new JLabel("f1"));
    pan7.add(f1Txt);
    
    //Make the color of the labels that identify the 
    // equations for f2 and f3 match the color that will
    // be used to plot those two equations.  f1 is 
    // already black so I didn't need to change its color.
    JLabel f2Label = new JLabel("f2");
    f2Label.setForeground(Color.RED);
    pan8.add(f2Label);
    pan8.add(f2Txt);

    JLabel f3Label = new JLabel("f3");
    f3Label.setForeground(Color.BLUE);
    pan9.add(f3Label);
    pan9.add(f3Txt);

    //Add the populated panels, the button, and the check
    // box to the control panels for the South and North
    // locations.
    southPanel.add(pan0);
    southPanel.add(pan1);
    southPanel.add(pan2);
    southPanel.add(pan3);
    southPanel.add(pan4);
    southPanel.add(pan5);
    southPanel.add(pan6);
    southPanel.add(graphBtn);
    
    northPanel.add(pan7);
    northPanel.add(pan8);
    northPanel.add(pan9);
    northPanel.add(reloadCkBox);

    //Add the sub-assemblies to the frame.  Set the frame's
    // location, size, and title, and make it visible.
    getContentPane().add(northPanel,BorderLayout.NORTH);
    getContentPane().add(southPanel,BorderLayout.SOUTH);
    getContentPane().add(canvas,BorderLayout.CENTER);
    canvas.setBackground(Color.WHITE);
    setBounds(0,0,frmWidth,frmHeight);
    setTitle(
       "ScienceGraph, Copyright 2006, Richard G. Baldwin");
                 
    setVisible(true);//Make the GUI visible
    
    //Set to exit on X-button
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    //Get and save the size of the plotting surface
    width = canvas.getWidth();
    height = canvas.getHeight();
    
    //Cycle visibility once to force the initial methods
    // to be displayed.  There must be a better way to
    // accomplish this.  Without this, the initial
    // methods are not displayed at startup.  This
    // appears to be the result of a timing problem
    // involving compilation, etc.
    setVisible(false);
    setVisible(true);

  }//end constructor
  //-----------------------------------------------------//

  //Method to compile the java source code file. Returns
  // true if successful, false otherwise.
  private static boolean compile(String file)
                                        throws IOException{
    System.out.println("Compiling " + file);
    Process p = Runtime.getRuntime().exec("javac " + file);
    
    //Note:  Sometimes the method named waitFor hangs up
    // and fails to return when there is a compiler error.
    // The following code is designed to deal with that
    // problem.  This code allows five seconds for the
    // compilation to complete successfully and the
    // waitFor method to return.  If the waitFor method
    // fails to return within five seconds, the code
    // declares a "Compilation timeout error" and
    // terminates the compilation process, returning false
    // from the method to indicate a compiler error.
    // However, it doesn't terminate the program and the
    // user may correct the program and try again.
    
    Thread myTimer = new Thread(
                   new Timer(Thread.currentThread(),5000));
    //Start  Timer thread
    myTimer.start();
    
    System.out.println("Waiting for completion");
    
    try{
      p.waitFor();//wait for completion

      //The waitFor method has returned,
      if(myTimer.isAlive()){
        //Stop the timer.
        myTimer.interrupt();
      }//end if
    }catch(InterruptedException e){
      //The timer expired before the waitFor method
      // returned and interrupted the waitFor method.
      System.out.println("Compilation timeout error.");
      p.destroy();
      return false;
    }//end catch

    //The waitFor method returned in five seconds or less.

    //p.exitValue() other than 0 indicates a compiler
    // error.
    return p.exitValue() == 0;
  }//end method compile
  //-----------------------------------------------------//
  
  //Method to write, compile, and load the new class.
  boolean updateClass(){
    boolean compileStatus = false;
  
    try{
      //Create a File object that points to a directory
      // where the class file will reside.
      File targetDir = new File(
                System.getProperty("user.dir")
                + File.separator +"temp" + File.separator);
        
      //If the directory doesn't exist, make it.
      if(!targetDir.exists()){
        targetDir.mkdir();
      }//end if
      
      //Get an output stream for writing the source code
      // for the new class file.
      DataOutputStream dataOut = new DataOutputStream(
             new FileOutputStream("temp" + File.separator +
                                        "Reload02a.java"));

      //Get the equations from the text fields.
      String eq1 = f1Txt.getText();
      String eq2 = f2Txt.getText();
      String eq3 = f3Txt.getText();

      //Create the source code for the new class file.
      dataOut.writeBytes(

        "import static java.lang.Math.*;" +
        "public class Reload02a{" +
          "public double f1(double x)"+
          "{" +
            "double y = 0.0;" + 
            eq1 + 
            "return y;" +
          "}" +
          
          "public double f2(double x)"+
          "{" +
            "double y = 0.0;" + 
            eq2 + 
            "return y;" +
          "}" +          
          
          "public double f3(double x)"+
          "{" +
            "double y = 0.0;" + 
            eq3 + 
            "return y;" +
          "}" +
        "}"
      );//end writeBytes method
      
      dataOut.close();//Close the output file.

      //Now compile the new class file
      compileStatus = compile(
               "temp" + File.separator + "Reload02a.java");

      if(compileStatus){
        System.out.println("Compile complete");

        //The class has been defined and compiled.  Now
        // force it to be loaded.
        //Get a URL object that points to the directory 
        // containing the class file.  A File object that
        // points to that directory was created earlier.
        //The compiled class file for the reloadable class 
        // is stored in a directory that is pointed to by a
        // reference variable of type File named targetDir.
        //Following the recommendations in the Sun docs, 
        // convert the File object to a URI and convert the
        // URI to a URL.  Deposit the reference to the URL
        // object into an one-element array of type URL.
        URI uri = targetDir.toURI();                   
        URL url = uri.toURL();
        URL[] theUrl = new URL[]{url};
  
        //Create a new class loader associated with the 
        // directory (URL) containing the reloadable class.
        ClassLoader classLoader = 
                                new URLClassLoader(theUrl);
    
        // Load the specified class, creating a Class
        // object that represents the class in the process.
        loadedClass = classLoader.loadClass("Reload02a");
    
      }else{
        System.out.println("Probable compile error");
      }//end else

    }catch (Exception e){
      e.printStackTrace();
      System.exit(0);
    }//end catch
    
    return compileStatus;
  }//end updateClass method
  //-----------------------------------------------------//

  //This event handler is registered on the JButton to
  // cause the methods to be replotted.
  public void actionPerformed(ActionEvent evt){
    System.out.println("nExecuting actionPerformed");
    //Set plotting parameters using data from the text
    // fields.
    xMin = Double.parseDouble(xMinTxt.getText());
    xMax = Double.parseDouble(xMaxTxt.getText());
    yMin = Double.parseDouble(yMinTxt.getText());
    yMax = Double.parseDouble(yMaxTxt.getText());
    xTicInt = Double.parseDouble(xTicIntTxt.getText());
    yTicInt = Double.parseDouble(yTicIntTxt.getText());
    xCalcInc = Double.parseDouble(xCalcIncTxt.getText());

    //Calculate new values for the length of the tic marks
    // on the axes.
    xTicLen = (yMax-yMin)/50;
    yTicLen = (xMax-xMin)/50;

    boolean compileStatus = true;
    
    if(reloadCkBox.isSelected()){
      //Clear the checkbox, recompile, and reload.
      reloadCkBox.setSelected(false);
      compileStatus = updateClass();
    }//end if on reloadCkBox.isSelected()
    
    if(compileStatus){
      //Repaint the plotting surface
      canvas.repaint();
    }else{
      System.out.println("Unable to compile new class");
    }//end else
  }//end actionPerformed
  //-----------------------------------------------------//
  
  //This is an inner class, which is used to override the
  // paint method on the plotting surface.
  class MyCanvas extends Canvas{
    //Factors to convert from double values to integer
    // pixel locations.
    double xScale;
    double yScale;
    //---------------------------------------------------//
    
    //Override the paint method
    public void paint(Graphics g){
  
      //Calculate the scale factors
      xScale = width/(xMax-xMin);
      yScale = height/(yMax-yMin);
  
      //Set the origin based on the minimum values in
      // x and y
      g.translate((int)((0-xMin)*xScale),
                 (int)((0-yMin)*yScale));
  
      drawAxes(g);//Draw the axes

      //Don't try to plot if the class has not been
      // successfully loaded.
      if(loadedClass != null){
        try{
          //Instantiate a new object of the class
          Object obj = loadedClass.newInstance();
          
          //Get a reference to an object of type Method
          // that represents the instance method belonging
          // to the object.  The method belonging to the
          // object will be invoked later by invoking the
          // method named invoke on the object of type
          // Method.
          //The first parameter specifies the name of the
          // method that will be invoked.  In this case,
          // the name is f1.
          //The second parameter specifies that the method
          // named f1 requires one parameter of type
          // double.
          Method methodObj = loadedClass.getDeclaredMethod(
                                "f1",
                                new Class[]{double.class});
          
          //Invoke the method named f1 by invoking the
          // method named invoke on the Method object
          // passing the object's reference and a
          // reference to a parameter array as parameters.
          // Cast the return value from Object to Double.
          // Extract the double value from the returned
          // value and save it in tempY.
          //Note that the double parameter value is wrapped
          // in an object of type Double.  It will be
          // unwrapped automatically and delivered to the
          // f1 method as type double.
          //Similarly, when the method named f1 returns a
          // value of type double, it is automatically
          // wrapped in an object of type Double and 
          // returned by the invoke method as type Object.
          // Thus, it is necessary to cast the return 
          // value to type Double and extrace the double
          // value that it encapsulates.
          
          //Plot the first method in black.
          g.setColor(Color.BLACK);
          double xVal = xMin;
          int oldX = getX(xVal);
          
          double tempY = ((Double)methodObj.invoke(obj,new 
                Object[]{new Double(xVal)})).doubleValue();
          int oldY = getY(tempY);
          
          //Enter a while loop using the same process to
          // invoke the method named f1 several times in
          // succession and plotting returned value.
          while(xVal < xMax){
            tempY = ((Double)methodObj.invoke(obj,new 
                Object[]{new Double(xVal)})).doubleValue();
            int yVal = getY(tempY);
            int x = getX(xVal);
            g.drawLine(oldX,oldY,x,yVal);
            xVal += xCalcInc;
            oldX = x;
            oldY = yVal;
          }//end while loop
          
          //Now do the same thing for the other two
          // methods named f2 and f3.
          //Get a Method object that represents the
          // method named f2.
          methodObj = loadedClass.getDeclaredMethod(
                                "f2",
                                new Class[]{double.class});
          //Plot the second method in red
          g.setColor(Color.RED);
          xVal = xMin;
          oldX = getX(xVal);
          
          tempY = ((Double)methodObj.invoke(obj,new 
                Object[]{new Double(xVal)})).doubleValue();
          oldY = getY(tempY);
          
          while(xVal < xMax){
            tempY = ((Double)methodObj.invoke(obj,new 
                Object[]{new Double(xVal)})).doubleValue();
            int yVal = getY(tempY);
            int x = getX(xVal);
            g.drawLine(oldX,oldY,x,yVal);
            xVal += xCalcInc;
            oldX = x;
            oldY = yVal;
          }//end while loop

          //Get a Method object that represents f3.
          methodObj = loadedClass.getDeclaredMethod(
                                "f3",
                                new Class[]{double.class});
          //Plot the third method in BLUE
          g.setColor(Color.BLUE);
          xVal = xMin;
          oldX = getX(xVal);
          
          tempY = ((Double)methodObj.invoke(obj,new 
                Object[]{new Double(xVal)})).doubleValue();
          oldY = getY(tempY);
          
          while(xVal < xMax){
            tempY = ((Double)methodObj.invoke(obj,new 
                Object[]{new Double(xVal)})).doubleValue();
            int yVal = getY(tempY);
            int x = getX(xVal);
            g.drawLine(oldX,oldY,x,yVal);
            xVal += xCalcInc;
            oldX = x;
            oldY = yVal;
          }//end while loop
          
        }catch(Exception e){
          e.printStackTrace();
          System.exit(0);
        }//end catch
  
      }else{
        System.out.println("Class was not loaded");
      }//end if-else on loadedClass
  
    }//end overridden paint method
    //---------------------------------------------------//
  
    //Method to draw axes with tic marks and labels in the
    // color GREEN
    void drawAxes(Graphics g){
      g.setColor(Color.GREEN);
  
      //Lable left x-axis and bottom y-axis.  These are
      // the easy ones.  Separate the labels from the ends
      // of the tic marks by two pixels.
      g.drawString(
              "" + (int)xMin,getX(xMin),getY(xTicLen/2)-2);
      g.drawString(
              "" + (int)yMin,getX(yTicLen/2)+2,getY(yMin));
  
      //Label the right x-axis and the top y-axis.  These
      // are the hard ones because the position must be 
      // adjusted by the font size and the number of
      // characters.
      //Get the width of the string for right end of x-axis
      // and the height of the string for top of y-axis.
      //Create a string that is an integer representation
      // of the label for the plus end of the x-axis.  Then
      // get a character array that represents the string.
      int xMaxInt = (int)xMax;
      String xMaxStr = "" + xMaxInt;
      char[] array = xMaxStr.toCharArray();
  
      //Get a FontMetrics object that can be used to get
      // the size of the string in pixels.
      FontMetrics fontMetrics = g.getFontMetrics();
      //Get a bounding rectangle for the string
      Rectangle2D r2d = fontMetrics.getStringBounds(
                                   array,0,array.length,g);
      //Get the width and the height of the bounding
      // rectangle.  The width is the width of the label on
      // the positive x-axis.  The height applies to all
      // the labels, but is needed specifically for the
      // label at the positive end of the y-axis.
      int labWidth = (int)(r2d.getWidth());
      int labHeight = (int)(r2d.getHeight());
  
      //Label the positive x-axis and the positive y-axis
      // using the width and height from above to position
      // the labels.  These labels apply to the very ends
      // of the axes at the edge of the plotting surface.
      g.drawString("" + (int)xMax,getX(xMax)-labWidth,
                                        getY(xTicLen/2)-2);
      g.drawString("" + (int)yMax,getX(yTicLen/2)+2,
                                     getY(yMax)+labHeight);
  
      //Draw the axes
      g.drawLine(getX(xMin),getY(0.0),
                                     getX(xMax),getY(0.0));
  
      g.drawLine(getX(0.0),getY(yMin),
                                     getX(0.0),getY(yMax));
  
      //Draw the tic marks on axes
      xTics(g);
      yTics(g);
    }//end drawAxes
    //---------------------------------------------------//
  
    //Method to draw tic marks on x-axis
    void xTics(Graphics g){
      double xDoub = 0;
      int x = 0;
      int topEnd = getY(xTicLen/2);
      int bottomEnd = getY(-xTicLen/2);
  
      //Loop and draw a series of short lines to serve as
      // tic marks.
      //Begin with the positive x-axis moving to the right
      // from zero.
      
      while(xDoub < xMax){
        x = getX(xDoub);
        g.drawLine(x,topEnd,x,bottomEnd);
        xDoub += xTicInt;
      }//end while
  
      //Now do the negative x-axis moving to the left from
      // zero
      xDoub = 0;
      while(xDoub > xMin){
        x = getX(xDoub);
        g.drawLine(x,topEnd,x,bottomEnd);
        xDoub -= xTicInt;
      }//end while
  
    }//end xTics
    //---------------------------------------------------//
  
    //Method to draw tic marks on y-axis
    void yTics(Graphics g){
      double yDoub = 0;
      int y = 0;
      int rightEnd = getX(yTicLen/2);
      int leftEnd = getX(-yTicLen/2);
  
      //Loop and draw a series of short lines to serve as
      // tic marks. Begin with the positive y-axis moving
      // up from zero.
      while(yDoub < yMax){
        y = getY(yDoub);
        g.drawLine(rightEnd,y,leftEnd,y);
        yDoub += yTicInt;
      }//end while
  
      //Now do the negative y-axis moving down from zero.
      yDoub = 0;
      while(yDoub > yMin){
        y = getY(yDoub);
        g.drawLine(rightEnd,y,leftEnd,y);
        yDoub -= yTicInt;
      }//end while
  
    }//end yTics
    //---------------------------------------------------//
  
    //This method translates and scales a double y value
    // to plot properly in the integer coordinate system.
    // In addition to scaling, it causes the positive
    // direction of the y-axis to be from bottom to top.
    int getY(double y){
      double yDoub = (yMax+yMin)-y;
      int yInt = (int)(yDoub*yScale);
      return yInt;
    }//end getY
    //---------------------------------------------------//
  
    //This method scales a double x value to plot properly
    // in the integer coordinate system.
    int getX(double x){
      return (int)(x*xScale);
    }//end getX
    //---------------------------------------------------//
  }//end inner class MyCanvas
}//end class ScienceGraph
//=======================================================//

class Timer implements Runnable{
  Thread theCompilingThread;
  int delay;//time interval to sleep
  //-----------------------------------------------------//
  
  Timer(Thread theCompilingThread, int delay){//constructor
    this.theCompilingThread = theCompilingThread;
    this.delay = delay;
  }//end constructor
  //-----------------------------------------------------//
  
  //The significant functionality of all thread objects is
  // written into  run() method for the object.
  public void run(){
    try{
      Thread.currentThread().sleep(delay);
    }catch(InterruptedException e){
      //No action is required when this sleeping thread is
      // interrupted.
      return;
    }//end catch

    //Control is transferred to here when the sleeping
    // thread awakens naturally after the specified delay
    // period.  This means that the compilation process is
    // probably hung up.  Interrupt the compilation
    // process and terminate the run method.
    theCompilingThread.interrupt();
  }//end run method
}//end Timer class
//=======================================================//

Listing 36

Copyright 2006, Richard G. Baldwin.  Reproduction in whole or in part in any
form or medium without express written permission from Richard Baldwin is
prohibited.

About the author

Richard Baldwin is a
college professor (at Austin Community College in Austin, TX) and private
consultant whose primary focus is a combination of Java, C#, and XML. In
addition to the many platform and/or language independent benefits of Java and
C# applications, he believes that a combination of Java, C#, and XML will become
the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he
frequently provides onsite training at the high-tech companies located in and
around Austin, Texas.  He is the author of Baldwin’s Programming
Tutorials, which have gained a
worldwide following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of
practical experience in Digital Signal Processing (DSP).  His first job after he
earned his Bachelor’s degree was doing DSP in the Seismic Research Department of
Texas Instruments.  (TI is still a world leader in DSP.)  In the following
years, he applied his programming and DSP expertise to other interesting areas
including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has
many years of experience in the application of computer technology to real-world
problems.

[email protected]

Latest Posts

Related Stories