Implementing the Networked Logging Service
Sidebar 8: The ACE File Wrapper Facades
ACE encapsulates platform mechanisms for unbuffered file operations in accordance with the Wrapper Facade pattern. Like most of the ACE
wrapper facades families, the ACE File classes decouple the
• Initialization factories, such as which open and/or create from
• Data transfer classes, such as the ACE_FILE_IO, which applica-tions use to read and write data to a file opened by
Connector,
The symmetry of the ACE IPC and file wrapper facades demonstrates the generality of ACE's design and provides the basis for strategizing IPC mechanisms into the higher-level ACE frameworks described in
The Logging Method
This helper method can be used by the hook methods to initialize a log file using the ACE File wrapper facades outlined in Sidebar 8.
(ACE_FILE_IO
char + sizeof
if 0) { // Use client host name as f i l e ACE_INET_Addr
(filename, strcat (filename,
else}
strcpy (filename,
connector;
return
// No time-out.
// Ignored.
0, // try to reuse the addr.
ACE DEFAULT FILE
86 CHAPTER 4 Implementing the Networked Logging Service
We name the log file by default. name can
be overridden by using the host name of the connected client, as we show on page 156 in Section 7.4.
Sidebar 9: The Logging Service Message Framing Protocol
Since TCP is a bytestream protocol, we need an application-level mes-sage framing protocol to delimit log records in the data stream, We use an 8-byte, CDR-encoded header that contains the byte-order indica-tion and payload followed by the log record contents, as shown below:
4.4.2 The Logging Class
This class is used in logging servers to encapsulate the I/O and processing of log records in accordance with the message framing protocol described in Sidebar 9. The sender side of this protocol implementation is shown
in the method on page 96, and the receiver
side is shown in the : recv_log_record method on page 88.
The class definition is placed in a header file called
ttinclude
#include
class // Forward declaration.
Section 4.4 The Initial Logging Server 87
class Logging_Handler {
// Reference to a log file.
// Connected to the client.
// Initialization and termination methods.
Logging_Handler
: log_file_ logging_peer_ {}
(const
: log_file_ {}
int close () { return }
// Receive one log record from a connected client. Returns // length of record on success and contains the // hostname, contains the log record header // (the byte order and the length) and the data. Returns -1 on // failure or connection close.
int recv_log_record
// Write one record to the log file. The contains the // hostname and the <mblk->cont> contains the log record.
// Returns length of record written on success; -1 on int write_log_record
// Log one record by calling and
// Returns 0 on success and -1 on failure.
int log_record
ACE SOCK_Stream () { return logging peer } // Accessor.
Below, we show the implementation of the recv_log_record and log_record methods.
Logging record(). This method implements the receiv-ing side of the networked loggreceiv-ing service's message framreceiv-ing protocol (see Sidebar 9, page 86). It uses the ACE_SOCK_Stream,
and classes along with the extraction
88 CHAPTER 4 Implementing the Networked Logging Service
on page 79 to read one complete log record from a connected client. This code is portable and interoperable since we demarshal the contents re-ceived from the network using the class.
1 int (ACE_Message_Block
11 // Align Message Block for a CDR stream.
12
20 // Use helper method to disambiguate booleans from 21 cdr
30 // Reflect additional
31
32 return length; // Return length of the log record.
33 }
Lines We allocate a new in which to store the host name. We're careful to NUL-terminate the host
Section 4.4 The Initial Logging Server 89
name and to ensure that only the host name is included in the current length of the message block, which is defined as
-Lines We create a separate ACE_Message_Block to hold the log record. The size of the header is known (8 bytes), so we receive that first.
Since we'll use the CDR facility to the header after it's received, we take some precautions with the new message block. CDR's ability to demarshal data requires the marshaled data start on
an alignment boundary. doesn't provide any
alignment guarantees, so we call to force proper alignment before using () to receive the header. The alignment may result in some unused bytes at the start of the block's in-ternal buffer, so we must initially size load larger than the 8 bytes it will receive has a default value of 512).
Lines Following a successful call to recv_n to obtain the fixed-sized header, payload's write pointer is advanced to reflect the addition of 8 bytes to the message block.
Lines 17-25 We create a CDR object to demarshal the 8-byte header lo-cated in the payload message block. The CDR object copies the header, avoiding any alteration of the payload message block as the header is de-marshaled. Since the byte order indicator is a Boolean, it can be extracted regardless of the client's byte order. The byte order of the CDR stream is then set according to the order indicated by the input, and the length of the variable-sized log record payload is extracted.
Lines 27-32 Now that we know the length of the log record the payload message block is resized to hold the complete record. The second call appends the remainder of the log record to the payload message block, immediately following the header. If all goes well, we update the write pointer in payload to reflect the added data, chain payload to
via its continuation field, and return the length of the log
Lines 35-38 Error cases end up here, so we need to release the memory