################################################ # # # ## ## ###### ####### ## ## ## ## ## # # ## ## ## ## ## ### ## ## ## ## # # ## ## ## ## #### ## ## ## ## # # ## ## ###### ###### ## ## ## ## ### # # ## ## ## ## ## #### ## ## ## # # ## ## ## ## ## ## ### ## ## ## # # ####### ###### ####### ## ## ## ## ## # # # ################################################ The following paper was originally published in the Proceedings of the Third USENIX Conference on Object-Oriented Technologies and Systems Portland, Oregon, June 1997. For more information about USENIX Association contact: 1. Phone: 510 528-8649 2. FAX: 510 548-5738 3. Email: office@usenix.org 4. WWW URL: https://www.usenix.org Service Configurator : A Pattern for Dynamic Configuration of Services Prashant Jain and Douglas C. Schmidt pjain@cs.wustl.edu and schmidt@cs.wustl.edu Department of Computer Science Washington University St. Louis, MO 63130, (314) 935-4215 *This research is supported in part by a grant from Siemens Medical Engineering. Abstract This paper describes the Service Configurator pattern, which decouples the implementation of services from the time when they are configured. This pattern increases the flexibility and extensibility of applications by enabling their constituent services to be configured at any point in time. The Service Configurator pattern is widely used in application environments (e.g., to configure Java applets into WWW browsers), operating systems (e.g., to configure device drivers), and distributed systems (e.g., to configure standard Internet communication services). 1. Introduction A rapidly growing collection of services is now available on the Internet. The term service has several generally accepted meanings: (1) a single capability offered by a server (such as the echo service provided by the inetd superserver), (2) a collection of capabilities offered by a server (such as the inetd superserver itself), and (3) a collection of servers that cooperate to achieve a common task (such as a collection of rwho daemons in a LAN that periodically broadcast and receive status reports on user and host activities). Unless otherwise indicated, this paper uses the first definition of service, i.e., an identifiable component in a server that offers a single capability to communicating entities. The range of services available on the Internet include: WWW browsing and content retrieval services, software distribution services, electronic mail and network news transfer agents, file access on remote machines, remote terminal access, routing table management, host and user activity reporting, network time protocols, and object request brokerage services. A common way to implement these services is to develop each one as a separate program and then compile, link, and execute each program in a separate process. However, this ``static'' approach to configuring services yields inflexible, and often inefficient, applications and software architectures. The main problem with this static approach is that it tightly couples the implementation of a particular service with the configuration of the service with respect to other services in an application or system. This paper describes the Service Configurator pattern, which increases application flexibility, and often performance, by decoupling the behavior of services from the point in time at which these service implementations are configured into an application or system. The examples in this paper illustrate the Service Configurator pattern using Java applets. However, the Service Configurator pattern has been implemented in many ways, ranging from device drivers in modern operating systems (like Solaris and Windows NT) to Internet superservers (like inetd and the Windows NT Service Control Manager). This paper is organized as follows: Section describes the Service Configurator pattern using a variant of the GoF pattern format and Section presents concluding remarks. 2. The Service Configurator Pattern 2.1 Intent Decouples the behavior of services from the point in time at which service implementations are configured into an application or system. 2.2 Also Known As Super-server 2.3 Motivation The Service Configurator pattern decouples the implementation of services from the time at which the services are configured into an application or a system. This decoupling improves modularity of the services and allows the services to evolve over time independently of configuration issues, such as whether or not two services must be co-located or what concurrency model will be used to execute the services. In addition, the Service Configurator pattern provides centralized administration of all the services it configures. This facilitates automatic initialization and termination of the services and can optimize performance by performing common service initialization and termination activities. This section motivates the Service Configurator pattern using a distributed time service as an example. 2.3.1 Context The Service Configurator pattern should be applied when a service needs to be initiated, suspended, resumed, and terminated dynamically. In addition, the Service Configurator pattern should be applied when service configuration decisions must be deferred until run-time. To illustrate this pattern, consider the distributed time service shown in Figure . This service provides accurate, fault-tolerant clock synchronization for computers collaborating in local area networks and wide area networks. A synchronized time service is important in distributed systems that require multiple hosts to maintain accurate global time. For instance, large-scale distributed medical imaging systems require globally synchronized clocks to ensure that patient exams are accurately timestamped and analyzed expeditiously by radiologists throughout the health-care delivery system. As shown in Figure , the architecture of the distributed time service contains the following components: Time Server -- which answers queries about the time made by Clerks. Clerk -- which queries one or more Time Servers to determine the correct time, calculates the approximate correct time using one of several distributed time algorithms, and updates its own local system time. Client -- which uses the global time information maintained by a Clerk to provide consistency with the notion of time used by clients on other hosts. 2.3.2 Common Traps and Pitfalls One way to implement the distributed time service is to statically configure the logical functionality of Time Servers, Clerks, and Clients into separate physical stand-alone processes. In this static approach, one or more hosts would run Time Server processes, which handle time update requests from Clerk processes. Each host that requires global time synchronization would run a Clerk process. The Clerks periodically update their local system time based on values received from one or more Time Servers. Client processes would then use the synchronized time reported by their local Clerk. For platforms that support shared memory, communication overhead can be minimized by storing the current time into shared memory that is mapped into the address space of the Clerk and all Clients on the same host. In addition to the time service, other services (such as file transfer, remote login, and HTTP servers) provided by the hosts would also execute in separate statically configured processes. However, implementing and configuring services in the static manner shown above has the following drawbacks: Service configuration decisions must be made too early in the development cycle: This early binding is undesirable since developers may not know a priori the best way to co-locate or distribute service components. For example, minimal memory resources in wireless computing environments may force the split of Client and Clerk into two independent processes running on separate hosts. In contrast, in a real-time avionics environment it might be necessary to co-locate the Clerk and the Time Server into one process to reduce communication latency. Forcing developers to commit prematurely to a particular service configuration impedes flexibility and can reduce performance and functionality. Modifying or terminating a service may adversely affect other services: In the static approach, the implementation of each service component is tightly coupled with its initial configuration. This makes it hard to modify one service without affecting other services. For example, in the real-time avionics environment mentioned above, a Clerk and a Time Server might be statically configured to execute in one process to reduce latency. If the distributed time algorithm implemented by the Clerk is changed, the existing Clerk code would require modification, recompilation, and relinking. However, terminating the process to change the Clerk code would also terminate the Time Server. This disruption in service availability may not be acceptable for mission critical distributed systems (such as telecommunication switches or call centers). System performance may not scale up efficiently: Associating a process for each service ties up valuable OS resources (such as I/O descriptors, virtual memory, and process table slots). This can be wasteful if services are frequently idle. Moreover, processes are often the wrong concurrency model for many short-lived communication tasks (such as asking a Time Server for the current time or resolving a host address request in the Domain Name Service). In these cases, a multi-threaded Active Object or a single-threaded Reactive event loop may be more efficient. 2.3.3 Solution Often, a more convenient and flexible way to implement distributed services is to use the Service Configurator pattern . This pattern decouples the behavior of services from the point in time at which the service implementations are configured into an application or system. The Service Configurator pattern resolves the following forces: The need to defer the selection of a particular type, or a particular implementation, of a service until very late in the design cycle: Dynamic configuration allows developers to concentrate on the functionality of a service, without committing themselves prematurely to a particular configuration of services. By decoupling functionality from configuration, the Service Configurator pattern permits applications to evolve independently of the configuration policies and mechanisms used by the system. The need to build complete applications or systems by composing multiple independently developed services that do not require global knowledge: The Service Configurator pattern requires all services to have a uniform interface for configuration and control. This allows the services to be treated as modular building blocks that can be integrated easily as components in a larger application. Enforcing a uniform interface for all services makes them ``look and feel'' the same with respect to how they are configured, thereby simplifying application development. The need to optimize and control the behavior of a service at run-time: Decoupling implementation details of a service from configuration decisions makes it possible to fine-tune certain implementation or configuration parameters of services. For instance, depending on the parallelism available on the hardware and operating system, it may be either more or less efficient to run one or more services in separate threads or processes. The Service Configurator pattern enables applications to select and tune these behaviors at run-time, when additional information (such as the number of CPUs or the OS version) is available to help optimize the services. In addition, adding a new or updated service to a distributed system may not require downtime for existing services. Figure uses OMT notation to illustrate the structure of the distributed time service designed according to the Service Configurator pattern. The Service base class provides a standard interface for configuring and controlling services (such as Time Servers or Clerks). A Service Configurator-based application uses this interface to initiate, suspend, resume, and terminate a service, as well as to obtain service-specific information (such as the service name, host address, and port number). Services reside within a Service Repository and can be added to and removed from the Service Repository by Service Configurator-based applications. Two subclasses of the Service base class appear in the distributed time service: TimeServer and Clerk . Each subclass represents a concrete Service , which has specific functionality in the distributed time service. The TimeServer service is responsible for receiving and processing requests for time updates from Clerks . The Clerk service is a Connector factory that performs the following tasks: Creates a new connection for every server; Dynamically allocates a new handler to send time update requests to a connected server; Receives the replies from all the servers through the handlers; Updates the local system time based on an average of all TimeServer responses. The Service Configurator pattern improves the flexibility of the distributed time service by managing the configuration of service components in the time service. Thus, configuration decisions (such as whether or not to co-locate the TimeServer and Clerks ) are decoupled from implementation details (such as the algorithm used by a Clerk to update its notion of time). In addition, implementations of the Service Configurator pattern can provide a framework that consolidates the configuration and management of application services in one administrative unit. 2.4 Applicability Use the Service Configurator pattern when: Services must be initiated, suspended, resumed, and terminated dynamically; and An application or system can be simplified by being composed of multiple independently developed and dynamically configurable services; or The management of multiple services can be simplified or optimized by configuring them using a single administrative unit. Do not use the Service Configurator pattern when: Dynamic configuration is undesirable due to security restrictions (in this case, static configuration of trusted services may be necessary); or The initialization or termination of a service is too complicated or too tightly coupled to its context to be performed in a uniform manner; or Stringent performance requirements mandate the need to minimize the extra levels of indirection used by the the OS and language mechanisms for dynamic configuration. 2.5 Structure and Participants The structure of the Service Configurator pattern is illustrated using OMT notation in Figure . The key participants in the Service Configurator pattern include the following: Service ( Service ) Specifies the interface that contains the abstract hook methods (such as methods for initialization and termination) used by a Service Configurator-based application to dynamically configure each Service . Concrete Service ( Clerk and TimeServer ) Implements the service hook methods and other service-specific functionality (such as event processing and communication with clients and other services). Service Repository ( ServiceRepository ) Maintains a repository of all services offered by a Service Configurator-based application. This allows an administrative entity to centrally manage and control the behavior of application services. 2.6 Collaborations Figure depicts the collaborations in following three phases between components of the Service Configurator pattern: Service configuration -- The Service Configurator initializes a Service by calling its init method. Once the Service has been initialized successfully, the Service Configurator adds it to the ServiceRepository . The ServiceRepository is used by the Service Configurator to manage and control all Services that are installed. Service processing -- A Service is executed once it has been configured into the system. Once a Service is executing, the Service Configurator can suspend and resume the Service . Service termination -- The Service Configurator terminates a Service once it is no longer needed. The Service Configurator calls the fini method on the Service to allow it to clean up before terminating. Once a Service is terminated, it is removed from the ServiceRepository . Not all systems support service termination. For example, the Java run-time environment that implements the Service Configurator pattern provides no way to terminate an applet or unload it once it has been loaded into the run-time environment ( e.g., a WWW browser). 2.7 Consequences 2.7.1 Benefits The Service Configurator pattern offers the following benefits: Centralized administration of services: The pattern consolidates one or more services into a single administrative unit. This simplifies development by automatically performing common service initialization and termination activities (such as opening and closing files, acquiring and releasing locks, etc.). In addition, it centralizes the administration of services by enforcing a uniform set of configuration management operations on them (such as initialize, suspend, resume, and terminate). Increased modularity and reuse: The pattern improves the modularity and reusability of services by decoupling the implementation of these services from the configuration of the services. In addition, all services have a uniform interface by which they are configured, thereby encouraging reuse and simplifying development of subsequent services. Increased opportunity for tuning and optimization: The pattern increases the range of service configuration alternatives available to developers by decoupling service functionality from the concurrency models ( e.g., threads or processes) used to invoke the service. Developers can adaptively tune daemon concurrency levels to match client demands and available OS processing resources by choosing from a range of concurrency models. Some alternatives include spawning a thread or process upon receipt of a client request or pre-spawning a thread or process at service creation time. 2.7.2 Drawbacks The Service Configurator pattern has the following drawbacks: Lack of determinism: The pattern makes it hard to determine the behavior of a service and/or application until run-time. This is particularly problematic for real-time systems since a dynamically configured service may perform unpredictably when run with certain services. For example, if consumers in a real-time Event Service do not obey their periodic processing constraints, other real-time services will miss their deadlines and the system will not behave predictably. Reduced reliability: An application that uses the Service Configurator pattern may be less reliable than one that does not because a particular configuration of services may adversely affect the execution of the services. For instance, a faulty service may crash, thereby corrupting state information it shares with other co-located services. This is particularly problematic for open systems , such as Java applets within WWW browsers that configure and execute multiple services within the same process. Increased overhead: The pattern adds extra levels of indirection to execute a service. For instance, the Service Configurator first initializes the service and then loads it into the Service Repository. This may be undesirable or an unnecessary overhead in time-critical applications. In addition, the Service Configurator pattern often configures services via dynamic linking, which adds extra indirection to invoke functions and access global variables . Lack of generality: If services are tightly coupled, it may not be possible to dynamically configure them in arbitrary ways using the Service Configurator pattern. For example, it may be necessary to configure two services in a specific order or it may be necessary to always co-locate two services. The Service Configurator pattern only provides the mechanism of decoupling service implementation from service configuration -- it does not dictate any policy by which services are to be configured. Therefore, the Service Configurator is a building block in a ``pattern language'' of strategies for dynamically configuring and reconfiguring services. 2.8 Implementation The Service Configurator pattern has been implemented in many contexts, ranging from device drivers in operating systems like Solaris and Windows NT, Internet superservers like inetd, and Java applets in WWW browsers. This section explains the steps and alternatives involved when implementing the pattern. These steps and alternatives are summarized in Table . Define the service control interface: The following is the core interface that a service should support to enable the Service Configurator to configure and control the service: Initialization -- Provides an entry point into the service and performs initialization of the service; Termination -- Terminates execution of a service and provides a hook to cleanup application resources; Suspension -- Temporarily suspends the execution of a service; Resumption -- Resumes execution of a suspended service; Information -- Obtains information about a service to determine its identity and behavioral characteristics. There are two ways to define the service control interface: inheritance-based and message-based , as described below: Inheritance-based service control interface -- In this approach, each service inherits from a common base class. This approach is used by the ACE Service Configurator framework and Java applets, which defines abstract base classes that contain pure virtual ``hook'' methods. The following shows the Service interface similar to the one provided in ACE: class Service { public: // = Initialization and termination hooks. virtual int init (int argc, char *argv[]) = 0; virtual int fini (void) = 0; // = Scheduling hooks. virtual int suspend (void); virtual int resume (void); // = Informational hook. virtual int info (char **, size_t) = 0; }; The init method is the entry point hook into a Service. It is used by the Service Configurator to initialize and execute a Service . The fini method is a hook that allows the Service Configurator to terminate the execution of a Service . The suspend and resume methods serve as scheduling hooks and are used by the Service Configurator to suspend and subsequently resume the execution of a Service . The info method allows the Service Configurator to obtain Service -related information (such as its name and network address). Together, these methods impose a contract between the Service Configurator and the Service objects that it manages. Message-based service control interface -- Another way to control services is to program them to respond to a specific set of messages. This makes it possible to integrate the Service Configurator into non-OO programming languages (such as C). The Windows NT Service Control Manager ( SCM ) uses this scheme. Each Windows NT host has a master SCM process that automatically initiates and manages system services by passing them control messages such as pause , resume , and terminate . Each developer of an SCM -managed service must write code to process these messages and perform the intended actions. Define a Repository: A Service Repository maintains all the Service implementations in the form of objects, executable programs, and/or dynamically linked library (DLLs). A Service Configurator uses the Service Repository to access a service when it is configured into or removed from the system. In addition, the Repository maintains the current status of each service ( e.g., whether a service is active or suspended). This information may reside in main memory, a file system, or the kernel. Select a configuration mechanism: A service must be configured before it can execute. Configuring a service requires specifying attributes that indicate the location of the service's implementation (such as an executable program or DLL), as well as the parameters required to initialize a service at run-time. This configuration information can be specified in various ways ( e.g., on the command line, through a user interface, or through a configuration file). A centralized configuration mechanism (such as the NT Registry or inetd.conf file) simplifies the installation and administration of the services in an application by consolidating service attributes and initialization parameters in a single location. Determine the service concurrency model: A service that has been dynamically configured by a Service Configurator can be executed using various combinations of Reactive and Active Object schemes. These alternatives are briefly outlined below: Reactive execution - This approach uses a single thread of control to execute the Service Configurator and all the services it configures. Multi-threaded Active Objects - This approach runs the dynamically configured services in their own threads of control within the Service Configurator process. The Service Configurator can either spawn new threads ``on-demand'' or execute the services within an existing pool of threads. Multi-process Active Objects - This approach runs the dynamically configured services in their own processes. The Service Configurator can either spawn new processes ``on-demand'' or execute the services within an existing pool of processes. 2.9 Sample Code The following code shows an example of the Service Configurator pattern in the context of Java applets . An applet is a Java class that can be loaded and run by a Java application (such as a Web browser, an applet viewer, or an application). The example below focuses on the configuration-related aspects of the distributed time service described in Section . In addition, this example illustrates how other patterns (such as the Active Object pattern and the Acceptor and Connector patterns) are commonly used in conjunction with the Service Configurator pattern to develop flexible communication infrastructure and services. In the example, the Concrete Service class in the OMT class diagram shown in Figure is represented by the TimeServer class and the Clerk class. The Java code in this section implements the TimeServer and the Clerk classes. To save space, most of the detailed Java code and exception handling code has been omitted. Both classes inherit from java.applet.Applet . This allows them to be downloaded (e.g., from an HTTP server) and dynamically configured (e.g., into a Java interpreter within a WWW browser). The WWW server's file system serves as the Service Repository for the Java applets. In addition, the java.applet.Applet class provides hook methods that allow dynamic (1) configuration of a service ( init ), (2) suspension of a service ( stop ), and (3) resumption of a service ( start ). Note that the java.applet.Applet class does not provide a termination method equivalent of fini described in Section . The Service Configurator pattern remains at the heart of the Java applets, however, by allowing their implementation to be decoupled from their dynamic configuration. 2.9.1 The TimeServer Class The TimeServer uses the Acceptor class to accept connections from one or more Clerks. The Acceptor class uses the Acceptor pattern to create handlers for each Clerk connection that wants to receive requests for time updates. This design decouples the implementation of the TimeServer from its configuration. Therefore, developers can change the implementation of the TimeServer independently of its configuration. This design provides flexibility with respect to evolving the implementation of the TimeServer class. The TimeServer class inherits from the standard java.applet.Applet class, which enables a TimeServer to be dynamically loaded into a running Java application. Once the TimeServer applet has been downloaded and verified, the Java run-time system invokes its init hook. This method performs the Time Server -specific initialization code. The TimeServer class implements the Runnable interface. This allows it to become an active object and run in its own thread of control. Running TimeServer as an active object is useful if the applet's main thread of control must perform other tasks (such as responding to user GUI events and methods called by the system). import java.applet.Applet; public class TimeServer extends Applet implements Runnable { // Initialize the TimeServer when loaded. This // may include synchronizing server clock with // an atomic clock. This method corresponds // to the init() hook method of the Service // Configurator pattern. public void init () // Initialize. // (Re)start the TimeServer. Note that this method // gets called after init() when the applet first // starts up in the context of Java run-time // system and also when the applet is restarted // after being temporarily stopped. The method // spawns off a new thread to handle Clerk // connections if a thread is not already running. // Otherwise it resumes the currently suspended // thread. This method corresponds to the resume() // hook method of the Service Configurator pattern. public void start () if (serverThread_ == null) serverThread_ = new Thread (this); serverThread_.start (); else // Resume the server thread. serverThread_.resume (); // Temporarily stop/suspend the TimeServer. // This method suspends the thread that handles // Clerk connections. This method corresponds // to the suspend() hook method of the Service // Configurator pattern. public void stop () if (serverThread_ != null && serverThread_.isAlive ()) // Suspend the server thread. serverThread_.suspend (); // Return information about the TimeServer // by overriding the method defined in the // java.applet.Applet class. This method // corresponds to the info() hook method of // the Service Configurator pattern. public String getAppletInfo () // Return a String containing information // about this applet. This may include the // name of the host, the version number, etc. return new String ( ... ); // This method serves as the entry point for // the Time Server thread. It is called // when the thread starts. public void run () // Set the connection acceptor_ endpoint into // listen mode (using the Acceptor pattern). acceptor_.open (port_); // Now use the acceptor_ to accept // connections from Clerks. // ... // Acceptor used for Clerk connections. protected Acceptor acceptor_; // Port the TimeServer listens on. private int port_ = SERVER_PORT_NUMBER; // The Server Thread private Thread serverThread_ = null; // ... } The Java run-time system can suspend and resume the TimeServer by calling its stop and start hooks, respectively. In addition, it can call getAppletInfo method to obtain useful information about the service, such as the version number or the name of the author. 2.9.2 The Clerk Class The Clerk uses the Connector pattern to establish and maintain connections with one or more TimeServers . The Connector pattern creates a handler for every connection to a TimeServer. The handlers receive and process time updates from the TimeServers . The java.applet.Applet base class is the parent of the Clerk class. Therefore, like the TimeServer , a Clerk can be dynamically configured by the Java run-time system acting in the role of Service Configurator. The Java run-time system can initialize, suspend, resume, and obtain information about the Clerk by calling its init , stop , start , and getAppletInfo hooks, respectively. import java.applet.Applet; public class Clerk extends Applet implements Runnable { // Initialize the Clerk when loaded. This // may include initializing the algorithm // implementation to be used to compute the // Clerk's notion of time. This method // corresponds to the init() hook method of // the Service Configurator pattern. public void init () // Initialize. // (Re)start the Clerk. Note that this method // gets called after init() when the applet first // starts up in the context of Java run-time // system and also when the applet is restarted // after being temporarily stopped. The method // spawns off a new thread to setup connections // with the TimeServers if a thread is not already // running. Otherwise it resumes the currently // suspended thread. This method corresponds to // the resume() hook method of the Service // Configurator pattern. public void start () if (clerkThread_ == null) clerkThread_ = new Thread (this); clerkThread_.start (); else // Resume the Clerk thread. clerkThread_.resume (); // Temporarily stop/suspend the Clerk. This // method suspends the thread that handles // connection to TimeServers. This method // corresponds to the suspend() hook method // of the Service Configurator pattern. public void stop () if (clerkThread_ != null && clerkThread_.isAlive ()) // Suspend the Clerk thread. clerkThread_.suspend (); // Return information about the Clerk by // overriding the method defined in the // java.applet.Applet class. This method // corresponds to the info() hook method of // the Service Configurator pattern. public String getAppletInfo () // Return a String containing information about // this applet. This may include the name of // the host, the version number, etc. return new String ( ... ); // This method serves as the entry point for // the Clerk thread. It is called when the // thread starts. public void run () // Use the connector to set up connections // to all the TimeServers. Then use the // updateTime() method to send periodic requests // to the TimeServers for time updates, receive // the requests from the TimeServers, and compute // the local notion of time. // ... // Called periodically to compute the local // system time. protected void updateTime (long t) // Implement Clock Synchronization algorithm // here to compute local system time. // Connect to TimeServers. protected Connector connector_; // The Clerk Thread. private Thread clerkThread_ = null; } The Clerk periodically sends a request for time update to all its connected TimeServers . Once the Clerk receives responses from all its connected TimeServers , it recalculates its notion of the local system time. Thus, when Clients ask a Clerk for the current time, they receive a globally synchronized value. Figure shows a state diagram of the lifecycle of a Service (such as the Clerk service). Initially the service is idle. Depending upon requirements, the user can choose from various implementations dynamically, without having to focus on configuration issues. For instance, different Clerk services may exist corresponding to different algorithm implementations. Thus, the user may either select a Clerk service that implements the Berkeley algorithm or a Clerk service that implements Cristian's algorithm . The choice may depend upon the characteristics of the TimeServer . If the machine on which the TimeServer resides has a WWV receiver A WWV receiver intercepts the short pulses broadcasted by the National Institute of Standard Time (NIST) to provide Universal Coordinated Time (UTC) to the public. the TimeServer can act as a passive entity and Cristian algorithm would be best suited. On the other hand, if the machine on which the Time Server resides does not have a WWV receiver then an implementation of the Berkeley algorithm would be more appropriate. Once a Clerk service has been selected, it can be easily configured by loading it into the Java run-time environment (such as a Web browser, an applet viewer, or an application). The following HTML fragment shows how the Clerk applet can be loaded in an applet viewer or a Web browser: The APPLET tag specifies an applet to be run within a Web browser or an applet viewer. The PARAM tag specifies named parameters to be passed to the applet. An applet can look up the value of a parameter specified in a PARAM tag with the Applet.getParameter method. In the example above, the Clerk applet is passed the name of a service configuration file and a pollTime of 10 seconds. This configuration file ( svc.conf ) contains the hostnames and port numbers of all the Time Servers the Clerk will connect to. The pollTime indicates how frequently the Clerk will poll the Time Servers. To reduce communication latency, The Clerk service can be co-located with a Time Server service. The following HTML fragment shows how the Clerk applet can be loaded in an applet viewer or a Web browser together with a co-located Time Server applet: In this example, the Time Server class will listen at port 7734. Figure shows the Clerk running independently as well as running co-located with a Time Server. This configuration decision need not affect the implementation of the various time services. Note, however, that if the Clerk and the Time Server are co-located in the same process, the Clerk may optimize communication by (1) eliminating the need to set up a communication channel with the Server and (2) directly accessing the Server's local notion of time via shared memory. In general, the decoupling between a service implementation and its configuration exemplifies the flexibility offered by the Service Configurator pattern. 2.10 Known Uses The Service Configurator pattern has been used in a wide range of operating system and application programming environments including Java applets, UNIX, Windows NT, and ACE: Java applets: The applet mechanism in Java uses the Service Configurator pattern. Java supports dynamically downloading, initializing, starting, suspending and resuming applets. For instance, it defines methods ( e.g., stop and start ) that suspend and resume applet threads. A method in a Java applet can access the thread it is running in using Thread.currentThread() . In addition, threads can control each other by invoking methods like stop and start. Modern operating system device drivers: Modern operating systems (such as Solaris and Windows NT) support dynamically configurable kernel-level device drivers. For instance, Solaris drivers can be linked into and unlinked out of the system dynamically via init / fini / info hooks. This makes it possible to reconfigure the operating system without having to shut it down, recompile and relink new drivers into the kernel, and then restart the system. UNIX network daemon management: The Service Configurator pattern has been used in ``superservers'' that manage UNIX network daemons. Two widely available network daemon management frameworks are inetd and listen . Both frameworks consult configuration files that specify (1) service names (such as the standard Internet services ftp, telnet, daytime, and echo), (2) port numbers to listen on for clients to connect with these services, and (3) an executable file to invoke and perform the service when a client connects. These frameworks contain a master Acceptor process that reactively monitors the set of ports associated with the services. When a client connection occurs on a monitored port, the Acceptor process accepts the connection and demultiplexes the request to the appropriate pre-registered service handler. This handler performs the service (either reactively or in an active object) and returns any results to the client. The Windows NT Service Control Manager (SCM): Unlike inetd and listen , the Windows NT Service Control Manager ( SCM ) is not a port monitor. That is, it does not provide built-in support for listening to a set of I/O ports and dispatching server processes ``on-demand'' when client requests arrive. Instead, it provides an RPC-based interface that allows a master SCM process to automatically initiate and control ( i.e. , pause, resume, terminate, etc.) administrator-installed services (such as remote registry access). These services would otherwise run as separate threads within a single-service or a multi-service daemon process. Each installed service is individually responsible for configuring itself and monitoring any communication endpoints (which may be more general than I/O ports, e.g., named pipes or shared memory). The ADAPTIVE Communication Environment (ACE) framework: The ACE framework provides a set of C++ mechanisms for configuring and controlling services dynamically. The ACE Service Configurator extends the mechanisms provided by inetd , listen , and SCM to automatically support dynamic linking and unlinking of services. The mechanisms provided by ACE were influenced by the interfaces used to configure and control device drivers in modern operating systems. Rather than targeting kernel-level device drivers, however, the ACE Service Configurator framework focuses on dynamic configuration and control of application-level Service objects. 2.11 Related Patterns The intent of the Service Configurator pattern is similar to the Configuration pattern . The Configuration pattern decouples structural issues related to configuring services in distributed applications from the execution of the services themselves. The Configuration pattern has been used in frameworks for configuring distributed systems (such as Regis and Polylith) to support the construction of a distributed system from a set of components. In a similar way, the Service Configurator pattern decouples service initialization from service processing. The primary difference is that the Configuration pattern focuses more on the active composition of a chain of related services, whereas the Service Configurator pattern focuses on the dynamic initialization of service handlers at a particular endpoint. In addition, the Service Configurator pattern also focuses on decoupling service behavior from the service's concurrency strategies. The Manager Pattern manages a collection of objects by assuming responsibility for creating and deleting these objects. In addition, it provides an interface to allow clients access to the objects it manages. The Service Configurator pattern can use the Manager pattern to create and delete Services as needed, as well as to maintain a repository of the Services it creates using the Manager Pattern. However, the functionality of dynamically configuring, initializing, suspending, resuming, and terminating a Service created using the Manager Pattern must be added to fully implement the Service Configurator Pattern. A Service Configurator often makes use of the Reactor pattern to perform event demultiplexing and dispatching on behalf of configured services. Likewise, dynamically configured services that run for a long periods of time often execute using the Active Object pattern . Administrative interfaces (such as configuration files or GUIs) to a Service Configurator-based system provide a Facade. This Facade simplifies the management and control of applications that are executing within the Service Configurator. The virtual methods provided by the Service base class are callback ``hooks'' or ``hook methods''. These hooks are used by the Service Configurator to initiate, suspend, resume, and terminate services. A Service (such as the Clerk class) may be created using a Factory Method . This allows an application to decide the type of Service subclass to create. 3. Concluding Remarks This paper describes the Service Configurator pattern and illustrates how it decouples the implementation of services from their configuration. This decoupling increases the flexibility and extensibility of services. In particular, service implementations can be developed and evolved over time independently of many issues related to service configuration. The Service Configurator pattern also centralizes the administration of services it configures. This centralization can simplify programming effort by automating common service initialization tasks (such as opening and closing files, acquiring and releasing locks, etc). In addition, centralized administration can provide greater control over the lifecycle of services. The Service Configurator pattern has been applied widely in many contexts. This paper used Java applets to demonstrate the application of the Service Configurator pattern in the Java run-time system. The ability to decouple the development of Java applets from their configuration into the Java run-time system exemplifies the flexibility offered by the Service Configurator pattern. This decoupling allows different applets to be developed in accordance with different service implementations. The decision to configure a particular applet into the Java run-time system becomes a run-time decision, which yields greater flexibility. The Service Configurator pattern is also widely used in other contexts such as device drivers in Solaris and Windows NT, Internet superservers like inetd, the Windows NT Service Control Manager, and the ACE framework. In each case, the Service Configurator pattern decouples the implementation of a service from the configuration of the service. This decoupling supports both extensibility and flexibility of applications. 4. Availability The ADAPTIVE Communication Environment (ACE) provides an implementation of the Service Configurator pattern. ACE is freely available via the WWW at www.cs.wustl.edu/ schmidt/ACE.html .