<html>
<head>
<base href="https://wiki.asterisk.org/wiki">
<link rel="stylesheet" href="/wiki/s/2033/1/7/_/styles/combined.css?spaceKey=TOP&forWysiwyg=true" type="text/css">
</head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
<h2><a href="https://wiki.asterisk.org/wiki/display/TOP/An+Overview+of+Asterisk+SCF+Component+Interface+Versioning">An Overview of Asterisk SCF Component Interface Versioning</a></h2>
<h4>Page <b>edited</b> by <a href="https://wiki.asterisk.org/wiki/display/~dlee">David M. Lee</a>
</h4>
<div id="versionComment">
<b>Comment:</b>
Hydra -> AsteriskSCF<br />
</div>
<br/>
<h4>Changes (7)</h4>
<div id="page-diffs">
<table class="diff" cellpadding="0" cellspacing="0">
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >{code} <br>// ChannelServiceIf.ice <br></td></tr>
<tr><td class="diff-changed-lines" >module <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">Hydra</span> <span class="diff-added-words"style="background-color: #dfd;">AsteriskSCF</span> <br></td></tr>
<tr><td class="diff-unchanged" >{ <br> module ChannelService <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" > <br>{code} <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> Hydra::ChannelService::ChannelPrx channel = Hydra::ChannelService::ChannelPrx::checkedCast(someObjRef, Hydra::ChannelService::Version); <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> AsteriskSCF::ChannelService::ChannelPrx channel = AsteriskSCF::ChannelService::ChannelPrx::checkedCast(someObjRef, AsteriskSCF::ChannelService::Version); <br></td></tr>
<tr><td class="diff-unchanged" >{code} <br> <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" > <br>{code} <br></td></tr>
<tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;"> Hydra::ChannelService::V1::ChannelPrx channel = Hydra::ChannelService::V1::ChannelPrx::checkedCast(someObjRef, Hydra::ChannelService::V1::Version); <br></td></tr>
<tr><td class="diff-added-lines" style="background-color: #dfd;"> AsteriskSCF::ChannelService::V1::ChannelPrx channel = AsteriskSCF::ChannelService::V1::ChannelPrx::checkedCast(someObjRef, AsteriskSCF::ChannelService::V1::Version); <br></td></tr>
<tr><td class="diff-unchanged" >{code} <br> <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >The facet version of the cast operations is required because of the requirements on the server side for supporting multiple versions. It's worthwhile taking a step back and considering some aspects of object types in distributed systems. The uniqueness and immutability of what a "type" is an important feature of a system. For the purposes of this document a type is a *name* that is associated with a collection of *attributes* and *behaviors*. Basically the rule is "a type must not change once its been deployed". A corollary of the rule is that if the attributes or behaviors of a type has changed then it is no longer the same type! This is basically what versioning is all about. Chaos often ensues in systems that ignore this rule: weird bugs and unexpected exceptions start appearing, often resulting in a component-wise version audit of the entire system. <br> <br></td></tr>
<tr><td class="diff-changed-lines" >Considering this rule, the "suppress" feature may seem a violation. However, as noted earlier the suppress feature only alters the code that the client uses to access the object and make calls. The *type* is untouched. In the above example, the code may be <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">Hydra::ChannelService::ChannelPrx,</span> <span class="diff-added-words"style="background-color: #dfd;">AsteriskSCF::ChannelService::ChannelPrx,</span> but to the Ice type system it is still a <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">Hydra::ChannelService::V1::Channel.</span> <span class="diff-added-words"style="background-color: #dfd;">AsteriskSCF::ChannelService::V1::Channel.</span> This apparent minutiae has important consequences on the server side and is the reason why the facet version of casting is used. This detail is also extremely to the <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">Hydra::ChannelService::V1::SessionInfo</span> <span class="diff-added-words"style="background-color: #dfd;">AsteriskSCF::ChannelService::V1::SessionInfo</span> class as it affects both the server *and* the client, but for the moment let's focus on how facets are employed. <br></td></tr>
<tr><td class="diff-unchanged" > <br>Facets allow multiple types to be accessed through a single object reference. These types are usually related in some sense but are not part of the inheritance tree for the "main" type. _See the Ice documentation for a detailed description._ To see how facets are applied here, let's expand our Slice sample. <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
<tr><td class="diff-unchanged" >{code} <br>// ChannelServiceIf.ice <br></td></tr>
<tr><td class="diff-changed-lines" >module <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">Hydra</span> <span class="diff-added-words"style="background-color: #dfd;">AsteriskSCF</span> <br></td></tr>
<tr><td class="diff-unchanged" >{ <br> module ChannelService <br></td></tr>
<tr><td class="diff-snipped" >...<br></td></tr>
</table>
</div> <h4>Full Content</h4>
<div class="notificationGreySide">
<p>The term <b>versioning</b> can be applied in multiple ways to software. For this document, the focus is on Asterisk SCF's proposed approach for dealing with interface changes as components mature and adapt to new requirements.</p>
<h3><a name="AnOverviewofAsteriskSCFComponentInterfaceVersioning-SomeBackground"></a>Some Background</h3>
<p>Asterisk SCF is a framework for building communications systems from distributed components. These components communicate with each other using an open source object-oriented middleware product from ZeroC Inc called the Internet Communications Engine or "Ice". Like many object-oriented middleware systems, Ice utilizes an interface definition language (IDL) called "Slice". Types and interfaces defined in Slice can be translated into programmatic constructs in any programming language supported by Ice. Applications use the generated code to implement objects that can be accessed remotely as if they were local to the client <em>(Objects based on generated code are also easily accessed locally as well.)</em></p>
<p>Like any system of components, Asterisk SCF will evolve over time. It is inevitable that methods will be added to interfaces and new types introduced. The semantics of methods might also change. To avoid tightly coupling components by version, it will be necessary for components to support old functionality as well as the new. This enables an incremental migration of components that eases support burden, complexity of deployment and risk. </p>
<p>From the component consumer, or client, perspective there are three ways it may want to address new versions of a component:</p>
<ol>
        <li>Ignore any new functionality until the implementation it relies on is no longer supported.</li>
        <li>Employ the new functionality incrementally in order to exploit improvements while avoiding a complete changeover to the new version.</li>
        <li>Upgrade all code to use the new version.</li>
</ol>
<p>The problem is how to go about supporting all of these scenarios.</p>
<h3><a name="AnOverviewofAsteriskSCFComponentInterfaceVersioning-TheSolution"></a>The Solution</h3>
<p><em>This section assumes familiarity with Ice.</em></p>
<p>As all inter-component contracts are defined in Slice, versioning starts here. Fortunately, Ice and some Asterisk SCF-specific extensions provide a very straightforward way to add manageable versioning. The approach involves:</p>
<ul>
        <li>Enclosing all versioned components in a version specific module with the <b>suppress</b> metadata tag.</li>
        <li>Adding a string constant to module to identify the module.</li>
        <li>Translating the Slice to the required implementation language using the "--suppress" command line option. This is only required for the client code.</li>
        <li>The client code uses the specially translated code and employs the standard Ice facet feature when acquiring object references.</li>
</ul>
<p><em>The Slice translators use a C-type preprocessor that supports conditional compilation which can be used to add flexibility of how the suppress functionality is used.</em></p>
<p>The <b>suppress</b> metadata and command line option is an Asterisk SCF-specific extension^1^. It is a mechanism for selectively "hiding" a namespace defined in Slice from part of the generated code. In a client application, it simply looks like the scope associated with the suppressed namespace does not exist. However, all of the internal generated code maintains essential information related to the suppressed namespace. We'll revisit why this is necessary later, for the moment let's look at what this actually looks like.</p>
<p>First, here is some example slice containing a module for the version and the required constant: </p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<script type="syntaxhighlighter" class="toolbar: false; theme: Confluence; brush: java; gutter: false"><![CDATA[
// ChannelServiceIf.ice
module AsteriskSCF
{
module ChannelService
{
["suppress"]
module V1
{
const string Version = "V1";
class SessionInfo
{
};
interface Channel
{
...
};
};
};
};
]]></script>
</div></div>
<p>The translation step would look something like:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<script type="syntaxhighlighter" class="toolbar: false; theme: Confluence; brush: java; gutter: false"><![CDATA[
slice2cpp -I. --suppress ChannelServiceIf.ice
]]></script>
</div></div>
<p>On the client, we employ the facet version of the "cast" calls when obtaining the object reference.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<script type="syntaxhighlighter" class="toolbar: false; theme: Confluence; brush: java; gutter: false"><![CDATA[
AsteriskSCF::ChannelService::ChannelPrx channel = AsteriskSCF::ChannelService::ChannelPrx::checkedCast(someObjRef, AsteriskSCF::ChannelService::Version);
]]></script>
</div></div>
<p>If the --suppress option was not supplied in the translation step, this code would not compile! The following code would be required to do the same thing:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<script type="syntaxhighlighter" class="toolbar: false; theme: Confluence; brush: java; gutter: false"><![CDATA[
AsteriskSCF::ChannelService::V1::ChannelPrx channel = AsteriskSCF::ChannelService::V1::ChannelPrx::checkedCast(someObjRef, AsteriskSCF::ChannelService::V1::Version);
]]></script>
</div></div>
<p>Note the V1's in the namespace scope! This example illustrates quite nicely what the suppress feature does from the client code perspective.</p>
<p>The facet version of the cast operations is required because of the requirements on the server side for supporting multiple versions. It's worthwhile taking a step back and considering some aspects of object types in distributed systems. The uniqueness and immutability of what a "type" is an important feature of a system. For the purposes of this document a type is a <b>name</b> that is associated with a collection of <b>attributes</b> and <b>behaviors</b>. Basically the rule is "a type must not change once its been deployed". A corollary of the rule is that if the attributes or behaviors of a type has changed then it is no longer the same type! This is basically what versioning is all about. Chaos often ensues in systems that ignore this rule: weird bugs and unexpected exceptions start appearing, often resulting in a component-wise version audit of the entire system. </p>
<p>Considering this rule, the "suppress" feature may seem a violation. However, as noted earlier the suppress feature only alters the code that the client uses to access the object and make calls. The <b>type</b> is untouched. In the above example, the code may be AsteriskSCF::ChannelService::ChannelPrx, but to the Ice type system it is still a AsteriskSCF::ChannelService::V1::Channel. This apparent minutiae has important consequences on the server side and is the reason why the facet version of casting is used. This detail is also extremely to the AsteriskSCF::ChannelService::V1::SessionInfo class as it affects both the server <b>and</b> the client, but for the moment let's focus on how facets are employed.</p>
<p>Facets allow multiple types to be accessed through a single object reference. These types are usually related in some sense but are not part of the inheritance tree for the "main" type. <em>See the Ice documentation for a detailed description.</em> To see how facets are applied here, let's expand our Slice sample.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<script type="syntaxhighlighter" class="toolbar: false; theme: Confluence; brush: java; gutter: false"><![CDATA[
// ChannelServiceIf.ice
module AsteriskSCF
{
module ChannelService
{
["suppress"]
module V1
{
const string Version = "V1";
class SessionInfo
{
//...
};
interface Channel
{
// ...
};
};
module V2
{
const string Version = "V2";
class SessionInfo
{
// ...
};
interface Channel
{
// ...
};
};
};
};
]]></script>
</div></div>
<p>This Slice defines two versions of SessionInfo class and Channel interface. You'll notice that the version 2 SessionInfo and Channel do not inherit from the previous versions. Like most examples this is somewhat contrived, but highlights that the importance of using facets: version 2 need not be inherited from version 1! This is an important detail if version 2 alters the signature of an existing method as methods defined in a parent interface cannot be overridden in a child interface.</p>
<p>While the client code might only ever be interested in a single version, the server must deal with all versions. This generally means that the suppress option will not be with server side code. One consequence of this is that the generated code for the server and client will be different, but this is usually not an issue as the client components often take care of their own code generation anyway. </p>
<p>As all supported versions must be implemented on the server side, the developer creates servants for each type and activates them as facets on the relevant object adapter. If there is shared functionality between versions, the developer can easily implement the different servants in terms of each other, a shared class, etc.. The fact that the servants support distinct types does not introduce an obstacle to code sharing and reuse.</p>
<h3><a name="AnOverviewofAsteriskSCFComponentInterfaceVersioning-ImportantNotesonthe%22suppress%22feature"></a>Important Notes on the "suppress" feature</h3>
<p>As seen in the code examples above, the suppress feature removes a namespace from the generated code. It affects other language mappings in different ways depending on how modules are mapped into that language. For example, in Java a module is mapped to a package scope so the suppress feature puts the items defined in the suppressed module in the parent package. </p>
<p>While on the topic of Java and packages for suppressed scopes, an examination of the generated code reveals that the even though the items are moved to the parent package, a package for the suppressed scope is still generated. This is to support object factories for classes that are defined in the suppressed scope. As mentioned earlier in this document, maintaining the actual type for classes defined in Slice is important to both the client and server. This is due to how objects that are returned as out parameters or return values must be constructed by the client's runtime. In Java and C#, Ice uses a package lookup to find a factory class of the required type which then creates an instance of the required object type. In the case of a suppressed module, factory classes are still generated in the respective package to allow the package based factory lookup to function.</p>
<p>It's important to note that object references passed via an Ice method do not contain type information. The appropriate "checked cast" must be performed to check that an object reference is for a specific type. If an object reference is resolved to a specific type using an "unchecked cast", unexpected exceptions or behaviors might occur when it is used to perform invocations on a remote object.</p>
<p>1. The "suppress" extension has been submitted to ZeroC for consideration for inclusion in future releases.</p>
</div>
<div id="commentsSection" class="wiki-content pageSection">
<div style="float: right;">
<a href="https://wiki.asterisk.org/wiki/users/viewnotifications.action" class="grey">Change Notification Preferences</a>
</div>
<a href="https://wiki.asterisk.org/wiki/display/TOP/An+Overview+of+Asterisk+SCF+Component+Interface+Versioning">View Online</a>
|
<a href="https://wiki.asterisk.org/wiki/pages/diffpagesbyversion.action?pageId=3702807&revisedVersion=8&originalVersion=7">View Changes</a>
|
<a href="https://wiki.asterisk.org/wiki/display/TOP/An+Overview+of+Asterisk+SCF+Component+Interface+Versioning?showComments=true&showCommentArea=true#addcomment">Add Comment</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>