COMP 310
Spring 2010

Lec14:Encapsulated Functionality and Applet/Application Duality

Home  Info  Owlspace  Resources

We forgot to cover image painting the other day...

And once we finish that, we can continue our discussion from last time on action maps...

Encapsulated Functionality: Dynamically Created-User Interface Panels

As a system grows and evolves, perhaps, even while it is running, the needs of user interface change.   Features may come and go or get modified.  How can we manage this without coupling the view to the model or limiting what can be done in the future?  How can we get the view to do something we aren't planning for it to do?

Some useful notions:

Black-box framework: A component-framework system where the functionality of the components is unknown to the managing framework and vice versa.  The relationship between strategies and their hosts is a black-box framework.

White-box framework: A component-framework system where the functionality of the components and the managing framework is known to each other.   The Template design pattern is an example of a white-box framework because the superclass with the template method and abstract helper methods is intimately a part of and known to the sub-classes with their concrete helper methods.

Extensibility hook: A method or collection of methods on a system that allows additional functionality to be added.   The method on the hosts to execute ("accept") visitors in the Visitor Design Pattern is an example of an extensibility hook.

To solve the problem of adding new functionality to our GUI, we need to create a black-box framework with an extensibility hook.

Fortuitously the folks at Sun have already considered the issue of generic GUI creation when they designed the user interface system for Java.   The notion of "container" components (composite components) such as frames and panels, already creates a black-box framework since containers will accept and display an unlimited number of potentially customized Component objects who have unknown functionality. 

So, all we have to do is to expose the ability to add a component to a panel in the view, i.e. create an extensibility hook.   We do have to add a little bit of code to manage the width of the GUI to make sure that the new components display properly, however.

In the BallWar demo, the little panel that holds the choices of movement key sets is dynamically constructed.  It uses a javax.swing.Box container component called OptionPanel that has been customized to hold a label and a drop list, but is otherwise fairly generic.   It exposes a few methods to easily add and remove items from drop list and has a vararg constructor to allow one to easily instantiate it with as many initial choices as desired.   You can get the code for OptionPanel here. (You are in no way obligated to use this code.)  Notice that the OptionPanel is part of the view package as we are still trying to decouple the model from any UI components.

Here are the rest of the pieces we need:

In the controller:

We need to instantiate the OptionPanel. I am using some statically-defined IMovementKey sets that were created as a convenience to define the two most popular sets of movement keys.

private OptionPanel _keysPnl = new OptionPanel("Movement Keys", IMovementKeys.ARROWSEND, IMovementKeys.WASDX );

Once the view is instantiated, the movement key panel can be added to it via its extensibility hook (discussed below):

_view.addComponent(_keysPnl);

The model needs the movement keys when it creates a player, but the model has no idea where those movement keys are coming from.   On the other hand, the GUI doesn't even know that the movement keys even exist.   The movement key choices will appear on the GUI when its panel is added (see above) but the view never knows what that new component does.   Thus it is up to the controller to hook the movement key panel to the model, which is done inside the IGameControlAdapter when the view asks the model to create a player.   Here, the movement key set is obtained by removing the selected item from its drop list.

new IGameCtrlAdapter() { public Object makePlayer(String name) { return _model.makePlayer(name, (IMovementKeys) _keysPnl.removeSelectedItem()); } ...etc..

In the GUI:

The GUI must have an extensibility hook that allows a new component to be added, here, to the control panel where the rest of the UI controls are displayed.   The new component whatever it may be, is simply added and displayed as per the existing layout manager for the control panel.  Notice that the addComponent method takes the most abstract component that could be handled by the system to minimize any restrictions on the new components and thus gain maximum flexibility and extensibility.  A utility method to automatically resize the width of the GUI to insure that the new component can be seen is also needed.

public void addComponent(Component c) { _controlPnl.add(c); autoWidth(); } public void autoWidth() { int height = getHeight(); validate(); pack(); setSize(new Dimension(getWidth(), height)); }

 

The added component is really just another view entity in the system, which just happens to physically reside inside of another view.  But other than that, it is essentially a completely separate view being attached to the MVC system.

 

Running as both applet and application  -- the beauty of MVC

In the Java world, to run as application, the code needs a main() method somewhere that the Java runtime engine (JRE) can execute to run your program.   But to run off the web as part of a web page, the code must have an Applet class (or its subclass  an JApplet) somewhere, which has special start, stop and init functions that the web page will utilize to run and manage the program.

Luckily, by utilizing an MVC architecture, the management of the entire program is encapsulated in a single class, the controller.   That is, all that is needed to run our program is to instantiate and start the controller.   The difference between an application and an applet comes down to how the controller is instantiated and started...except for one little detail of making sure that the frame doesn't shut the JRE down when it closes as an applet, but that's easy to deal with.

Here's the gist of the trick:  The controller is a JApplet sub-class with a main() method.

First, let's look at what an applet must have, because that will tell use what we need to do in the main() method.   An applet must at least have the following two methods:

public void init()

This method is called by the web page when the applet is first loaded.   It is the applet's equivalent to the main() method.  Bottom line:  all of our code to instantiate and connect the model and view goes in here.   The web page will instantiate the applet (the controller) by calling its no-parameter constructor, so we must define a no-parameter constructor.  That constructor's body can be empty because all the actual work will be done in the init() method.

public void start()

This method is called when the web page shows the applet.   This may be called more than once if the web page loses focus because the user switches away from it and back or navigates from and back to it.   But this method is exactly the same as the start method we already have that is used to start the timers, etc. once the model and view have been fully instantiated and connected.  Bottom line:  we already have this method.

public void stop()  (optional)

This method is called by the web page when the page loses focus because the user has navigated or switched away from it.   You can use this method to stop your times.

With these two required methods, the program will run as an applet, once we fix what happens when we close the main frame...

Controlling the System Exit Behavior

By default, when you close the main frame of the GUI, a call goes out to System.exit(0) , which shuts the JRE down, causing the program to exit to the operating system.   The only problem is that when run as an applet, doing a system exit is generally bad news because the applet is not being run from the operating system as applications are but rather, from a web browser.  We need to change the window closing behavior of the main frame when running as an applet.

There are various techniques to change what happens when a frame closes.   See the web page in the Comp310 Java Resources for possibilities.   Any method is fine to use.  Make the following changes to your system:

  1. Change the constructor of the GUI to take a parameter that will control the window closing behavior, such as the window closing option or a WindowListener.

  2. Add code to the GUI to use that new parameter to explicitly set the window closing behavior.

  3. Add field to the controller that holds the value of the window closing parameter.   By default, set it to do nothing, which is the setting that an applet needs.

  4. Modify the code in the controller so that it uses that value when it constructs the GUI.  

  5. Add a parameterized constructor to the controller that

    1. Takes a window closing parameter as an input parameter and sets the field value to it and then

    2. Calls the init() method to perform the model and view construction.

The main() method

The controller's public static void main(String[] args) method needs to do only two things, which can be accomplished in only one line of code:

  1. Construct the controller using the parameterized constructor, handing it a window closing parameter that does a system exit when the frame is closed, then

  2. Start the controller.

The program will now run properly as an application.

Making a web page for an applet:

Using either Java's built-in JAR capability or Eclipse's JAR Export feature, create a JAR file of your program.   If you create a manifest that points to the controller, the JAR file will also run as an application (double-click it). 

To create a web page that will show your beautiful program, simply include the following line somewhere in the body of the web page (change the names to match your system):

<applet code="ballwar.controller.BallControl.class" archive=BallWar.jar width=1 height=1></applet>

This creates a little 1x1 pixel transparent dot on the web page that will create the running applet when the page is loaded.  This is how all the Ballworld/Ballwar demo pages are made.

Upload your JAR file to the same directory as the web page that shows it and you will be able to show off your work to the world!

 

 


© 2010 by Stephen Wong