Acceptor-Connector An Object Creational Pattern for Connecting and Initializing Communication Services
Douglas C. Schmidt [email protected] Department of Computer Science Washington University St. Louis, MO 63130, USA
An earlier version of this paper appeared in a chapter in TRACKING the book Pattern Languages of Program Design 3, edited SATELLITES STATION by Robert Martin, Frank Buschmann, and Dirke Riehle pub- PEERS lished by Addison-Wesley, 1997.
1Intent STATUS INFO The Acceptor-Connector design pattern decouples connec- WIDE AREA NETWORK tion establishment and service initialization in a distributed COMMANDS BULK DATA system from the processing performed once a service is ini- TRANSFER tialized. This decoupling is achieved with three compo- nents: acceptors, connectors,andservice handlers. A con- GATEWAY nector actively establishes a connection with a remote ac- ceptor component and initializes a service handler to pro- cess data exchanged on the connection. Likewise, an ac- LOCAL AREA NETWORK ceptor passively waits for connection requests from remote GROUND connectors, establishing a connection upon arrival of such a STATION PEERS request, and initializing a service handler to process data ex- changed on the connection. The initialized service handlers Figure 1: The Physical Architecture of a Connection- then perform application-specific processing and communi- oriented Application-level Gateway cate via the connection established by the connector and ac- ceptor components. The Gateway transmits data between its Peers using the connection-oriented TCP/IP protocol [1]. In our exam- 2 Example ple network configuration, each service is bound to a con- nection endpoint designated by an IP host address and a TCP To illustrate the Acceptor-Connector pattern, consider the port number. The port number uniquely identifies the type of multi-service, application-level Gateway shown in Fig- service. Maintaining separate connections for each type of ure 1. In general, a Gateway decouples cooperating com- service/port increases the flexibility of routing strategies and ponents in a distributed system and allows them to interact provides more robust error handling if network connections without having direct dependencies on each other. The par- shutdown unexpectedly. ticular Gateway in Figure 1 routes data between different In our distributed application, Gateway and Peers must service endpoints running on remote Peers used to mon- be able to change their connection roles to support different itor and control a satellite constellation. Each service in use-cases. In particular, either may initiate a connection ac- the Peers sends and receives several types of data via the tively or may wait passively for connection requests. For Gateway, such as status information, bulk data, and com- example, in one configuration, the Gateway may actively mands. In general, Peers can be distributed throughout lo- initiate connections to remote Peers in order to route data cal area networks (LANs) and wide-area networks (WANs). to them. In another configuration, the Gateway may pas- The Gateway is a router that coordinates the communi- sively receive connection requests from Peers which then cation among its PeersFrom˙ the Gateway’s perspective, route data through the Gateway to another Peer.Like- the Peer services whose data it routes differ solely in terms wise, Peers may be active connection initiators in one use- of their application-level communication protocols, which case and then be passive connection acceptors in another use- may use different framing formats and payload types. case.
1 Due to the nature of our distributed application, con- may be necessary to extend the Gateway to interoper- ventional designs that designate connection establishment ate with a directory service that runs over the IPX/SPX and service initialization roles a priori and hard-code them communication protocol, rather than TCP/IP.. into the Gateway and Peer components are too inflexi-
It should be possible to decouple (1) the connection ble. Such a design overly couples the connection establish- roles, i.e., which process initiates a connection vs. ac- ment, service initialization, and service processing compo- cepts the connection, from (2) the communication roles, nents. This tight coupling make it hard to change connection i.e., which service endpoint is the client or the server. In roles independently of the communication roles. general, the distinction between “client” and “server” refer to communication roles, which may be orthogo- 3 Context nal to connection roles. For instance, clients often play the active role when initiating connections with a pas- A client/server application in a distributed system that uti- sive server. However, these connection roles can be re- lizes connection-oriented protocols to communicate between versed. For example, a client that plays an active com- service endpoints. munication role may wait passively for another process to connect to it. The example in Section 2 illustrates this latter use-case. 4Problem It should be possible to write communication software that is portable to many OS platforms in order to max- Distributed applications often contain complex code that per- imize availability and market share. Many low-level forms connection establishment and service initialization. In network programming APIs have semantics that are general, the processing of data exchanged between service only superficially different. Therefore, it hard to write endpoints in a distributed application is largely independent portable application using low-level APIs, such as sock- of configuration issues such as (1) which endpoint initiated ets and TLI, due to syntactic incompatibilities. the connection, i.e.,theconnection role vs. the communca-
tion role and (2) the connection management protocol vs. the It should be possible to shield programmers from the network programming API. These issues are outlined below: lack of typesafety in low-level network programming APIs like sockets or TLI. For example, connection es-
Connection role vs. communication role: Connection establishment roles are inherently asymmetrical, i.e., the pas- tablishment code should be completely decoupled from sive service endpoint waits and the active service endpoint subsequent data transport code to ensure that endpoints initiates the connection. Once the connection is established, are used correctly. Without this strong decoupling, for however, the communication role can be orthogonal to the instance, services may mistakenly read or write data on connection role. Thus, data can be transferred between ser- passive-mode transport endpoint factories that should vice endpoints in any manner that obeys the service’s com- only be used to accept connections.
munication protocol. Common communication protocols in- It should be possible to reduce connection latency by clude peer-to-peer, request-response, and oneway streaming. using OS features like asynchronous connection es- tablishment. For instance, applications with a large The connection management protocol vs. the network programming API: Different network programming in- number of peers may need to asynchronously establish terfaces, such as sockets or TLI, provide different APIs to many connections concurrently. Efficient and scalable establish connections using various connection management connection establishment is particularly important for protocols. Regardless of the protocol used to establish a con- applications that run over long-latency WANs.
nection, however, data can be transferred between endpoints It should be possible to reuse as much general-purpose using uniform message passing operations, e.g., send/recv connection establishment and service initialization soft- calls. ware as possible in order to leverage prior development In general, the strategies for connection establishment and effort. service initialization change much less frequently than ap- plication service implementations and communication pro- tocols. Thus, decoupling these aspects so that they can vary 5 Solution independently is essential for developing and maintaining distributed applications. The following forces impact the so- For each service offered by a distributed application, use the lution to the problem of separating the connection and ini- Acceptor-Connector pattern to decouple connection estab- tialization protocols from the communication protocol: lishment and service initialization from subsequent process- ing performed by the two endpoints of a service once they
It should be easy to add new types of services, new ser- are connected and initialized. vice implementations, and new communication proto- Introduce two factories that produce connected and ini- cols without affecting the existing connection establish- tialized service handlers, which implement the application ment and service initialization software. For instance, it services. The first factory, called acceptor, creates and ini-
2 Service Handler Service Service Handler Connector Handler ACTIVATES Acceptor connect(sh, addr) n 1 peer_acceptor_ ACTIVATES peer_stream_ complete() open() accept() 1 n open()
NOTIFY WHEN NOTIFY WHEN CONNECTION COMPLETES CONNECTION ARRIVES
Dispatcher
Figure 2: Structure of Participants in the Acceptor-Connector Pattern tializes a transport endpoint that passively listens at a par- Handler and uses its transport endpoint factory to accept a ticular address for connection requests from remote connec- new connection into the Service Handler. tors. The second factory, a connector, actively initiates a con- Connector nection to a remote acceptor. Acceptor and connectors both Connector: A is a factory that implements initialize the corresponding service handlers that process the the strategy for actively establishing a connection and ini- Service Handler data exchanged on the connection. Once the service handlers tializing its associated . It provides a Acceptor are connected and initialized they perform the application- method that initiates a connection to a remote . specific processing, and generally do not interact with the Likewise, it provides another method that finishes activating Service Handlers acceptor and connector any further. whose connections were initiated either synchronously or asynchronously. The Connector uses two separate methods to support asynchronous connec- 6 Structure tion establishment transparently. Both the Acceptor and Connector activate a The structure of the participants in the Acceptor-Connector Service Handler by calling its activation hook method pattern is illustrated by the Booch class diagram [2] in Fig- when a connection is established. Once a Service ure 2.1 Handler is completely initialized by an Acceptor or Service Handler: A Service Handler implements an Connector factory it typically does not interact with these application service, typically playing the client role, server components any further. role, or both roles. It provides a hook method that is called Dispatcher: For the Acceptor,theDispatcher de- Acceptor Connector by an or to activate the application multiplexes connection requests received on one or more service when the connection is established. In addition, the transport endpoints to the appropriate Acceptor.The Service Handler offers a data-mode transport endpoint Dispatcher allows multiple Acceptors to register with that encapsulates an I/O handle, such as a socket. Once con- it in order to listen for connections from different peers on Service nected and initialized, this endpoint is used by the different ports simultaneously. Handler to exchange data with its connected peer. For the Connector,theDispatcher handles the Acceptor: An Acceptor is a factory that implements completion of connections that were initiated asyn- the strategy for passively establishing a connection and chronously. In this case, the Dispatcher calls back to initializing its associated Service Handler. In addi- the Acceptor when an asynchronous connection is es- tion, the Acceptor contains a passive-mode transport end- tablished. The Dispatcher allows multiple Service point factory that creates new data-mode endpoints used by Handlers to have their connections initiated and com- Service Handler’s to transmit data between connected pleted asynchronously by a Connector. Note that the peers. The Acceptor’s open method initializes its trans- Dispatcher is not necessary for synchronous connection port endpoint factory once by binding it to a network address, establishment since the thread of control that initiates the such as the TCP port number the Acceptor is listening on. connection also completes the service handler activation. Once initialized, the passive-mode transport endpoint fac- A Dispatcher is typically implemented using an event tory listens for connection requests from peers. When a con- demultiplexing pattern, such those provided by the Re-
nection request arrives, the Acceptor creates a Service actor [3] or Proactor [4], which handle synchronous and 1 asynchronous demultiplexing, respectively. Likewise, the In this diagram dashed clouds indicate classes; dashed boxes in the clouds indicate template parameters; a solid undirected edge with a hollow Dispatcher can be implemented as a separate thread or circle at one end indicates a uses relation between two classes. process using the Active Object pattern [5].
3 7 Dynamics threads, opening log files, and/or registering the Service Handler with a Dispatcher. The following section describes the collaborations per- formed by the Acceptor and Connector components 3. Service processing phase: After the connection has in the Acceptor-Connector pattern. We examine the three been established passively and the Service Handler canonical scenarios: for the Acceptor, asynchronous has been initialized, the service processing phase begins. Connector, and synchronous Connector. In this phase, an application-level communication protocol, such as HTTP or IIOP, is used to exchange data between the 7.1 Acceptor Component Collaborations local Service Handler and its connected remote Peer via its peer stream endpoint. When this exchange is Figure 3 illustrates the collaboration between the complete the connection and Service Handlers can be Acceptor and Service Handler participants. These shut down and resources released.
acc : sh: : disp Server Acceptor Service Dispatcher Handler 7.2 Connector Component Collaborations open() INITIALIZE PASSIVE ENDPOINT The Connector component can initialize its Service
INSERT IN DISPATCHER register_handler(acc) Handler using two general schemes: synchronous and
PHASE handle_events() asynchronous. Synchronous service initialization is useful START EVENT LOOP ENDPOINT for the following situations: INITIALIZATION FOREACH EVENT DO select() accept() CONNECTION EVENT
CREATE, sh = new Service_Handler If the latency for establishing a connection is very low, ACCEPT, peer_acceptor.accept e.g., establishing a connection with a server on the same AND ACTIVATE (sh->peer_stream_) host via the loopback device; or PHASE OBJECT SERVICE sh->open() REGISTER HANDLER
register_handler(sh) If multiple threads of control are available and it is effi- INITIALIZATION FOR CLIENT I/O cient to use a different thread to connect each Service handle_event() Handler synchronously; or DATA EVENT
PROCESS MSG service() PHASE If the services must be initialized in a fixed order and SERVICE
PROCESSING the client cannot perform useful work until connections are established. Figure 3: Collaborations Among Acceptor Participants Likewise, asynchronous service initialization is useful in collaborations are divided into three phases: opposite situations: 1. Endpoint initialization phase: To initialize a connec- tion passively, an application calls the open method of the If connection latency is high and there are many peers to Acceptor. This method creates a passive-mode trans- connect with, e.g., establishing a large number of con- port endpoint and binds it to a network address, e.g. the nections over a high-latency WAN; or local host’s IP name and a TCP port number, and then
If only a single thread of control is available, e.g.,ifthe listens for connection requests from peer Connectors. OS platform does not provide application-level threads; Next, the open method registers the Acceptor object or with a Dispatcher so that the dispatcher can call back to the Acceptor when connection events arrive. Finally, If the order in which services are initialized is not im- the application initiates the Dispatcher’s event loop, portant and if the client application must perform addi- which waits for connection requests to arrive from peer tional work, such as refreshing a GUI, while the con- Connectors. nection is being established. 2. Service initialization phase: When a connection request arrives, the Dispatcher calls back to the The collaborations among the participants in the syn- Acceptor’s accept method. The accept method chronous Connector scenario can be divided into the fol- assembles the resources necessary to (1) create a new lowing three phases: Service Handler, (2) use its passive-mode transport endpoint factory to accept the connection into the data- 1. Connection initiation phase: To synchronously ini- mode transport endpoint of this handler, and (3) activate the tiate a connection between a Service Handler and Service Handler by calling its open hook. The open its remote Peer, an application calls the Connector’s hook of the Service Handler can perform service- connect method. This method actively establishes the specific initialization, such as allocating locks, spawning connection by blocking the thread of control of the calling thread until the connection completes synchronously.
4 3. Service processing phase: This phase is similar to the con : sh: : disp Client Connector Service Handler Dispatcher other service processing phases described earlier. Once the Service Handler is activated it performs application- FOREACH CONNECTION connect(sh, addr) specific service processing using the data exchanged with the INITIATE CONNECTION INITIATION/ remote Service Handler it is connected to. INITIATION open() ACTIVATE OBJECT PHASE Figure 5 illustrates these three phases of collaboration us- INSERT IN DISPATHER register_handler(sh) ing asynchronous connection establishment. In the asyn-
SEVICE INITIALIZATION CONNECTION con : sh: : disp handle_events() Client START EVENT LOOP Connector Service_Handler Dispatcher select() FOREACH EVENT DO handle_event() FOREACH CONNECTION DATA ARRIVES connect(sh, addr) PHASE INITIATE CONNECTION SERVICE service() register_handler(con) PROCESS DATA INSERT IN DISPATCHER PROCESSING PHASE PHASE
INITIATION CONNECTION handle_events() START EVENT LOOP Figure 4: Collaborations Among Connector Participants for FOREACH EVENT DO select() Synchronous Connections complete() CONNECTION COMPLETE
PHASE ACTIVATE OBJECT open() SERVICE
INSERT IN REACTOR register_handler(sh) 2. Service initialization phase: After the connection INITIALIZATION completes, the Connector’s connect method calls the complete method to activates the Service Handler. DATA ARRIVES handle_event() The complete method performs the activation by invok- PROCESS DATA service() PHASE ing the Service Handler’s open hook method, which SERVICE PROCESSING performs service-specific initialization.
3. Service processing phase: This phase is similar to the service processing phase a Service Handler performs Figure 5: Collaborations Among Connector Participants for once it is created by an Acceptor. In particular, once a Asynchronous Connections Service Handler is activated it performs application- specific service processing using data exchanged with the chronous scenario, note how the connection initiation phase remote Service Handler it is connected to. is separated temporally from the service initialization phase. This decoupling enables multiple connection initiations (via The collaboration for synchronous service initialization connect) and completions (via complete) to proceed in is shown in Figure 4. In this scheme, the Connector parallel within each thread of control. combines the connection initiation and service initialization phases into a single blocking operation. In this scenario, only one connection is established for every invocation of 8 Implementation connect in each thread of control. The collaborations among the participants in the asyn- This section explains the steps involved in building commu- chronous Connector can be divided into the following nication software applications using the Acceptor-Connector three phases: pattern. The implementation in this section is based on the reusable components and applications in the ACE OO net- 1. Connection initiation phase: To asynchronously ini- work programming toolkit [6]. ACE provides a rich set of tiate a connection between a Service Handler and reusable C++ wrappers and framework components that per- its remote Peer, an application calls the Connector’s form common communication software tasks across a range connect method. As with the synchronous scenario, the of OS platforms. Connector actively establishes the connection. How- The participants in the Acceptor-Connector pattern are di- ever, it does not block the thread of control of the caller vided into the Reactive, Connection,andApplication layers, while the connection completes asynchronously. Instead, as shown in Figure 6. 2 it registers the Service Handler’s transport endpoint, The Reactive and Connection layers perform generic, which we call peer stream in this example, with the application-independent strategies for dispatching events Dispatcher and returns control to its caller. and initializing services, respectively. The Application layer instantiates these generic strategies by providing concrete 2. Service initialization phase: After the connection com- pletes asynchronously, the Dispatcher calls back the 2 This diagram illustrates additional Booch notation: directed edges in- Connector’s complete method. This method activates dicate inheritance relationships between classes; a dashed directed edge in- Service Handler open dicates template instantiation; and a solid circle illustrates a composition the by calling its hook. This relationship between two classes. open hook performs service-specific initialization.
5 CONNECTOR SERVICE HANDLER ACCEPTOR ROLE ROLE ROLE
Concrete_Service_Handler Concrete_Service_Handler n SOCK Stream 1 SOCK_Acceptor 1 SOCK_Connector Concrete Concrete Service Concrete Acceptor LAYER LAYER Connector Handler APPLICATION APPLICATION
SERVICE_HANDLER SERVICE_HANDLER PEER_ACCEPTOR A PEER_CONNECTOR A ACTIVATES Acceptor Connector PEER_STREAM make_service_handler() connect_service_handler() Service accept_service_handler() activate_service_handler() ACTIVATES Handler activate_service_handler() complete() open() connect(sh, addr) open() A accept() LAYER LAYER CONNECTION CONNECTION connect_service_handler 1: (sh, addr); sh = make_service_handler(); accept_service_handler (sh); activate_service_handler activate_service_handler (sh); 2: (sh);
Event Initiation Handler n 1 Dispatcher LAYER LAYER REACTIVE REACTIVE Figure 6: Layering and Partitioning of Participants in the Acceptor-Connector Pattern Implementation classes that establish connections and perform service pro- Initiation Dispatcher: Defines an interface for regis- cessing. This separation of concerns increases the reusabil- tering, removing, and dispatching Event Handlers. ity, portability, and extensibility in the implementation of the The Synchronous Event Demultiplexer,suchas Acceptor-Connector pattern. select [8] or WaitForMultipleObjects [9], in- The following discussion of the Acceptor-Connector pat- forms the Initiation Dispatcher when to call back tern implementation starts at the bottom with the Reactive application-specific event handlers in response to certain layer and works upwards through the Connection layer and types of events. Common events include connection ac- Application layer. ceptance events, data input and output events, and timeout events. Note that the Initiation Dispatcher is an imple- 8.1 Reactive Layer mentation of the Dispatcher participant described in Sec- Dispatcher The Reactive layer handles events that occur on transport tion 6. In general, the Acceptor-Connector endpoints represented by I/O handles, such as socket end- participant can be reactive, proactive, or multi-threaded. The Initiation Dispatcher points. The two participants in this layer, the Initiation particular in this implementa- Dispatcher and Event Handler, are defined by the tion uses the reactive model to demultiplex and dispatch con- Reactor pattern [3]. This pattern enables efficient demul- crete event handlers in a single thread of control. In our ex- Initiation Dispatcher tiplexing of multiple types of events from multiple sources ample the is a Singleton [10] within a single thread of control. since we only need one instance of it for the entire process. The two main roles in the Reactive layer are: 8.2 Connection Layer Event Handler: Specifies an interface consisting of hook methods [7] that abstractly represent the event processing op- The Connection layer erations that can be provided by an application. For instance, 1. Creates Service Handlers; these hook methods signify events such as a new connec- tion request, a completion of a connection request started 2. Passively or actively connects Service Handlers asynchronously, or the arrival of data from a connected peer. to their remote peers; and The Acceptor and Connector components are concrete 3. Activates Service Handlers once they are con- event handlers that derive from Event Handler. nected.
6 All behavior in this layer is completely generic. In particu- lar, note how the classes in the implementation described be- // Initialization method. Connector (void); low delegate to concrete IPC mechanisms and Concrete Service Handlers, which are instantiated by the Appli- // Actively connecting and activate a service. int connect (SERVICE_HANDLER *sh, cation layer described in Section 8.3. const PEER_CONNECTOR::PEER_ADDR &addr, The manner in which the Application layer delegates Connect_Mode mode); to the Connection layer is similar to how the Connection protected: layer delegates to the Reactive layer. For instance, the // Defines the active connection strategy. Initiation Dispatcher in the Reactive layer pro- virtual int connect_service_handler (SERVICE_HANDLER *sh, cesses initialization-related events, such as establishing con- const PEER_CONNECTOR::PEER_ADDR &addr, nections asynchronously, on behalf of the Connection layer. Connect_Mode mode); There are three primary roles in the Connection layer: // Register the SERVICE_HANDLER so that it can Service Handler, Acceptor,andConnector. // be activated when the connection completes. int register_handler (SERVICE_HANDLER *sh, Service Handler: This abstract class inherits from Connect_Mode mode); Event Handler and provides a generic interface for pro- // Defines the handler’s concurrency strategy. cessing services provided by clients, servers, or components virtual int activate_service_handler that perform both roles. Applications must customize this (SERVICE_HANDLER *sh); class via inheritance to perform a particular type of service. // Activate a SERVICE_HANDLER whose The Service Handler interface is shown below: // non-blocking connection completed. virtual int complete (HANDLE handle); // PEER_STREAM is the type of the // Concrete IPC mechanism. private: template
7 prove run-time efficiency since template instantiation occurs If the value of the Connect Mode parameter is SYNC the at compile-time. SERVICE HANDLER will be activated once the connection An even more dynamic type of decoupling could be completes synchronously, as illustrated in Figure 7. This achieved via inheritance and polymorphism by using the figure is similar to Figure 4, but provides additional imple- Factory Method and Strategy patterns described in [10]. For mentation details, such as the use of the get handle and instance, a Connector could store a pointer to a PEER handle event hook methods. CONNECTOR base class. The connect method of this PEER CONNECTOR could be dynamically bound at run- sh: con : : SOCK & Client Service : Initiation time in accordance with the subclass of PEER CONNECTOR Connector Connector returned from a Factory. In general, the tradeoff between Handler Dispatcher parameterized types and dynamic binding is that parameter- FOREACH CONNECTION connect(sh, addr, SYNC) INITIATE CONNECTION connect_service_handler(sh, addr) ized types can incur additional compile/link-time overhead, INITIATION & INITIATION SYNC CONNECT connect() whereas dynamic binding can incur additional run-time over- PHASE activate_service_handler(sh) INITIALIZATION ACTIVATE OBJECT INITIALIZATION head. open() The connect method is the entry point an application INSERT IN REACTOR register_handler(sh) SEVICE EXTRACT HANDLE get_handle() uses to initiate a connection via a Connector. It’s imple- CONNECTION handle_events() mentation is shown below. 3 START EVENT LOOP select() FOREACH EVENT DO handle_event() template
PROCESSING // Store the SERVICE_HANDLER in the map of // pending connections. handler_map_.bind Figure 8: Collaborations Among the Connector Participants (connector_.get_handle (), service_handler); } for Asynchronous Connections } else if (mode == SYNC) // Activate if we connect synchronously. vides additional details that correspond to the current imple- activate_service_handler (service_handler); mentation. } Once instantiated, the PEER CONNECTOR class pro-
3 To save space, most of the error handling in this paper has been omitted. vides the concrete IPC mechanism to initiate connections
8 synchronously or asynchronously. The implementation Connector
// Remove handle from the map. // Invoked when connection requests arrive. handler_map_.unbind (handle); virtual int handle_event (HANDLE, EVENT_TYPE);
// Connection is complete, so activate handler. private: activate_service_handler (service_handler); // IPC mechanism that establishes } // connections passively. PEER_ACCEPTOR peer_acceptor_; The complete method finds and removes the connected }; SERVICE HANDLER from its internal map and trans- // Useful "short-hand" macros used below. fers the I/O HANDLE to the SERVICE HANDLER.Fi- #define SH SERVICE_HANDLER nally, it initializes the SERVICE HANDLER by calling #define PA PEER_ACCEPTOR activate service handler. This method delegates The Acceptor is parameterized by a particular type of to the concurrency strategy designated by the SERVICE PEER ACCEPTOR and SERVICE HANDLER.ThePEER HANDLER’s open hook, as follows: ACCEPTOR provides the transport mechanism used by the template
9 SERVICE HANDLER provides the service that processes Demultiplexer,suchasselect,isthenusedtode- data exchanged with its remote peer. Note that the SERVICE tect and demultiplex incoming connection requests from HANDLER is a concrete service handler that is provided by clients. Since the Acceptor class inherits from Event the application layer. Handler,theInitiation Dispatcher can auto- Parameterized types decouple the Acceptor’s connec- matically call back to the Acceptor’s handle event tion establishment strategy from the type of service handler, method when a connection arrives from a peer. This network programming interface, and transport layer connec- method is an Adapter that transforms the Initiation tion initiation protocol. As with the Connector, the use Dispatcher’s event handling interface to a call to the of parameterized types helps improve portability by allow- Acceptor’s accept method, as follows: ing the wholesale replacement of the mechanisms used by template
10 The SERVICE HANDLER connection acceptance strat- SOCK Stream classes used in Section 9 are part of the egyusedbytheAcceptor is defined below by the ACE C++ socket wrapper library [11]. These wrappers accept service handler method: encapsulate the stream-oriented semantics of connection- template
11 terns to simplify connection establishment and service ini- Handler class processes status data sent to and received tialization. Section 9.1 illustrates how the Peers play a from a Gateway: passive role and Section 9.2 illustrates how the Gateway class Status_Handler : public PEER_HANDLER plays an active role in establishing connections with the pas- { sive Peers. public: // Performs handler activation. virtual int open (void) { // Make handler run in separate thread (note 9.1 Concrete Components for Peers // that Thread::spawn requires a pointer to Figure 9 illustrates how the Concrete Acceptor and // a static method as the thread entry point). Concrete Service Handler components are struc- Thread::spawn (&Status_Handler::service_run, tured in a Peer.TheAcceptor components in this figure this); }
// Static entry point into thread, which blocks Command_Handler // on the handle_event () call in its own thread. SOCK_Stream SOCK_Acceptor static void *service_run (Status_Handler *this_) { Command n 1 Command // This method can block since it Acceptor Handler ACTIVATES // runs in its own thread. while (this_->handle_event () != -1) SOCK_Stream Bulk_Data_Handler SOCK_Acceptor continue; } Bulk Data n ACTIVATES 1 Bulk Data Handler LAYER LAYER Acceptor // Receive and process status data from Gateway. APPLICATION APPLICATION SOCK_Stream Status_Handler virtual int handle_event (void) { Status SOCK_Acceptor char buf[MAX_STATUS_DATA]; n 1 Status stream_.recv (buf, sizeof buf); Handler ACTIVATES Acceptor // ... }
// ... PEER_STREAM SERVICE_HANDLER }; Service PEER_ACCEPTOR Handler LAYER LAYER Acceptor The PEER HANDLER also can be subclassed to produce con- ACCEPTOR ACCEPTOR crete service handlers that process bulk data and commands. Bulk Data Handler Figure 9: Structure of Acceptor Participants for Peers For instance, the class processes bulk data sent to and received from the Gateway. complement the Connector components in Figure 11. class Bulk_Data_Handler : public PEER_HANDLER { public: Service Handlers for Communicating with a Gateway: // Performs handler activation. The classes shown below, Status Handler, Bulk virtual int open (void) { // Handler runs in separate process. Data Handler,andCommand Handler, process rout- if (fork () == 0) // In child process. ing messages sent to and received from a Gateway. // This method can block since it Since these Concrete Service Handler classes in- // runs in its own process. while (handle_event () != -1) herit from Service Handler they are capable of being continue; initialized passively by an Acceptor. // ... To illustrate the flexibility of the Acceptor-Connector pat- } tern, each open routine in the Service Handlers can // Receive and process bulk data from Gateway. implement a different concurrency strategy. In particular, virtual int handle_event (void) { char buf[MAX_BULK_DATA]; when the Status Handler is activated it runs in a sep- stream_.recv (buf, sizeof buf); arate thread; the Bulk Data Handler runs as a sep- // ... arate process; and the Command Handler runs in the } same thread as the Initiation Dispatcher that de- // ... multiplexes connection requests for the Acceptor facto- }; ries. Note how changes to these concurrency strategies do The Command Handler class processes bulk data sent to not affect the implementation of the Acceptor,whichis and received from a Gateway: generic and thus highly flexible and reusable. We start by defining a Service Handler that uses class Command_Handler : public PEER_HANDLER SOCK Stream for socket-based data transfer: { public: typedef Service_Handler
12 } Gateway. When connections arrive, the Initiation Dispatcher calls back to the appropriate Acceptor, // Receive and process command data from Gateway. virtual int handle_event (void) { which creates the appropriate PEER HANDLER to perform char buf[MAX_COMMAND_DATA]; the service, accepts the connection into the handler, and ac- // This method cannot block since it borrows // the thread of control from the tivates the handler. // Initiation_Dispatcher. Figure 10 illustrates the relationship between Concrete stream_.recv (buf, sizeof buf); Acceptor components in the Peer after four connections // ... } have been established with the Gateway shown in Figure 12 and four Service Handler have been created and ac- //... }; tivated. While the Concrete Service Handlers ex-
Acceptors for creating Peer Service Handlers: The : Command s acceptor, bd acceptor,andcacceptor objects : Status : Acceptor : Status shown below are Concrete Acceptor factory instances : Service Status Handlers Bulk Data Handler : Command that create and activate , : Bulk Data : Service Handlers,andCommand Handlers, respectively. Handler : Service : Acceptor Handler
// Accept connection requests from Gateway and : Status // activate Status_Handler. ACTIVE Acceptor
13 }; PEER_ROUTER public: public : { Status_Router class from or to data ser- tus routing subsequent the the instance, For all for vices. basis the forms class This
/... // } Peers. from/to { data (void) status handle_event route int and virtual Receive // } thread. own { its *this_) blocks in (Status_Router which call *service_run thread, handle_event() void into the static point on entry // Static // } { thread. (void) separate open in int router virtual Activate // REACTIVE CONNECTION APPLICATION esatb enn a defining by start We LAYER LAYER LAYER /Ruigtkspaehere... place takes buf); Routing sizeof // (buf, peer_stream_.recv buf[MAX_STATUS_DATA]; char -1) thread. != it own () since its (this_->handle_event block in while can runs method // This // point). entry thread the a as to method (&Status_Router::service_run, pointer static Thread::spawn a requires // Thread::spawn // Command continue; Router Handler Service PEER_ROUTER; SOCK_Stream Bulk Data Router PEER_STREAM hc sgnrcadtu ihyflxbeand flexible highly thus and generic is which , Router Status SOCK_Stream SOCK_Stream n Peers Handler Handler Event Event n this); ttsRouter Status n evc Handler Service ACTIVATES : ACTIVATES ACTIVATES n n 1 Connector 1 Status 1 1 SOCK_Connector Status_Router Connector Bulk Data Dispatcher Dispatcher 1 Initiation Initiation ls otssta- routes class Connector Connector SOCK_Connector Bulk_Data_Router Connector Command PEER_CONNECTOR SERVICE_HANDLER PEER_CONNECTOR SERVICE_HANDLER hti spe- is that SOCK_Connector Command_Router 14 The }; PEER_ROUTER public: public : { Bulk_Data_Router class The asdt the to the passed contrast, In types. priori of type Handler specific a Handler create to tory each that is this single a require SOCK_Connector> Connector
// A vector of PEER_ROUTERs that perform established, the Gateway will route and forward messages // the Gateway’s routing services. sent to it by Peers. vector
// Get vector of Peers to connect with. get_peer_addrs (peers); 10 Known Uses // Iterate through all the Routers and // initiate connections asynchronously. The Acceptor-Connector pattern has been used in a wide for (vector
15 Ericsson EOS Call Center Management System: this Efficiently utilize the inherent parallelism in the network system uses the Acceptor-Connector pattern to allow and hosts: By using the asynchronous mechanisms shown application-level Call Center Manager Event Servers [19] to in Figure 8, the Connector pattern can actively establish con- actively establish connections with passive Supervisors in a nections with a large number of peers efficiently over long- distributed center management system. latency WANs. This is an important property since a large distributed system may have several hundred Peers con- Project Spectrum: The high-speed medical image trans- nected to a single Gateway. One way to connect all these fer subsystem of project Spectrum [20] uses the Acceptor- Peers to the Gateway is to use the synchronous mecha- Connector pattern to passively establish connections and ini- nisms shown in Figure 7. However, the round trip delay for a tialize application services for storing large medical images. 3-way TCP connection handshake over a long-latency WAN Once connections are established, applications then send and (such as a geosynchronous satellite or trans-atlantic fiber ca- receive multi-megabyte medical images to and from these ble) may take several seconds per handshake. In this case, image stores. synchronous connection mechanisms cause unnecessary de- lays since the inherent parallelism of the network and com- ACE Framework: Implementations of the Service puters is underutilized. Handler, Connector,andAcceptor classes described in this paper are provided as reusable components in the ACE object-oriented network programming framework [6]. 11.2 Drawbacks The Acceptor-Connector pattern has the following draw- 11 Consequences backs: Additional indirection: The Acceptor-Connector pattern 11.1 Benefits can incur additional indirection compared with using the un- derlying network programming interfaces directly. However, The Acceptor-Connector pattern provides the following ben- languages that support parameterized types (such as C++, efits: Ada, or Eiffel), can implement these patterns with no signif- icant overhead since compilers can inline the method calls Enhances the reusability, portability, and extensibility of used to implement these patterns. connection-oriented software by decoupling mechanisms for initializing services from subsequent service process- Additional complexity: This pattern may add unneces- ing. For instance, the application-independent mechanisms sary complexity for simple client applications that connect in the Acceptor and Connector are reusable compo- with a single server and perform a single service using a sin- nents that know how to (1) establish connections passively gle network programming interface. and actively, respectively and (2) initialize the associated Service Handler once the connection is established. In contrast, the Service Handler knows how to perform 12 See Also application-specific service processing. This separation of concerns is achieved by decoupling The Acceptor-Connector pattern use the Template Method the initialization strategy from the service handling strategy. and Factory Method patterns [10]. The Acceptor’s Thus, each strategy can evolve independently. The strategy accept and the Connector’s connect and complete for active initialization can be written once, placed into a functions are Template Methods that implements a generic class library or framework, and reused via inheritance, ob- service strategy for connecting to remote peers and initial- ject composition, or template instantiation. Thus, the same izing a Service Handler when the connection is estab- passive initialization code need not be rewritten for each lished. The use of the Template Method pattern allows sub- application. Services, in contrast, may vary according to classes to modify the specific details of creating, connect- different application requirements. By parameterizing the ing, and activating Concrete Service Handlers.The Acceptor and Connector with a Service Handler, Factory Method pattern is used to decouple the creation of a the impact of this variation is localized to a small number of Service Handler from its subsequent use. components in the software. The Acceptor-Connector pattern has an intent similar to the Client-Dispatcher-Server pattern described in [21]. Improves application robustness: Application robust- They both are concerned with separating active connec- ness is improved by strongly decoupling the Service tion establishment from the subsequent service. The pri- Handler from the Acceptor. This decoupling en- mary difference is that the Acceptor-Connector pattern ad- sures that the passive-mode transport endpoint factory dresses passive and active service initialization for both syn- peer acceptor cannot accidentally be used to read or chronous and asynchronous connections, whereas the Client- write data. This eliminates a common class of errors that can Dispatcher-Server pattern focuses on synchronous connec- arise when programming with weakly typed network pro- tion establishment. gramming interfaces such as sockets or TLI [11].
16 Acknowledgements [18] D. C. Schmidt, D. L. Levine, and S. Mungee, “The Design and Performance of Real-Time Object Request Brokers,” Com- Thanks to Frank Buschmann and Hans Rohnert for their puter Communications, vol. 21, pp. 294–324, Apr. 1998. helpful comments on this paper. [19] D. C. Schmidt and T. Suda, “An Object-Oriented Framework for Dynamically Configuring Extensible Distributed Commu- nication Systems,” IEE/BCS Distributed Systems Engineering References Journal (Special Issue on Configurable Distributed Systems), vol. 2, pp. 280–293, December 1994. [1] W. R. Stevens, TCP/IP Illustrated, Volume 1. Reading, Mas- [20] G. Blaine, M. Boyd, and S. Crider, “Project Spectrum: Scal- sachusetts: Addison Wesley, 1993. able Bandwidth for the BJC Health System,” HIMSS, Health
[2] G. Booch, Object Oriented Analysis and Design with Ap- Care Communications, pp. 71–81, 1994. nd
plications (2 Edition). Redwood City, California: Ben- [21] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and jamin/Cummings, 1993. M. Stal, Pattern-Oriented Software Architecture - A System of [3] D. C. Schmidt, “Reactor: An Object Behavioral Pattern for Patterns. Wiley and Sons, 1996. Concurrent Event Demultiplexing and Event Handler Dis- patching,” in Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.), pp. 529–545, Reading, MA: Addison-Wesley, 1995. [4] T. Harrison, I. Pyarali, D. C. Schmidt, and T. Jordan, “Proac-
tor – An Object Behavioral Pattern for Dispatching Asyn- th
chronous Event Handlers,” in The 4 Pattern Languages of Programming Conference (Washington University technical report #WUCS-97-34), September 1997. [5] R. G. Lavender and D. C. Schmidt, “Active Object: an Object Behavioral Pattern for Concurrent Programming,” in Pattern Languages of Program Design (J. O. Coplien, J. Vlissides, and N. Kerth, eds.), Reading, MA: Addison-Wesley, 1996. [6] D. C. Schmidt, “ACE: an Object-Oriented Framework for
Developing Distributed Applications,” in Proceedings of the th
6 USENIX C++ Technical Conference, (Cambridge, Mas- sachusetts), USENIX Association, April 1994. [7] W. Pree, Design Patterns for Object-Oriented Software De- velopment. Reading, MA: Addison-Wesley, 1994. [8] W.R.Stevens,UNIX Network Programming, Second Edition. Englewood Cliffs, NJ: Prentice Hall, 1997. [9] H. Custer, Inside Windows NT. Redmond, Washington: Mi- crosoft Press, 1993. [10] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Pat- terns: Elements of Reusable Object-Oriented Software. Read- ing, MA: Addison-Wesley, 1995. [11] D. C. Schmidt, T. H. Harrison, and E. Al-Shaer, “Object-
Oriented Components for High-speed Network Program- st
ming,” in Proceedings of the 1 Conference on Object- Oriented Technologies and Systems,(Monterey,CA), USENIX, June 1995. [12] D. C. Schmidt, “IPC SAP: An Object-Oriented Interface to Interprocess Communication Services,” C++ Report,vol.4, November/December 1992. [13] W. R. Stevens, UNIX Network Programming, First Edition. Englewood Cliffs, NJ: Prentice Hall, 1990. [14] S. Rago, UNIX System V Network Programming. Reading, MA: Addison-Wesley, 1993. [15] D. L. Presotto and D. M. Ritchie, “Interprocess Communica- tion in the Ninth Edition UNIX System,” UNIX Research Sys- tem Papers, Tenth Edition, vol. 2, no. 8, pp. 523–530, 1990. [16] Object Management Group, The Common Object Request Broker: Architecture and Specification, 2.0 ed., July 1995. [17] D. C. Schmidt and C. Cleeland, “Applying Patterns to De- velop Extensible ORB Middleware,” Submitted to the IEEE Communications Magazine, 1998.
17