Mostrando entradas con la etiqueta ZK Framework. Mostrar todas las entradas
Mostrando entradas con la etiqueta ZK Framework. Mostrar todas las entradas

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 }


miércoles, 28 de agosto de 2013

Integrando Spring Security con el Framework ZK

clip_image002

Spring Security es una solución común para el desarrollador para satisfacer las necesidades de seguridad de una aplicación web de Java, que es ampliamente utilizado y es una tecnología probada. Sin embargo, debido a su naturaleza para proteger los recursos por parte de patrones a través de URL, no es obvia para los desarrolladores de aplicaciones para darse cuenta de la Seguridad de Spring se puede adaptar con una petición Ajax mecanismo de manejo específico de un marco de Ajax [1].

Así que en este artículo, voy a presentarles cómo integrar Spring Security con ZK sin problemas por ir a través de la construcción de una aplicación de demostración sencilla (Una publicación del artículo y un sistema de edición).

Recursos para descargar

Puede descargar el código fuente de aquí

El proyecto está basado en Maven, si quieres probar diferentes versiones de ZK o spring, cambie el número de versión en pom.xml

Demostración Detalles de la aplicación

Esta aplicación Demo es un simple artículo publicar y editar sistema que permite tres tipos de usuarios para acceder a:

  • usuario con rol ROLE_USER
  • usuario con rol ROLE_EDITOR
  • usuario anónimo con rol IS_AUTHENTICATED_ANONYMOUSLY

clip_image004

  • El usuario Anónimo puede visitar la página de inicio que contiene la lista de artículos, y se puede ver el contenido de un artículo haciendo clic en el enlace de página de inicio.
  • Al usuario con rol ROLE_USER se le permite publicar artículos nuevos y editar sus propios artículos.
  • El usuario con rol ROLE_EDITOR es el más poderoso, es capaz de editar y borrar cualquier artículo.

Este artículo se basa en los requisitos de implementación de esta aplicación para demostrar la integración de Spring Security con ZK.

Configuración de Spring Security

En primer lugar, vamos a ver cómo configurar nuestro proyecto. Para utilizar Spring Security, tenemos que añadir algunos oyentes y declaraciones de filtro en web.xml

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/applicationContext.xml

/WEB-INF/applicationContext-security.xml

</param-value>

</context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<listener>

<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>

</listener>

<listener>

<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>

</listener>

<filter><!-- the filter-name must be preserved, do not change it! -->

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


Como se puede ver, a pesar de los oyentes de contexto ordinarios de Spring (RequestContextListener y ContextLoaderListener), declaramos HttpSessionEventPublisher y springSecurityFilterChain de Spring Security. Aquí HttpSessionEventPublisher es opcional y está diseñada para Spring Security para hacer el control de sesión simultánea detallada, springSecurityFilterChain es el gancho principal de toda la funcionalidad de Spring Security de la aplicación, es necesario y debe ser nombrado springSecurityFilterChain.

applicationContext-security.xml

Aquí, en este proyecto, separamos el fichero applicationContext.xml de Spring en dos archivos, el original applicationContext.xml es para el backend bean y el service bean declarations y el adicional applicationContext-security.xml es sólo para la configuración de Spring Security.

En applicationContext-security.xml, hay dos elementos importantes que tenemos que configurar, que son el elemento <http> y el elemento <authentication-manager>.

Configuración del elemento Http

El elemento <http> es para decirle a Spring qué tipo de recursos necesita ser asegurado, el puerto que será utilizado por contenedor para conexiones http y https, y qué tipo de inicio de sesión en solución se utiliza en esta aplicación web.

<http auto-config="true">

<port-mappings>

<port-mapping http="8080" https="8443"/>

</port-mappings>

<intercept-url pattern="/zkau/**" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any"/>

<intercept-url pattern="/login.zul" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https" />

<intercept-url pattern="/newArticle.zul" access="ROLE_USER" requires-channel="https" />

<intercept-url pattern="/j_spring_security_check" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https" />

<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any" />

<session-management session-fixation-protection="none" />

<form-login login-page="/login.zul"

authentication-failure-url="/login.zul?login_error=1"

login-processing-url="/j_spring_security_check"/>

<logout logout-success-url="/index.zul" invalidate-session="true" />

</http>


Configuración del Authentication Manager

La declaración de elemento <authentication-manager> está diseñado para gestionar authentication-provider, que es el proveedor de la instancia de autenticación de Spring y hará el trabajo real de la autenticación. Puede declarar múltiples authentication-providers, con el fin de aprovechar distintas fuentes de los usuarios (por ejemplo: openidAuthenProvider o LDAPAuthenProvider), en este proyecto se extiende UserDetailsService del authentication-provider predeterminado:

<authentication-manager>

<authentication-provider user-service-ref="myUserDetailsService">

<password-encoder hash="md5" />

</authentication-provider>

</authentication-manager>

<beans:bean id="myUserDetailsService"

class="org.zkoss.demo.springsec.model.MyUserDetailsService"/>

La protección de una solicitud de página

Después de la configuración de Spring Security, ahora, vamos a ver cómo utilizarla para proteger a una solicitud de archivo zul.

Escenario: cuando el usuario solicita un recurso limitado

En nuestro proyecto de demostración, NewArticle.zul es un recurso limitado que sólo pueden acceder los usuarios que ya se ha iniciado sesión, lo que es natural, porque para poder hacer un artículo, tenemos que saber quién es el autor. En Spring Security, para restringir un recurso para cierto usuario (que tiene suficiente permiso) para tener acceso es muy sencillo, simplemente declaramos elemento <intercept-url> bajo <http> en applicationContext-security.xml:

<intercept-url pattern="/newArticle.zul" access="ROLE_USER" requires-channel="https" />

Aquí el atributo patrón se utiliza para determinar qué solicitud esta configuración tendrá efecto, y si la dirección URL de una solicitud coincide con este patrón, el atributo de acceso se utilizará para comprobar autorizaciones del usuario actual.

Como podemos ver, la solicitud de protección de página en Spring Security es muy sencilla e intuitiva, todo se basa en la comparación de patrones de solicitar la ruta url.

Log-in Page Implementation in ZK


Now, after the newArticle.zul has been secured, the log-in page(login.zul) which is required for authentication process has to be implemented for user to perform log in. Here let's see how to implement a log-in page in ZUL:

Implementación de la Página Log-in en ZK

Ahora, después de que el newArticle.zul ha sido asegurado, la página de log-in (login.zul) que se requiere para proceso de autenticación tiene que ser implementada para que usuario pueda acceder:

<html:form id="f" name="f" action="j_spring_security_check" method="POST"

xmlns:html="native">

<grid>

<rows>

<row>User: <textbox id="u" name="j_username"/></row>

<row>Password: <textbox id="p" type="password" name="j_password"/></row>

<row spans="2">

<hbox>

<html:input type="reset" value="Reset"/>

<html:input type="submit" value="Submit Query"/>

<button type="submit" label="Accedi_trendy" mold="trendy"/>

<button type="submit" label="Accedi_os" />

</hbox>

</row>

</rows>

</grid>

</html:form>


  1. Un elemento HTML <form> tiene que ser declarado fuera de las entradas de nombre de usuario y contraseña.
  2. El formulario debe contener dos elementos de entrada denominados j_username y j_passoword. Para aplicar un efecto ajax y las características de estos insumos, se puede utilizar el componente <textbox> en su lugar.
  3. El Atributo action Form tiene que asignar el valor del atributo login-processing-url del elemento <form-login> en applicationContext-security.xml

Si un usuario anónimo, hace clic en el botón Nuevo Artículo en página de inicio, será re-direccionado a nuestro login.zul.

Asegurar Vista parcial mediante el uso de EL en ZUL

Con el fin de asegurar parte de la vista parcial de la página web, Spring Security tiene su propio taglib que brinda soporte básico para el acceso a la información de seguridad y la aplicación de restricciones de seguridad en las páginas JSP. Y aquí la seguridad en ZUL, marco ZK también proporciona métodos para definir la nueva biblioteca de etiquetas. En nuestra aplicación de ejemplo, para el uso de Spring Security a través de EL hicimos nuestra propia biblioteca de etiquetas.

Biblioteca de etiquetas Zul para Spring Security.

Aunque ZK Developer Reference proporciona información muy detallada sobre lo que podemos hacer en un taglib personalizado de ZK, aquí sólo nos centramos en lo que queremos tener para Spring Security.

En primer lugar, vamos a crear un security.tld bajo / WEB-INF /. Sin embargo, para los proyectos más serios, se puede hacer la ruta de clases: Método metainfo / tld / config.xml como se menciona en la Referencia del programador.

En segundo lugar, se definen las funciones de EL / WEB-INF/security.tld, aquí está el ejemplo:

<taglib>

<uri>http://www.zkoss.org/demo/integration/security</uri>

<description>

Methods and actions for ZK + Spring Security

</description>

<function>

<name>isAllGranted</name>

<function-class>org.zkoss.demo.springsec.SecurityUtil</function-class>

<function-signature>

boolean isAllGranted(java.lang.String authorities) {

</function-signature>

<description>

Return true if the authenticated principal is granted authorities

of ALL the specified roles.

</description>

</function>

...

Como podemos ver, el documento debe comenzar con un elemento raíz: <taglib> con un elemento <uri> en ella. A continuación, se puede declarar funciones. En la declaración de la función, mapeamos un método (isAllGranted ()) de org.zkoss.demo.springsec.SecurityUtil a nuestra función EL isAllGranted, sobre la SecurityUtil, la aplicación de la misma se basa en SecurityContextHolder de Spring Security.

Ahora vamos a ver cómo utilizar taglib personalizado Spring Security en ZUL.

Modo de Uso: Protección de Acción

En nuestro proyecto de demostración, de acuerdo con los casos de uso más arriba, tenemos una función "eliminar artículo" que sólo usuario con rol: ROLE_EDITOR puede acceder. Ahora, con nuestras bibliotecas de etiquetas personalizadas para Spring Security, podemos establecer algunas limitaciones de esta manera:

<?taglib uri="/WEB-INF/security.tld" prefix="sec"?>

...

<button id="deleteBtn" label="Delete"

if="${sec:isAllGranted('ROLE_USER')}"

disabled="${not sec:isAllGranted('ROLE_EDITOR')}"/>

...

Atributos Especiales del componente ZK para la Seguridad

En el código de ejemplo anterior, se ha utilizado el atributo personalizado EL + disabled del botón de ZK para proteger el clic del usuario, ZK también ofrece otras características que pueden ser aplicables para la restricción de seguridad tales como visible y de sólo lectura (para combobox). Estos atributos que se basan en efectos html son muy útiles para cumplir con los requisitos de la escalada de permiso para acceder a la interfaz de usuario.

Protegiendo la solicitud de ZK Ajax

En un marco como el Ajax ZK, hay dos tipos de solicitudes. Ahora, vamos a hablar de la parte más difícil de esta integración es decir, cómo hacer frente a la petición de Ajax ZK usando Spring Security?

En primer lugar, tenemos que echar un vistazo a cómo proteger una solicitud de página de una manera más programática.

Haga Security Check en Listener de ZK

En algunas situaciones, puede ser que necesite el usuario para hacer control de seguridad en el código java no en ZUL, por ejemplo, en nuestro accessDeniedExTest.zul, tenemos un iniciador que hacer el registro de seguridad en el método public void doInit(Page page, Map<String, Object> args) throws Exception.

public class AccessDeniedExInit extends GenericInitiator {

public void doInit(Page page, Map<String, Object> args) throws Exception {

if(SecurityUtil.isNoneGranted("ROLE_EDITOR")){

throw new AccessDeniedException("this is a test of AccessDeniedException!");

}

}

}

El AccessDeniedException es clave para la cadena de filtros de Spring Security para manejar, y todo funciona bien en una situación de solicitud de la página, pero lo que si tiramos la excepción en una acción que dice EventListener de ZK?

Chequeo de Seguridad en Acciones de ZK.

Lanzando AccessDeniedException durante una solicitud Ajax no activar el proceso de autenticación de Spring Securit. Esta autorización se debe a la perspectiva del ZK AU Engine, que es imposible para cualquier otro outsider de org.zkoss.zk.au.http. DHtmlUpdateServlet saber cómo componer una respuesta significativa y correctamente estructurada para el motor del cliente en el navegador. Por lo que cualquier excepción que debe ser manejado dentro de ZK AU Engine.

Para hacer esto tenemos que convertir petición Ajax ZK a una petición de una página normal, así que podemos dejar Spring Security para manejar la AccessDeniedException en una solicitud de página normal. la puesta en práctica de esta idea incluye 3 pasos:

PASO 1: Convertir Ajax Request to Request

En primer lugar, tenemos que decirle ZK queremos encargo del mecanismo de control de errores para AccessDeniedException. Para ello es muy simple, en zk.xml añadimos esta sección:

<error-page>

<exception-type>org.springframework.security.access.AccessDeniedException</exception-type>

<location>security_process.zul</location>

</error-page>

Manejo de errores de ZK es muy sencillo, se asigna un tipo de error a una página ZUL, la página ZUL se analizará con base en el escritorio actual, si ocurre tal error. En nuestro security_process.zul simplemente tenemos un iniciador dentro de manejar AccessDeniedException:

<?init class="org.zkoss.demo.springsec.ui.error.SpringSecurityHandleInit"?>

<zk>

<!-- DO NOTHING! -->

</zk>

Luego, en SpringSecurityHandleInit, podemos comprobar si se trata de una petición Ajax, almacenamos todos los datos necesarios en la sesión y le pedimos al motor del cliente para hacer el cambio de dirección de nuevo a este security_process.zul nuevo.

if(exec.isAsyncUpdate(null) ){

//STEP 1: convert Ajax Request to Page Request(Error Handling Page Request)

System.out.println(">>>> Security Process: STEP 1");

if(ex instanceof AccessDeniedException){

sess.setAttribute(VAR_DESKTOP_REQ_URI, getOriginalDesktopUri());// for login-success-url

sess.setAttribute(VAR_SPRING_SECURITY_ERROR, ex);

Executions.sendRedirect(toSecurityProcessUrl((AccessDeniedException) ex));// GOTO STEP 2 by redirection.

}else{

throw new IllegalArgumentException(

"How come an unexpected Exception type will be mapped to this handler? please correct it in your zk.xml");

}

PASO 2: Lanzar AccessDeniedException de redirección para adaptarse Proceso de seguridad Resorte

El cambio de dirección será de nuevo a este security_process.zul nuevo, y porque esta vez estamos dentro de una solicitud de página, se puede obtener la excepción de regresar de HttpSession y tírelo a la basura para que la cadena de filtros de Spring Security para manejarlo.

Exception err = (Exception) sess.getAttribute(VAR_SPRING_SECURITY_ERROR);

String dtPath = (String) sess.getAttribute(VAR_DESKTOP_REQ_URI);

if(err!=null){

//STEP 2: throw Error in Error Handling Page Request.

System.out.println(">>>> Security Process: STEP 2");

sess.removeAttribute(VAR_SPRING_SECURITY_ERROR);

throw err;// we suppose Spring Security Error Filter Chain will handle this properly.

}

Ahora, Spring Security comprobará principio del usuario actual y hacer el proceso de autenticación o autorización. En el proceso de autenticación, el usuario será redirigido a la login.zul como lo hemos configurado, y si log-in es el éxito, el usuario será redirigido a la ubicación original donde se produjo esta excepción y la ubicación aquí será nuestra security_process.zul que es no es el sitio original en el que la petición Ajax causó el error. Así que tenemos el tercer paso para manejar esta situación.

PASO 3: Maneje el Login Success redirección

Vamos a volver a ver el código de ejemplo de paso 1, el código

sess.setAttribute(VAR_DESKTOP_REQ_URI, getOriginalDesktopUri());

reservado la ruta de solicitud original del ZUL que generó el escritorio, y que se prepara la información para STEP 3, la aplicación de la forma de recuperar la ruta original del escritorio (impl de getOriginalDesktopUri ()) es la siguiente:

private static String getOriginalDesktopUri(){

// developer may implement this part to adapt to PushState or any other Page based Framework, that might have interference to request URI.

String str = Executions.getCurrent().getDesktop().getRequestPath();

String qs = Executions.getCurrent().getDesktop().getQueryString();

System.out.println(">>>security Process: Desktop path= "+str);

return str+"?"+qs;

}

Y ahora en el PASO 3, simplemente redirigir usuarios de nuevo a nuestro camino escritorio:




else if(dtPath!=null){

System.out.println(">>>> Security Process: STEP 3");

//STEP 3: if Spring Security Authentication was triggered at STEP 2,

//then we need STEP 3 to redirect back to original URI the very first desktop belongs to.

sess.removeAttribute(VAR_DESKTOP_REQ_URI);

exec.sendRedirect(dtPath);

}

Para más detalles, por favor, eche un vistazo al código fuente de SpringSecurityHandleInit.

Uso: Proteja botón Abrir Editor

Después de tejer ZK petición Ajax al Spring Security, vamos a ver cómo aplicar restricción de seguridad a la acción del usuario en nuestro proyecto de demostración, en primer lugar, en uno de nuestros casos de uso del usuario escenario se le permite editar un artículo si es el autor o tiene 'ROLE_EDITOR , por lo que el botón de editor abierto en ArticleContentViewCtrl para articleContent.zul se implementa como esto:

@Listen("onClick=#openEditorBtn")

public void edit(){

//ownership & permission check.

if(!isOwner() && SecurityUtil.isNoneGranted("ROLE_EDITOR")){

throw new AccessDeniedException(

"The user is neither the author, nor a privileged user.");

}

ArticleEditor editor = new ArticleEditor();

editor.setParent(container);

editor.doHighlighted();

}

Como se muestra, tiramos AccessDeniedException directamente en un detector de eventos, y podemos hacer lo mismo en la acción ZK Ver Modelo:

public class TestVModel {

...

@Command

@NotifyChange("fullName")

public void doChange(){

if(SecurityUtil.isNoneGranted("ROLE_EDITOR")){

throw new AccessDeniedException("you are not an editor!");

}

}

}

Si está utilizando un bean de Spring con anotación @ Secured etiquetado de algunos métodos, cuando el usuario no pasa el control de seguridad, el AccessDeniedException lanzado será tratada de la misma manera.

Resumen

En este artículo, hablamos de cómo utilizar Spring Security en una aplicación web ZK, y también mostró una solución de adaptar petición Ajax ZK de cadena de filtros de Spring Security.

Esperamos que le sea útil y quedamos al tanto de sus comentarios.




{ Leer Más }


viernes, 26 de julio de 2013

Desplazamiento – Scrolling- en Tablet usando el Framework ZK.

clip_image002

Visualmente, la diferencia más notable entre dispositivos de escritorio y los táctiles es que no existen barras de desplazamiento en los dispositivos táctiles [1]. También los usuarios finales no son capaces de desplazarse usando los periféricos, como un ratón o un teclado, el movimiento en sentido vertical se hace mediante una acción de movimiento con el dedo.

ZK 6,5 se centra en esta característica y se han hecho muchas mejoras para hacer que todo funcione bien y sin problemas en los tablets. Este artículo tiene el objetivo de introducirlo en las funcionalidades de movimiento en sentido vertical en los tablets, en cómo los componentes de ZK admiten estas funciones y un ejemplo al final para expresar algunos consejos para la migración de aplicaciones de escritorio hacia los tablets.

Principio

clip_image004

El principio de la barra de desplazamiento es simple, si la altura del contenedor es menor que su contenido, barra de desplazamiento será visible para el desplazamiento, lo que significa que el valor de la altura del recipiente y el contenido decidirá el comportamiento de la barra de desplazamiento.

En el escritorio, la barra de desplazamiento es manejada por el navegador. En la tableta, la barra de desplazamiento está a cargo de ZK, ZK calculará la altura de los componentes, construyendo una barra de desplazamiento personalizada y transformando la acción para que sea fácil realizar el desplazamiento con el dedo.

Soporte de componentes ZK

Los componentes de ZK actúan indistintamente en la PC de escritorio y en el Tablet, con respecto a si una aplicaciones es desplazable o no. Si un componente se puede recorrer en una PC de escritorio, es desplazable en una tableta también. Del mismo modo, si un componente no admite el desplazamiento en una PC escritorio, no sería desplazable en una tableta. Los desarrolladores deben ser conscientes de este problema al migrar un proyecto de PC escritorio para tableta.

En ZK, algunos componentes permite desplazarse de forma predeterminada, algunos desactivan el desplazamiento por defecto. Esta sección le mostrará cómo puede cambiar sus valores por defecto para satisfacer diferentes necesidades.

Lo mismo para la PC escritorio y el Tablet, estos componentes se pueden desplazar de forma predeterminada:

  • Grid
  • Listbox
  • Tree

Para desactivar el desplazamiento, debe establecer el atributo data-scrollable de la siguiente forma:

<listbox xmlns:ca="client/attribute" ca:data-scrollable="false" />

Los siguientes componentes no se pueden desplazar de forma predeterminada para PC o tablet. Para habilitar el desplazamiento, debe establecer los atributos de la siguiente manera:


  • (Borderlayout) Center, East, West, North, South

  • autoScroll="true"

  • Groupbox, Window

  • contentStyle="overflow:auto"

  • PanelChildren, Tabpanel

  • style="overflow:auto"

ZK 6.5 también introduce un componente contenedor ScrollView. El se puede conectar a cualquier componente y proporciona la capacidad de desplazamiento.

Otras cuestiones

En los casos en que el padre y el hijo son componentes desplazables la acción de deslizar será manejada únicamente por el hijo y bloqueando los padres de desplazamiento. Al igual que el ejemplo de abajo, "pie de página" es invisible a los usuarios finales por lo que no sería capaz de desplazarlo.

<borderlayout height="100px">

<center autoscroll="true">

<div vflex="min">

<listbox height="100px">

<listitem forEach="1,2,3,4,5">

<listcell label="${each}"></listcell>

</listitem>

</listbox>

<label>footer</label>

</div>

</center>

</borderlayout>


clip_image005

Tenga en cuenta que componentes que no son desplazable a veces podrán tener desplazamiento porque navegador también proporciona un comportamiento de desplazamiento (antes de iOS 5, debe utilizar dos dedos para desplazarse). En el siguiente ejemplo, aunque <div> es no-desplazable, los usuarios finales pueden todavía desplazarse y ver "pie de página" porque navegador soporta esta funcionalidad.

<zk>

<div style="background-color:green" width="100%" height="2000px" />

<label>footer</label>

</zk>


clip_image006

Ejemplo

Ahora, vamos a analizar un ejemplo y reescribirla para que pueda entender el comportamiento del desplazamiento componente ZK con más detalle y cómo transformar el código existente para trabajar con dispositivos de tableta de manera eficiente.

Primero, lea este código:

<window id="root">

<tabbox width="100%">

<tabs>

<tab label="Demo"/>

</tabs>

<tabpanels><tabpanel>

<window id="inner">

<listbox id="lbx" />

<zscript><![CDATA[

//generate data for listbox

String[] data = new String[50];

for (int i = 0; i < data.length; i++) {

data[i] = "item " + i;

}

org.zkoss.zul.ListModel strset = new org.zkoss.zul.SimpleListModel(data);

lbx.setModel(strset);

]]></zscript>

</window>

</tabpanel></tabpanels>

</tabbox>

</window>


Parece ser normal, que el navegador haba que la barra de desplazamiento y el desplazamiento funcionen bien. Pero puede no funcionar en la tableta. ¿Por qué?

El cuadro de lista en el ejemplo anterior no hace que su propia barra de desplazamiento porque no nos limitamos a la altura del cuadro de lista, por lo que su tamaño se extiende a tantos elementos como sea necesario que en este caso es de 50 artículos. Esto da como resultado el contenido y el recipiente de ser del mismo tamaño y que el navegador sólo hace que la barra de desplazamiento para toda la página, esta página se convierte en un-desplazables porque los usuarios bloques listbox de deslizar toda la página.

clip_image007

Para aclarar la situación, si establece Listbox con width = "50%", el listbox todavía no es desplazables pero el espacio vacío de la derecha que no está cubierto por el listbox desplazable y vuelve cuando deslizar esa área, toda la página se desplaza .

Por supuesto, la asignación de una altura fija al cuadro de lista puede resolver este problema, pero, como modificar el código para que funcionara correctamente en la tableta con diseño flexible?

Solución

En primer lugar, debemos establecer un límite de alturas definida para los componentes de los padres por lo tanto para la ventana y Tabbox fijamos "height =" 100%". Entonces, nos pusimos Listbox VFLEX =" 1 "y dejamos ZK para calcular la altura del Listbox.

<window id="root" height="100%">

<tabbox width="100%" height="100%">

<tabs>

<tab label="Demo"/>

</tabs>

<tabpanels><tabpanel>

<window id="inner" height="100%">

<listbox id="lbx" vflex="1" />

<zscript><![CDATA[

String[] data = new String[50];

for (int i = 0; i < data.length; i++) {

data[i] = "item " + i;

}

org.zkoss.zul.ListModel strset = new org.zkoss.zul.SimpleListModel(data);

lbx.setModel(strset);

]]></zscript>

</window>

</tabpanel></tabpanels>

</tabbox>

</window>

clip_image008

La otra forma equivalente visual es dejar ventanas cuyo id es "interno" para manejar el desplazamiento. Para ello, retire VFLEX del Listbox para darle a tamaño completo y desactivar el desplazamiento de manera que la ventana puede recibir el evento de desplazamiento:

<window id="root" height="100%">

<tabbox width="100%" height="100%">

<tabs>

<tab label="Demo"/>

</tabs>

<tabpanels><tabpanel>

<window id="inner" height="100%" contentStyle="overflow:auto">

<listbox id="lbx" xmlns:ca="client/attribute" ca:data-scrollable="false" />

<zscript><![CDATA[

String[] data = new String[50];

for (int i = 0; i < data.length; i++) {

data[i] = "item " + i;

}

org.zkoss.zul.ListModel strset = new org.zkoss.zul.SimpleListModel(data);

lbx.setModel(strset);

]]></zscript>

</window>

</tabpanel></tabpanels>

</tabbox>

</window>


clip_image009

Conclusión

El mecanismo de desplazamiento es casi la misma cosa entre la PC de escritorio y la tableta. En el pasado, escribiendo la disposición incorrectamente no se producían errores serios de funcionamiento en la PC de escritorio, pero estos malos usos afectaban el funcionamiento de la aplicación en la tableta. Si los desarrolladores siguen los consejos en este artículo al escribir una aplicación, van a disminuir sustancialmente el costo de la migración de PC escritorio hacia tableta.

{ Leer Más }


lunes, 22 de julio de 2013

Construir una interfaz de usuario en ZK con un Richlet.

clip_image002

Una alternativa para no tener que escribir ficheros ZUL que se sirvan bajo petición del usuario, es desarrollar aplicaciones usando Richlet, que es un pequeño programa en Java que construye la interfaz de usuario mediante programación en Java. Cuando un usuario hace una petición mediante una URL que está enlazada en el Richlet, ZK Loader hace responsable al Richlet de la construcción de la interfaz de usuario. Esto les da a los desarrolladores todo el poder a la hora de crear los componentes [1]. La elección entre páginas ZUML y Richlets depende de su preferencia. En este artículo, demostraremos cómo usar un Richlet creando una pequeña aplicación que consistirá en un buscador. Para una explicación más detallada ver: Richlet en la ZK Developer's Reference.

Aplicación de ejemplo

La aplicación de ejemplo que vamos a construir es un simple catálogo de coches. Esta aplicación tiene 2 funciones principales:

  • Buscar coches. Escribir una palabra clave en el campo de texto, hacer clic en el botón de buscar y mostrar los resultados de la búsqueda en la lista contigua.
  • Ver los detalles de un coche. Haciendo clic en uno de los coches de la lista, el área contigua mostrará los detalles del coche seleccionado, incluyendo el modelo, precio, descripción y vista previa.

clip_image003

Implementación

Para implementar un Richlet, tu clase tiene que implementar el interfaz Richelt. A pesar de que, no es necesario implementarla tal cual. En vez de eso puedes extender GenericRichlet y solo sobrescribir el método Richlet.service(Page). Este método será el que se llame cuando se haga una petición al Richlet por parte del usuario.

clip_image004

Para tener mejor control, puedes incluso implementar los métodos Richlet.init(RichletConfig) y Richlet.destroy() para inicializar y destruir cualquier cosa que necesite el Richlet para funcionar.

Creando la Interfaz de Usuario

Una de las funciones principales de un Richlet es crear la interfaz de usuario. Es recomendable empezar leyendo los conceptos básicos sobre interfaz de usuario en ZK antes de crearla mediante Java.

  • El interfaz de usuario en ZK es un árbol de componentes, cada componente tiene un componente padre, y a su vez puede tener o no múltiples componentes hijos.
  • No todos los componentes aceptan como hijos a todos los tipos de componentes que existen, solo a aquellos que se correspondan, o incluso ninguno.

Por ejemplo, un Grid en XUL acepta columnas y Filas como hijos. Para más detalle mirar la documentación al respecto: ZK Component Reference.

clip_image005

Línea 12: Para crear un componente, simplemente lo instanciamos.
Línea 14: Cada atributo de un componente tiene su correspondiente método setter que podemos usar para asignarle el valor correspondiente.
Línea 18: Para establecer una relación Padre-Hijo entre componentes, podemos usar el método appendChild(Component). Esto enlaza el componente indicado en el método, al final de la lista de componentes hijos (que ya tuviera) del componente padre. Hay otros métodos similares para hacer esto como setParent(Component), insertBefore(Component,Component). Puedes encontrar más información al respecto en la documentación

Renderizando el modelo de datos

Después de proveer el objeto Modelo al componente ListBox, normalmente necesitamos indicar cómo mostrar los datos que contiene. Necesitamos crear un objeto Render para ello.

Un Render es una clase Java en la que se indica cómo se va a mostrar cada dato del modelo. Lo creamos implementando la interfaz de tipo Render que corresponda a cada componente, para un Listbox usaremos ListitemRenderer. En la ZK Developer's Reference están la del resto de componentes.

Item renderer de un Listbox

clip_image007

  • En el método render(), nos encargamos de crear el interfaz del usuario que queremos añadiendo los componentes hijos al componente padre Listitem.

Después de crear el Render necesitamos asignarlo al ListBox.

carListbox.setItemRenderer(new CarRenderer());

Event Listener

En nuestra aplicación de ejemplo, un usuario puede hacer click en el botón de "Search" para realizar su búsqueda, tenemos por lo tanto que escuchar el evento "onClick". Podemos resolverlo invocando al método AbstractComponent.addEventListener(String, EventListener) del componente. El primer parámetro es el nombre del evento, y el segundo es un objeto que implemente la interfaz EventListener. En un EventListener, puedes manipular los componentes para añadir tu lógica de la aplicación, como puede ser cambiar atributos de otros componentes, crear nuevos componentes, o eliminar uno existente. Puedes ver todas las características de los componentes en la ZK Component Reference, y ver qué atributos puedes necesitar usar.

clip_image009

Línea 7: Puedes crear una clase a parte que implemente EventListener. En este ejemplo usamos una clase anónima por simplificar. Pero ojo, en un clúster debes implementar la versión serializable SerializableEventListener.
Línea 10: Escribe la lógica de tu aplicación dentro del método onEvent(), cosas como leer un valor con un get, cambiar un atributo de un componente o actualizar información

Otra función del ejemplo es que cuando el usuario selecciona un coche de la lista, mostramos sus detalles en el área de información. Para manejar esto, tenemos que escuchar el evento de seleccionar del componente Listbox, y entonces leer los atributos del coche y asignar los valores a los componentes correspondientes de la vista.

clip_image011

Configuración del Richlet

Hay 2 requisitos para que el Richlet esté disponible para la aplicación cliente.

  1. Activar el soporte de Richlets (en WEB-INF/web.xml)
  2. Mapear la URL (que puede ser estática o un patrón) a la que responderá el Richlet (in WEB-INF/zk.xml)
Activar el soporte de Richlets

Por defecto, los richlets están desactivados. Para activarlos, hay que añadir la siguiente declaracíon en el fichero WEB-INF/web.xml.

<servlet-mapping>
     <servlet-name>zkLoader</servlet-name>
     <url-pattern>/zk/*</url-pattern>

</servlet-mapping>

De este ejemplo, puedes cambiar /zk/* a otro patrón (que corresponda con una URL válida) que tu quieras, como por ejemplo /do/*. Fíjate que no puedes mapear la expresión como si de una extensión de un fichero se tratase (como por ejemplo *.do) o se procesará como una página ZUML en vez de como un Richlet.

Mapear la URL a la que responderá el Richlet

Cada Richlet que implementes, debes declararlo en la configuración WEB-INF/zk.xml de la siguiente forma:

clip_image012

Línea 6: Después de definir el Richlet, puedes mapearlo bajo tantas URLs como quieras mediante el elemento de configuración richlet-mapping

Puedes visitar http://localhost:8080/PROJECT_NAME/zk/search y ver tu Richlet. La URL especificada en el parámetro url-pattern debe empezar siempre con "/".

Si la URL termina en /*, se asocia a cualquier petición con el mismo prefijo. Para recibir la URL de la petición actual puedes chequear el valor que retorna el método getRequestPath de la página en la que te encuentres.

clip_image013

Conclusiones

ZK es tan versátil que provee de múltiples opciones para que seas usted quien elija cómo construir la Interfaz de usuario, o incluso varias al mismo tiempo, dependiendo de tus preferencias o entorno. El código fuente usado en el artículo puedes descargarlo aquí.

{ Leer Más }


jueves, 11 de julio de 2013

Paginador de datos usando ZK.

En el ejemplo que hemos estado usando para mostrar las facilidades de desarrollo que se tienen con el framework ZK se ha implementado un típico Caso de Uso CRUD (Create, Read, Update, Delete) con las operaciones básicas, como pueden ver en la siguiente imagen.

clip_image002

Como pueden ver mientras más datos vamos insertando el scroll se incluye automáticamente y nos facilita el ir viendo los datos, pero esta solución no es efectiva cuando manejamos miles o millones de tuplas por las siguientes razones:

· No existe un mecanismo de paginado que nos permita ir mostrando la información de una manera amigable y no toda a la vez.

· Cada actualización de la información incluye una petición al servidor y por lo tanto al SGBD devolviendo toda la información actualizada.

Para resolver el primer punto ZK incluye un mecanismo automático del lado del cliente que nos permite mostrar un componente para movernos por cada uno de las páginas. Veamos cómo se ve:

clip_image004

Como pueden ver se ha agregado un componente para el paginado que nos permite irnos moviendo por las distintas páginas, cada una con 5 elementos a mostrar.

El código modificado es en la página .zul como se muestra a continuación.

clip_image006

Me interesa que vean como en la definición del componente listbox incluimos la propiedad mold=”paging” y especificamos que está ubicado encima del listado usando la propiedad pagingPosition=”top” y el tamaño de la página definiendo la propiedad pageSize=”5”.

De esta manera se resuelve el primer punto pero igual sigue quedando el problema del segundo punto ya que ZK carga todos los elementos, determina su cantidad y en función de esa información nos muestra el componente, y como decíamos no es rentable si cada vez que se vaya a realizar una acción se tienen que obtener todos los datos desde el servidor hacia el cliente y pueden ser solo algunas decenas de tuplas, pero cuando son miles o millones el rendimiento general del sistema se ve fuertemente comprometido.

La variante que se propone para resolver este problema es que solo obtengamos del servidor la página que queremos mostrar y nada más. Para eso debemos hacer modificaciones en:

· La forma de obtener los datos del servidor.

· En la clase viewmodel que accede al modelo y notifica a la vista.

· En la vista para incluir el componente paging que estaremos usando.

En el modelo debemos incluir 2 métodos:

· Un método que nos de la cantidad de elementos en la BD para poder calcular la cantidad de páginas.

· Un método que dado una posición inicial y la cantidad de elementos a mostrar nos devuelva el listado, y eso será la página a mostrar en la vista.

La interface queda como sigue:

clip_image007

En azul aparecen señalados los 2 métodos agregados, y su implementación es la siguiente:

clip_image008

En GenericDAO la implementación es la siguiente:

clip_image010

Vean como para obtener una página pasamos la clase, una variable offset que nos indica la posición actual y una limit que nos determina la cantidad de elementos a obtener. Entonces creamos una sesión y construimos una query, definiendo la consulta y los parámetros del primer resultado y la cantidad de resultados a devolver.

Para la operación de obtener la cantidad de elementos en BD llamamos a un método que nos devuelve la cantidad de elementos en la tabla persona, dejándole al SGBD realizar esta actividad y solo obteniendo el valor.

En la clase viewmodel los cambios son los siguientes:

clip_image011

Como pueden ver agregamos 3 atributos nuevos para mantener los valores del tamaño de la página, cual página tenemos activa, y el valor total de registros en la tabla de la BD. Además de sus get/set métodos claro.

Otro cambio fue en el siguiente método:

clip_image012

Como pueden para obtener el listado de personas, que ya no son todas si no una página, debemos llamar a getPaginaPersona() y pasarle el valor del offset y del limit.

Otros métodos que cambiamos fueron setPageactive y getTotalsize()

clip_image013

Al primero le añadimos la anotación @NotifyChange para indicarle a la vista que se cambió la página que está activa de lo contrario no verán el cambio en la UI,

Al segundo solo le agregamos la llamada al método que nos devuelve la cantidad de registros en BD.

En la vista solo agregamos una línea y quitamos otra. O sea eliminamos el componente paging dentro del listbox y creamos uno aparte:

clip_image015

Con estos pequeños cambios ya tenemos un paginado funcional que optimiza el consumo de recursos en las llamadas a la BD.

La UI se ve como se muestra a continuación.

clip_image017

Les dejo adjunto el código fuente con las modificaciones realizadas.

Para el paginado del lado del cliente la página es index2.zul, y para el paginado del lado del servidor es index3.zul.

NOTA: ¿Qué pasará cuando intentemos buscar dentro de los registros, con esta nueva modificación? La solución a lo que pasa en otra entrada.

{ Leer Más }


IconIconIcon