Home arrow Java arrow Deployment Frameworks
JAVA

Deployment Frameworks


Once you have learned how to program games in Java, you will want to be able to deploy them on different types of deployment frameworks. This article will cover browser applets, executable JARs, and Java Web Start. It is excerpted from the book Advanced Java Game Programming written by David Wallace Croft (Apress, 2004; ISBN 1590591232).

Author Info:
By: Apress Publishing
Rating: 4 stars4 stars4 stars4 stars4 stars / 11
April 12, 2005
TABLE OF CONTENTS:
  1. · Deployment Frameworks
  2. · Reading from a JAR File
  3. · Signing Applets
  4. · Deploying with Java Web Start
  5. · Isolating Optional Packages
  6. · Deploying Multiple Applets as One
  7. · Lifecycle
  8. · MultiApplet
  9. · CroftSoftCollection

print this article
SEARCH DEVARTICLES

Deployment Frameworks
(Page 1 of 9 )

Diligence is the mother of good luck. —Benjamin Franklin

In this chapter, you will learn about the different ways to deploy your Java games using the various types of deployment frameworks. By framework, I mean standardized mechanisms for a container to deploy, launch, run, and gracefully terminate your games on multiple platforms. The frameworks covered include browser applets, executable JARs, and Java Web Start. I detail a reusable framework that will permit you to deploy your games to all three of these environments without recompiling. I achieve this by identifying the methods required for animation thread management, then defining them within a common interface.

Deploying as an Applet

Applets are traditionally thought of as small Java programs that are used to display simple animations embedded within a web page. This is not strictly true, as applets can be large programs used for purposes other than animation, in containers other than web browsers. What is common to all applets, however, is that they all extend class java.applet.Applet, which lets you use them in any Applet-compatible framework.

Implementing the Lifecycle Methods

I'll never forget the definition of the term framework given to me in a Design Patterns course I took a few years back. The instructor stated simply, “It is the Hollywood model: ‘Don’t call us, we’ll call you!’” For a container to initialize, start, stop, and destroy your game object, it must have access to pre-defined methods that it can call on the game object as required. Because these lifecycle methods are consistent within a given framework, any game written to that framework can run within a framework-compatible container regardless of the vendor or platform.

Class java.applet.Applet defines the four lifecycle methods init(), start(), stop(), and destroy() which are called by a framework-compatible container such as a web browser. Applet subclass implementations override one or more of these empty methods to create the animation. There are some subtleties to the semantics so I recommend that you review the Javadoc for these methods. The following comments are supplementary.

Method init()

You initialize your game using the init() method. You load your images, audio files, and other static content here. You create any dynamic data you need to generate before the game can start in the init() method as well. You may also load previously saved game data such as the high score in this method.

Keep in mind that the init() method is never called twice on the same Applet instance. None of the other methods in the Applet subclass are called before the init() method with the exception of the no-argument constructor method. This means you should not have to worry about the container attempting to display your applet by calling its paint() method before it is ready.

I have always been told that you should perform all your initialization code in the init() method and not in a constructor. In fact, before Java 1.4, the Applet class did not even define a public constructor method that subclass implementations could override.

Method start()

The start() method starts the game-loop thread that drives the animation. The start() method is not only used to start animation, or whatever the applet is doing, but also to resume animation after it has been stopped. A typical example of when a container may resume processing an applet is when a player minimizes and then maximizes the web page window. The container may call the start() method many times throughout the life of an individual applet instance.

When implementing your Applet subclass implementations, you should be prepared to receive a call to your paint() method by the container, usually a web browser, before the container ever calls your start() method for the first time. The idea that your code might be called upon to display its first frame of animation, or at least something, before the animation thread starts is not intuitive. If your code occasionally or even consistently throws a NullPointerException from the paint() method when the applet is first loaded, but not when it has warmed up, you probably have some of your graphics initialization code in your start() method. Move it to your init() method and all will be well.

Method stop()

The stop() method is used to suspend the animation when the game is paused. According to the method definition, the container might call the stop() method multiple times throughout the life of the applet instance. Can stop() be called multiple times by the container without an intervening call to start()? Can start() be called multiple times by the container without an intervening call to stop()? I am not certain so I often write my implementations of these two methods in such a way that it does not matter.

Note also that the stop() method is guaranteed to be called right before the destroy() method. I like to implement my destroy() methods under the assumption that the container might or might not have already called stop(). I do this because I do not trust the container to implement the framework contract properly, having created some slightly buggy home-grown applet containers myself. I also do this because the call to stop() might have failed and thrown an exception.

You can write an ill-behaved stop() method implementation. For example, when called by the container, your stop() code can stall until the container finally kills the thread. Your code can also ignore the call to stop() and continue running, treating it as a suggestion rather than a command. I do not know about you, but when I implement code such as this, it is usually not on purpose.

Method destroy()

The container calls the destroy() method when the game ends to de-allocate any resources used by the game. Here are a few quick facts about the destroy() method. The container never calls it more than once in the life of an Applet instance. With the exception of the finalize() method, no other method in the Applet is called afterward. A container never reuses an Applet instance after its destroy() method is called by calling its init() method once again. Just as with the stop() method, you can write a rude destroy() method implementation that does not do what is expected.

You might wonder why you need to even have a destroy() method. After all, the garbage collector should automatically reclaim or close all resources used when the container dereferences the applet. The problem with this logic is that the garbage collector might not process the applet right away. This means that your applet might hold on to resources such as unclosed Socket connections, undisposed Graphics contexts, and undisposed Window native screen resources indefinitely. If you notice that your applet always crashes after being used several times, but never just after restarting your browser, you should suspect that you are not completely destroying all your initialized resources at the end of the applet lifecycle.

The Object class finalize() method is meant to be overridden by subclasses that want to perform some action, usually the release of resources held, right before an object is garbage collected. Do not use the finalize() method. It is unreliable because there is no guarantee that it will ever be called. If there is sufficient memory, the garbage collector might not bother collecting garbage. If the JVM is shutting down, it might not bother to call finalize() on its way.

In general, it is best to rely upon the destroy() method for the timely release of system resources.

Managing the Applet Animation Thread

What was once prescribed is now proscribed when it comes to starting and stopping applet animation threads. In the past, the lifecycle method implementations might have looked like this:

  public void start ( )
  //////////////////////////////////////////////////////
  {
  animationThread = new Thread ( this );
  animationThread.start ( );
  }
  public void stop ( )
  //////////////////////////////////////////////////////
  {
   animationThread.stop ( );
  }

Or even like this:

public void start ( )
////////////////////////////////////////////////////////
{
  if ( animationThread == null )
  {
   animationThread = new Thread ( this );
   animationThread.start ( );
  }
  else
  {
   animationThread.resume ( );
  }
}

public void stop ( )
/////////////////////////////////////////////////////////
{
  animationThread.suspend ( );
}

public void destroy ( )
/////////////////////////////////////////////////////////
{
  animationThread.stop ( );
}

The problem with these techniques is that using the java.lang.Thread class methods stop(), suspend(), and resume() can sometimes put an object in an inconsistent state or cause a deadlock. This can result in random side effects that are difficult to debug. For this reason, these methods are now deprecated and should not be used in almost all circumstances. I have seen strange bugs simply disappear as a result of replacing old code that used Thread.stop(). Please see the online article from Sun “Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated?”1

public void start ( )
/////////////////////////////////////////////////////////
{
  stopRequested = false;
  animationThread = new Thread ( this );
  animationThread.start ( );
}

public void stop ( )
////////////////////////////////////////////////////////
{
  stopRequested = true;
}

public void run ( )
////////////////////////////////////////////////////////

{
  while ( !stopRequested )
  {
    animate ( );
  }
}

Instead of hard-stopping a thread, you can let it die gracefully by having it poll a boolean stop request flag in a loop as shown in the preceding code. Note that the animation thread does not stop immediately when the request is made, as the stop request flag is only checked once per loop iteration. This can cause a problem if the start() method is called immediately after the stop() method but before the animation loop has had a chance to poll the flag. It is possible with this technique that with some bad luck and bad timing you might end up with two or more threads running simultaneously.

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

public void stop ( )
/////////////////////////////////////////////////////////
 {
  animationThread = null;
 }

public void run ( )
////////////////////////////////////////////////////////
{
 Thread animationThread = Thread.currentThread ( );
 while ( animationThread == this.animationThread )
 {
  animate ( );
 }
}

The preceding code addresses that issue. Here the test is not a shared boolean flag but rather whether the current thread is still the one that the applet instance has designated as the primary animation thread. This operation is performed using an identity comparison. Note that in the run() method, animationThread is a local method reference and this.animationThread is an object instance reference. In the worst case, the old animation thread only overlaps the new animation thread for one more iteration before bowing out.

public synchronized void start ( )
////////////////////////////////////////////////////////
{
  stopRequested = false;
  if ( animationThread == null )
{
  animationThread = new Thread ( this );
  animatonThread.start ( );
 }
 else
 {
   notify ( );
 }
}
public synchronized void stop ( )
/////////////////////////////////////////////////////////
 {
  stopRequested = true;
  animationThread.interrupt ( );
 }
public void run ( )
////////////////////////////////////////////////////////
  {
  while ( animationThread != null )
  {
   try
   {

    animate ( );
   if ( stopRequested )
   {
    synchronized ( this )
    {
      while ( stopRequested )
      {
       wait ( );
      }
     }
   }
 }
  catch ( InterruptedException ex )
  {
  }
 }
}
public synchronized void destroy ( )
////////////////////////////////////////////////////////
{
  animationThread = null;
  stopRequested = false;
  notify ( );
}

The preceding code demonstrates how to prevent old and new animation threads from overlapping even momentarily during the transition by relying upon a single thread that is suspended and resumed using a boolean flag stopRequested and the Object class wait() method. Note that the code uses synchronization, which tends to slow things down. This should not be a major problem because no synchronization requests are within the main animation loop when the animation is not stopped.

Methods start(),destroy(), and part of method run() are synchronized because any thread that calls the notify() or wait() method must be the owner of the object monitor. Method stop() is synchronized because it modifies the shared variable stopRequested.

Note that the stop() method calls animationThread.interrupt(). This is because the animate() method in the loop might monitor the interrupted status to determine if it should exit early. It might check the status at successive steps in a lengthy animation-frame generation process. It definitely checks the status if it uses Thread.sleep() to delay within the animate() implementation to slow down the loop to a desired frame rate.

The animation loop exits when the destroy() method dereferences the animationThread. Note that the animation loop does not exit immediately when destroy() is called but only upon the check at the beginning of the next loop iteration. This means that the loop could still be using resources in the next-to-last loop iteration for animation after the destroy() method has completed. This is most likely to occur if the container calls the destroy() method immediately after the stop() method. If you intend to add additional code to the preceding destroy() method to de-allocate resources used during animation, you might need to place some of it at the end of the run() method instead.

The try/catch block within the loop is used to catch InterruptedExceptions thrown either by the animate() method or by the wait() method. Note that it will probably be thrown most of the time by the animate() method as the animationThread.interrupt() call is in the stop() method. The stop() method is normally not called when the thread is already suspended on the wait().

Calling the stop() method without an intervening call to start() advances the animation by exactly one frame. This is because the wait() throws an InterruptedException and the loop then continues for half an iteration. It calls animate() just once before stalling on the wait() command again. Normally, a lifecycle container does not call stop() twice in a row and this single frame advance feature was not planned when the code was designed. You might, however, find it useful nonetheless.

Because I believe that this last technique for animation-thread management is the most robust, I selected it for the design of the reusable animation library described in the following chapter.

This article is excerpted from Advanced Java Game Programming by David Wallace Croft (Apress, 2004; ISBN 1590591232). Check it out at your favorite bookstore today. Buy this book now.


blog comments powered by Disqus
JAVA ARTICLES

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials