Johan Anderson Columbia University New York, NY 10027 USA johan@columbia.edu |
Thor Denmark Columbia University New York, NY 10027 USA thor@columbia.edu |
Melvin Lew Columbia University New York, NY 10027 USA melvin@columbia.edu |
JINCS (Java Interactive Networked Classroom System) is a extensible platform-independent, object-oriented, multi-threaded client/server, graphical teaching environment (and it's Y2K compliant). It allows an instructor to distribute class materials, lecture, draw diagrams, and conduct discussions with students anywhere on the Internet.
JINCS is implemented in the Java programming language (jdk-1.1.5) for several reasons. Java's platform independence allows the widest possible range of students to use JINCS. The ease of implementing multiple threads in Java made it simple to create a robust and highly scalable server. The client utilizes Java's Abstract Windowing Toolkit (AWT) to create a simple to use and portable user interface.
To stay on the cutting edge, future versions of Java will contain many new features useful for collaborative applications. JINCS will be able to take advantage of these developments to add features such as postscript slides, streaming audio/video, and high resolution image distribution.
Reserved characters in our protocol --> ':', ',' Reserved usernames on our server --> "ALL", "SERVER" Message protocol is --> SENDER:RECIPIENT:COMMAND Where SENDER is 1) a username 2) "SERVER" which means it's a message from the server Where RECIPIENT is 1) a username 2) a comma delimited list of usernames (ie: "user1,user2") 3) "ALL" which is a message for the whole class 4) "SERVER" which means it's a message only for the server Where COMMAND is 1) COMMAND_NAME:ARGS COMMAND_NAME indicates which command is being given (see below), and ARGS is a comma delimited list of arguments to the command (ie: "arg1,arg2,arg3,..."). NOTES ----- Commands are case insensitive.
Command | Arguments | |
---|---|---|
Chat Commands | ||
Syntax: chat(message)
Description:
|
message | contents of the chat message |
Syntax: clearChatBox()
Description:
|
no arguments | |
Syntax: raiseHand()
Description:
|
no arguments | |
Whiteboard Commands | ||
Syntax: line(x1,y1,x2,y2,thickness,r,g,b)
Description:
|
x1 | x-coordinate of the first point |
y1 | y-coordinate of the first point | |
x2 | x-coordinate of the second point | |
y2 | y-coordinate of the second point | |
thickness | thickness of the line | |
r | red component of the line's color | |
g | red component of the line's color | |
b | blue component of the line's color | |
Syntax: rectangle(x1,y1,x2,y2,thickness,r,g,b)
Description:
|
x1 | x-coordinate of the upper-left corner |
y1 | y-coordinate of the upper-left corner | |
x2 | x-coordinate of the lower-right corner | |
y2 | y-coordinate of the lower-right corner | |
thickness | thickness of the rectangle's borders (value of -1 means draw a filled rectangle) | |
r | red component of the rectangle's color | |
g | red component of the rectangle's color | |
b | blue component of the rectangle's color | |
Syntax: circle(x1,y1,radius,thickness,r,g,b)
Description:
|
x1 | x-coordinate of the circle's center |
y1 | y-coordinate of the circle's center | |
radius | radius of the circle | |
thickness | thickness of the circle's circumference (value of -1 means draw a filled circle) | |
r | red component of the circle's color | |
g | red component of the circle's color | |
b | blue component of the circle's color | |
Syntax: clear()
Description:
|
no arguments | |
Management Commands | ||
Syntax: login()
Description:
|
handle | username you'd like to be known as |
Syntax: dupHandle(bad_handle)
Description:
|
bad_handle | the handle that is already in use |
Syntax: userList(user1, type1, muted1, host1, port1, etc...)
Description:
|
user1 | first user's handle |
type1 | first user's type (teacher or student) | |
muted1 | first user's mute status (true or false) | |
host1 | first user's internet hostname | |
port1 | first user's port number | |
etc... | more users with their information grouped as shown above in sets of four | |
Syntax: shutDown()
Description:
|
no arguments | |
Syntax: changeType(type)
Description:
|
type | type to change to (types are currently student, teacher, and maybe observer) |
Syntax: raiseHand(true/false)
Description:
|
true/false | true raises hand, false lowers hand |
Syntax: muteClient(true/false)
Description:
|
true/false | true mutes client, false un-mutes client |
The recipient address consists of either a username, a comma delimited list of usernames, the reserved word SERVER, or the reserved word ALL. When a list of usernames is given, the server forwards the message only to those users. When the recipient is SERVER, the server does not forward the message to any clients. When the recipient is ALL, the server broadcasts the message to all connected clients.
Note that usernames cannot contain the reserved ``:'', ``,'', or newline characters, nor can they be any of the reserved words described above.
ClientInfo
objects explained below). Each client may be
either a teacher or student, have its hand raised or not, and be allowed to
speak or not (mute). At startup, each student client has its hand lowered
and is muted from broadcasting messages to all the clients. By default,
only teachers are allowed to speak by broadcast to all of the clients, and
only teachers may un-mute student clients to allow them to speak. (Note
that students are allowed to send private messages to other clients
at any time -- the restrictions are only on broadcast messages to all
clients.)
A student client may "raisehand" in order to get the attention of the teacher client(s). The teacher client may choose to allow a student to speak to everyone by un-muting the student's client. The teacher client may also re-mute the student's client to prevent further speaking to the group.
When the JINCS user interface is started it presents the user with a login dialog asking for the handle that the user wants to use, the server and the port on that server to connect to. If the user correctly fills out this dialog, they are logged in.
In the upper-right corner of the control panel they will see a list of other users logged into the server. The bottom half of the control panel is the chat area of the interface where the user can see what has been said and add their own comments to the discussion. A user can send private messages to one or more other users by clicking on their names in the user list before sending their message. When the user initially logs in, they are not permitted to send broadcast messages. They must raise their hand by clicking on the "raise hand" button and be acknowledged by the teacher before they are allowed to pose a general question. The user may upgrade their status to teacher by clicking on the "become an instructor" button.
The whiteboard itself appears in a separate window. It accepts basic mouse input similar to other drawing applications provided that the user has permission to write to the whiteboard. These drawings are shared with the entire class. Below the whiteboard area are several selection boxes allowing the user to set the current drawing color, pen thickness, and pen shape.
ClientSocket
that communicates with
the server, and a ClientParser
which interprets incoming
messages from the server. The ClientSocket
uses a
ClientReader
thread to read incoming messages from the server
and a ClientWriter
thread to write messages destined for the
server in a non-blocking manner. Once the ClientReader
reads
in a message, that message is passed off to the ClientParser
to be parsed. Once the source, destination, and command is parsed out of
the incoming message, the ClientParser
invokes the appropriate
method in the user interface.
State variables in the code keep track of the user's type, a list of users
currently attending class, whether or not the client is connected, has a
raised hand, or has been muted. To invoke the client, make sure that the
jincs.jar
archive is in the classpath environment variable and
then run java whiteBoard
. For further information on
installation and configuration of the client, see the
Program Documentation section.
WBSListenThread
, WBCListenThread
, and
WBCPrintThread
.
Threads are essentially lightweight processes and require very few additional system resources. By utilizing threads, the server is more reliable and can handle significantly more clients than a comparable implementation utilizing forked processes. As a result, this server is scalable and can operate a large collaborative session. Of course, there are cases where this architecture of a single server supporting multiple clients is not ideal. Depending on the application, it may be preferable to use multicast or use multiple servers.
The server is responsible for maintaining information about each client,
such as the hostname, port, username, type, and how to communicate to the
client. Also, the server maintains the state information for the client,
such as for the teacher/student model described above. Each client is
associated with a ClientInfo
object that stores all this
information. The ClientInfo
objects are then inserted into a
HashTable
for easy and efficient access.
WBSListenThread
. This thread runs the entire time the server
remains running. The WBSListenThread
opens a socket on a
user-specified port, and continually listens for client connections.
Whenever a new connection is detected on the listening port, the
WBSListenThread
spawns a thread called a
WBCListenThread
to handle it.
At instantiation, the WBCListenThread
creates a new
ClientInfo
object for the client, and inserts it into the
server's HashTable
. For every client connected to the server,
there is a corresponding WBCListenThread
that handles incoming
data from that client. This allows each client to communicate independently
with the server.
Whenever a message is received from a client, WBCParser
is
called to process the command. The WBCParser
parses the
command into the colon separated FROM, TO,
COMMAND, and ARGUMENTS fields. It then does a caseless
string match on the COMMAND field to determine how to handle the
message. Some commands may require messages to be sent to clients. For
each message sent to each client, a new thread is spawned that is called a
WBCPrintThread
.
The sole purpose of each instance of WBCPrintThread
is to send
a single message to a single client. As soon as the task of a particular
WBCPrintThread
instance is finished, the thread ends. This
allows the server to simultaneously send many messages to many clients
without having to wait for each client to receive its own messages. Each
WBCPrintThread
may need to block while waiting to write to a
client, but this does not hinder other WBCPrintThread
's from
completing their tasks. In addition, by using synchronized
write access to each client, there is no possibility for inter-thread
interference in simultaneous writes to the same client.
changetype
from:SERVER:CHANGETYPE:type
SERVER:ALL:USERLIST:username1,type1,muted1,hostname1,port1,...
"
message to all clients, updating them with the new type information.
duphandle
SERVER:to:DUPHANDLE:handle
from:SERVER:LOGIN:handle
" message request for a handle
already in use by another client.
login
from:SERVER:LOGIN:handle
SERVER:ALL:USERLIST:username1,type1,muted1,hostname1,port1,...
"
message to all clients, updating them with the new client
information. If the requested handle is already in use by another
client, the server sends a "SERVER:to:DUPHANDLE:handle
"
message to the client.
muteclient
from:to:MUTECLIENT:state
from:to:MUTECLIENT:state
"message to all affected clients
listed in the to field. In addition, this command causes
all affected clients listed in the to field to lower their
hands. Also, a
"SERVER:ALL:USERLIST:username1,type1,muted1,hostname1,port1,...
"
message is sent to all clients to updated the mute states of each
client.
raisehand
from:SERVER:RAISEHAND:state
from:ALL:RAISEHAND:state
" message to
all clients to inform them of the change.
shutdown
from:SERVER:SHUTDOWN
SERVER:from:CHAT:Bye!
" message to the client and
disconnects from it.
userlist
SERVER:ALL:USERLIST:username1,type1,muted1,hostname1,port1,...
SERVER:to:USERLIST:username1,type1,muted1,hostname1,port1,...
from:SERVER:USERLIST
" request. This informs the
client of the the complete userlist information for all clients.
from:SERVER:USERLIST
SERVER:from:USERLIST:username1,type1,muted1,hostname1,port1,...
"
userlist to be sent to it with the current information for all clients.
generic commands
from:ALL:COMMAND[:ARGUMENTS]
from:to:COMMAND[:ARGUMENTS]
generic command examples
from:to:chat:message
from:to:circle:x1,y1,radius,thickness,r,g,b
from:to:clear
from:to:clearchatbox
from:to:line:x1,y1,x2,y2,thickness,r,g,b
from:to:rectangle:x1,y1,x2,y2,thickness,r,g,b
from:to:roundedrectangle(x1,y1,x2,y2,thickness,r,g,b
from:to:text:x1,y1,x2,y2,thickness,r,g,b
java WBServer [port]
on the
machine where you want to run the server. To run the client, type the
command java whiteBoard
at the command prompt.