El framework Apache Tapestry para el desarrollo de aplicaciones Java basadas en componentes es un de los proyectos que sigo desde hace mucho tiempo. Hasta el día de hoy no he encontrado y probado nada mejor en lo que he visto con diferencia para el propósito de desarrollar aplicaciones web en el que el HTML es generado en el lado del servidor. Está basado en componentes y se consigue una alta productividad y reutilización en el mismo proyecto y diferentes proyectos. En marzo del 2020 se publicó la versión 5.5.0.
La tendencia de las aplicaciones actuales es desarrollar la parte del cliente con tecnologías basadas en JavaScript como React, Vue, Angular o incluso Web Components que hagan uso de APIs ya sean en REST o GraphQL en el lado del servidor que solo transmitan los datos y todo el renderizado del HTML se haga en el lado del cliente. Sin embargo, esto tiene sus inconvenientes como que la página parece que se carga en diferentes pasos, el tiempo de carga inicial es mayor y el JavaScript en grandes proyectos adolece de los mismos problemas de los lenguajes no tipados salvo que se use TypeScript. Para algunos proyectos renderizar el HTML en el lado del servidor es una opción más sencilla y todos estos frameworks con ciertos años siguen siendo válidos.
En la medida de mis posibilidades he buscado formas de contribuir al proyecto Apache Tapestry aún sin tener la posibilidad de usar el proyecto profesionalmente. Una de ellas ha sido escribir un libro en español y totalmente gratis con muchos de los artículos que he escrito sobre el framework. Esto es algo relacionado pero externo al proyecto.
Con el paso del tiempo y continuando siguiendo el proyecto otras formas en las que he contribuido y ahora sí al proyecto es el arquetipo de Maven que permite iniciar una aplicación de Apache Tapestry rápidamente. Y más recientemente la actualización de la página web del proyecto junto con el artwork del logotipo.
Página web del proyecto
Entre esas cosas que consideraba mejorables era la página web del proyecto y documentación. Con el paso del tiempo el diseño de la misma estaba desfasado, no era adaptable para dispositivos móviles y tiene algunos errores como que la búsqueda personalizada de Google no funcionaba y algunos enlaces rotos. Otra mejora ha sido redirigir el tráfico del protocolo http a https con JavaScript.
He teneido que conocer como se generaba el HTML de la página y cuál era el proceso de publicación. El contenido se edita en Confluence, se exporta con un script y unas plantillas y el resultado es incorporado en un repositorio de Subversion desde el cual es servido con algún servidor web.
Con mi experiencia con Hugo me planteaba reescribir la forma de edición del sitio web, sin embargo, esto es una tarea más grande que requiere reemplazar una buena parte de la infraestructura existente, como cambiar de Subversion a Git, reescribir la página web exportando el contenido existente de HTML a markdown, no imposible pero requeriría más trabajo, y sustituir el flujo de trabajo actual por otro que permita la edición del contenido desde un dispositivo móvil y que disponga de despliegue continuo cuando se hagan cambios como ocurre desde la edición actual con Confluence.
Debido a la mayor complejidad de los cambios y que la persona del proyecto encargada de la página web y documentación requería que el proceso siguiese siendo parecido pudiendo editar desde un dispositivo móvil la opción ha sido mejorar lo actual aún sabiendo las limitaciones de Confluence y el proceso actual. Con esta opción sabía que únicamente podría mejorar el diseño, para corregir algunos otros fallos es necesario hacer los cambios editando el contenido en Confluence.
El resultado ha sido este, antes y después. El actual lo puedes ver en la página web de Tapestry.
Artwork del logotipo
Al mismo tiempo que la mejora de la página web he mejorado el logotipo y generado un nuevo artwork. Después de recopilar las diferentes versiones logotipos y viendo las diferencias de cada una de ellas en tipo de fuente, formas del icono, colores, etc he creado uno nuevo seleccionado lo que he considerado lo mejor de cada uno de ellos. La forma del unicornio de uno, los elementos de fondo del icono de otro, el ojo del unicornio, la fuente del texto, cambiado el color de la palabra apache en vez de gris a negro de modo que en resoluciones bajas y fondo claro a resoluciones bajas se siga leyendo correctamente, añadido la mosca del trademark, ™ para cumplir con algunos requerimientos de la fundación Apache para proteger sus marcas. En las versiones originales unicamente ofrecen un fondo claro en las nuevas también una versión para fondos oscuros.
La edición la he realizado con editor de imágenes vectorial Inkscape y mi intención ha sido generar no solo un logotipo sino varias versiones. Viendo los archivos SVG originales en alguno había un intento de logotipo para fondo oscuro aunque nunca he visto un logotipo de Tapestry generado para este caso así es posible que ni siquiera las personas del proyecto supiesen que existía.
He generado varias versiones fondos claros, para fondos oscuros, con icono y texto, solo el icono y solo el texto. He simplificado y eliminado muchos de los degradados del icono y efectos. Como fuentes he utilizado las originales que conocía, Trebuchet MS y Georgia, son fuentes propietarias de Microsoft pero tienen un aspecto muy bueno y son las fuentes con las que conocía el logotipo del proyecto. El resultado ha sido el siguiente.
Como las combinaciones de logotipo son muchas exportar todas esas versiones manualmente desde el SVG original me iba a requerir mucho tiempo con lo que he aprovechado la funcionalidad del Inkscape por línea de comandos para automatizar la exportación del SVG. Esto permite mostrar y ocultar capas, ajustar el SVG al contenido visible y exportar a PNG por ejemplo para generar las diferentes versiones de los iconos.
El resultado es una colección de más de 50 imágenes en formato PNG y SVG entre las que un usuario pueda elegir la mejor entre sus preferencias sin tener que realizar ninguna edición.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
#!/usr/bin/env bash
set -e
DIRECTORY=`dirname $0`
BASENAME="apache-tapestry"
ARTWORK_DIR="artwork"
ARTWORK_FILE="$ARTWORK_DIR/$BASENAME-artwork.svg"
cd $DIRECTORY
rm -R $ARTWORK_DIR
mkdir -p $ARTWORK_DIR
cp "$BASENAME.svg" "$ARTWORK_FILE"
inkscape --verb LayerShowAll --verb FileSave --verb FileQuit "$ARTWORK_FILE"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject -w 300 --export-png="$ARTWORK_DIR/$BASENAME-icontext-300.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject -w 600 --export-png="$ARTWORK_DIR/$BASENAME-icontext-600.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject -w 800 --export-png="$ARTWORK_DIR/$BASENAME-icontext-800.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-icontext-1200.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject --export-background white -w 300 --export-png="$ARTWORK_DIR/$BASENAME-icontext-300-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject --export-background white -w 600 --export-png="$ARTWORK_DIR/$BASENAME-icontext-600-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject --export-background white -w 800 --export-png="$ARTWORK_DIR/$BASENAME-icontext-800-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightObject --export-background white -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-icontext-1200-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkObject --export-background black -w 300 --export-png="$ARTWORK_DIR/$BASENAME-icontext-300-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkObject --export-background black -w 600 --export-png="$ARTWORK_DIR/$BASENAME-icontext-600-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkObject --export-background black -w 800 --export-png="$ARTWORK_DIR/$BASENAME-icontext-800-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkObject --export-background black -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-icontext-1200-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 32 --export-png="$ARTWORK_DIR/$BASENAME-icon-32.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 57 --export-png="$ARTWORK_DIR/$BASENAME-icon-57.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 72 --export-png="$ARTWORK_DIR/$BASENAME-icon-72.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 114 --export-png="$ARTWORK_DIR/$BASENAME-icon-114.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 144 --export-png="$ARTWORK_DIR/$BASENAME-icon-144.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 300 --export-png="$ARTWORK_DIR/$BASENAME-icon-300.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 600 --export-png="$ARTWORK_DIR/$BASENAME-icon-600.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 800 --export-png="$ARTWORK_DIR/$BASENAME-icon-800.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-icon-1200.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 32 --export-png="$ARTWORK_DIR/$BASENAME-icon-32-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 57 --export-png="$ARTWORK_DIR/$BASENAME-icon-57-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 72 --export-png="$ARTWORK_DIR/$BASENAME-icon-72-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 114 --export-png="$ARTWORK_DIR/$BASENAME-icon-114-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 144 --export-png="$ARTWORK_DIR/$BASENAME-icon-144-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 300 --export-png="$ARTWORK_DIR/$BASENAME-icon-300-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 600 --export-png="$ARTWORK_DIR/$BASENAME-icon-600-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 800 --export-png="$ARTWORK_DIR/$BASENAME-icon-800-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightIconObject --export-background white -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-icon-1200-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 32 --export-png="$ARTWORK_DIR/$BASENAME-icon-32-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 57 --export-png="$ARTWORK_DIR/$BASENAME-icon-57-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 72 --export-png="$ARTWORK_DIR/$BASENAME-icon-72-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 114 --export-png="$ARTWORK_DIR/$BASENAME-icon-114-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 144 --export-png="$ARTWORK_DIR/$BASENAME-icon-144-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 300 --export-png="$ARTWORK_DIR/$BASENAME-icon-300-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 600 --export-png="$ARTWORK_DIR/$BASENAME-icon-600-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 800 --export-png="$ARTWORK_DIR/$BASENAME-icon-800-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkIconObject --export-background black -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-icon-1200-dark.png"
#(cd $ARTWORK_DIR/ && for f in *.png; do convert -colorspace Gray -strip "$f" -set filename:f "%t-gray.%e" %[filename:f]; done;)
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject -w 300 --export-png="$ARTWORK_DIR/$BASENAME-text-300.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject -w 600 --export-png="$ARTWORK_DIR/$BASENAME-text-600.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject -w 800 --export-png="$ARTWORK_DIR/$BASENAME-text-800.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-text-1200.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject --export-background white -w 300 --export-png="$ARTWORK_DIR/$BASENAME-text-300-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject --export-background white -w 600 --export-png="$ARTWORK_DIR/$BASENAME-text-600-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject --export-background white -w 800 --export-png="$ARTWORK_DIR/$BASENAME-text-800-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id lightTextObject --export-background white -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-text-1200-light.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkTextObject --export-background black -w 300 --export-png="$ARTWORK_DIR/$BASENAME-text-300-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkTextObject --export-background black -w 600 --export-png="$ARTWORK_DIR/$BASENAME-text-600-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkTextObject --export-background black -w 800 --export-png="$ARTWORK_DIR/$BASENAME-text-800-dark.png"
inkscape "$ARTWORK_FILE" --export-id-only --export-id darkTextObject --export-background black -w 1200 --export-png="$ARTWORK_DIR/$BASENAME-text-1200-dark.png"
(cp "$ARTWORK_FILE" "$ARTWORK_DIR/$BASENAME-icon-light.svg" && inkscape "$ARTWORK_DIR/$BASENAME-icon-light.svg" --verb LayerHideAll --verb DialogLayers --verb LayerToggleHide --verb FitCanvasToDrawing --verb FileSave --verb FileQuit)
(cp "$ARTWORK_FILE" "$ARTWORK_DIR/$BASENAME-icon-dark.svg" && inkscape "$ARTWORK_DIR/$BASENAME-icon-dark.svg" --verb LayerHideAll --verb DialogLayers --verb LayerPrev --verb LayerToggleHide --verb FitCanvasToDrawing --verb FileSave --verb FileQuit)
(cp "$ARTWORK_FILE" "$ARTWORK_DIR/$BASENAME-text-light.svg" && inkscape "$ARTWORK_DIR/$BASENAME-text-light.svg" --verb LayerHideAll --verb DialogLayers --verb LayerPrev --verb LayerPrev --verb LayerToggleHide --verb EditDeselect --verb FitCanvasToDrawing --verb FileSave --verb FileQuit)
(cp "$ARTWORK_FILE" "$ARTWORK_DIR/$BASENAME-text-dark.svg" && inkscape "$ARTWORK_DIR/$BASENAME-text-dark.svg" --verb LayerHideAll --verb DialogLayers --verb LayerPrev --verb LayerPrev --verb LayerPrev --verb LayerToggleHide --verb FitCanvasToDrawing --verb FileSave --verb FileQuit)
(cp "$ARTWORK_FILE" "$ARTWORK_DIR/$BASENAME-icontext-light.svg" && inkscape "$ARTWORK_DIR/$BASENAME-icontext-light.svg" --verb LayerHideAll --verb DialogLayers --verb LayerPrev --verb LayerPrev --verb LayerPrev --verb LayerPrev --verb LayerToggleHide --verb FitCanvasToDrawing --verb FileSave --verb FileQuit)
(cp "$ARTWORK_FILE" "$ARTWORK_DIR/$BASENAME-icontext-dark.svg" && inkscape "$ARTWORK_DIR/$BASENAME-icontext-dark.svg" --verb LayerHideAll --verb DialogLayers --verb LayerPrev --verb LayerPrev --verb LayerPrev --verb LayerPrev --verb LayerPrev --verb LayerToggleHide --verb FitCanvasToDrawing --verb FileSave --verb FileQuit)
rm "$ARTWORK_FILE"
rm "$BASENAME-artwork.zip"
zip "$BASENAME-artwork.zip" ./generate-logotype-artwork.sh "$BASENAME.svg"
zip "$BASENAME-artwork.zip" -r $ARTWORK_DIR --include "*.png" --include "*.svg"
|
generate-logotype-artwork.sh
Artefacto de Maven quickstart
En una contribución anterior había realizado cambios en el artefacto de Maven que permite generar un esqueleto de aplicación que hace uso de Tapestry. Y simplemente no funcionaba, además de usar una librería para iniciar un Tomcat embebido más antigua.
Las mejoras que he realizado ha sido refactorizar el artefacto de Maven, usar Spring Boot para iniciar la aplicación, añadir Gradle como herramienta de construcción y algunos teses de ejemplo. El nuevo artefacto está incluido en la versión 5.5.0.
1
2
3
4
5
6
7
8
9
|
$ mvn archetype:generate -B \
-DarchetypeGroupId=org.apache.tapestry \
-DarchetypeArtifactId=quickstart \
-DarchetypeVersion=5.5.0 \
-DgroupId=com.foo \
-DartifactId=foo \
-Dpackage=com.foo \
-Dversion=1.0
$ ./gradlew run
|
quickstart.sh
Conclusión
Los que trabajan en proyecto open source tienen mucho conocimiento deben tenerlo si son capaces de construir un proyecto que en algunos casos utilizan miles de desarrolladores y empresas pero su tiempo es limitado, ni en muchos casos es su trabajo principal y en algunos aspectos no tienen por qué saber más que tú en todo. Contribuir a un proyecto es sencillo solo requiere tiempo y algo de conocimiento para conocer que cambios hay que realizar, con interés y tiempo poco a poco uno va conociendo más detalles del proyecto que permiten realizar contribuciones como estas.
Estas contribuciones no aportan código al proyecto pero la documentación y página web es también importante como muestra del estado del proyecto. Si usase a diario una aplicación con Apache Tapestry en el trabajo es posible que pudiese realizar algunas contribuciones al código fuente del framework pero sin tener esa posibilidad mi motivación para realizarlas no es tan alta.
Libro PlugIn Tapestry
Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.