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 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- [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 // IPC mechanism that establishes class Service_Handler : public Event_Handler // connections actively. { PEER_CONNECTOR connector_; public: // Pure virtual method (defined by a subclass). // Collection that maps HANDLEs virtual int open (void) = 0; // to SERVICE_HANDLER *s. Map_Manager // Accessor method used by Acceptor and handler_map_; // Connector to obtain the underlying stream. PEER_STREAM &peer (void) { // Inherited from the Event_Handler -- will be return peer_stream_; // called back by Eactor when events complete } // asynchronously. virtual int handle_event (HANDLE, EVENT_TYPE); // Return the address that we’re connected to. }; PEER_STREAM::PEER_ADDR &remote_addr (void) { return peer_stream_.remote_addr (); // Useful "short-hand" macros used below. } #define SH SERVICE_HANDLER #define PC PEER_CONNECTOR protected: // Concrete IPC mechanism instance. PEER_STREAM peer_stream_; The Connector is parameterized by a particular type of }; PEER CONNECTOR and SERVICE HANDLER.ThePEER Once the Acceptor or Connector establishes a con- CONNECTOR provides the transport mechanism used by the nection, they call the open hook of a Service Handler. Connector to actively establish the connection, either syn- This pure virtual method must be defined by a Concrete chronously or asynchronously. The SERVICE HANDLER Service Handler subclass, which performs service- provides the service that processes data exchanged with its specific initializations and subsequent processing. connected peer. C++ parameterized types are used to decou- ple (1) the connection establishment strategy from (2) the Connector: This abstract class implements the generic type of service handler, network programming interface, and strategy for actively establishing connections and initializing transport layer connection protocol. Service Handler Connector s. The interface of the Parameterized types are an implementation decision that is shown below: help improve portability. For instance, they allow the // The SERVICE_HANDLER is the type of service. wholesale replacement of the IPC mechanisms used by // The PEER_CONNECTOR is the type of concrete the Connector. This makes the Connector’s connec- // IPC active connection mechanism. template tain different network programming interfaces, e.g., sock- class Connector : public Event_Handler ets but not TLI, or vice versa. For example, the PEER { public: CONNECTOR template argument can be instantiated with ei- enum Connect_Mode { ther a SOCK Connector or a TLI Connector, depend- SYNC, // Initiate connection synchronously. ASYNC // Initiate connection asynchronously. ing on whether the platform supports sockets or TLI [11]. }; Another motivation for using parameterized types is to im-

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 int DATA ARRIVES PHASE Connector::connect SERVICE PROCESS DATA service() (SERVICE_HANDLER *service_handler, PROCESSING const PEER_CONNECTOR::PEER_ADDR &addr, Connect_Mode mode) { Figure 7: Collaborations Among the Connector Participants connect_service_handler (service_handler, for Synchronous Connections addr, mode); } To connect with multiple Peers efficiently, the This method uses the Connector also may need to actively establish connec- [10] to allow Concrete Connectorsto tions asynchronously, i.e., without blocking the caller. Asyn- transparently modify the connection strategy, without chang- chronous behavior is specified by passing the ASYNC con- ing the component interface. Therefore, the connect nection mode to Connector::connect, as illustrated in method delegates to the Connector’s connection strategy, Figure 8. This figure is similar to Figure 5, but it also pro- connect service handler, which initiates a connec- tion as shown below: con : : SOCK sh: Client : Initiation template int Connector Connector Service Connector::connect_service_handler Handler Dispatcher (SERVICE_HANDLER *service_handler, FOREACH CONNECTION connect(sh, addr, ASYNC) const PEER_CONNECTOR::PEER_ADDR &remote_addr, INITIATE CONNECTION Connect_Mode mode) connect_service_handler(sh, addr) ASYNC CONNECT connect() { PHASE register_handler(con) // Delegate to concrete PEER_CONNECTOR PHASE INSERT IN DISPATCHER INITIATION // to establish the connection. CONNECTION handle_events() START EVENT LOOP select() if (connector_.connect (*service_handler, FOREACH EVENT DO handle_event() remote_addr, CONNECTION COMPLETE mode) == -1) { activate_service_handler(sh) if (mode == ASYNC && errno == EWOULDBLOCK) { PHASE ACTIVATE OBJECT open() SERVICE // If connection doesn’t complete immediately register_handler(sh) INSERT IN REACTOR INITIALIZATION // and we are using non-blocking semantics get_handle() // then register this object with the EXTRACT HANDLE // Initiation_Dispatcher Singleton so it will // callback when the connection is complete. handle_event() DATA ARRIVES Initiation_Dispatcher::instance PHASE PROCESS DATA service() ()->register_handler (this, WRITE_MASK); SERVICE

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::activate_service_handler of the Connector pattern shown here uses asynchronous (SERVICE_HANDLER *service_handler) { connection mechanisms provided by the OS and com- service_handler->open (); munication protocol stack. For instance, on UNIX or } Win32 the Connector can set sockets into non-blocking mode and use an event demultiplexer like select or The Service Handler’s open hook is called when WaitForMultipleObjects to determine when the con- a connection is established successfully. Note that it is nection completes. called regardless of whether (1) connections are initiated To handle asynchronous connections that are still pending synchronously or asynchronously or (2) they are connected completion, the Connector maintains a map of Service actively or passively. This uniformity makes it possible to Handlers. Since the Connector inherits from Event write Service Handlers whose processing can be com- Handler,theInitiation Dispatcher can automat- pletely decoupled from how they are connected and initial- ically call back to the Connector’s handle event ized. method when a connection completes. Acceptor: This abstract class implements the generic strat- The handle event method is an Adapter [10] that egy for passively establishing connections and initializing transforms the Initiation Dispatcher’s event han- Service Handlers. The interface of the Acceptor is dling interface to a call to the Connector pattern’s shown below. complete method. // The SERVICE_HANDLER is the type of service. The Connector’s handle event method is shown // The PEER_ACCEPTOR is the type of concrete below: // IPC passive connection mechanism. template int class PEER_ACCEPTOR> Connector::handle_event (HANDLE handle, class Acceptor : public Event_Handler EVENT_TYPE type) { { public: // Adapt the Initiation_Dispatcher’s event // Initialize local_addr transport endpoint factory // handling API to the Connector’s API. // and register with Initiation_Dispatcher Singleton. complete (handle); virtual int open } (const PEER_ACCEPTOR::PEER_ADDR &local_addr); // Factory Method that creates, connects, and The complete method activates the SERVICE HANDLER // activates SERVICE_HANDLER’s. whose non-blocking connection just completed successfully, virtual int accept (void); as follows: protected: // Defines the handler’s creation strategy. virtual SERVICE_HANDLER * template int make_service_handler (void); Connector::complete (HANDLE handle) { // Defines the handler’s connection strategy. SERVICE_HANDLER *service_handler = 0; virtual int accept_service_handler (SERVICE_HANDLER *); // Locate the SERVICE_HANDLER corresponding // to the HANDLE. // Defines the handler’s concurrency strategy. handler_map_.find (handle, service_handler); virtual int activate_service_handler (SERVICE_HANDLER *); // Transfer I/O handle to SERVICE_HANDLER *. service_handler->set_handle (handle); // Demultiplexing hooks inherited from Event_Handler, // which is used by Initiation_Dispatcher for // Remove handle from Initiation_Dispatcher. // callbacks. Initiation_Dispatcher::instance virtual HANDLE get_handle (void) const; ()->remove_handler (handle, WRITE_MASK); virtual int handle_close (void);

// 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 int Acceptor to passively establish the connection. The

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 int the Acceptor. This makes the connection establishment code Acceptor::handle_event (HANDLE, portable across platforms that contain different network pro- EVENT_TYPE) gramming interfaces, such as sockets but not TLI, or vice { // Adapt the Initiation_Dispatcher’s event handling versa. For example, the PEER ACCEPTOR template argu- // API to the Acceptor’s API. ment can be instantiated with either a SOCK Acceptor or accept (); a TLI Acceptor, depending on whether the platform sup- } ports sockets or TLI more efficiently. As shown below, the accept method is a Template The implementation of the Acceptor’s methods is pre- Method [10] that implements the Acceptor-Connector pat- sented below. Applications initiatize an Acceptor by call- tern’s passive initialization strategy for creating a new ing its open method, as follows: SERVICE HANDLER, accepting a connection into it, and template int activating the service: Acceptor::open (const PEER_ACCEPTOR::PEER_ADDR &local_addr) template int { Acceptor::accept (void) // Forward initialization to the PEER_ACCEPTOR. { peer_acceptor_.open (local_addr); // Create a new SERVICE_HANDLER. SH *service_handler = make_service_handler (); // Register with Initiation_Dispatcher, which // ‘‘double-dispatches’’ without get_handle() // Accept connection from client. // method to extract the HANDLE. accept_service_handler (service_handler); Initiation_Dispatcher::instance ()->register_handler (this, READ_MASK); // Activate SERVICE_HANDLER by calling } // its open() hook. activate_service_handler (service_handler); The open method is passed a local addr. This parame- } ter contains a network address, e.g., the local host’s IP name and TCP port number, used to listen for connections. It This method is very concise since it factors all low-level forwards this address to the passive connection acceptance details into the concrete SERVICE HANDLER and PEER mechanism defined by the PEER ACCEPTOR. This mecha- ACCEPTOR, which are instantiated via parameterized types nism initializes the transport endpoint factory, which adver- and can be customized by subclasses of the Acceptor.In tises its address to clients who are interested in connecting particular, since the accept is a Template Method, sub- with the Acceptor. classes can extend any or all of the Acceptor’s connec- The behavior of the transport endpoint factory is deter- tion establishment and initialization strategies. This flexibil- mined by the type of PEER ACCEPTOR instantiated by a ity makes it possible to write Service Handlers whose user. For instance, it can be a C++ wrapper [12] for sockets behavior is decoupled from the manner in which they are [13], TLI [14], STREAM pipes [15], Win32 Named Pipes, passively connected and initialized. etc. The make service handler factory method defines After the transport endpoint factory has been initialized, the default strategy an Acceptor uses to create SERVICE the open method registers itself with the Initiation HANDLERs, as follows: Dispatcher Initiation Dispatcher .The per- template SH * forms a “double dispatch” back to the Acceptor’s Acceptor::make_service_handler (void) get handle method to obtain the underlying transport { return new SH; endpoint factory HANDLE, as follows: } template HANDLE Acceptor::get_handle (void) The default behavior uses a “demand strategy,” which cre- { ates a new SERVICE HANDLER for every new connection. return peer_acceptor_.get_handle (); } However, subclasses of Acceptor can override this strat- egy to create SERVICE HANDLERs using other strategies, The Initiation Dispatcher stores this HANDLE such as creating an individual Singleton [10] or dynamically internally in a table. A Synchronous Event linking the SERVICE HANDLER from a shared library.

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 int oriented protocols like TCP and SPX with efficient, portable, Acceptor::accept_service_handler and type-safe C++ wrappers. (SH *handler) The three main roles in the Application layer are described { peer_acceptor_->accept (handler->peer ()); below. } Concrete Service Handler: This class defines the con- The default behavior delegates to the accept method pro- crete application service activated by a Concrete vided by the PEER ACCEPTOR. Subclasses can override the Acceptor or a Concrete Connector.AConcrete accept service handler method to perform more so- Service Handler is instantiated with a specific type of phisticated behavior such as authenticating the identity of the C++ IPC wrapper that exchanges data with its connected client to determine whether to accept or reject the connec- peer. tion. Concrete Connector: This class instantiates the generic The Acceptor’s SERVICE HANDLER concurrency Connector factory with concrete parameterized type argu- strategy is defined by the activate service handler ments for SERVICE HANDLER and PEER CONNECTOR. method: Concrete Acceptor: This class instantiates the generic template int Acceptor factory with concrete parameterized type argu- Acceptor::activate_service_handler (SH *handler) ments for SERVICE HANDLER and PEER ACCEPTOR. { Concrete Service Handlers can also define a ser- handler->open (); } vice’s concurrency strategy. For example, a Service Handler may inherit from the Event Handler and em- The default behavior of this method is to activate the ploy the Reactor [3] pattern to process data from peers in a SERVICE HANDLER by calling its open hook. This single-thread of control. Conversely, a Service Handler allows the SERVICE HANDLER to choose its own might use the Active Object pattern [5] to process incoming concurrency strategy. For instance, if the SERVICE data in a different thread of control than the one used by the HANDLER inherits from Event Handler it can regis- Acceptor to connect it. Below, we implement Concrete ter with the Initiation Dispatcher. This allows Service Handlers for our Gateway example, illus- the Initiation Dispatcher to dispatch the SERVICE trates how several different concurrency strategies can be HANDLER’s handle event method when events oc- configured flexibly without affecting the structure or behav- cur on its PEER STREAM endpoint of communication. ior of the Acceptor-Connector pattern. Concrete Acceptors can override this strategy to do In the sample code in Section 9, SOCK Connector and more sophisticated concurrency activations. For instance, SOCK Acceptor are the IPC mechanisms used to estab- a subclass could make the SERVICE HANDLER an Active lish connections actively and passively, respectively. Like- Object [5] that processes data using multi-threading or multi- wise, a SOCK Stream is used as the data transport deliv- processing. ery mechanism. However, parameterizing the Connector When an Acceptor terminates, either due to er- and Acceptor with different mechanisms (such as a TLI rors or due to the entire application shutting down, Connector or Named Pipe Acceptor) is straightfor- the Initiation Dispatcher calls the Acceptor’s ward since the IPC mechanisms are encapsulated in C++ handle close method, which can release any dynami- wrapper classes. Likewise, it is easy to vary the data trans- cally acquired resources. In this case, the handle close fer mechanism by parameterizing the Concrete Service method simply forwards the close request to the PEER Handler with a different PEER STREAM,suchasanSVR4 ACCEPTOR’s transport endpoint factory, as follows: UNIX TLI Stream or a Win32 Named Pipe Stream. template int Section 9 illustrates how to instantiate a Concrete Acceptor::handle_close (void) { Service Handler, Concrete Connector,and peer_acceptor_.close (); Concrete Acceptor that implement the Peers and } Gateway described in Section 2. This particular example of the Application layer customizes the generic initializa- 8.3 Application Layer tion strategies provided by the Connector and Acceptor components in the Connection layer. The Application Layer supplies concrete interprocess com- munication (IPC) mechanisms and a concrete Service Handler. IPC mechanisms are encapsulated in C++ 9 Example Resolved classes to simplify programming, enhance reuse, and to en- able wholesale replacement of IPC mechanisms. For ex- The code below illustrates how the Peers and Gateway ample, the SOCK Acceptor, SOCK Connector,and described in Section 2 can use the Acceptor-Connector pat-

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 // Performs handler activation. PEER_HANDLER; virtual int open (void) { // Handler runs in same thread as main // Initiation_Dispatcher singleton. The PEER HANDLER typedef forms the basis for all the Initiation_Dispatcher::instance subsequent service handles. For instance, the Status ()->register_handler (this, READ_MASK);

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 s_acceptor; PASSIVE HANDLERS : Bulk Data : Acceptor LISTENERS // Accept connection requests from Gateway and : Service // activate Bulk_Data_Handler. : Initiation Handler Acceptor bd_acceptor; Dispatcher // Accept connection requests from Gateway and // activate Command_Handler. Acceptor c_acceptor; Figure 10: Object Diagram for Acceptor Components in the Peer Note how the use of templates and dynamic binding per- mits specific details to change flexibly. In particular, no change data with the Gateway,thethreeAcceptors con- Acceptor component changes when the concurrency strat- tinue to listen for new connections in the main thread. egy is modified throughout this section. The reason for this flexibility is that the concurrency strategies have been fac- tored out into the Service Handlers, rather than coupled 9.2 Concrete Components for the Gateway with the Acceptors. Figure 11 illustrates how the Concrete Connector and The Peer main function: The main program initializes the Concrete Service Handler components are struc- Acceptor open concrete factories by calling their hooks tured in a hypothetical configuration of the Gateway.The Acceptor with the TCP ports for each service. Each Connector components in this figure complement the factory automatically registers itself with an instance of Acceptor components in Figure 9. the Initiation Dispatcher in its open method, as shown in Section 8.2. Service Handlers for Gateway routing: The classes // Main program for the Peer. shown below, Status Router, Bulk Data Router, int main (void) and Command Router, route data they receive from { a source Peer to one or more destination Peers. // Initialize acceptors with their // well-known ports. Since these Concrete Service Handler classes in- s_acceptor.open (INET_Addr (STATUS_PORT)); herit from Service Handler they can be actively con- bd_acceptor.open (INET_Addr (BULK_DATA_PORT)); nected and initialized by a Connector. c_acceptor.open (INET_Addr (COMMAND_PORT)); To illustrate the flexibility of the Acceptor-Connector pat- // Event loop that handles connection request tern, each open routine in a Service Handler imple- // events and processes data from the Gateway. ments a different concurrency strategy. In particular, when for (;;) the Status Router is activated it runs in a separate Initiation_Dispatcher::instance thread; the Bulk Data Router runs as a separate pro- ()->handle_events (); } cess; and the Command Router runs in the same thread as the Iniitation Dispatcher that demultiplexes con- Once the Acceptors are initialized, the main pro- nection completion events for the Connector factory. As gram enters an event loop that uses the Initiation with the Acceptor, note how changes to these concur- Dispatcher to detect connection requests from the rency strategies do not affect the implementation of the

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 Service_Handler typedef transfer: data socket-based for cialized reusable. Connector the for Participants Connector of Structure Gateway 11: Figure

/... // } 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> Connectoreitrhnlr(hs READ_MASK); (this, ()->register_handler ERROUTERS PEER PEER_CONNECTOR; continue; typedef hrfr,tecmlt yems eknown be must type complete the Therefore, . ,suchasa : : ukDt Router Data Bulk oceeAcceptor Concrete Connector oceeConnector Concrete . oceeAcceptor Concrete ukDt Handler Data Bulk a esblse opoueconcrete produce to subclassed be can ensa defines oceeSrieHandler Service Concrete : ls otsCmaddt oor to data Command routes class ’s connect Connector oceeAcceptor Concrete oceeService Concrete otsbl aat or to data bulk routes opnns eonly we components, ehdaeinitial- are method h esnfor reason The . sue safac- a as used is atr special- factory ra or Command The a s The Gateway main function: The main program for the Gateway get peer addrs is shown below. The func- : Bulk Data : Command tion creates the Status, Bulk Data,andCommand Routers that route messages through the Gateway.This : Service : Service Handler Handler function (whose implementation is not shown) reads a list of : Status Peer : Status : Status addresses from a configuration file or naming service. : Service : Status Each Peer address consists of an IP address and a port num- Handler : Service : Service Handler : Command ber. Once the Routers are initialized, the Connector Handler : Service Handler factories defined above initiate all the connections asyn- : Service PENDING Handler chronously by passing the ASYNC flag to the connect CONNECTIONS method. ACTIVE HANDLERS : Bulk Data // Main program for the Gateway. : Connector : Service // Obtain an STL vector of Status_Routers, : Initiation Handler // Bulk_Data_Routers, and Command_Routers Dispatcher // from a config file. void get_peer_addrs (vector &peers); int main (void) Figure 12: Object Diagram for Connector Components in the { Gateway // Connection factory for PEER_ROUTERS. PEER_CONNECTOR peer_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 peers;

// 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::iterator i = peers.begin ();range of frameworks, toolkits, and systems: i != peers.end (); i++) { UNIX network superservers: such as inetd [13], PEER_ROUTER &peer = *i; listen [14], and the Service Configurator dae- peer_connector.connect (peer, peer.remote_addr (), mon from the ASX framework [6]. These superservers utilize PEER_CONNECTOR::ASYNC); a master Acceptor process that listens for connections on a } set of communication ports. Each port is associated with a // Loop forever handling connection completion // events and routing data from Peers. communication-related service (such as the standard Internet services ftp, telnet, daytime,andecho). The Accep- for (;;) Initiation_Dispatcher::instance tor process decouples the functionality of the inetd super- ()->handle_events (); server into two separate parts: one for establishing connec- /* NOTREACHED */ tions and another for receiving and processing requests from } peers. When a service request arrives on a monitored port, All connections are invoked asynchronously. They com- the Acceptor process accepts the request and dispatches an plete concurrently via the Connector’s complete appropriate pre-registered handler to perform the service. method, which is called back within the event loop of the Initiation Dispatcher. This event loop CORBA ORBs: The ORB Core layer in many implemen- also demultiplexes and dispatches routing events for tations of CORBA [16] use the Acceptor-Connector to pas- Command Router objects, which run in the Initiation sively initialize server object implementations when clients Dispatcher’s thread of control. The Status Routers request ORB services. [17] describes how the Acceptor- and Bulk Data Routers execute in separate threads and Connector pattern is used to implement The ACE ORB processes, respectively. (TAO) [18], which is a real-time implementation of CORBA. Figure 12 illustrates the relationship between compo- nents in the Gateway after four connections have been WWW Browsers: The HTML parsing components in established with the Peer shown in Figure 10 and four WWW browsers like Netscape and Internet Explorer use the Concrete Service Handlers have been created and asynchronous version of the connector component to es- activated. This diagram illustrates four connections to an- tablish connections with servers associated with images em- other Peer that have not yet completed are “owned” by the bedded in HTML pages. This behavior is particularly impor- Connector.WhenallPeer connections are completely tant so that multiple HTTP connections can be initiated asyn- chronously to avoid blocking the browsers main event loop.

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 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, 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 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, 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