Home > Projects > Course Projects > ICE Design
Brian Harrington
University of North Texas
Denton, Texas
brh@cs.unt.edu
Abstract: This paper provides an overview of the design for the International Collaborative Environment (ICE) software. It includes an overview of the design decisions and some explanations for them for the primary parts of the software including the server, client, and some of the current plugins.
Introduction
The International Collaborative Environment (ICE) software is a collection of several applications including the server, client, administrative applications, and plugins built upon a common set of libraries. The diagram below shows the general layers of the software:

Figure 1: Layers of ICE software
These layers roughly correspond to the packages. The layers have the following roles:
- Environment: this is the Java Runtime Environment (or if generalized the environment the software is based on such as wxWindows, Qt, Windows API, etc.).
- Platform: this layer contains utility classes and extensions built on top of the environment that are not specific to ICE. For example, a font chooser component since one is not provided in Swing.
- Core: this layer contains utility classes that are common to all of the applications such as the client, server, and administrative programs.
- Server: this package contains the server application.
- Client: this package contains the client application.
- Plugin API: this package contains the API for plugins. The container part is implemented by the client application and the plugin part is implemented by all plugins.
- Plugins: these packages are for applications that implement the plugin API's.
The diagram below shows the package structure of the ICE software and the various package dependencies that exist:

Figure 2: Package structure
As you can see in general packages from lower layers should not have any dependencies on packages in higher layers. The only exception to this is the plugin API because it is meant to be two sided (that is both the plugins and the container can be changed). The rest of the paper provides a more detailed look at the various packages and the design of those pieces.
Platform
The platform package contains miscellaneous utilities that are not specific to ICE. The classes in this package only depend on the environment and third party libraries. There are no dependencies between this package and any other package of ICE. The five subpackages of platform are GUI, IO, Net, SQL, and Util.
Note, this section does not discuss the design of individual aspects of classes in the platform package. Most of the classes do not depend on each other and some are from other sources. This section provides a brief overview of what each subpackage contains and what it is intended for.
GUI
This package contains custom or enhanced widgets that are commonly needed for ICE applications such as a font chooser, enhanced file chooser, custom layout managers, etc. Custom widgets all start with an "I" similar to the Swing convention of widgets starting with a "J". For example, the font chooser is named "IFontChooser".
In general this package is where any general widget or GUI utility class would be placed.
IO
This package contains the following custom IO streams:
Bit: simplifies dealing with individual bits in a stream. In addition, it provides methods for working with crumbs (2 bits) and nibbles (4 bits).
RLE: these streams perform run-length encoding on the data for simple and fast compression/decompression of certain data (for example screen captures).
VT100: provides a simple way to output data formatted for VT100 terminal emulators (output stream only, a good VT100 reference can be found at [1]). In general this package is where any custom IO classes are placed.
Net
This package contains networking utilities for doing FTP connections as well as other common tasks such as listening to a specific port in a background thread. In general this package is where any network protocol classes are placed.
SQL
This package contains utilities for simplifying the task of working with databases. The two primary utilities are:
Database: wrapper for dealing with database connections. Provides utilities for removing escape characters from SQL strings and reconnecting if connection failed.
DatabasePool: pool of Database objects similar to commonly used connection pools. The primary difference is the database object already provides mechanisms for reconnecting if a connection fails so the database pool is a little bit simpler than connection pools.
Util
This package contains miscellaneous utilities that do not fit in the other packages. These include reflective actions [2], base 64 encoding [3], and a thread pool [4].
Core
The core package contains the base classes and parts of ICE that are used throughout the other parts and are specific to ICE. The core package has four basic parts: data objects, data storage/retrieval, talkback, and networking.
Data Objects
There are three basic objects that need to be stored and dealt with in the system: users, groups, and logs. The following class diagram shows the structure of these objects:

Figure 3: Structure of data objects
Data Storage/Retrieval
There are two interfaces called DataStore and DataStoreFactory which define the required database operations that are needed. The implementations of this define how the data is stored. There are currently two implementations: JDBO (serialization to disk) [5] and JDBC (relational database). The data store API follows the factory design pattern [6] as shown in the figure below:

Figure 4: Structure of data store API
Talkback
Talkback provides feedback capabilities by allowing exceptions to be sent via email to an administrator. So that there is no dependency on the configuration files there is a hardcoded email address. However, it can be overridden by using the ice.talkback property. Talkback has the following structure:

Figure 5: Structure of talkback
Networking
The software abstracts the networking so that it is easy to modify the software to use different network implementations such as TCP, UDP, or tunneled through HTTP to get around firewalls. The API uses the Abstract Factory [6] pattern to create clients and servers. The following shows the structure:
Figure 6: Structure of networking API
Server
ICE uses the directory services [7] approach for identifying peers (other ICE clients). The server fills this role as well as providing mechanisms for getting around NAT devices and collecting log data.
Directory Services
The primary role of the server is to provide peer identification. This part is task/request oriented. Peers connect and give the server a task or request that can be one of six things: verify, connect, disconnect, check in, firewall, and user message relay (for the first four a detailed overview is provided in [8]). The last two have to do with getting around simple firewalls or NAT devices that block incoming connections. The firewall task checks is incoming connections can be made to the machine. If connections cannot be made the client will keep a connection to the server, then messages for that user will be sent to the server which then relays them to the client.
HTTP and FTP
The ICE server also includes HTTP and FTP servers. The HTTP server is a version of Apache Tomcat that is embedded [9] to provide easy administration of the database. This can also be used to host updates for the application launcher.
The FTP server is primarily used to collect the log data form the clients. In addition it can also be used to provide the FTP server that backs the file sharing plugin.
Configuration
The ICE server has one properties file, iceserver.ini, that is used to adjust its configuration. An example configuration file is provided below. The comments in the file explain what each field does.
# Web Administration Password ice.admin.password = password # Invisible user password ice.inv.password = password # Port used for logging in clients. ice.server.port = 3000 # Port for the FTP server ice.ftp.port = 2800 # Name of log file ice.server.log = server.dat ice.server.error = error.dat # Period to use for clean up check in minutes ice.server.time = 20 # Data store factory ice.datastore = ice.core.SQLDataStoreFactory # Database ice.db.driver = org.gjt.mm.mysql.Driver ice.db.dsn = jdbc:mysql://localhost/ICEDatabase?user=brh&password=pswd ice.db.username = ice.db.password = # Number of database connections to use for pool ice.db.count = 2 # Web administration settings ice.web.on = true ice.web.port = 9090
Plugins
Plugins are the central part of the client system. The client is a flexible container for plugins and has no interface without them. The plugin API has the following structure:
Figure 7: Structure of plugin interfaces
Basic Plugin
Writing a plugin consists of subclassing the Plugin class. This will provide the plugin with basic facilities to interact with the system such as determining users, networking, and logging. The following example shows how to create a simple chat plugin:
package ice.plugin.examples;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import ice.core.*;
import ice.plugin.*;
public class ChatExample extends Plugin
{
private JTextArea incoming;
private JTextField field;
// Plugins must have a constructor with no parameters
public ChatExample()
{
super();
setLayout(new BorderLayout());
incoming=new JTextArea();
incoming.setEnabled(false);
add(new JScrollPane(incoming), BorderLayout.CENTER);
field=new JTextField();
field.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
sendToGroup(field.getText());
incoming.append(getUser().getName()+"-> "+
field.getText()+"\n");
field.setText("");
}
});
add(field, BorderLayout.SOUTH);
}
// Must be overridden and must be unique for each plugin in the system
public String getName()
{
return "Chat Example";
}
// Override receive method to get messages
public void receive(User user, String message)
{
incoming.append(user.getName()+"-> "+message+"\n");
}
}
Controllable Plugins
Some plugins require that only a one user can use the plugin at a certain time. These should extend ControllablePlugin. The ControllablePlugin class provides capabilities to swap control reliably and will keep new plugins consistent with others that already use this capability. The following example is a modified version of the chat example to support control:
package ice.plugin.examples;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import ice.core.*;
import ice.plugin.*;
public class ControlChatExample extends ControllablePlugin
{
private JTextArea incoming;
private JTextField field;
private JLabel status;
private JButton request;
// Plugins must have a constructor with no parameters
public ControlChatExample()
{
super();
setLayout(new BorderLayout());
incoming=new JTextArea();
incoming.setEnabled(false);
add(new JScrollPane(incoming), BorderLayout.CENTER);
ControlledField cf=new ControlledField();
cf.setLayout(new GridLayout(2, 1));
setControllableComponent(cf);
field=new JTextField();
field.setEnabled(false);
field.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
sendToGroup(field.getText());
incoming.append(getUser().getName()+"-> "+
field.getText()+"\n");
field.setText("");
}
});
cf.add(field);
JPanel panel=new JPanel();
panel.setLayout(new BorderLayout());
status=new JLabel("Someone must request control.");
panel.add(status, BorderLayout.CENTER);
request=new JButton("Request Control");
request.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
requestControl();
}
});
panel.add(request, BorderLayout.EAST);
cf.add(panel);
add(cf, BorderLayout.SOUTH);
}
// Must be overridden and must be unique for each plugin in the system
public String getName()
{
return "Control Chat Example";
}
// Component that is being controlled
public class ControlledField extends JComponent
implements ControllableComponent
{
// Override receive method to get messages
public void receive(User user, String message)
{
incoming.append(user.getName()+"-> "+message+"\n");
}
// Do any synchronization that needs to be done at startup, note control
// syncronization will be taken care of for you
public void synchronize(User user)
{}
// Set the enabled status of controlled component
public void setEnabled(boolean enabled)
{
field.setEnabled(enabled);
}
// Stuff to do when you start controlling
public void startControlling()
{
request.setEnabled(false);
}
// Stuff to do when you stop controlling
public void stopControlling()
{
request.setEnabled(true);
}
// Shows who has control in status bar, text is null for default message
public void setStatusText(String text)
{
status.setText(text==null ? "Someone must request control." : text);
}
}
}
Remote Control
With all plugins you can get a Remote object which will allow you to call public methods of the class that extends plugin with either no parameters or one string parameter of the subclass of the plugin. This is useful because it provides access to the plugins for other plugins or scripts without the need to hardcode for a specific plugin. The following example shows how to make the Text To Speech plugin say "Hello World":
Plugin p=getPlugin("ice.plugin.tts.TTS");
Remote r=p.getRemote();
r.call("speak", "Hello World");
Client
The ICE client is designed to be a flexible container for plugins. Basically it provides an implementation for the PluginImpl class based off of the core package and provides a graphical interface for embedding the plugins to show the user.
The client uses the objects provided in the core package to deal with users and groups. It also uses implementations of the networking API's provided in the core to communicate with the server and provide the necessary support for the networking methods in the plugin API.
The client interface is similar to what is provided with the border layout manager. It composes split panes to provide five distinct areas on the main frame where a plugin can be placed using the ice.dock.<north | south | west | east | center> properties in the INI file. The diagram below shows the sections available for placing plugins. In addition the system dynamically creates the menu from a XML file that is provided in the configuration set. The menu system allows for plugins to be loaded and have methods called or to be shown in a dialog. In order to make the application load faster none of the plugins are loaded until they are needed. However, this means that you must be sure and test all options carefully since there is no way of knowing if the code for a plugin is there until it is loaded at runtime. Note if plugins rely on user listeners to update events it is also wise to use a plugin listener to refresh the user list. Otherwise the plugin may show empty user lists because no user events have come in since it was loaded. If a plugin must receive all user events then it should be placed in the interface so it has to be loaded to create the original interface.

Figure 8: Parts of client interface
Distributions
Distributions of the ICE software that have the same binaries but different sets of configuration files. A front end is provided so that a user can choose which distribution he/she wants to use. There is a file in the java properties format that provides the details for which distribution to use called dists.ini. A sample distribution configuration file is shown below:
# Title for dialog ice.dist.title = Distribution # Question to ask ice.dist.question = Please select: # Default path to use, if negative it will not show dialog and will use # ice.dist.(|ice.dist.default|-1) ice.dist.default = -1 # Available distribution options ice.option.0 = Student System ice.option.1 = Trial System # Path for option, (numbers should match options above) ice.dist.0 = student ice.dist.1 = trial
INI File
The INI is where the options for the ICE client and its plugins can be set. The file is in the format for the java.util.Properties class. At startup this file is loaded and added to the system properties. This means that the properties can easily be accessed from anywhere in the code by using the java.lang.System.getProperty() method. If a system property already exists with the same key as one in the INI file it will not be overwritten. This allows for properties in the INI file to be overridden by using the -D option at runtime. The following is the base INI file:
############################################################################## # # International Collaborative Environment 2.00 # # This file specifies the runtime attributes for the ICE client software. This # file should only be modified by system personnel. # ############################################################################## # INI Version, note ice.version should be set by launcher ice.ini.version = 2.00 pre 3 ice.ini.updated = April 15, 2003 ice.ini.dist = Student System # Minimum JRE version on which this will run ice.jre.version = 1.4 # ICE login and time server. ice.server = localhost ice.server.port = 3000 ice.client.port = 3200 # Use free ports, ice.client.port option ignored if true ice.freeports = false # Keep alive interval in seconds ice.keepalive = 600 # Logging mode, either remote or local, and local log increment ice.log.on = true ice.log.server = localhost ice.log.port = 2800 ice.log.username = anonymous ice.log.password = vcu@icsd ice.log.increment = 20 ice.log.dir = local_logs # Components to use for ICE ice.dock.north = ice.plugin.chat.Chat ice.dock.north.width = 170 ice.dock.west = ice.client.plugins.ActionBar ice.dock.west.width = 120 #ice.dock.east = test.gui.PluginTest #ice.dock.east.width = 400 #ice.dock.south = test.gui.CrashPlugin #ice.dock.south.width = 428 ice.dock.center = ice.client.plugins.MDI # Macro support class to use #ice.client.macro = ice.client.jess.JessMacroSupport #ice.client.macro = ice.client.bsh.BshMacroSupport ice.client.macro = test.misc.PropertiesMacroSupport
Normally there will also be entries for the properties of plugins and macro support classes as well. See the documentation for a particular plugin to determine there properties.
Menu Configuration
The main menu of the application is configured based off of a file called menu.xml. An example menu file is shown below:
<menubar>
<menu mnemonic="S" name="System">
<menuitem name="Connect to group" plugin="System" action="connect"
enabled="disconnected" />
<menuitem name="Open whiteboard" plugin="ice.client.plugins.MDI"
action="showWindow" param="ice.plugin.whiteboard.Whiteboard"
enabled="connected" />
<separator />
<menuitem name="Exit" plugin="System" action="exit" />
</menu>
</menubar>
The menuitem tag can have five different attributes: name, plugin, action, param, and enabled. The param and enabled attributes are optional. The attributes specify the following:
- name: gives the string shown to the user in the menu.
- plugin: class name of plugin that has the method or "System". If it is "System" the action can be any of the public methods from the ice.client.MenuHandler class.
- action: the method to execute or it can be <modal> or <non_modal>. If if is one of the modal entries the plugin will be shown in a dialog that is either modal or non-modal respectively.
- param: this is for passing a string parameter to a method that requires one.
- enabled: is used to tell when the menu item should be enabled. This can be one of always, connected, or disconnected. The default is always.
You may have noticed one large omission, there is no way of specifying accelerators for menu items. This is a fairly easy feature to add and was left out for a good reason. Plugins can have their own menus which in turn can have accelerator keys. Allowing the main menu to have them would make the interface confusing because you would never know which one would be processed if there is more than one menu item with the same accelerator keys. To overcome this problem plugin menus use accelerators since they would need the more common ones such as cut and paste, but do not use mnemonics for the menu. The system menu uses mnemonics but not accelerator keys. This allows for keyboard access to all menus.
As some users have found it confusing to have menus on the plugins themselves it would probably be best to create a common menu policy for all plugins and integrate this with the system menu. Toolbars and context menus could then be used to overcome common features such as cut, copy, and paste that are found in multiple plugins.
Messages Configuration
There are two XML files that can be used to configure messages within the system. The first is for setting error messages called errors.xml. An error message has three parts: the title, message, and fix. The title is shown in the title area of the dialog and is usually set to the name of the application (ICE) or of the plugin that generated the error. The message is a short description of the problem and is shown in bold. The fix is a brief paragraph telling the user what the recommended approach to dealing with the problem is. An example error file is shown below:
<errors>
<error name="bind">
<title>ICE</title>
<message>Could not bind to port $port.</message>
<fix>The system needs to be able to listen on port $port for
connections from other users. Check to make sure there are no other
applications on port $port and try to reconnect.</fix>
</error>
</errors>
The name attribute is how the specific error message is looked up. The message and fix sections can also have variables which start with a "$" character similar to PHP or Perl. Note that the replacement value for the variable must be explicitly given in the code so you do not want to use arbitrary variables. For example in the sample above "$port" would be replaced by the port number. The code below shows how to display an error in your code:
import ice.platform.gui.*;
...
GUIUtilities.showError("errors.xml", "bind",
new String [] {"\\$port", "3200"});
The questions file is similiar but is for questions with a yes/no answer. A question has two parts: the title and the question. An example question file is shown below:
<questions>
<question name="override">
<title>ICE</title>
<message>The username "$username" is already logged in.
Do you want to continue?</message>
</question>
</questions>
The message field still supports variables and the title is still the name of the application or plugin. An example asking the user a question is shown below:
import ice.platform.gui.*;
...
int answer=GUIUtilities.showQuestion("questions.xml", "override",
new String [] {"\\$username", username});
if(answer==IMessageBox.APPROVED)
{ ... }
else if(answer==IMessageBox.CANCELLED)
{ ... }
Using this approach has a number of advantages to just showing a JOptionPane or you own dialog for errors or questions for the user. The primary benefits of this approach are:
- Helps keep messages consistent for all parts of the application.
- Messages can be edited without knowing the code.
- It makes it easier to do internationalized versions if desired.
- It makes it easier to update the user's guide since the messages can just be copied into the manual.xml file.
Macro Support
A simple way to process and provide custom events or actions is to implement the macro support class. The system can have one macro support implementation configured at a time by specifying the ice.client.macro property. An example implementation is shown below:
package test.misc;
import java.net.*;
import java.util.*;
import ice.core.*;
import ice.client.*;
import ice.plugin.*;
/**
* Example for how to write a macro support class. This class will execute
* properties files as scripts if the file exists. The property files have the
* format:
*
* # <class name>=function
* # <class name>=function:parameter
*/
public class PropertiesMacroSupport extends MacroSupport
{
/**
* Registers a user listener so we can detect when user connects,
* disconnects, etc.
*/
public void init()
{
getUserManager().registerUserListener(new UserAdapter()
{
public void userConnected(User u)
{
load("ice.props.startup");
}
public void userDisconnected(User u)
{
load("ice.props.shutdown");
}
});
}
/**
* Invoked when a message is sent from a plugin in the system to a user.
*
* @param user the user the message is to
* @param plugin the plugin that sent message
* @param message the message that was sent
*/
public void sent(User user, String plugin, String message)
{
load("ice.props.sent_user");
}
/**
* Invoked when a message is sent from a plugin in the system to a group.
*
* @param group the group the message is to
* @param plugin the plugin that sent message
* @param message the message that was sent
*/
public void sent(Group group, String plugin, String message)
{
load("ice.props.sent_group");
}
/**
* Invoked when a message is sent from a plugin in the system to all users.
*
* @param plugin the plugin that sent message
* @param message the message that was sent
*/
public void sent(String plugin, String message)
{
load("ice.props.sent_all");
}
/**
* Invoked when a message is received from a plugin in a remote system.
*
* @param user the user the message was from
* @param plugin the plugin to receive message
* @param message the message that was received
*/
public void received(User user, String plugin, String message)
{
load("ice.props.received");
}
/**
* Load and execute the code in the file with the given name.
*
* @param file file with the code to execute
*/
private void load(String param)
{
// Check if the param is there
String file=System.getProperty(param);
if(file==null)
return;
try
{
// Get input stream
URL url=getClass().getClassLoader().getResource(file);
if(url==null)
return;
Properties props=new Properties();
props.load(url.openStream());
// Execute
Enumeration enum=props.propertyNames();
while(enum.hasMoreElements())
{
String name=(String)enum.nextElement();
String function=props.getProperty(name);
Plugin p=getPlugin(name);
Remote r=p.getRemote();
if(function.indexOf(":")!=-1)
{
String[] parts=function.split(":");
r.call(parts[0], parts[1]);
}
else
r.call(function);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
A sample properties file which speaks and opens some windows is shown below:
# Example script using properties format that says hello to the user when he # logs in. ice.plugin.tts.TTS = speak:Good Morning, how are you today? # Opens the whiteboard in the MDI plugin ice.client.plugins.MDI = showWindow:ice.plugin.whiteboard.Whiteboard
Client Plugins
As the client is just a container for plugins and does not have any interface there are a few simple plugins which are provided in the plugins subpackage of the client package to provide basic functionality. These include:
- About: this plugin provides information about the current system including the versions and distribution name. It also has a list of acknowledgements for groups/people who have helped with the project.
- ActionBar: this plugin provides a toolbar that opens plugins in the MDI plugin when a user selects it. ActionBar displays the large icon for a plugin. The plugins it has on the toolbar can be configured using the ice.actionbar.<number> properties in the INI file.
- Calendar: this plugin provides a basic calendar for the user. This plugin is based on the JCalendar component [10].
- Feedback: this is a simple plugin that allows the user to enter their opinions and send them to an email address specified with the ice.feedback.email property.
- MDI: this plugin provides a multiple document interface (MDI) container for plugin windows. Plugins are shown by using the showWindow(String plugin) method which can be accessed with its remote.
- What's my group doing?: this plugin shows a list of users who are currently in your group and tells what they are doing in the system (i.e. what plugin is currently active in the MDI).
- Who's Connected: this plugin provides a tree view showing all users who are currently connected to the system.
Chat
The chat plugin allows you to send a message to all members in your group or to specific individuals, beep another user to get their attention, set your availability, and view a chat history. In terms of coding it is basically a more advanced version of the ChatExample plugin.
Desktop Sharing
The desktop sharing plugin allows a user to give other users in their group the ability to see and control their desktop. The techniques applied to this are very similar to the open source VNC project [11] created by AT&T.
Initial Connection
When a user first shares their desktop they start a server similar to the VNC server which listens for incoming connections and then the user sends messages to the users in his group to connect. When another user connects a transmitter thread is created to process that user. The first information to be sent is 4 bytes long, 2 bytes each for the width and height of the screen being transmitted.
Screen Requests and Events
Also like VNC, the clients receiving a user's desktop data have to request an update. This has two primary advantages: it prevents the server from flooding the client with more data than it can process [12], and it provides an easy way to reduce the amount of data to be sent. As the viewing window for the clients is usually much smaller than the screen resolution of the server, the request will speficy that it only wants an update for the rectangle that is in the viewable area of the scrollpane. This is also advantageous for the server because the smaller the screen capture is the faster it can be done and less processing has to be done to reduce the colors and encode the data.
Events are also sent with update requests. If a remote user has control of a user's desktop then the mouse and keyboard events for the remote user are collected and sent each time an update request happens. This is why you might sometimes notice a lot of events happening really fast separated by pauses. The update request has the following format:

Figure 9: Format of update request
In addtion to the basic encoding the screen data is compressed using a simple RLE compression. This works fairly well especially for subrectangles that have to use the RAW type. User screens usually have long runs of same color pixels and with the color reduced to 16 or 256 colors (i.e. either or one or two pixels per byte) this significantly reduces the amount of data sent. Furthermore, the RLE compression is very simple and fast so it does not slow down the processing of the data or increase the memory use that much.
Encoding
The encoding used to transfer the screen is a slightly modified version of the hextile encoding [13] used with VNC. Basically the screen is divided into 16x16 subrectangles and each rectangle is classified as one of three types:
- RAW: raw pixel data sent in right to left scanline order.
- FILL: fill rectangle with one color.
- SAME: same as previously sent rectangle at that location. (i.e. nothing has to be done at the client side)
When an update request is received, a screen update is sent which tells the client the number of colors the update is encoded with (can be 16 or 256), the rectangle of the screen to update (the rectangle that was asked for by the client), and the screen data in the hextile encoding. A screen update has the following format:

Figure 10: Format of update
Editor
The editor plugin is intended to be a simple shared text editor with an interface resembling Microsoft Notepad. The basic approach is to have a text area in which only one user in the group has control. The user with control can use the text are and the changes detected by the DocumentListener are sent to the other group members. The editor will usually send a lot of small messages, however, when opening files there is a possiblity that a very large message will be sent with the file contents.
Several users have suggested having an editor that allows multple users to not only see but work on the same document at the same time as well. This would be very hard to synchronize in a peer to peer system such as ICE especially with larger groups and would also require low level access to the text area being used to implement the locks for different parts of the file. Because of these problems, the editor uses a much simpler but more stable approach.
Another suggestion has been to allow for multiple instances of some plugins such as the editor which are based around a document. This is a feasible approach but there are some questions that must be answered for this such as: What happens when one user closes the document?, and How are these plugins accessed from the interface?
File Sharing
The file sharing plugin is basically a restricted FTP client that can only access one site specified by the ice.fileshare.url property. The interface was designed to look like a common FTP client called WS_FTP by Ipswitch. By using a central FTP server the storage is persistent and can be used as a form of asynchronous communication.
Groups have a quota for the amount of space they can use on the server which can be configured with the ice.fileshare.quota property. There is also a limit to the file size for uploads which can be configured with the ice.fileshare.upload property.
Help
The help plugin is what is used to view the online (as in within system, not on the Internet) help. The help viewer is based on the Java Swing HTML renderer so it can view simple HTML pages. The primary navigation is provided by a tree component to the left of the interface. The contents of this are generated from an XML file speficied with the ice.help.contents property. The XML file has the following format:
<help> <chapter link="chapter1.html" name="Chapter One"> <section link="section1.html" name="Section One"/> <section link="section2.html" name="Section Two"/> </chapter> <chapter link="chapter2.html" name="Chapter Two"/> </help>
This file and the appropriate HTML files are currently generated by the build scripts for the help. The help also has a ice.help.home property which specifies the home page for the help viewer.
Scribble
The scribble plugin was designed to allow users to work on things like math that require fomulas and cannot easily be done with other means. It basically captures the mouse movements and uses these to draw. Other devices that look like a mouse to the system such as writing tablets can be used as well.
Files can be stored using the scribble file format or in an image format such as PNG or JPEG. The scibble format has several parts separated by the "#" character. This character was chosen because it is not used in base 64 encoding. The first part consists of "open" which is used to identify the string when sent as a command (the file format is also used to send a document over the network). The second part is either the string "null" or a base 64 encoded string of a PNG format background image. The background image will automatically be converted to PNG if it is a different format. The maximum allowable size of the background image can be set with the ice.scribble.bg_quota property. The third part is a number in RGB format for the background color.
After that all of the parts define a line. Each line has 7 parts that are separated by the ":" character. The first part is the string "line" which is used to identify it as a command (lines can be sent individually in the same format). The other parts are x1, y1, x2, y2, RGB number for the color of the line, and "true" or "false" for whether the line is antialiased. The string below shows a sample document:
open#null#-1#line:28:19:41:35:-16777216:true#line:41:35:55:50:-16777216:true# SSH Sharing
The SSH sharing plugin is intended to be a fast alternative to desktop sharing when users are working with shell based programs. The SSH sharing plugin is based on two open source libraries. The terminal emulation is from the Java Telnet Applet project [14] and the SSH communication is done with the J2SSH library [15]. The terminal emulator supports VT100, VT220, VT320, and ANSI. We are currently using the VT100 emulation.
Text To Speech
The text to speech plugin is based on the open source FreeTTS project [16]. It was written to use the FreeTTS API directly with the kevin16 voice (medium quality 16 KHz diphone voice) instead of the recommended approach of using the JSAPI implementation. This is because the JSAPI implementation is under a different license which would complicate distribution. Furthermore, it reduces the size since there is less code we have to distribute with the application.
The user interface is simple to allow users to send basic messages to the other users of their group which are then spoken using the FreeTTS speech engine. This plugin also allows for other plugins or scripts to speak with the speak(String text) method.
Voice Chat
The voice chat plugin allows for audio network conferencing. It uses a TCP connection to send the audio data in a 8 KHz signed PCM 8 bit stereo format. The samples are recorded using a 44.1 KHz sampling rate as this is the only format linux and Mac OS X [17] support. A sample rate converter from the Trinonus project [18] is used to reduce it to 8 KHz for transmitting. The Tritonus libraries must be in the system classpath because the jar is registered with the system as a service provider [19].
The voice chat plugin has one property that can be set. The port the server that listens for incoming connections from voice receivers with the ice.voice.port property. Note it would probably be better to use UDP, however, UDP is blocked by many firewalls and NAT devices.
Web Browser
The web browser plugin is based on the NetClue web browser component [20]. This component provides much better rendering of HTML and CSS compared to the Swing HTML renderer. However, the NetClue component lacks flexibility and as it is closed source it cannot be fixed by us under the license we have. There are a number of other rendering engines but currently there are no open source options with good rendering. Two projects that show promise however are Jazilla [21] and Multivalent [22]. Jazilla is supposed to be a java port of the NGLayout [23] engine. Multivalent is a project to make a flexible document viewer. The benefit of this project is it can also view other formats such as PDF.
The web browser has two properties that can be set. The first is the home page with the ice.webbrowser.home property. The other is the search page with the ice.webbrowser.search property. Note you can also set the user agent that is given when the java HTTP library accesses a site with the http.agent property.
Whiteboard
The whiteboard plugin allows for users to create simple structured diagrams such as flow charts and er diagrams. The whiteboard has a fairly simple design where shapes are all have a single base class called WBShape. Additional shapes can easily be added by creating a subclass. The WBShape class automatically provides all the needed functionality except for drawing the particular shape. (Note some shapes such as line also have to customize the code for drawing resize handles and possibly cloning the shape.) The structure of the whiteboard can be seen with the following diagram:

Figure 11: Whiteboard plugin structure
File Format
The whiteboard has a simple XML file format that is composed of shape tags. Note that the each shape can be taken out and used as an independent XML file. The file format is the same format that is used to send the information about shapes over the network. An example whiteboard file is shown below:
<whiteboard>
<shape type='line' zorder='8' id='brian_1058372622427' slope='3' arrow='2'>
<position x='162' y='146' width='186' height='12'/>
</shape>
<shape type='line' zorder='7' id='brian_1058372618890' slope='2' arrow='1'>
<position x='162' y='132' width='184' height='12'/>
<foreground style='default'>
<color r='0' g='51' b='204'/>
</foreground>
</shape>
<shape type='line' zorder='6' id='brian_1058372615075' slope='2' arrow='0'>
<position x='163' y='114' width='183' height='12'/>
</shape>
<shape type='line' zorder='5' id='brian_1058372608544' slope='2' arrow='-1'>
<position x='164' y='100' width='178' height='12'/>
</shape>
<shape type='ellipse' zorder='4' id='brian_1058372592844' antialiased='true'>
<position x='29' y='82' width='109' height='46'/>
<textcolor style='default'>
<color r='255' g='0' b='0'/>
</textcolor>
<text font='SansSerif' halign='right' valign='bottom'>Hello</text>
</shape>
<shape type='round_rectangle' zorder='3' id='brian_1058372574412'>
<position x='258' y='17' width='91' height='71'/>
<border width='1' dashed='true'/>
<text font='SansSerif' halign='left' valign='top'>Hello</text>
</shape>
<shape type='diamond' zorder='2' id='brian_1058372557075'>
<position x='158' y='12' width='81' height='75'/>
<background style='default'>
<color r='204' g='204' b='0'/>
</background>
<text font='SansSerif'>Hello</text>
</shape>
<shape type='rectangle' zorder='1' id='brian_1058372552741'>
<position x='26' y='6' width='79' height='52'/>
</shape>
</whiteboard>
This code would generate the following whiteboard image:

Figure 12: Whiteboard example
Future Work
With this structure it would be fairly easy to apply the composite design pattern [6] to allow for groups to be implemented. This feature would allow for shapes to be grouped and then treated as a single shape. However, you would have to figure out how to integrate the creation of groups into the interface and file format. The diagram below shows how the whiteboard structure would look:

Figure 13: Whiteboard plugin structure with composite
The most requested feature from users is to add an undo capability. This has been left out for several reasons. The first is whether the undo would only work for your actions or you and other group members. The second is that it would make it much more likely for the whiteboard to become out of sync with other group members. Furthermore, it would increase memory because a history of shapes would have to be recorded for every modification of any shape.
Conclusion
This paper has briefly described the design of the ICE software and the various parts that make it up. Currently the software has well over 200 java source files with over 16000 NCSS [24] lines of code and over 43000 physical lines of code not including code for testing, native OS integration, and web applications. The project is currently growing, mainly with the frequent addition of new plugins. Future work is centered around refactoring the system to make it cleaner, more flexible, and more reliable as well as the addition of new plugins to facilitate more uses.
References
- HyunWoo, Lee. Java VT100/ANSI, TN3270 Terminal Technical Reference. http://myhome.elim.net/~hwlee/works/jprj/term_tech/.
- Wilson, Steve; Kesselman, Jeff. Java Platform Performance. Section 6.2.3. http://java.sun.com/docs/books/performance/.
- Harder, Robert. Base 64. http://iharder.net/base64.
- Hyde, Paul. Java Thread Programming. Chapter 13. SAMS Publishing. 1999. (Chapters 9 and 13 available online at http://developer.java.sun.com/developer/Books/javaprogramming/threads/.)
- Brazile, Robert. Java Database Objects. http://poseidon.csci.unt.edu/brazile/jdbo4139.zip.
- Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. 1995.
- Harrington, Brian. Peer-to-Peer Systems. http://www.harringtonweb.com/brh/pdfs/p2p.pdf.
- Brazile, Robert; Swigger, Kathy; Harrington, Brian; Harrington, Ben; Peng, Xiabo; Roberts, Cynthia. Software Architecture for the International Collaborative Environment (ICE). http://zeus.csci.unt.edu/publications/ice_arch.pdf.
- Goodwill, James. Embedding Tomcat into Java Applications. O'Reilly Network. http://www.onjava.com/lpt/a/onjava/2002/04/03/tomcat.html.
- Toedter, Kai. JCalendar. http://www.toedter.com/en/jcalendar/.
- RealVNC Group. VNC - What is it?. http://www.realvnc.com/what.html.
- RealVNC Group. VNC - How it works. http://www.realvnc.com/howitworks.html.
- Richardson, Tristan; Wood, Kenneth. The RFB Protocol. http://www.realvnc.com/rfbproto.pdf.
- Jugel, Matthias; Meissner, Marcus. The Java Telnet/SSH Applet. http://www.javassh.org/.
- Painter, Lee David. SSHTools. http://www.sshtools.com/.
- Walker, Willie; Lamere, Paul; Kwok, Philip. FreeTTS. http://freetts.sourceforge.net/.
- Apple Computer. Java 1.4.1 Development for Mac OS X. http://developer.apple.com/documentation/Java/Conceptual/Java141Development/.
- Tritonus Group. Tritonus: Open Source Java Sound. http://www.tritonus.org/.
- Sun Microsystems. JAR File Specification. http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html.
- NetClue Software. Java Web Browser. http://www.netcluesoft.com/ (Link is not active because when last checked—June 28, 2006—it was not valid.).
- Jazilla Group. Jazilla Web Browser. http://jazilla.sf.net/.
- Phelps, Tom. Multivalent: A Platform for New Ideas. http://www.cs.berkeley.edu/~phelps/Multivalent/.
- Mozilla Group. Mozilla Layout Engine. http://www.mozilla.org/newlayout/.
- Lee, Clemens. JavaNCSS - A Source Measurement Suite for Java. http://www.kclee.com/clemens/java/javancss/.