Until today, all of the web applications we've created have been programs that run on the web server's hardware, generating an HTML page that is then sent to the user's computer. The user's computer, or more often, the user's browser is often called the client. As far as the client is concerned, it is being served a static web page. Granted, it's a web page that might be different each time it is loaded, but it's just a web page written in HTML. The client can't tell what logic was used to create that web page. They can't even tell what language was used. If you visit any of the web pages you created for any of your assignments and selected "show source code" in your browser, all you would see is HTML. You wouldn't see any Java, PHP, or Python code.
This is because all of the methods we've seen for creating web applications so far have been server-side applications. You'll notice that we always had to make sure that our server had the right software to be able to run the applications we wrote, but all that we needed to use the applications was a web browser.
But not all web applications work that way. Some web applications are run on the user's computer (i.e., by the client). The server doesn't do any computation at all; it just sends the application (either as a compiled program or as an uncompiled script) to the client along with (or embedded in) the HTML file, and it's up to the user's computer to actually run the program. This is what's called a client-side application. Today we're going to learn how to use Java to write web applications that will be downloaded to the user's computer and run by a plug-in for their browser. Such applications are called Java applets.
Here's a list of server- and client-side programming or scripting languages that you might or might not be familiar with.
*JSP is a lot like PHP, only the code is written in Java. In other words, it's a way to embed Java code directly into an HTML file. We haven't talked about it in this class, but if you can handle PHP and you can handle Java, it shouldn't be too difficult to pick up.
**There is such a thing as server-side Javascript, but that's not how Javascript is usually used.
The Java Applet
class is meant to be used for relatively small applications that are downloaded by the user and run on their own computer. Usually (but not always), applets are embedded in web pages and are executed by the the browser's Java plug-in. Unlike Javascript, Java applets are compiled before they are sent to the user's computer. Unlike Java servlets (which are server-side applications) where only the output of the program is sent to the client, Java applets (which are client-side applications) the entire program is sent to the client to be executed there. They're called "applets" because they're applications that need to be small enough to be sent over the internet. Java servlets came a couple years after applets were named, and the name "servlet" was probably derived from "applet".
Java applets are always compiled to a .class
file before being sent to the client, and they're usually (but not always) compressed into a .jar
file before being sent. In our class, we won't be packaging our applets up into .jar
files since our applets won't be big enough to make compression worth the effort.
There was a lot of overhead that went into the applet we created in class. And for a solid ready-for-production, that overhead might be necessary. But I've been able to make a number of simplifications the applet we created in class, and it will still run and do what we want it to. For our purposes, we don't need to worry about starting up threads with the invokeAndWait()
method. We can just write into the milestone methods like init()
(see below) directly.
We'll be using a package called "Swing" that has a lot of nice features when it comes to laying out applets. When you see a class that has a "J" in front of its name (like JApplet
, JLabel
, or JButton
), it's probably the Swing version of a more basic applet class (like Applet
, Label
, or Button
). All of our applets will be subclasses of the Swing class JApplet
. So let's start writing our first applet:
import javax.swing.*;
public class HelloWorld extends JApplet {
}
When we created stand-alone Java applications, we always implemented the main()
method, which was called as soon as we ran the program. When we created Java servlets, we overrode* the doGet()
method, which was called whenever the client called up a web page using the GET method (which is the default method for HTTP). We could also have overridden doPost()
if we had implemented a form that had used the POST method instead of the GET method. For Java applets, there are at least** four different methods that will frequently be overridden: init()
, start()
, stop()
, and destroy()
. Each of these four methods is called at a particular point (called a milestone) in the life cycle of the applet.
*Every object of the HttpServlet
class already has a doGet()
method, but it doesn't really do anything, so when we defined our own subclass of HttpServlet
, when we defined doGet()
, we're technically overriding the existing method. But since the original method did essentially nothing, it feels more like we're just filling in the details.
**There is also a method called paint()
that actually draws all of the components on the screen. Since we're just going to use the premade components provided by Swing, we won't need to be doing our own painting. Swing is very versatile, and you can actually get quite far without ever needing to override the paint()
method.
The first milestone is when the applet is loaded by the Java plug-in. At this point, the initialization method (called init()
) is executed. This is where the applet sets up everything it needs to do, such as creating all the components for the user interface and determining what they'll do. For us, most of the interesting stuff will happen here.
Immediately after the init()
method finishes running, the start()
is executed. The init()
method only ever runs once during the lifetime of the applet but in theory, the start()
method may be run more than once.
When the user leaves the applet (perhaps by leaving the page or closing the browsser), the stop()
method is called, and then the destroy()
method is called. Again, in theory, there are situations in which stop()
might be called without then calling destroy()
, but not the other way around.
Many sources I've seen claim that when you leave a web page that has a Java applet running, the stop()
method is called, but not the destroy()
method. They also claim that if you return to the site, the start()
method will be called, but not the init()
method. Because of the conflicting information I've seen, I decided to run a few simple tests to see if this was true. And (at least in the latest versions of the Java plug-in for the latest versions of Firefox and Chrome), this does not seem to be true. Every time you navigate away from a page with an applet running, stop()
is called and then destroy()
is called. Every time you navigate to a page with an applet on it, init()
is called and then start()
is called. Every time you reload a page with an applet running, stop()
is called, then destroy()
is called, then init()
, and finally start()
. There might be some circumstances in which Java will stop()
an applet without then proceeding to destroy()
it, or in which Java will call start()
without having first called init()
, but I have no idea what those circumstances would be.
So while it's not clear exactly why init()
and start()
both exist (nor stop()
and destroy()
), it is clear when they are executed, and in what order they're executed. To sum up: when the applet is loaded, first init()
and then start()
are run, and when the applet is unloaded, first stop()
and then destroy()
are run.
There are a few recommendations I've seen that are relatively consistent across different sources. Any code that starts a thread (which we haven't really talked about) should go in the init()
method. Code that starts introductory animations or sounds should go in the start()
method. Code for stopping animations or sounds should go in the stop()
method. And any code that closes threads or streams should go in the destroy()
method.
Four our little applet here, we'll only need to override the init()
method.
import javax.swing.*;
public class HelloWorld extends JApplet {
public void init() {
}
}
The Applet
class (and even more so the JApplet
class) will do a lot of work for us when it comes to actually drawing our user interface. All we really have to do is tell it what kinds of things (technically, they're called components) we want to put in the applet (and maybe where to put them), and the already existing class definitions will take care of the rest. If we don't specify exactly where to put our components, Java will assume that we've put them into the root frame, which is the basic platform on which we need to put everything that will show up on the screen. So let's put something there.
import javax.swing.*;
public class HelloWorld extends JApplet {
public void init() {
JLabel label1 = new JLabel("Hello World!");
add(label1);
}
}
Our component is a label. (Specifically, it's an object of type JLabel
, which is a special Swing subclass of the class Label
, but I'll probably just call them "labels" to save space.) A label has content that is displayed on the screen when it is painted. It doesn't do anything (like a button a slider, or a text input field might), but it is a little more versatile than just a bit of text in a web page defined using HTML.
When we were doing server-side applications like servlets, we were able to change the text on a web page, but only when the page was reloaded (for example, after the user clicked a button). It wasn't possible to change the content of the web page without reloading it. Applets don't have this restriction; at any point in time, we can decide to change the just about anything that is being displayed inside the applet frame. In particular, we could change the content of a label.
But we initialized our label to have the text "Hello World!" by using one of the constructors for the JLabel
class: new JLabel("Hello World!")
Of course, creating a label isn't useful unless we tell the paint()
method where to actually put it. So we used the command add(label)
to add our new label to the root frame.
Now the applet is written, and we're almost ready to run it. But there are a few bits and pieces of configuration we have to set up first. First of all, we need to compile the applet into a .class
file called HelloWorld.class
. Next, we need to put our compiled applet where it can be accessed from the web, and we should also probably have a web page to load up our applet. Note that for applets, we don't need to use Tomcat. Remember: all the execution will happen on the user's computer. So we can just use our ordinary Apache web server. Make sure Apache is running, and then move HelloWorld.class
into the ~/apache/htdocs/helloworld/
directory, and then create an HTML file in that same directory named hello.html
. Put the following code into the HTML web page:
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>A Hello World Applet</h1>
<object type="application/x-java-applet" width=200 height=200>
<param name="code" value="HelloWorld"/>
Applet failed to load.
</object>
</body>
</html>
In older versions of HTML (HTML4 or earlier), you would've used the <applet> tags instead of the <object> tags, but times have changed. There are a lot of other things we could specify when we insert the applet into our web page, but we can get away with just a few. We definitely need to include information about what kind of object it is. Remember that the string "application/x-java-applet" has to be written exactly as it is here, or the browser might not recognize what kind of thing the applet is. It's also a good idea to set the width and height of the applet frame, so that they don't get too far out of hand. Most importantly, we have to tell it what Java class contains the code for our applet. Note that you don't need to put the .class
suffix in the value; you just need to specify the name of the class. If you were loading an applet from a compressed .jar
file, there would be another <param/> tag with the name "archive" and the name of the archive file as the value. Lastly, we've included a bit of alternate text to display on the web page in case something goes wrong with running the applet.
We're ready to try to run our applet now, but there's a problem. A Java application is potentially dangerous bit of software. Since it's being run on a computer, it might have access to lots of things on your computer that you wouldn't want random internet applications to have access to. Because of this, the default behavior for the Java interpreter is to not allow unsigned Java applications from untrusted web sites. If you want to start distributing Java applets, you'll need to look into how to sign your Java applications so that if you do something bad with your application, your users will know who you are and so that they can track you down and punish you.
What does this mean for us? It means that we'll have to adjust the settings on our computers to allow us to run unsigned Java applets that we've loaded from the Apache web server on Silo. For the first time in a while, we'll have to muck with the settings on the computer we're actually using, as opposed to the computer that is serving the website. That's because Java applets are run client-side, and not server-side. If you log into a different computer later, you'll have to change the Java settings again. You might even have to change the settings if you log into the same computer again, if you're using one of the labs on campus.
Okay, so load up the Java configuration program. If you're on a Windows system, you can just click on the Start
button and start typing Configure Java
into the search bar. The right application should pop up pretty quickly. Once the configuration window pops up, go to the Security
tab and then click on the Edit Site List...
button. There you'll see a list of sites that you can run Java applets from. There might not be anything on that list yet. Click on the Add
button, and type in http://silo.soic.indiana.edu:apacheportnumber
. (Remember to enter in your Apache port number.) Then click Ok
. You'll probably get a warning that sites using unsecured HTTP are potentially dangerous. Click Continue
to ignore that warning. I wouldn't worry too much; it will still give you a warning message any time an unsigned applet wants to be loaded from that domain.*
* If you're really worried that someone else might set up a server on Silo using the same port number and then try to get you to run a malicious Java applet from there, then you can remove the exception when you're done with this class.
While we're in the Java configuration window, there's one other useful change that we can make. Move to the Advanced
tab and look down where it says Java console
. Select show console
, if it isn't already selected.
Finally, click Ok
to save these settings. We're finally ready to view our web page and run our applet. I recommend using Firefox to do this. Chrome will run the applet just fine, but the only way I know to get Chrome to reload the applet file from the website (instead of just reloading the one in the cache) is to completely close down all your Chrome windows and then open them up again. With Firefox, we'll be able to use the Java console to clear the cache and reload the applet. And since we'll have to reload the applet every time we make a change to it, I think it's worth it. I don't know how Internet Explorer or Safari behave when it comes to reloading Java applets.
So open up your browser and load up http://silo.soic.indiana.edu:apacheportnumber/helloworld/hello.html
. You'll probably get another warning (from the browser this time), asking if you really want to run an unsigned application from this web site. Say yes (you can even tell it to always trust applications from this web site if you want to). It should look something like this:
That's a lot of work for just some text, but we can do a lot more with this. Let's start by adding another component to the applet, let's say a button. Unfortunately, the Swing root frame can only have one component in it because it's got no layout manager. But fortunately, there's a special kind of component called a panel that can hold lots of components. It also comes with a layout manager (which we can change or alter) to decide where to put the components on the panel. So we'll create a panel (specifically an object of class JPanel
) and add that to the root frame. Then we'll add our label and our new button (specifically an object of class JButton
) to the panel.
import javax.swing.*;
public class HelloWorld extends JApplet {
public void init() {
JPanel mainPanel = new JPanel();
JLabel label1 = new JLabel("Hello World!");
JButton button1 = new JButton("Push");
mainPanel.add(label1);
mainPanel.add(button1);
add(mainPanel);
}
}
The default layout manager for a Swing panel uses the flow method. This is the same method that browsers use to lay out the parts of a web page written in HTML. Components are just placed in a row, from left to right, until one of the objects is too big to fit, and that one is wrapped to the next row. There are many other layout managers we can use, and there are lots of ways we can tweak each of those layouts but for now, let's just stick with the default, even if the results are ugly.
So make the changes to the source code and recompile it. Now we need to tell Java to refresh the applet and dump the old version is has cached up. If you're using Firefox and you enabled Show console
in the settings, then you should have noticed that as soon as the applet loaded, another window popped up on your computer for the Java Console
. Switch over to that window and hit the X key on your keyboard to clear classloader cache
. Now you can go reload the web page and see the new applet in action. If you're using Chrome, you'll have to shut down all your open Chrome windows and then open Chrome again and reload the page.
The page should look something like this:
You can click the button, but it won't do anything. This is not the same as when we created an HTML form button and then didn't write any code to interpret it. In that case, when you clicked the button, it reloaded the web page, adding some environment variables to the query string in the URL. In this case, it's all happening within the Java applet, and the only thing that the applet knows to do when someone clicks on a button is what the default Swing methods say to do (which is mostly just to make it look like the button was pressed). If we want it to do something, we'll need to add an object called a listener to the button to wait for the button to be pushed and then tell it what to do when that happens. But that's a task for next Monday.