Slide 1

Slide 1 text

PlugIn Tapestry Desarrollo de aplicaciones y páginas web con Apache Tapestry @picodotdev http://picodotdev.github.io/blog-bitix/ https://github.com/picodotdev #tapestry5

Slide 2

Slide 2 text

Libro PlugIn Tapestry Presentación del libro Página de descarga Contiene todo lo de esta presentación, más ampliado además de otras muchas cosas (no solo de Tapestry) ¡Más de 250 páginas, en español y al precio de 0,00€!

Slide 3

Slide 3 text

Definición Apache Tapestry es un framework orientado a componentes para crear aplicaciones web rápidas, dinámicas, robustas y altamente escalables en la plataforma Java. ➔ Componentes: pieza de software reutilizable ➔ Rápidas: inicio, ejecución, … ➔ Dinámicas: comportamiento variable ➔ Robustas: no fallos simples ➔ Escalables: aplicación pequeña convertible a grande

Slide 4

Slide 4 text

Características ➔ Compilador ◆ 0 errores de “compilación”, no malos merges con conflicto ◆ 100% código analizado ➔ IDE ◆ Refactors ◆ Asistente código ◆ Feedback instantáneo ➔ Políglota ◆ Groovy, Scala, …, cualquier lenguaje JVM Java

Slide 5

Slide 5 text

Características ➔ Libertad de elegir ◆ Centrado en hacer bien una parte ➔ No encadenados al framework ➔ Se puede conseguir ◆ Inicio rápido con arquetipo maven ◆ Entorno integrado para desarrollo ● Servidor embebido que inicie aplicación ● Ejecución de teses unitarios y de integración No fullstack

Slide 6

Slide 6 text

Características ➔ Pieza reutilizable de código / DRY ◆ En mismo proyecto, misma página, diferentes proyectos ➔ Caja negra ◆ Encapsulación lógica ◆ Para usar uno solo necesitamos conocer parámetros, no los CSS, JS, imágenes, literales, … necesarios ◆ El componente define sus recursos, Tapestry los incluye ➔ 3 tipos: páginas, componentes y mixins ◆ Mismos conceptos ➔ Pueden lanzar eventos ◆ Mismo evento comportamiento diferente según contenedor Basado en componentes

Slide 7

Slide 7 text

➔ Fácil reutilización => Alta productividad ➔ Productividad a base de reutilización ◆ No copiar y pegar ➔ Todo componente es reutilizable ◆ Varias veces en una misma o distintas páginas ➔ Librerías de componentes ◆ En diferentes proyectos ◆ Muchos proyectos pequeños ◆ Varios proyectos grandes ◆ Librería de componentes == jar, basta con incluir dependencia Características Alta reutilización y productividad

Slide 8

Slide 8 text

Características ➔ Las URLs de páginas y eventos las crea Tapestry ◆ Refactors ◆ Actualizaciones ➔ URLs de páginas siguen esquema RESTful ◆ Sin fichero de “mapeo” ➔ Los datos de un formulario se dejan en objetos ◆ Propiedades (componente/entidad/POJO) ◆ Parámetros de método ➔ Datos en sesión a través de propiedades (anotación) Objetos y propiedades (no URLs y parámetros)

Slide 9

Slide 9 text

Características ➔ Solución propia ➔ Unidad básica, servicio (interfaz + implementación) ➔ Inyección por tipo (refactors) ➔ Adaptable, flexible, modular ◆ Configuración nueva, redefinir y añadir servicios, decorar, reemplazar, dependencias mutuas ➔ Distribuido ◆ No hay archivo de configuración único ◆ Basta con incluir dependencia (con un módulo) ◆ Java: rápido, compilador, refactor, IDE, más adecuado, … ➔ Módulos para Hibernate, Shiro, ... Contenedor IoC

Slide 10

Slide 10 text

Características ➔ Sin herencia ◆ Metaprogramación, anotación, convención, inyección ◆ Testable ◆ Actualizaciones ➔ Componente define, framework llama ➔ Convención sobre configuración o anotaciones POJO

Slide 11

Slide 11 text

Características ➔ Ajax ◆ Cero javascript ◆ 1 petición actualiza múltiples zonas ◆ Actualizaciones parciales, sin “microgestión”* ➔ RequireJS ◆ Dependencias cliente, no polución, carga asíncrona ➔ CoffeeScript ➔ LESS/sass ➔ Minimización y agregación Soporte cliente

Slide 12

Slide 12 text

Características ➔ 10 años de conocimiento ➔ Suficiente para aprender de forma autodidacta ➔ Como consulta ➔ Guía inicio rápido ➔ Tutoriales ➔ Libros ➔ Javadoc ➔ Aplicación con ejemplos (JumpStart) ◆ Código completo ejecutable ◆ Contiene casi todo lo que podemos necesitar Documentado

Slide 13

Slide 13 text

Características ➔ No dar información clara para resolver un problema se considera un bug ➔ Inicio aplicación ➔ Informe de error ◆ No solo la traza de la excepción ◆ También en peticiones Ajax ➔ Página Dashboard ◆ Estado páginas ◆ Estado servicios ◆ Estadísticas Hibernate Informativo

Slide 14

Slide 14 text

Características ➔ Diferentes opciones ◆ A nivel de aplicación, componente o mixto ➔ Assets localizables ➔ Plantillas localizables Soporte internacionalización (i18n) y localización (l10n)

Slide 15

Slide 15 text

Características ➔ Redirect After Post (todas las URLs son añadibles a marcadores) ➔ Instrumentalización invisible ◆ No “destrucción” html, colaboración diseñadores <=> programadores ➔ Componentes lanzan eventos (comportamiento diferente según contenedor) ➔ Plantillas xml bien formado ◆ 0 errores “compilación” con página Dashboard ➔ Parámetros con tipo (conversión transparente y extensible) ➔ Muchos componentes built-in (Ajax, beans, condiciones y bucles, controles de formulario, grid, árboles, enlaces y botones, de salida y mensajes) ◆ Algunos complejos como grid y árboles Otras

Slide 16

Slide 16 text

Arquitectura aplicaciones Presentación Servicios (lógica de negocio) DAO (persistencia) Base de datos Spring Hibernate Servidor de aplicaciones Tapestry RequireJS Bootstrap LESS Sass CoffeeScript Cliente

Slide 17

Slide 17 text

Modelo Vista Controlador (MVC) Tapestry (basado en componentes) Basados en acciones (Grails, Play!, Symfony, Django, .NET MVC, Struts, Spring MVC) ➔ Controlador [con plantilla] = 1 componente ➔ Vista puede acceder al controlador = lógica solo en controlador (código java), no duplicación ➔ Modelo «pull» (vista tira) ➔ Controlador más plantilla(s) = N piezas a unir ➔ Vista no accede al controlador = lógica en vistas (scriptlets, condiciones if, “set”), duplicación ➔ Modelo «push» (controlador empuja) ➔ Controlador debe conocer los datos que necesita la vista => No tan independientes

Slide 18

Slide 18 text

Estructura de aplicación

Slide 19

Slide 19 text

Ejemplo componente (I) (Sin plantilla) package es.com.blogspot.elblogdepicodev.plugintapestry.components; ... public class NumeroProductos { @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "Hay %d productos") String mensaje; @Inject ProductoDAO dao; // @BeginRender boolean beginRender(MarkupWriter writer) { long numero = dao.countAll(); writer.write(String.format(mensaje, numero)); return false; } } -> Hay X productos Hay N productos -> Hay N productos -> Hay X productos -> Hay X productos que quizá te interesen

Slide 20

Slide 20 text

Ejemplo componente (II) (Con plantilla) package es.com.blogspot.elblogdepicodev.plugintapestry.components; … public class NumeroProductos { @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "Hay %d productos") String mensaje; @Inject ProductoDAO dao; public String getTextoMensaje() { return String.format(mensaje, dao.countAll()); } } ${textoMensaje}

Slide 21

Slide 21 text

Ejemplo componente (II) package es.com.blogspot.elblogdepicodev.plugintapestry.components; … public class NumeroProductos { @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "Hay %d productos") String mensaje; @Inject ProductoDAO dao; public String getTextoMensaje() { return String.format(mensaje, dao.countAll()); } } ${textoMensaje} Página con muchos datos ¿Push?

Slide 22

Slide 22 text

Fases de renderizado Anotaciones y convenciones Métodos de ciclo de vida del componente http://tapestry.apache.org/component-rendering.html

Slide 23

Slide 23 text

Peticiones de página es/com/blogspot/elblogdepicodev/plugintapestry/pages/admin/ProductoAdmin.java http://localhost/admin/producto/1 http://tynamo.org/tapestry-routing+guide public class ProductoAdmin { @Inject ProductoDAO dao; @Property private Producto producto; void onActivate(Long id) { producto = dao.findById(id)); } Object[] onPassivate() { return new Object[] { producto.getId() }; } }

Slide 24

Slide 24 text

Peticiones de evento ➔ Convención manejador evento onEvento[FromComponente] o anotaciones. ➔ Posibles retornos manejadores: Respuesta nula, cadena, clase, instancia página, error HTTP, stream (fichero), objeto enlace (Link), URL ➔ Redirect After Post (Petición evento => redirect => Petición página) Sumar uno a la cuenta public class Index { @Property @Persist(value = PersistenceConstants.SESSION) private Long cuenta; void onActionFromSumarCuenta1() throws Exception { cuenta += 1; } }

Slide 25

Slide 25 text

Componentes disponibles Componentes específicos para Ajax (AjaxFormLoop, AddRowLink, RemoveRowLink, ProgressiveDisplay, Zone). Mostrado y edición de beans (BeanDisplay, BeanEditForm, BeanEditor, PropertyDisplay, PropertyEditor). Condicionales y de bucle (If, Case, Loop, Unless, Delegate). Controles de formulario (Checkbox, Checklist, DateField, Form, FormFragment, FormInjector, Hidden, Label, KaptchaField, KaptchaImage, Palette, PasswordField, Radio, RadioGroup, Select, SubmitNotifier, TextArea, TextField, Upload). Grid, tablas y árboles (Grid, GridCell, GridColumns, GridPager, GridRows, GridRows). Enlaces y botones (ActionLink, EventLink, LinkSubmit, Submit, PageLink). De salida y mensajes (Alerts, Dynamic, Error, Errors, ExceptionDisplay, Output, OutputRaw, TextOutput). Mixins (Autocomplete, DiscardBody, FormFieldFocus, NotEmpty, OverrideFieldFocus, RenderClientId, RenderDisabled, RenderInformals, RenderNotification, TriggerFragment, ZoneRefresh). Páginas de Tapestry (ExceptionReport, PageCatalog, PropertyDisplayBlocks, PropertyEditBlocks, ServiceStatus, Dashboard). Componentes base (AbstractComponentEventLink, AbstractConditional, AbstractField, AbstractLink, AbstractPropertyOutput, AbstractTextField, BaseMessages) Diversos (Any, Doctype, RenderObject, Trigger). Referencia Más componentes en librerías de terceros.

Slide 26

Slide 26 text

Inyecciones ➔ Cualquier servicio (propio, de Tapestry, Spring) ➔ De cualquier módulo ➔ Los servicios se referencian por tipo (no por nombre) public class ProductoAdmin { @Inject private ProductoDAO dao; @Inject private AjaxResponseRenderer renderer; }

Slide 27

Slide 27 text

Configuración IoC (Módulo) public class AppModule { public static void bind(ServiceBinder binder) { binder.bind(ProductoDAO.class, ProductoDAOImpl.class); } public static void contributeFileServiceDispatcher(MappedConfiguration configuration) { configuration.add("txt", new TextFileService()); configuration.add("pdf", new PDFFileService()); } public static FileServiceDispatcher buildFileServicerDispatcher(Map contributions) { return new FileServiceDispatcherImpl(contributions); } public static void contributeApplicationDefaults(MappedConfiguration configuration) { configuration.add(SymbolConstants.PRODUCTION_MODE, false); } }

Slide 28

Slide 28 text

Conversiones ➔ Grafo ➔ Navegables ➔ Extensible ➔ Automáticas, al haber diferencia entre tipo parámetro y tipo esperado ➔ Parámetros componente => Con coercers ➔ De cliente (String) a servidor (Objeto) y de servidor a cliente => Con translators ➔ Soporte para entidades Hibernate => Con encoders

Slide 29

Slide 29 text

Extendiendo conversiones (Coercer) // Módulo public static void contributeTypeCoercer(Configuration configuration) { Coercion coercion = new Coercion() { public Money coerce(BigDecimal input) { return new Money(input); } }; configuration.add(new CoercionTuple(BigDecimal.class, Money.class, coercion)); } ➔ Conversiones en parámetros de componentes

Slide 30

Slide 30 text

Extendiendo conversiones (Translator) public class DateTranslator extends AbstractTranslator { private String patron; public DateTranslator(String patron) { super("date", Date.class, "date-format-exception"); this.patron = patron; } @Override public String toClient(Date value) { if (value == null) { return null; } return new SimpleDateFormat(patron).format(value); } @Override public Date parseClient(Field field, String clientValue, String message) throws ValidationException { if (clientValue == null) { return null; } try { return new SimpleDateFormat(patron).parse(clientValue); } catch (ParseException e) { throw new ValidationException(MessageFormat.format(message, field.getLabel())); } } @Override public void render(Field field, String message, MarkupWriter writer, FormSupport formSupport) {} } // Módulo public static void contributeTranslatorSource(MappedConfiguration configuration) { configuration.add(Date.class, new DateTranslator("dd/MM/yyyy")); } ➔ Conversiones de cliente a servidor y de servidor a cliente

Slide 31

Slide 31 text

Assets ➔ Cualquier tipo de contenido estático que puede ser descargado a un cliente. ➔ Son internacionalizables (i18n) ➔ Minimizado de assets automático (css, javascript, less/sass/coffeescript, hash) en modo producción ◆ configuration.add(SymbolConstants.PRODUCTION_MODE, true); Banner @Import(stylesheet = "context:css/site.css") public class MiComponente { @Inject @Path("context:images/tapesty.png") private Asset banner; }

Slide 32

Slide 32 text

Stacks @Import(stack = { "core", "MiStack" }) public class MiPagina { ... } public class MiStack implements JavaScriptStack { public String getInitialization() { … } public List getModules() { … } public List getJavaScriptLibraries() { … } public List getStylesheets() { … } public List getStacks() { … } } ➔ Agregación de assets

Slide 33

Slide 33 text

Módulos RequireJS ➔ No javascript incrustado en el html ➔ Todas las ventajas de RequireJS (gestión dependencias en cliente, no polución, asíncrono) ➔ Soporte para inicialización de módulos RequireJS public class Saludador { // Propiedades selector, mensaje … @Environmental private JavaScriptSupport support; void setupRender() { JSONObject o = new JSONObject(); o.put("selector", selector); o.put("mensaje", mensaje); support.require("app/saludador").invoke ("init").with(o); } } define("app/saludador", ["jquery"], function($) { function Saludador(spec) { this.spec = spec; } Saludador.prototype.render = function(){ $(this.spec.selector).html(this. spec.mensaje); } function init(spec) { new Saludador(spec).render(); } return { init: init } });

Slide 34

Slide 34 text

Componentes formularios Datos enviados desde el cliente y enviados al cliente. Form Hidden TextField Label Checkbox Radio TextArea * FormFragment * FormInjector * KaptchaField * KaptchaImage PasswordField Select DateField Checklist RadioGroup Upload * Palette * SubmitNotifier

Slide 35

Slide 35 text

Envío de datos ➔ Datos en las propiedades de objetos y componentes (no en la request) ➔ Convertidos al tipo de la propiedad ➔ En caso de error (validación y conversión), decoración automática de los elementos de formulario public class ProductoAdmin { @Property private Producto producto; } @Entity public class Producto implements Serializable { @NotNull @Min(value = 0) @Max(value = 1000) @Column(name = "cantidad") private Long cantidad; }

Slide 36

Slide 36 text

Eventos formularios Eventos del componente Form: prepareForRender prepare prepareForSubmit validate failure o success submit onEvento[FromComponente] public class ProductoAdmin { @Inject private ProductoDAO dao; @Property private Producto producto; void onPrepareForSubmitFromForm(Long id) { if (id != null) { // Si se envía un id se trata de una edición, buscarlo producto = dao.findById(id); } if (producto == null) { producto = new Producto(); } } void onSuccessFromForm() { dao.persist(producto); setModo(Modo.LISTA, null); } }

Slide 37

Slide 37 text

Validaciones en formularios Declarativa en html Centralizadas en el modelo Bean Validation API (JSR 303) @Entity public class Producto implements Serializable { @Id @GeneratedValue private Long id; @NotNull @Min(value = 0) @Max(value = 1000) @Column(name = "cantidad") private Long cantidad; } Colaboración Bean Validation API y validaciones Tapestry

Slide 38

Slide 38 text

Macros de validaciones public static void contributeValidatorMacro(MappedConfiguration configuration) { configuration.add("password","required,minlength=5,maxlength=15"); } // Clase componente @Validate("password") private String password;

Slide 39

Slide 39 text

Personalización mensajes error Convenciones formId-fieldId-validatorName-message fieldId-validatorName-message form-nombre-required-message=El nombre del usuario es requerido. ssn-regexp=\d{3}-\d{2}-\d{4} ssn-regexp-message=Social security numbers are in the format 12-34-5678.

Slide 40

Slide 40 text

Aplicación ejemplo ➔ Rápido ◆ Inicio aplicación ➔ Componentes: HolaMundo, HolaMundoTemplate, HolaMundoAjax ➔ Recarga en caliente ➔ RequireJS Organización javascript ➔ Informativo ◆ Inicio ◆ Exception report ◆ Dashboard ● 0 errores en plantillas por “compilación” ● Estado páginas, servicios, Hibernate ● Problema SQL N+1 https://github.com/picodotdev/elblogdepicodev/tree/master/PlugInTapestry ➔ Soporte Ajax Actualización múltiples zonas en 1 petición, con 0 javascript Exception report ➔ Aplicación inicial con arquetipo Maven ◆ $MAVEN_HOME/bin/mvn archetype:generate - DarchetypeCatalog=https://repository.apache. org/content/repositories/staging ➔ Pruebas unitarias y de integración ➔ Agregación y minimización (modo producción) ◆ Módulos ◆ Javascript ➔ Mixin N-click ➔ Más ejemplos en JumpStart

Slide 41

Slide 41 text

¿Preguntas?

Slide 42

Slide 42 text

¡Gracias! :) @picodotdev http://picodotdev.github.io/blog-bitix/ https://github.com/picodotdev

Slide 43

Slide 43 text

Licencia Este obra está bajo una licencia de Creative Commons Reconocimiento- CompartirIgual 4.0 Internacional.