lunes, 23 de septiembre de 2013

Servicios web (webservices) con REST sobre CodeIgniter

PRIMERA PARTE

Acoplar bibliotecas para brindar servicios webs (webservices) sobre CodeIgniter, como en tantos otros casos, resulta extremadamente sencillo si has encontrado la biblioteca correcta. La complicación podría venir posteriormente cuando quieras utilizar las funcionalidades de la biblioteca utilizada. En este artículo te mostraré como utilizar dos tendencias: REST y SOAP, sobre CodeIgniter.

REST (Representational state transfer: transferencia representacional de estados), es un estilo que abstrae los elementos arquitectónicos en un sistema de hipermedia distribuido. REST ignora los detalles de implementación de los componentes y la sintaxis del protocolo, enfocándose en los roles de los componentes, su interpretación y la interacción con otros componentes. Ha emergido y es predominante como un API modelo de diseño web.

SOAP (Simple Object Access Protocol: protocolo de acceso a objetos) es un estándar que define cómo dos objetos en diferentes procesos pueden comunicarse por medio de intercambio de datos XML. Cuenta con buena extensibilidad, neutralidad (puede ser utilizado sobre cualquier protocolo de transporte como HTTP, SMTP, TCP o JMS) e independencia (permite cualquier modelo de programación).

Ambas tecnologías son buenas según tu objetivo. En internet se suele decir que REST es muy fácil de utilizar (por su abstracción) pero se recomienda para sistemas sencillos, en cambio SOAP es bastante difícil de utilizar (por ser tan genérica y flexible) y se recomienda para sistemas más complejos. Así que lo primero es definir qué modelo utilizarás según la complejidad de tu sistema.

En este artículo te mostraré ambas formas. Mi intención no es que entiendas perfectamente el funcionamiento de estas bibliotecas ni que aprendas a usarlas con solo leer este artículo. Solo quiere que te lleves la idea de la diferencia de complejidad en el uso de una u otra biblioteca, y que te sirva como punto de partida para buscar más información y brindar servicios web seguros y robustos. A la vez, demostrarte la facilidad con que ambas bibliotecas se integran con CodeIgniter.

REST

Para trabajar con REST en CodeIgniter te recomiendo descargar la biblioteca “CodeIgniter Rest Server” [2] (para PHP 5.2 o mayor y del CodeIgniter 2.1.0 al 3.0-dev). También puedes descargar el “CodeIgniter Rest Client” [3] si lo que deseas es consumir servicios. En mi caso solamente me interesa publicarlos.

Por cierto, si quieres consumir servicios con REST necesitas trabajar con cURL que te permite encapsular los datos que le pasas al servidor. Para esto puedes usar la biblioteca “CodeIgniter Curl library” [4].

Una vez descargada y descompactada la “CodeIgniter Rest Server”, notarás que es todo un proyecto de CodeIgniter con sus carpetas application y system. De esto solamente necesitas copiar “Format.php” y “REST_Controller.php” del application/libraries de la biblioteca para la carpeta application/libraries de tu sitio; y el fichero “rest.php” del application/config de la biblioteca para el application/config de tu sitio.

Luego adicionas un controlador con cualquier nombre, que herede de REST_Controller:

class Services_rest extends REST_Controller

Y listo, ya tienes acoplada la biblioteca a tu proyecto de CodeIgniter.

El segundo paso sería utilizar la biblioteca. Dado que puedes recibir peticiones por GET o por POST, la manera de que CodeIgniter Rest Server entienda cuál es la función adecuada es poniendo el sufijo get o post al final de las funciones. Por ejemplo el método students_get se ejecutará cuando se llame al servicio “students” vía GET, y el método students_post se ejecutará cuando se llame al servicio “students” vía POST. También existen los sufijos para PUT y DELETE.

En este link [1] podrás encontrar un tutorial muy completo de cómo acoplar CodeIgniter Rest Server a CodeIgniter, y te explica también cuándo y cómo usar PUT y DELETE, las ventajas y cómo usar cURL para consumir los servicios, etc.

Un ejemplo de una función-servicio con REST podría ser (dentro del controlador services_rest):

function students_get()
{
$result = new Response_students();

$id = $this->get('id');
$this->load->model('students_model');
$student = $this->students_model->get_student($id);

if (!empty($equipment))
{
$result->responseCode = 0;
$result->responseMessage = 'Equipo encontrado';
$result->student_data = $student;
}
else
{
$result->responseCode = 1;
$result->responseMessage = 'El estudiante no existe';
$result-> student_data = null;
}

$this->response($result, 200);
}

“Response_students” es una clase que creé para gestionar mejor los códigos y mensajes de errores, pero se puede retornar simplemente $this->response($student, 200) con los resultados sacados de una BD en forma de tabla, sin importar si es una fila o varias (por eso se destaca el nivel de abstracción de REST). El resultado se enviará al cliente sin problema alguno (el 200 es un código de respuesta, como 400, 404, etc).


Esta función como habrás notado se llamará vía GET y recibe un “id”. La forma de llamarla para comprobar sería:

http://sitio/services_rest/id/25 (sitio / controlador / nombre del parámetro / valor del parámetro, puedes ver en [1] una mejor explicación de esta sintaxis).

Puesto así se retorna una estructura XML que puedes ver directamente en tu navegador. Si lo quisieras en otro formato simplemente pondrías /format/el_formato, por ejemplo:

http://sitio/services_rest/id/25/format/json

Si la función fuera para ser utilizada por POST, capturarías el parámetro con $id = $this->post('id').

Con esto ya tienes montado tu servidor de webservices con REST y cada función es muy sencilla de implementar, con la facilidad de que REST traduce tus datos enviados por response a varios formatos de salida sin importar el tipo de dato proveniente de la BD (int, string…).

Faltaría prevenir que cualquiera teclee la dirección en la barra y acceda a tus datos o acceda al servicio sin autorización. Para esto entras a application/config/rest.php y pones:

$config['rest_auth'] = 'digest';   //línea 62
$config['rest_valid_logins'] = array(
'user1' => 'pass1',
'user2' => 'pass2'); //línea 109


Terminado. En la segunda parte de este artículo te hablaré entonces sobre el trabajo son la biblioteca nusoap sobre CodeIgniter.

Referencias:

[1] Working with RESTful Services in CodeIgniter,

http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/

[2] Descarga de biblioteca CodeIgniter Rest Server:

http://github.com/philsturgeon/codeigniter-restserver

[3] Descarga de biblioteca CodeIgniter Rest Client:

http://github.com/philsturgeon/codeigniter-restclient

[4] Descarga de biblioteca CodeIgniter Curl library:

http://getsparks.org/packages/curl/show

{ Leer Más }


viernes, 20 de septiembre de 2013

¿Ingeniero de Software o Programador?

Hola comunidad. Decidí comenzar, con la pregunta que da título a este, una serie de artículos sobre ingeniería de software, programación y buenas prácticas en sentido general. A pesar del tiempo la interrogante sigue siendo polémica; fundamentalmente cuando nos estamos introduciendo en una especialidad tan amplia como la informática donde, muchas veces, desde cada posición se tiende a menospreciar la importancia del trabajo del otro.

clip_image002Y la pregunta en cuestión para mí sólo tiene una respuesta: AMBOS. No sólo porque son imprescindibles para desarrollar un software de calidad, sino porque, si somos capaces de conocer ambos desempeños seremos mucho mejores programadores e ingenieros de software.

En los inicios de lo que conocemos actualmente como industria del software, los primeros roles establecidos fueron el de analista y programador, en el intento de salir de la era del desarrollo de software “artesanal”. Estos roles aún existen por sus nombres, pero para aquel entonces sus competencias eran mucho más limitadas. El analista tenía como responsabilidad conocer paso a paso la actividad que se deseaba automatizar, la describía en papel detalladamente y era entonces que el programador, con sus conocimientos del lenguaje, las traducía a un programa. Dado que los problemas de comunicación entre estos roles eran considerables, la tendencia fue que el analista conociera de programación para manejar los mismos términos que el programador y que este, a su vez, conociera de la concepción de un programa para que no fuera sólo un traductor, sino un agente activo en la mejora del mismo. Ese proceso de interdisciplinariedad generó una expansión de ambos perfiles y tuvo como resultado el surgimiento de nuevos roles en la ingeniería de un proceso de desarrollo de software cada vez más depurado.

Actualmente, un programador tiene dominio de uno o más lenguajes y sus herramientas, que cada vez son más acabadas y complejas e incluyen definiciones arquitectónicas y patrones de diseño que, inevitablemente, llevan a que el programador esté cada vez más cerca del conocimiento integral del desarrollo de un sistema. Por lo que, si como programador tengo los conocimientos ingenieriles necesarios, se eleva mi rendimiento y obtengo como resultado calidad y eficiencia.

Por otro lado, desde mi punto de vista, un ingeniero de software sin experiencia de programación se verá muy limitado en el desempeño de los diferentes roles para los que supuestamente fue preparado. Pudiera enumerar un conjunto de situaciones frecuentes en las que se vería sumamente limitado su desempeño:

  1. ¿Cómo podría definir el flujo de un proceso a automatizar sin contar con un pensamiento orientado a objetos que dé un acabado al análisis del problema y sea el punto de partida al diseño?
  2. ¿Con qué base pudiera definirse la arquitectura de un sistema y tomar las decisiones arquitectónicas que se requieran en su desarrollo?
  3. ¿Cómo definir un diseño de clases correctamente y decidir que patrones quiero aplicar?
  4. ¿Cómo pudiera crear un diagrama UML de secuencias con las correspondientes llamadas entre clases sin saber que eso que está escribiendo “no compila”?
  5. ¿Cómo pudiera definir correctamente para un problema qué algoritmo utilizar y su complejidad?
  6. ¿Cómo definir correctamente pruebas de caja blanca?
  7. ¿Cómo liderar con efectividad un equipo de programadores sin saber qué es lo que hacen?

En general, salvo algunos roles que pudiera desempeñar con escasos o nulos conocimientos de programación, el ingeniero de software degenera a poco más que a ese primer analista que hablé existía en un principio, negando todos estos años de evolución del proceso de desarrollo de software, donde el ingeniero es quien lo hace posible.

{ Leer Más }


martes, 10 de septiembre de 2013

Desarrollando una lista de TO-DO usando base de datos y el Framework ZK.

clip_image002

Te mostraremos, paso a paso, como construir una sencilla aplicación web con ZK que use una base de datos. La aplicación web permitirá almacenar sucesos que vamos a hacer en el futuro o sea una lista de TO-DO. Una aplicación web así, requeriría usar una base de datos. Para la conclusión de este tutorial usaremos una base de datos Java (HSQL DB) la cual no requiere que instalemos un servidor de base de datos [1].

Pasos para ejecutar la aplicación:

  1. Descargue el código fuente de aquí.
  2. cd todo
  3. mvn jetty:run (si usted no tiene maven instalado por favor descárguelo aquí )
  4. Abra su navegador y coloque la siguiente url: http://localhost:8080/todo

clip_image003

Todos los Escenarios

§ Introduce toda la información relativa a un evento y presiona el botón  Add  para insertarlo en la base de datos.

§ Selecciona cualquier fila en la tabla para mostrar la información del evento en los campos de abajo para que modificar su información, después presiona el botón  Update  para modificar la información.

§ Selecciona cualquier fila en la tabla y presiona el botón  Delete  para eliminar el "Event" seleccionado.

Modelo

En los siguientes párrafos, el esquema de la base de datos, el objeto del dominio y el objeto DAO serán introducidos.

Esquema de la base de datos

Una tabla de la base de datos la cual va a almacenar los datos de nuestra aplicación, necesitará los siguientes atributos: event id, event name, event priority and event date.

El esquema de base de datos está listado abajo.

Field

Type

id

varchar(50)

name

varchar(50)

priority

int

date

date

Objeto Dominio

Para la la tabla de arriba, creamos el objeto de dominio correspondiente, como se indica:

public class TodoEvent {

private String id;

private String name;

private int priority;

private Date date;

public TodoEvent(String id, String name, int priority, Date date) {

this.id = id;

this.name = name;

this.priority = priority;

this.date = date;

}

// getter and setter methods are ommited, please refer to the source code.

}

Objeto de Acceso a Datos


Para que fácilmente se acceda a nuestra base de datos necesitaremos un objeto DAO con los siguientes métodos: findAll(),delete(),insert() and update().

public class EventDAO {

// The implementation is ommited, please refer to the source code.

public List<TodoEvent> findAll() {

}

public boolean delete(TodoEvent evt) {

}

public boolean insert(TodoEvent evt) {

}

public boolean update(TodoEvent evt) {

}

}

La Vista


Tu primer componente ZK


El primer paso es crear un fichero cuya extensión debe ser zul, pongamos todo.zul, y situamos este fichero bajo el directorio de nuestra aplicación web, por ejemplo, $TOMCAT_HOME/webapps/ProjectName/. Declaras un componente ZK de igual forma a como declaras un componente usando HTML.

Prueba a declarar tu primer componente  window  de la siguiente forma:

<window title="To do list" border="normal">

</window>

Después inicia Tomcat y usa el navegador para visitar la página, eg. http://localhost:8080/todo/todo.zul. El resultado se muestra abajo,  window  con el título 「To do List」.

Todo en ZK es un componente. Puedes cambiar el título, el alto y el borde de tu  window  Es muy sencillo e intuitivo. Intenta cambiar estos atributos y comprueba el resultado.

Relaciones jerárquicas entre los componentes de ZK


Lo siguiente, vamos a intentar enriquecer esta página con más componentes ZK. Para mostrar datos en una tabla, podemos usar un componente  listbox  el cual está diseñado para mostrar datos. Para insertar un listbox lo declaramos dentro de los tags del componente  window, como se indica:

<window title="To do list" border="normal">

<listbox id="box" multiple="true" rows="5">

</listbox>

</window>

En este ejemplo, el componente  listbox  es un componente hijo de  window. Sí, hay relaciones jerárquicas entre los componentes de ZK, te encontrarás una excepción UI si intentas declarar un componente dentro de un contexto equivocado, por ejemplo, al declarar un componente  window  como hijo de un componente  listbox.

Un componente anidado


Un listbox es un componente anidado, el cual soporta dos tipos de componentes hijos, listhead (aka: columna de la tabla), and listitem (aka: fila de la tabla). Con la declaración del  listbox, asignamos el atributo id a 「box」, ahora podemos usar este atributo para referenciar el listbox.

<window id="win" title="To do list" width="640px" border="normal">

<listbox id="box" multiple="true" rows="5">

<listhead>

</listhead>

<listitem>

</listitem>

</listbox>

</window>

Aún no hemos terminado. Vamos a declarar tres componentes listheader dentro de los tags de listhead. Necesitamos tres columnas -- 「Item」, 「Priority」, and 「Date」 -- dentro de la tabla, como se indica:

<window id="win" title="To do list" width="640px" border="normal">

<listbox id="box" multiple="true" rows="5">

<listhead>

<listheader label="Item" />

<listheader label="Priority" width="80px" />

<listheader label="Date" width="170px" />

</listhead>

<listitem>

</listitem>

</listbox>

</window>

Tenemos tres columnas en nuestra tabla, cada fila de la tabla también requerirá tres campos. Declaramos tres componentes listcell dentro de los tags del componente listitem.

<window id="win" title="To do list" width="640px" border="normal">

<listbox id="box" multiple="true" rows="5">

<listhead>

<listheader label="Item" />

<listheader label="Priority" width="80px" />

<listheader label="Date" width="170px" />

</listhead>

<listitem>

<listcell />

<listcell />

<listcell />

</listitem>

</listbox>

</window>

La estructura anidada del componente  listbox  quedaría así:

listbox 
+-- listhead
|    |
|    +-- listheader
|
+-- listitem
      |
     +-- listcell

Componentes para introducir información


En adición a mostrar estos eventos en el  listbox, necesitamos introducir información relativa al evento, incluyendo el evento name (campo texto), el evento priority (campo numérico) y el evento date (campo fecha). Para lograr esto, declare un textbox, un intbox y un datebox dentro de los tags del componente  window  de la siguiente forma:

<window id="win" title="To do list" width="640px" border="normal">

<listbox id="box" multiple="true" rows="5">

<listhead>

<listheader label="Item" />

<listheader label="Priority" width="80px" />

<listheader label="Date" width="170px" />

</listhead>

<listitem>

<listcell />

<listcell />

<listcell />

</listitem>

</listbox>

Item: <textbox id="name" cols="25" />

Priority: <intbox id="priority" cols="1" />

Date: <datebox id="date" cols="8" />

<button id="add" label="Add" />

<button id="update" label="Update" />

<button id="delete" label="Delete" />

</window>

 


Componentes Layout


Para distinguir los componentes de entrada de datos (input) de los componentes listbox de abajo, declaramos un groupbox para agrupar los componentes. Esto dibujará un borde alrededor de los componentes que estén contenidos dentro del groupbox, en este caso, los componentes de entrada de datos (input).

<window id="win" title="To do list" width="640px" border="normal">

<listbox id="box" multiple="true" rows="5">

<listhead>

<listheader label="Item" />

<listheader label="Priority" width="80px" />

<listheader label="Date" width="170px" />

</listhead>

<listitem>

<listcell />

<listcell />

<listcell />

</listitem>

</listbox>

<groupbox>

<caption label="Event" />

Item: <textbox id="name" cols="25" />

Priority: <intbox id="priority" cols="1" />

Date: <datebox id="date" cols="8" />

<button id="add" label="Add" />

<button id="update" label="Update" />

<button id="delete" label="Delete" />

</groupbox>

</window>

Adicionalmente a los componentes  groupbox  declaramos un componente  caption  para mostrar una etiqueta 「Event」 a lo largo de la caja que agrupa a los componentes. El componente caption trabaja de forma similar al legendario elemento en HTML.

Controlador


Nuestros requisitos incluyen mostrar, añadir, editar y borrar sucesos/eventos. En los siguientes párrafos, implementaremos las interacciones entre nuestra aplicación web y la base de datos.

Definiendo un Controlador


El primer paso es definir un EventController el cual herede de org.zkoss.zk.ui.util.GenericForwardComposer.

Implementando org.zkoss.zk.ui.util.GenericForwardComposer

Crea un EventController el cual heredará de org.zkoss.zk.ui.util.GenericForwardComposer permitiéndote un fácil acceso a la vista y definir un CRUD mediante los métodos Java definidos.

// org.zkforge.todo.event.EventController.java

public class EventController extends GenericForwardComposer {

private static final long serialVersionUID = -9145887024839938515L;

private EventDAO eventDao = new EventDAO();

// Note: Something is omitted at here. You can view detail about this class on source code.

public List<TodoEvent> getAllEvents() {

return eventDao.findAll();

}

public void onClick$add() {

}

public void onClick$update() {

}

public void onClick$delete() {

}

}

Asociando el Controlador con la Vista

Para implementar la interacción entre la Vista y el Controlador asignamos el atributo apply al componente window en la ruta de nuestro EventController.

<window id="win" title="To do list" width="640px" border="normal"

apply="org.zkforge.todo.event.EventController">

......

Visualizando datos en una Vista


Para visualizar los datos recuperados de la base de datos en nuestra Vista solo requerimos tres pasos:


  • Activar un mecanismo de DataBinding Mechanism
  • Asociar datos con la Vista
  • Definir una plantilla

Activando el mecanismo de DataBinding

Para usar el mecanismo Data Binding, deberemos activar el Data Binding Manager (org.zkoss.zkplus.databind.AnnotateDataBinderInit) en la parte superior de la página.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>

<window id="win" title="To do list" width="640px" border="normal"

apply="org.zkforge.todo.event.EventController">

....

Asociando los datos con la vista

El paso siguiente es recuperar los datos de la base de datos mediante el EventController usando la expresión (win$composer.allEvents) para invocar EventController.getAllEvents(). El método EventController.getAllEvents() devuelve una lista que contiene todos los eventos almacenados en la base de datos que pueden ser asociados al atributo model del Listbox.

<window id="win" title="To do list" width="640px" border="normal"

apply="org.zkforge.todo.event.EventController">

<listbox id="box" multiple="true" rows="5" model="@{win$composer.allEvents}">

....

Puedes seguir los siguientes pasos para definir una plantilla de Interfaz de Usuario para renderizar los datos dentro de la vista.

Definiendo una plantilla

Es posible definir una plantilla de interfaz de usuario para renderizar DataBinding dentro de los correspondientes componentes de interfaz de usuario. Esto se logra gracias a la utilización de los atributos que ofrece el componente "listitem" definiendo una variable para representar cada instancia a reflejar.

<window id="win" title="To do list" width="640px" border="normal"

apply="org.zkforge.todo.event.EventController">

<listbox id="box" multiple="true" rows="5"

model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"

selectedItem="@{win$composer.current}">

<listhead>

<listheader label="Item" sort="auto(name)" />

<listheader label="Priority" width="80px" sort="auto(priority)" />

<listheader label="Date" width="170px" sort="auto(date)" />

</listhead>

<listitem self="@{each='event'}" value="@{event}">

<listcell label="@{event.name}" />

<listcell label="@{event.priority}" />

<listcell label="@{event.date}" />

</listitem>

</listbox>

Para más información, por favor dirígete a Associate UI Components with a Collection.

Sincronización de vistas


Una vez el usuario selecciona un ToDoEvent en el listbox, Necesitamos mostrarlo en el componente groupbox también. Sería mucho mejor si este tedioso trabajo fuera realizado de forma automáticamente. Data Binding es la respuesta! Simplemente asocia los datos con todo lo relacionado con los componentes de UI. El DataBinding se sincronizará entonces con todos los componentes de Interfaz de Usuario una vez la propiedad ha sido modificada.


  • Definir una instancia en un Controlador
  • Asociar múltiples componentes UI a la instancia

Definir una instancia "ToDoEvent" a un Controlador

Define una instancia ToDoEvent' en EventController, y define métodos getter y setter para que puedan ser accesibles desde la vista.

// org.zkforge.todo.event.EventController.java

public class EventController extends GenericForwardComposer {

private TodoEvent current = new TodoEvent();

// Omitted...

public TodoEvent getCurrent() {

return current;

}

public void setCurrent(TodoEvent current) {

this.current = current;

}

Asociando múltiples componentes UI con la instancia EventController

Primero, asocia la instancia EventController con la propiedad selectedItem del listbox. Después, asocia las propiedades del ToDoEvent con el correspondiente componente UI, incluyendo textbox, intbox, y datebox. Una vez el usuario selecciona un item en un listbox, la instancia se actualizará en relación a los componentes UI.

<listbox id="box" multiple="true" rows="5"

model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"

selectedItem="@{win$composer.current}">

<!-- Omitted -->

<groupbox>

<caption label="Event" />

Item: <textbox id="name" cols="25" value="@{win$composer.current.name}" />

Priority: <intbox id="priority" cols="1" value="@{win$composer.current.priority}" />

Date: <datebox id="date" cols="8" value="@{win$composer.current.date}" />

Sincronizando la vista con la base de datos


Además de mostrar los datos de la base de datos en la vista, nos gustaría implementar eventos para añadir, actualizar y eliminar. Esta funcionalidad requiere de tres pasos, monitorizar la actividad del usuario, interactuando con los controladores para actualizar la base de datos y actualizar la vista.


  • Registrar el evento Listener en el controlador
  • Actualizar la vista usando Databinding

Registrar el evento Listener en el Controlador

Para monitorizar la actividad del usuario, simplemente registramos un evento listener onClick' en los siguientes tres botones add, update and delete. Cuando cliqueamos en estos botones que se corresponden a los métodos definidos en EventController estos serán invocados. Estos métodos son definidos así:

public class EventController extends GenericForwardComposer {

private EventDAO eventDao = new EventDAO();

// Omitted...

public void onClick$add() {

if (current != null) {

current.setId(UUID.randomUUID().toString());

if (validate(current)) {

// insert into database

eventDao.insert(current);

}

}

}

public void onClick$update() {

if (current != null && validate(current)) {

// update database

eventDao.update(current);

}

}

public void onClick$delete() {

if (current != null && validate(current)) {

eventDao.delete(current);

}

}

}

<button id="add" label="Add" />

<button id="update" label="Update" />

<button id="delete" label="Delete" />


Actualizar la vista usando Databinding

Después de interactuar con la base de datos, el último paso es actualizar la vista. Simplemente notificar al Databinding para que actualice el modelo por ti, cuando el usuario cliqueé en cualquier botón. Intenta registrar un atributo load-after en el atributo model del listbox, y el Databinding actualizará la vista automáticamente si el evento especificado es capturado.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>

<window id="win" title="To do list" width="640px" border="normal"

apply="org.zkforge.todo.event.EventController">

<listbox id="box" multiple="true" rows="5"

model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"

selectedItem="@{win$composer.current}">

<!-- Omitted... -->

<button id="add" label="Add" />

<button id="update" label="Update" />

<button id="delete" label="Delete" />

</groupbox>

</window>

Eso es todo, esperamos que le sea útil y quedamos al tanto de sus comentarios.




{ Leer Más }


jueves, 5 de septiembre de 2013

Actualización de formularios con JQuery y Ajax sobre CodeIgniter

En este artículo te mostraré cómo actualizar el contenido de un formulario o una vista en general, usando Jquery y luego un ejemplo donde se consulta con Ajax la BD para recargar un componente visual.

Para “rellenar” el contenido de una parte de la vista luego de que esta ha sido cargada (por ejemplo cuando se dé clic en algún lado, cuando se haga una consulta, etc.), puedes usar la propiedad “html” de los componentes. Puedes poner en tu vista algún lugar “vacío” para llenarlo luego… en ese lugar pondrías:

<div id="my_area"></div>

Y entonces en código script:

<script type="text/javascript">
$("#my_area").html("contenido del área");
</script>

Donde el “contenido del área” puede ser una cadena como esa misma que puse entre comillas, o cualquier contenido web como una tabla, una imagen, etc.


Usando esto entonces se pueden hacer consultas a la base de datos desde la vista, sin tener que ir a la función controller_name/function/parameters_get, y por tanto no recargarías toda la página sino el segmento que vas a refrescar. El código para hacer un post desde la vista es:

<script type="text/javascript">

//ejemplo con variable por get y ninguna por post
var value_name = 'Mary';
$.post('<?php echo site_url()?>controller_name/example_1/'+value_name,
{},
function(data){
$('#my_area').html(data);
});

//ejemplo con variables por post y ninguna por get
var value_name = 'Mary';
var value_age = 20;
$.post('<?php echo site_url()?>controller_name/example_2',
{'name': value_name, 'age': value_age },
function(data){
$('#my_area').html(data);
});

</script>

En el primer ejemplo llamo una función de un controlador que no espera variables por post, en el segundo estoy pasando dos variables que se recogerían en el controlador con “input”, como si estuvieras recibiendo directamente un formulario. Si realmente quieres recibir los valores de un formulario entonces puedes acceder al valor de un componente de la siguiente manera:

var value_name = $("#text_name").val(); //dentro de <script>

Es importante que el nombre, en este caso “text_name”, lo pongas como id del componente. Si el valor no te está llegando es lo primero que debes revisar. Otra cosa es que los componentes no tienen que estar necesariamente dentro de un formulario, a jquery no le importa eso. Lo pones en un formulario si quisieras hacer además el submit tradicional.

La función que hace el post retorna un “data” (esta variable puede tener cualquier nombre) que es el que se renderea dentro del área “my_area” en formato html. Para esto al final de la función del controlador debes hacer un “echo”. Para el ejemplo anterior podría ser algo así:


function example_1 ($name)
{
echo 'El nombre es: '.$name;
}

function example_2 ()
{
$name = $this->input->post('name');
$age = $this->input->post('age');

echo 'El nombre es: '.$name.' y la edad es: '.$age;
}

Como ves es bastante sencillo y sumamente útil. Por ejemplo puedes hacer que cuando se seleccione una opción en un combobox se rellene el contenido de otro combobox, por ejemplo que al seleccionar un estado de un país en un combo, en el siguiente salgan los municipios correspondientes. Para esto en el “onchange” del primer combo capturas su valor, haces un post con Ajax pasando este valor al controlador, en el controlador obtienes los municipios correspondientes de la base de datos, construyes un combobox en código html y lo retornas como data, que luego pondrías en el lugar del segundo combobox.


Es todo, espero te haya sido de utilidad.

{ Leer Más }


martes, 3 de septiembre de 2013

Programación multi-hilos usando QWaitCondition y QMutex.

clip_image002

En el artículo anterior mostramos los principales elementos a tener en cuenta para realizar programación concurrente usando el Framework Qt, específicamente empleamos la clase QSemaphore para resolver el problema del productor-consumidor. En este nuevo artículo explicaremos otra alternativa que también es muy eficiente para programar aplicaciones multi-hilos, se trata de la combinación de QWaitCondition y QMutex.

El objetivo del artículo es mostrar cómo utilizar QWaitCondition y QMutex para controlar el acceso a un búfer circular que comparten un hilo productor y otro hilo consumidor [1].

El productor escribe datos en el búfer hasta que se llega al final del búfer, en cuyo punto se reinicia desde el principio, sobrescribiendo los datos existentes. El hilo consumidor lee los datos a medida que se produce y lo escribe en la salida de error estándar.

Las condiciones de espera (Wait Condition) hacen que sea posible tener un mayor nivel de concurrencia que el que lograríamos si usáramos solamente mutex. Si los accesos al búfer simplemente fueron vigilados por un QMutex, el hilo consumidor no podría acceder al búfer al mismo tiempo que el hilo productor. Sin embargo, no hay nada malo en tener dos hilos que trabajan en diferentes partes del búfer al mismo tiempo.

El ejemplo consta de dos clases: Productor (Producer) y Consumidor (Consumer). Ambos heredan de QThread. El búfer circular utilizado para la comunicación entre estas dos clases y las herramientas de sincronización que lo protegen son variables globales.

Variables globales

Vamos a empezar por la revisión del búfer circular y las herramientas de sincronización asociadas:

const int DataSize = 100000;

const int BufferSize = 8192;
char buffer[BufferSize];

QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;

DataSize es la cantidad de datos que el productor va a generar. Para mantener el ejemplo tan simple como sea posible, hacemos una constante. BufferSize es el tamaño del búfer circular. Es menos de DataSize, lo que significa que en algún momento el productor alcanzará el final del búfer y reiniciará desde el principio.

Para sincronizar el productor y el consumidor, necesitamos dos condiciones de espera y un mutex. La condición bufferNotEmpty se señala cuando el productor ha generado algunos datos, diciéndole al consumidor que puede empezar a leerlo. La condición bufferNotFull se señala cuando el consumidor haya leído algunos datos, dice la productora que pueda generar mucho más. La variable numUsedBytes indica el número de bytes en el búfer que contiene los datos.

Juntos, las condiciones de espera, la exclusión mutua, y el contador numUsedBytes deben garantizar que el productor nunca está escribiendo bytes en una ubicación mayor que bufferSize por delante del consumidor, y que el consumidor nunca lee los datos que el productor no ha generado todavía.

Clase Productor

Repasemos el código de la clase Producer:

class Producer : public QThread
{
public:
Producer(QObject *parent = NULL) : QThread(parent)
{
}

void run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == BufferSize)
bufferNotFull.wait(&mutex);
mutex.unlock();

buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];

mutex.lock();
++numUsedBytes;
bufferNotEmpty.wakeAll();
mutex.unlock();
}
}
};

El productor genera DataSize bytes de datos. Antes de escribir un byte en el búfer circular, debe comprobar primero si el búfer está lleno (es decir, si numUsedBytes es igual a BufferSize). Si el búfer está lleno, el hilo espera en la condición bufferNotFull.

Al final, el productor incrementa la variable numUsedBytes y envía la señal indicando que la condición bufferNotEmpty es verdadera, ya que numUsedBytes es necesariamente mayor que 0.

Guardamos todos los accesos a la variable numUsedBytes con un mutex. Además, la función QWaitCondition::wait() acepta un mutex como argumento. Este mutex está desbloqueado antes el hilo se pone a dormir y bloqueado cuando el hilo se despierta. Por otra parte, la transición desde el estado de bloqueo al estado de espera es atómica, para evitar que se produzcan las condiciones de carrera.

Clase del Consumidor

Volvamos a la clase de los consumidores:

class Consumer : public QThread
{
Q_OBJECT
public:
Consumer(QObject *parent = NULL) : QThread(parent)
{
}

void run()
{
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
mutex.unlock();

fprintf(stderr, "%c", buffer[i % BufferSize]);

mutex.lock();
--numUsedBytes;
bufferNotFull.wakeAll();
mutex.unlock();
}
fprintf(stderr, "\n");
}

signals:
void stringConsumed(const QString &text);
};

El código es muy similar a la del productor. Antes de leer el byte, comprobamos si el búfer está vacío (numUsedBytes es 0) y esperamos en la condición bufferNotEmpty si está vacío. Después de que hemos leído el byte, hacemos el decremento de la variable numUsedBytes (en lugar de incrementarlo), y señalamos la condición bufferNotFull (en lugar de la condición bufferNotEmpty).

La función main ()

En la función main (), creamos los dos hilos y llamamos a la función QThread::wait() para asegurar de que ambos contextos de ejecución tienen tiempo para terminar antes de la salida:

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}

Entonces, ¿qué sucede cuando ejecutamos el programa? Inicialmente, el hilo productor es el único que puede hacer cualquier cosa, el consumidor está bloqueado en espera de que la condición bufferNotEmpty sea señalizada (numUsedBytes es 0). Una vez que el productor ha puesto un byte en el buffer, numUsedBytes es BufferSize - 1 y la condición bufferNotEmpty se marca. En ese momento, pueden ocurrir dos cosas: o bien el hilo consumidor se hace cargo y lee el byte, o el productor genera un segundo byte.

El modelo productor-consumidor que se presenta en este ejemplo permite escribir aplicaciones multiprocesos altamente concurrentes. En un equipo con varios procesadores, el programa es potencialmente hasta el doble de rápido que el programa equivalente basado en exclusión mutua, ya que los dos hilos pueden estar activos al mismo tiempo en diferentes partes del búfer.

Tenga en cuenta que estos beneficios no siempre son efectivos. Los procesos de bloqueo y desbloqueo de un QMutex tiene un costo. En la práctica, probablemente valdría la pena dividir el búfer en trozos, pues es más eficiente operar en trozos en lugar de bytes individuales. El tamaño del búfer es también un parámetro que se debe seleccionar cuidadosamente, basado en la experimentación.

El código fuente de esta aplicación pueden descargarlo desde aquí.




{ Leer Más }


IconIconIcon