sábado, 28 de noviembre de 2015

Aplicaciones Web Java usando Gradle y el plugin Gretty

Introducción

En un post anterior (el cual se recomienda leer antes de continuar) se mostró cómo se pueden desarrollar aplicaciones web usando Gradle y su plugin oficial Jetty. Aunque tiene algunos beneficios, también se evidenciaron las falencias que aún tiene dicho plugin.

Es por ello que en esta ocasión se mostrará cómo usar el plugin Gretty, el cual también permite el desarrollo de aplicaciones web con Java y Gradle, pero con varias características que lo hacen una alternativa moderna y mucho más práctica que el plugin Jetty.

La principal ventaja que tiene Gretty es que soporta Jetty Embebido en sus versiones 7, 8 y 9 e inclusive también soporta Tomcat Embebido 7 y 8, por lo cual pueden usarse funcionalidades más recientes y las especificaciones Servlet 3 y JSP 2.2. Las versiones exactas de los servidores y especificaciones soportadas se pueden encontrar en el archivo "gradle.properties" de Gretty.

Al igual que en aquel post anterior, se desarrollará un pequeño proyecto web con dos páginas JSP y un Servlet, pero esta vez usando Jetty 9 y Servlet 3.

El proyecto completo se puede descargar desde: https://github.com/guillermo-varela/webapp-gradle-gretty-example

Dependencias y Configuración Gradle

build.gradle

plugins {
  id 'net.researchgate.release' version '2.0.2'
}

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'

sourceCompatibility = 1.7
targetCompatibility = 1.7

task wrapper(type: Wrapper) {
    gradleVersion = '2.8'
}

repositories {
    jcenter()
}

dependencies {
    providedCompile "javax.servlet.jsp:jsp-api:2.2"
    providedCompile "javax.servlet:javax.servlet-api:3.0.1"
    providedCompile "javax.el:el-api:2.2"
    compile "javax.servlet:jstl:1.2"
}

En general este archivo es muy similar al que se tenía en el post sobre el plugin Jetty, con la diferencia de que en la línea 8 se aplica el plugin Gretty y en la sección de dependencias se usan las versiones actualizadas.

Por defecto Gretty usa Jetty 9, por lo cual no hace falta indicarlo explícitamente, pero por ejemplo si se quisiera usar Tomcat 8 lo único que tendría que hacerse es agregar el siguiente fragmento a "build.gradle":

gretty {
  servletContainer = 'tomcat8'
}

Actualmente los valores permitidos son: jetty7, jetty8, jetty9, tomcat7 y tomcat8

gradle.properties

version=1.0.0-SNAPSHOT

Servlet

ExampleServlet.java

Simplemente pondrá un objeto Date en la petición procesada e indicará que se muestre la página "example.jsp"

package com.blogspot.nombre_temp.webapp.gradle.jetty.example;

import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "exampleServlet", urlPatterns = {"/example"})
public class ExampleServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("currentDate", new Date());
        request.getRequestDispatcher("example.jsp").forward(request, response);
    }
}

La anotación "WebServlet" fue introducida en la especificación Servlet 3.0 para permitir indicar como se debe ejecutar el Servlet de manera equivalente al Descriptor de Despliegue (Deployment Descriptor), el cual ya no es requerido para aplicaciones y servidores que soporten esta nueva versión de la especificación. Aquí el atributo "urlPatterns" indica que recibirá peticiones que lleguen a la ruta "/example", por ejemplo http://localhost:8080/webapp-gradle-gretty-example/example.

Para ver otros cambios en Servlet 3.0 puede consultarse: https://community.oracle.com/docs/DOC-983211

Contenido Web

Con Gretty también se requiere crear manualmente la carpeta "src/main/webapp/" y a su vez dentro de esta se crearán los siguientes archivos:

index.jsp

Será una página sencilla para verificar que la aplicación está funcionando.
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false" %>
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8" />
<title>Webapp Gradle Gretty Example</title>
</head>

<body>
    <h1>
        Hello World
    </h1>
</body>
</html>

example.jsp

Mostrará la fecha y hora a partir del objeto Date enviado por "ExampleServlet" usando el formato indicado en el tag JSTL.

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Webapp Gradle Gretty Example</title>
</head>

<body>
    <h1>
        Current Date: <fmt:formatDate value="${currentDate}" pattern="yyyy-MM-dd HH:mm:ss" />
    </h1>
</body>
</html>

Nota: quienes usan Eclipse y aplicaron el plugin "eclipse-wtp" debe asegurarse de que la carpeta "src/main/webapp/" sea incluida en "Deployment Assembly", lo cual puede hacerse manualmente haciendo clic derecho en el proyecto y seleccionando "Properties" -> "Deployment Assembly" o actualizando el proyecto Gradle usando "Buildship" o "Gradle Integration for Eclipse". En caso de que aparezcan errores cargando el "Deployment Descriptor" (web.xml), basta con cerrar el proyecto y abrirlo de nuevo en Eclipse.

Figura 1 - Deployment Assembly con la carpeta webapp

Ejecutar la Aplicación

Desde Eclipse

Quienes usan Eclipse y aplicaron el plugin "eclipse-wtp" pueden usar exactamente las mismas instrucciones indicadas en el post anterior.

Desde Gretty

Gretty tiene dos pares de tareas que permiten ejecutar la aplicación:

  • "appRun" y "appRunWar", las cuales deben ejecutarse desde la línea de comandos (terminal) ya que se inician en modo interactivo (desde una carpeta o el WAR generado correspondientemente), con lo cual al presionar "Enter" se detiene el servidor, sin necesidad de ejecutar otra tarea.
./gradlew appRun
:prepareInplaceWebAppFolder
:createInplaceWebAppFolder
:compileJava
:processResources
:classes
:prepareInplaceWebAppClasses
:prepareInplaceWebApp
:appRun
22:58:29 INFO  Jetty 9.2.10.v20150310 started and listening on port 8080
22:58:29 INFO  webapp-gradle-gretty-example runs at:
22:58:29 INFO    http://localhost:8080/webapp-gradle-gretty-example
Press any key to stop the server.
> Building 87% > :appRun

  • "appStart" y "appStartWar", las cuales pueden ejecutarse no sólo desde la línea de comandos, sino también desde el IDE que se esté usando. Para detener el servidor se deberá ejecutar la tarea "appStop".
./gradlew appStart
:prepareInplaceWebAppFolder
:createInplaceWebAppFolder
:compileJava
:processResources
:classes
:prepareInplaceWebAppClasses
:prepareInplaceWebApp
:appStart
23:04:35 INFO  Jetty 9.2.10.v20150310 started and listening on port 8080
23:04:35 INFO  webapp-gradle-gretty-example runs at:
23:04:35 INFO    http://localhost:8080/webapp-gradle-gretty-example
Run 'gradle appStop' to stop the server.
> Building 87% > :appStart

Figura 2 - JSP con la hora actual desde el Servlet señalando la versión de Jetty

Como puede verse en la figura 2, la aplicación funciona igual a su contra-parte que usaba el plugin Jetty, pero ahora puede verse que se está usando Jetty 9.

Depurando la Aplicación (Debug)

En caso de usar un servidor configurado en Eclipse, pueden usar exactamente las mismas instrucciones indicadas en el post anterior.

Gretty sí tiene una diferencia con respecto al plugin Jetty y es que sí incluye dos tareas para iniciar el servidor en modo debug, sin necesidad de tener que crear la variable de entorno "GRADLE_OPTS", y corresponden a los equivalentes de las tareas para ejecutar la aplicación: "appRunDebug", "appRunWarDebug", "appStartDebug" y "appStartWarDebug":

./gradlew appStartDebug
:prepareInplaceWebAppFolder
:createInplaceWebAppFolder
:compileJava
:processResources
:classes
:prepareInplaceWebAppClasses
:prepareInplaceWebApp
:appStartDebug
Listening for transport dt_socket at address: 5005

Estas tareas inician la aplicación y la suspenden mientras se conecta un proceso de debug al puerto 5005. Para más información sobre cómo iniciar dicho proceso desde Eclipse puede consultarse la sección "Depurar una aplicación Java (Debug)" del post sobre "Gradle Integration for Eclipse" o sobre "Buildship".

Para detener la aplicación puede presionarse "Enter", en caso de usar una tarea en modo interactivo o ejecutando la tarea "appStop".

Generando el WAR

Dado que aún se usa el plugin War, la generación del archivo WAR se realiza ejecutando la tarea de Gradle "build", lo cual creará el archivo en la carpeta "build/libs".

Generar un producto

Una característica adicional que tiene Gretty es la posibilidad de generar un producto, lo cual consiste en un conjunto de archivos, librerías y scripts que permiten ejecutar directamente la aplicación sin necesidad de desplegar el WAR en un contenedor web. El producto puede generarse usando la tarea de Gradle "buildProduct".

./gradlew buildProduct
:compileJava
:processResources
:classes
:war
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
:buildProduct

BUILD SUCCESSFUL

Total time: 15.335 secs

Al finalizar se tendrá una carpeta con el nombre del proyecto en "build/output"

├───build/
│   ├───output/
│   │   └───webapp-gradle-gretty-example/
│   │       ├───conf/
│   │       ├───runner/
│   │       ├───starter/
│   │       ├───temp/
│   │       ├───webapps/
│   │       └───webapps-exploded/
│   │       └───restart.bat
│   │       └───restart.sh
│   │       └───run.bat
│   │       └───run.sh
│   │       └───start.bat
│   │       └───start.sh
│   │       └───stop.bat
│   │       └───stop.sh
│   │       └───VERSION.txt

Los scripts "run" y "start" (.bat o .sh según el sistema operativo) permiten iniciar el contenedor web  embebido configurado para el proyecto (Jetty 9 en este caso). El primer script inicia el servidor en una consola interactiva permitiendo finalizarlo al presionar "Enter", mientras que el segundo requiere ejecutar el script "stop".

Nota: En caso de que al iniciar el servidor e intentar acceder a la aplicación web aparezca el error "PWC6345: There is an error in invoking javac. A full JDK (not just JRE) is required", se debe verificar que el ejecutable "java" usado por el sistema operativo es el del JDK. En Windows puede usarse el comando "where java", mientras que en sistemas Linux y Mac "which java", y corregir la variable de entorno "PATH" según corresponda.

Conclusiones

Como pudo verse Gretty no solamente tiene más funcionalidades que el plugin Jetty de Gradle, sino que es más fácil de usar (no requiere variables de entorno para el modo debug), por ende es preferible el uso de Gretty.

Aquí sólo se mostraron las funcionalidades más básicas de Gretty, por lo cual se recomienda consultar su documentación oficial para revisar algunas más avanzadas como por ejemplo el soporte para HTTPS, seguridad en Jetty y Tomcat o el soporte para múltiples aplicaciones web (farm).

Como se indicó al inicio, las versiones exactas de Jetty y Tomcat vienen pre-definidas en el plugin, por lo cual aunque se garantiza que todos los desarrolladores usarán la misma versión del contenedor web, esta puede no coincidir con la que se tenga en el ambiente de producción.

Quienes usen Tomcat y quieran garantizar el uso de la misma versión en todos los ambientes, tienen la opción de usar "gradle-tomcat-plugin" aunque de momento no se tiene soporte completo para Tomcat 8: https://github.com/bmuschko/gradle-tomcat-plugin/issues/73

Por lo demás Gretty es un plugin bastante completo y cumple con las necesidades para desarrollar fácil y cómodamente aplicaciones web con Java y Gradle.

Referencias

No hay comentarios.:

Publicar un comentario