IIIMSF implementation note MIYASHITA Hisashi himi@OpenI18N.org Overview
This note describes the detailed implementation of IIIMSF.
I hope the source code of IIIMSF is clear and easy to understand.
Developpers that modify or extend IIIMSF, however, may not be able to
tell the intension of the design from the source code. This document
helps them to complehend the design of IIIMSF.
IIIMSF itself can be characterized as a middleware.
To the IIIM client side, IIIMSF serves IM facilities via IIIMP. On the other
hand, to the Language Engine side, IIIMSF serves client requests and receives
the result of LE with LE SPI.
IIIMSF works as a daemon (on UNIX). After the initialization, it starts to
accept requests from clients, and then interves the communications between
IIIM clients and Language Engines.
The static structure
The following shows the static
structure of IIIMSF in UML.
IIIMSF static structure.
Approximately, the static structure consists of the following three parts:
protocol handler main part (manages the current IM information) LE interface part. scheduler
Refer to about what structure
is correspond to each of the above part.
In the following sections, I explain those major parts of IIIMSF
one by one.
protocol handler
The protocol handler of IIIMSF manages all of the
communications related to the protocol, and actively invokes the interfaces
provided by the main part.
The protocol handler also manages all states related to the protocol. It
must properly send and receive messages to communicate with clients.
IMProtocol, IMState and ICState abstract classes keep the current state of
the protocol. Their corresponding IIIMProtocol, IIIMP_IMState and IIIMP_ICState
classes actually manage the IIIMP-specific data and methods.
IMProtocol and IIIMProtocol classes provides the direct entry interfaces,
which are used by IMScheduler, and properly create IMState object by
negotiating with the main part via IMAccept interface. In addition,
IMProtocol has a message dispatcher that receives a message and then pass
it to a IMState object.
IMState and IIIMP_IMState classes manage all of the state related to Input
Method handle. By negotiating with the main part via IMHandler interface,
it manages creation and annihilation of ICState. Besides it, IMState deals
with client's request that is independent of IC, and delegate it to
the main part via IMHandler.
ICState and IIIMP_ICState classes manage all of the state related to Input
Context handle. They actually deal with client's request of the Input
Context to which the ICState corresponds.
ICState and IIIMP_ICState classes manage all of the state related to Input
Context handle. They actually deal with client's request of the Input
Context to which the ICState corresponds.
scheduler
The scheduler is defined as IMScheduler and its subclasses (currently,
only IMScheduler_MTPC is available).
IMScheduler manages the execution thread and concurrency policy.
It recevies only IMProtocol and IMState objects. By using such objects'
interfaces, IMScheduler determines how to process the next task.
IMScheduler_MTPC(MTPC means Make a Thread Per Connection) is one of the
implementation of IMScheduler. It allocates one thread for one IMState.
The scheduler part occupies a small portion of IIIMSF, but it is
a crucial part, for it rules the concurrency policy of IIIMSF.
main part
The main part manages all of the informations of currently effective
clients, e.g, connection, desktop, user, context, .etc.
This part accepts requests from protocol handler, and properly directs
LE to process those with LE interface part. The main part also
determines which LE processes them.
From the perspective of the protocol handler, the main part works as a
passive object. It is the important characteristic. In other words,
the main part does not issue any method invocations without requesting
it via IMAccept, IMHandler, and ICHandler. These three are the public
interfaces of the main part.
In the following subsections, we will explain the essential classes of
the main part one by one.
Connection management
IIIMSF starts by start method of IMSvr class, which
is initialized at the startup, and creates an LEMgr (see the subsection of
LE management) and an IMProtocol (actually creates a IIIMProtocol object
in the current implementation),
then initializes them by looking up the configurations (from command line
argument, configuration file, or something else. These informations
are encapsulated by IMSvrCfg and its subclasses. Notice that they are
not shown in the static structure diagram so that refer to the source
code for detail).
IMSvr object provides IMAccept interface, actually it is implemented by
the abstract superclass of IMSvr, but it is not a limitation. The protocol
handler must forward the incomming request of new connection establishment
to IMAccept interface. IMSvr checks it by authenticating the request, and
if it is passed, IMSvr create IMConnection object for the new connection.
One IMConnection object represents one connection. IMSvr assigns the
incomming connection to a newly created IMConnection object. IMConnection
provides IMHandler interface. IMSvr pass it to the protocol handler via
IMAccept interface.
User management
IMUser encapsulates each user's information. IMUser object will be created
per one user that is currently connected to the server.
Inevtitably, the creation of IMUser cannot be separated from the authentication
system. IMUserMgr class handles user authentication, and create a new IMUser
object when it accepts the credential sent from a client.
Since IMUser can be related to the multiple connection (note that each
connection is represented by IMConnection object), IMConnection object
have a reference to one IMUser object, but currently, IMUser has no
direct references to IMUser object, which can be 1:n mapping.
Input context management
The protocol part requests a new input context to the IMConnection object
via IMHandler interface. The IMConnection object creates a new IMInputContext
object, which represents a created input context, and pass the ICHandler interface
provided by the object to the protocol part.
Since IMInputContext instance represents an actual entity of input context,
it naturally has one or more contexts managed by language engine(s), which
are represented by LEContext instance.
LEContext instance is created by LEBase instance, i.e., it is so-called
prototype pattern. LEMgr chooses the best candidate of LEBase by inspecting
attributes of the LEContext instance, then create a LEContext instance
from the LEBase instance. For the detail of LEBase and its manager, LEMgr,
refer to .
Language Engine management
When initialing internally, htt_server list up the currently available
language engines, which is done by LEMgr instance. LEMgr is initialized
by the IMSrv instance.
LEMgr provides approximately two sorts of services; (1) available input
methods and languages; and (2) choose an language engine (actually, it
is an LEBase instance) by the given predicate.
IMInputContext choose the best candidate of LEBase instances for the
sake of LEMgr, and then create LEContext as discussed so far.
LE(Language Engine) interface part
LE interface part sends events dispatched from the main part,
and provides functionalities to LEs.
LE interface part informs each LE of function pointers to
generate and execute IML instruction. LE can utilize them
to respond to sent events.
IML instruction is a abstract data to encapsulate actual behaviors
of an input method. Executed IML instructions are queued by IMLExec
object, and properly dispatched to clients by the protocol handler.
Dynamics Scheduler
In general, the design of dynamics is the most important but
difficult subject. If it is complicated, the software will have
many problems, to make matters worse, these are hard to resolve,
and the further change tends to be a nightmare.
In order to reduce such difficulties, in IIIMSF, I separated
the part that rules concurrency from the others, which is
the scheduler. The scheduler is a very small portion, and
its requirements are fully encapsulated by IMSchedule class.
IMSchedule class is an abstract class, thus it has naturally
one or more concrete subclasses (currently, IIIMSF has only
IMScheduler_MTPC for it).
IMScheduler has an instance of IMProtocol, and by actively
calling the method of it, it decides what operation should be
dealt with next. IMScheduler_MTPC (MTPC means Making a Thread
Per Connection) is one of the simplest implementaion of IMScheduler,
it just creates one thread every time IMProtocol makes IMState
object.
The main part must choose which implementation of IMScheduler is
used. Currently, it initializes IMScheduler_MTPC by calling
IMScheduler::construct at the startup.
protocol handler.
In contrast to the scheduler, the protocol handler does not touch
with concurrency policy. Instead, it has a responsibility for
the synchronization, and the protocol state. Every time invoked from
the scheduler, the protocol handler properly manage the current
state; receive the messages from and send ones to clients.
Therefore, in principle, the protocol handler must not block execution
flow. For example, because I/O operations may block the execution,
the protocol must use overlapped I/O or check whether I/O operations are
available before actually processing them.
state management
Since IIIMP is stateful protcol, the protocol handler must deal with
the transition of protocol state, and maintain the current state per
each input context.
In this protocol handler, `state' pattern is adopted to manage it.
This pattern represents the kind of state by class. For example,
IIIMP_ICState class represents the base state of input context, and
IIIMP_ICState_REQUESTED class represents the state that something is
now requested by a client.
In order to avoid updating references owned by other objects when a state
is changed, we adopt the proxy pattern. When changing a current state,
we actually do the following:
Create a new object for a new state (i.e. its class represents
the kind of the state.)
Relate the reference of this object to the base state object.
When the base object accepts a message, forward it to the
new object.
For resetting the state of the protocol, we call reset_state() of
such state object. This method deletes a substate object if the
base state has it and then clear the reference.
main part
From the protocol handler, the main part must behave as a passive
object. In principle, it must not actively calls any methods of
the protocol handler.
There is one exeption for it. The main part (or the other entities)
may call IMLExec::push_insts() actively. Because it only queues
IML instruction, and then return immediately. Thus, it does not
block the execution flow. In this meaning, IMLExec is not a part
of the protocol handler, but this object is passed to the protocol
handler, and executes IML instructions by interacting ICState or
IMState objects so that we should regard it as a part of the
protocol handler.