Lenguaje de definición de Patrones
De MorfeoWiki
Tabla de contenidos |
Introducción
Un «patrón» es una definición de flujo de trabajo. En principio se va a trabajar con una definición próxima al modelado mediante redes de Petri, y por lo tanto un patrón tendrá un conjunto de «lugares» y «transiciones» entre ellos.
Un patrón puede ser «desplegado» en un «contenedor». A partir de ese momento es posible crear «instancias» del patrón.
Una instancia de un patrón puede asociarse a un «recurso REST» (de ahora en adelante, «recurso»), para que se encargue de «gestionar» algún aspecto del recurso.
Definición de un patrón
Catalogación
La definición de todo patrón debe incluir un elemento de descripción. El objetivo es proporcionar información que permita una búsqueda eficiente de patrones dentro de un repositorio.
Se propone utilizar RDF y algún vocabulario como Dublin Core [5] o similar. Por ejemplo:
<pattern-definition …>
…
<rdf:Description rdf:about="http://devel.ule.es/patterns/timer">
<dc:creator>jlgarcia</dc:creator>
<dc:title>A timer with programmed interval</dc:title>
<dc:subject>timer, timing</dc:subject>
<dc:description>
Provides a timer that will reach the 'timed-out' state
in a programmed time interval</dc:description>
<dc:date>2007-01-20</dc:date>
<dc:title xml:lang='es'>Un temporizador programable</dc:title>
</rdf:Description>
…
</pattern-definition>
Modelo de proceso
El modelo de proceso asociado al patrón consta de un conjunto de lugares, transiciones y eventos.
Ejemplo — un patrón «cronógrafo» tiene dos lugares «parado» y «corriendo», y dos transiciones «iniciar» y «detener».
No se establecen, en principio, restricciones sobre el modelo subyacente más allá de que sea representable mediante una red de Petri binaria. Se dotará a la definición de un marcado inicial, aunque es posible que en el momento de crear una instancia del patrón el marcado sea diferente y deba ser obtenido de su entorno.
Cada lugar y transición tienen un nombre que los identifica y una descripción. Las transiciones definen, además, sus lugares de entrada y de salida.
Cada transición tiene asociado un evento implícito que no lleva datos adjuntos. Si fuera necesario, el desarrollador definirá nuevos eventos con datos adjuntos y podrá asociarlos a varias transiciones. El procesamiento de un evento genera una respuesta.
Gramáticas
La definición de un patrón contiene varios elementos que necesitan especificar y manejar tipos de datos XML:
- La propia representación del estado del patrón incluye la parte común a todos los patrones, donde se indica el estado del flujo de trabajo asociado, y una parte específica de cada patrón con sus datos internos.
- Los eventos pueden adjuntar un conjunto de datos.
Se propone utilizar W3C XML Schema [4] para las definiciones de los tipos de datos, ya que está integrado en tecnologías como XQuery y XPath que se utilizarán en los patrones. Las definiciones podrán estar embebidas en la propia descripción del patrón o en documentos externos.
TODO — espacio de nombres - El espacio de nombres para la definición de patrones es «http://smartflow.morfeo-project.org/2006/patterns» y en este documento aparece, por convenio, asociado al prefijo «pl».
Representación
Una instancia de patrón, como recurso REST, tiene una representación XML. Parte de ella es común a todos los patrones:
<pattern-instance … definition=’…’
<workflow>
<marking><place name=’empty’/>…</marking>
<enabled-events><event name=’in(X)’/>…</enabled-events>
</workflow>
<targets><target link=’…’ type=’…’/>…</targets>
<actions><action …>…</action>…</actions>
<configuration><!-- specific content here --></configuration>
<!-- internal data representation -->
</pattern-instance>
La representación de una instancia incluye:
- Referencia a la definición del patrón.
- Lugares marcados en el flujo de trabajo.
- Eventos que la instancia puede recibir.
- Recursos con los que se relaciona la instancia del patrón.
- Acciones insertadas en la instancia.
- Configuración de la instancia.
- Representación propia del tipo de patrón.
El lenguaje W3C Schema proporciona la definición de tipos por extensión, en la que el contenido del tipo derivado es el de la base más nuevo contenido. Se definirán los tipo base pl:patternRepresentationType con la representación común a todos los patrones, que el desarrollador deberá extender en la definición de su patrón, y pl:patternConfigurationType para la configuración.
Datos de eventos
Los datos adjuntos a un tipo de evento deben venir definidos por un fragmento W3C Schema, con un tipo que derive de pl:eventDataType. En esta primera versión del lenguaje de patrones, el tipo de datos adjuntos y los eventos de un patrón se definen en diseño. Quizá sea necesario que al menos el tipo de datos adjuntos pueda especificarse para cada aplicación.
Configuración.
Parámetros que deben ser configurados a lo largo de la vida útil del patrón para que éste funcione correctamente.
Ejemplo — el patrón «cronógrafo» necesita la dirección de un servidor público de tiempo. El fragmento XML de configuración debe incluir un elemento «time-source» de tipo «xs:anyURI».
Lista de recursos objetivo.
Un patrón mantendrá una lista de los recursos que deben ser objeto de las acciones registradas en el patrón. A la hora de ejecutar las acciones, se examinará esta lista para determinar sobre quién hay que ejecutarlas.
Acciones
El primer tipo de acciones que necesita un patrón son aquellas que completan su comportamiento y que siempre deben ejecutarse al cambiar de estado. Por ejemplo, un patrón «do-while» evalúa una condición de salida y si no se cumple debe ejecutar la acción necesaria para iniciar una nueva iteración.
Este tipo de acciones no pueden eliminarse ni modificarse, ya que entonces el patrón dejaría de ser consistente con su definición. Se denominará a este tipo de acciones, «acciones estáticas».
Por otra parte, un patrón es un elemento genérico que debe adaptarse a la gestión de diferentes recursos. Por ejemplo, un patrón «do-while» alcanza un lugar «do» en cada iteración, pero lo que debe hacer en cada caso es diferente. Como elemento de un flujo de trabajo, podría incorporar la creación de una secuencia de items de trabajo. Otra aplicación podría invocar en cada iteración una operación en algún recurso.
Las acciones que adaptan un patrón a cada uso son «acciones dinámicas» y se dividen en dos grupos: «acciones de recurso» y «acciones de instancia». En ambos casos, la acción se dispara para una interacción del patrón con un recurso determinado. En las acciones de recurso, es suficiente que éste sea de un tipo dado. En las de instancia, debe además tratarse de una instancia específica de ese tipo de recurso.
En las acciones tienen un papel preponderante las interacciones con recursos REST y el procesamiento de XML. Para programar una acción se utilizará XQuery [1] y una librería de funciones para completar el lenguaje y facilitar la interacción con recursos REST.
Ejemplo - XQuery — la siguiente consulta combina («join») los datos de varias fuentes XML (catalog.xml, parts.xml y suppliers.xml) para construir un resultado «descriptive-catalog».
<descriptive-catalog>
{
for $i in fn:doc("catalog.xml")/items/item,
$p in fn:doc("parts.xml")/parts/part[partno = $i/partno],
$s in fn:doc("suppliers.xml")/suppliers/supplier[suppno = $i/suppno]
order by $p/description, $s/suppname
return
<item>
{
$p/description,
$s/suppname,
$i/price
}
</item>
}
</descriptive-catalog>
Condiciones de disparo
El desarrollador puede especificar la condición para que se ejecute su acción. En principio podría declararse en términos del tipo de recurso involucrado y del estado del flujo de trabajo del patrón.
Ejemplo (1) — al disparar la transición «iniciar» el cronógrafo debe (siempre) obtener la hora desde un servidor público y guardarla en el estado interno del patrón, en «start-time».
Ejemplo (2) — al disparar la transición «detener» el cronógrafo debe (siempre) obtener la hora, calcular la diferencia con el valor de «start-time» y guardar el resultado en «elapsed-time».
Ejemplo (3) — al alcanzar el lugar «parado» y para el recurso «asignación», hay que copiar el valor de «elapsed-time» en el recurso, bajo «//timing-info/@exec-time».
La condición de disparo se va a limitar a «transición disparada» y «lugar alcanzado». Una opción más flexible pero más compleja es evaluar una expresión contra la representación de la instancia, que como ya se ha visto incluye información no sólo sobre el estado del flujo de trabajo asociado.
Ejemplo — mediante expresiones podría disparase una acción cuando «elapsed-time» sea mayor que un valor dado.
Ejemplo — las acciones de los ejemplos (1) y (2) son estáticas, ya que proporcionan el funcionamiento del patrón.
Ejemplo — la acción (3) sería un caso de acción de recurso.
Protocolo de ejecución
En un momento de su ejecución, un patrón cambia de estado. Ejecutará entonces las acciones internas definidas. A continuación debe calcular la lista de acciones a ejecutar para el nuevo estado y los recursos con los que está asociado.
Cada acción tiene una condición de disparo de la forma [e,t,i] (estado, tipo de recurso, instancia de recurso). Por otra parte, hay una lista de recursos asociados al patrón, con su tipo e instancia.
- Considerar las acciones A1 que cumplan «e» — por ejemplo, transición «start» disparada.
- Seleccionar aquellas que coincidan en «t» e «i» con algún recurso asociado.
- El valor de «i» puede ser «any», para expresar que debe ejecutarse la acción para cualquier instancia de ese tipo de recurso. Así, [«T1»,«any»] se selecciona para ejecutarse si existe al menos un recurso de tipo «T1» asociado.
- La acción [«T1»,«…/ax12»] se selecciona si existe el recurso «…/ax12» asociado al patrón, de tipo «T1».
- Ejecutar las acciones con «i» igual a «any». Las acciones para un tipo de recurso se consideran así más genéricas que las de una instancia particular.
- Para cada acción y recurso asociado concordante, ejecutar la acción.
- Ejecutar las acciones con «i» distinto de «any».
Las acciones con «i» igual a «any» se ejecutarán antes que las específicas de una instancia. Sin embargo, no se garantiza el orden de ejecución dentro de cada grupo.
Contexto de ejecución
El código XQuery de la acción seleccionada se ejecuta en un contexto en el que la plataforma introduce referencias que están así disponibles en el código.
- A la instancia del patrón: «$this».
- Al recurso para el que se ejecuta la acción: «$target» (acciones dinámicas).
- Los datos adjuntos al evento, si los hubiera: «$event».
Especificación de acciones
Dentro de la definición del patrón se dan las acciones estáticas. Las instancias incorporarán además acciones dinámicas. En el esquema para el lenguaje de patrones se definen los tipos pl:internalActionType, pl:resourceActionType y pl:instanceActionType.
<pattern-instance … definition=’…’>
<actions>
<action xsi:type=’pl:resourceActionType’>
<firing-condition place=’done’
resource='http://engine01.example.org/2006/assignment'/>
<body><![CDATA[
(: XQuery code to notify every assignment associated with :)
(: this instance :)
]]>
</body>
</action>
…
</actions>
</pattern-instance>
Codificación en XML
Definición de un patrón
La definición de un patrón es un documento XML, conforme a un esquema W3C.
<pattern-definition xmlns="http://xerxes.local/patterns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://xerxes.local/2006/patterns ../xsd/patterns.xsd">
En primer lugar se incluye información para la catalogación del patrón. Por ahora no se especifica el formato de ésta información, pero podría hacer uso de vocabularios RDF para incluir autor, fecha de creación, etc. También sería interesante dar algún ejemplo de uso y palabras clave.
<description>
<dc:author>Perengano</dc:author>
<dc:date>2006-11-01T23:40:21Z</dc:date>
</description>
A continuación aparecen las definiciones de datos, mediante esquemas W3C. Cada patrón tiene su propia representación interna, que debe ser definida aquí como un tipo derivado de «patternRepresentationType». En el tipo base podrían incluirse ya los elementos para los datos comunes a todos los patrones, como aquellos que recogen los estados marcados o las transiciones habilitadas.
<grammar>
<xs:schema targetNamespace="http://unileon.es/patterns/timer"
xmlns:pn="http://xerxes.local/patterns">
<xs:element name="representation">
<xs:complexType>
<xs:complexContent
base="pn:patternRepresentationType">
<xs:attribute name="start-time"
type="xs:dateTime" />
<xs:attribute name="programmed-interval"
type="xs:duration" />
</xs:complexContent>
</xs:complexType>
</xs:element>
Dentro de la definición de datos se presenta también la configuración del patrón. En este caso, el temporizador necesita la URI de un servicio de tiempo.
<xs:element name="configuration">
<xs:complexType>
<xs:sequence>
<xs:element name="time-service">
<xs:complexType>
<xs:complexContent base="pn:patternConfigurationType">
<xs:attribute name="href" type="xs:anyURI" />
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</grammar>
El siguiente elemento es la definición del flujo de trabajo asociado al patrón. En principio, una visión abstracta de un modelo basado en redes de Petri.
<workflow>
<places>
<place name="stopped">
<description>The timer is stopped</description>
</place>
<place name="running">
<description>The timer is ticking</description>
</place>
<place name="timed-out">
<description>The programmed time interval timed out</description>
</place>
</places>
<transitions>
<transition name="start">
<description>Start ticking</description>
<arcs>
<inbound>stopped</inbound>
<outbound>running</outbound>
</arcs>
</transition>
<!-- other transitions here -->
</transitions>
<!-- each transition implicitly defines an event with no data -->
<events>
<event name="start">
<transitions>start</transitions>
<data>
<xs:complexType xsi:base="pn:eventDataType">
<xs:complexContent>
<xs:attribute name="interval"
type="xs:duration" use="required" />
</xs:complexContent>
</xs:complexType>
</data>
</event>
</events>
</workflow>
Por último, en la propia definición aparecen las acciones internas. Quizá haya que posibilitar la definición de acciones internas de usuario en Java, tal y como se hace en la máquina de estados actual.
<actions>
<action xsi:type="pn:internalAction">
<firing-condition transition="start" />
<body>
<!--
retrieve current UTC from {$this}//time-service/@href and
store as {$this}//representation/@start-time
-->
</body>
</action>
</actions>
</pattern-definition>
Instancia de patrón
El patrón instanciado es un recurso REST con una URL dada (L) y un conjunto de operaciones definidas mediante algún lenguaje, por ejemplo WADL [3].
La instancia presenta su estado como un documento XML.
<pattern-instance xmlns="http://xerxes.local/patterns"
La instancia contiene una referencia a la definición del patrón, que una herramienta de diseño podría utilizar para obtener información como el tipo de datos para un evento determinado.
pattern-definition="../definitions/timer-pattern-def.xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://xerxes.local/2006/patterns ../xsd/patterns.xsd">
En la representación aparecen los datos propios de todos los patrones y los añadidos en este tipo particular.
<workflow>
<marking>stopped</marking>
<enabled-transitions>start</enabled-transitions>
<available-events>start</available-events>
</workflow>
Tras crear una instancia del patrón, habrá que configurarlo. En este caso, es necesario dar un servicio de reloj antes de utilizar la instancia.
<configuration>
<time-service href="http://wwp.greenwichmeantime.com/public-time-server"/>
</configuration>
Por ahora, la instancia no tiene acciones (además de las internas). Tampoco está asociada a ningún recurso.
<actions/>
<targets/>
<timer>
<start-time/>
<programmed-interval/>
</timer>
</pattern-instance>
Instanciación.
En el momento de diseñar una adaptación de un patrón, hay que elegir en primer lugar el tipo de patrón que se desea como punto de partida y configurarlo.
A continuación es posible insertar acciones para personalizar el patrón genérico a la situación que se está diseñando. Aquí podría ser interesante disponer de conjuntos ya definidos de acciones. Por ejemplo, un mismo patrón «sequence» puede adaptarse para el control de flujo al insertar un conjunto ya predefinido de acciones.
Si la arquitectura de ejecución lo permite, un diseño terminado de patrón sería una representación válida de una instancia. Esa representación puede haber sido construída con una aplicación de diseño o generada de forma automática, por ejemplo mediante una transformación sobre el estado de un recurso.
Al insertar la representación en una URL {R} y cargar en el contenedor el estado inicial a partir de ella, se obtendría una instancia en {R}. Cabe la posibilidad de que en la representación aparezcan referencias a otros recursos que no se conocen en diseño; en el momento de instanciar el patrón habrá que sustituirlas por referencias válidas.
Interfaz REST
A continuación se describen las operaciones identificadas hasta ahora.
Consulta del estado
Método «GET L», devuelve un documento XML con:
- Representación de los datos internos.
- Valores de configuración.
- Lista de los estados marcados y transiciones habilitadas.
- Lista de las acciones definidas en el patrón.
- Referencia a la definición del patrón — ahí se pueden consultar los eventos definidos, configuración, estructuras de datos, etc.
Configuración de la instancia
Método «PUT L/configuration» con el fragmento de XML correspondiente, definido en la especificación del patrón.
Respuestas a este método:
- «OK» (200) — patrón configurado con éxito.
- «Bad Request» (400) — el fragmento XML adjunto no es válido.
Procesamiento de un evento
Método «POST L/events/{event-id}» con el XML de datos de evento como entidad, si lo hubiera.
Los posibles resultados del procesamiento de un evento son:
- «OK» (200) — evento procesado.
- «Bad Request» (400) — la petición no es válida. El evento no está definido, los datos no son los adecuados, etc.
Gestión de acciones
Métodos «GET L/actions» y «PUT L/actions» para trabajar con la lista completa de acciones.
Nota - REST — este patrón de interacción REST puede ofrecerse como métodos «GET» y «PUT» sobre la lista completa de acciones. La idea sería trabajar con ese XML mediante algún lenguaje como XQuery y volver a insertar toda la lista resultante en el recurso. Otra forma es ofrecer URLs «L/actions/{action}» y métodos «GET», «PUT» (modificar), «DELETE» (eliminar). Añadir una acción supondría hacer un «POST L/actions» con la definición de la nueva acción.
SmartFlow-StateEngine — contenedor de patrones.
La definición de un patrón debe ser transformada para generar una especificación de bajo nivel, específica para una plataforma de ejecución determinada. El resultado debe ofrecer la interfaz REST definida para los patrones y una semántica acorde con este documento.
La transformación de referencia se realizará con la máquina de estados SmartFlow-StateEngine como objetivo. En la siguiente figura se muestra una posible arquitectura del sistema para la ejecución de patrones en esta plataforma.
La interfaz REST de la instancia la ofrece un recurso «gateway» que podría ser, por ejemplo, un servlet. Es el encargado de resolver las peticiones a la instancia apoyándose en la máquina de estados y en el servidor de base de datos XML.
La máquina de estados ejecuta la red asociada al patrón y se encarga de las operaciones que cambian su estado, como por ejemplo el procesamiento de eventos.
Las representaciones se mantienen en un gestor de base de datos XML que ofrece sus servicios (básicamente GET/PUT) en una interfaz REST. Algunas de las peticiones a la instancia serán resueltas directamente contra la base de datos, sin necesidad de cambiar el flujo de trabajo. Por ejemplo, insertar nuevas acciones u obtener la representación de la instancia.
La ejecución de las acciones asociadas a la red consiste en solicitar a la base de datos el conjunto de acciones insertadas en la instancia, calcular aquellas que deben dispararse y ejecutar el código XQuery asociado. Este procedimiento es común a todos los patrones y se incorporará en una clase de acciones de la máquina de estados.
Por otra parte, la máquina de estados debe actualizar la representación de la instancia cuando la red del flujo de trabajo cambia de estado.
Ejemplos
Patrón «collection»
En la siguiente figura se muestra una red de Petri asociada a un patrón colección. Incorporado como patrón oyente, ofrece el registro de acciones para recursos que estén interesados en situaciones como «nuevo elemento», «contenedor vacío» o «elemento eliminado».
Patrón «do-while»
Un patrón puede ser útil para gestionar flujo de control. En la siguiente figura aparece una posible red de Petri para un patrón «do-while».
Si la evaluación de la condición de parada es una acción interna, está fuera del control del cliente. La expresión de la condición de cada instancia podría ser una parte de la configuración del patrón, o simplemente consultar un dato interno que el cliente cambiará mediante acciones.

