<?xml version='1.0' ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
               "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
<!ENTITY % myvars SYSTEM "../variables.ent">
%myvars;
]>
<sect1 id="apm-design" xreflabel="Package Manager Design">
<title>Package Manager Design</title>


<authorblurb>
<para>By Bryan Quinn</para>
</authorblurb>


<sect2 id="apm-design-essentials">
<title>Essentials</title>


<itemizedlist>
<listitem><para><ulink url="/acs-admin/apm">OpenACS Administrator directory</ulink></para></listitem>

<listitem><para><xref linkend="apm-requirements"/></para></listitem>

<listitem><para><xref linkend="packages"/></para></listitem>

<listitem><para><ulink url="../images/apm.pdf">ER diagram</ulink></para></listitem>

<listitem><para>Tcl API </para>

<itemizedlist>
<listitem><para><ulink url="/api-doc/procs-file-view?path=packages%2facs%2dtcl%2ftcl%2fapm%2dprocs%2etcl">
apm-procs.tcl</ulink></para></listitem>

<listitem><para><ulink url="/api-doc/procs-file-view?path=packages%2facs%2dtcl%2ftcl%2fapm%2dinstall%2dprocs%2etcl">
apm-install-procs.tcl</ulink> (Supports installation of packages)</para></listitem>

<listitem><para><ulink url="/api-doc/procs-file-view?path=packages%2facs%2dtcl%2ftcl%2f20%2dapm%2dload%2dprocs%2etcl">
20-apm-load-procs.tcl</ulink> (Bootstraps APM for server startup)</para></listitem>

<listitem><para><ulink url="/api-doc/procs-file-view?path=packages%2facs%2dtcl%2ftcl%2fapm%2dadmin%2dprocs%2etcl">
apm-admin-procs.tcl</ulink> (Supports APM UI)</para></listitem>
</itemizedlist>
</listitem>

<listitem><para>PL/SQL file </para>

<itemizedlist>
<listitem><para><ulink url="/doc/sql/display-sql?url=apm-create.sql&amp;package_key=acs-kernel">apm-create.sql</ulink></para></listitem>
</itemizedlist>
</listitem>
</itemizedlist>

</sect2>

<sect2 id="apm-design-intro">
<title>Introduction</title>

<para>
In general terms, a <emphasis role="strong">package</emphasis> is a unit of software that
serves a single well-defined purpose. That purpose may be to provide a
service directly to one or more classes of end-user, (e.g., discussion forums
and file storage for community members, user profiling tools for the site
publisher), or it may be to act as a building block for other packages (e.g.,
an application programming interface (API) for storing and querying access
control rules, or an API for scheduling email alerts). Thus, packages fall
into one of two categories: 
</para>

<itemizedlist>
<listitem><para><emphasis role="strong">OpenACS Applications:</emphasis> a &quot;program or group of programs
designed for end users&quot; (the <ulink url="http://www.pcwebopaedia.com/TERM/a/application.html">Webopedia
definition</ulink>); also known as <emphasis>modules</emphasis>, for historical reasons.
Examples of applications include <ulink url="/doc/forums">Forums</ulink> and <ulink url="/doc/news">News</ulink>. 


</para></listitem>

<listitem><para><emphasis role="strong">OpenACS Services:</emphasis> the aforementioned building blocks.
Examples of services include the <ulink url="/doc/acs-content-repository">OpenACS
Content Repository</ulink>, the <ulink url="/doc/acs-templating">OpenACS Templating
System</ulink>, and the <link linkend="kernel-doc">OpenACS Kernel</link>, which includes
APM.</para></listitem>
</itemizedlist>

<para>An installation of the OpenACS includes the OpenACS Kernel, some services that
extend the kernel&#39;s functionality, and some applications intended for
end-users. Packages function as individual pieces of <link linkend="subsites-design">subsites</link>. A subsite can contain multiple
application and service instances that provide the end-user with capabilities
and content customized to the particular subsite.</para>

<para>This architecture supports the growth of collaborative commerce. For
example, Jane User starts a forum focusing on the merits of View Cameras by
creating an instance of the Forum application for her personal subsite on an
OpenACS Installation. Jack User discovers Jane&#39;s forum and includes a link to
it in his subsite. As interest in Jane&#39;s forum grows, she creates a
subsite specializing in providing information about View cameras. This
subsite now includes several package instances beyond Forum; it could
potentially include its own Ecommerce capabilities (ala <ulink url="http://shopping.yahoo.com">Yahoo! Shopping</ulink>). This could include a
knowledge management application that allows users to spread expertise about
view cameras and a portal application that links to reliable camera models
and resellers. Any subsite enabled package that is added to the OpenACS
installation through APM is another potential package instance that can
become part of Jane&#39;s View Camera subsite.</para>

<para>The APM provides an architecture for packaging software, making instances
of that software available to subsites, specifying configuration parameters
for each instance, and managing the creation and release of new packages.</para>

</sect2>

<sect2 id="apm-design-hist-considerations">
<title>Historical Considerations</title>

<para>
Prior to ACS 3.3, all packages were lumped together into one monolithic
distribution without explicit boundaries; the only way to ascertain what
comprised a given package was to look at the top of the corresponding
documentation page, where, by convention, the package developer would specify
where to find: 
</para>

<itemizedlist>
<listitem><para>the data model</para></listitem>

<listitem><para>the Tcl procedures</para></listitem>

<listitem><para>the user-accessible pages</para></listitem>

<listitem><para>the administration pages</para></listitem>
</itemizedlist>

<para>Experience has shown us that this lack of explicit boundaries causes a
number of maintainability problems for pre-3.3 installations:</para>

<orderedlist>
<listitem><para>Package interfaces were not guaranteed to be stable in any formal way, so
a change in the interface of one package would often break dependent packages
(which we would only discover through manual regression testing). In this
context, any of the following could constitute an interface change: 

</para>

<itemizedlist>
<listitem><para>renaming a file or directory that appears in a URL</para></listitem>

<listitem><para>changing what form variables are expected as input by a page</para></listitem>

<listitem><para>changing a procedural abstraction, e.g., a PL/SQL or Java stored
procedure or a Tcl procedure</para></listitem>

<listitem><para>changing a functional abstraction, e.g., a database view or a PL/SQL or
Java stored function</para></listitem>

<listitem><para>changing the data model</para></listitem>
</itemizedlist>

<para>This last point is especially important. In most cases, changing the data
model should <emphasis>not</emphasis> affect dependent packages. Rather, the package
interface should provide a level of abstraction above the data model (as well
as the rest of the package implementation). Then, users of the package can
take advantage of implementation improvements that don&#39;t affect the
interface (e.g., faster performance from intelligent denormalization of the
data model), without having to worry that code outside the package will now
break.</para>


</listitem>

<listitem><para>A typical ACS-backed site only uses a few of the modules included in the
distribution, yet there was no well-understood way to pick only what you
needed when installing the ACS, or even to uninstall what you didn&#39;t
need, post-installation. Unwanted code had to be removed manually. 


</para></listitem>

<listitem><para>Releasing a new version of the ACS was complicated, owing again to the
monolithic nature of the software. Since we released everything in the ACS
together, all threads of ACS development had to converge on a single
deadline, after which we would undertake a focused QA effort whose scale
increased in direct proportion to the expansion of the ACS codebase. 


</para></listitem>

<listitem><para>There was no standard way for developers outside of ArsDigita to extend
the ACS with their own packages. Along the same lines, ArsDigita programmers
working on client projects had no standard way to keep custom development
cleanly separated from ACS code. Consequently, upgrading an already installed
ACS was an error-prone and time-consuming process.</para></listitem>
</orderedlist>

<para>Consistent use of the APM format and tools will go a long way toward
solving the maintainability problems listed above. Moreover, APM is the
substrate that will enable us to establish a central package repository,
where developers will be able publish their
packages for other OpenACS users to download and install.</para>

<para>For a simple illustration of the difference between ACS without APM
(pre-3.3) and ACS with APM (3.3 and beyond), consider a hypothetical ACS
installation that uses only two of the thirty-odd modules available circa ACS
3.2 (say, bboard and e-commerce):</para>



<mediaobject>
  <imageobject>
    <imagedata fileref="images/acs-without-apm-vs-with-apm.png" format="PNG" align="center"/>
  </imageobject>
</mediaobject>



<para>APM itself is part of a package, the <emphasis role="strong">OpenACS Kernel</emphasis>, an OpenACS
service that is the only mandatory component of an OpenACS installation.</para>



</sect2>

<sect2 id="apm-design-competitors">
<title>Competitive Analysis</title>


<para>The OpenACS is a platform for web-based application software, and any software
platform has the potential to develop problems like those described above.
Fortunately, there are many precedents for systematic solutions,
including:</para>

<itemizedlist>
<listitem><para><ulink url="http://www.debian.org/">Debian GNU/Linux</ulink> and the <ulink url="http://www.debian.org/doc/packaging-manuals/packaging.html/">Debian
Packaging manual</ulink></para></listitem>

<listitem><para><ulink url="http://www.freebsd.org/">FreeBSD</ulink> has <ulink url="http://www.freebsd.org/handbook/ports.html">the Ports
collection</ulink></para></listitem>

<listitem><para><ulink url="http://www.redhat.com/">Red Hat Linux</ulink> has <ulink url="http://rpm.redhat.com/">the Red Hat Package Manager (RPM)</ulink></para></listitem>
</itemizedlist>

<para>Borrowing from all of the above, OpenACS 3.3 introduces its own package
management system, the OpenACS Package Manager (APM), which consists of:</para>

<itemizedlist>
<listitem><para><emphasis role="strong">a standard format for APM packages</emphasis> (also called
&quot;OpenACS packages&quot;), including: </para>

<itemizedlist>
<listitem><para>version numbering, independent of any other package and the OpenACS as a
whole</para></listitem>

<listitem><para>specification of the package interface</para></listitem>

<listitem><para>specification of dependencies on other packages (if any)</para></listitem>

<listitem><para>attribution (who wrote it) and ownership (who maintains it)</para></listitem>
</itemizedlist>


</listitem>

<listitem><para><emphasis role="strong">web-based tools for package management:</emphasis> </para>

<itemizedlist>
<listitem><para>obtaining packages from a remote distribution point</para></listitem>

<listitem><para>installing packages, if and only if: </para>

<orderedlist>
<listitem><para>all prerequisite packages are installed</para></listitem>

<listitem><para>no conflicts will be created by the installation</para></listitem>
</orderedlist>
</listitem>

<listitem><para>configuring packages (obsoleting the monolithic OpenACS configuration
file)</para></listitem>

<listitem><para>upgrading packages, without clobbering local modifications</para></listitem>

<listitem><para>uninstalling unwanted packages</para></listitem>
</itemizedlist>


</listitem>

<listitem><para><emphasis role="strong">a registry of installed packages</emphasis>, database-backed and
integrated with filesystem-based version control 


</para></listitem>

<listitem><para><emphasis role="strong">web-based tools for package development:</emphasis> </para>

<itemizedlist>
<listitem><para>creating new packages locally</para></listitem>

<listitem><para>releasing new versions of locally-created packages</para></listitem>
</itemizedlist>
</listitem>
</itemizedlist>

</sect2>

<sect2 id="apm-design-design-tradeoffs">
<title>Design Tradeoffs</title>

<para>
The design chosen for APM was meant to satisfy the following constraints: 
</para>

<itemizedlist>
<listitem><para>The process of authoring a package must be as simple as possible.</para></listitem>

<listitem><para>Strict conventions must be established that provide a set of canonical
locations and names for files and patterns, for OpenACS application
development.</para></listitem>

<listitem><para>The processes of installing, upgrading, and using packages must be
straightforward and accessible through a web-based UI.</para></listitem>

<listitem><para>Package instances must be able to have subsite-specific content available
at an easily configurable URL.</para></listitem>
</itemizedlist>

<para>All of these requirements were met, but at the cost of development
simplicity. As <xref linkend="packages"/> demonstrates, a set of strict directory conventions are
required in order for a package to use APM. This contrasts with the apparent
simplicity available to developers of the OpenACS 3.3 system. However, while the
system has become more complex for developers to build packages, this
complexity is easily managed and is compensated for by additional
capabilities.</para>

<para>For example, to make a new application available to the system, a
developer must:</para>

<orderedlist>
<listitem><para>Create the necessary files to support the data model, Tcl API, and UI
pages.</para></listitem>

<listitem><para>Put the files in the correct locations for APM to be aware of them.</para></listitem>

<listitem><para>Use APM to create a new package and enable it.</para></listitem>

<listitem><para>Use the Site Map facility to create an instance of the package, mount it
on an appropriate URL, and set parameters for that particular instance.</para></listitem>
</orderedlist>

<para>While this is complex, especially to a new OpenACS developer, the
documentation walks the developer through each of these steps. Moreover, from
following these steps, the package can be subsite specific, available to
subsites across the system, and be available for distribution to other OpenACS
installations without doing a monolithic upgrade or reinstall.</para>

</sect2>

<sect2 id="apm-design-api">
<title>API</title>


<para>The APM is composed of systems for accomplishing a set of package-related
tasks. Each of these tasks comprise a feature area that has an API, data
model, and a UI:</para>

<itemizedlist>
<listitem><para>Authoring a Package</para></listitem>

<listitem><para>Maintaining Multiple Versions of a Package</para></listitem>

<listitem><para>Creating Instances of the Package</para></listitem>

<listitem><para>Specifying Configuration Parameters for each Instance</para></listitem>
</itemizedlist>



<para><emphasis role="strong">Authoring a Package</emphasis></para>

<para>Full instructions on how to prepare an OpenACS package are available in <xref linkend="packages"/>. The API here can be invoked manually by a package&#39;s data model
creation script, but need not to be used. This API is part of the <ulink url="/api-doc/plsql-subprogram-one?type=PACKAGE&amp;name=APM">APM PL/SQL
package</ulink>.</para>

 

<programlisting>

-- Informs the APM that this application is available for use.
procedure register_application (
    package_key         in apm_package_types.package_key%TYPE,
    pretty_name         in apm_package_types.pretty_name%TYPE,
    pretty_plural       in apm_package_types.pretty_plural%TYPE,
    package_uri         in apm_package_types.package_uri%TYPE,
    singleton_p         in apm_package_types.singleton_p%TYPE
                                default &#39;f&#39;,
    spec_file_path      in apm_package_types.spec_file_path%TYPE
                                default null,
    spec_file_mtime     in apm_package_types.spec_file_mtime%TYPE
                                default null
);

</programlisting>


<para>The procedure above registers an OpenACS application in the APM. It creates a
new OpenACS object and stores information about the package, such as its name, in
the APM data model. There is an analogous procedure for OpenACS services, called
<computeroutput>apm.register_service</computeroutput>.</para>

<para>To remove an application from the system, there are the calls
<computeroutput>apm.unregister_application</computeroutput> and
<computeroutput>apm.unregister_service</computeroutput>.</para>

 

<programlisting>

-- Remove the application from the system.  
procedure unregister_application (
    package_key     in apm_package_types.package_key%TYPE,
    -- Delete all objects associated with this application.
    cascade_p       in char default &#39;f&#39;  
);

</programlisting>


<para>Use the <computeroutput>cascade_p</computeroutput> only if you want to completely remove the
package from the OpenACS.</para>

<para>In order to determine if a particular package exists in the system, use
the <computeroutput>register_p</computeroutput> predicate. It returns 1 if the specified
<computeroutput>package_key</computeroutput> exists in the system, 0 otherwise.</para>

 

<programlisting>

function register_p (
    package_key     in apm_package_types.package_key%TYPE
) return integer;

</programlisting>


<para><emphasis role="strong">Maintaining Multiple Versions of a Package</emphasis></para>

<para>While the package authoring API provides a means for registering a
package, some information about a package is version dependent. For example,
between versions, the owner of a package, its vendor, its URI, and its
dependency information may change. The API for package versions allows this
information to be specified. All of these APIs are part of the <ulink url="/api-doc/plsql-subprogram-one?type=PACKAGE&amp;name=APM%5fPACKAGE%5fVERSION">
<computeroutput>apm_package_version</computeroutput> PL/SQL package</ulink>.</para>

<para>To create a new package version, use the
<computeroutput>apm_package_version.new</computeroutput> constructor function.</para>

 

<programlisting>

function new (
    version_id          in apm_package_versions.version_id%TYPE
                default null,
    package_key         in apm_package_versions.package_key%TYPE,
    version_name        in apm_package_versions.version_name%TYPE
                                default null,
    version_uri         in apm_package_versions.version_uri%TYPE,
    summary         in apm_package_versions.summary%TYPE,
    description_format      in apm_package_versions.description_format%TYPE,
    description         in apm_package_versions.description%TYPE,
    release_date        in apm_package_versions.release_date%TYPE,
    vendor          in apm_package_versions.vendor%TYPE,
    vendor_uri          in apm_package_versions.vendor_uri%TYPE,
    installed_p         in apm_package_versions.installed_p%TYPE
                                default &#39;f&#39;,
    data_model_loaded_p     in apm_package_versions.data_model_loaded_p%TYPE
                        default &#39;f&#39;
) return apm_package_versions.version_id%TYPE;

</programlisting>


<para>In order to use this function, an existing <computeroutput>package_key</computeroutput> must
be specified. The <computeroutput>version_name</computeroutput> parameter must follow a strict
convention:</para>

<orderedlist>
<listitem><para>A major version number</para></listitem>

<listitem><para>at least one minor version number. Although any number of minor version
numbers may be included, three minor version numbers is sufficient and is the
convention of software developers.</para></listitem>

<listitem><para>One of the following: </para>

<itemizedlist>
<listitem><para>The letter <computeroutput>d</computeroutput>, indicating a development-only version</para></listitem>

<listitem><para>The letter <computeroutput>a</computeroutput>, indicating an alpha release</para></listitem>

<listitem><para>The letter <computeroutput>b</computeroutput>, indicating a beta release</para></listitem>

<listitem><para>No letter at all, indicating a final production release</para></listitem>
</itemizedlist>
</listitem>
</orderedlist>

<para>In addition, the letters <computeroutput>d</computeroutput>, <computeroutput>a</computeroutput>, and
<computeroutput>b</computeroutput> may be followed by another integer, indicating a version
within the release.</para>

<para>For those who like regular expressions:</para>

 

<programlisting>

version_number := ^[0-9]+((\.[0-9]+)+((d|a|b|)[0-9]?)?)$

</programlisting>


<para>So the following is a valid progression for version numbers:</para>

<blockquote><para><computeroutput>0.9d, 0.9d1, 0.9a1, 0.9b1, 0.9b2, 0.9, 1.0, 1.0.1, 1.1b1,
1.1</computeroutput></para></blockquote>

<para>To delete a given version of a package, use the
<computeroutput>apm_package_version.delete</computeroutput> procedure:</para>

 

<programlisting>

procedure delete (
    package_id      in apm_packages.package_id%TYPE  
);

</programlisting>


<para>After creating a version, it is possible to edit the information
associated with it using <computeroutput>apm_package_version.edit</computeroutput>.</para>

 

<programlisting>

function edit (
      new_version_id        in apm_package_versions.version_id%TYPE
                default null,
      version_id        in apm_package_versions.version_id%TYPE,
      version_name      in apm_package_versions.version_name%TYPE
                default null,
      version_uri       in apm_package_versions.version_uri%TYPE,
      summary           in apm_package_versions.summary%TYPE,
      description_format    in apm_package_versions.description_format%TYPE,
      description       in apm_package_versions.description%TYPE,
      release_date      in apm_package_versions.release_date%TYPE,
      vendor            in apm_package_versions.vendor%TYPE,
      vendor_uri        in apm_package_versions.vendor_uri%TYPE,
      installed_p       in apm_package_versions.installed_p%TYPE
                default &#39;f&#39;,
      data_model_loaded_p   in apm_package_versions.data_model_loaded_p%TYPE
                default &#39;f&#39;
) return apm_package_versions.version_id%TYPE;

</programlisting>


<para>Versions can be enabled or disabled. Enabling a version instructs APM to
source the package&#39;s libraries on startup and to make the package
available to the OpenACS.</para>

 

<programlisting>

procedure enable (
    version_id          in apm_package_versions.version_id%TYPE
);

procedure disable (
    version_id          in apm_package_versions.version_id%TYPE  
);

</programlisting>


<para>Files associated with a version can be added and removed. The path is
relative to the <emphasis role="strong">package-root</emphasis> which is
<computeroutput>acs-server-root/packages/package-key</computeroutput>.</para>


<programlisting>
-- Add a file to the indicated version. 
function add_file(
    file_id             in apm_package_files.file_id%TYPE 
                        default null, 
    version_id in       apm_package_versions.version_id%TYPE, 
    path                in apm_package_files.path%TYPE,
    file_type           in apm_package_file_types.file_type_key%TYPE 
) return apm_package_files.file_id%TYPE; 

-- Remove a file from the indicated version.
procedure remove_file( 
    version_id          in apm_package_versions.version_id%TYPE,
    path                in apm_package_files.path%TYPE 
);
</programlisting>

<para>Package versions need to indicate that they provide interfaces for other
software. An interface is an API that other packages can access and utilize.
Interfaces are identified as a URI and a version name, that comply with the
specification of a version name for package URIs.</para>

 

<programlisting>

-- Add an interface provided by this version.
function add_interface(
    interface_id        in apm_package_dependencies.dependency_id%TYPE
                    default null,
    version_id          in apm_package_versions.version_id%TYPE,
    interface_uri       in apm_package_dependencies.service_uri%TYPE,
    interface_version       in apm_package_dependencies.service_version%TYPE
) return apm_package_dependencies.dependency_id%TYPE;

procedure remove_interface(
    interface_id        in apm_package_dependencies.dependency_id%TYPE,
    version_id          in apm_package_versions.version_id%TYPE
);

procedure remove_interface(
    interface_uri       in apm_package_dependencies.service_uri%TYPE,
    interface_version       in apm_package_dependencies.service_version%TYPE,
    version_id          in apm_package_versions.version_id%TYPE
);

</programlisting>


<para>The primary use of interfaces is for other packages to specify required
interfaces, known as dependencies. A package cannot be correctly installed
unless all of its dependencies have been satisfied.</para>

 

<programlisting>

-- Add a requirement for this version.  A requirement is some interface that this
-- version depends on.
function add_dependency(
    requirement_id      in apm_package_dependencies.dependency_id%TYPE
                    default null,
    version_id          in apm_package_versions.version_id%TYPE,
    requirement_uri     in apm_package_dependencies.service_uri%TYPE,
    requirement_version     in apm_package_dependencies.service_version%TYPE
) return apm_package_dependencies.dependency_id%TYPE;

procedure remove_dependency(
    requirement_id      in apm_package_dependencies.dependency_id%TYPE,
    version_id          in apm_package_versions.version_id%TYPE
);

procedure remove_dependency(
    requirement_uri     in apm_package_dependencies.service_uri%TYPE,
    requirement_version     in apm_package_dependencies.service_version%TYPE,
    version_id          in apm_package_versions.version_id%TYPE
);

</programlisting>


<para>As new versions of packages are created, it is necessary to compare their
version names. These two functions assist in that task.</para>

 

<programlisting>

-- Given a version_name (e.g. 3.2a), return
-- something that can be lexicographically sorted.
function sortable_version_name (
    version_name        in apm_package_versions.version_name%TYPE
) return varchar;

-- Given two version names, return 1 if one &gt; two, -1 if two &gt; one, 0 otherwise. 
-- Deprecate?
function compare(
    version_name_one        in apm_package_versions.version_name%TYPE,
    version_name_two        in apm_package_versions.version_name%TYPE
) return integer;

</programlisting>


<para><emphasis role="strong">Creating Instances of a Package</emphasis></para>

<para>Once a package is registered in the system, it is possible to create
instances of it. Each instance can maintain its own content and
parameters.</para>

 

<programlisting>

create or replace package apm_application
as

function new (
    application_id  in acs_objects.object_id%TYPE default null,
    instance_name   in apm_packages.instance_name%TYPE
            default null,
    package_key     in apm_package_types.package_key%TYPE,
    object_type     in acs_objects.object_type%TYPE
               default &#39;apm_application&#39;,
    creation_date   in acs_objects.creation_date%TYPE default sysdate,
    creation_user   in acs_objects.creation_user%TYPE default null,
    creation_ip     in acs_objects.creation_ip%TYPE default null,
    context_id      in acs_objects.context_id%TYPE default null
) return acs_objects.object_id%TYPE;

procedure delete (
    application_id      in acs_objects.object_id%TYPE
);
end apm_application;

</programlisting>


<para>Just creating a package instance is not sufficient for it to be served
from the web server. A corresponding site node must be created for it. As an
example, here is how the <ulink url="/api-doc">OpenACS API Documentation</ulink> service
makes itself available on the OpenACS main site:</para>

 

<programlisting>

declare
    api_doc_id integer;
begin
    api_doc_id := apm_service.new (
      instance_name =&gt; &#39;OpenACS API Browser&#39;,
      package_key =&gt; &#39;acs-api-browser&#39;,
      context_id =&gt; main_site_id
    );

    apm_package.enable(api_doc_id);

    api_doc_id := site_node.new (
      parent_id =&gt; site_node.node_id(&#39;/&#39;),
      name =&gt; &#39;api-doc&#39;,
      directory_p =&gt; &#39;t&#39;,
      pattern_p =&gt; &#39;t&#39;,
      object_id =&gt; api_doc_id
    );

    commit;
end;
/
show errors


</programlisting>


<para><emphasis role="strong">Specifying Configuration Parameters for each Instance</emphasis></para>

<para>A parameter is a setting that can be changed on a package instance basis.
Parameters are registered on each <computeroutput>package_key</computeroutput>, and the values
are associated with each instance. Parameters can have default values and can
be of type &#39;string&#39; or &#39;number.&#39; There is support with this
API for setting a number of minimum and maximum values for each parameter,
but for most instances, the minimum and maximum should be 1. It is useful to
allow or require multiple values for packages that need to store multiple
pieces of information under one parameter. Default values are automatically
set when instances are created, but can be changed for each instance.</para>

<para>All of the functions below are in the <ulink url="/api-doc/plsql-subprogram-one?type=PACKAGE&amp;name=APM">APM PL/SQL
package</ulink>.</para>

 

<programlisting>

-- Indicate to APM that a parameter is available to the system.
function register_parameter (
    parameter_id        in apm_parameters.parameter_id%TYPE 
                default null,
    parameter_name      in apm_parameters.parameter_name%TYPE,
    description         in apm_parameters.description%TYPE
                default null,
    package_key         in apm_parameters.package_key%TYPE,
    datatype            in apm_parameters.datatype%TYPE 
                default &#39;string&#39;,
    default_value       in apm_parameters.default_value%TYPE 
                default null,
    section_name        in apm_parameters.section_name%TYPE
                default null,
    min_n_values        in apm_parameters.min_n_values%TYPE 
                default 1,
    max_n_values        in apm_parameters.max_n_values%TYPE 
                default 1
) return apm_parameters.parameter_id%TYPE;

function update_parameter (
    parameter_id        in apm_parameters.parameter_id%TYPE,
    parameter_name      in apm_parameters.parameter_name%TYPE,
    description         in apm_parameters.description%TYPE
                default null,
    package_key         in apm_parameters.package_key%TYPE,
    datatype            in apm_parameters.datatype%TYPE 
                default &#39;string&#39;,
    default_value       in apm_parameters.default_value%TYPE 
                default null,
    section_name        in apm_parameters.section_name%TYPE
                default null,
    min_n_values        in apm_parameters.min_n_values%TYPE 
                default 1,
    max_n_values        in apm_parameters.max_n_values%TYPE 
                default 1
) return apm_parameters.parameter_name%TYPE;

-- Remove any uses of this parameter.
procedure unregister_parameter (
    parameter_id        in apm_parameters.parameter_id%TYPE 
                default null
);

</programlisting>


<para>The following functions are used to associate values with parameters and
instances:</para>

 

<programlisting>

-- Return the value of this parameter for a specific package and parameter.
function get_value (
    parameter_id        in apm_parameter_values.parameter_id%TYPE,
    package_id          in apm_packages.package_id%TYPE         
) return apm_parameter_values.attr_value%TYPE;

function get_value (
    package_id          in apm_packages.package_id%TYPE,
    parameter_name      in apm_parameters.parameter_name%TYPE
) return apm_parameter_values.attr_value%TYPE;

-- Sets a value for a parameter for a package instance.
procedure set_value (
    parameter_id        in apm_parameter_values.parameter_id%TYPE,
    package_id          in apm_packages.package_id%TYPE,        
    attr_value          in apm_parameter_values.attr_value%TYPE
);

procedure set_value (
    package_id          in apm_packages.package_id%TYPE,
    parameter_name      in apm_parameters.parameter_name%TYPE,
    attr_value          in apm_parameter_values.attr_value%TYPE
);  

</programlisting>


</sect2>

<sect2 id="apm-design-data-model">
<title>Data Model Discussion</title>


<para>The central piece of the data model is the <computeroutput>apm_package_types</computeroutput>
table where each package is registered. When a new application or service is
installed on an OpenACS instance, a corresponding row in this table is inserted
with information about the type of package, e.g. if the <ulink url="/doc/forum">forum package</ulink> is installed on your OpenACS server, a row
in <computeroutput>apm_package_types</computeroutput> will be created, noting that it&#39;s an
application package type.</para>

<para>The <computeroutput>apm_packages</computeroutput> table is used to contain information about
the <emphasis>instances</emphasis> of packages currently created in the system. The
<computeroutput>package_key</computeroutput> column references the <computeroutput>apm_package_types</computeroutput>
table to ensure that no package instance can be created for a type that does
not exist.</para>

<para>The <computeroutput>apm_package_versions</computeroutput> table contains information specific
to a particular version of a package. Several tables reference this one to
provide further information about the particular version:</para>

<itemizedlist>
<listitem><para><computeroutput>apm_package_owners</computeroutput>
 Stores information about the owners of a particular version of a package.


</para></listitem>

<listitem><para><computeroutput>apm_package_files</computeroutput>
 Stores information about the files that are part of a version.


</para></listitem>

<listitem><para><computeroutput>apm_package_dependencies</computeroutput>
 Stores information about what interfaces the package provides and
requires.</para></listitem>
</itemizedlist>

<para>Parameter information is maintained through two tables:</para>

<itemizedlist>
<listitem><para><computeroutput>apm_parameters</computeroutput>
 This table contains the definition of each of the parameters for a package.


</para></listitem>

<listitem><para><computeroutput>apm_parameter_values</computeroutput>
 This table holds all of the values of parameters for specific package
instances.


</para></listitem>
</itemizedlist>

<para>A number of views are available for obtaining information about packages
registered in the APM.</para>

<itemizedlist>
<listitem><para><computeroutput>apm_package_version_info</computeroutput>
 Provides information about all of the versions in the system with
information available from the <computeroutput>apm_package_types</computeroutput> table.


</para></listitem>

<listitem><para><computeroutput>apm_enabled_package_versions</computeroutput>
 A view (subset) of the above table with only enabled versions.


</para></listitem>

<listitem><para><computeroutput>apm_file_info</computeroutput>
 Provides a public interface for querying file information.</para></listitem>
</itemizedlist>



</sect2>

<sect2 id="apm-design-ui">
<title>User Interface</title>


<para>The <ulink url="/acs-admin/apm">APM&#39;s user interface</ulink> is part of the
<ulink url="/acs-admin">OpenACS Administration Service</ulink>. The UI is the primary
point of contact with APM by developers and administrators. It is part of OpenACS
Administration, because only the site-wide administrator should be able to
access it. Thus in order to develop a package, the developer must be granted
site-wide administration.</para>

</sect2>

<sect2 id="apm-design-config">
<title>Configuration/Parameters</title>


<para>APM has two parameters for configuring how it interacts with the UNIX
filesystem, accessible via the <ulink url="/admin/site-map/">Site Map admin</ulink>
page. These parameters need not be changed under most circumstances, but may
need to be tweaked for Windows compatibility.</para>

<itemizedlist>
<listitem><para><emphasis role="strong">GzipExecutableDirectory</emphasis>
 This directory points to where the <computeroutput>gunzip</computeroutput> program can be found
for uncompressing <computeroutput>gzip</computeroutput> archives. This is needed for the
installation of <computeroutput>.apm</computeroutput> files which are simply <computeroutput>gzip</computeroutput>ed
tarballs. Default is <computeroutput>/usr/local/bin</computeroutput> 


</para></listitem>

<listitem><para><emphasis role="strong">InfoFilePermissionsMode</emphasis>
 This sets the default UNIX permissions used when creating files using the
APM. Default is 775.</para></listitem>
</itemizedlist>

</sect2>

<sect2 id="apm-design-future">
<title>Future Improvements/Areas of Likely Change</title>


<para>APM has been in production since OpenACS 3.3, and as of version 4.0 offers a
stable set of features. One major feature planned is integration with the OpenACS
Package Repository for automatic dependency satisfaction. When a user tries
to install a package that depends on other packages, the APM will contact the
package repository, determine what packages depend on it, and offer the user
a chance to download and install them all. This improvement offers value to
end users by facilitating the extension of their OpenACS systems.</para>

<para>Architecturally, minor improvements to the data model and the
specification file are planned to increase modularity. The current
implementation puts all package specification information in a single file.
This approach has certain advantages, such as centralization, but splitting
this information into several files allows for flexible extensions to the APM
architecture over time.</para>

<para>APM packages currently lack provisions to verify security information.
There are plans to add MD5 time stamps and PGP signatures to packages to
enable secure authentication of packages. These steps are necessary for APM
to be usable as a scalable method to distribute packages on multiple
repositories worldwide.</para>

<para>Another anticipated change is to split the APM UI into separate systems
for authoring, maintaining, and installing packages. The current UI presents
all of this functionality in one interface and it can be confusing from a
usability perspective.</para>

</sect2>

<sect2 id="apm-design-authors">
<title>Authors</title>

<itemizedlist>
<listitem><para>System creator: Bryan Quinn, Jon Salz, Michael Yoon, Lars Pind, Todd
Nightingale.</para></listitem>

<listitem><para>System owner: Bryan Quinn</para></listitem>

<listitem><para>Documentation author: Bryan Quinn, building from earlier versions by Jon
Salz, Michael Yoon, and Lars Pind.</para></listitem>
</itemizedlist>

</sect2>

<sect2 id="apm-design-rev-history">
<title>Revision History</title>


 
<informaltable>
<tgroup cols="4">
<tbody>
<row>
<entry><emphasis role="strong">Document Revision #</emphasis></entry>
<entry><emphasis role="strong">Action Taken, Notes</emphasis></entry>
<entry><emphasis role="strong">When?</emphasis></entry>
<entry><emphasis role="strong">By Whom?</emphasis></entry>
</row>

<row>
<entry>0.1</entry>
<entry>Creation</entry>
<entry>9/25/2000</entry>
<entry>Bryan Quinn</entry>
</row>

<row>
<entry>0.8</entry>
<entry>Ready for QA</entry>
<entry>9/29/2000</entry>
<entry>Bryan Quinn</entry>
</row>

<row>
<entry>0.9</entry>
<entry>Edited for ACS 4 Beta release</entry>
<entry>10/02/2000</entry>
<entry>Kai Wu</entry>
</row>

<row>
<entry>1.0</entry>
<entry>Edited for OpenACS 4.5 Beta release</entry>
<entry>03/02/2002</entry>
<entry>Roberto Mello</entry>
</row>
	  
</tbody></tgroup></informaltable>


</sect2>

</sect1>
