SimLink

Introduction

SimLink is a system for deployment and use of Java based downloadable plugins within the BuddySpace instant messaging system. It also supports a mechanism to 'share state' in the group chat environment of BuddySpace. This means that we can design a plugin whose current state is synchronized in all participants of a group chat session. For example, one of  sample plugins provided, allows participants to play a networked game of Chess, and additionally progress of the game can be viewed by participants not involved in the game.

In practice, SimLink allows multiple users to share control of simulations and other software at much lower bandwidth penalty than 'raw' screen-sharing would entail.

Getting started

Please note that this SimLink system (introduced in BuddySpace 2.6) is still in an experimental phase and is not enabled by default. But please feel free to experiment with it.

To enable SimLink, you need to go to BuddySpace preferences and click on the plugins tab where you will find an option enable it.

To use SimLink, start a Groupchat and click on the "Show plugin" option, to get the SimLink Plugin panel visible.

Note that some of the sample SimLink plugins are 'standalone'  i.e. they are not linked with the plugin state of other users in the room. But for the plugins, 'Global warming and cooling', 'Web browser'  and 'Chess Board', all users with these plugins loaded within the same Groupchat room, will see interactions with the plugin synchronised. E.g. If a user moves a piece on the chess board, all users in the room will see the same move.
[Note: 'Global warming and cooling' plugin only works under Windows as it uses some native code.]

Note that there is also an item on the Jabber Menu for displaying all the SimLink plugins in 'standalone' mode.

Technical Notes for Developers

Currently, the list of available plugins is provided on demand by a jabber client with JID (simlink@buddyspace.org). This jabber bot client must be running in order to use the SimLink system.

The BuddySpace client issues a request to simlink@buddyspace.org  whenever a list of SimLink plugins is required. The current implementation of the SimLink bot needs to be rebuilt and restarted in order to add to the list of available SimLink plugins.  The BuddySpace client does not need modification in order to add to the list of available plugins because both the list and the plugins themselves are retrieved on-demand.

Here is an extract from SimLinkManger.java showing the currently available plugins, their titles, java class names and download location.

   private static Plugin plugins[] =
    {
    new Plugin(
        "Global warming and cooling",
        "edu.ou.kmi.simlink.plugins.globalwarming.GlobalWarming",
        "http://buddyspace.org/simlink/globwarm.jar"),
    new Plugin(
        "Web Browser",
        "edu.ou.kmi.simlink.plugins.webbrowser.LinkedWebBrowser",
        "http://buddyspace.org/simlink/browser.jar"),
    new Plugin(
        "Are you ready for S269?",
        "AppletMain",
        "http://buddyspace.org/simlink/s269.jar"),
    new Plugin(
        "Chess Board",
        "edu.ou.kmi.simlink.plugins.chessboard.ChessBoard",
        "http://buddyspace.org/simlink/chess.jar"),
    new Plugin(
        "4x4 Puzzle",
        "edu.ou.kmi.simlink.plugins.puzzle.Puzzle",
        "http://buddyspace.org/simlink/puzzle.jar")
    };
    
It would be fairly straightforward to modify  SimLinkManager.java so that list is retrieved from a more dynamic source such as a database.

Creating a plugin

I'll assume here that the reader is familiar with java programming and the creation of JAR files to deploy compiled code.

A vanilla plugin

If we don't need the plugin to be synchronised in a group chat environment, there's basically not much to do apart build a java component by extending from any class that has class java.awt.Component as one of its ancestors. In a typical case I would expect the usual class you would choose to extend was javax.swing.JPanel, but virtually any user interface component should work as well. e.g.

  package mypackage;
  class MyPlugin extends javax.swing.JPanel {
    public MyPlugin()
    {
      add(new javax.swing.JButton("hello world"));
    }
  }
  
The full class name of your extending class must be added to the list of plugins, e.g. in the example above you'd need to add mypackage.MyPlugin to the list.

A synchronised (yoked) plugin

In order to provide a mechanism for passing state information between users in a group chat environment, you are required to implement the methods of a very simple java interface called SimLinkPlugin. The example below shows how the linked web browser plugin was implemented.

package edu.ou.kmi.simlink.plugins.webbrowser;

import java.awt.*;

import edu.ou.kmi.simlink.system.*;

public class LinkedWebBrowser extends WebBrowser implements SimLinkPlugin
{
    // This method will be called by BuddySpace and your implementation
    // must return component instance that is to be displayed.
    public Component createGUI(String userID)
    {
        return this;
    }

    // When the plugin changes to state that you requires synchronisation
    // with other users, your implementation of this method must return
    // a string representing the absolute state of the application.
    // In this case, we just need to send the URL, but can be more complex
    // data, such as the parameters of a simulation.   
    public String getPackedParameters()
    {
    	// Note that we add the classname as a prefix to the
    	// parameters so we can rely on the setPackedParameters 
    	// method to filter out parameters intended for other
    	// plugins.
        return getClass().getName() + "=" + address.getText();
    }

    // Your implemtation of this method must set the plugin to the
    // desired state based on the parameter string that it receives.
    public void setPackedParameters(String parameters)
    {
        if (parameters == null || parameters.length() == 0) return;
        
        // Check parameters belong to this plugin
        if (!parameters.startsWith(getClass().getName() + "=")) return;

		// Inorder to prevent a recursive loop, we need to 
		// prevent state changes from being propogated during 
		// a 'synched' update of the plugin. In this case we temporarily
		// disable the change listener whilst updating the page.
        ParameterChangeListener oldListener = parameterChangeListener;
        parameterChangeListener = null;
        setPage(parameters.substring(getClass().getName().length() + 1), true);
        parameterChangeListener = oldListener;

    }

    private ParameterChangeListener parameterChangeListener = null;
    
    // Whenever state changes in your plugin that requires synchronisation,
    // your plugin must call the parameterChanged method of the listener
    // provided by this method.
    public void setParameterChangeListener(ParameterChangeListener listener)
    {
        parameterChangeListener = listener;
    }

    // This method overrides the setPage method of the underlying WebBrowser 
    // component. It is called whenever the user changes the webpage address that
    // they are viewing. So here we just notify the parameter change listener
    // that something has changed.
    public void setPage(String url, boolean addToHistory)
    {
        super.setPage(url, addToHistory);
        if (parameterChangeListener != null)
        {
        	// Notify the change by passing the instance of the 
        	// SimLinkPlugin to the parameter change listener.
            parameterChangeListener.parameterChanged(this);
        }
    }

}