/usr/share/mozart/doc/apptut/node18.html is in mozart-doc 1.4.0-8ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE>Part V: Distributed Applications</TITLE><LINK href="ozdoc.css" rel="stylesheet" type="text/css"></HEAD><BODY><TABLE align="center" border="0" cellpadding="6" cellspacing="6" class="nav"><TR bgcolor="#DDDDDD"><TD><A href="node15.html#part.www"><< Prev</A></TD><TD><A href="index.html">- Up -</A></TD><TD><A href="node19.html#part.foreign.extensions">Next >></A></TD></TR></TABLE><DIV id="part.distributed.applications"><H1 align="center" class="part"><A name="part.distributed.applications">Part V: Distributed Applications</A></H1><H1><A name="label40">14 Chat Application</A></H1><P>A chat system permits participants on arbitrary machines on the internet to engage in a real-time text-based discussion. New individual can join or leave the chat forum at any time. This scenario is intended to be realistic, which means that the chat system must be reasonably robust in the face of network failures, as well as machine and process crashes.</P><P>In this tutorial application, we will not set out to solve all problems that may be associated with distributed applications; rather, we will demonstrate how simple it is to realize a fully distributed application with reasonable robustness properties.</P><H2><A name="label41">14.1 Chat Server</A></H2><P>The server creates a port <CODE>NewsPort</CODE> and makes it available through a ticket. The ticket, as usual, is saved into a file which clients normally will load through a url. When a client wants to participate in the discussion forum, it needs not only <CODE>NewsPort</CODE> in order to post messages, but also the stream of messages that results from all posts to <CODE>NewsPort</CODE>, in order to display these messages to the user. The server could hand out the stream of all messages from the creation of <CODE>NewsPort</CODE>, but it seems more desirable to only hand out a stream that has only the messages posted after the client's request to connect to the discussion.</P><P>When a client wants to connect to the chat forum, it obtains <CODE>NewsPort</CODE> by means of the ticket that the server made available at some url, and it posts a message of the form <CODE>connect(Messages)</CODE>, where <CODE>Messages</CODE> is a new variable. The server then binds the variable to the stream of messages following the <CODE>connect(<SPAN class="keyword">...</SPAN>)</CODE> message. </P><DL><DT><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A name="label42">Chat Server</A><SPAN class="chunkborder">>=</SPAN></SPAN></DT><DD class="code"><CODE><SPAN class="keyword">functor</SPAN> <BR><SPAN class="keyword">import</SPAN> <BR> Application(getCmdArgs) Connection(gate) Pickle(save)<BR><SPAN class="keyword">define</SPAN> <BR> Args = {Application<SPAN class="keyword">.</SPAN>getCmdArgs<BR> record(ticketfile(single type:string optional:<SPAN class="keyword">false</SPAN>))}<BR> NewsPort<BR> <SPAN class="keyword">local</SPAN> Ticket <SPAN class="keyword">in</SPAN> <BR> {New Connection<SPAN class="keyword">.</SPAN>gate init(NewsPort Ticket) _}<BR> {Pickle<SPAN class="keyword">.</SPAN>save Ticket Args<SPAN class="keyword">.</SPAN>ticketfile}<BR> <SPAN class="keyword">end</SPAN> <BR> {List<SPAN class="keyword">.</SPAN>forAllTail {Port<SPAN class="keyword">.</SPAN>new $ NewsPort}<BR> <SPAN class="keyword">proc</SPAN><SPAN class="variablename"> </SPAN>{<SPAN class="functionname">$</SPAN> H<SPAN class="keyword">|</SPAN>T}<BR> <SPAN class="keyword">case</SPAN> H <SPAN class="keyword">of</SPAN> connect(Messages) <SPAN class="keyword">then</SPAN> Messages=T <SPAN class="keyword">else</SPAN> <SPAN class="keyword">skip</SPAN> <SPAN class="keyword">end</SPAN> <BR> <SPAN class="keyword">end</SPAN>}<BR><SPAN class="keyword">end</SPAN></CODE></DD></DL><P> The server (<A href="chat-server.oz">source in <CODE>chat-server.oz</CODE></A>) can be compiled as follows: </P><BLOCKQUOTE class="code"><CODE>ozc -x chat-server.oz</CODE></BLOCKQUOTE><P> and invoked as follows: </P><BLOCKQUOTE class="code"><CODE>chat-server --ticketfile </CODE><I>FILE</I></BLOCKQUOTE><P></P><H2><A name="label43">14.2 Chat Client</A></H2><P>The client consists of 2 agents: (1) a user interface agent and (2) a message stream processor. </P><DL><DT><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A name="label44">Chat Client</A><SPAN class="chunkborder">>=</SPAN></SPAN></DT><DD class="code"><CODE><SPAN class="keyword">functor</SPAN> <BR><SPAN class="keyword">import</SPAN> <BR> Application(getCmdArgs) Pickle(load) Connection(take)<BR> Viewer(chatWindow) <SPAN class="keyword">at</SPAN> <SPAN class="string">'chat-gui.ozf'</SPAN> <BR><SPAN class="keyword">define</SPAN> <BR> Args = {Application<SPAN class="keyword">.</SPAN>getCmdArgs<BR> record(url(single type:string optional:<SPAN class="keyword">false</SPAN>)<BR> name(single type:string optional:<SPAN class="keyword">false</SPAN>)<BR> )}<BR> NewsPort={Connection<SPAN class="keyword">.</SPAN>take {Pickle<SPAN class="keyword">.</SPAN>load Args<SPAN class="keyword">.</SPAN>url}}<BR> SelfPort<BR> </CODE><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A href="node18.html#label45">Chat Client: obtain and process message stream</A><SPAN class="chunkborder">></SPAN></SPAN><CODE> <BR> </CODE><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A href="node18.html#label46">Chat Client: create user interface agent</A><SPAN class="chunkborder">></SPAN></SPAN><CODE> <BR> </CODE><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A href="node18.html#label47">Chat Client: process message stream</A><SPAN class="chunkborder">></SPAN></SPAN><CODE> <BR><SPAN class="keyword">end</SPAN></CODE></DD></DL><P> The client obtains the stream of messages from the server by sending a <CODE>connect(<SPAN class="keyword">...</SPAN>)</CODE> message. It then forwards every message on that stream to its internal <CODE>SelfPort</CODE>. The user interface will also direct messages to this internal port. </P><DL><DT><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A name="label45">Chat Client: obtain and process message stream</A><SPAN class="chunkborder">>=</SPAN></SPAN></DT><DD class="code"><CODE><SPAN class="keyword">thread</SPAN> <BR> {ForAll {Port<SPAN class="keyword">.</SPAN>send NewsPort connect($)}<BR> <SPAN class="keyword">proc</SPAN><SPAN class="variablename"> </SPAN>{<SPAN class="functionname">$</SPAN> Msg} {Port<SPAN class="keyword">.</SPAN>send SelfPort Msg} <SPAN class="keyword">end</SPAN>}<BR><SPAN class="keyword">end</SPAN></CODE></DD></DL><P> When creating the user interface, we supply it with the internal <CODE>SelfPort</CODE> so that it may also post internal messages. In this simplistic implementation, the user interface simply posts messages of the form <CODE>say(</CODE><I>String</I><CODE>)</CODE> to request that this <I>String</I> be posted to the global chat message stream. </P><DL><DT><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A name="label46">Chat Client: create user interface agent</A><SPAN class="chunkborder">>=</SPAN></SPAN></DT><DD class="code"><CODE>Chat = {New Viewer<SPAN class="keyword">.</SPAN>chatWindow init(SelfPort)}</CODE></DD></DL><P> Finally, here is where we process all messages on the internal stream. A <CODE>msg(FROM TEXT)</CODE> message is formatted and shown in the chat window. A <CODE>say(TEXT)</CODE> message is transformed into <CODE>msg(NAME TEXT)</CODE>, where <CODE>NAME</CODE> identifies the user, and posted to the global chat stream; actually <CODE>TEXT</CODE> is additionally converted into the more compact byte string representation for more efficient transmission. </P><DL><DT><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A name="label47">Chat Client: process message stream</A><SPAN class="chunkborder">>=</SPAN></SPAN></DT><DD class="code"><CODE>NAME = Args<SPAN class="keyword">.</SPAN>name<BR>{ForAll {Port<SPAN class="keyword">.</SPAN>new $ SelfPort}<BR> <SPAN class="keyword">proc</SPAN><SPAN class="variablename"> </SPAN>{<SPAN class="functionname">$</SPAN> Msg}<BR> <SPAN class="keyword">case</SPAN> Msg <SPAN class="keyword">of</SPAN> msg(FROM TEXT) <SPAN class="keyword">then</SPAN> <BR> {Chat show(FROM<SPAN class="keyword">#</SPAN><SPAN class="string">':\t'</SPAN><SPAN class="keyword">#</SPAN>TEXT)}<BR> <SPAN class="keyword">elseof</SPAN> say(TEXT) <SPAN class="keyword">then</SPAN> <BR> {Port<SPAN class="keyword">.</SPAN>send NewsPort msg(NAME {ByteString<SPAN class="keyword">.</SPAN>make TEXT})}<BR> <SPAN class="keyword">else</SPAN> <SPAN class="keyword">skip</SPAN> <SPAN class="keyword">end</SPAN> <BR> <SPAN class="keyword">end</SPAN>}</CODE></DD></DL><P> The client (<A href="chat-client.oz">source in <CODE>chat-client.oz</CODE></A>) can be compiled as follows: </P><BLOCKQUOTE class="code"><CODE>ozc -x chat-client.oz</CODE></BLOCKQUOTE><P> and invoked as follows: </P><BLOCKQUOTE class="code"><CODE>chat-client --name </CODE><I>USER</I><CODE> --url </CODE><I>URL</I></BLOCKQUOTE><P></P><H2><A name="label48">14.3 Graphical User Interface</A></H2><P>The user interface is always what requires the most code. We won't go through the details here (but see the Window Programming Tutorial for extensive information), but merely point out that the <CODE><SPAN class="keyword">@</SPAN>entry</CODE> widget is asked to respond to a <KBD>Return</KBD> keypress, by invoking the <CODE>post</CODE> method. The latter posts a <CODE>say(</CODE><I>Text</I><CODE>)</CODE> message to the internal port, where <I>Text</I> is the text of the entry as typed by the user. This text is then deleted and the entry can be reused to compose and submit another message. </P><DL><DT><SPAN class="chunktitle"><SPAN class="chunkborder"><</SPAN><A name="label49">Chat GUI</A><SPAN class="chunkborder">>=</SPAN></SPAN></DT><DD class="code"><CODE><SPAN class="keyword">functor</SPAN> <BR><SPAN class="keyword">import</SPAN> <BR> Tk Application(exit:Exit)<BR><SPAN class="keyword">export</SPAN> <BR> ChatWindow<BR><SPAN class="keyword">define</SPAN> <BR> <SPAN class="keyword">class</SPAN> <SPAN class="type">ChatWindow</SPAN> <SPAN class="keyword">from</SPAN><SPAN class="type"> Tk.toplevel</SPAN> <BR> <SPAN class="keyword">attr</SPAN> canvas y:0 vscroll hscroll tag:0 selfPort entry quit<BR> <SPAN class="keyword">meth</SPAN> <SPAN class="functionname">init</SPAN>(SelfPort)<BR> Tk<SPAN class="keyword">.</SPAN>toplevel<SPAN class="keyword">,</SPAN>tkInit<BR> selfPort <SPAN class="keyword">:=</SPAN> SelfPort<BR> canvas <SPAN class="keyword">:=</SPAN> {New Tk<SPAN class="keyword">.</SPAN>canvas<BR> tkInit(parent:<SPAN class="keyword">self</SPAN> bg:ivory width:400 height:300)}<BR> vscroll <SPAN class="keyword">:=</SPAN> {New Tk<SPAN class="keyword">.</SPAN>scrollbar tkInit(parent:<SPAN class="keyword">self</SPAN> orient:v)}<BR> hscroll <SPAN class="keyword">:=</SPAN> {New Tk<SPAN class="keyword">.</SPAN>scrollbar tkInit(parent:<SPAN class="keyword">self</SPAN> orient:h)}<BR> entry <SPAN class="keyword">:=</SPAN> {New Tk<SPAN class="keyword">.</SPAN>entry tkInit(parent:<SPAN class="keyword">self</SPAN>)}<BR> quit <SPAN class="keyword">:=</SPAN> {New Tk<SPAN class="keyword">.</SPAN>button tkInit(parent:<SPAN class="keyword">self</SPAN> text:<SPAN class="string">'Quit'</SPAN> <BR> action:<SPAN class="keyword">proc</SPAN>{<SPAN class="functionname">$</SPAN>} {Exit 0} <SPAN class="keyword">end</SPAN>)}<BR> {Tk<SPAN class="keyword">.</SPAN>addYScrollbar <SPAN class="keyword">@</SPAN>canvas <SPAN class="keyword">@</SPAN>vscroll}<BR> {Tk<SPAN class="keyword">.</SPAN>addXScrollbar <SPAN class="keyword">@</SPAN>canvas <SPAN class="keyword">@</SPAN>hscroll}<BR> {<SPAN class="keyword">@</SPAN>canvas tk(configure scrollregion:q(0 0 200 0))}<BR> {<SPAN class="keyword">@</SPAN>entry tkBind(event:<SPAN class="string">'<KeyPress-Return>'</SPAN> <BR> action:<SPAN class="keyword">proc</SPAN><SPAN class="variablename"> </SPAN>{<SPAN class="functionname">$</SPAN>} {<SPAN class="keyword">self</SPAN> post} <SPAN class="keyword">end</SPAN>)}<BR> {Tk<SPAN class="keyword">.</SPAN>batch [grid(row:0 column:0 <SPAN class="keyword">@</SPAN>canvas sticky:ns)<BR> grid(row:1 column:0 <SPAN class="keyword">@</SPAN>entry sticky:ew)<BR> grid(row:0 column:1 <SPAN class="keyword">@</SPAN>vscroll sticky:ns)<BR> grid(row:2 column:0 <SPAN class="keyword">@</SPAN>hscroll sticky:ew)<BR> grid(row:3 column:0 <SPAN class="keyword">@</SPAN>quit sticky:w)<BR> grid(columnconfigure <SPAN class="keyword">self</SPAN> 0 weight:1)<BR> grid(rowconfigure <SPAN class="keyword">self</SPAN> 0 weight:1)]}<BR> <SPAN class="keyword">end</SPAN> <BR> <SPAN class="keyword">meth</SPAN> <SPAN class="functionname">show</SPAN>(TEXT)<BR> {<SPAN class="keyword">@</SPAN>canvas tk(create text 0 <SPAN class="keyword">@</SPAN>y text:TEXT anchor:nw tags:<SPAN class="keyword">@</SPAN>tag)}<BR> <SPAN class="keyword">local</SPAN> <BR> [X1 Y1 X2 Y2] = {<SPAN class="keyword">@</SPAN>canvas tkReturnListInt(bbox all $)}<BR> <SPAN class="keyword">in</SPAN> <BR> y<SPAN class="keyword">:=</SPAN>Y2<BR> {<SPAN class="keyword">@</SPAN>canvas tk(configure scrollregion:q(X1 Y1 X2 Y2))}<BR> <SPAN class="keyword">end</SPAN> <BR> <SPAN class="keyword">end</SPAN> <BR> <SPAN class="keyword">meth</SPAN> <SPAN class="functionname">post</SPAN> <BR> {Port<SPAN class="keyword">.</SPAN>send <SPAN class="keyword">@</SPAN>selfPort say({<SPAN class="keyword">@</SPAN>entry tkReturn(get $)})}<BR> {<SPAN class="keyword">@</SPAN>entry tk(delete 0 <SPAN class="string">'end'</SPAN>)}<BR> <SPAN class="keyword">end</SPAN> <BR> <SPAN class="keyword">end</SPAN> <BR><SPAN class="keyword">end</SPAN></CODE></DD></DL><P></P></DIV><TABLE align="center" border="0" cellpadding="6" cellspacing="6" class="nav"><TR bgcolor="#DDDDDD"><TD><A href="node15.html#part.www"><< Prev</A></TD><TD><A href="index.html">- Up -</A></TD><TD><A href="node19.html#part.foreign.extensions">Next >></A></TD></TR></TABLE><HR><ADDRESS><A href="http://www.ps.uni-sb.de/~duchier/">Denys Duchier</A>, <A href="http://www.ps.uni-sb.de/~kornstae/">Leif Kornstaedt</A> and <A href="http://www.ps.uni-sb.de/~schulte/">Christian Schulte</A><BR><SPAN class="version">Version 1.4.0 (20110908185330)</SPAN></ADDRESS></BODY></HTML>
|