PDREP adaptor: list


The repository manager version of the PDREP adaptor stores procdefs as attachments in a list.
 
#include <stdio.h>
#include <stdarg.h>
#include "repmgr.h"
The adaptor_info structure is used to pass adaptor info (duh) back to the config module when it's building an adaptor instance. Here's what it contains:
 
static char *names[] = 
{
   "init",
   "free",
   "info",
   "version",
   "load"
};

XML * PDREP_list_init (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_list_free (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_list_info (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_list_version (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_list_load (WFTK_ADAPTOR * ad, va_list args);

static WFTK_API_FUNC vtab[] = 
{
   PDREP_list_init,
   PDREP_list_free,
   PDREP_list_info,
   PDREP_list_version,
   PDREP_list_load
};

static struct wftk_adaptor_info _PDREP_list_info =
{
   5,
   names,
   vtab
};
Cool. So here's the incredibly complex function which returns a pointer to that:
 
struct wftk_adaptor_info * PDREP_list_get_info ()
{
   return & _PDREP_list_info;
}
Thus concludes the communication with the config module. Now on with the actual implementation of functionality.
 
XML * PDREP_list_init (WFTK_ADAPTOR * ad, va_list args) {
   const char * parms;
   XML * mark;

   parms = xml_attrval (ad->parms, "parm");
   if (!*parms) parms = "";

   if (*parms) {
      mark = repos_defn (ad->session, parms);
      if (!mark) xml_setf (ad->parms, "error", "List '%s' is not defined in the repository.", parms);
      xml_set (ad->parms, "list", parms);
      xml_setf (ad->parms, "spec", "list:%s", parms);
      return NULL;
   }

   mark = xml_search (ad->session, "list", "pdrep-default", "yes");
   if (mark) {
      xml_set (ad->parms, "list", xml_attrval (mark, "id"));
      xml_setf (ad->parms, "spec", "list:%s", xml_attrval (mark, "id"));
   } else {
      xml_set (ad->parms, "list", "_procdefs");
      xml_set (ad->parms, "spec", "list:_procdefs");
   }

   return (XML *) 0;
}
That was exactly analogous to the same code in DSREP_list. In fact, I cut and pasted it whole cloth. Freeing the adaptor, like most adaptors, requires no extra work.
 
XML * PDREP_list_free (WFTK_ADAPTOR * ad, va_list args) { return (XML *) 0; }
Next up is the info call, which builds and returns a little XML telling the caller about the adaptor. If the adaptor itself is NULL, then it just returns info about the installed adaptor handler; otherwise it's free to elaborate on the adaptor instance.
 
XML * PDREP_list_info (WFTK_ADAPTOR * ad, va_list args) {
   XML * info;

   info = xml_create ("info");
   xml_set (info, "type", "pdrep");
   xml_set (info, "name", "list");
   xml_set (info, "ver", "1.0.0");
   xml_set (info, "compiled", __TIME__ " " __DATE__);
   xml_set (info, "author", "Michael Roberts");
   xml_set (info, "contact", "wftk@vivtek.com");
   xml_set (info, "extra_functions", "0");

   return (info);
}
The most crucial functionality of the procdef repository from the point of view of regular workflow is the ability to return a current version and retrieve actual versions. We don't really care about maintenance of procdefs, because if they're kept in the repository, we already will use the repmgr to deal with them, and if they're somewhere else, presumably that other datasource has some sort of maintenance processes. So although back in 2000 I thought this adaptor class would grow, at this point that seems improbable.

March 22, 2004: Well. Turns out I'd really like to be lazier than that when setting up simple repositories -- so I'm writing some more complex logic to find workflow definitions in trickier places, like directly in the repository definition and places like that. It's just too irritating to have to define attachments by hand.

There are two basic points of entry to the procdef adaptor: a version query to retrieve the current version of a procdef, and the retrieval of a specific version. In the "normal" case of a versioned attachment, these are nothing more than simple repository manager calls. If we're doing something different, the logic changes: for one thing, if we're not in a versioned attachment, then there is no version. So we have to return a dummy: "0" seems reasonable, as it gives us the chance to transition to versioning if we want to take a procdef in that direction after an initial simple setup.
 
XML * PDREP_list_version  (WFTK_ADAPTOR * ad, va_list args)
{
   char * id = (char *) 0;
   XML * field;
   XML * value;
   XML * obj;

   xml_set (ad->parms, "error", "");
   if (args) id = va_arg (args, char *);
   if (!id) {
      xml_set (ad->parms, "error", "No ID given.");
      return (XML *) 0;
   }

   obj = repos_get (ad->session, xml_attrval (ad->parms, "list"), id);
   if (obj) {
      field = xml_search (obj, "field", "type", "document");
      if (field) {
         /*repos_attach_getver (ad->session, xml_attrval (ad->parms, "list"), id, xml_attrval (value, "id"));*/
         value = xml_create ("value");
         xml_set (value, "value", *xml_attrval (field, "ver") ? xml_attrval (field, "ver") : "1");
         xml_free (obj);
         return (value);
      }
      xml_free (obj);
   }

   value = xml_create ("value");
   xml_set (value, "value", "0");
   return value;
}
To actually load the procdef, things depend on whether we have a versioned attachment or not. If the version is "0", we know we don't. That makes things simple.
 
XML * PDREP_list_load (WFTK_ADAPTOR * ad, va_list args) {
   char *id = (char *) 0;
   char *ver = (char *) 0;
   XML * ret;
   char * defn;
   XML * obj;

   if (args) id = va_arg (args, char *);
   if (!id) {
      xml_set (ad->parms, "error", "No ID given.");
      return (XML *) 0;
   }
   ver = va_arg (args, char *);
   if (!ver) {
      xml_set (ad->parms, "error", "No version given.");
      return (XML *) 0;
   }

   if (strcmp (ver, "0")) {
      ret = repos_retrieve_load (ad->session, xml_attrval(ad->parms, "list"), id, NULL, ver);
      return ret;
   }

   obj = repos_get (ad->session, xml_attrval (ad->parms, "list"), id);
   if (obj) {
      defn = xmlobj_get (obj, NULL, "defn");
      if (defn) {
         ret = xml_parse (defn);
         free (defn);
         xml_free (obj);
         return (ret);
      }
      xml_free (obj);
      xml_setf (ad->parms, "error", "Procdef '%s' has no attachment and no field named 'defn'", id);
      return (xml_create ("workflow"));
   }

   obj = xml_locf (ad->session, ".workflow[%s]", id);
   if (obj) {
      return (xml_copy (obj));
   }

   xml_setf (ad->parms, "error", "Procdef '%s' is not defined.", id);
   return (xml_create ("workflow"));
}

This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2003-2004, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. This presentation was prepared with LPML. Try literate programming. You'll like it.