Added: uima/sandbox/uima-ducc/trunk/uima-ducc-duccdocs/src/site/tex/duccbook/part5/ducc-pops-component-sm.tex URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/uima-ducc-duccdocs/src/site/tex/duccbook/part5/ducc-pops-component-sm.tex?rev=1727979&view=auto ============================================================================== --- uima/sandbox/uima-ducc/trunk/uima-ducc-duccdocs/src/site/tex/duccbook/part5/ducc-pops-component-sm.tex (added) +++ uima/sandbox/uima-ducc/trunk/uima-ducc-duccdocs/src/site/tex/duccbook/part5/ducc-pops-component-sm.tex Mon Feb 1 17:36:08 2016 @@ -0,0 +1,578 @@ +% +% Licensed to the Apache Software Foundation (ASF) under one +% or more contributor license agreements. See the NOTICE file +% distributed with this work for additional information +% regarding copyright ownership. The ASF licenses this file +% to you under the Apache License, Version 2.0 (the +% "License"); you may not use this file except in compliance +% with the License. You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, +% software distributed under the License is distributed on an +% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +% KIND, either express or implied. See the License for the +% specific language governing permissions and limitations +% under the License. +% + +% \section{DUCC Service Manager} + This section describes the architecture and internal structure of the + DUCC Service Manager, referred to as the ``SM''. + +\section{Introduction} + The SM function is to insure that any services needed by + DUCC jobs are running and functional at the time they are needed by + jobs. Previous to the incarnation of the SM it was necessary for + users to manually invoke the processes implementing their services. If + these processes were to crash, jobs dependent on them would stop until + some human was able to restart the service. If the operating system, + or batch system supporting the jobs (DUCC, in our case) was to + be restarted, users would again have to manually start the services. + + By ``registering'' a service with the SM, a user can trust DUCC to + keep the service alive and functional across all manner of faults and + system restarts. As well, the SM has a mechanism for ``testing'' a + service to determine if it is operational, and to inform the DUCC + Web Server when it is not. + + If a user submits a job that declares a dependency on a service, the SM + is able to start the service as needed, and is able to stop the service + when no longer needed, freeing resources. + + In essence, the SM can be thought of as a ``proxy user'' dedicated + to insuring that services are always available when needed. + +\section{Architectural Overview} + + Figure ~\ref{fig:sm-structure} below shows the high-level object, + threading, and process structure of SM and should be referenced + while reading this document. + + The SM can be pictured as being composed of four major parts: + \begin{enumerate} + \item Initialization and interaction with external components. + External components include user requests and other DUCC components such as + the Orchestrator. + \item A ``Service Instance'' manager. This part resolves + dependencies on services, starts and stops service instances + according to the needs of jobs and the policies declared in + the service registries, and handles the service instance + lifetimes. + \item A ``Service Health'' manager. This part continually + ``tests'' services to determine whether they are + functional. This is referred to as the ``pinger'' and the + test is known as a ``ping''. + \item A ``CLI Handler'' which reacts to requests from users. + \end{enumerate} + + \begin{figure}[H] + \centering + \includegraphics[width=5.5in]{images/ducc-internals/sm-structure.png} + \caption{Service Manager Structure} + \label{fig:sm-structure} + \end{figure} + + The terminology around Services can be confusing. We review the ideas here. + + There are three ``countable'' entities involved in services. + \begin{description} + \item[Service Registration] When a service is ``registered'' the Service Manager assigns + a new, unique {\em Registration ID} to the registration. This ID is associated with, and + remains with, the service throughout its lifetime and beyond when it is archived. + + \item[Service Instance] When the Service Manager starts a service it issues a series of + ``submit'' orders to the Orchestrator, one for each {\em Service Instance}. All these + instances are associated with the {\em Service Registration}. The orchestrator assigns + a unique ID to each service instance, which is also permanently associated with that entity. + + This is analogous to a {\em job}, but with a single allocation. The SM organizes + multiple {\em Service Instances} under a single {\em Service Registration}. + + \item[Share Id] When an instance is submitted to the Orchestrator, the Orchestrator + ``submits'' a request to the Resource Manager to find resources. Each instance is + treated independently by the RM. When a resource is found for the instance, the RM + assigns a {\em Share ID} to the allocation. + + This is analagous to a {\em job's} process or ``PE''. + \end{description} + + The {\em Service Registration} ID appears on the Services' page of the webserver. The {\em + Service Instance} ID and the {\em Share Id} appear in the ID column of the service details in + the DUCC webserver. For example, this ID: {\tt 289661.34094} indicates {\em Service Instance} + ID {\tt 289661} and {\em Share Id} {\tt 34094}. + +\section{Initialization and Interaction with DUCC} + The component responsible for initialization and external interaction is + implemented in the source file {\em ServiceManagerComponent.java}. This + straightforward bit of code performs the following functions, details of which + are easy to understand by reading the source code itself. + + \begin{description} + \item[Initialization] This consists of the methods init() and + start(). The DUCC framework instantiates ServiceManagerCompoent and calls + its start() method. This initializes various structures from + {\em ducc.properties}, initializes the database connection, and + initializes the two main threads: + \begin{enumerate} + \item The SM proper, {\em ServiceManagerComponent}, which + fires its {\em run()} method which in turn calls {\em init()}. + \item The Service Instance manager, implemented in {\em ServiceHandler.java}. + \end{enumerate} + The {\em init} method reads all registrations from its state repository and + passes them to {\em register()} (using the same code path as the {\em register} CLI), + to establish them in a memory map and possibly initialize them. + + + \item[Interaction With DUCC] There are three primary interactions to be aware of: + \begin{enumerate} + \item Incoming Orchestrator publications. This arrives on an external + communication thread and passed to + the method {\em orchestratorStateArrives} which, if it accepts the + publication, saves the incoming publication and issues a {\em notify()} to + the main {\em ServiceManagerComponent} thread to allow processing of the state. + + The method {\em processIncoming} is then called which does standard DUCC + state-differencing and passes updates to the {\em Instance Management} code + in {\em ServiceHandler}. + + \item CLI requests. These are passed via the usual DUCC event handlers to specific + second-level handlers, one for each type of CLI request (e.g. {\em register()}). Each of these + second-level handlers is responsible for these actions: + \begin{description} + \item[User validation.] Insure the caller of the CLI is {\em authenticated}, i.e. + is the user he claims to be. + \item[Ducc is running.] The DUCC Orchestrator must be actively publishing state + before SM is allowed to interact with users. + \end{description} + + If these simple tests are passed, the request is passed to the Instance Management + code in {\em ServiceHandler} to check for authorization (i.e. is this user allowed + to perform this action against this service). + + \item Outgoing state publications. Outgoing state is a simple map, one entry per + job (a ``job'' for SM is any unit of work in the system: UIMA-AS job, Service instance, + Reservation, AP). The entry contains the state of the job relative to any + services it depends on, which is interpreted by the Orchestrator and Web Server. + + \end{enumerate} + + Note that Orchestrator publications and CLI requests may be ignored under these two conditions: + \begin{enumerate} + \item SM initialization is not complete. Completion is flagged as the last + action of {\em init()}. + \item RM has not yet assigned the JD node. The incoming OR publication includes a flag to + indicate whether the JD node is assigned. We have to wait here because we do not want + the SM to process work or initialize any services until it is confirmed that the system + is fully initialized. Note that this minimizes the occurrence of errors and simplifies + error management because you can all errors occurred in a fully initialized environment. + \end{enumerate} + + \end{description} + + +\section{Service Instance Management: ServiceHandler and ServiceSet} + After the differencing engine has determined the various work events that have + occurred, the Service Instance Management code examines each event + and acts upon it. + +\subsection{Operational Overview} + The code described below runs in a thread separate from the main thread + described in the previous section. Incoming events are placed on lists + segregated by function (a list for new Jobs, a list for updated Jobs, etc). As soon + as all incoming events are placed on these lists the {\em Service Instance Management} + thread is {\em notified}. The {\em Service Instance Management} thread sets a lock, + drains the lists into internal structures, and releases the lock. This segregates the + the actions of the {\em ServiceManagerComponent} from {\em Service Instance Management}. + + As incoming events are acted upon, a summary of the service state for all + incoming work is built up. After all events are processed, the {\em ServiceManagerComponent} + (previous section) is notified and the state publication is sent to the Orchestrator. + + There are two primary components involved in {\em Service Instance Management:} + \begin{description} + \item[ServiceHandler.java] This is a singleton object which runs in its own + thread sepparate from the {\em ServiceManagerComponent}. It fields the + updates from the Orchestrator and CLI, resolves Service dependencies, and signals + the {\em ServiceSet} for each affected service so appropriate action can be taken. + It maintains all the records of registered services and service-dependent jobs + in an inner class {\em ServiceStateHandler}. + \item[ServiceSet.java] There is one {\em ServiceSet} for every registered service. It is + instantiated on receipt of a registration and destroyed only when a service is unregistered. + It is responsible for submitting service instances to the Orchestrator, reacting to state + changes of the Service Instances, enforcing management policies ({\em reference start}, {\em + autostart}, {\em manual start}), and fielding the data from the service Pinger. + \end{description} + +\subsection{ServiceHandler.java} + The work-related events, fielded by {\em ServiceHandler}, are described below. + These events can be placed into two broad categories: + \begin{enumerate} + \item Events relating to work that requires services, usually UIMA-AS jobs + \item Events relating to service instances for registered services. + \end{enumerate} + + Within each category are three types of interesting events. + \begin{enumerate} + \item A new job or service instance has entered the system. This is essentially a REQUEST + from the Orchestrator, asking if all necessary services are available. No work + has been started in the system, and will not be until SM responds ``services available'' + to the request. + + NOTE that the Orchestrator has a ``fast-path'' for work that has no service dependencies, in + that it does not wait for the SM to respond regarding such work. SM does in fact respond, + but after the fact, and the response is not used. + + \item An existing job or service instance has changed state. This is work that the + Orchestrator has started: there are physical processes either started, or in the act + of starting and their states may be evolving. + + \item An existing job or service instance has terminated. + \end{enumerate} + + While technically any type of DUCC work can be dependent on a service, by far the most common + is UIMA-AS Jobs and Service instances. The SM must treat work which IS a Service Instance + a little differently from all other work (because all work is potentially depdenent on the + state of Service Instances). Below we will use the term ``Job'' to refer to any + kind of work that is not a service instance. + + NOTE: for simplicity, the descriptions in this section use the term ``services available'' to refer + to the single state {\em Available} which indicates a service is running and is successfully + pinging, and ``services unavailable'' to refer to all other states. The complete set of + states is encoded in the class +\begin{verbatim} +org.apache.uima.ducc.transport.event.sm.IService.java +\end{verbatim} + in the enum {\tt ServiceState} as shown below. +\begin{verbatim} + public enum ServiceState + { + Pending, // Work is waiting on at least one service to start + Waiting, // A job is waiting on at least one service to ping + Starting, // Instance is started, but not yet to Initializing + Initializing, // A job is waiting on at least one service to initialize + Available, // All services for this job are active and pinging, or else + // no services are needed for the job + NotAvailable, // SM to OR only: reference to a non-existent service + Stopped, // The service is not started + Stopping, // Service is told to stop but it takes a while + Undefined, // Catch-all, means basically "who cares" + ; + } +\end{verbatim} + + \paragraph{Service Events}. Service events are {\em processed in the order shown + below.} The order is important because overall service state is advancing through the + first three events. + + \begin{description} + \item[A new Service Instance has arrived.] The associated {\em ServiceSet} + is found and signalled. If the associated {\em ServiceSet} cannot be found this + is considered a ``rogue'' service instance and is ignored. This occurs if + the incoming process cannot be matched with a registered service; for example, if + the {\em DuccServiceSubmit} CLI is called outside of SM. Usually this is an + error condition but is not considered fatal by SM. + + If this service is dependent on other services, the {\em ServiceSet}s for those + other services are fetched and signalled, which may in turn cause additional + service instances to be submitted. If all of these other services are Running, + the new service is marked ``services available'' and the Orchestrator will + physically start the new instance. Otherwise it is marked ``services unavailable'' + and will not be allowed to start until it's own service dependencies are running. + + Note how this implements a sort of ``domino'' effect for starting services. Suppose + you have two services, A dependent on B, and a job dependent on A, + with none of these running. When job A arrives at SM it is marked ``services unavailable'' and + service A is submitted. When service A arrives from Orchestrator it is marked ``services unavailable'' and + service B is submitted. When service B arrives from Orchestrator it is marked ``services available'' so + that Orchestrator may start it. When it starts, service A is marked ``services available'' and is + started by Orchestrator. When service A starts, the job is finally marked ``services available'' and + is allowed to start. + + As mentioned above, if the services is NOT dependent on other services, the Orchestrator + will fast-path its start, and SM will mark it ``services available''. + + \item[An existing Service Instance has arrived.] The {\em ServiceSet} is + fetched and signalled with the state of the incoming instance. This may cause + the Service State to be updated, for example from Initializing to Running. Pingers + may be started as needed. + + Note that, because this is processed BEFORE jobs are processed, job state may be + updated from ``services available'' to ``services unavailable'' (or vice-versa). + + \item[A Service Instance has exited.] Some service instance for some managed + service has exited for some reason. + + The associated {\em ServiceSet} is found and signalled. The {\em ServiceSet} + takes appropriate action, restarting the instance if the exit + was unexpected, or perhaps simply updating its records if the service is + being stopped or the number of instances reduced. + + \item[A new job has arrived.] + The declared service dependencies are parsed and the {\em ServiceSet} + object for each such service is signalled. The {\em ServiceSet} increments the reference count for + the service and, in the case of {\em reference-started} + services, starts some number of instances (using the ``hidden'' CLI utility + {\em DuccServiceSubmit}. + + Based in the current state of the service, the job is flagged with ``services available'' or + ``services unavailable''. The job is marked ``services available'' if-and-only-if all + its declared services are started, in running state, and being successfully {\em tested} (``pinged''). + + \item[An existing job has arrived.] Work that is not new may need its service state reevaluated. + The declared service dependencies are parsed and the associated {\em ServiceSet} + objects are fetched. An analysis of the combined work states and service + states is done. The job is flagged ``services available'' or ``services unavailable''. + + Note that a job's service-state can change from ``services available'' to ``services + unavailable'' if a service fails for some reason. + + \item[A job has exited.] A job which might be dependent + on a service has left the system. + + The ServiceHandler examines all the declared services for the work. For each + dependency, if the service is started, the ServiceSet for the service is + signalled. Each affected {\em ServiceSet} decrements its reference count. If the + count goes to zero, and if this is + a reference-started service, the {\em ServiceSet} stops all instances. + + \end{description} + +\subsection{ServiceSet.java} +{\em ServiceSet} is responsible for the care-and-feeding of the set of objects, threads, and +processes used to manage an individual service. There is one {\em ServiceSet} instantiated for +every registered service. {\em ServiceSet} does NOT run in its own thread. Its methods are always +executed either on the thread of the {\em ServiceHandler} or on a {\em pinger} thread. It is +responsible for maintaining the correct number of running instances, fielding pings, and updating +the state repository (as of DUCC 2.1.0, a database) for a single service. + + The primary functions include: + \begin{description} + \item[Enforce Autostart] If the service is registered for autostart, insure sufficient instances are started + and start new ones if needed. This is called at the end of each update cycle from the {\em ServiceHandler}. + + \item[Manage references] Maintain a reference count to reflect all work that is + referencing the service. If the service is ``reference started'' this can trigger + the start of new instances and the shutdown of existing instances. This count is + always maintained so if the administrator changes the start-up policy for the service, + the reference count is already correct and is used. + + \item[Start-up and hot start] On hot-start, the method {\em bootInstances()} is called to + synchronize each service set with the incoming orchestrator publications, followed by + bootComplete() to finalize bookkeeping and update the meta state data. + + \item[Field pings] When the Orchestrator state indicates that at least one instance for the + service is in Running state, the {\em ServiceSet} starts a pinger. After every ping, + the pingers call {\em signalReblance()} to respond to the ping and enforce any + potential state changes as a result of the ping, including starting and stopping + specific instances. + + \item[Sequence Instance Startup] The {\em ServiceSet} also sequences instance start-up so that + in general, there is only a single instance starting at time. This is managed in the + method {\em needNextStart()}; + + Instances are sequenced up for several reasons: + \begin{itemize} + \item Avoid flooding the system with start requests during boot. + \item Avoid start/fail loops with faulty services. + \end{itemize} + + \item[Respond to Instance State Change] The {\em ServiceHandler} invokes the {\em ServiceSet} + method {\em signalUpdate} on every Orchestrator update. This method examines the entire + state of the service and coordinates any actions which may be triggered by state change + of a service instance. + + \item[State Accumulation] The state of a service is dependent on the cumulative states of + all of its instances PLUS the state if its pinger. The method {\em cumulativeJobState()} + is responsible for state accumulation. + + The design point is that states are associated with an ordinal. The larger the ordinal, + the ``closer to functional'' a service is. The lower the ordinal, the ``farther from functional'' + the service is. State accumulation walks the states of all relevant components maintaining + the {\em maximum} state encountered. This {\em maximum} is considered to be the state of the + service overall. The method {\em translateJobState()} is used to assign the ordinal + based on the state of the service instances. + + \item[State Management] There is a very simple state machine managed by two methods, + {\em signal()} and {\em setState()}. The details can be found by examining these methods. + The design point is this: most actions in the SM are considered {\em idempotent}. Thus, + regardless of the outcome of state change (or lack of state change), these actions are + ALWAYS called; for example, ``start the pinger''. The methods implementing the actions + are responsible to determine whether the current situation is compatible with the requested + action. For example, if the method {\em startPingThread()} is called, that method must + check to see if the ping thread is already running, and not start a new thread if so. + + This design makes the state machine extremely simple and easy to maintain. + + \item[Lingering Stop] {\em Lingering stop} occurs in a reference-started service when the + final reference has exited. It is implemented by a Java TimerTask, {\em LingerTask}. + If the timer ``pops'', this task stops the service. If a new reference arrives + before the timer ``pops'', the task is canceled. + + \end{description} + +\subsection{ServiceInstance.java} + The {\em ServiceInstance} object is a simple helper whose responsibility it is to start + a new instance. It spawns a {\em DuccServiceSubmit} process as the user via {\em ducc\_ling} with a pointer to the + registration properties, and scrapes the output in the response to get the Orchestrator-assigned + ID. The ID and any error messages are returned to the calling {\em ServiceSet}. + + This mechanism is used to manage ping-only services as well. When a ping-only service is started, + a subclass of ServiceInstance is started. This {\em PingOnlyServiceInstance} simulates the start + of an actual instance. It maintains an internal thread that invokes {\em ServiceSet.signal()} to simulate + the {\em ServiceHandler}'s regular state updates. It is essentially a ``service proxy'' for the + non-DUCC-handled service that is being pinged, eliminating the need for most special cases in the + handling of ping-only services. + +\section{Service Health Management: Ping support} + When the {\em ServiceSet} detects that at least one instance has achieved {\em Running} state, + it calls the method {\em startPingThread()}. + + {\em startPingThread()} starts a thread dedicated to managing the pinger, with the class + {\em PingDriver} running as its (logical) ``main''. The {\em PingDriver} reaches back into its + {\em ServiceSet} for the parameters needed to manage the pinger (Java class, ping interval, + etc). + + The {\em PingDriver} implements two cases: + \begin{enumerate} + \item Internal pingers + \item External pingers + \end{enumerate} + + In both cases, the {\em PingDriver} object creates an object from {\em Ping.java}, loads + the object with a {\em Map} of user and DUCC-supplied parameters, and passes the {\em Ping} to + the ping implementation. A timer is set and a response is waited for. + + In both cases the ping mechanism collects ping parameters and passes them to the {\em AServicePing} + object which was extended to create the pinger. The ping mechanism then calls various + methods on {\em AServicePing} to extract its state, constructs a {\em Pong} object, and uses + this to communicate with the SM by invoking {\em ServiceSet.signalRebalance()}. The details + of how this occurs is different for internal and external pingers in the {\em PingDriver}. + + \subsection{Internal Pingers} + + If the pinger is registered as an ``Internal'' pinger, a Java ClassLoader is invoked to load + the pinger's registered class. A timer loop is established to invoke the pinger. On + each invocation, the {\em PingDriver} directly invokes the methods on {\em AServicePing} + required to invoke the pinger and retrieve its state. A {\em Pong} object is created + from the state and passed to common code in the method {\em handleResponse()}. + + \subsection{External Pingers} + + If the pinger is registered as an ``External'' pinger, a ServerSocket is started and its listen + port acquired. An instance of the class {\em ServicePingMain} is spawned under the identity of + the owning userid of the service via {\em ducc\_ling}, passing the listen port and appropriate + arguments needed to start the user's pinger. The stdout and stderr of the resultant process is + captured and written to the SM log and the ping loop (describe below) is started. + + The {\em ServicePingMain} object receives a set of start-up parameters including the + name of the user's ping class, classpath, start-up parameters, and endpoint. The user's + pinger, extending {\em AServicePing} is instantiated by {\em ServicePingMain} using a + ClassLoader. {\em ServicePingMain} then goes into + an infinite, untimed loop waiting for the {\em Ping} requests from the {\em PingDriver}. When + a request arrives, the parameters in the request are gathered and sent to the user's pinger + by calling methods on {\em AServicePing} as is done for internal pingers. + The response is received and returned to {\em ServiceSet} in a {\em Pong} object. + + The embedded class {\em PingDriver.PingThread} is used to implement the protocol between SM and + the external pinger. The wire protocol between SM and {\em ServicePingMain} is quite simple. + + On the SM side: + \begin{itemize} + \item SM starts a ServerSocket and then issues an {\em accept()} waiting for the external pinger to connect. + \item Once the pinger connects, enter a loop until terminated by SM: + \begin{itemize} + \item Write a new {\em Ping} object to the socket stream. + \item Read a new {\em Pong} object from the socket stream. + \end{itemize} + \end{itemize} + + If SM terminates the pinger, the {\em Ping} object includes a flag that is read by + {\em ServicePingMain} causing it to shutdown the user's pinger and exit. + + On the external pinger's side, with {\em ServicePingMain} started by {\em ducc\_ling} as + the owner of the service: + \begin{itemize} + \item Read the command-line parameters and then: + \begin{itemize} + \item Connect to the SM on it's listen socket. + \item Instantiate the user's ping class using a classloader. + \end{itemize} + \item Enter a loop: + \begin{itemize} + \item Read the next Ping + \item If the Ping has the {\em exit} flag set, call the pinger's {\em stop()} method, close the + input and output streams, and exit. + \item Otherwise invoke the pinger's base methods defined in {\em AServicePing}. + \item Construct a {\em Pong} object and write it to the SM's socket. + \end{itemize} + \end{itemize} + + + \section{CLI Management} + The CLI management code is relatively simple but has a few details worth mentioning. + + {\em ServiceManagerComponent} receives the incoming CLI call, insures the user identified + in the incoming packet is the one who submitted it, that the DUCC Orchestrator processes + is functioning, and passes the incoming packet to {\em ServiceHandler}. Each different + CLI function is passed to the appropriate second-level handler in {\em ServiceHandler}; for + example, a registration request is passed to {\em ServiceHandler.register()}. + + Secondary vetting of the incoming request is then performed: + \begin{itemize} + \item Check to see if the service exists. If so, and it's a registration then fail with a + ``duplicate services'' message. If it is NOT a registration and it does not exist, fail with a + ``service not found'' message. + \item Insure the caller is authorized for the desired action (e.g. is the caller an + admin or owner for the service). + \item If the request can be performed immediately, do so and return confirmation. These requests are + \begin{itemize} + \item Register + \item Query + \item Enable + \item Disable + \item Ignore references + \item Observe references + \end{itemize} + \item If the request can take an arbitrarily long time, then build a deferred CLI object + and enqueue it. This deferred object is implemented in {\em ApiHandler.java}. Return + confirmation to the user that the request is accepted. These requests are + \begin{itemize} + \item Start + \item Stop + \item Modify + \item Unregister + \end{itemize} + + \end{itemize} + + Deferred requests are dequeued and executed one at a time. The user is not directly + informed of this execution as it may complete at an arbitrary time in the future. For + example, stopping or unregistering a service both involve an elaborate sequence of + actions to stop the processes, deallocate the space, and update the SM records. + Starting a service is also quite elaborate and additionally, service starts are + sequenced so they are submitted one-at-a-time; services with many instances can take + a significant time to fully start. + + These requests are synchronized with other SM activities to minimize race conditions by + handling them in the same execution thread in {\em ServiceHandler} that is handling incoming Orchestrator + publications. Deferred requests are implemented in two methods each: + \begin{enumerate} + \item An ``immediate'' method which vets the CLI parameters, constructs, and enqueues the deferred request: + \begin{itemize} + \item ServiceHandler.start() + \item ServiceHandler.stop() + \item ServiceHandler.modify() + \item ServiceHandler.unregister() + \end{itemize} + \item A ``deferred'' method, invoked from the deferred request: + \begin{itemize} + \item ServiceHandler.doStart(); + \item ServiceHandler.doStop(); + \item ServiceHandler.doModify(); + \item ServiceHandler.doUnregister(); + \end{itemize} + + \end{enumerate} +