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.