August 21, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Developing Ajax Web Applications using ThinWire and Java

  • August 22, 2006
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2500


Preface

This is the first in a series of lessons designed to teach you how to develop rich web applications using ThinWire and Java.

What in the world is ThinWire?

I gave you a brief introduction to ThinWire in my earlier lesson entitled Deployment of Web Applications in Jakarta Apache Tomcat 5.  In this, and subsequent lessons, I will teach you a great deal more about ThinWire.

Here is a quotation from the ThinWire web site that helps to explain what ThinWire is.

"ThinWire™ is a development framework that allows you to easily build applications for the web that look and feel like the desktop applications you're familiar with."

Partial list of interesting features

Here is an edited list of interesting features that I extracted from the ThinWire website.  (Note that I used boldface to highlight some features that I find particularly interesting.)

  • Development framework for architecting Rich Internet Applications (RIA) that utilize Ajax techniques
  • Familiar event-driven GUI programming model
  • Develop exclusively in server-side language only
    • Never use HTML, CSS, or JavaScript again!
    • Program exclusively in Java!
    • Server-Side execution of all application logic
  • Rich Set of Complex Widget Components
    • Menu with image & shortcut-key support
    • Grid with multiple sortable columns
    • TextField & DropDown with real-time edit masking
    • Editable DropDown with multiple sortable columns
    • Tree control with image support
    • TabFolder with image support
    • Push buttons with images!
  • Content is sent incrementally and only when needed
  • All Major Browsers Supported
  • Deploy on any Java Servlet Container

What does this author think of the ThinWire framework?

For those who know how to write stand-alone, event-driven Java/OOP applications, this is the easiest way that I know of to develop rich web applications.

Why will I be hedging?

From time to time in this series of lessons, you may detect that I am unwilling to make a definitive statement about some ThinWire topic.  I will sometimes use phrases line "I think" and "I believe that something or another is true.

My unwillingness to make a definitive statement about this or that will derive from the fact that many of the details regarding the ThinWire API have not yet been published.  Much of what I will be telling you in this series of tutorials is based on what I have learned through experimentation.

ThinWire is very new

What I believe was the first announcement of the release of the technology appeared on the CCS website with a date of June 15, 2006.  That announcement read as follows:

"6.15.2006  CCS announces the open source release of its new ThinWire(TM) technology.

This advanced new technology platform enables highly secure solutions within a zero footprint environment. The technology is now available for general use within any web based application under open source terms."

Somehow I stumbled onto that announcement, and was immediately impressed with the capabilities and the potential of the ThinWire framework.  However, that was less than two months before the date on which I am writing this first tutorial lesson in the series, and there are some things that I am still unsure of.

An adventure into a new technology

If you join me in this ThinWire adventure, we will be plowing new ground until such time as the ThinWire documentation catches up with the ThinWire capability.

Normally, I wouldn't get involved with a new product at such an early stage in its development.  Given that, my willingness to become deeply involved in the ThinWire framework technology is an indication of my extremely favorable impression of its capabilities.

Written exclusively in Java

As mentioned above, with ThinWire, all of the code for a web application is written exclusively in Java.  You don't have to contend with JavaScript or HTML.  Furthermore, you don't even have to contend with the complexity of Java servlets.  The code is written like an event-driven standalone desktop application.  All of the complexity of converting the application so that it will run as a server-side servlet-based web application is handled by the ThinWire framework.

Deploy on any Java Servlet Container

The last item in the above list of features is a double-edged sword.  JavaHeads like me would argue that this is clearly a useful feature because it means that web applications developed using ThinWire can be deployed on a variety of competing servers from different vendors.

Critics, on the other hand, would probably turn the statement around and complain that a web application developed using ThinWire can only be deployed on servers that support Java servlets.  Clearly, this is a question of whether the glass is half full or half empty.

So, what is the ThinWire framework?

At the minimum, the ThinWire framework consists of the set of three JAR files shown in Figure 1, plus a set of javadocs and a template of a file named web.xml:

 22,379 commons-fileupload.jar
  5,521 retroweaver-rt.jar
313,731 thinwire.jar
Figure 1

(The list of JAR files in Figure 1 shows the size of each file in bytes in addition to the names of the Jar files.  You must copy these JAR files onto your computer and adjust your classpath at compile time so that they are at the beginning of the classpath.  I will explain how I do this later.)

No install operation required

No complex install operation is required for the ThinWire framework.  You simply download a zip file that contains the following items:

  • A LICENSE.TXT file and a ReadMe.txt file,
  • The jar files listed in Figure 1,
  • The javadocs,
  • A template for a web.xml file required for deployment in the servlet container.
  • Some demo programs, and
  • A scaled-down Tomcat server.

(As of the date of this writing, ThinWire version 1.2 beta is available for free downloading.  I expect that by the time this tutorial makes its way through the publication queue and into publication, a later version will have been released.)

Just extract the pieces that you need

For installation, you simply open the zip file and extract the pieces that you need.  As a minimum, you will need the set of jar files, the web.xml file template, and the javadocs.  (Of course, you will want to read the license file and the ReadMe file.)

A servlet container is required for application testing

If you already have access to a servlet container such as Tomcat, you won't need the scaled-down Tomcat server, so you won't even need to extract it from the zip file.  If not, you can extract and use the scaled-down Tomcat server for application development and testing until you gain access to a full-featured servlet container.

(You can download a full Jakarta Tomcat server for free from a variety of locations on the web, including a pre-configured version from Marty Hall's web site.  See my earlier lesson entitled Getting Started with Jakarta Tomcat, Servlets, and JSP.)

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.

Building the Web Application

I suspect that the burning question on everyone's mind is:

"How do I use the ThinWire framework along with a Java source code file to build a web application?"

So, I'm going to give you the bottom line at the beginning.

(I wasted quite a lot of time at the beginning searching the ThinWire framework looking for something like a "build file" that could be used to build the web application.  When I finally discovered how it is done, I experienced one of those WOW! moments.)

The bottom line

Once you have a correctly written Java application all that is necessary to build the web application is to:

Compile your Java application using Sun's javac compiler with the three ThinWire JAR files at the beginning of the classpath.

That's all there is to it.  It's that simple.  (I will explain later how I handle the classpath issue.)

If the compilation is successful, it will produce all the class files you need to deploy your web application in a servlet container.  All you will need is those class files and a very simple web.xml file that I will also explain later (see Listing 1).

What is a correctly written Java application?

A correctly written Java application in the context of ThinWire is one that was written taking the ThinWire programming API into account.  In particular, this means that in those cases where the ThinWire API contains classes, interfaces, and methods with names that duplicate the names of classes, interfaces, and methods in the standard Java library, you must write your code to be consistent with the ThinWire API.

For example, both the ThinWire API and Sun's J2SE 5.0 API contain a class named Button.  However, the Button class in the ThinWire API provides a feature that is not available with the Button class in the J2SE 5.0 API.  In particular, you can put an image on a ThinWire Button, but you can't put an image on a J2SE 5.0 Button.

To implement this added feature, there is an overloaded Button constructor in the ThinWire API that allows you to pass the name of an image file as a parameter to the constructor.  This causes the image to appear on the button, something that isn't possible with a standard Sun Button.

Dealing with the classpath

The easiest way that I have found to deal with the classpath issue mentioned above is to use the -cp switch of Sun's javac compiler.  The easiest and most reliable way to do that is to execute a batch file containing the statement needed to compile my Java application.  For example, here is the statement that I would use in the batch file to compile the Java source code for the web application named WebApp003.

javac -cp 
C:\...\webapps\WebApp003\WEB-INF\lib\thinwire.jar;
C:\...\webapps\WebApp003\WEB-INF\lib\
retroweaver-rt.jar;
C:\...\webapps\WebApp003\WEB-INF\lib\
commons-fileupload.jar 
WebApp003.java

The ellipses (...) indicate the path from the root of my C drive to my webapps development directory.

Must be a single-line command

Keep in mind that even though I entered line breaks for display purposes in the version of the command shown above, this is a single command so it would appear on a single line if the display space would accommodate such a long line.  When you use your text editor to create your batch file, you must put it all on a single line.

Location of the batch file

My batch file is named compile.bat and it is located in the same directory as the Java source file being compiled.  (See Figure 5.)  Then I simply double-click on the batch file to perform the compilation.

It will be useful for you to compare this command with the directory structure shown in Figure 5 to see how it fits into the overall directory structure.

Preview

In this lesson, I will present and explain the Java source code for four different sample web applications that I have developed using ThinWire.  The following list identifies the main features illustrated by each application.  The detailed explanation of the source code that I will provide later will identify other features.

  • WebApp003 - Illustrates PropertyChangeEvent handling on a Frame object.  Also illustrates the use of a TextArea object in the user interface.
  • WebApp004 - Illustrates PropertyChangeEvent handling on a TextArea object and ActionEvent handling on a Button.
  • WebApp006 - Illustrates the ability to use the power of server-side Java servlets to provide a computational service to a web client as a web application.  Also illustrates control over the style of GUI components and images on buttons.
  • WebApp008 - Same as WebApp006 but uses a better programming style.

Before getting into the detailed discussion of the applications, I will show and explain screen shots of the user interface for each application as rendered in an Internet Explorer web browser.

WebApp003

The user interface for this web application is shown in Figure 2.


Figure 2

As mentioned earlier, this application illustrates PropertyChangeEvent handling on a Frame object.  In addition, the application illustrates the use of a TextArea object in the user interface.

Instantiating a Frame object

As you will see later when I explain the Java code, the instantiation of a Frame object in ThinWire differs from the way that a Frame object is instantiated in J2SE 5.0.  Further, the visual manifestation of the Frame object is also different in ThinWire.

Visual manifestation of the Frame object

A Frame object that is instantiated using J2SE 5.0 is a top-level container with decorations that can be placed directly on the computer desktop.

A Frame object instantiated under ThinWire takes over the browser window in the browser being used to communicate with the web application.

The entire browser window (not just the client area of the browser) is contained in the Frame object.  The title of the Frame object becomes the browser's title as shown at the top of the image in Figure 2.  When rendered in a Firefox tabbed browser, the title becomes the title for the tab.

What happens when you manually resize the browser?

Because the entire browser window is contained in the Frame object., manually resizing the browser also resizes the Frame object.

The behavior of the application

The primary behavior of the application named WebApp003 is to listen for PropertyChangeEvents on the Frame object, with special emphasis on the width and height properties.  When a PropertyChangeEvent attributed to one of those two properties is fired by the Frame, the program gets and displays the new size of the Frame object in a TextArea object.

Changing the size of the browser

When the size of the browser changes, the width property and/or the height property of the Frame object will also change, causing a PropertyChangeEvent to be fired.  The event handler gets the new width and height of the Frame object and concatenates that new information onto the text that was already in the TextArea object with a line break separating the new material from the old material.

The TextArea object

The TextArea object is shown in gray in Figure 2.  It is used for display purposes only in this application.  Therefore, it is disabled to prevent the user from being able to enter text into it.

When the text area becomes full, vertical scroll bars automatically appear on the text area.

Only 22 lines of Java source code required

As you will see later, the entire Java program required to produce this web application consisted of only 22 lines of Java source code.  This included about five or six lines that contained nothing but curly braces.

WebApp004

The user interface, as rendered in an Internet Explorer browser, is shown in Figure 3.


Figure 3

Action and PropertyChange event handlers

This application illustrates ActionEvent handling on a Button object and PropertyChangeEvent handling on a TextArea object.

Differences in listener registration methods

As you will see later, the addActionListener and addPropertyChangeListener methods in ThinWire are different from those in J2SE 5.0.  These two methods in ThinWire have an extra parameter.  The extra parameter is a String object or an array of String objects.

For the addActionListener method, the String object identifies the actions that will be monitored by the listener object.

For the addPropertyChangeListener method, the String object(s) identify the properties that the listener will monitor in an attempt to determine if the property values have changed.

One action, many properties

As of ThinWire version 1.2 beta, there is only one kind of action and it is specified as ACTION_CLICK.  However, there are dozens of different properties that apply in different combinations to different components.  This program uses PROPERTY_WIDTH and PROPERTY_HEIGHT.

Components to be notified of events

The addActionListener method registers a listener on the component that will be the source of the event when the specified action occurs.

The addPropertyChangeListener method registers a listener on the component that will be the source of the event when the value of any of the specified properties changes.

Four Button objects and a TextArea object

As you can see in Figure 3, this program places four Button objects and a disabled TextArea object in a Frame object that fills the entire browser window.

Behavior of the program

Anonymous ActionEvent handlers are registered on each of the buttons.  Two of the buttons increase and decrease the width of the text area when they are clicked.  The other two buttons increase and decrease the height of the text area when they are clicked.

An anonymous PropertyChangeListener is registered on the TextArea object, listening for a change in either the width or the height of the text area.  Clicking any one of the four buttons will change the width or the height of the text area, causing a PropertyChangeEvent to be fired by the TextArea object.

The PropertyChangeEvent handler displays the new width and height of the TextArea object in the text area, concatenating the new information onto the text that was already there, with a line break separating the new material from the old material.

Scroll bars appear automatically

When the TextArea becomes full, a vertical scroll bar automatically appears.

When the width of the text area is decreased to less than the length of the longest line of text, a horizontal scrollbar does not appear.  Rather, the text wraps to the next line, and appears to wrap correctly at word boundaries.  However, when the width of the text area becomes less than the length of the longest word, a horizontal scroll bar does appear automatically.

WebApp006

The user interface for this web application, as rendered in Internet Explorer, is shown in Figure 4.


Figure 4

The power of server-side Java

This web application illustrates the ability to use the power of server-side Java to provide a computational service to a client as a web application.  Although the computational example used in this application is not sophisticated, it should serve to illustrate that if you can program a computational algorithm in Java, you can serve it as a web application.

Component style and images on components

The application also illustrates how to change the style of GUI components, and how to put images on buttons.

The computational service

This application provides information about the monthly balance in a savings account instrument.  It assumes a monthly deposit of a fixed amount into a savings instrument for a fixed number of months, at a fixed annual percentage rate.  The following three parameters are provided as user input via three TextField objects:

  • Monthly deposit amount
  • Annual percentage rate
  • Number of months

The user interface

As you can see in Figure 4, the application places the three TextField objects, a Button object, and a TextArea object in a Frame (along with some Label objects for cosmetic purposes).  Note that there is an image on the Button.

The behavior

Each time the user clicks the OK button, the application gets the Monthly Deposit amount, the Annual Percentage Rate, and the Number of Months from the client via the three TextField objects.

Then the application computes and displays a table in the TextArea showing the month number and the balance at the end of each month.  (Note that the TextArea object shown in Figure 4 is automatically displaying a vertical scroll bar.)

Data entry errors

If the user enters something into one of the text fields that can't be parsed into a numeric value, and then clicks the OK button, the text area is cleared and an error message appears in the leftmost TextField object.

Changing the style of a component

For illustration purposes, the style of the leftmost TextField object was changed from the default to the following:

  • Background Color = RED
  • Text = Bold
  • Border Color = BLACK
  • Border Type = DOUBLE

The remaining two TextField objects were displayed in their default style, which is probably what you are more accustomed to seeing for a text field.

Construction of the Button object with an image

The Button object was constructed by passing the name of an image file to the constructor for the Button.  Note that this constructor for the ThinWire Button class is different from and more sophisticated than any of the Button constructors in the AWT.  An AWT button cannot display an image (but a Swing JButton can).

Different from an AWT TextField object

The ability to change the style of a TextField object is also different from the capabilities offered by the AWT.  As I recall, it is not possible to modify the default border for an AWT TextField object.  (However, it is possible to modify the border for a Swing JTextField object.)

Somewhere between the AWT and Swing

Therefore, the available features of the ThinWire GUI component set seem to fall somewhere between the AWT and Swing.  ThinWire GUI components are somewhat richer than the corresponding AWT components, and probably less rich than the corresponding Swing components.

For example, you can specify any one of nine different border styles for a ThinWire Button, which is more than you can do with the AWT.  (You can't change the default border on an AWT button.)  However, you can change the border on a Swing JButton in an almost infinite variety of ways.

You can also make a Swing JButton transparent, but I don't believe that you can make a ThinWire Button transparent.

Additional components

ThinWire version 1.2 beta provides about 25 different GUI components.  The component set includes the typical components such as Button, Label, and TextField, which are available in the standard Java AWT, plus some fancy components that don't exist in the AWT:

  • Tree
  • TabFolder
  • WebBrowser

I will have more to say about GUI components in a future lesson that will concentrate on the ThinWire GUI component set.

The Playground Demo interactive web application

To give you a brief idea of what's coming down the pike, ThinWire will soon release a sophisticated interactive Ajax web application currently being referred as the "Playground Demo."  This interactive web application, which will become an important programming tool and not just a demo, is being developed using ThinWire technology.

Truly impressive!

As of the date of this writing, the Playground web application hasn't been released to the public, but I have been given access to a preview version.  This web application is truly impressive in its current state, and it is getting better all the time.

An important programming tool

In its current state, the Playground Demo lets you interact with each of about eighteen different ThinWire GUI components.  In operation, you can interactively:

  • View the component on the screen as it would be rendered in your browser.
  • Change any of the properties and see the results of each change when you click a button to apply the modified properties.
  • Change any of the style parameters and see the results of each change when you apply the modified style parameters.
  • View the list of supported events.

And perhaps best of all, you can cause the Playground Demo to generate the Java source code for the current component and settings you’ve selected by clicking the “Source Code” tab.

WebApp008

The user interface for this web application, as rendered in Internet Explorer, is the same as WebApp006 and therefore is also shown in Figure 4.

The behavior of this web application is identical to WebApp006.  The purpose of this application is to illustrate the use of a different programming style than was used in WebApp006.  I will explain the differences later when I explain the code.

The web application directory structure

In my earlier lesson entitled Deployment of Web Applications in Jakarta Apache Tomcat 5, I explained the general directory structure requirements for deploying a web application in jakarta-tomcat-5.0.27.  The web applications that I will explain in this lesson follow those general requirements.

The most complicated directory structure

The most complicated directory structure of the four web applications in this lesson is the structure required for WebApp008.  This directory structure is shown in Figure 5 with directory names being highlighted in boldface and file names being shown in ordinary text.

Dir: WebApp008
.Dir: images
..redball.gif
.Dir: WEB-INF
..Dir: classes
...Dir: pkgdemo
....GUI$1.class
....GUI.class
....WebApp008$1.class
....WebApp008.class
....compile.bat
....WebApp008.java
..Dir: lib
...commons-fileupload.jar
...retroweaver-rt.jar
...thinwire.jar
..web.xml
Figure 5

Compare with requirements in the earlier lesson

If you compare Figure 5 with the examples shown in the earlier lesson entitled Deployment of Web Applications in Jakarta Apache Tomcat 5, you will see that it compares very favorably with those examples.  The biggest difference is that the Java source code in Figure 5 is stored along with the class files in the directory named pkgdemo, whereas a special directory named src was set aside for the storage of source code in the earlier lesson.

(As a practical matter, there is no technical requirement to store the source code on the server at all.  It is simply a convenient place to store the source code along with the other files that constitute the web application.)

The file named web.xml

You learned about the required file named web.xml in the earlier lesson entitled Deployment of Web Applications in Jakarta Apache Tomcat 5.  I will explain the Java source code for each application in the sections that follow.  However, I will explain the code for the file named web.xml only once, and this is it.

A general template for the web.xml file is shown in Listing 1.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.
//DTD Web Application 2.3//EN" 
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>ThinWire_SimpleAppDemo</display-name>
  <description>ThinWire Simple App Demo Application
  </description>

  <servlet>
    <servlet-name>thinwire</servlet-name>      
    <description>ThinWire Servlet Engine</description>
    <servlet-class>thinwire.render.web.WebServlet
    </servlet-class>
  
    <init-param>
      <param-name>mainClass</param-name>
      <param-value>Path.AppName</param-value>        
    </init-param>
  </servlet>
    
  <servlet-mapping>
    <servlet-name>thinwire</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>    
</web-app>


Listing 1

Replace content of param-value element

You will need to replace Path.AppName in Listing 1 by the path (relative to the classes directory) and file name of the class file produced by compiling the Java source code that contains your main method.  Otherwise, you can probably copy and use the code in Listing 1 for most, if not all ThinWire web applications.

Optional replacements

In addition, you can optionally replace the content of the display-name and description elements to make them more descriptive of your application, but that is not a technical requirement.

Discussion and Sample Code

I will provide a detailed discussion and explanation of each of the four web applications described above in this section.

The Web Application Named WebApp003

Behavior of the Web Application

I described the general behavior of this web application earlier, so I won't repeat that explanation here.

The Tomcat directory structure

The directory structure for this application is shown in Figure 6.

Dir: WebApp003
.ReadMe.htm
.Dir: WEB-INF
..Dir: classes
...Dir: pkgdemo
....compile.bat
....WebApp003$1.class
....WebApp003$2.class
....WebApp003.class
....WebApp003.java
..Dir: lib
...commons-fileupload.jar
...retroweaver-rt.jar
...thinwire.jar
..web.xml
Figure 6

Practical considerations

Because I deploy my web applications in jakarta-tomcat-5.0.27 for localhost testing, my approach to developing web applications is to create a development tree that has the same directory structure as that which will ultimately be required by jakarta-tomcat-5.0.27.

I create my Java source code file, my web.xml file, my compilation batch file, etc., in that directory tree.  For the development of web applications using ThinWire, I also place a copy of the ThinWire JAR files in the lib directory of that tree as well.

Copy the completed web application

After I have successfully compiled the application, I simply copy the entire directory tree, beginning with the application root directory (the WebApp003 directory in this case) into the webapps directory of the jakarta-tomcat-5.0.27 tree structure.

Testing the web application

Having copied the completed web application into the jakarta-tomcat-5.0.27 tree structure, I start the server running and point my browser to the URL that defines the application root directory for the application in the server.  For example, to test this application, I would point my browser to the following URL:

http://localhost/WebApp003/

Be careful when copying

The jakarta-tomcat-5.0.27 tree structure is fairly large and complex.  In order to avoid the possibility of copying my completed application into the wrong location in that tree structure, and then having to repair the damage, I don't copy directly into the tree.  Rather, I have created a shortcut on my desktop that points to the webapps directory in the jakarta-tomcat-5.0.27 tree structure.

When time comes to do the copy, I copy the completed application onto the folder icon that visually represents the shortcut on the desktop.  That causes it to be copied into the webapps directory.  If I happen to miss the mark, it is simply copied onto the desktop, which is a much easier situation to repair.

Starting and stopping the server

The safest approach for testing is to stop the server following each test, perform the copy, and then restart the server.  However, sometimes you can get by with doing the copy without stopping the server first.  This won't cause any lasting problems, but sometimes your application won't execute properly if you do this.  In that case, just stop and restart the server and repeat the test.

Don't let the server get bloated

Because the server software does some configuration work for each web application on startup, the more web applications that exist in the webapps directory, the longer it will take for the server to become ready for use on startup.  Therefore, if you are testing a lot of web applications, it is a good idea to stop the server and move those applications that are not currently being used into an archive directory.  This will allow the jakarta-tomcat-5.0.27 server to become ready for use more quickly when you start it.

The web.xml file

The required web.xml file for this application matches that shown in Listing 1 except that Path.AppName in Listing 1 is replaced by pkgdemo.WebApp003 for this application.

The Java source code

A complete listing of the Java source code for this application is shown in Listing 25 near the end of the lesson.  I will discuss and explain that source code in fragments.  The first such fragment is presented in Listing 2.

The class definition and the main method for WebApp003 begin in Listing 2.

package pkgdemo;

import thinwire.ui.*;
import thinwire.ui.event.*;

public class WebApp003{
  public static void main(String[] args){

Listing 2

The package declaration

First note the package declaration in Listing 2.  This is what requires that the class files for this application be stored in the directory named pkgdemo, shown as a child of the directory named classes in Figure 6.

All four of the web applications that I will explain in this lesson are declared to be in the same package, so I won't need to mention this again.

The import directives

The ThinWire API for version 1.2 beta is subdivided into the following five packages:

  • thinwire.ui
  • thinwire.ui.event
  • thinwire.ui.layout
  • thinwire.ui.style
  • thinwire.util

As you can see from Listing 2, this application imports classes from the first two packages in the list.  Classes in the first package are required in order to use the GUI components Frame and TextArea.

Classes in the second package are required to support event handling for a PropertyChangeListener.  All four applications that I will explain in this lesson will require the importing of classes from these two packages.  In addition, one of the later applications will also require the importing of classes from the thinwire.ui.style package.

The name of the class

A ThinWire web application can consist of many different classes.  Although I'm not certain, I believe that the class that defines the main method must have the same name as the name of the web application.  This is so that the compilation will produce a class file with the same name as the web application.  In this case, the name of the class is WebApp003, which is also the name of the web application.

(It may be possible to use different names and to rectify the differences in the web.xml file.  In any event, the applications in this lesson follow the rules given above.)

The main method

As I explained earlier, creating a web application using ThinWire is almost exactly the same as creating a standalone Java application having a main method.  The only real difference is that you must comply with the ThinWire API for those classes, interfaces, and methods that duplicate the names of classes, interfaces, and methods in the standard J2SE library.

The standard signature for the main method is shown as the last line of code in the fragment in Listing 2.

Where is the executable code?

The first three applications that I will explain in this lesson have all of the executable code placed in the main method.  Although I don't consider this to be a good programming style, I used it because it is easy to write and easy to understand.

To illustrate that the use of this programming style is not a ThinWire requirement, the fourth application, named WebApp008, is a rewrite of WebApp006 where most of the code was removed from the main method and placed in a different class.

Get a Frame object

The code in Listing 3 instantiates a Frame object and sets its title.

    final Frame frame = Application.current().getFrame();

    frame.setTitle("PropertyChangeEvent Test");

Listing 3

Note that the syntax for instantiating a Frame object in ThinWire is considerably different from the syntax used to instantiate a Frame object in J2SE 5.0.  Unlike a J2SE 5.0 Frame object, a ThinWire Frame object is not a top-level container that can be independently placed on the desktop.  Instead, a ThinWire Frame object essentially takes over the entire browser window.  It cannot be placed on the desktop outside of the real estate occupied by the browser.  There are no public constructors for a ThinWire Frame object.

What do the ThinWire javadocs have to say?

According to the ThinWire javadocs, the code shown in Listing 3 "Gets the Frame that represents the primary application window."  I'm not going to try to explain this in any more depth, because I couldn't even if I wanted to.  Rather, I will simply refer you to the ThinWire javadocs for additional information if you need it.

As a reminder, this is one of those cases where you must write your Java code taking the ThinWire API into account.

The Frame title

Listing 3 uses a conventional setter method to establish the title for the Frame object.  In the case of the ThinWire web application, this title becomes the browser's title, and in the case of a Firefox tabbed browser, it also becomes the title for the tab.

The TextArea object

The code in Listing 4:

  • Instantiates a new TextArea object.
  • Establishes where it will appear relative to the upper left corner of the Frame.
  • Establishes the size of the TextArea object in pixels.
  • Disables the TextArea object to prevent the user from entering data into it.  The TextArea object is used solely for display purposes in this application.

    final TextArea textArea = new TextArea(
                         "Frame Size is Displayed Here\n");
    textArea.setBounds(0,0,200,200);
    textArea.setEnabled(false);

Listing 4

Identical to standalone application code

The code in Listing 4 is identical to code that you would write if you were creating a stand-alone Java application for which the layout manager on the Frame has been set to null.

The reference to the TextArea object is declared to be final because it is a local variable that will be referenced in an inner class.  This is a general Java requirement and not a ThinWire requirement.

Add the TextArea to the Frame

Listing 5 causes the TextArea object to be added to the Frame object.

This syntax is also different from what you find in J2SE 5.0, and you will see a lot of it as you pursue the ThinWire API.

    frame.getChildren().add(textArea);

Listing 5

In J2SE 5.0, you would simply invoke an add method on the Frame object, passing the TextArea object's reference as a parameter.

Interpretation of the code

The syntax in Listing 5 appears to get a reference to a data collection object that contains a list of the objects belonging to the Frame, and to invoke the add method on the collection, adding the TextArea to the collection.  I suspect that this is also what is done in J2SE 5.0, except that Sun has abstracted some of the details away from the programmer.

This general approach will show up again later when dealing with component properties.

The addPropertyChangeListener method

The listener registration methods in J2SE 5.0 typically require a single parameter.  That parameter must be a reference to an object instantiated from a specific listener interface, such as the PropertyChangeListener interface.

The listener registration methods in ThinWire typically require two parameters.  The second parameter is the same as the only parameter in J2SE 5.0, so I won't belabor it.  The interesting parameter is the first parameter in the ThinWire version.

Two overloaded versions

There are two overloaded versions of the addPropertyChangeListener method for the Frame class in ThinWire.  One version requires a reference to a String object as the first parameter.  The other version requires a reference to an array object of type String as the first parameter.

First consider the version that requires a parameter of type String.  The ThinWire javadocs describe the contents of the String object as "the name of the property that the listener will receive change events for."  In other words, given that a component has many properties that are subject to change, if you want to monitor for changes in only one of those properties, this is how you specify the property that you want to monitor.

(Note that this approach is considerably different from, and somewhat simpler than the approach used in J2SE 5.0.  J2SE 5.0 uses a concept known as bound properties to accomplish essentially the same thing.  You can learn about bound properties in my earlier lesson entitled JavaBeans, Properties of Beans, Bound Properties.)

How do you specify the property?

If you open the Index at the top of a javadocs page and then select any of the items in the index that begin with PROPERTY_, that will expose a link named Constant Field Values.  Select that link and you will see a huge table, apparently containing all of the named constants defined in the ThinWire API.

(There is probably an easier way to access this table, but I haven't yet figured out what it is.)

This table includes dozens of properties which apply in different combinations to different components.  The named constants for these properties have names like PROPERTY_WIDTH and PROPERTY_HEIGHT.  This table also provides the string values for these constants, such as "width" and "height", but it would probably be better programming style to use the named constants rather than their absolute values.

Also, the applicable named constants for a particular component will be given in the javadocs for that component.  Be aware, however, that they will often be in the sections of the javadocs that identify the fields were inherited from a superclass, because they are often defined in a superclass of the component class.

A String array parameter

Now consider the other overloaded version of the addPropertyChangeListener that requires a reference to an array object of type String as the first parameter.  (That is the version used in this application.)  The javadocs describe the contents of the String object referred to by the references in the array object as "a string array of property names that the listener will receive change events for."

In other words, if you want a given listener object to monitor for changes in two or more different properties belonging to the component on which it is registered, create an array object containing references to two or more String objects that identify those properties.  (Once again, it would probably be better to use the named constants rather than the absolute values of the strings.)  Then pass that array object's reference as the first parameter to the registration method named addPropertyChangeListener.

Implementing this concept

That brings us to the code that implements this concept that begins in Listing 6.

    String[] properties = {Frame.PROPERTY_WIDTH,
                           Frame.PROPERTY_HEIGHT};

Listing 6

Listing 6 creates and populates a two-element array object that will be used to tell the registered PropertyChangeListener object to monitor for changes in the values of the two properties shown, and to execute the method named propertyChange when the value of either property belonging to the Frame object changes.

An anonymous inner class

The remaining implementation of this concept is shown in Listing 7.

    frame.addPropertyChangeListener(properties,
      new PropertyChangeListener(){
        public void propertyChange(PropertyChangeEvent e){
          textArea.setText(textArea.getText() + 
                  "Width = " + frame.getWidth() +
                  " Height = " + frame.getHeight() + "\n");
        }//end propertyChange method
      }//end PropertyChangeListener constructor
    );//end addPropertyChangeListener method

Listing 7

To begin with, the code in Listing 7 makes use of an anonymous inner class.  If you are unfamiliar with anonymous inner classes, see lessons number 1636, 1638, and 1640 at http://www.dickbaldwin.com/tocmed.htm.

The extra method parameter

The extra method parameter (relative to J2SE 5.0), which is a reference to the array object identifying the properties of interest is shown in boldface in the first line in Listing 7.

The event handler method

The entire event handler method named propertyChange is shown in boldface in Listing 7.  When one of the property values of interest changes, this method is invoked.  The behavior of the method is to:

  • Get the current String contents of the TextArea object.
  • Get the new width and height values of the Frame object.
  • Construct a new String object in which the new information is concatenated onto the old information.
  • Replace the old String in the TextArea object with the new updated String object.

Make it all visible

Finally, the code in Listing 8 causes the Frame object, and its contents (a TextArea object) to become visible in the browser window.

    frame.setVisible(true);
  }//end main
}//end class WebApp003

Listing 8

Put it to work

From this point forward, if you manually change the size of the browser, you will also change the size of the Frame object that contains the entire browser window.  This, in turn will cause the property values for width, height, or both to change, causing a PropertyChangeEvent to be fired, triggering the behavior described above.

Listing 8 also signals the end of the main method and the class named WebApp003.

Compile and deploy the web application

You should be able to compile this program to produce the required class files for the servlets that constitute the web application.  Then, you should be able to create the required web.xml file and deploy the web application in your favorite servlet container.

Having done that, you should be able to access the web application via your browser and confirm the behavior described above.

A caution regarding browsers

If you are using any browser other than Internet Explorer, see the caution regarding browsers at the top of Listing 25.  The people at ThinWire are working to correct whatever it is that is causing this problem.  Hopefully, it will be resolved in the next release.

And that concludes the discussion of the web application named WebApp003.

The Web Application Named WebApp004

Behavior of the Web Application

I described the general behavior of this web application earlier, so I won't repeat that explanation here.

The Tomcat directory structure

Given what you already know about Tomcat directory structures, there is nothing about the directory structure for this application that is new or unusual, so I won't bore you with additional discussion on that topic.

The Java source code

A complete listing of the Java source code for this web application is shown in Listing 26 near the end of the lesson.  As before, I will discuss and explain the source code in fragments.  The first such fragment is presented in Listing 9.

Beginning of the class definition

Except for the name of the class, this application begins just like the previous application.  The code in Listing 9 is essentially the same as the code in Listing 2 and Listing 3, so there would be no point in repeating that discussion.

public class WebApp004{
  public static void main(String[] args){
    final Frame frame = Application.current().getFrame();
    frame.setTitle("Action and PropertyChangeEvent Test");

Listing 9

Instantiate the four Button objects

Listing 10 instantiates the four Button objects shown at the top of Figure 3.

    Button moreWidth = new Button("Increase Width");
    moreWidth.setBounds(0,0,100,25);
    
    Button lessWidth = new Button("Decrease Width");
    lessWidth.setBounds(110,0,100,25);
    
    Button moreHeight = new Button("Increase Height");
    moreHeight.setBounds(0,35,100,25);
    
    Button lessHeight = new Button("Decrease Height");
    lessHeight.setBounds(110,35,100,25);

Listing 10

You will recall from the earlier description that these buttons are used to modify the width and height of the text area also shown in Figure 3.

Set the location and size of buttons

In addition to instantiating the objects, Listing 10 invokes the setBounds method on each new button to establish its location and size in pixels relative to the upper-left corner of the client area of the browser.

(The setBounds method is part of the standard J2SE 5.0 library.  The parameters to the method are given in absolute pixels.)

Create the TextArea object

Listing 11 instantiates the TextArea object shown in the bottom half of Figure 3.

    final TextArea textArea = new TextArea(
                        "TextArea Size is Displayed Here");
    textArea.setBounds(0,80,200,100);
    textArea.setEnabled(false);

Listing 11

In addition, Listing 11:

  • Sets the size and position of the TextArea object within the Frame object.
  • Establishes the text that initially appears in the TextArea object.
  • Disables the TextArea object to prevent the user from entering text into it.  The object is being used solely for display purposes in this application.

Register an ActionListener on the first Button object

Listing 12 registers an ActionListener object on the top left-most Button object shown in Figure 3.

    moreWidth.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          textArea.setWidth(textArea.getWidth() + 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener

Listing 12

Most of the earlier comments apply

Most of the comments that I made earlier regarding the addPropertyChangeListener method apply also to the addActionListener method used in Listing 12.  The biggest difference between the two is the number of possible strings that can be passed as the first parameter to the method.  Whereas there are dozens of properties that can be monitored by a listener, as of ThinWire version 1.2 beta, there is only one action that can be monitored.  As shown in Listing 12, that action is specified by the named constant Button.ACTION_CLICK.

Firing an ActionEvent

Whenever the user clicks the upper-left button in Figure 3 (or presses the space bar while that button has the focus), the button will fire an ActionEvent.  This will cause the actionPerformed method highlighted in boldface in Listing 12 to be executed, increasing the value of the width property on the TextArea object by 10 pixels.

Firing a PropertyChangeEvent

In addition to causing the TextArea to become visibly wider, this will cause the TextArea object to fire a PropertyChangeEvent.  We will see the result of that event having been fired a little later.

Register ActionListener objects on the remaining three buttons

Listing 13 does essentially the same thing to the remaining three buttons shown in Figure 3.

    lessWidth.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setWidth(textArea.getWidth() - 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener 
    
    moreHeight.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setHeight(textArea.getHeight() + 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
    
    lessHeight.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setHeight(textArea.getHeight() - 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener

Listing 13

Once the code in Listing 13 has been executed, all four buttons will be prepared to fire an ActionEvent if it is clicked.  The behavior of each of the actionPerformed methods is slightly different, but each one causes a change to either the width property or the height of the TextArea object.

Register as PropertyChangeListener on the TextArea object

The code in Listing 14 is essentially the same as the code that you saw in Listing 6 and Listing 7, except this time we are registering the listener object on the TextArea object instead of the Frame object.

    String[] properties = {TextArea.PROPERTY_WIDTH,
                           TextArea.PROPERTY_HEIGHT}; 

    textArea.addPropertyChangeListener(properties,
      new PropertyChangeListener(){
        public void propertyChange(PropertyChangeEvent e){
          textArea.setText(textArea.getText() + 
                      "\nWidth = " + textArea.getWidth() +
                      " Height = " + textArea.getHeight());
        }//end propertyChange method
      }//end PropertyChangeListener constructor
    );//end addPropertyChangeListener method

Listing 14

Once again, what happens when the buttons are clicked?

When any of the four buttons is clicked as described above, that will cause the value of either the width property or the height property of the TextArea object to change.  The TextArea object will fire a PropertyChangeEvent causing the propertyChange method defined in Listing 14 to be executed.  The behavior of this event-handling method is to concatenate the new width and height information onto the information already in the TextArea and to store the new information back in the TextArea object.

The new information will then appear in the visible window of the TextArea object in Figure 3.

That's almost a wrap

Listing 15 adds the components to the Frame.

    //Add the components to the Frame.
    frame.getChildren().add(moreWidth);
    frame.getChildren().add(lessWidth);
    frame.getChildren().add(moreHeight);
    frame.getChildren().add(lessHeight);    
    
    frame.getChildren().add(textArea);
    
    //Make the whole thing visible
    frame.setVisible(true);
  }//end main
}//end class WebApp004

Listing 15

Listing 15 also signals the end of the main method and the end of the class named WebApp004.  And that is a wrap for WebApp004

The Web Application Named WebApp006

Behavior of the Web Application

I described the general behavior of this web application earlier, so I won't repeat that explanation here.

The Tomcat directory structure

Given what you already know about the Tomcat directory structures, and given that you have already seen Figure 5 that includes a directory named images, there is nothing about the directory structure for this application that is new or unusual.  However, I will need to refer back to the directory structure for this application later, so I have provided the directory structure in Figure 7.

Dir: WebApp006
.Dir: images
..redball.gif
.Dir: WEB-INF
..Dir: classes
...Dir: pkgdemo
....compile.bat
....WebApp006$1.class
....WebApp006.class
....WebApp006.java
..Dir: lib
...commons-fileupload.jar
...retroweaver-rt.jar
...thinwire.jar
..web.xml
Figure 7

The images directory in Figure 7 contains an image file named redball.gif, which is used to put the red ball on the button in Figure 4.

The Java source code

A complete listing of the Java source code for this web application is shown in Listing 27 near the end of the lesson.  As before, I will discuss and explain that source code in fragments.  The first such fragment is shown in Listing 16.

Beginning of the class definition

As you can see in Listing 16, this program begins pretty much like the two previous programs.  The one important difference is that this program also imports classes from the directory named thinwire.ui.style, which was not the case for the previous two programs.

package pkgdemo;

import thinwire.ui.*;
import thinwire.ui.event.*;
import thinwire.ui.style.*;

public class WebApp006{
  
  public static void main(String[] args){

    //Create a Frame object that will fill the client area
    // in the browser.
    final Frame frame = Application.current().getFrame();
    frame.setTitle("Compound Interest");//Browser title.

Listing 16

Configure the Label objects

Listing 17 instantiates and configures the three Label objects shown across the top of the browser's client window in Figure 4.

    //Create, size, and position three Label objects.
    final Label depositLabel = new Label(
                                        "Monthly Deposit");
    depositLabel.setBounds(0,0,100,25);
    depositLabel.setAlignX(AlignX.CENTER);

    final Label rateLabel = new Label("Annual Rate");
    rateLabel.setBounds(110,0,100,25);
    rateLabel.setAlignX(AlignX.CENTER);
    
    final Label monthsLabel = new Label(
                                       "Number of Months");
    monthsLabel.setBounds(220,0,100,25);
    monthsLabel.setAlignX(AlignX.CENTER);

Listing 17

Given what you have learned so far, the code in Listing 17 is straightforward and shouldn't require further explanation.

The left-most TextField object

Figure 4 shows a row containing three TextField objects, followed by a Button object on the right end of the row.

The red TextField object on the left will garner most of our attention in this initial discussion.  The left-most TextField object is instantiated, sized, and positioned in Listing 18.

    final TextField depositField = new TextField();
    depositField.setBounds(0,35,100,25);

Listing 18

There's nothing remarkable about the code in Listing 18 so it doesn't deserve further discussion.  However, the code in Listing 19 is a different matter.

Set the style for the TextField object

In addition to about a dozen properties that can be set on a TextField object (such as width, height, visible, enabled, etc.) a TextField object also has about a dozen style parameters that can be set.  This includes various choices for the background color, the font used for the text, and three choices regarding the border style (border type, border size, and border color).

Listing 19 illustrates the general procedure for setting the style of a component.

    depositField.getStyle().getBackground().setColor(
                                                Color.RED);

    depositField.getStyle().getFont().setBold(true);

    depositField.getStyle().getBorder().setColor(
                                              Color.BLACK);
    depositField.getStyle().getBorder().setType(
                                       Border.Type.DOUBLE);

Listing 19

The getStyle method

Consider the first statement in Listing 19.  The getStyle method is invoked on the TextField object, which, according to the javadocs, "Returns a Style object representing this Component's current style settings."

A Style object defines about eight methods, three of which are:

  • getBackground
  • getBorder
  • getFont

As you may have guessed, the getBackground method returns a reference to an object of type Background.  The getBorder method returns a reference to an object of type Border, and the getFont method returns a reference to an object of type Font.

The Background object

The Background object defines several methods, one of which is setColor.  As you probably guessed already, the purpose of this method is to set the background color of the component.  The setColor method requires a single parameter, which is a reference to an object of type Color.

The Color class

The Color class is defined in J2SE 5.0, and is also defined in ThinWire.  Thus, when coding with the ThinWire JAR files, the version that is defined in ThinWire will prevail.

The J2SE 5.0 Color class defines about fifteen named color constants such as RED, GREEN, BLUE, YELLOW, etc.  In addition, it defines some methods that allow the program to generate any of about sixteen million different colors by mixing the primary colors of red, green, and blue in different proportions.

The ThinWire Color class does not provide the ability to mix the primary colors, but it does define about 200 different named color constants, such as RED, PEACHPUFF, PAPAYAWHIP, etc.  (I never knew there were so many colors with names.)

Set the background color to RED

In the final analysis, the first statement in Listing 19 sets the background color of the left-most TextField object in Figure 4 to RED, using a named color constant from the Color class.

Set the style for the font and the border

I could go through a similar series of steps to explain that the second statement in Listing 19 sets the font style for the leftmost TextField in Figure 4 to bold, but that would probably put you to sleep.

Finally, the last two statements in Listing 19 set the border color to BLACK and set the border type to DOUBLE.

This general approach should suffice for setting the style on any ThinWire GUI component for which you have a choice of styles.

Create the remaining two TextField objects

Listing 20 instantiates, sizes, and positions the remaining two TextField objects shown in Figure 4.

    final TextField rateField = new TextField();
    rateField.setBounds(110,35,100,25);
    
    final TextField termField = new TextField();
    termField.setBounds(220,35,100,25);

Listing 20

The default style was accepted for both of these TextField objects so no further explanation should be required.

A Button with an image

The ThinWire Button class provides three overloaded constructors:

  • Button():  Constructs a new Button with no text or image.
  • Button(java.lang.String text):  Constructs a new Button with the specified text and no image.
  • Button(java.lang.String text, java.lang.String image):  Constructs a new Button with the specified text and image.

Construct the Button with an image

The third constructor in the above list was used in the first statement in Listing 21 to construct the Button with the red ball image shown in Figure 4.

    final Button okButton = new Button(
                                "OK","images/redball.gif");

    okButton.setBounds(330,35,100,25);

Listing 21

As you can see in listing 21, the second parameter passed to the constructor was the name of the image file stored in the images directory shown in Figure 7.

The second statement in Listing 21 simply sizes and positions the button to the location shown in Figure 4.

Complete the GUI setup code

The code in Listing 22 completes the construction of the Graphical User Interface shown in Figure 4.

    //Instantiate, size, and position a TextArea object. 
    // Disable the object so that the user cannot modify
    // the text.  It is being used as a display component
    // only in this program.
    final TextArea display = new TextArea("");
    display.setBounds(0,80,200,100);
    display.setEnabled(false);
    
    //Add all the components to the Frame, and hence to the
    // client area of the browser.  Note that the size
    // and location of each component is determined by the
    // invocation of the setBounds method on the component
    // earlier and is independent of the order in which the
    // components are added to the Frame.
    frame.getChildren().add(depositLabel);
    frame.getChildren().add(rateLabel);
    frame.getChildren().add(monthsLabel);
    
    frame.getChildren().add(depositField);
    frame.getChildren().add(rateField);
    frame.getChildren().add(termField);
    
    frame.getChildren().add(okButton);
    
    frame.getChildren().add(display);
    
    //Make the whole thing visible
    frame.setVisible(true);

Listing 22

By now, everything is Listing 22 should be familial to you, and should not require an explanation beyond that provided by the comments.

Register an ActionListener on the Button

Recall that the purpose of this application is to do the following each time the user clicks the button in Figure 4:

  • Fetch three user input values via the three TextField objects.
  • Perform a financial computation having to do with interest rates, monthly deposits, etc.
  • Display a table of results showing the results of that computation.

This is accomplished by the ActionEvent handler registered on the button as shown in Listing 23.

    okButton.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          try{
            //Convert contents of the text fields to
            // numeric data.  Watch out for invalid data
            // that can't be parsed into numeric data.
            double monthlyDeposit = Double.parseDouble(
                                   depositField.getText());
            double annualInterestRate = Double.parseDouble(
                                      rateField.getText());
            int numberMonths = Integer.parseInt(
                                      termField.getText());
            
            //Convert the rate from percent format to 
            //decimal format.  Also convert it from an
            // an annual rate to a monthly rate in the
            // process.
            double monthlyInterestRate =  
                                 annualInterestRate/1200.0;
            //Initialize the balance.
            double balance = 0.0;
            
            //Initialize the display.
            display.setText("Month / Balance\n");
            
            //Process each month.
            for(int cnt = 0;cnt < numberMonths;cnt++){
              balance = balance + 
                        balance * monthlyInterestRate + 
                        monthlyDeposit;
              //Eliminate all but two digits to the right
              // of the decimal point in the display only.
              int temp = (int)(balance*100.0);
              double printBalance = temp/100.0;
              
              //Concatenate the new data onto the string
              // that already exists in the display.
              display.setText(display.getText() + 
                 (cnt + 1) + " / $" + printBalance + "\n");
            }//end for loop
          }catch(NumberFormatException ex){
            //Deal with input data that can't be parsed
            // into numeric data.
            //Would be better to test each input field
            // individually and to put the error message in
            // the field with the bad data.  Could be done
            // by using three consecutive try-catch blocks,
            // one for each text field.
            depositField.setText("Entry error");
            display.setText("");
          }//end catch
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
    
  }//end main
}//end class WebApp006

Listing 23

Won't walk you through the code

I'm not going to walk you through the code.  The code in Listing 23 is completely straightforward Java event-driven programming code.  Except for the requirement for the extra parameter passed to the addActionListener method, nothing about this code is peculiar to ThinWire.  Either you already under stand Java event-driven programming or you don't, and if you don't, you should avail yourself of the programming tutorials on my web site.

Listing 23 also signals the end of the main method and the end of the WebApp006 class.

And, that wraps up the explanation of the web application named WebApp006.

The Web Application Named WebApp008

A poor programming style

The first three web applications in this tutorial use a poor Java programming style.  In particular, all of the executable code for each of those three programs is contained in the main method.  While this is a quick and dirty way to throw some Java code together to get something running, it is a poor style from an object-oriented programming viewpoint.

Not a ThinWire requirement

I didn't want you to leave this lesson believing that the ThinWire framework requires you to use a poor programming style.  Rather, the ThinWire framework is compatible with the best Java programming style that you can muster.

A program rewrite

As a demonstration that ThinWire is compatible with an improved programming style, WebApp008 is a rewrite of WebApp006 but written in a much better style.  In particular, as you can see in Listing 24, the main method in WebApp008 consists of a single statement, which instantiates an object of the class named GUI.

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

Listing 24

With the exception of the single statement in Listing 24, all of the executable Java code was moved into the class named GUI.  From that point, if the size of the program were to justify doing so, you could define many new classes and instantiate many new objects in order to accomplish your object-oriented programming goals.

Just write a standalone Java application

To create a ThinWire web application, just write your web application as though you were writing a standalone Java application using the best programming style that you know how to write.  The ThinWire framework will take care of turning it into a web application when you compile it.

Once again, I won't walk you through the code

Since this is not a tutorial on how to program in Java, I'm not going to walk you through the code in WebApp008.  Except for the overall organization, this code is essentially the same as the code that I explained in conjunction with WebApp006.  Rather, I will simply refer you to a complete listing of the program, which you will find in Listing 28.

The same behavior

The behavior of WebApp008 is the same as WebApp006.  However, as you can see by comparing Figure 5 with Figure 7, this application does create a couple of additional class files in the pkgdemo directory.  Regardless of the number of class files, deployment of this web application into the servlet container is the same as deployment of the first three web applications in this lesson.

Check with your server administrator

Your server administrator can tell you the best way to deploy the application, depending on the type of servlet container being used.  However, for test purposes, you can deploy the application onto a Tomcat server running as localhost simply by copying the directory tree named WebApp008 from your development tree into the webapps directory of the server, just like for the first three applications.

And that's about all I need to say about the web application named WebApp008.

Run the Program

I encourage you to download the ThinWire framework and the Jakarta Tomcat server and give them a try.  Copy my code from Listing 25 through Listing 28 and compile it being sure to have the ThinWire JAR files at the beginning of your classpath.  Then deploy the resulting web applications onto the Tomcat server and test them using your favorite browser.

Experiment with my code, making changes and observing the results of your changes.  Learn how to write web applications using this exciting new technology.  Above all, enjoy the process.  Programming can be fun.

Summary

ThinWire makes it as easy to develop rich web applications as it is to write standalone Java applications.  Once you have a correctly written Java application all that is necessary to build the web application is to:

Compile your Java application using Sun's javac compiler with the three ThinWire JAR files at the beginning of the classpath.

That's all there is to it.  It's that simple.

Late Breaking Information

I have received invaluable cooperation from members of the ThinWire development and support team during my efforts to learn about the ThinWire framework and to write this tutorial.  Because this framework is in the very early stages of development, the situation is fairly fluid and things can change very rapidly.

Release of ThinWire version 1.2 beta2

I will be submitting this tutorial lesson to my publisher later today for publication on or about August 22, 2006.  All of the sample web applications and all of the discussion in this tutorial were based on ThinWire version 1.2 beta.  I was notified a few minutes ago that ThinWire version 1.2 beta2 was released earlier today.  The text of the release message, which I received from the ThinWire team via email, is presented in Figure 8.

Beta2 is now available for download at http://thinwire.com/download.html and there is a full changelog at https://sourceforge.net/project/shownotes.php?release_id=437788&group_id=170706
We have also updated our demos and made the demo server public.  There is a decent summary on the thinwire blog.
Figure 8

The ThinWire blog mentioned in the message is located at http://thinwire.com/blog/.

Comments from the ThinWire team

Several days ago, I submitted a copy of this tutorial to the ThinWire team for their review and comments because I want to be as accurate as possible in my depiction of their framework.  Because the javadocs for ThinWire version 1.2 beta were still somewhat lacking in detail when this tutorial was being written, the information contained herein was sometimes based on my best interpretation at the time.

Today I received very welcome feedback and comments from the ThinWire team.  I have decided that rather than to provide my own interpretation of their comments, I will simply present those comments to you verbatim.  The comments are provided for your review in Figure 9 below.

First, the three JAR files:
  • thinwire.jar is the ThinWire framework, the other 2 JAR files are 3rd party libraries, and are not technically part of the framework
  • commons-fileupload.jar is from the Apache Commons, and provides the ability for our FileChooser component to upload files to the server. (http://jakarta.apache.org/commons/fileupload/)
  • retroweaver-rt.jar is a runtime library that enables ThinWire to work with J2SE 1.4 (The ThinWire sources are written using Java 1.5 specific features like generics, and retroweaver allows us to backport to support Java 1.4) (http://retroweaver.sourceforge.net/)
Second, the web.xml file and main method

There is no requirement that the class that contains the main method have the application name.  In fact, the main method for the Playground is in a class called Main.  What is required is that you specify the class that contains your main method in the web.xml as follows:

<init-param>
    <param-name>mainClass</param-name>
    <param-value>package.and.class.containing.main.method</param-value>
</init-param>

So, for the Playground, the main method is in Main.java which is in the package thinwire.apps.playground.  The web.xml entry would be:

<init-param>
    <param-name>mainClass</param-name>
    <param-value>thinwire.apps.playground.Main</param-value>
</init-param>

Third, the Color class

You mentioned that the AWT Color class allows you to specify mixtures of red green and blue to create any color.  The ThinWire Color class provides this ability as well.  The static method valueOf(String colorId) accepts rgb values in the form: "rgb(r, g, b)", as well as the HEX values (ex: "#5F9EA0").  The predefined constants mirror the color constants supported by CSS.

Finally, a philosophical point

We don't view the ThinWire api as set of overloaded Swing components.  We view ThinWire as it's own UI widget set.  The only thing it shares with Swing or AWT or SWT (or MFC or Cocoa, etc) are the names and general concepts of GUI components (buttons, text fields, etc).

On behalf of (team member names deleted for privacy) and myself, thanks for creating these tutorials.  We are very excited about their publication on developer.com, and are hopeful that they show many people how easy Rich Internet Application programming can be with ThinWire.  Please continue to send us your questions and we will continue to help you to the best of our ability. 
Figure 9

What's Next?

I hope that you have found this tutorial on Getting Started with ThinWire useful.  Although my plans often change over time for a variety of reasons, at this time I plan to publish a series of tutorial lessons on using ThinWire that will include the following topics:

  • GUI components
  • The ThinWire Playground tool
  • Layout
  • The Visual Form Creator tool
  • Interaction with JDBC and  MySQL
  • Dealing with large images

In addition, I also plan to publish tutorial lessons on developing web applications using competing technologies such as the Google Web Toolkit, (GWT).

Complete Program Listings

Complete listings of the web applications discussed in this lesson are shown in Listing 25 through Listing 28 below.
 
/*File WebApp003.java
Copyright 2006, R.G.Baldwin

This is a sample ThinWire web application that illustrates 
PropertyChangeEvent handling on a Frame object.

Caution:  As of ThinWire v1.2 beta, this application will 
not execute properly under either Firefox v 1.5.0.5 or
Netscape 7.2.  In both cases, an instability appears when 
the size of the frame is changed using the handle at the 
lower right of the frame.  However, it executes properly 
under Internet Explorer v6.0.xx.

Instantiates a Frame object that fills the entire browser 
window.  

Places a disabled TextArea in the Frame.

Registers a PropertyChangeListener on the Frame, listening 
for a change in either the width or the height of the 
Frame.

When the browser is manually resized, the size of the 
Frame also changes because the Frame fills the entire 
client window of the browser.  This causes a 
PropertyChangeEvent to be fired by the Frame.  The event 
handler displays the new width and height in the TextArea,
concatenating them onto the text that was already there 
with a line break separating the new material from the old
material.

When the TextArea becomes full, a vertical scroll bar 
automatically appears.

Note that the addPropertyChangeListener method in ThinWire
is different from J2SE 5.0.

Registration methods in ThinWire have an extra parameter.
The extra parameter is a String object or an array of 
String objects that identify the things that are to be
monitored by the listener object.

Also note that the syntax required to create a new Frame
object, and the syntax for adding a component to a 
Frame object is different between the ThinWire API and
J2SE 5.0.

Tested using J2SE 5.0, ThinWire 1.2 Beta, and 
jakarta-tomcat-5.0.27 under WinXP.
**********************************************************/

package pkgdemo;

import thinwire.ui.*;
import thinwire.ui.event.*;

public class WebApp003{
  public static void main(String[] args){
    //Note that this approach to getting a Frame object is
    // different from the approach used in J2SE 5.0.
    final Frame frame = Application.current().getFrame();
    //The following title becomes the title in the banner
    // of the browser, and also the title of the tab in
    // a Firefox tabbed browser.
    frame.setTitle("PropertyChangeEvent Test");
    
    final TextArea textArea = new TextArea(
                         "Frame Size is Displayed Here\n");
    textArea.setBounds(0,0,200,200);
    textArea.setEnabled(false);
    
    //Note the unusual syntax, (relative to J2SE 5.0),
    // that is required to add a component to the frame.
    frame.getChildren().add(textArea);
    
    //Create an array object containing the properties
    // for which changes will be detected by the listener
    // to be registered on the frame.
    String[] properties = {Frame.PROPERTY_WIDTH,
                           Frame.PROPERTY_HEIGHT};
                           
    //Note the somewhat unusual (relative to J2SE 5.0) 
    // first parameter in the following method call that
    // registers a listener on the frame.
    frame.addPropertyChangeListener(properties,
      new PropertyChangeListener(){
        public void propertyChange(PropertyChangeEvent e){
          textArea.setText(textArea.getText() + 
                  "Width = " + frame.getWidth() +
                  " Height = " + frame.getHeight() + "\n");
        }//end propertyChange method
      }//end PropertyChangeListener constructor
    );//end addPropertyChangeListener method
    
    frame.setVisible(true);
  }//end main
}//end class WebApp003

Listing 25

Listing 26

/*File WebApp004.java
Copyright 2006, R.G.Baldwin

This program illustrates the use of ThinWire to create a
web application.

Illustrates ActionEvent handling on a Button and 
PropertyChangeEvent handling on a TextArea object.

Note that the addActionListener and 
addPropertyChangeListener methods in ThinWire are different
from those in J2SE 5.0.

These two methods in ThinWire have an extra parameter.  The
extra parameter is a String object or an array of String 
objects that specify actions or properties, depending on 
the specific method involved.

As of ThinWire v1.2 Beta, there is only one kind of action 
and it is specified as ACTION_CLICK.  However, there are 
dozens of different properties that apply in different 
combinations to different components.

This program uses PROPERTY_WIDTH and PROPERTY_HEIGHT.

The action registration method registers a listener on the 
component that will be notified when the specified action 
occurs.

The property registration method registers a listener on 
the component that will be notified when any of the 
specified properties change.

This program places four buttons and a disabled TextArea 
object in a Frame that fills the entire browser window.

Anonymous ActionEvent handlers are registered on each of 
the buttons.  Two of the buttons increase and decrease the 
width of the text area.  The other two buttons increase and
decrease the height of the text area.

An anonymous PropertyChangeListener is registered on the 
TextArea object, listening for a change in either the width
or the height of the text area.  

Changing the width or the height of the text area causes a 
PropertyChangeEvent to be fired.  The event handler 
displays the new width and height in the TextArea, 
concatenating them onto the text that was already there 
with a line break separating the new material from the old 
material.

When the TextArea becomes full, a vertical scroll bar 
automatically appears.  However, I could find no way to 
cause it to automatically scroll to the bottom so as to 
display the newest material.

When the width of the text area is decreased to less than
the length of the longest line of text, a horizontal scroll
bar does not appear.  Rather, the text wraps to the next
line, and appears to wrap correctly at word boundaries.
However, when the width of the text area becomes less than
the length of the longest word, a horizontal scroll bar
appears automatically.

Tested using J2SE 5.0, ThinWire 1.2 Beta, and 
jakarta-tomcat-5.0.27 under WinXP.
**********************************************************/

package pkgdemo;

import thinwire.ui.*;
import thinwire.ui.event.*;

public class WebApp004{
  public static void main(String[] args){
    //Note that this approach to getting a Frame object is
    // different from the approach used in J2SE 5.0.
    final Frame frame = Application.current().getFrame();
    //The following title becomes the title in the banner
    // of the browser, and also the title of the tab in
    // a Firefox tabbed browser.
    frame.setTitle("Action and PropertyChangeEvent Test");
    
    //Instantiate the four buttons that are used to modify
    // the width and height of the text area.  Note that
    // the position and size of the buttons in the
    // browser window is specified in absolute pixel
    // coordinates relative to the upper-left corner of the
    // browser window.
    Button moreWidth = new Button("Increase Width");
    moreWidth.setBounds(0,0,100,25);
    
    Button lessWidth = new Button("Decrease Width");
    lessWidth.setBounds(110,0,100,25);
    
    
    Button moreHeight = new Button("Increase Height");
    moreHeight.setBounds(0,35,100,25);
    
    Button lessHeight = new Button("Decrease Height");
    lessHeight.setBounds(110,35,100,25);
    
    //Instantiate, position, and set the size of the text
    // area.  Provide initial text for the text area.
    // Disable the text area so that the user cannot modify
    // the text.  It is being used as a display component
    // only in this program.
    final TextArea textArea = new TextArea(
                        "TextArea Size is Displayed Here");
    textArea.setBounds(0,80,200,100);
    textArea.setEnabled(false);
    
    //Note the somewhat unusual (relative to J2SE 5.0) 
    // first parameter in the following method calls that
    // register action listeners on the buttons.  (See
    // earlier comments.)
    moreWidth.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setWidth(textArea.getWidth() + 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
    
    lessWidth.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setWidth(textArea.getWidth() - 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener 
    
    moreHeight.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setHeight(textArea.getHeight() + 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
    
    lessHeight.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
         textArea.setHeight(textArea.getHeight() - 10);
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
    
    //Create an array object containing the properties
    // for which changes will be detected by the listener
    // to be registered on the text area.
    String[] properties = {TextArea.PROPERTY_WIDTH,
                           TextArea.PROPERTY_HEIGHT}; 
    //Note the somewhat unusual (relative to J2SE 5.0) 
    // first parameter in the following method call.
    textArea.addPropertyChangeListener(properties,
      new PropertyChangeListener(){
        public void propertyChange(PropertyChangeEvent e){
          textArea.setText(textArea.getText() + 
                      "\nWidth = " + textArea.getWidth() +
                      " Height = " + textArea.getHeight());
        }//end propertyChange method
      }//end PropertyChangeListener constructor
    );//end addPropertyChangeListener method
    
    //Add the components to the Frame.
    frame.getChildren().add(moreWidth);
    frame.getChildren().add(lessWidth);
    frame.getChildren().add(moreHeight);
    frame.getChildren().add(lessHeight);    
    
    frame.getChildren().add(textArea);
    
    //Make the whole thing visible
    frame.setVisible(true);
  }//end main
}//end class WebApp004


Listing 26

Listing 27

/*File WebApp006.java
Copyright 2006, R.G.Baldwin

This program illustrates the use of ThinWire to create a
web application.

Caution:  As of ThinWire v1.2 beta, this application will 
not execute properly under either Firefox v 1.5.0.5 or
Netscape 7.2.  In both cases, an instability appears when 
the text area is updated.  In addition, in Netscape 7.2, 
the use of the tab key to traverse the components results 
in the focus hitting only every second component.

However, neither problem exists with Internet Explorer 
v6.0xx.  The program executes properly under Internet 
Explorer v6.0.xx.

Illustrates the ability to use the power of server-side
Java to provide a computational service to a client as a 
web application.

Assumes a monthly deposit of a fixed amount into a
savings account for a fixed number of months, at a fixed
annual percentage rate.

Places three TextField objects, a Button object, and a 
TextArea object in a Frame object (along with some Label 
objects for cosmetic purposes).  Note that there is an 
image on the Button.

Gets Monthly Deposit, Annual Percentage Rate, and Number of
Months from the client via the TextField objects.  Each 
time the user clicks the OK button, the program computes
and displays a table in the TextArea showing the balance 
at the end of each month, 

Tested using J2SE 5.0, ThinWire 1.2 Beta, and 
jakarta-tomcat-5.0.27 under WinXP.
**********************************************************/

package pkgdemo;

import thinwire.ui.*;
import thinwire.ui.event.*;
import thinwire.ui.style.*;

public class WebApp006{
  
  public static void main(String[] args){

    //Create a Frame object that will fill the client area
    // in the browser.
    final Frame frame = Application.current().getFrame();
    frame.setTitle("Compound Interest");//Browser title.
    
    //Create, size, and position three Label objects.
    final Label depositLabel = new Label(
                                        "Monthly Deposit");
    depositLabel.setBounds(0,0,100,25);
    depositLabel.setAlignX(AlignX.CENTER);

    final Label rateLabel = new Label("Annual Rate");
    rateLabel.setBounds(110,0,100,25);
    rateLabel.setAlignX(AlignX.CENTER);
    
    final Label monthsLabel = new Label(
                                       "Number of Months");
    monthsLabel.setBounds(220,0,100,25);
    monthsLabel.setAlignX(AlignX.CENTER);
    
    //Create, size, and position a TextField object and set
    // the style of the TextField.
    final TextField depositField = new TextField();
    depositField.setBounds(0,35,100,25);
    
    //Illustrate how to set the style of a component.
    depositField.getStyle().getBackground().setColor(
                                                Color.RED);
    depositField.getStyle().getFont().setBold(true);
    depositField.getStyle().getBorder().setColor(
                                              Color.BLACK);
    depositField.getStyle().getBorder().setType(
                                       Border.Type.DOUBLE);
    
    //Create, size, and position two more TextField objects
    // and a Button object.  Accept the default style for
    // each.  Note that there is an image on the Button.
    final TextField rateField = new TextField();
    rateField.setBounds(110,35,100,25);
    
    final TextField termField = new TextField();
    termField.setBounds(220,35,100,25);
    
    //Note the format of the constructor parameter list to
    // place an image on the button.
    final Button okButton = new Button(
                                "OK","images/redball.gif");
    okButton.setBounds(330,35,100,25);
    
    //Instantiate, size, and position a TextArea object. 
    // Disable the object so that the user cannot modify
    // the text.  It is being used as a display component
    // only in this program.
    final TextArea display = new TextArea("");
    display.setBounds(0,80,200,100);
    display.setEnabled(false);
    
    //Add all the components to the Frame, and hence to the
    // client area of the browser.  Note that the size
    // and location of each component is determined by the
    // invocation of the setBounds method on the component
    // earlier and is independent of the order in which the
    // components are added to the Frame.
    frame.getChildren().add(depositLabel);
    frame.getChildren().add(rateLabel);
    frame.getChildren().add(monthsLabel);
    
    frame.getChildren().add(depositField);
    frame.getChildren().add(rateField);
    frame.getChildren().add(termField);
    
    frame.getChildren().add(okButton);
    
    frame.getChildren().add(display);
    
    //Make the whole thing visible
    frame.setVisible(true);
    
    
    //Define and register an action listener on the
    // okButton.  The purpose of this event handler is to
    // get the relevant information from the user and use
    // that information to compute and display a table
    // showing monthly balances in the savings account.
    okButton.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          try{
            //Convert contents of the text fields to
            // numeric data.  Watch out for invalid data
            // that can't be parsed into numeric data.
            double monthlyDeposit = Double.parseDouble(
                                   depositField.getText());
            double annualInterestRate = Double.parseDouble(
                                      rateField.getText());
            int numberMonths = Integer.parseInt(
                                      termField.getText());
            
            //Convert the rate from percent format to 
            //decimal format.  Also convert it from an
            // an annual rate to a monthly rate in the
            // process.
            double monthlyInterestRate =  
                                 annualInterestRate/1200.0;
            //Initialize the balance.
            double balance = 0.0;
            
            //Initialize the display.
            display.setText("Month / Balance\n");
            
            //Process each month.
            for(int cnt = 0;cnt < numberMonths;cnt++){
              balance = balance + 
                        balance * monthlyInterestRate + 
                        monthlyDeposit;
              //Eliminate all but two digits to the right
              // of the decimal point in the display only.
              int temp = (int)(balance*100.0);
              double printBalance = temp/100.0;
              
              //Concatenate the new data onto the string
              // that already exists in the display.
              display.setText(display.getText() + 
                 (cnt + 1) + " / $" + printBalance + "\n");
            }//end for loop
          }catch(NumberFormatException ex){
            //Deal with input data that can't be parsed
            // into numeric data.
            //Would be better to test each input field
            // individually and to put the error message in
            // the field with the bad data.  Could be done
            // by using three consecutive try-catch blocks,
            // one for each text field.
            depositField.setText("Entry error");
            display.setText("");
          }//end catch
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
    
  }//end main
}//end class WebApp006


Listing 27

Listing 28

/*File WebApp008.java
Copyright 2006, R.G.Baldwin

This program illustrates the use of ThinWire to create a
web application.

This program is an update to the earlier program named
WebApp006.  The sole purpose of this update is to move
most of the code from the main method into another class.
The behavior should not be changed relative to the behavior
of the earlier program.

Caution:  As of ThinWire v1.2 beta, this application will 
not execute properly under either Firefox v 1.5.0.5 or
Netscape 7.2.  In both cases, an instability appears when 
the text area is updated.  In addition, in Netscape 7.2, 
the use of the tab key to traverse the components results 
in the focus hitting only every second component.

However, neither problem exists with Internet Explorer 
v6.0xx.  The program executes properly under Internet 
Explorer v6.0.xx.

Illustrates the ability to use the power of server-side
Java to provide a computational service to a client as a 
web application.  Also illustrates control over
the style of GUI component, and images on buttons.

Assumes a monthly deposit of a fixed amount into a
savings account for a fixed number of months, at a fixed
annual percentage rate.

Places three TextField objects, a Button object, and a 
TextArea object in a Frame object (along with some Label 
objects for cosmetic purposes).  Note that there is an 
image on the Button.

Gets Monthly Deposit, Annual Percentage Rate, and Number of
Months from the client via the TextField objects.  Each 
time the user clicks the OK button, the program computes
and displays a table in the TextArea showing the balance 
at the end of each month, 

Tested using J2SE 5.0, ThinWire 1.2 Beta, and 
jakarta-tomcat-5.0.27 under WinXP.
**********************************************************/

package pkgdemo;

import thinwire.ui.*;
import thinwire.ui.event.*;
import thinwire.ui.style.*;

public class WebApp008{
  
  public static void main(String[] args){
    new GUI();
  }//end main
}//end class WebApp008
//=======================================================//

class GUI{
  GUI(){//constructor
  
    //Create a Frame object that will fill the client area
    // in the browser.
    final Frame frame = Application.current().getFrame();
    frame.setTitle("Compound Interest");//Browser title.
    
    //Create, size, and position three Label objects.
    final Label depositLabel = new Label(
                                        "Monthly Deposit");
    depositLabel.setBounds(0,0,100,25);
    depositLabel.setAlignX(AlignX.CENTER);

    final Label rateLabel = new Label("Annual Rate");
    rateLabel.setBounds(110,0,100,25);
    rateLabel.setAlignX(AlignX.CENTER);
    
    final Label monthsLabel = new Label(
                                       "Number of Months");
    monthsLabel.setBounds(220,0,100,25);
    monthsLabel.setAlignX(AlignX.CENTER);
    
    //Create, size, and position a TextField object and set
    // the style of the TextField.
    final TextField depositField = new TextField();
    depositField.setBounds(0,35,100,25);
    
    //Illustrate how to set the style of a component.
    depositField.getStyle().getBackground().setColor(
                                                Color.RED);
    depositField.getStyle().getFont().setBold(true);
    depositField.getStyle().getBorder().setColor(
                                              Color.BLACK);
    depositField.getStyle().getBorder().setType(
                                       Border.Type.DOUBLE);
    
    //Create, size, and position two more TextField objects
    // and a Button object.  Accept the default style for
    // each.  Note that there is an image on the Button.
    final TextField rateField = new TextField();
    rateField.setBounds(110,35,100,25);
    
    final TextField termField = new TextField();
    termField.setBounds(220,35,100,25);
    
    //Note the format of the constructor parameter list to
    // place an image on the button.
    final Button okButton = new Button(
                                "OK","images/redball.gif");
    okButton.setBounds(330,35,100,25);
    
    //Instantiate, size, and position a TextArea object. 
    // Disable the object so that the user cannot modify
    // the text.  It is being used as a display component
    // only in this program.
    final TextArea display = new TextArea("");
    display.setBounds(0,80,200,100);
    display.setEnabled(false);
    
    //Add all the components to the Frame, and hence to the
    // client area of the browser.  Note that the size
    // and location of each component is determined by the
    // invocation of the setBounds method on the component
    // earlier and is independent of the order in which the
    // components are added to the Frame.
    frame.getChildren().add(depositLabel);
    frame.getChildren().add(rateLabel);
    frame.getChildren().add(monthsLabel);
    
    frame.getChildren().add(depositField);
    frame.getChildren().add(rateField);
    frame.getChildren().add(termField);
    
    frame.getChildren().add(okButton);
    
    frame.getChildren().add(display);
    
    //Make the whole thing visible
    frame.setVisible(true);
    
    
    //Define and register an action listener on the
    // okButton.  The purpose of this event handler is to
    // get the relevant information from the user and use
    // that information to compute and display a table
    // showing monthly balances in the savings account.
    okButton.addActionListener(Button.ACTION_CLICK,
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          try{
            //Convert contents of the text fields to
            // numeric data.  Watch out for invalid data
            // that can't be parsed into numeric data.
            double monthlyDeposit = Double.parseDouble(
                                   depositField.getText());
            double annualInterestRate = Double.parseDouble(
                                      rateField.getText());
            int numberMonths = Integer.parseInt(
                                      termField.getText());
            
            //Convert the rate from percent format to 
            //decimal format.  Also convert it from an
            // an annual rate to a monthly rate in the
            // process.
            double monthlyInterestRate =  
                                 annualInterestRate/1200.0;
            //Initialize the balance.
            double balance = 0.0;
            
            //Initialize the display.
            display.setText("Month / Balance\n");
            
            //Process each month.
            for(int cnt = 0;cnt < numberMonths;cnt++){
              balance = balance + 
                        balance * monthlyInterestRate + 
                        monthlyDeposit;
              //Eliminate all but two digits to the right
              // of the decimal point in the display only.
              int temp = (int)(balance*100.0);
              double printBalance = temp/100.0;
              
              //Concatenate the new data onto the string
              // that already exists in the display.
              display.setText(display.getText() + 
                 (cnt + 1) + " / $" + printBalance + "\n");
            }//end for loop
          }catch(NumberFormatException ex){
            //Deal with input data that can't be parsed
            // into numeric data.
            //Would be better to test each input field
            // individually and to put the error message in
            // the field with the bad data.  Could be done
            // by using three consecutive try-catch blocks,
            // one for each text field.
            depositField.setText("Entry error");
            display.setText("");
          }//end catch
        }//end actionPerformed
      }//end ActionListener constructor
    );//end addActionListener
  
  }//end constructor
}//end class GUI


Listing 28


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.

Baldwin@DickBaldwin.com






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel