<?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>Interfaces</title>
<para>
The primary purpose of <command>rad</command> is to consistently expose
the various pieces of the system for administration. Not all
subsystems are alike, however: each has a data and state model tuned to
the problems they are solving. Though there are major benefits to
using a common model across components when possible, uniformity comes
with trade-offs. The increased inefficiency and client complexity
— and risk of decreased developer adoption — often warrant
using an interface designed for problem at hand.
</para>
<para>
An interface is a formal definition of how a client may
interact with a <command>rad</command> server object. An
interface may be shared amongst several objects, e.g. when
maintaining a degree of uniformity is possible and useful, or may be
implemented by only one. It is analogous to an interface or pure
abstract class in an object oriented programming language. In the case
of <command>rad</command>, an interface consists of a
name, the set of features a client may interact with, optionally a set
of derived types referenced by the features, and a version. The
features supported include methods (procedure calls made in the context
of a specific object), properties (which are functionally equivalent to
methods but bear different semantics), and asynchronous event sources.
</para>
<section><title>Name</title>
<para>
Each interface has a name. This name is used by the
toolchain to construct indentifier names when generating code, and
is returned by the server along with the rest of the
interface definition when an object is examined by a
client. There isn't a global namespace for
interfaces, however. A client is expected either to
know which objects implement which interfaces (typical
consumer) or to query <command>rad</command> for the object's full
interface definition (debugger or interactive tools).
</para>
<para>
Though in the future <command>rad</command> may establish a
namespace for interfaces, the lack of a namespace
doesn't present a serious problem today. A <trademark
class='trade'>Unix</trademark> executable isn't going to open every
library in <filename class='directory'>/usr/lib</filename> until it
finds one that claims to offer the <quote>libc</quote> interface;
it is going to open <filename
class='libraryfile'>/usr/lib/libc.so.1</filename>. Likewise, the
typical client isn't going to search for objects claiming to export
a particular interface, it will be acting on the
specific object documented to have not just the desired
interface, but the desired behavior as well.
</para>
</section>
<section><title>Derived Types</title>
<para>
Three classes of derived types may be defined for use by features:
<type>structure</type>s, <type>union</type>s, and
<type>enumeration</type>s. Each type defined must be uniquely
named. As with interfaces, there isn't a global type
namespace. The types defined in an <acronym>API</acronym> are only
available to the features defined in that <acronym>API</acronym>.
</para>
</section>
<section><title>Features</title>
<para>
The only thing all three feature types — methods, attributes,
and events — have in common is that they are named. All
three feature types' names exist in the same namespace, and must
therefore be unique. i.e. it is not possible to have both a method
and an attribute called <quote>foo</quote>. This exclusion is
intended as a simple mechanism to avoid the majority of conflicts
that could arise when trying to naturally map these interface
features to a client environment.
</para>
<note>
<para>
Enforcing a common namespace for interface features
isn't always enough. Some language environments place additional
constraints on naming. For instance, a <acronym>JMX</acronym>
client using MXBean proxies will see an interface with
synthetic methods of the form <function>getXXX()</function> or
<function>setXXX()</function> for accessing attribute XXX that must
coexist with other method names — explicitly defining methods
with those names will cause a conflict. Unfortunately, at the
moment, it is up to the developer to be aware of these issues. In
the future such situations may be identified by the toolchain and a
warning issued.
</para>
<para>
One alternative would be to escape method and attribute names in
the <acronym>JMX</acronym> client. By prefixing every method name
with <quote>M</quote> and every property with <quote>P</quote>, for
example, overlaps between the synthetic and real methods would be
eliminated. Each approach has its trade-offs.
</para>
</note>
<section><title>Methods</title>
<para>
A method is a procedure call made in the context of the object it
is called on. In addition to a name, a method
<emphasis>may</emphasis> define a return type, can define
<emphasis>zero or more</emphasis> arguments, and
<emphasis>may</emphasis> declare that it returns an error,
optionally with an error return type.
</para>
<para>
If a method does not define a return type, it returns no value. It
is effectively of type <type>void</type>. If a method defines a
return type, and that type is permitted to be nullable (see
previous chapter), the return value may be defined to be nullable.
</para>
<para>
Each method argument has a name and a type. If any argument's type
is permitted to be nullable (see previous chapter) that argument
may be defined to be nullable.
</para>
<para>
If a method doesn't declare that it returns an error, it
theoretically cannot fail. No system is infallible, though.
Because the connection to <command>rad</command> could be broken
(either due to a network problem or a catastrophic failure in
<command>rad</command> itself), <emphasis>all</emphasis> method
calls can fail with an I/O error. If a method declares that it
returns an error but does not specify a type, the method may fail
due to <acronym>API</acronym>-specific reasons. Clients will
distinguish this failure type from I/O failures. Finally, if a
method also defines an error return type, data of that type may be
provided to the client in the case where the
<acronym>API</acronym>-specific failure occurs. Error payloads are
implicitly optional, and must therefore be of a type that is
permitted to be nullable.
</para>
<para>
Methods names may not be overloaded.
</para>
</section>
<section><title>Attributes</title>
<para>
An attribute is metaphorically a property of the object. In
addition to a name, each attribute has a type, is defined to be one
of read-only, read-write, or write-only, and (like a method)
<emphasis>may</emphasis> declare that accessing the attribute
returns an error, optionally with an error return type.
</para>
<para>
Reading a read-only or read-write attribute returns the value of
that attribute. Writing a write-only or read-write attribute sets
the value of that attribute. It is invalid to read a write-only
attribute or write a read-only attribute. Clients may treat
attempts to write to a read-only attribute as a write to an
attribute that doesn't exist. Likewise, attempts to read from a
write-only attribute may be treated as an attempt to read from an
attribute that doesn't exist.
</para>
<para>
If an attribute's type is permitted to be nullable, its value may
be defined to be nullable.
</para>
<para>
An attribute may optionally declare that it returns an error, with
the same semantics as declaring (or not declaring) an error for a
method. Unlike a method, an attribute may have different error
declarations for reading the attribute and writing the attribute.
</para>
<para>
Attribute names may not be overloaded. To be clear, it is not
valid to define a read-only attribute and a write-only attribute
with the same name.
</para>
<para>
Given methods, attributes are arguably a superfluous
interface feature. Writing an attribute of type
<type>X</type> can be implemented with a method that takes one
argument of type <type>X</type> and returns nothing, and reading an
attribute of type <type>X</type> can be implemented with a method
that takes no arguments and returns a value of type
<type>X</type>. Attributes are included because they have slightly
different semantics, a fact that is recognized by many language
environments. In particular, an explicit attribute mechanism:
<itemizedlist>
<listitem><para>
Enforces symmetric access for reading and writing read-write
attributes.
</para></listitem>
<listitem><para>
Can be easily and automatically translated to a form
natural to the client language-environment.
</para></listitem>
<listitem><para>
Communicates more about the nature of the interaction. Though
it isn't mandatory, it is recommended that reading an attribute
not affect system state. It is also recommended that the value
written to a read-write attribute be the value returned on
subsequent reads (unless there is an intervening change to the
system that effectively <quote>writes</quote> a new value).
</para></listitem>
</itemizedlist>
</para>
</section>
<section><title>Events</title>
<para>
An event is an asynchronous notification generated by rad and
consumed by clients. A client may subscribe to events by name to
register interest in them. The subscription is performed on an
object which implements an interface. In addition to a name, each event
has a type.
</para>
<para>
Events have the following characteristics:
<itemizedlist>
<listitem><para>
Sequential.
</para></listitem>
<listitem><para>
Volatile
</para></listitem>
<listitem><para>
Guaranteed
</para></listitem>
</itemizedlist>
</para>
<para>
A client can rely on sequential delivery of events from a server
as long as the connection to the server is maintained. If the
connection fails, then events will be lost. On re-connection, a
client must re-subscribe to resume the flow of events.
</para>
<para>
Once a client has subscribed to an event, event notifications will
be received until the client unsubscribes from the event.
</para>
<para>
On receipt of a subscribed event, a client receives a payload of
the defined type.
</para>
</section>
</section>
<section><title>Version</title>
<para>
<acronym>API</acronym>s change over time. Sometimes a change to an
<acronym>API</acronym> is incompatible with existing consumers.
Other times a change is compatible with existing consumers, but may
breed a new batch of consumers that are unable to use the
<acronym>API</acronym> that was in place before the change
occurred. Moreover, not all features are created equal. Some
represent committed interfaces whose compatibility is paramount,
others are private interfaces that are changed only in lockstep
with the software that uses them. To address all these issues,
<command>rad</command> interfaces are versioned.
</para>
<section><title>Numbering</title>
<para>
Measuring the compatibility of a change is the first problem that
needs to be solved. <command>rad</command> uses a simple
<replaceable>major</replaceable>.<replaceable>minor</replaceable>
versioning scheme. When a compatible change to an
interface is made, its minor version number is
incremented. When an incompatible change is made, its major
version number is incremented and its minor version number is reset
to 0.
</para>
<para>
In other words, an implementation of an interface that claims to be
version <replaceable>X</replaceable>.<replaceable>Y</replaceable>
(where <replaceable>X</replaceable> is the major version and
<replaceable>Y</replaceable> is the minor version) must support any
client expecting version
<replaceable>X</replaceable>.<replaceable>Z</replaceable>, where
<replaceable>Z</replaceable> <= <replaceable>Y</replaceable>.
</para>
<para>
The following interface changes are considered
compatible:
<itemizedlist>
<listitem><para>
Adding a new event.
</para></listitem>
<listitem><para>
Adding a new method.
</para></listitem>
<listitem><para>
Adding a new attribute.
</para></listitem>
<listitem><para>
Expanding the access supported by an attribute (e.g. from
read-only to read-write).
</para></listitem>
<listitem><para>
A change from nullable to non-nullable for a method
return value or readable property (i.e. decreasing the range
of a feature).
</para></listitem>
<listitem><para>
A change from non-nullable to nullable for a method
argument or writable property (i.e. increasing the domain
of a feature).
</para></listitem>
</itemizedlist>
</para>
<para>
The following interface changes are considered
incompatible:
<itemizedlist>
<listitem><para>
Removing an event.
</para></listitem>
<listitem><para>
Removing a method.
</para></listitem>
<listitem><para>
Removing an attribute.
</para></listitem>
<listitem><para>
Changing the type of an attribute, method, or event.
</para></listitem>
<listitem><para>
Changing a type definition referenced by an attribute, method,
or event.
</para></listitem>
<listitem><para>
Decreasing the access supported by an attribute (e.g. from
read-write to read-only).
</para></listitem>
<listitem><para>
Adding or removing method arguments.
</para></listitem>
<listitem><para>
A change from non-nullable to nullable for a method
return value or readable property (i.e. increasing the range
of a feature).
</para></listitem>
<listitem><para>
A change from nullable to non-nullable for a method
argument or writable property (i.e. decreasing the domain
of a feature).
</para></listitem>
</itemizedlist>
</para>
<note>
<para>
An interface is more than just a set of methods,
attributes, and events. Associated with those features are
well-defined behaviors. If those behaviors change, even if the
structure of the interface remains the same, a change
to the version number may be required to honor the above
guarantee.
</para>
</note>
</section>
<section><title>Commitment</title>
<para>
To solve the problem wherein different features are intended for
different consumers, <command>rad</command> defines three
commitment levels: private, uncommitted, and committed. Each
method, attribute, and event in an interface defines
its commitment level independently. The interface is
assigned a separate version number, as described in the previous
section, per commitment level.
</para>
<para>
Each commitment level is considered a superset of the next
more-committed level. e.g. <quote>private</quote> is a superset of
<quote>uncommitted</quote>. This means then when an uncommitted
(or committed) interface changes, the private version number needs
to be changed as well. By having separate version numbers (instead
of just adding more dots to the existing one), private/uncommitted
consumers aren't broken by compatible changes to
uncommitted/committed interfaces.
</para>
<para>
When a feature changes commitment level, it is treated as if the
feature was removed from the old commitment level and added to the
new one. If a feature becomes less committed, then that implies an
incompatible change for every commitment level that no longer
includes that feature, but no change for every commitment level
that still includes the feature (due to the implicit nesting of
commitment levels). If a feature becomes more committed, then that
implies a compatible change for each commitment level that gained
the feature, and no change for each commitment level that had it
before.
</para>
<para>
In the simple case of an <acronym>API</acronym>containing
interfaces of only a single commitment level, this reduces to
traditional commitment-agnostic major/minor versioning.
</para>
</section>
<section><title>Clients and Versioning</title>
<para>
A <command>rad</command> client can ask for interface
version information (if the protocol in use doesn't automatically
provide it). What the client does with this information is
entirely up to it. It can expose that information directly to
interface consumers, or it can provide <acronym>API</acronym>s
that encapsulate
verifying that the version the client expects is the version the
server is providing.
</para>
<para>
In the future, it is conceivable that changes to the
<command>rad</command> server would result in relaxing what
constitutes an incompatible change. For instance, additional
nullable method arguments or carefully constrained derived type
modifications could both be allowed without impacting consumers.
</para>
</section>
</section>
<!--
<section><title>Example</title>
<para>
<classsynopsis class="class">
<oointerface><interfacename>UserManagement</interfacename></oointerface>
<fieldsynopsis>
<modifier>RO</modifier>
<modifier>nullable</modifier>
<type>UserData[]</type><varname>userlist</varname>
</fieldsynopsis>
<fieldsynopsis>
<modifier>WO</modifier>
<type>password</type><varname>root_password</varname>
</fieldsynopsis>
<fieldsynopsis>
<modifier>RW</modifier>
<type>string</type><varname>default_shell</varname>
</fieldsynopsis>
<methodsynopsis>
<type>void</type>
<methodname>createUser</methodname>
<methodparam>
<type>string</type><parameter>name</parameter>
</methodparam>
<methodparam>
<modifier>nullable</modifier>
<type>string</type><parameter>dataset</parameter>
</methodparam>
<exceptionname>UserEx</exceptionname>
</methodsynopsis>
<fieldsynopsis>
<modifier>Event</modifier>
<type>string</type><varname>newusers</varname>
</fieldsynopsis>
</classsynopsis>
</para>
</section>
-->
</section>