1 jul 2017

Docker I - Primera aproximacion

Imagen relacionada

1. Introducción


Voy a comenzar una serie de howto dedicados a una de las tecnologías que más se han puesto de moda en los ultimos tiempos : ‘Docker’.

En esta primera aproximación, vamos a describir que es Docker, su instalación, primeros pasos, y en los sucesivos vamos a adentrarnos un poco más para poner casos de uso reales.

2. Requisitos


Los posts están realizados sobre un equipo que corre una Ubuntu 16.04 LTS.
Para otros sistemas como Windows y demás, habría que cambiar varias cosas, que las iremos comentando.

3. Definicion


Resultado de imagen de docker

Siempre he pensado que una imagen vale más que mil palabras, por lo que si nos fijamos en la imagen de arriba, y lo comparamos con algo que ya conocemos, como es la Virtualización de una máquina, nos fijamos que la diferencia es grande.
En la virtualización de una máquina al completo, estamos virtualizando todos y cada uno de los componentes de la misma, algo que seguramente en la mayoría de los casos no nos sea de utilidad.
En el caso de Docker, es una herramienta openSource que nos permite realizar una ‘virtualización ligera’, con la que poder empaquetar entornos y aplicaciones que posteriormente podremos desplegar en cualquier sistema que disponga de esta tecnología.

Para ello Docker extiende LXC (LinuX Containers), que es un sistema de virtualización que permite crear múltiples sistemas totalmente aislados entre sí, sobre la misma máquina o sistema anfitrión.

La gran diferencia es que una máquina virtual necesita contener todo el sistema operativo mientras que un contenedor Docker aprovecha el sistema operativo sobre el cual se ejecuta, comparte el kernel del sistema operativo anfitrión e incluso parte de sus bibliotecas.
Respecto al almacenamiento en disco, una máquina virtual puede ocupar varios gigas ya que tiene que contener el sistema operativo completo, sin embargo los contenedores Docker sólo contienen aquello que las diferencia del sistema operativo en las que se ejecutan.

Desde el punto de vista del consumo de procesador y de memoria RAM, los contenedores Docker hacen un uso mucho más eficiente del sistema anfitrión, pues comparten con él, el núcleo del sistema operativo y parte de sus bibliotecas, con lo que únicamente usarán la memoria y la capacidad de cómputo que estrictamente necesiten.

Por ultimo, la filosofía de Docker, es una orientación a servicios, lo que nos va a permitir que tengamos que pensar a partir de ahora, en nuestros "problemas" para darle la solución en base a los servicios que necesitemos. Esto nos permitirá escalar rápidamente, y ya veremos como con otra herramienta como son Kubernetes, nos podremos sentir tranquilos a la hora de obtener un buen rendimiento.

Otras ventajas destacables del uso de Docker serían:


  • Las instancias se arrancan en pocos segundos.
  • Es fácil de automatizar e implantar en entornos de integración continua.
  • Existen multitud de imágenes que pueden descargarse y modificarse libremente.

5. Imágenes y Contenedores


Los términos que hay que manejar con Docker son principalmente 2, las imágenes y contenedores.

Las imágenes en Docker se podrían ver como un componente estático, pues no son más que un sistema operativo base, con un conjunto de aplicaciones empaquetadas, mientras que un contenedor es la instanciación o ejecución de una imagen, pudiendo ejecutar varios contenedores a partir de una misma imagen.

6. Instalación


En el caso de Ubuntu, la vida es más fácil en Linux, solo debemos:


1
2
3
4
5
6
7
sudo apt-get update
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main'

sudo apt-get update
sudo apt-get install -y docker-engine

Y para activarlo y ver el estado


1
2
3
4
5
6
7
8
9
sudo systemctl status docker


Output
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2016-05-01 06:53:52 CDT; 1 weeks 3 days ago
     Docs: https://docs.docker.com
 Main PID: 749 (docker)

Ejecutar el Comando Docker Sin Sudo (Opcional)


De forma predeterminada, ejecutar el comando docker requiere privilegios de root, es decir, tiene que prefijar el comando con sudo. También puede ser ejecutado por un usuario en el grupo docker, que se crea automáticamente durante la instalación de Docker. Si intenta ejecutar el comando docker sin prefijarlo con sudo o sin estar en el grupo docker, obtendrá una salida como esta:


1
2
3
Output
docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See 'docker run --help'.

Lo solucionamos así:


1
2
3
sudo usermod -aG docker $(whoami)

sudo usermod -aG docker username


7. Uso del Comando Docker




1
docker [option] [command] [arguments]


8. Trabajar con Imágenes de Docker


Vamos a usar nuestra primera imagen de Docker, y crear nuestro contenedor.


1
docker run hello-world

El resultado es:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

Esto es así porque la imagen ya la tenía descargada, si no se hubiera puesto a descargarla, eso es!, el solito.
Para ver las imágenes descargadas (en mi caso):

1
2
3
4
5
6
7
8
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
lamp_php            latest              e086b8f727ab        11 hours ago        380MB
lamp_apache         latest              fb45ef33b220        12 hours ago        175MB
php                 7.0-fpm             9b2d6959af7b        9 days ago          380MB
debian              jessie              62a932a5c143        10 days ago         123MB
hello-world         latest              1815c82652c0        2 weeks ago         1.84kB
mysql               5.6.35              a0f5d7301767        3 months ago        329MB

Se puede apreciar que hay imágenes que son de hace tiempo, y otras de hace horas. Las que son de hace horas, es porque aparte de poder usar una imagen "estándar" por así llamarla, la podemos customizar, y crear una imagen nuestra, y además podremos compartirla en Docker Hub docker hub


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ docker search ubuntu

Output
NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
ubuntu                            Ubuntu is a Debian-based Linux operating s...   3808      [OK]       
ubuntu-upstart                    Upstart is an event-based replacement for ...   61        [OK]       
torusware/speedus-ubuntu          Always updated official Ubuntu docker imag...   25                   [OK]
rastasheep/ubuntu-sshd            Dockerized SSH service, built on top of of...   24                   [OK]
ubuntu-debootstrap                debootstrap --variant=minbase --components...   23        [OK]       
nickistre/ubuntu-lamp             LAMP server on Ubuntu                           6                    [OK]
nickistre/ubuntu-lamp-wordpress   LAMP on Ubuntu with wp-cli installed            5                    [OK]
nuagebec/ubuntu                   Simple always updated Ubuntu docker images...   4                    [OK]
nimmis/ubuntu                     This is a docker images different LTS vers...   4                    [OK]
maxexcloo/ubuntu                  Docker base image built on Ubuntu with Sup...   2                    [OK]
admiringworm/ubuntu               Base ubuntu images based on the official u...   1                    [OK]

...


De esas que ya tenemos, podemos hacer una búsqueda para que nos sirva de base.
Y si queremos descargar alguna:


1
$ docker pull ubuntu

9. Ejecutar un Contenedor Docker


Vale, pues ya tenemos las imágenes, pero como hemos comentado al inicio, ahora lo que necesitamos es ejecutar el contenedor.


1
$ docker run -d --name sonarqube -p 9000:9000 sonarqube

Como se puede ver, si observamos el comando de arriba, hemos hecho uso de una imagen que es sonarqube, la hemos ejecutado como si fuera un daemon (-d) y le damos unos puertos de entrada y salida con los que comunicarnos (-p 9000:9000)

¿Y si necesitamos tener otro Sonar? Pues nada, instanciamos de nuevo la imagen para crear otro contenedor, pero en otro puerto de mi máquina, para que no se solapen.

1
$ docker run -d --name sonarqube2 -p 7000:9000 sonarqube

Y si es una imagen de SO y queremos trabajar "dentro", pues igual de sencillo:


1
2
3
4
$ docker run -it ubuntu

Output
root@d9b100f2f636:/#

Importante: Observe el identificador del contenedor en el símbolo del sistema. En el ejemplo anterior, es d9b100f2f636.


10. Hacer Cambios en un Contenedor a una Imágen de Docker


Pues cuando paremos el contenedor, todo se perderá, pero como hemos visto antes, podemos crear nuestras imágenes, o guardar estados de las mismas.
Confirmar los cambios en una nueva instancia de imagen de Docker mediante el siguiente comando:


1
$ docker commit -m "What did you do to the image" -a "Author Name" container-id repository/new_image_name

 El modificador -m es para el mensaje de confirmación que le ayuda a usted ya los demás a saber qué cambios hizo, mientras que -a se utiliza para especificar el autor. El identificador del contenedor es el que anotó anteriormente en el tutorial cuando inició la sesión de docker interactivo. A menos que haya creado repositorios adicionales en Docker Hub, el repositorio suele ser el nombre de usuario de Docker Hub.

11. Trabajar con contenedores


Después de usar Docker por un tiempo, tendrá muchos contenedores activos (en ejecución) e inactivos en su computadora. Para ver los activos:


1
2
3
4
5
$ docker ps

Output
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
f7c79cc556dd        ubuntu              "/bin/bash"         3 hours ago         Up 3 hours                              silly_spence


1
2
3
4
5
$ docker ps -a

$ docker ps -l

$ docker stop container-id


Con los comandos anteriores podemos ver todos los contenedores, listar el ultimo modificado, y para el correspondiente.

12. Subiendo Imágenes de Docker a un Repositorio Docker


Es la parte final, para tener controlados nuestros cambios.
Deberíais haceros cuenta en Docker Hub, para tener vuestros repositorios, no solo controlados, si no también privados.



1
$ docker login -u docker-registry-username


Ahora ya podemos pushear nuestra imagen:


$ docker push docker-registry-username/docker-image-name

13. Conclusión


Ha sido un poco largo, y eso que eso la introducción. Hay un mundo de Docker, mucho más de lo que he escrito en este artículo, pero esto debería ser suficiente para empezar a trabajar con él en Ubuntu 16.04.
Al igual que la mayoría de los proyectos de código abierto, Docker se construye a partir de una base de código de rápido desarrollo, así que os invito a que le pregunteis a Google.

En próximos posts revisaremos como unir contenedores.




Read More

21 jun 2017

Nginx - Instalacion y configuracion

Introduccion


Web Server Load Balancing with NGINX Plus

Nginx es uno de los servidores web más populares del mundo y es responsable de alojar algunos de los sitios más grandes y de mayor tráfico en Internet. Es más fácil de usar que Apache en la mayoría de los casos y puede usarse como un servidor web o un proxy inverso.

Wikipedia: "nginx (pronunciado en inglés “engine X”) es un servidor web/proxy inverso ligero de alto rendimiento y un proxy para protocolos de correo electrónico (IMAP/POP3).
Es software libre y de código abierto, licenciado bajo la Licencia BSD simplificada; también existe una versión comercial distribuida bajo el nombre de nginx plus.3 Es multiplataforma, por lo que corre en sistemas tipo Unix (GNU/Linux, BSD, Solaris, Mac OS X, etc.) y Windows.
El sistema es usado por una larga lista de sitios web conocidos,5 como: WordPress, Netflix, Hulu, GitHub, Ohloh, SourceForge, TorrentReactor y partes de Facebook (como el servidor de descarga de archivos zip pesados)."

Instalacion


Dentro de Ubuntu, disponemos de su paquete oficial, por lo que la instalacion se hace bastante sencilla.


1
2
sudo apt-get update
sudo apt-get install nginx

Ajuste el Firewall


No tenemos porque tenerlo activado, en un server de produccion entiendo que sí ;), pero para nuestras pruebas locales no haría falta.
En caso de tener el FW, lo configuramos así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
sudo ufw app list

sudo ufw allow 'Nginx HTTP'

sudo ufw status


Output
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)

En línea 1 sacamos la lista de reglas a aplicar.
Vemos en la línea 3 como aplicar a la parte de HTTP. Nos habrá salido también:

  • Nginx Full: Este perfil abre tanto el puerto 80 (tráfico web normal, sin cifrar) como el puerto 443 (tráfico cifrado TLS / SSL)
  • Nginx HTTP: Este perfil abre sólo el puerto 80 (normal, tráfico web no cifrado)
  • Nginx HTTPS: Este perfil abre sólo el puerto 443 (tráfico cifrado TLS / SSL)

En el status, ya podemos observar cómo queda configurado el trafico


Servicio de Nginx



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: en
   Active: active (running) since mié 2017-06-21 22:51:45 CEST; 1min 17s ago
 Main PID: 6683 (nginx)
   CGroup: /system.slice/nginx.service
           ├─6683 nginx: master process /usr/sbin/nginx -g daemon on; master_pro
           ├─6684 nginx: worker process                           
           ├─6685 nginx: worker process                           
           ├─6686 nginx: worker process                           
           ├─6687 nginx: worker process                           
           ├─6688 nginx: worker process                           
           ├─6689 nginx: worker process                           
           ├─6690 nginx: worker process                           
           └─6691 nginx: worker process   

Los comandos para trabajar con Nginx son:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
sudo systemctl stop nginx

sudo systemctl start nginx

sudo systemctl restart nginx

sudo systemctl reload nginx

sudo systemctl disable nginx

sudo systemctl enable nginx

Bastantes descriptivos todos ellos.

Estructura de Nginx


  • /etc/nginx/nginx.conf: El directorio de configuración de nginx. Todos los archivos de configuración de Nginx residen aquí.
  • /etc/nginx/nginx.conf: El archivo de configuración principal de Nginx. Esto se puede modificar para realizar cambios en la configuración global de Nginx.
  • /etc/nginx/sites-available: El directorio donde se pueden almacenar los "bloques de servidor" por sitio. Nginx no utilizará los archivos de configuración que se encuentren en este directorio a menos que estén vinculados al directorio sites-enabled (ver abajo). Normalmente, toda la configuración del bloque del servidor se realiza en este directorio y se habilita mediante la vinculación al otro directorio.
  • /etc/nginx/sites-enabled/: Se almacena el directorio donde están habilitados los "bloques de servidor" por sitio. Por lo general, estos se crean mediante la vinculación a los archivos de configuración que se encuentran en el directorio sites-available.
  • /etc/nginx/snippets: Este directorio contiene fragmentos de configuración que se pueden incluir en cualquier otro lugar de la configuración de Nginx. Los segmentos de configuración potencialmente repetibles son buenos candidatos para la refactorización en fragmentos.

Registros del Servidor



  • /var/log/nginx/access.log: Cada solicitud a su servidor web se registra en este archivo de registro a menos que Nginx esté configurado para hacerlo de otra manera.
  • /var/log/nginx/error.log: Cualquier error Nginx se registrará en este registro.

Conclusion


Como vemos, la instalación es bastante sencilla. En próximos posts, veremos más a fondo la configuración a aplicar para crear nuestro site.
Read More

Struts2 - Action (profundizando) y III

Introduccion

Tras la entradas anteriores
Struts2 - Action (profundizando)
Struts2 - Action (profundizando) II

Vamos a ver la ultima parte sobre los Actions que sería la parte de Error and Action Message


Action Error and Action Message

1. etiqueta ActionError : Esta etiqueta se utiliza en conjunción con la validación de la clase de acción para campos de formulario. Si la validación falla por cualquier campos de formulario, podemos añadir los errores de acción y luego Struts 2 reenvía la petición de “entrada” a la página de resultados en la que podemos utilizar esta etiqueta para mostrar los mensajes de error. 
Esta etiqueta es útil en la validación del lado del servidor para campos de formulario y luego volver  a la página de entrada con el mensaje de error. Sintaxis de esta etiqueta es:


2. etiqueta actionmessage: Esta etiqueta se utiliza para mostrar un mensaje personalizado añadido por las clases de acción en la página de resultados. Por ejemplo, podemos utilizar esta etiqueta para recibir a un usuario y mostrar la última hora de entrada en la parte superior de la página. 
La sintaxis de esta etiqueta es:


Ambas etiquetas generan una lista desordenada de los errores de acción o mensajes añadidos en la clase de acción.

Objetivos

Seguimos con nuestro proyecto:

Vemos que ahora tenemos un fichero global.properties


1
2
3
4
5
6
7
8
9
#global messages
msg.welcome=Hi
label.username=User Name
label.password=Password
label.submit.login=Login

#error messages
error.username.required=User Name is required field
error.password.required=Password is required field

Vemos ahora nuestro LoginModelDrivenAction

 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
package com.mr.knight.struts2.action;

import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.apache.struts2.convention.annotation.Results;

import com.mr.knight.beans.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

@org.apache.struts2.convention.annotation.Action("loginModelDriven")
@Namespace("/User")
@ResultPath("/")
@Results({ @Result(name = "success", location = "homeModelDriven.jsp"),
  @Result(name = "input", location = "loginModelDriven.jsp") })
public class LoginModelDrivenAction extends ActionSupport implements Action, ModelDriven<User> {

 @Override
 public String execute() throws Exception {
  if ("jose".equals(user.getUserID()) && "admin".equals(user.getPassword())) {
   user.setUserName("Jose Caballero");
   addActionMessage("Welcome Admin, do some work.");
   return SUCCESS;
  } else {
   addActionError("User name is not valid");
   addActionError("Password is wrong");
   return INPUT;
  }
 }

 @Override
 public User getModel() {
  return user;
 }

 private User user = new User();

}


Vemos los métodos:
addActionMessage("Welcome Admin, do some work.");
addActionError("User name is not valid");

Y vemos como se representan:


 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
<%@ page language="java" contentType="text/html; charset=US-ASCII"
 pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%-- Using Struts2 Tags in JSP --%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Login Page</title>
<!-- Adding CSS for Styling of error messages -->
<style type="text/css">
.errorDiv {
 background-color: gray;
 border: 1px solid black;
 width: 400px;
 margin-bottom: 8px;
}
</style>
</head>
<body>
 <h3>Welcome User, please login below</h3>
 <s:if test="hasActionErrors()">
  <div class="errorDiv">
   <s:actionerror />
  </div>
 </s:if>
 <s:form action="loginModelDriven">
  <s:textfield name="userID" label="User Name"></s:textfield>
  <s:textfield name="password" label="Password" type="password"></s:textfield>
  <s:submit value="Login"></s:submit>
 </s:form>
</body>
</html>



Con esto termino la serie de 3 posts sobre las Action class de Struts2.
Para el lector queda el profundizar en estos temas, y enlazarlo con la parte del Backen final, bien con conexiones a servicios REST, o con una conexión a BD directamente.
Read More

Struts2 - Action (profundizando) II

Introducción


Ya hemos visto en el anterior post un poco de teoría
Struts2 - Action (profundizando)

Vamos a ver ahora un par de ejemplos, con las aproximaciones que hace Struts2 para el manejo de la info que viaja en la app. Es decir, esos Beans que se manejan en la aplicación, querremos usarlos de una u otra manera.
Las dos aproximaciones que tenemos son:
1. Object-backed action classes
2. ModelDriven action classes


Objetivo


Nuestro proyecto debe quedar algo por estilo. Recuerda que siempre lo tienes disponible desde mi enlace a GitHub.


Bean


Como hemos dicho, lo primero es que la información viaje a través de la aplicación, para lo cual vamos a hacer uso de un Bean de usuario.

 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
package com.mr.knight.beans;

public class User {
 private String userID;
 private String password;
 private String userName;

 public String getUserID() {
  return userID;
 }

 public void setUserID(String userID) {
  this.userID = userID;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public String getUserName() {
  return userName;
 }

 public void setUserName(String userName) {
  this.userName = userName;
 }
}

Primera aproximación. Object-backed Action Class


Vamos a ver las modificaciones que debemos realizar.
Lo primero es fijarnos que debemos una variable para manejar el Bean.

LoginObjectBackedAction.java


 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
package com.mr.knight.struts2.action;

import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.apache.struts2.convention.annotation.Results;

import com.mr.knight.beans.User;
import com.opensymphony.xwork2.Action;

@org.apache.struts2.convention.annotation.Action("loginObject")
@Namespace("/User")
@ResultPath("/")
@Results({ @Result(name = "success", location = "homeObject.jsp"),
  @Result(name = "input", location = "loginObject.jsp") })
public class LoginObjectBackedAction implements Action {

 @Override
 public String execute() throws Exception {
  if ("jose".equals(getUser().getUserID()) && "admin".equals(getUser().getPassword())) {
   getUser().setUserName("Jose Caballero");
   return SUCCESS;
  } else {
   return INPUT;
  }
 }

 private User user;

 public User getUser() {
  return user;
 }

 public void setUser(User user) {
  this.user = user;
 }

}

A nivel de la vista, debemos referenciar esa misma variable, para aunque mal dicho, "inyectárselo" al JSP.

loginObject.jsp


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Login Page</title>
</head>
<body>
    <h3>Welcome User, please login below</h3>
    <s:form action="loginObject">
        <s:textfield name="user.userID" label="User Name"></s:textfield>
        <s:textfield name="user.password" label="Password" type="password"></s:textfield>
        <s:submit value="Login"></s:submit>
    </s:form>
</body>
</html>

Vemos como para hacer referencia al Bean User, lo marcamos como tal, y luego referenciamos sus propiedades.

Segunda aproximación. ModelDriven Action Class


Para las Action ModelDriven, tenemos que implementar com.opensymphony.xwork2.ModelDriven. Esta interfaz es de tipo parametrizado utilizando genéricos de Java y necesitamos proporcionar el tipo como nuestra de la clase bean. La interfaz ModelDriven  contiene sólo un método getModel () que necesitamos para implementar y devolver el Bean.

El otro punto importante a destacar es que el Action debe tener una variable de nivel de clase para el bean y necesitamos crear una instancia.
Esto se ve mejor con un ejemplo:

LoginModelDrivenAction.java


 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
package com.mr.knight.struts2.action;

import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.apache.struts2.convention.annotation.Results;

import com.mr.knight.beans.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

@org.apache.struts2.convention.annotation.Action("loginModelDriven")
@Namespace("/User")
@ResultPath("/")
@Results({ @Result(name = "success", location = "homeModelDriven.jsp"),
  @Result(name = "input", location = "loginModelDriven.jsp") })
public class LoginModelDrivenAction extends ActionSupport implements Action, ModelDriven<User> {

 @Override
 public String execute() throws Exception {
  if ("jose".equals(user.getUserID()) && "admin".equals(user.getPassword())) {
   user.setUserName("Jose Caballero");
   addActionMessage("Welcome Admin, do some work.");
   return SUCCESS;
  } else {
   addActionError("User name is not valid");
   addActionError("Password is wrong");
   return INPUT;
  }
 }

 @Override
 public User getModel() {
  return user;
 }

 private User user = new User();

}

loginModelDriven.jsp


 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
<%@ page language="java" contentType="text/html; charset=US-ASCII"
 pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%-- Using Struts2 Tags in JSP --%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Login Page</title>
<!-- Adding CSS for Styling of error messages -->
<style type="text/css">
.errorDiv {
 background-color: gray;
 border: 1px solid black;
 width: 400px;
 margin-bottom: 8px;
}
</style>
</head>
<body>
 <h3>Welcome User, please login below</h3>
 <s:if test="hasActionErrors()">
  <div class="errorDiv">
   <s:actionerror />
  </div>
 </s:if>
 <s:form action="loginModelDriven">
  <s:textfield name="userID" label="User Name"></s:textfield>
  <s:textfield name="password" label="Password" type="password"></s:textfield>
  <s:submit value="Login"></s:submit>
 </s:form>
</body>
</html>

homeModelDriven.jsp


 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
<%@ page language="java" contentType="text/html; charset=US-ASCII"
 pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Welcome Page</title>
<style type="text/css">
.welcome {
 background-color: green;
 border: 1px solid black;
 width: 400px;
}

//
ul.actionMessage is added by Struts2 API 
ul.actionMessage li {
 color: yellow;
}
</style>
</head>
<body>
 <h3>Example msg</h3>
 <s:if test="hasActionMessages()">
  <div class="welcome">
   <s:actionmessage />
  </div>
 </s:if>
 <br>
 <br>
 <s:property value="getText('msg.welcome')" />
 <s:property value="userName" />
</body>
</html>


Como podemos ver, es más cómodo no referencias a la variable del Action que tenemos con el Bean. Nos independiza un poco más del hecho de que sea uno u otro objeto el que la maneje.


Resumen


Si nos fijamos en ambos enfoques, el enfoque ModelDriven es fácil de implementar y mantener, ya que no requiere cambios en las páginas JSP.
Ahora cada cual...


Read More

20 jun 2017

Struts2 - Action (profundizando)

Introduccion


En los anteriores posts de la serie de artículos sobre Struts2, comentamos que Struts2, se centra, al igual que su antecesor, en el patrón MVC.

Recordamos la imagen. Y vemos como los Actions, son la parte de la C del MVC.

Vamos a ver que aproximaciones tenemos, y qué más podemos hacer con ellos.

Action


Un action, vamos a ponernos a muy alto nivel, se encarga de ser la parte "frontal" de la aplicación, es decir, la parte encargada de manejar la Request.

Action - Tareas


1. Contiene la logica de negocio. Obviamente, si esta es muy grande, lo más lógico es apoyarse en Helpers classes para integrarlas posteriormente en el Action.
2. Trasnporta los datos. Luego veremos, que podremos mapear los Beans directamente, y podremos gestionar las peticiones a través de Struts, incluyendo esos datos que vienen de la vista.
3. Determinar el resultado. Para ello hemos visto que podemos tener SUCCESS, ERROR, INPUT, etc...

Tipos de Action

Tipo 1. Simple.

  En este caso, simplemente ejecutamos el método execute(), y prácticamente se convierte en una redirección.

Tipo 2. Implementing Action interface. 

  Aqui ya tenemos más juego, puesto que nos permite tener algunas constantes para ofrecer el resultado (SUCCESS, ERROR, NONE, INPUT and LOGIN).

Tipo 3. Using Struts 2 Annotations.

  Podemos crear una clase Action, haciendo uso de anotaciones. Algo bastante potente, y que nos permite tener el código ordenado.

Tipo 4. Extender ActionSupport Class. 

  Con esta extension, podemos acceder todo el tema de Validation y soporte para i18n.


Read More

18 jun 2017

Struts2 - construyendo la aplicacion III

Objetivos


Resultado de imagen de log4j

Añadiremos un sistema de Loggin a nuestra aplicacion.

1. Introducción


Log4j es una librería adicional de java que permite a nuestra aplicación mostrar mensajes de información de lo que está sucediendo en ella, lo que habitualmente se conoce como un log. Tiene la ventaja de ser dinámico y, por tanto, configurable.

2. Configuración


Una de las ventajas de Log4j es que es configurable a través de un fichero, bien un properties o un xml.
En nuestro caso lo colocaremos en la carpeta de Resources.

3. Niveles de Log o Traza



  • trace() El menos importante, que se suele usar en la fase de desarrollo. Es el típico System.out.println(“paso por aquí”).
  • debug() Información útil para depurar. Algún resultado parcial, el valor de alguna variable, etc.
  • info() Información sobre la aplicación que pueden tener cierto interés para ser mostrados en el log. Se establece una conexión con base de datos, se conecta un cliente a nuestro servidor, un usuario entra en sesión, se guarda la sesión en base de datos, etc.
  • warn() Advertencias o fallos sin importancia para la ejecución del programa. Un usuario introduce una contraseña errónea, un fichero de configuración no existe y la aplicación coge la configuración por defecto que se establezca en el código, etc.
  • error() Errores importantes, pero que no obligan a terminar la aplicación. No se puede conectar a la base de datos pero hay ciertas funcionalidades que sí pueden seguir ofreciéndose, aun sin base de datos.
  • fatal() Errores que obligan a terminar la aplicación. Se acaba la memoria disponible.


4. Fichero de configuración


Es lo más importante, porque un sistema muy cargado de Logs "no sirve de nada", cuidado con esta afirmación, hay ahora mismo procesos en Big Data que darían mucho juego.
El caso es que estamos haciendo trabajar a la máquina (no deja de ser una escritua en disco, lo más costoso a no ser que hablemos de SSD), por lo tanto debemos se cautelosos con este tema.
Un fichero básico podría ser:


 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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
     
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
       <layout class="org.apache.log4j.PatternLayout"> 
          <param name="ConversionPattern" value="%d %-5p %c.%M:%L - %m%n"/> 
       </layout> 
    </appender>
  
    <!-- specify the logging level for loggers from other libraries -->
    <logger name="com.opensymphony">
        <level value="DEBUG" />
    </logger>
 
    <logger name="org.apache.struts2">
         <level value="DEBUG" />
    </logger>
    <logger name="com.mr.knight.struts2">
         <level value="DEBUG" />
    </logger>   
   <!-- for all other loggers log only info and above log messages -->
     <root>
        <priority value="INFO"/> 
        <appender-ref ref="STDOUT" /> 
     </root> 
     
</log4j:configuration>

5. Para hacerlo funcionar


Añadimos la dependencia MAVEN en el pom.xml

Generamos el logger y lo usamos

LoginAction.java:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import org.apache.log4j.Logger;

....


public class LoginAction {
 final static Logger logger = Logger.getLogger(LoginAction.class);


.....

 public String execute() throws Exception {
  logger.debug("Execute");
  if ("jose".equals(getName()) && "admin".equals(getPwd())) {
   logger.debug("Success");
   return "SUCCESS";
  } else {
   logger.debug("Error validating");
   return "INPUT";
  }
 }

6. Resultado



1
2
3
4
5
6
7
8
jun 18, 2017 10:47:42 AM org.apache.catalina.startup.Catalina start
INFORMACIÓN: Server startup in 2184 ms
2017-06-18 10:48:03,746 DEBUG com.mr.knight.struts2.action.LoginAction.execute:26 - Execute
2017-06-18 10:48:03,749 DEBUG com.mr.knight.struts2.action.LoginAction.execute:31 - Error validating
2017-06-18 10:48:05,698 DEBUG com.mr.knight.struts2.action.LoginAction.execute:26 - Execute
2017-06-18 10:48:05,699 DEBUG com.mr.knight.struts2.action.LoginAction.execute:31 - Error validating
2017-06-18 10:48:21,467 DEBUG com.mr.knight.struts2.action.LoginAction.execute:26 - Execute
2017-06-18 10:48:21,468 DEBUG com.mr.knight.struts2.action.LoginAction.execute:28 - Success


Quedaría pendiente, el migrarlo a log4j2


Recuerda, tienes que hacer commit ;)
Read More