Java EE 8 - MVC 1.0

MVC es una nueva característica que vendrá en Java EE 8. No reemplaza al JSF, ya que es otra filosofía. JSF está basado en estados de la aplicación, mientras que MVC es justamente la implementación del patrón Modelo Vista Controlador. Uno mismo va a crear el Modelo, también la vista (generalmente JSP) y el controlador.

Cuando comencemos a verlo, notaremos que tiene un parecido a Spring MVC (para ver una pequeña introducción de Spring MVC, puedes ver el post anterior: Conociendo Spring MVC. La diferencia es que este está más Java EE, con más anotaciones que archivos de configuración, y más Oracle.


MVC está especificado en el JSR 371. Está basado en JAX-RS, por lo que veremos que se parece muchísimo a los servicios RESTful, además que se puede incluir tecnologías Java EE como CDI y Bean Validation.

Existe una implementación de este JSR y es el proyecto Ozark.

Software utilizado

Para este post he utilizado
  • NetBeans 8.1
  • GlassFish 4.1.1
  • Maven (que ya viene incluido en el IDE)

Creando el proyecto

Para probar este framework, crearemos nuestro proyecto web con Maven, y debemos considerar la siguiente dependencia.
    <dependencies>
        <dependency>
            <groupId>com.oracle.ozark</groupId>
            <artifactId>ozark</artifactId>
            <version>1.0.0-m01</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


Como vemos, se puede trabajar tranquilamente sobre Java EE 7.

Y, como se mencionó anteriormente, está basado en JAX-RS (RESTful), por lo que las notaciones nos serán muy naturales. ¿Quieres conocer más sobre RESTful? Mira en la sección "Series" y verás la colección de post relacionados con RESTful.

Por tanto, necesitamos configurar la aplicación principal del RESTful: Creamos una clase que extiende a javax.ws.rs.core.Application y con la anotación javax.ws.rs.ApplicationPath indicando el subcontexto raíz de la aplicación MVC.
package com.apuntesdejava.demomvc;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("main-app")
public class Main extends Application{
    
}


Por ahora no necesitamos más que esa definición, sin más contenido.

La clase controladora

Nuevamente, necesitamos usar las notaciones REST tales como javax.ws.rs.Path y javax.ws.rs.GET, además de otras anotaciones.

Miremos la siguiente clase:
package com.apuntesdejava.demomvc.controller;

import javax.mvc.Controller;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;


@Path("/saludo")
@Controller
public class HolaController {
    @GET
    public String hola(){
        return "/WEB-INF/jsp/saludo.jsp";
    }
}

La línea 10 donde se declara la notación @javax.mvc.Controller indica que este servicio REST se comportará como un controlador de la aplicación bajo la ruta "/saludo" que está declarada en la línea 9. El método hola de la línea 13 se ejecutará cuando sea invocado vía método GET, tal como está declarado en la línea 12.

Esto ya nos hace la idea de que para este controlador podemos tener los cuatro métodos HTTP como GET, POST, DELETE y UPDATE. Además, podemos tener subrutas de este controlador por cada método, ya que en cada método se le pondrá otras anotaciones javax.ws.rs.Path para indicar su ubicación dentro del controlador.

Vemos también en la línea 14 que devuelve una ruta, que es el jsp que será la vista de respuesta del controlador. Aquí también podemos pensar que podemos poner cualquier JSP, y no necesariamente tiene que ser la misma página de respuesta (ahora vemos que la filosofía de JSF es diferente de la de MVC).

Creando la vista

Ahora, crearemos la vista. Esta será un JSP común y corriente. Luego le pondremos más cosas.
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

Ejecutando la aplicación

Ahora, para poder ejecutarlo, necesitamos crear un HTML que invocará a nuestra aplicación MVC. En la raíz del módulo web pondremos el siguiente index.html.
<!DOCTYPE html>
<html>
    <head>
        <title>Start Page</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h1>Ejemplo de MVC</h1>
        <a href="main-app/saludo">Hola mundo</a>
    </body>
</html>

Y al ejecutarlo, nos mostrará esta página.



Y al hacer clic en el enlace, nos mostrará esto. Notar el URL


Es más natural, más fácil de entender y con menos archivos de configuración.

Parámetros y valores de respuesta

Ahora bien, en una aplicación web normal contamos siempre con dos objetos principales: el que maneja las peticiones del cliente Request, y el que prepara las respuestas al cliente Response. En JSF todo es a través de los atributos del Managed Bean. 

Aquí en MVC, las peticiones del cliente son recibidas como parámetros del método (sí, como RESTful) y las respuestas se guardan en un contenedor de datos llamado Modelo. El modelo consiste en la clase javax.mvc.Models que es una extensión a la clase java.util.Map Por tanto, podemos guardar todas los valores del modelo de la aplicación como mapa. El alcance que tendrá el modelo de este controlador estará definido por la anotación javax.enterprise.context.RequestScoped que se pondrá en la declaración de la clase controladora. Esta es la misma anotación que se utiliza para el alcance de los EJB.

Agreguemos un método a nuestra clase controladora. Para no tocar el método actual, crearemos otro que recibirá el parámetro por medio de un formulario. 
package com.apuntesdejava.demomvc.controller;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Controller;
import javax.mvc.Models;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

@Path("/saludo")
@Controller
@RequestScoped
public class HolaController {

    @Inject
    Models models;

    @GET
    public String hola() {
        return "/WEB-INF/jsp/saludo.jsp";
    }

    @POST
    @Path("/nombre")
    public String holaNombre( @FormParam("nombre") String nombre) {
        String mensaje = "Hola " + nombre;
        models.put("mensaje", mensaje);
        return "/WEB-INF/jsp/saludo-nombre.jsp";

    }
}


  • En las líneas 17 y 18 se declara el modelo del controlador.
  • En la línea 25 indica que se invocará usando el método HTTP POST
  • En la línea 26 está la ruta dentro del controlador.
  • En la línea 27 se indica el parámetro que recibirá el método y será enviado por formulario. Muy JAX-RS
  • En la línea 29 se guarda el valor en el modelo. Como vemos, es un Mapa común de Java. Sí, hasta aquí es muy Java puro.
Y necesitamos crear nuestro jsp donde mostrará el contenido del mensaje. Este tendrá el siguiente contenido.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>${mensaje}</h1>
    </body>
</html>
Veamos la línea 9. Así es como se hace para recibir el valor del model.

Además, necesitamos el formulario. Este estará en nuestro index.html al que le agregaremos las siguientes líneas:
        <h2>Probando con método POST</h2>
        <form  action="main-app/saludo/nombre" method="post">
            Nombre: <input type="text" name="nombre" placeholder="Escribe tu nombre aquí"/><br/>
            <button type="submit">Enviar</button>
        </form>
Notar la línea 11 la manera cómo se invoca al controlador, tanto la ruta como el método.

Al ejecutarse, este será nuestro formulario.



Y esta será la respuesta.


Un poco más sobre los controladores

¿Solo permite que devuelva tipo String? La verdad, no, también se puede hacer que devuelva void, pero se le debe indicar cuál es la vista en una anotación. También se puede usar tipo Response, y la respuesta será mismo JAX-RS. Aquí veamos unos ejemplos.
    @GET
    @View("/WEB-INF/jsp/saludo.jsp")
    @Path("/void")
    public void metodoVoid(){
        LOG.info("Método de tipo void");
    }
    
    @GET
    @Path("/viewable")
    public Viewable metodoViewable(){
        LOG.info("Método de tipo Viewable");
        return new Viewable("/WEB-INF/jsp/saludo.jsp");
    }
    
    @GET
    @Path("/response")
    public Response metodoResponse(){
        LOG.info("Método de tipo Response. Bien JAX-RS");
        return Response.status(Response.Status.OK)
                .entity("/WEB-INF/jsp/saludo.jsp")
                .build();
    }
¿También el controlador puede convivir con métodos JAX-RS? Pues, la verdad.. Sí! solo diferencia quién lo puede llamar. Solo pruébalo.

Un poco más sobre la vista

Hemos visto cómo mostrar un simple mensaje, pero si guardo en el modelo una colección ¿Cómo se podrá obtener ese arreglo?

Como se mencionó antes, ese "modelo" es una alternativa a los request/response. Por tanto, los valores del mapa se guardan como atributos del requerimiento. Eso significa que podemos hacer lo siguiente.
    @GET
    @Path("/lista-nombres")
    @View("/WEB-INF/jsp/lista-nombres.jsp")
    public void listaNombres() {
        List nombres = Arrays.asList("Ann", "Bernard", "Carl");
        models.put("nombres", nombres);
    }
Y en nuestro JSP podemos invocarlo así
  <%List<String> nombres= (List<String>) request.getAttribute("nombres")  %>
y lo trabajamos como scriplet.

O si queremos usar JSTL, agregamos la dependencia...
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2-rev-1</version>
        </dependency>

... y lo invocamos de una manera natural.

        <ul>
            <c:forEach items="${nombres}" var="n">
                <li>${n}</li>
            </c:forEach>
        </ul>

Código fuente

El código fuente de este proyecto, con todos sus ejemplos, lo puedes descargar aquí: https://bitbucket.org/apuntesdejava/java-ee-8-demo-mvc/get/514936ee3e45.zip

Y también está disponible como Git aquí: https://bitbucket.org/apuntesdejava/java-ee-8-demo-mvc/

Comentarios

Entradas más populares de este blog

UML en NetBeans

Cambiar ícono a un JFrame

RESTful... la forma más ligera de hacer WebServices (Parte 1)