--- a/usr/src/doc/rad-dev/c-best-practices.xml Fri Mar 23 12:47:27 2012 -0400
+++ b/usr/src/doc/rad-dev/c-best-practices.xml Fri Mar 23 12:21:59 2012 -0700
@@ -19,6 +19,288 @@
<chapter>
<title>RAD Best Practices</title>
+ <para>
+ This chapter provides guidance when using RAD. The guidance
+ material is grouped around the following topics.
+ </para>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ When to use RAD
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ How to use RAD
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <section id="best-practices.when">
+ <title>When To Use RAD</title>
+ <para>
+ RAD is designed to provide remote administrative interfaces
+ for operating system components/sub-systems. Such interfaces
+ support the distributed administration of systems and greatly
+ increase the abilities of system administrators to support
+ large installations.
+ </para>
+ <para>
+ It is not intended to be a general purpose mechanism for
+ building distributed applications, many alternative
+ facilities (e.g.: RPC, RMI, CORBA, MPI, ...) exist for
+ such applications.
+ </para>
+ </section>
+ <section id="best-practices.how">
+ <title>How To Use RAD</title>
+ <para>
+ This section contains specific guidance on how to use RAD.
+ </para>
+ <section id="best-practices.how.interface">
+ <title>Interface Guidelines</title>
+ <para>
+ Designing a RAD interface requires judgement and the
+ application of domain knowledge.
+ </para>
+ <section id="best-practices.how.who">
+ <title>Target Audience</title>
+ <para>
+ The users of the interface represent the primary
+ goal driving the design. There are two broad
+ categories of consumers:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Administrators
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Developers
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Unfortunately, it's rarely possible to accomodate
+ the desires of these two categories easily within
+ one interface. The first group desire "task-based" APIs
+ which match directly onto well-understood and
+ defined administrative activities. The second
+ group desire detailed, "operation-based" interfaces which
+ may be aggregated to better support unusual or
+ niche administrative activities.
+ </para>
+ <para>
+ For any given sub-system, you can view existing
+ command line utilities (CLIs) and libraries (APIs)
+ as expressions of the RAD APIs which are
+ required. The CLIs represent the "task-based"
+ administrative interfaces and the APIs represent
+ the "operation-based" developer interfaces.
+ </para>
+ <para>
+ The goal in using RAD is to provide interfaces
+ that address the lowest-level objectives of the
+ target audience. If targeting administrators
+ (task-based), this could translate to matching
+ existing CLIs. If targeting developers, this
+ could mean significantly less aggregation of
+ the lower-level APIs.
+ </para>
+ </section>
+ <section id="best-practices.how.constraints">
+ <title>Legacy Constraints</title>
+ <para>
+ Many sub-systems present incomplete interfaces to
+ the world. There are CLIs which contain processing
+ capabilities that are not accessible from an
+ existing API (This is another motivation for
+ providing "task-based" administrative interfaces before
+ introducing more detailed interfaces.).
+ </para>
+ <para>
+ Such constraints must be considered in the RAD API
+ design. It is valid to consider migrating
+ functionionality from the CLI into the API to
+ facilitate the creation of the new interface. It
+ is also valid to consider presenting an interface
+ which wraps the CLI and takes advantage of the
+ existing functionality. It is not valid to simply
+ duplicate the functionality in the new RAD
+ interface. This would introduce redundancy and
+ significantly increase maintenance complexity. One
+ particular area where RAD interface developers
+ need to be careful is to avoid duplication around
+ parameter checking and transformation. This is
+ likely to be a sign that existing CLI
+ functionality should be migrated to an API.
+ </para>
+ <para>
+ RAD modules must be written in C. For some
+ sub-systems, for instance those written in other
+ languages, there is no mechanism for a C module to
+ access API functionalty. In these cases RAD module
+ creators have little choice but to access whatever
+ functionality is available in the CLI or to
+ undertake potentially significant engineering
+ effort to access the existing functionality
+ (re-write existing code in C, embed a language
+ interpreter in their C module, ...).
+ </para>
+ </section>
+ <section id="best-practices.how.conservative">
+ <title>Conservative Design</title>
+ <para>
+ Desiging a RAD interface is very similar to
+ designing a library interface. The same general
+ principles of design apply: be conservative, start
+ small, consider evoloutionary paths, carefully
+ consider committment levels.
+ </para>
+ <para>
+ Once an interface is established, the use of
+ versioning and considered, incremental
+ improvements will flesh out the functionality.
+ </para>
+ </section>
+ </section>
+ <section id="best-practices.how.component">
+ <title>Component Guidelines</title>
+ <para>
+ This section presents specific design advice on the
+ most significant components of a RAD module. Naming is
+ addressed separately in the naming section of this
+ chapter.
+ </para>
+ <section id="best-practices.how.component.api">
+ <title>API</title>
+ <para>
+ APIs represent the primary deliverable of a RAD
+ module. They represent a grouping of interfaces,
+ events, methods and properties which allow a user
+ to interact with a sub-system.
+ </para>
+ <para>
+ When exposing the elements of a sub-system
+ consider carefully how existing functions can be
+ grouped together to form an interface. Imperative
+ languages, such as C, tend to pass structures as
+ the first argument to functions. This provides a
+ clear indicator as to how best to group functions
+ into APIs.
+ </para>
+ </section>
+ <section id="best-practices.how.component.method">
+ <title>Method</title>
+ <para>
+ Methods provide mechanisms for examining and
+ modifying administrative state.
+ </para>
+ <para>
+ Consider grouping together existing native APIs
+ into aggregated RAD functions which allow higher
+ order operations to be exposed.
+ </para>
+ <para>
+ Follow established good practice for RPC style
+ development. RAD is primarily for remote
+ administration and avoiding excessive network load
+ is good practice.
+ </para>
+ </section>
+ <section id="best-practices.how.component.property">
+ <title>Property</title>
+ <para>
+ Do define an <error> element with properties
+ which can be modified.
+ </para>
+ </section>
+ <section id="best-practices.how.component.event">
+ <title>Event</title>
+ <para>
+ The module is responsible for providing a sequence
+ number and it's at the discretion of the module
+ author to decide how this is done. Monotonically
+ increasing sequence numbers are recommended for
+ use, since these will be of most potential use to
+ any clients.
+ </para>
+ <para>
+ Consider providing mechanisms for allowing a
+ client to throttle event generation.
+ </para>
+ <para>
+ Carefully design event payloads to minimize
+ network load.
+ </para>
+ <para>
+ Don't try to replicate the functionality of
+ network monitoring protocols such as SNMP.
+ </para>
+ </section>
+ <section id="best-practices.how.component.proxy">
+ <title>Module location: proxy or slave</title>
+ <para>
+ Judicious use of the is_proxy variable allows a
+ module implementor to control where a module is
+ loaded for execution: in the RAD proxy or a slave
+ process.
+ </para>
+ <para>
+ A module should, by default, be designed to be
+ loaded into a slave process unless the following
+ conditions apply:
+ </para>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Module performs self authentication
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Module is very simple and cannot fail
+ fatally
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
+ <section id="best-practices.how.component.synchronous">
+ <title>Synchronous and Asynchronous Invocation</title>
+ <para>
+ All method invocations in RAD are
+ synchronous. Asynchrnous behaviour can be obtained
+ by adopting a design pattern that relies on the
+ use of events to provide notifications. Refer to
+ the section on this in the modules chapter for
+ more details.
+ </para>
+ </section>
+ <section id="best-practices.how.component.duplication">
+ <title>Duplication</title>
+ <para>
+ Do not duplicate code from existing CLIs.
+ </para>
+ </section>
+ <section id="best-practices.how.component.client">
+ <title>Client Library Support</title>
+ <para>
+ RAD modules are designed to have a language
+ agnostic interface. However, it's possible (and
+ sometimes advisable) to provide additional
+ language support via the delivery of a language
+ specific extension. Such deliverables should be
+ restricted in use and the main reason for their
+ existence is to help improve the "fit" of an
+ interface into a language idiom.
+ </para>
+ </section>
+ </section>
<section id="best-practices.naming">
<title>Naming</title>
@@ -252,9 +534,169 @@
</variablelist>
</section>
+</section>
<section>
<title>Relationships to other agents</title>
</section>
+ <section><title>Interface Design Examples</title>
+
+ <para>
+ Given the tools described so far in this document, it should be
+ clear how they can be combined to construct an interface with a
+ known design. Arriving at that design, however, can be a
+ challenge. There is seldom a single solution for a particular
+ problem, and of the available solutions there may not even be a
+ <quote>best</quote> solution. These examples illustrate the best
+ practices we have described previously.
+ </para>
+ <section><title>User Management Example</title>
+ <para>
+ Object/<acronym>API</acronym> granularity is subjective. Imagine
+ an interface for managing a user. The user has a few modifiable
+ properties:
+ </para>
+
+<table><title>Example User Properties</title>
+<tgroup cols='2'>
+<thead>
+<row>
+<entry>Property</entry>
+<entry>Type</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><property>name</property></entry>
+<entry><type>string</type></entry>
+</row>
+<row>
+<entry><property>shell</property></entry>
+<entry><type>string</type></entry>
+</row>
+<row>
+<entry><property>admin</property></entry>
+<entry><type>boolean</type></entry>
+</row>
+</tbody>
+</tgroup></table>
+
+ <para>
+ The API for managing this user might consist solely of a set of
+ attributes corresponding the the above properties. Alternatively,
+ it could consist of a single attribute that is a
+ <type>structure</type> containing fields that correspond to the
+ properties (possibly more efficient if all properties are usually
+ read or written together). The object implementing this might be
+ named:
+ </para>
+
+<programlisting>
+com.example.users:type=TheOnlyUser
+</programlisting>
+
+ <para>
+ If instead of managing a single user, you need to manage multiple
+ users, you have a couple choices. One option would be to modify
+ the interface to use methods instead of attributes, and to add a
+ "user" argument to the methods. e.g.
+ </para>
+
+<programlisting>
+setUserAttributes(username, attributes) throws UserError
+attributes getUserAttributes(username) throws UserError
+</programlisting>
+
+ <para>
+ Given there isn't a lot you can do to a single user, and there are
+ bunch of other global operations you might want to support (e.g.
+ add a user, delete a user, get a list of users), this is pretty
+ reasonable. We might want to give it a more appropriate name,
+ though:
+ </para>
+
+<programlisting>
+com.example.users:type=UserManagement
+</programlisting>
+
+ <para>
+ Imagine, though, that there were a lot more properties associated
+ with the user (job history, eye color, favorite dishes at area
+ restaurants), and a lot more things you would want to do with a
+ user (send them email, give them a bonus). As the server
+ functionality grows, UserManagement's <acronym>API</acronym> grows
+ increasingly cluttered. It would accumulate a mixture of global
+ operation and per-user operations, and the need for each per-user
+ operation to specify a user to operate on (and specify the errors
+ associated with not finding that user) would start looking
+ redundant.
+ </para>
+
+<programlisting>
+username[] listUsers()
+addUser(username, attributes)
+giveRaise(username, dollars) throws UserError
+fire(username) throws UserError
+sendEmail(username, message) throws UserError
+setUserAttributes(username, attributes) throws UserError
+attributes getUserAttributes(username) throws UserError
+</programlisting>
+
+ <para>
+ A cleaner alternative would be to separate the global operations
+ from the user-specific operations and create two
+ <acronym>API</acronym>s. The UserManagement object would use the
+ global operations <acronym>API</acronym>:
+ </para>
+
+<programlisting>
+username[] listUsers()
+addUser(username, attributes)
+</programlisting>
+
+ <para>
+ … and a separate object for each user would implement the
+ user-specific <acronym>API</acronym>:
+ </para>
+
+<programlisting>
+setAttributes(attributes)
+attributes getAttributes()
+giveRaise(dollars)
+fire()
+sendEmail(message)
+</programlisting>
+
+ <para>
+ (One could argue that <function>fire</function> operates more on
+ the namespace than the user, and should be present in
+ UserManagement where it would need to take a username argument).
+ </para>
+
+ <para>
+ Finally, the different objects would be named such that the
+ different objects could be easily differentiated and be directly
+ accessed by the client:
+ </para>
+
+<programlisting>
+com.example.users:type=UserManagement
+com.example.users:type=User,name=ONeill
+com.example.users:type=User,name=Sheppard
+...
+</programlisting>
+
+ <para>
+ This example also highlights a situation where the
+ <command>rad</command> server may not want to enumerate all objects
+ when a client issues a <literal>LIST</literal> request. Listing
+ all users may not be particularly expensive, but pulling down a
+ list of potentially thousands of objects on every
+ <literal>LIST</literal> call won't benefit the majority of
+ clients.
+ </para>
+ </section>
+
+</section>
</chapter>
--- a/usr/src/doc/rad-dev/c-concepts-design.xml Fri Mar 23 12:47:27 2012 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
-<!--
- PDL HEADER START
-
- Public Documentation License Notice
-
- The contents of this Documentation are subject to the Public
- Documentation License Version 1.01 (the "License"); you may only
- use this Documentation if you comply with the terms of this License.
- A copy of the License is available at
- http://www.opensolaris.org/os/community/documentation/license.
-
- PDL HEADER END
-
- Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
--->
-
-<section><title>Interface Design</title>
-
-<para>
-Given the tools described so far in this chapter, it should be clear
-how they can be combined to construct an interface with a known
-design. Arriving at that design, however, can be a challenge. There
-is seldom a single solution for a particular problem, and of the
-available solutions there may not even be a <quote>best</quote>
-solution. These are some of the factors that should be considered when
-designing an interface for <command>rad</command>.
-</para>
-
- <section><title>Granularity</title>
- <para>
- Object/<acronym>API</acronym> granularity is subjective. Imagine
- an interface for managing a user. The user has a few modifiable
- properties:
- </para>
-
-<table><title>Example User Properties</title>
-<tgroup cols='2'>
-<thead>
-<row>
-<entry>Property</entry>
-<entry>Type</entry>
-</row>
-</thead>
-<tbody>
-<row>
-<entry><property>name</property></entry>
-<entry><type>string</type></entry>
-</row>
-<row>
-<entry><property>shell</property></entry>
-<entry><type>string</type></entry>
-</row>
-<row>
-<entry><property>admin</property></entry>
-<entry><type>boolean</type></entry>
-</row>
-</tbody>
-</tgroup></table>
-
- <para>
- The API for managing this user might consist solely of a set of
- attributes corresponding the the above properties. Alternatively,
- it could consist of a single attribute that is a
- <type>structure</type> containing fields that correspond to the
- properties (possibly more efficient if all properties are usually
- read or written together). The object implementing this might be
- named:
- </para>
-
-<programlisting>
-com.example.users:type=TheOnlyUser
-</programlisting>
-
- <para>
- If instead of managing a single user, you need to manage multiple
- users, you have a couple choices. One option would be to modify
- the interface to use methods instead of attributes, and to add a
- "user" argument to the methods. e.g.
- </para>
-
-<programlisting>
-setUserAttributes(username, attributes) throws UserError
-attributes getUserAttributes(username) throws UserError
-</programlisting>
-
- <para>
- Given there isn't a lot you can do to a single user, and there are
- bunch of other global operations you might want to support (e.g.
- add a user, delete a user, get a list of users), this is pretty
- reasonable. We might want to give it a more appropriate name,
- though:
- </para>
-
-<programlisting>
-com.example.users:type=UserManagement
-</programlisting>
-
- <para>
- Imagine, though, that there were a lot more properties associated
- with the user (job history, eye color, favorite dishes at area
- restaurants), and a lot more things you would want to do with a
- user (send them email, give them a bonus). As the server
- functionality grows, UserManagement's <acronym>API</acronym> grows
- increasingly cluttered. It would accumulate a mixture of global
- operation and per-user operations, and the need for each per-user
- operation to specify a user to operate on (and specify the errors
- associated with not finding that user) would start looking
- redundant.
- </para>
-
-<programlisting>
-username[] listUsers()
-addUser(username, attributes)
-giveRaise(username, dollars) throws UserError
-fire(username) throws UserError
-sendEmail(username, message) throws UserError
-setUserAttributes(username, attributes) throws UserError
-attributes getUserAttributes(username) throws UserError
-</programlisting>
-
- <para>
- A cleaner alternative would be to separate the global operations
- from the user-specific operations and create two
- <acronym>API</acronym>s. The UserManagement object would use the
- global operations <acronym>API</acronym>:
- </para>
-
-<programlisting>
-username[] listUsers()
-addUser(username, attributes)
-</programlisting>
-
- <para>
- … and a separate object for each user would implement the
- user-specific <acronym>API</acronym>:
- </para>
-
-<programlisting>
-setAttributes(attributes)
-attributes getAttributes()
-giveRaise(dollars)
-fire()
-sendEmail(message)
-</programlisting>
-
- <para>
- (One could argue that <function>fire</function> operates more on
- the namespace than the user, and should be present in
- UserManagement where it would need to take a username argument).
- </para>
-
- <para>
- Finally, the different objects would be named such that the
- different objects could be easily differentiated and be directly
- accessed by the client:
- </para>
-
-<programlisting>
-com.example.users:type=UserManagement
-com.example.users:type=User,name=ONeill
-com.example.users:type=User,name=Sheppard
-...
-</programlisting>
-
- <para>
- This example also highlights a situation where the
- <command>rad</command> server may not want to enumerate all objects
- when a client issues a <literal>LIST</literal> request. Listing
- all users may not be particularly expensive, but pulling down a
- list of potentially thousands of objects on every
- <literal>LIST</literal> call won't benefit the majority of
- clients.
- </para>
- </section>
-
-</section>
--- a/usr/src/doc/rad-dev/c-concepts.xml Fri Mar 23 12:47:27 2012 -0400
+++ b/usr/src/doc/rad-dev/c-concepts.xml Fri Mar 23 12:21:59 2012 -0700
@@ -13,7 +13,7 @@
PDL HEADER END
- Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
-->
<chapter xmlns:xi="http://www.w3.org/2001/XInclude"><title>Concepts</title>
@@ -63,8 +63,4 @@
<xi:include href="c-concepts-namespace.xml" />
- <!-- Section: Design / Granularity -->
-
- <xi:include href="c-concepts-design.xml" />
-
</chapter>