It is a design target to provide a environment for doblet clients and servers to be written in many different languages. However, for the short term, we will focus on java based servers and clients, for simplicity, and getting a better understanding of the problem and the do's and dont's.
The interfaces and this document can be split up in two separate parts:
package org.doblet.message public interface SendableMessage { String getId(); void send(); } public interface ReceivedMessage { String getId(); Serializable getBody; Channel getReceiveChannel(); SendableMessage createReply(Serializable body); } public interface Channel { SendableMessage createMessage(Serializable body); void destroy(); } public interface MessageReceiver { void handleMessage(ReceivedMessage msg); } public interface ThreadSafeReceiver extends MessageReceiver { }The general idea is that two "objects" (it is not yet defined what kind of objects), communicate with each other over a dedicated
Channel
.
SendableMessage
using a
Channel
, and invokes the send
method.
Channel
as a ReceivedMessage
and is passed to the
MessageReceiver
object on that other side, using the
handleMessage
method.
MessageReceiver
can examine the message body, but
also it's origin, by identifying which channel the message was
received from.
Channel
to create a new message.
However there is a special method createReply
to create a
reply to a specific ReceivedMessage
.
Using this special method gives the underlying implementation a
possibility to correlate the "reply" to the original "request".
This feature has not yet been worked out any further, but might allow
a more convenient request.reply mechanism to be used in the future.
MessageReceiver
will
receive messages one at a time, i.e. the handleMessage
will not be called until the previous handleMessage
has
finished and returned.
This makes it easy for programmers, because they don't have to worry
about thread safety.
A more advanced programmer might try to make his code thread safe
anyway, and mark it as such by implementing the special marker
interface ThreadSafeReceiver
.
If a MessageReceiver
also implemtents that interface the
message dispatcher is allowed (but not obliged) to pass multiple
messages to the receiver in separate threads.
This might give rise to improved performance, but with properly
designed asynchronous code it should not be necessary.
Note that even a normal MessageReceiver
might have a risk
of multiple threads, because a GUI in general has a separate event
loop thread.
So even if it will have to handle only one message at a time, there is
a possibility that it will receive a GUI event during the handling of
the message.
When the Channel
is broken (by either side, or if a
network problem is detected), both sides of the Channel
will receive a special system message.
public interface SystemMessage extends ReceivedMessage { } public interface ChannelDestroyedMessage extends SystemMessage { }This message will always be the last message to be received on a Channel. A
SystemMessage
is special since it is not sent by the
other side of the Channel
but by the system/middleware
itself.
A SystemMessage will not have an Id (i.e. it will return a null String
as id).
You will not be able to create a reply to a ChannelDestroyMessage,
since the channel is already destroyed.
Channel
as defined in the previous interfaces.
Once connected, they can exchange messages as described above.
Each client has exactly one Channel
(connecting it to
it's server), while a server may have zero, one or more
Channel
's, depending on the number of clients.
There are four main objects to be identified:
Doblet
's, each one
identified by a unique url.
package org.doblet public interface DobClient extends MessageReceiver { void init(DobletBrowser, Channel, Logger); } public interface Doblet extends MessageReceiver { void init(DobletServer, Logger); } public interface DobletBrowser { Request connect(String Url); } public interface DobletServer { Request createDoblet(DobletClass class); } public interface NewClientMessage extends SystemMessage {} public interface WelcomeMessage extends SystemMessage {}Note: For a definition and an explanation of the
Request
interface see the "Asynchronous Object Requests" document.
A DobletServer and a DobletBrowser are typically quite large object or applications, with all kind of administration and runtime functionality and a GUI to use it. The interfaces presented here only show the core methods to be implemented by each corresponding object. More specifically, the interfaces basically show how a DobletServer and a Doblet cooperate, and how a DobletBrowser and a DobletClient cooperate.
DobletServer::createDoblet
with
the DobletClass of which an instance is to be created.
Doblet::init
.
createDoblet
call, or are performed outside
of this method.
Step 2 should be performed asynchronously if retrieving the Doblet
code is done over a network.
Step 3 should in general always be performed asynchronously, because a
Doblet might use asynchronous initialization (e.g. connecting to a
remote resource, such as a database server).
handleMessage
on the
requested Doblet with a NewClientMessage
.
The channel in this message is the newly created channel.
destroy
method.
NewClientMessage
.
Any data passed in the body of this reply will be received by
the client as it's first message which is of type
WelcomeMessage
.
handleMessage
call.
Alternatively it might take a long time, and should be done
asynchronously outside of the handleMessage
call.
A typical case of the latter would be, where the Doblet sends messages
to other clients (e.g. all existing clients, or the client with
"operator" status.), who manually have to accept or decline the new
client.
The middleware should guard objects from sending messages on a channel
that is not accepted yet (or that has been destroyed), by throwing an
exception in such cases.
This exception should be thrown by the send
method, and
not by the createMessage
or createReply
method.
The reason for this is that someone may create a message while the
channel is still active, but when he "finally" tries to send the
message, the Channel has been destroyed.
ChannelDestroyedMessage
.
This will happen in all cases where a client disconnects:
destroy
on it's channel.
destroy
on it's channel.
DobletBrowser::connect
with
the url of the Doblet it wants to connect to.
DobletClient::init
.
ChannelDestroyedMessage
.
This will happen in all cases where a client is disconnected:
destroy
on it's channel.
destroy
on it's channel.
Serialiable
or should we
try to be more specific (e.g. XML-able), or less java specific?
SystemMessage
interfaces OK, or should we encode this in the body of an ordinary
message?
ChannelDestroyedMessage
, or will the
middleware act as if this channle has never existed?
public class SystemMessageImpl implements SystemMessage { private Channel channel; public SystemMessageImpl(Channel chan) { channel = chan; } public String getId() { return null; } public Serializable getBody() { return null; } public Channel getReceiveChannel() { return channel; } public SendableMessage createReply(Serializable body) { return null; } } public class ChannelDestroyedMessageImpl extends SystemMessageImpl implements ChannelDestroyedMessage { }