Tenéis que estar orgullosos de vuestro enemigo
F. Nietzsche

Archivos en la categoría J2EE

Patrones de Diseño - Singleton

El Patrón de Diseño Singleton (en español, Instancia Única) se utiliza para garantizar que una clase sólo tenga una única instancia y para facilitar un punto de acceso global a la misma.

Se utiliza cuando se necesita :

  • que sea un único objeto el que coordine acciones a lo largo de todo el sistema
  • que el objeto posea estado
  • variables globales en el sistema (es más limpio y eficiente utilizar Singleton)

La propia clase es única responsable de crear la única instancia (ocultando al constructor) y de facilitar el acceso global a la instancia. Un ejemplo de implementación en Java :

public class Singleton {

      //Instancia como variable estática

    private static Singleton instanciaUnica;

    //Constructor privado : no se puede acceder desde fuera

    private Singleton() {}

    //Método para obetener una instancia

    public static Singleton getInstancia() {

      synchronized(Singleton.class) {

              if (instanciaUnica == null) {

                  instanciaUnica = new Singleton();

              }

      }

      return instanciaUnica;

    }

}

Es delicado en sistemas multihilo, ya que si dos hilos de ejecución intentarán crear una instancia al mismo tiempo y ésta no existiese todavía, sólo uno de los dos debería de lograrlo. La solución clásica para este problema es utilizar exclusión mutua en el método de creación de la clase que implementa el patrón.

En algunas ocasiones se aplica este patrón de una forma menos restrictiva, permitiendo que exista un número máximo de instancias en lugar de una única.

Patrones de Diseño - Façade

El Patrón de Diseño Façade (o Facade, o en español Fachada) es en mi opinión uno de los más básicos y simples, que todos hemos implementado alguna vez aún sin conocerlo.

Una fachada no se trata más que de un objeto que ofrezca una sencilla interfaz que ocultará uno o varios sistemas más complejos y sus interacciones.

Con este patrón ofrecemos un acceso sencillo y desacoplamos al máximo nuestro sistema cliente (el que accede a la fachada) de los sistemas ocultos.

Típicamente se utiliza en librerías o en sistemas diseñados en capas. Desde luego si estás construyendo un sistema que van a utilizar otros 20 sistemas de tu empresa, implementa una fachada.

Algunas ventajas que disfrutaremos en el propio desarrollo son :

  • Facilitamos la utilización y comprensión (acompañando la interfaz con una documentación mínima) de los sistemas ocultados
  • Los clientes se olvidan de toda la complejidad del negocio, sólo les importa los resultados obtenidos.

Y a la larga, el mantenimiento será más fácil

  • En los clientes reducimos las dependencias del código externo.
  • Aislamos a los clientes de cambios sustanciales en los sistemas ocultos, ya sea un cambio de requisitos, de diseño de base de datos, de tecnología utilizada…
  • Hace el código cliente más legible

Este patrón de diseño se puede implementar como una interfaz o como una clase abstracta, sin detallar los detalles de implementación (es justo lo que queremos ocultar).

Es habitual que se implemente la fachada utilizando también el patrón Singleton (sólo se necesita una fachada).

Como vemos es un patrón de diseño muy simple e intuitivo, que nace del sentido común y experiencia. Los beneficios que nos ofrece son enormes en comparación con su coste de implementación.

Pero ojo, su no utilización nos provocará enormes problemas tanto a la hora de desarrollar como sobre todo en el mantenimiento. Podemos ver algo parecido a lo que te sucederá si no lo utilizas en la viñeta siguiente… Así que no te dejes de utilizarlo !

Manejo de Excepciones y Errores en una arquitectura Java

Podemos considerar que una Excepción en programación es una situación poco habitual que provoca resultados inesperados o incoherentes en nuestros métodos, pero que es contemplada en el análisis, diseño y construcción de nuestro sistema : el sistema se puede recuperar de estas situaciones.

Como ejemplos podemos citar envíos duplicados de información, una caída de nuestra base de datos, información inválida o incosistente como un número de teléfono con un formato incorrecto…

Para mantener códigos robustos que manejen estas situaciones, los lenguajes de programación incorporan mecanismos para el manejo de excepciones como parte de sus estructuras de control.

Y las situaciones poco habituales que provocan resultados inesperados o incoherentes y que no controlamos ? Eso es un error en tiempo de ejecución en nuestro sistema, que puede desembocar en una inconsistencia de datos en nuestra base de datos o en la finalización inesperada de nuestro sistema.

Un buen indicador de la calidad de un sistema software es comprobar cómo responde cuando se producen fallos, ya sean esperados y controlados (excepciones) o inesperados e incontrolados (errores).

Excepciones en Java

En el caso de Java se utilizan bloques try - catch - finally, throw y throws, y las subclases de java.lang.Exception. Un método lanzarán (mediante un throw) una excepción (una clase que hereda java.lang.Exception) provocando que se abandone el flujo de ejecución normal y cediendo el control al bloque en el que se capturará (en un bloque try - catch - finally). En la declaración del método se incluye throws para avisar de que puede lanzar una excepción.

Existen muchas excepciones predefinidas en Java, pero también nosotros podemos crear excepciones particulares a nuestros sistemas : basta crear una clase que extienda de java.lang.Exception.

Existen dos grandes tipos de excepciones en Java :

  • Checked Exceptions (excepciones controladas). Se corresponden a errores previstos, controlados en el codigo y por tanto permite al sistema recuperarse. Son lo que hemos definido anteriormente como excepciones. El compilador Java es una gran ayuda ya que te obliga a capturarlas en un bloque try - catch - finally o a declarar que el método puede devolver una excepción (declarando un throws).
  • Unchecked Exceptions (excepciones no controladas). Son excepciones no previstas ni controladas, y por tanto puede provocar inconsistencia de datos y la finalización inesperada del sistema : lo que hemos definido anteriormente como errores en tiempo de ejecución. En Java se implementan como subclases de java.lang.RuntimeException (que a su vez hereda de java.lang.Exception) y el compilador no nos obliga a capturarlas ni a declarar que nuestros métodos pueden lanzarlas. NullPointerException o IllegalArgumentException son dos ejemplos de estas excepciones.

No olvides utilizar la parte finally de los bloques try - catch - finally para liberar recursos como conexiones o archivos.

Manejo de Excepciones en una arquitectura Java

Al invocar un método deberíamos obtener como resultado o bien el resultado normal de la ejecución o bien no hacer absolutamente nada. Las excepciones nos servirán para comunicar a las capas superiores información sobre por qué no se ha hecho nada si fuese necesario.

La utilización de bloques try - catch - finally es más costoso en términos de tiempo de ejecución y recursos que la utilización de bloques if - else.

  • Por tanto es recomendable utilizar excepciones para controlar el flujo, es mejor utilizar los mecanismos de control de flujo ya existentes que consumen menos y son más fácilmente mantenibles.
  • Tampoco es recomendable capturar una excepción en un bloque catch y no hacer nada, o no hacer nada más que lanzarla nuevamente.

Es aconsejable que las excepciones no rompan la encapsulación. Por ejemplo si en una capa inferior se produce una excepción (por ejemplo una SQLException en la capa de persistencia), a la capa superior no le interesa conocer los detalles de implementación, sino:

  • si se puede recuperar del error, es aconsejable transformar la excepción original en otra excepción controlada o checked con la información necesaria para recuperar que la capa superiro se pueda recuperar.
  • o si no puede recuperarse del error pasamos a la capa superior una excepción no controlada o unchecked si no se debe continuar el flujo normal, o bien un resultado nulo si no se quiere hacer nada especial.

Sólo deberíamos de crear excepciones propias cuando aporten algo útil como información extra cuando sean capturadas o comportamientos propios (como un tratamiento especial en un log).

Al capturar excepciones también capturamos las que heredan de ésta. Por tanto al capturar una excepción del tipo java.lang.Exception también estamos capturando todas las unchecked (ya que heredan de java.lang.RuntimeException y ésta hereda de java.lang.Exception). Hay que tener cuidado de si esto es realmente lo que queremos.

Por todo lo dicho anteriormente, la gestión de las excepciones nos debería de servir para recuperarnos ante errores esperados. Pero deberíamos tener en cuenta que pueden existir errores no controlados. Podríamos gestionar estos errores en un único punto, desviando todas las excepciones no capturadas a un action donde se tratarán (típicamente presentación de una página de error con un mensaje y las acciones permitidas para el usuario, así como la auditoría del error). Podemos indicar en el web.xml esta redirección :

<error-page>

      <exception-type>java.lang.Throwable</exception-type>

      <location>/gestionErrores.do</location>

</error-page>

Enlaces recomendados

El Lenguaje de Programación Java

Java es un Lenguaje de Programación de Alto Nivel diseñado por Sun Microsystems a primeros de los años 90, en plena transición desde la Programación Estructurada, en la que el lenguaje C copaba el mercado, hacia la emergente Programación Orientada a Objetos (POO en español, OOP en inglés), en la que competiría con C++.

Sus características fueron la base de su éxito  :

  • Al ser un lenguaje de más alto nivel es más fácil de aprender y se reduce el tiempo y coste de los desarrollos y de las pruebas (se simplifica la programación, se eliminan los punteros, la gestión de la memoria ya no la hace el programador…).
  • Implementa el paradigma de programación POO : Jerarquía de Clases, Herencia, Polimorfismo, Encapsulación y ocultamiento de la información.
  • Hacía especial énfasis en la Modularidad y Reutilización de los componentes, es la base para la aparición de APIs (Application Programming Interface). Todo esto facilita enormemente el desarrollo, el trabajo en paralelo entre equipos y disminuía enormemente el tiempo de desarrollo y pruebas.
  • Es un lenguaje Multiplataforma, es decir es independiente de la platarforma (máquina + sistema operativo) en la que se ejecute, con lo cual obtenemos programas Portables, gracias a la Máquina Virtual Java (JVM).
  • Es gratuito !
  • La irrupción poco después de Internet supondría el éxito definitivo de este lenguaje.

Qué necesitamos para comenzar a programar con este lenguaje ?

  • Documentación, un montón de documentación. Creo que es lo primero en que pensar.
  • Después necesitaremos las herramientas para desarrollar en Java que podemos descargarnos gratuitamente de Sun : el Java Development Kit o JDK, que básicamente nos proporciona un compilador java y un entorno para ejecutar programas java. Podemos encontrar sus diferentes versiones en el sitio web de Sun. Podemos utilizar Java 2 Standard Edition (J2SE), Java 2 Enterprise Edition (J2EE) o Java 2 Mobile Edition (J2ME).
  • No confundir con el Java Runtime Environment o JRE que son las herramientas imprescindibles que necesitan los programas basados en Java para poder ejecutarse (la JVM es una instancia del JRE).
  • Ahora nos basta con cualquier editor de texto, pero es altamente recomendable (imprescindible profesionalmente) utilizar software que nos ayude, un IDE (Integrated Development Kit) que nos proporciona editores de texto más potentes y otras herramientas que nos ayudan al desarrollo y pruebas. Eclipse es el más extendido entre los gratuitos, pero también Intelli J Idea es útil y potente entre los gratuitos.

Integrar Struts con Spring - holamundoStruts138Spring

Integrar Struts con Spring - holamundoStruts138SpringVamos al turrón, vamos a implementar lo que hemos visto a nivel teórico.

Creamos un nuevo proyecto web dinámico, holamundoStruts138Spring. Copiamos todo lo que tenemos en holamundoStruts138 sobre este nuevo proyecto para no partir de cero.

Básicamente sobreescribermos el controlador de Struts y utilizaremos la configuración de Spring para inyectar las dependencias a nuestros objetos.

LyckaRequestProcessor

package es.lycka.holamundoStruts138.action;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.action.ActionServlet;

import org.apache.struts.action.RequestProcessor;

import org.apache.struts.config.ModuleConfig;

import org.apache.struts.util.RequestUtils;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.struts.DelegatingActionUtils;

public class LyckaRequestProcessor extends RequestProcessor{

      private WebApplicationContext webApplicationContext;

      public void init(ActionServlet actionServlet, ModuleConfig moduleConfig) throws ServletException {

            super.init(actionServlet, moduleConfig);

            if (actionServlet != null) {

                  this.webApplicationContext = initWebApplicationContext(actionServlet, moduleConfig);

            }

      }

      protected WebApplicationContext initWebApplicationContext(

                  ActionServlet actionServlet, ModuleConfig moduleConfig) throws IllegalStateException {

            return DelegatingActionUtils.findRequiredWebApplicationContext(actionServlet, moduleConfig);

      }

      protected final WebApplicationContext getWebApplicationContext() {

            return webApplicationContext;

      }

      /**

       * Método que sobreescribimos para utilizar beans de Spring.

       */

      protected Action processActionCreate(HttpServletRequest request,

                  HttpServletResponse response, ActionMapping mapping)

                  throws IOException {

            // Acquire the Action instance we will be using (if there is one)

            String className = mapping.getType();

            if (log.isDebugEnabled()) {

                  log.debug(" Looking for Action instance for class " + className);

            }

            // If there were a mapping property indicating whether

            // an Action were a singleton or not ([true]),

            // could we just instantiate and return a new instance here?

            Action instance;

            //Acceso sincronizado al HasMaps de actions

            synchronized (actions) {

                  // Return any existing Action instance of this class

                  instance = (Action) actions.get(className);

                  if (instance != null) {

                        if (log.isTraceEnabled()) {

                             log.trace("  Returning existing Action instance");

                        }

                        return (instance);

                  }

                  // Create and return a new Action instance

                  if (log.isTraceEnabled()) {

                        log.trace("  Creating new Action instance");

                  }

                  try {

                        //String beanName = determineActionBeanName(mapping);

                        String beanName = mapping.getType();

                        if (getWebApplicationContext().containsBean(beanName))

                             instance = (Action)getWebApplicationContext().getBean(beanName, Action.class);                           

                        else

                             instance = (Action) RequestUtils.applicationInstance(className);

                        // Maybe we should propagate this exception

                        // instead of returning null.

                  } catch (Exception e) {

                        log.error(getInternal().getMessage("actionCreate",

                                   mapping.getPath()), e);

                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,

                                   getInternal().getMessage("actionCreate", mapping.getPath()));

                        return (null);

                  }

                  actions.put(className, instance);

            }

            if (instance.getServlet() == null) {

                  instance.setServlet(this.servlet);

            }

            return (instance);

      }

}

Para que funcione correctamente además de las librerías de Struts que ya tenemos importadas, debemos utilizar spring.jar y spring-struts.jar.

Bases, Interfaces e Implementaciones

Ya aprovechamos que vamos a utilizar Spring para desdoblar nuestras clases en una clase Base por cada capa, y para cada clase utilizaremos un interfaz y una implementación.

Para no poner todas las implementaciones, que ya bastante larga va a quedar la entrada, sólo transcribo la implementación de IdentificarBOImpl. Sólo sería necesario el set del bean identificarDAO, pero por comodidad dejo ambas. Las demás implementaciones serían similares.

package es.lycka.holamundoStruts138Spring.negocio.impl;

import es.lycka.holamundoStruts138Spring.model.Usuario;

import es.lycka.holamundoStruts138Spring.negocio.IdentificarBO;

import es.lycka.holamundoStruts138Spring.persistencia.IdentificarDAO;

public class IdentificarBOImpl extends BaseBOImpl implements IdentificarBO  {

      private IdentificarDAO identificarDAO;

      public boolean esUsuarioAutorizado (Usuario usuario) throws Exception {

            boolean esAutorizado               = false;

            try {

                  if (identificarDAO.consultarUsuario (usuario) != null ) {

                        esAutorizado = true;

                  }

            } catch (Exception e) {

                  throw e;

            }

            return esAutorizado;

      }

      public IdentificarDAO getIdentificarDAO() {

            return identificarDAO;

      }

      public void setIdentificarDAO(IdentificarDAO identificarDAO) {

            this.identificarDAO = identificarDAO;