Tobago

RECU-0136 (Recurso Referencia)

Descripción

Tobago es mucho más que una librería de etiquetas. Las siguientes afirmaciones caracterizan a Tobago y lo hacen diferente de otros frameworks.

  • La finalidad de Tobago es crear aplicaciones de negocio sin la necesidad de un diseño HTML. El desarrollo de páginas con Tobago se parece más al diseño de interfaces de usuario convencionales que al diseño de páginas webs.
  • Los componentes de la Interfaz de Usuario son abstracciones del HTML y cualquier diseño de información que haga, no pertenece a la estructura general de la página. El formato final de salida es determinado por el cliente.
  • Un mecanismo basado en temas hace fácil cambiar la apariencia y permite proveer implementaciones especiales para ciertos navegadores. Una solución basada en la readaptación ante fallos nos asegura la reusabilidad de gran parte del código para nuevos temas.
  • Se usa un gestor de diseño para organizar los componentes de manera automática. Esto quiere decir, que no hace falta un diseño manual con HTML para tablas u otros componentes.

Modo de empleo

Una aplicación con Tobago es una aplicación web estándar y necesita un descriptor (WEB-INF/web.xml). Como aplicación JSF, el archivo web.xml tiene que incluir una definición para el servlet FacesServlet y el correspondiente mapeo. Es conveniente que durante el desarrollo, los recursos que van a ser usados, como imágenes, scripts y hojas de estilos estén fuera del jar del tema. Para lograr esto, el archivo web.xml debe contener la definición de ResourceServlet y el correspondiente mapeo.

Una página con Tobago, tiene el siguiente aspecto:

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://myfaces.apache.org/tobago/component" prefix="tc" %>
<%@ taglib uri="http://myfaces.apache.org/tobago/extension" prefix="tx" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<f:view>
  <tc:page>
    <f:facet name="layout">
      <tc:gridLayout/>
    </f:facet>
    <tc:out value="Hola Mundo"/>
  </tc:page>
</f:view>

Características

A continuación se resumen las características principales de Tobago:

Layout

Tobago organiza la colocación de los componentes con la ayuda de los administradores de Layout. El controlador de distribución principal se llama diseño de cuadrícula. Se divide el espacio rectangular disponible en las células de la red. La rejilla es generada por la columna y los valores de fila de la etiqueta <tc:gridLayout>. La sintaxis de estos valores se basa en la multilongitud de la notación que se hace en HTML.

Para añadir un controlador de distribución a un contenedor como la caja, el panel o la página se tiene que agregar una faceta de diseño (es decir, una faceta con el nombre de 'diseño') a la etiqueta de contenedores respectivos.

<tc:panel> 
   <f:facet name="layout">
     <tc:gridLayout columns="*" rows="fixed;fixed;*"/>
   </ f: facet>
   <tx:in Name"/> label="Nombre"
   <tx:in Name"/> label="Apellido"
   <tc:cell />
</ tc: Panel>

En este ejemplo ponemos a dos controles de entrada con etiquetas en dos filas consecutivas. Por debajo de los dos campos de entrada se añade un elemento separador. El token de diseño de Layout 'fixed' indica al administrador de layout que la altura de las filas será la fijada por el tema seleccionado.

Los valores de la columna y fila de los atributos de la etiqueta <tc:gridLayout> pueden contener una lista de valores separada por punto y coma. Los valores a incluir pueden ser una razón de longitud exacta en píxeles como 200px, con una longitud en porcentaje como 25%, con una longitud relativa como 2 , o una longitud específica "fixed" que será determinada por el tema seleccionado y asegura que el control es útil. Un único control de entrada de línea, por ejemplo, tiene que ser tan alto que los caracteres de la fuente asignada se puedan leer dentro del control.

La longitud relativa se determina pasado por el controlador de distribución. El espacio que queda disponible se distribuye entre las longitudes existentes relativas. El gestor de diseño se encarga de los atributos empleados en la renderización de los controles. Si un atributo es modificado de forma dinámica, el número de controles mostrado en la página puede cambiar.. El controlador de distribución se puede distribuir el espacio disponible entre los controles existentes en cada momento.

Marcado

Puesto que no tenemos control directo sobre el diseño sin tener que escribir un tema propio, Tobago apoya el concepto de marcado. Puede asignar determinados valores de marcado lógico a un control para ajustar la representación. Un tema especifica el marcado de cada control. El tema norma ya establece algunos márgenes de utilidad.

...
<tc:label value="Normal"/>
<tc:out value="999.99"/>

<tc:label value="Number"/>
<tc:out markup="number" value="999.99"/>

<tc:label value="Emphasized"/>
<tc:out markup="strong" value="999.99"/>

<tc:label value="Emphasized Number"/>
<tc:out markup="number,strong" value="999.99"/>
...

Renderizado parcial

Para evitar la recarga de pantalla completa Tobago establece el renderizado parcial, que indica al cliente que actualice sólo partes de la pantalla para optimizar la cantidad de datos enviados al cliente y el tiempo para hacer las actualizaciones necesarias. Algunos controles, como el control de ficha y la hoja de apoyo, soportan el renderizado parcial. Los controles del tipo contenedor permiten que su conteniod sea renderizado como un grupo.

Formularios Virtuales

La etiqueta de página "form" establece un formulario implícito formado por  todos los controles en la pantalla. La etiqueta "tc:form" permite dividir estos controles en pequeños formularios para poder de gestionar la validación sólo para estos controles agrupados. Un ejemplo de código sería el siguiente:

<tc:form> 
   <TX: etiqueta selectOneChoice = "# () bundle.footerLanguage"
       value = "# () controller.language">
     <f:selectItems value="#{controller.languages}"/>
     <f:facet name="change">
       <tc:command action="#{controller.languageChangedList}"/>
     </ f: facet>
   </ tx: selectOneChoice>
</ tc: form>

Seguridad

La extensión módulo de seguridad de Tobago permite proteger las referencias a métodos con la ayuda de anotaciones. El módulo proporciona comandos alternativos a los componentes que permiten manejar la seguridad. Las anotaciones disponibles son: @ RolesAllowed, DenyAll @ y @ PermitAll. Un ejemplo de código sería el siguiente:

public class AdminController ( 

   @ RolesAllowed ( "admin")
   admin public String () (
     OUTCOME_ADMIN retorno;
   )

   ...
)

Buenas prácticas y recomendaciones de uso

Manejo de Errores

A la hora de usar Tobago para manejar errores es una práctica extendida utilizar alguna de las tres formas siguientes:

  • Se lanza una excepción en la aplicación y el contenedor de servlet se dirige a una página definida en el fichero web.xml.
  • El manejador del navegador no se refiere a ninguna página existente. El código de error 404 en la web.xml es afectado.
  • El manejador del navegador se dirige a una página con un error de sintaxis.

A continuación mostramos el código de un fichero jsp con las tres posibilidades:

<%@ taglib uri="http://myfaces.apache.org/tobago/component"
prefix="tc" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %>
<layout:overview>
  <jsp:body>
    <tc:box label="Sample Error Scenarios">
      <f:facet name="layout">
        <tc:gridLayout rows="35px;35px;35px;*" columns="*;100px" />
      </f:facet>
      <tc:out value="An exception is thrown in the application. The
servlet container forwards to a page defined in the web.xml." />
      <tc:button action="#{bestPracticeController.throwException}"
label="Application" />
      <tc:out value="The navigation handler refers to a non existing
page. The error code 404 in the web.xml is affected." />
      <tc:button action="404" label="Not Found" />
      <tc:out value="The navigation handler refers to a page with a
syntax error." />
      <tc:button action="syntax" label="Syntax Error" />
      <tc:cell />
      <tc:cell />
    </tc:box>
  </jsp:body>
</layout:overview>

Para ver el funcionamiento de las tres opciones remitimos al lector a la siguiente dirección donde hay un ejemplo de este funcionamiento:

http://tobago.atanion.net/tobago-example-demo/faces/overview/intro.jsp;jsessionid=FB66FE1B8C082543A23DBD8612DFA5A5

Temas

En Tobago es fácil dar a una aplicación una buena apariencia de diseño. Podemos usar temas predefinidos como “speyside” o “scarborough”. Lo único que tenemos que hacer es configurar los temas en el lugar correcto. Esto debe hacerse en el fichero tobago-config.xml para definir un tema por defecto. Por ejemplo:

<tobago-config>
  <theme-config>
    <default-theme>speyside</default-theme>
  </theme-config>
  <resource-dir>tobago</resource-dir>
</tobago-config>

Transiciones

Para hacer transiciones de una página a otra tenemos dos posibilidades. En la primera, la transición se hace desvaneciendo la página actual y mostrando una barra de carga. En la segunda la transición es mas brusca, pasando de una página a la otra directamente después de haberse cargado la segunda en nuestra máquina. A continuación mostramos el código jsp de ambas opciones.

<%@ taglib uri="http://myfaces.apache.org/tobago/component"
      prefix="tc" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %>
<layout:overview>
  <jsp:body>
    <tc:box label="Transitions between pages">
      <f:facet name="layout">
        <tc:gridLayout rows="fixed;fixed;fixed;*" />
      </f:facet>
      <tc:out value="Prevent double-clicks" />
      <tc:button label="Sleep 5 s (transition=true)"
      action="#{transitionController.sleep5s}"/>
      <tc:button label="Sleep 5 s (transition=false)"
      action="#{transitionController.sleep5s}" transition="false"/>
      <tc:cell />
    </tc:box>
  </jsp:body>
</layout:overview>

Ejemplos

A continuación pasamos a mostrar el código necesario para visualizar en nuestra aplicación algunos de los componentes que aporta Tobago:

Para que en nuestra página se nos renderice un botón, no tenemos más que definir el siguiente fuente:

<tc:button label="Borrar" action="#{controller.delete}" 
    image="imagenes/borrar.png" defaultCommand="false">
  <f:facet name="confirmation">
    <tc:out value="¿Realmente me quieres borrar?" />
  </f:facet>
</tc:button>

Al agregar al código del botón la etiqueta f:facet se obliga a que, al pulsar sobre el botón, el navegador pida confirmación de la acción que se va a ejecutar. Si se pulsa el botón "Ok" se ejecutará la acción demandada.

Esta estructura es equivalente para los enlaces.

Se puede utilizar un control genérico tc:command para capturar eventos en los controles tc:selectBooleanCheckbox, tc:selectOneRadio, tc:selectManyCheckbox, y tc:selectOneChoice. A continuación se muestra un ejemplo para que vaya al servidor tras la pulsación de los temas disponibles:

<tx:selectOneChoice label="#{bundle.footerTheme}" value="#{controller.theme}">
  <f:selectItems value="#{controller.themeItems}" />
  <f:facet name="change">
    <tc:command action="#{controller.themeChanged}"/>
  </f:facet>
</tx:selectOneChoice>

Tablas

El componente tc:sheet permite mostrar datos de forma tabular. Podemos ver un ejemplo en el que se muestra como almacenar una agenda:

<tc:sheet columns="1*;1*;1*" value="#{controller.currentAddressList}"
    var="address" state="#{controller.selectedAddresses}"
    sortActionListener="#{controller.sheetSorter}" rows="25"
    showRowRange="left" showPageRange="right" showDirectLinks="center">
  <tc:column id="firstName" label="#{bundle.listFirstName}" sortable="true"
      rendered="#{controller.renderFirstName}">
    <tc:out value="#{address.firstName}" />
  </tc:column>
  <tc:column id="lastName" label="#{bundle.listLastName}" sortable="true"
      rendered="#{controller.renderLastName}">
    <tc:out value="#{address.lastName}" />
  </tc:column>
  <tc:column id="dayOfBirth" label="Birthday" sortable="true"
      rendered="#{controller.renderDayOfBirth}">
    <tc:out value="#{address.dayOfBirth}">
      <f:convertDateTime pattern="#{bundle.editorDatePattern}" />
    </tc:out>
  </tc:column>
</tc:sheet>

El atributo value nos devuelve una lista desde el controlador utilizado que contiene la lista con los datos. El tag usado tc:sheet es la raíz de un árbol con tres columnas identificadas por tc:column.

  • En el tag sheet se define una variable local address la cual identificará cada una de las filas que nos ha devuelto la lista con los resultados.
  • En cada columna se mostrará en la cabecera el valor de la etiqueta label de cada columna.

Pestañas

El control tc:tabGroup renderiza lo que conocemos como pestañas, es decir, vistas de distintos paneles en la misma página. Para la implementación de este control, el proyecto Tobago ha utilizado Ajax para cambiar el contenido interno de los paneles.

<tc:tabGroup switchType="reloadTab" immediate="true">
  <tc:tab label="#{bundle.editorTabPersonal}">
    <jsp:include page="tab/personal.jsp"/>
  </tc:tab>

  <tc:tab label="#{bundle.editorTabBusiness}" rendered="#{!controller.simple}">
    <jsp:include page="tab/business.jsp"/>
  </tc:tab>

  <tc:tab label="#{bundle.editorTabMisc}" rendered="#{!controller.simple}">
    <jsp:include page="tab/misc.jsp"/>
  </tc:tab>
</tc:tabGroup>

En este ejemplo se han introducido unas condiciones para la visualización de la segunda y tercera pestaña, utilizando para ello el atributo rendered.

Otros controles

  • Para que la aplicación desarrollada con Tobago tenga la apariencia de una aplicación de escritorio se puede usar el control denominado tc:menu, con este control y otros que mostramos a continuación conseguimos la visualización de un menú.
<tc:menuBar id="menuBar">
  <tc:menu label="_File">
    <tc:menuItem label="_New" action="#{controller.createAddress}" image="image/org/tango-project/tango-icon-theme/16x16/actions/contact-new.png"/>
    <tc:menuItem label="_Add Dummy Addresses"
        action="#{controller.addDummyAddresses}"/>
    <tc:menuSeparator/>
    <tc:menuItem label="_Logout" image
        ="image/org/tango-project/tango-icon-theme/16x16/actions/system-log-out.png"/>
  </tc:menu>

  <tc:menu label="_Settings">
    ...
    <tc:menu label="_Theme">
      <tx:menuRadio action="#{controller.themeChanged}"
          value="#{controller.theme}">
        <f:selectItems value="#{controller.themeItems}"/>
      </tx:menuRadio>
    </tc:menu>
    <tc:menuCheckbox label="Simple _Mode" value="#{controller.simple}"/>
  </tc:menu>
  ...
</tc:menuBar>
  • Si queremos implementar en nuestra página una especie de grupo de botones a modo de barra de herramientas, lo conseguimos con el control tc:toolbar
<tc:toolBar iconSize="big">
  <tc:toolBarCommand label="#{bundle.toolbarAddressList}"
      action="#{controller.search}" immediate="true" image=
 "image/org/tango-project/tango-icon-theme/32x32/mimetypes/x-office-address-book.png"
      disabled="#{facesContext.viewRoot.viewId == '/application/list.jsp'}"/>
  ...
</tc:toolBar>
  • Si tenemos la necesidad de subir algún fichero, usaremos el control tc:file. El archivo que se va a subir será alojado dentro de un atributo de tipo FileItem dentro del paquete commons-fileupload
<tc:file value="#{controller.uploadedFile}" required="true">
  <tc:validateFileItem contentType="image/*"/>
</tc:file>

Con el controlador tc:validateFileItem se establece un filtro para que el usuario no pueda subir un fichero que no sea imagen.