elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Guía actualizada para evitar que un ransomware ataque tu empresa


  Mostrar Temas
Páginas: 1 2 [3]
21  Programación / Java / [Guía] Patrones de diseño - Parte 1 en: 30 Enero 2015, 23:30 pm
PATRONES DE DISEÑO: PARTE 1

Los patrones de diseño son la base para la búsqueda de soluciones a problemas comunes en el desarrollo de software y otros ámbitos referentes al diseño de interacción o interfaces.
Un patrón de diseño resulta ser una solución a un problema de diseño. Para que una solución sea considerada un patrón debe poseer ciertas características. Una de ellas es que debe haber comprobado su efectividad resolviendo problemas similares en ocasiones anteriores. Otra es que debe ser reutilizable, lo que significa que es aplicable a diferentes problemas de diseño en distintas circunstancias.

Los patrones de diseño pretenden:

  • Proporcionar catálogos de elementos reusables en el diseño de sistemas software.
  • Evitar la reiteración en la búsqueda de soluciones a problemas ya conocidos y solucionados anteriormente.
  • Formalizar un vocabulario común entre diseñadores.
  • Estandarizar el modo en que se realiza el diseño.
  • Facilitar el aprendizaje de las nuevas generaciones de diseñadores condensando conocimiento ya existente.

Asimismo, no pretenden:

  • Imponer ciertas alternativas de diseño frente a otras.
  • Eliminar la creatividad inherente al proceso de diseño.

No es obligatorio utilizar los patrones, solo es aconsejable en el caso de tener el mismo problema o similar que soluciona el patrón, siempre teniendo en cuenta que en un caso particular puede no ser aplicable. "Abusar o forzar el uso de los patrones puede ser un error".


PATRÓN ADAPTER

El patrón adapter nos permite ampliar la funcionalidad de una clase o interfaz adaptando objetos que no coinciden con una determinada jerarquía de clases.

Convierte la interfaz de una clase en otra interfaz que el cliente espera. Adapter permite a las clases trabajar juntas, lo que de otra manera no podría hacerlo debido a sus interfaces incompatibles.

Cuándo usarlo:

  • Se desea usar una clase existente, y su interfaz no se iguala con la necesitada.
  • Cuando se desea crear una clase reusable que coopera con clases no relacionadas, es decir, las clases no tienen necesariamente interfaces compatibles.
Problema: Se nos pide adaptar un auto eléctrico a un sistema de abastecimiento de combustible para autos. Siendo que el auto eléctrico, utiliza como combustible la energía eléctrica.

Solución: Utilizar el patrón Adapter para extender la funcionalidad de la interfaz adaptando el auto eléctrico.

Creamos una interfaz llamada Car que representará de forma genérica a un Auto:

Car.java
Código
  1. package net.elhacker.adapter;
  2.  
  3. public interface Car {
  4.  
  5. void fillTank();
  6. default void start() {
  7. System.out.println("Encendiendo auto...");
  8. }
  9. }

NOTA: el método start() lo predefinimos porque será el mismo para todos las implementaciones.

Ahora sus implementaciones: GasolineCar y GasCar. Representan un auto a gasolina y otro a gas.

GasolineCar.java
Código
  1. package net.elhacker.adapter;
  2.  
  3. public class GasolineCar implements Car {
  4.  
  5. public GasolineCar() {
  6. super(); // llamada al constructor padre
  7. System.out.println("Creando un auto a gasolina...");
  8. }
  9.  
  10. @Override
  11. public void fillTank() {
  12. System.out.println("Colocando gasolina...");
  13. }
  14. }
  15.  

GasCar.java
Código
  1. package net.elhacker.adapter;
  2.  
  3. public class GasCar implements Car {
  4.  
  5. public GasCar() {
  6. super(); // llamada al constructor padre
  7. System.out.println("Creando un auto a gas...");
  8. }
  9.  
  10. @Override
  11. public void fillTank() {
  12. System.out.println("Colocando gas...");
  13. }
  14.  
  15. }

Ambos autos, GasolineCar y GasCar necesitan combustible para funcionar. Por eso, el método fillTank llena el combustible de ellos, dependiendo si es gasolina o gas.

Ahora queremos añadir un auto eléctrico a nuestra jerarquía de clases.

ElectricCar.java
Código
  1. package net.elhacker.adapter;
  2.  
  3. public class ElectricCar {
  4.  
  5. public ElectricCar() {
  6. super(); // llamada al constructor padre
  7. System.out.println("Creando un auto eléctrico...");
  8. }
  9.  
  10. public void connect() {
  11. System.out.println("Conectando motor a generador de electricidad...");
  12. }
  13. }

Nos damos con la sorpresa que éste auto no coincide con nuestra jerarquía. La interfaz Car dice que todos los autos que la implementen deben tener el método fillTank. Pero, ¿Como un auto eléctrico puede llenar el tanque? ¿Cómo hacemos para adaptar éste auto a nuestra jerarquía?

Un error es común es modificar la interfaz o clase abstracta. Ésto viola el principio OCP, el cual nos dice:
Citar
Las entidades de software deben ser abiertas para ser extendidas y cerradas para no ser modificadas.

Aquí toma importancia en patrón Adapter. Éste patrón nos permite ampliar la funcionalidad de una interfaz si tener que cambiar código en ella. Veamos como funciona.

Código
  1. package net.elhacker.adapter;
  2.  
  3. public class ElectricCarAdapter implements Car {
  4.  
  5. ElectricCar electricCar;
  6.  
  7. public ElectricCarAdapter() {
  8. electricCar = new ElectricCar();
  9. }
  10.  
  11. @Override
  12. public void fillTank() {
  13. electricCar.connect();
  14.  
  15. }
  16.  
  17. }

Como vemos hemos podido adaptar nuestro auto eléctrico a nuestra interfaz Car. ¡Ahora, podemos crear tanto autos a gasolina, gas o eléctricos aplicando polimorfismo!

Veamos que salida nos arroja:

AdapterTest.java
Código
  1. package net.elhacker.adapter;
  2.  
  3. public class AdapterTest {
  4.  
  5. public static void main(String[] args) {
  6. Car gasolineCar = new GasolineCar();
  7. gasolineCar.fillTank();
  8. gasolineCar.start();
  9.  
  10. System.out.println();
  11.  
  12. Car gasCar = new GasCar();
  13. gasCar.fillTank();
  14. gasCar.start();
  15.  
  16. System.out.println();
  17.  
  18. Car electricCar = new ElectricCarAdapter();
  19. electricCar.fillTank();
  20. electricCar.start();
  21. }
  22.  
  23. }

Salida:

Código:
Creando un auto a gasolina...
Colocando gasolina...
Encendiendo auto...

Creando un auto a gas...
Colocando gas...
Encendiendo auto...

Creando un auto eléctrico...
Conectando motor a generador de electricidad...
Encendiendo auto...

Y así podemos extender la funcionalidad de nuestra aplicación de forma sencilla y eficiente.


PATRÓN FACADE

Descripción: El patrón fachada viene motivado por la necesidad de estructurar un entorno de programación y reducir su complejidad con la división en subsistemas, minimizando las comunicaciones y dependencias entre éstos.

Cuándo usarlo:

  • Se usa para proporcionar una interfaz sencilla para un sistema complejo.
  • Se quiere desacoplar un subsistema de sus clientes u otros subsistemas, haciéndolo mas independiente y portable.
  • Se quiera dividir los sistemas en niveles: las fachadas serian el punto de entrada a cada nivel.
Pros/contras:

  • + La principal ventaja del patrón fachada consiste en que para modificar las clases de los subsistemas, sólo hay que realizar cambios en la interfaz/fachada, y los clientes pueden permanecer ajenos a ello. Además, y como se mencionó anteriormente, los clientes no necesitan conocer las clases que hay tras dicha interfaz.
  • - Como inconveniente, si se considera el caso de que varios clientes necesiten acceder a subconjuntos diferentes de la funcionalidad que provee el sistema, podrían acabar usando sólo una pequeña parte de la fachada, por lo que sería conveniente utilizar varias fachadas más específicas en lugar de una única global.
Problema: Crear una aplicación que haga tres operaciones bancarias: Crear una cuenta, depositar dinero y retirar dinero. Las operaciones deben hacerse dentro de una sola entidad que las maneje.

Solución: Aplicar el patrón Facade para encapsular todos los objetos que hacen las 3 operaciones.

Bank.java
Código
  1. package net.elhacker.facade;
  2.  
  3. public class Bank {
  4.  
  5. public Bank() {
  6.  
  7. }
  8.  
  9. public void createAccount(String account) {
  10. System.out.println("Creando cuenta N° "+account);
  11. }
  12. }

Deposit.java
Código
  1. package net.elhacker.facade;
  2.  
  3. public class Deposit {
  4.  
  5. public Deposit() {
  6.  
  7. }
  8.  
  9. public void makeDeposit(double amount, String account) {
  10. System.out.println("Se ha depositado $"+amount+" a la cuenta "+account);
  11. }
  12.  
  13. }

Withdrawal.java
Código
  1. package net.elhacker.facade;
  2.  
  3. public class Withdrawal {
  4.  
  5. public Withdrawal() {
  6.  
  7. }
  8.  
  9. public void makeWidthdrawal(double amount, String account) {
  10. System.out.println("Se ha retirado $"+amount+" de la cuenta "+account);
  11. }
  12.  
  13. }

Ahora, creamos nuestra clase principal:

FacadeTest.java
Código
  1. package net.elhacker.facade;
  2.  
  3. public class Facade {
  4.  
  5. public static void main(String[] args) {
  6.  
  7. Bank bank = new Bank();
  8. Deposit deposit = new Deposit();
  9. Withdrawal withdrawal = new Withdrawal();
  10.  
  11. bank.createAccount("9343435093");
  12. deposit.makeDeposit(2599.90, "9343435093");
  13. withdrawal.makeWidthdrawal(699.90, "9343435093");
  14.  
  15. }
  16.  
  17. }

Como vemos creamos 3 objetos los cuales se encargan de efectuar las acciones. Pero, ¿es necesario crear éstos tres objetos en éste ambito? ¿Los vamos a necesitar siempre?

Una mejor idea sería encapsular éstos objetos dentro de uno solo que se encargue de realizar todas las operaciones. Éste es el propósito del patrón Facade, actuar como intermediario entre la interfaz y las funcionalidades de un sistema. Veamos como se representa:

OperationsFacade.java
Código
  1. package net.elhacker.facade;
  2.  
  3. public class OperationsFacade {
  4.  
  5.  
  6. public OperationsFacade() {
  7. }
  8.  
  9. public void createAccount(String account) {
  10. new Bank().createAccount(account);
  11. }
  12.  
  13. public void makeDeposit(double amount, String account) {
  14. new Deposit().makeDeposit(amount, account);
  15. }
  16.  
  17. public void makeWithdrawal(double amount, String account) {
  18. new Withdrawal().makeWidthdrawal(amount, account);
  19. }
  20.  
  21. }

Como podemos observar, ésta clase encapsula el comportamiento de las clases Bank, Deposit y Withdrawal. Ya no tenemos que declarar los objetos porque éstos se crean en los métodos de OperationsFacade y se destruyen al finalizar el mismo. ¡Además ahorramos memoria!

Ahora, veamos como queda la clase principal:

FacadeTest.java
Código
  1. package net.elhacker.facade;
  2.  
  3. public class FacadeTest {
  4.  
  5. public static void main(String[] args) {
  6. OperationsFacade facade = new OperationsFacade();
  7. facade.createAccount("9343435093");
  8. facade.makeDeposit(2599.90, "9343435093");
  9. facade.makeWithdrawal(699.90, "9343435093");
  10. }
  11.  
  12. }

Como podemos ver, solo necesitamos de un objeto OperationsFacade para realizar todas las operaciones. Así aplicamos también el principio KISS (Keep it simple stupid!).

Salida:

Código:
Creando cuenta N° 9343435093
Se ha depositado $2599.9 a la cuenta 9343435093
Se ha retirado $699.9 de la cuenta 9343435093

PATRÓN ABSTRACT FACTORY

Descripción: El patrón Abstract Factory nos permite crear, mediante una interfaz, conjuntos o familias de objetos (denominados productos) que dependen mutuamuente y todo esto sin especificar cual es el objeto concreto.

Cuándo usarlo:

  • Un sistema debe ser independiente de como sus objetos son creados.
  • Un sistema debe ser 'configurado' con una cierta familia de productos.
  • Se necesita reforzar la noción de dependencia mutua entre ciertos objetos.
Pros/contras:

  • + Brinda flexibilidad al aislar a las clases concretas.
  • + Facilita cambiar las familias de productos.
  • - Para agregar nuevos productos se deben modificar tanto las fabricas abstractas como las concretas.
Problema: Se nos pide crear animales de X tipo sin especificar cuál es el objeto en concreto.

Solución: Utilizar el patrón Abstract Factory para crear los objetos solicitados.

Primero, creamos una clase abstracta llamada Animal que representará de forma genérica a un Animal:

Animal.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public abstract class Animal {
  4.  
  5. protected String type;
  6. protected String family;
  7. protected String habitat;
  8.  
  9. public Animal(String type, String family, String habitat) {
  10. this.type = type;
  11. this.family = family;
  12. this.habitat = habitat;
  13. }
  14.  
  15. public String getType() {
  16. return type;
  17. }
  18.  
  19. public void setType(String type) {
  20. this.type = type;
  21. }
  22.  
  23. public String getFamily() {
  24. return family;
  25. }
  26.  
  27. public void setFamily(String family) {
  28. this.family = family;
  29. }
  30.  
  31. public String getHabitat() {
  32. return habitat;
  33. }
  34.  
  35. public void setHabitat(String habitat) {
  36. this.habitat = habitat;
  37. }
  38.  
  39. @Override
  40. public String toString() {
  41. return "Tipo de animal: "+getType()+"\nFamilia: "+getFamily()+
  42. "\nHábitat: "+getHabitat();
  43. }
  44. }
  45.  

Y tres animales que extienden de Animal:

Dog.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class Dog extends Animal {
  4.  
  5. public Dog(String type, String family, String habitat) {
  6. super(type, family, habitat);
  7. }
  8.  
  9. }

Shark.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class Shark extends Animal {
  4.  
  5. public Shark(String type, String family, String habitat) {
  6. super(type, family, habitat);
  7. }
  8.  
  9.  
  10. }

Lion.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class Lion extends Animal {
  4.  
  5. public Lion(String type, String family, String habitat) {
  6. super(type, family, habitat);
  7. }
  8.  
  9.  
  10. }

Bien, ya tenemos nuestros 3 tipos de animales que heredan de Animal: Dog, Shark y Lion. Como la tarea es construir objetos sin especificar de qué tipo son, creamos una fábrica abstracta que especifica la creación de un Animal genérico sin especificar de qué tipo:

AbstractAnimalFactory.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public interface AbstractAnimalFactory {
  4.  
  5. Animal createAnimal();
  6. }

Ahora, el siguiente paso es hacer las implementaciones de ésta fábrica abstracta, es decir las fábricas concretas que crearán los objetos concretos:

DogFactory.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class DogFactory implements AbstractAnimalFactory {
  4.  
  5. protected String type;
  6. protected String family;
  7. protected String habitat;
  8.  
  9. public DogFactory(String type, String family, String habitat) {
  10. this.type = type;
  11. this.family = family;
  12. this.habitat = habitat;
  13. }
  14.  
  15. @Override
  16. public Animal createAnimal() {
  17. return new Dog(type, family, habitat);
  18. }
  19.  
  20. }

SharkFactory.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class SharkFactory implements AbstractAnimalFactory {
  4.  
  5. protected String type;
  6. protected String family;
  7. protected String habitat;
  8.  
  9. public SharkFactory(String type, String family, String habitat) {
  10. this.type = type;
  11. this.family = family;
  12. this.habitat = habitat;
  13. }
  14.  
  15. @Override
  16. public Animal createAnimal() {
  17. return new Shark(type, family, habitat);
  18. }
  19.  
  20. }

LionFactory.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class LionFactory implements AbstractAnimalFactory {
  4.  
  5. protected String type;
  6. protected String family;
  7. protected String habitat;
  8.  
  9. public LionFactory(String type, String family, String habitat) {
  10. this.type = type;
  11. this.family = family;
  12. this.habitat = habitat;
  13. }
  14.  
  15. @Override
  16. public Animal createAnimal() {
  17. return new Lion(type, family, habitat);
  18. }
  19.  
  20. }

Analicemos un poco el código. Cada una de las implementaciones de AbstractAnimalFactory especifican la implementación que tendrá para crear un tipo de animal.

Cada factoría concreta, tiene las 3 propiedades que necesita cada objeto para poder crearse. Así mismo, dichos datos se pasan como parámetros a su constructor para establecer los valores en las propiedades que se utilizarán para crear una instancia del objeto.

Establece las propiedades:

Código
  1. public LionFactory(String type, String family, String habitat)

Y usa esas mismas propiedades para crear el objeto:

Código
  1. @Override
  2. public Animal createAnimal() {
  3. return new Lion(type, family, habitat);
  4. }

Finlamente, se sobre-escribe el método de la factoría abstracta createAnimal() para retornar un nuevo objeto de acuerdo al tipo de factoría. Así, la factoría de Perros, creará objetos tipo Perro, la factoría de Tiburones creará objetos tipo Tiburón y la factoría de Leones, crearán objetos tipo León.

Por último, especificamos una Fábrica global que utilizará las 3 fábricas para crear los objetos (Aquí se aplica también el patrón Facade):

AnimalFactory.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public abstract class AnimalFactory {
  4.  
  5. public static Animal create(AbstractAnimalFactory factory) {
  6. return factory.createAnimal();
  7. }
  8. }

Ésta clase recibe un objeto que implemente la interfaz AbstractAnimalFactory, por lo tanto podrá recibir objetos tipo: DogFactory, SharkFactory y LionFactory. Luego simplemente usa la factoría especificada para crear un animal mediante polimorfismo.

Ahora construyamos nuestra clase principal para probar el funcionamiento del patrón:

FactoryTest.java
Código
  1. package net.elhacker.factory;
  2.  
  3. public class FactoryTest {
  4.  
  5. public static void main(String[] args) {
  6. Animal dog = AnimalFactory.create(new DogFactory("Perro","Caninos","Doméstico"));
  7. Animal shark = AnimalFactory.create(new SharkFactory("Tiburón", "Lámnidos", "Mar"));
  8. Animal lion = AnimalFactory.create(new LionFactory("León", "Felinos", "Selva"));
  9.  
  10. System.out.println(dog.toString());
  11. System.out.println();
  12. System.out.println(shark.toString());
  13. System.out.println();
  14. System.out.println(lion.toString());
  15. }
  16. }

Utilizamos el método estático create() de AnimalFactory que recibe una implementación de la interfaz AbstractAnimalFactory, en éste caso un objeto DogFactory al que se le asignan los valores “Perro”, “Caninos” y “Doméstico” y finalmente AnimalFactory llama al método createAnimal() de DogFactory para crear un animal tipo Dog y devolverlo hacia AnimalFactory que lo devuelve y lo guarda en el objeto 'dog'. El mismo procedimiento es para todas las factorías.

Salida:

Código:
Tipo de animal: Perro
Familia: Caninos
Hábitat: Doméstico

Tipo de animal: Tiburón
Familia: Lámnidos
Hábitat: Mar

Tipo de animal: León
Familia: Felinos
Hábitat: Selva

Y de ésta manera sencilla, podemos usar tantas fábricas como queramos para poder crear objetos si generar dependencias ;)

PATRÓN SINGLETON

Cuándo usarlo:

  • Cuando la aplicación requiere que solo exista una instancia de un determinado objeto.
Problema: Encapsular la configuración de una aplicación en un objeto y compartirlo con los demás objetos de la aplicación que lo requiera.

Solución: Utilizar el patrón Singleton para encapsular la configuración de la aplicación.

Para utilizar éste patrón se deben seguir dos reglas:

  • El primer paso es hacer el constructor privado para que no se pueda llamar y por lo tanto, no se puedan crear instancias.
  • El segundo paso es crear una instancia de la clase y devolverla mediante un método estático.

Teniendo en cuenta éstas reglas, creamos una clase que representa a la configuración de la aplicación:

Configuration.java
Código
  1. package net.elhacker.singleton;
  2.  
  3. import java.util.HashMap;
  4. import java.util.Map;
  5.  
  6. public class Configuration {
  7.  
  8. private Map<String,Object> appOptions = null;
  9. private static Configuration config;
  10.  
  11. private Configuration() {
  12.  
  13. }
  14.  
  15. public static Configuration getConfiguration() {
  16. if(config == null) {
  17. config = new Configuration();
  18. }
  19. return config;
  20. }
  21.  
  22. public Map<String,Object> getAppOptions() {
  23. if(appOptions == null) {
  24. appOptions = new HashMap<>();
  25. appOptions.put("theme", "dark");
  26. appOptions.put("show_hidde_files", true);
  27. }
  28. return appOptions;
  29. }
  30.  
  31. public void setAppOptions(Map<String,Object> appOptions) {
  32. this.appOptions = appOptions;
  33. }
  34.  
  35. }

Y simplemente, cuando la necesitemos, obtenemos su única instancia:

SingletonTest.java
Código
  1. package net.elhacker.singleton;
  2.  
  3. import java.util.Map;
  4.  
  5. public class SingletonTest {
  6.  
  7. public static void main(String[] args) {
  8. Configuration cfg = Configuration.getConfiguration();
  9.  
  10.                // recorre el hashmap para leer las claves y valores
  11. for(Map.Entry<String, Object> entry: cfg.getAppOptions().entrySet()) {
  12. System.out.println(entry.getKey()+": "+entry.getValue());
  13. }
  14.  
  15. }
  16.  
  17. }

Salida:

Código:
show_hidde_files: true
theme: dark


NOTA IMPORTANTE:

Generalmente, cuando se usa éste patrón, se garantiza que solamente existirá una instancia de dicha clase. Pero, si se trata de una aplicación web y se va a integrar una aplicación web con otra, es probable que ya no exista una sola instancia.

Ésto se debe a los ClassLoaders. Cada contenedor de cada aplicación web (WAR) tiene su propio ClassLoader, por lo que en el supuesto caso de una integración de WARs, las instancias serán dos y no una como se había previsto.
22  Programación / Java / [Tutorial] Hibernate, Servlet y AJAX en: 23 Enero 2015, 20:08 pm
PARTE 1: PREPARACIÓN DEL IDE (ECLIPSE)


AJAX

AJAX (Asynchronous javascript And XML), es una tecnología que es muy usada en el desarrollo de sitios o aplicaciones web. Ésta tecnología nos permite mostrar, recargar contenido sin necesidad de refrescar toda la página web.

Esto es muy útil, sobre todo desde el punto de vista de rendimiento ya que, de la forma normal cuando se recarga toda la página, se vuelven a realizar todas las peticiones que ya han sido mandadas, como son peticiones a tu hoja de estilos, a tus scripts JS, fuentes, librerías. Por otro lado, con AJAX, solo se recarga la parte que se desea.

HIBERNATE

Hibernate es un framework ORM (Object Relational Mapping). Un ORM es una aplicación que nos permite “mapear” nuestras tablas y mostrarlas en nuestra aplicación como “objetos” (Si no sabes qué es un objeto lee primero sobre POO y vuelve cuando ya hayas asimilado los conceptos).

Cuando trabajamos con un ORM, nosotros ya no tenemos que preocuparnos por iniciar la conexión, cerrarla ni construir sentencias SQL. El ORM lo hace por nosotros. Alternativamente, Hibernate nos permite utilizar “NamedQueries” que son sentencias HQL (Hibernate Query Language) y poder llamarlas cuando las necesitemos.

LEVANTAMIENTO DEL PROYECTO

Herramientas:

  • Eclipse Luna JEE
  • MySQL 5.6
  • Hibernate 4.3.8
  • Java-JSON
  • jBCrypt

    Creamos una tabla en MySQL llamada “users4login” en nuestra BBDD. Ésta tabla tendrá los siguientes atributos (vamos a mantenerlo simple):

    Código
    1. CREATE TABLE users4login
    2. (
    3.   `user_id` INT AUTO_INCREMENT NOT NULL,
    4.   `username` VARCHAR(45) NOT NULL,
    5.   `email` VARCHAR(90) NOT NULL,
    6.   `password` VARCHAR(255) NOT NULL,
    7.   CONSTRAINT pk_user PRIMARY KEY(`user_id`)
    8. );

    A continuación ingresamos los siguientes datos:
    Código:
    username: Duke
    email: duke@localtest.me
    password: 12345 (hasheado en BCrypt):
    $2a$10$1Yf2nGEJWacanDyfftzpru8vcy4L5bOB6ohJ9bG4FeSg1T568DGWa

    PREPARACIÓN DEL ENTORNO

    Registrar el driver MySQL en Eclipse:

    Nos dirigimos a Eclipse y clickeamos en:

     
    Código:
    Window -> Preferences.

    Se abrirá una ventana con muchas opciones. Nos dirigimos a:

    Código:
    Data Management -> Conectivity -> Driver definitions

    Seleccionamos Add y se nos abrirá la siguiente ventana:



    Desplegamos el combo “Vendor Filter” y seleccionamos “MySQL”. Escojemos la versión 5.1. Ahora, nos dirigmos a la pestaña “JAR list”.
    Aquí hacemos click en “Add JAR/Zip” y nos mostrará un diálogo para seleccionar el conector jdbc para MySQL. Navegamos hacia donde tengamos el conector y lo seleccionamos. Una vez seleccionado el conector, hacemos clic en “OK”.

    NOTA: Si no tenemos el conector, lo podemos bajar de Aquí.


    CREACIÓN DE UN DATA SOURCE

    Nos dirigimos hacia “data source explorer” en Eclipse y en “Database Connections” hacemos click derecho y seleccionamos “New..”.



    En la ventana que nos mostrará, podemos ver todos los gestores de bases de datos. Seleccionamos MySQL y le colocamos el nombre “Users_Test”. Damos click en “Next”. Por último, colocamos los datos de nuestra BBDD y le damos finish:




    REGISTRAR UN SERVIDOR DE APLICACIONES (TOMCAT)

    Nos dirigimos a:

     
    Código:
    Window -> Preferences -> Server -> Runtime enviroments



    En la siguiente pantalla, solo indicamos la ruta donde tenemos descomprimido Tomcat 8 y le damos finish.

    NOTA: Si no lo tienes, lo puedes descargar desde Aquí.

    Aceptamos todo y nos dirigimos en la pestaña Servers del panel derecho. Aquí damos click derecho en el panel y seleccionamos:

    Código:
    New -> Server



    Y finalmente, seleccionamos nuestro servidor Tomcat 8 que hemos registrado y le damos finish:



    Ya tenemos listo nuestro IDE para poder trabajar.



    PARTE 2: CREACIÓN Y EJECUCIÓN DEL PROYECTO


    Nos dirigimos a:

    Código:
    New -> Project -> JPA Project

     Le colocamos el nombre “LoginDemo”. Escojemos nuestro servidor Tomcat 8, hacemos click en “Modify” y seleccionamos lo siguiente:



    Aceptamos y damos click en Next. Aquí nos pedirá la conexión a la BBDD para poder mapear las tablas a clases. Seleccionamos nuestra conexión y Next:



    Debe quedar así. Por último, en la siguiente pantalla seleccionamos la casilla “Generate web xml deployment descriptor” y le damos finish.

    Nuestro proyecto debe verse así:



    Dentro de WEB-INF hay una carpeta lib. Ésta carpeta contiene las librerías que necesita la aplicación, por lo tanto, añadimos las librerías de Hibernate, MySQL, JSON y BCrypt (éstas dos últimas las veremos más adelante). Debe quedar así:



    Al estar en esta carpeta, Eclipse automáticamente las agrega al classpath.

    Ahora, necesitamos especificar la configuración de nuestra conexión a la BBDD. El archivo persistence.xml queda así:

    Código
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    5.  
    6. <!--  nombre de nuestra persistencia y tipo de transacción (local) -->
    7. <persistence-unit name="LoginDemo" transaction-type="RESOURCE_LOCAL">
    8.  
    9.    <!-- proveedor jpa - hibernate -->
    10.    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    11.  
    12.    <!-- configuracion -->
    13.    <properties>
    14.      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test_db"/>
    15.      <property name="javax.persistence.jdbc.user" value="usuario"/>
    16.      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
    17.      <property name="javax.persistence.jdbc.password" value="contra"/>
    18.      <property name="dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
    19.      <property name="hibernate.hbm2ddl.auto" value="update"/>
    20.      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
    21.      <property name="show_sql" value="true"/>
    22.    </properties>
    23.  
    24. </persistence-unit>
    25. </persistence>

  • Primero le damos un nombre a la unidad de persistencia, en éste caso LoginDemo.
  • Indicamos el proveedor de la persistencia. En éste caso Hibernate.
  • Indicamos la configuración de la conexión.

Hay que prestar especial atención en el atributo name de persistence-unit. El nombre que le pongas, será el que debes especificar más adelante cuando inicialices Hibernate.

NOTA: Las siguientes líneas dependen de tu usuario en MySQL y tu contraseña:

Código
  1. <property name="javax.persistence.jdbc.user" value="usuario"/>
  2. <property name="javax.persistence.jdbc.password" value="contra"/>

Una vez hecho esto, procedemos a mapear nuestra tabla users4login a una clase.

MAPEO DE TABLAS

Primero creamos un paquete:

Código:
Click derecho en src -> New-> Package

Le colocamos el nombre: com.mycompany.models.entities

Luego, mapeamos nuestra tabla a una clase:

Código:
Click derecho en com.mycompany.model.entities -> New-> JPA entities from tables

Veremos la siguiente ventana. Aquí seleccionamos nuestra conexión y las tablas a mapear:



Finalmente damos click en Finish. Y se nos creará una clase. Vamos a modifcarla un poco, y quedará así:

Código
  1. package com.mycompany.models.entities;
  2.  
  3. import java.io.Serializable;
  4. import javax.persistence.*;
  5.  
  6.  
  7. /**
  8.  * The persistent class for the users4login database table.
  9.  *
  10.  */
  11. @Table(name="users4login")
  12. @NamedQueries({
  13. @NamedQuery(name="Users4login.findAll", query="SELECT u FROM User u"),
  14. @NamedQuery(name="Users4login.findUser", query = "SELECT u FROM User u WHERE u.username = :username OR u.email = :username")
  15. })
  16. public class User implements Serializable {
  17. private static final long serialVersionUID = 1L;
  18.  
  19. @Id
  20. @Column(name="user_id",nullable=false)
  21. @GeneratedValue(strategy = GenerationType.IDENTITY)
  22. private int userId;
  23.  
  24. @Column(name="email",nullable=false)
  25. private String email;
  26.  
  27. @Column(name="password",nullable=false)
  28. private String password;
  29.  
  30. @Column(name="username",nullable=false)
  31. private String username;
  32.  
  33. public User() {
  34. }
  35.  
  36. public int getUserId() {
  37. return this.userId;
  38. }
  39.  
  40. public void setUserId(int userId) {
  41. this.userId = userId;
  42. }
  43.  
  44. public String getEmail() {
  45. return this.email;
  46. }
  47.  
  48. public void setEmail(String email) {
  49. this.email = email;
  50. }
  51.  
  52. public String getPassword() {
  53. return this.password;
  54. }
  55.  
  56. public void setPassword(String password) {
  57. this.password = password;
  58. }
  59.  
  60. public String getUsername() {
  61. return this.username;
  62. }
  63.  
  64. public void setUsername(String username) {
  65. this.username = username;
  66. }
  67.  
  68. }

Aquí voy a explicar algunas cosas:

  • @Table: hace referencia a la tabla en la BBDD y el atributo name indica el nombre de la tabla. Por ésta, razón le podemos poner el nombre que queramos a la clase. Para hacerlo más sencillo, se lo he cambiado por “User” en lugar de “Users4login”.
  • @Entity: indica que dicha clase es una entidad.
  • @Id: Indica que dicho campo es la llave primaria.
  • @GeneratedValue: indica el tipo de generación, en éste caso GenerationType.IDENTITY, indica que la llave primaria tendrá un generador AUTO_INCREMENT en MySQL.
  • @Column: indica que dicho campo es una columna en la base de datos. El atributo name, indica el nombre de la columna y nullable, si el campo puede recibir null.
  • @NamedQuery: especifica una consulta a la base de datos y se identifica con un nombre (atributo name).

Una vez explicado éstos conceptos, creamos un paquete llamado com.mycompany.util. Dentro de éste paquete, creamos una clase llamada EntityManagerUtil:

Código
  1. package com.mycompany.util;
  2.  
  3. import javax.persistence.EntityManagerFactory;
  4. import javax.persistence.Persistence;
  5.  
  6. /**
  7.  *
  8.  * @author Gus
  9.  */
  10. public class EntityManagerUtil {
  11.  
  12. private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("LoginDemo");
  13.  
  14.    private EntityManagerUtil() {
  15.  
  16.    }
  17.  
  18.    public static EntityManagerFactory getEntityManagerFactory()
  19.    {
  20.        return emf;
  21.    }
  22. }

Lo que hace ésta clase es leer el archivo persistence.xml y a partir de él, crear una factoría de EntityManager. EntityManager, como su nombre lo indica, es el encargado de administrar entidades (leer, persistir, actualizar, eliminar).

Aquí es cuando el atributo name del persistence-unit del archivo persistence.xml tiene importancia. "LoginDemo", es el nombre que le hemos puesto al persistence-unit, por lo tanto, debemos indicárselo en el método createEntityManagerFactory().

En ese mismo paquete, creamos una nueva clase llamada UserUtil, la cual hará las consultas a la BBDD para comprobar si un usuario existe en la tabla.

Código
  1. package com.mycompany.util;
  2.  
  3. import java.util.List;
  4.  
  5. import javax.persistence.*;
  6.  
  7. import com.mycompany.models.entities.User;
  8.  
  9. /**
  10.  *
  11.  * @author Gus
  12.  */
  13. public class UserUtil {
  14.  
  15.    public static User getUser4login(String username)
  16.    {
  17.        EntityManager em = null;
  18.        User user = null;
  19.  
  20.        /**
  21.          * @description Obtenemos el EMF y a continuación el EM. Luego iniciamos una
  22.          * transacción (em.getTransaction().begin()). Posteriormente cargamos la NamedQuery
  23.          * que creamos en la clase 'User', y le asignamos el parámetro ':username' (es equivalente
  24.          * al '?' de las sentencias preparadas), en este caso el nombre de usuario/email que recibe
  25.          * por parámetro. Ejecuta la sentencia y guarda - si encuentra - el usuario en la variable 'user'.
  26.          * Finalmente termina la transacción.
  27.          * @error Pueden ocurrir dos errores: Un error al obtener el EM o al no encontrarse el usuario.
  28.          * @after Cerramos la conexión del EM y devolvemos la varianle 'user'.
  29.          */
  30.        try
  31.        {
  32.            em = EntityManagerUtil.getEntityManagerFactory().createEntityManager();
  33.            em.getTransaction().begin();
  34.            Query query = em.createNamedQuery("User.findUser",User.class);
  35.            query.setParameter("username", username);
  36.            @SuppressWarnings("unchecked")
  37. List<User> users = (List<User>) query.getResultList();
  38.            if(!users.isEmpty())
  39.             user = (User) users.get(0);
  40.            em.getTransaction().commit();
  41.        }
  42.        catch (Exception ex)
  43.        {
  44.         ex.printStackTrace();
  45.            try { throw new Exception("Ha ocurrido un error: "+ex.getMessage());}
  46.            catch (Exception e) {
  47.             if(em.getTransaction().isActive())
  48.             em.getTransaction().rollback();
  49.            }
  50.        }
  51.        finally
  52.        {
  53.            if(em != null && em.getTransaction().isActive()) {
  54.                em.close();
  55.            }
  56.        }
  57.        return user;
  58.    }
  59. }
  60.  

Ahora bien, ¿Cómo trabaja JPA y qué significa éste código? A continuación, paso a explicarlo:

Éste método recibe como parámetro un nombre de usuario o email. A continuación, creamos un objeto tipo EntityManager (el cual ya expliqué más arriba su propósito) y un objeto tipo User para devolverlo en caso hayan coincidencias.

Dentro del try, hago una llamada al método estático getEntityManagerFactory() de la clase EntityManagerUtil, el cual me devuelve una instancia tipo EntityManagerFactory, seguidamente contateno ésta llamada al método propio de EntityManagerFactor, getEntityManager(), el cual me devolverá una instancia de tipo EntityManager para poder manejar entidades (entidad User en nuestro caso).

Obtengo una instancia Transaction del objeto EntityManager (em.getTransaction()) e inicio una nueva transacción llamando al método begin de Transaction (em.getTransaction().begin()). Listo, tengo mi transacción lista para realizar operaciones.

Creo una NamedQuery con la NamedQuery que creamos en la entidad User. El método createNamedQuery recibe dos parámetros: El nombre de la NamedQuery y la entidad propietaria. Por ésta razón, seguido del nombre de la NamedQuery le pasamos la clase User (User.class). Éste método devuelve un objeto Query construido con los parámetros enviados listo para realizar la consulta.

Antes de realizar una consulta con un Query, necesitamos establecer los valores que hemos dejado en “duda” en nuestra NamedQuery de la entidad User:

Código
  1. @NamedQuery(name="Users4login.findUser", query = "SELECT u FROM User u WHERE u.username = :username OR u.email = :username")

Lo que es equivalente a un PreparedStatement o a un stmt en PHP:

Código
  1. String query = "SELECT * FROM User u WHERE u.username = ? OR u.email = ?";
  2. ps.setString(1,”usuario”);
  3. ps.setString(2,”email@algo.com);

PHP:

Código
  1. Stmt.Bind_param(“ss”,”username”,”email@algo.com”);

Tomar atención que la NamedQuery no hace referencia al nombre de la tabla, si no al nombre de la clase, porque la clase hace referencia a la tabla.

  • Ejecutamos la sentencia: query.getResultList(). Éste método devuelve una lista de registros de acuerdo a la consulta.
  • Evaluamos, si la lista no está vacía guardamos en el objeto User que creamos al inicio del método, el Usuario encontrado traído de la BBDD.
  • Por último, cerramos la transacción: em.getTransaction().commit() y retornamos el objeto User declarado al principio del método, independientemente si está null (no se encontró Usuario).
  • En el catch, si la la conexión del EntityManager está activa, hacemos un rollback(), que es deshacer cambios.
  • En el finally, cerramos la conexión del EntityManager, sólo si ésta está activa.

En el mismo paquete, crearemos una clase llamada JSONUtil, con el siguiente código:

Código
  1. package com.mycompany.util;
  2.  
  3. import java.util.HashMap;
  4. import java.util.Map;
  5.  
  6. import org.json.JSONException;
  7. import org.json.JSONObject;
  8.  
  9. public class JSONUtil {
  10.  
  11. public static Map<String,Object> jsonToMap(String stringifyJSON)
  12. {
  13. JSONObject jsonObj = null;
  14.        Map<String,Object> data = null;
  15.        try {
  16. jsonObj = new JSONObject(stringifyJSON);
  17. data = new HashMap<>();
  18.  
  19. for(byte i=0; i<jsonObj.names().length(); i++)
  20. {
  21. data.put(jsonObj.names().getString(i), jsonObj.get(jsonObj.names().getString(i)));
  22. }
  23. } catch (JSONException e) {
  24. e.printStackTrace();
  25. }
  26.  
  27.        return data;
  28. }
  29. }

Ésta clase tiene un método bien simple. Recibe un objeto JSON en tipo String, lo convierte a JSON con ayuda de la librería json-java, lo recorre y guarda los datos que tenga en un objeto Map (si vienes de PHP, imagina que son arrays asociativos).

JSONObject.names() devuelve un objeto JSON con las llaves del objeto JSON. Y JSONObject.get(String key), devuelve el valor asociado a dicha llave.

La estructura del proyecto hasta el momento está así:



Antes de proceder:

  • Crear un folder dentro de WebContent llamado resources.
  • Dentro de resources crear 3 folders: css, js, img y vendor.
  • Descargar jQuery 2.1.1 en su versión minificada y colocarla dentro del folder vendor.
  • Descargar la hoja de estilos por ser un poco larga, y colocarla dentro del folder css.
CSS

Así quedaría nuestra estructura:




Creamos un archivo llamado index.jsp dentro del folder WebContent:

Código:
New -> JSP file.

Éste archivo tendrá el siguiente código:

Código
  1. <%--
  2.    Document   : index
  3.    Created on : 21/01/2015, 09:51:38 AM
  4.    Author     : Gus
  5. --%>
  6.  
  7. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  8. <!DOCTYPE html>
  9.    <head>
  10.        <meta charset="UTF-8">
  11.        <link rel="stylesheet" href="resources/css/font-awesome.min.css"/>
  12.        <link rel="stylesheet" href="resources/css/general.css"/>
  13.        <title>Demo Login Servlet/AJAX</title>
  14.    </head>
  15.    <body>
  16.  
  17.        <section class="main-container">
  18.  
  19.            <header>
  20.                <nav class="navbar">
  21.                    <span class="header-logo"></span>
  22.                    <h1 class="header-title">EJEMPLO LOGIN: INTEGRANDO SERVLETS Y AJAX</h1>
  23.                </nav>
  24.            </header>
  25.  
  26.            <section class="main-content">
  27.  
  28.                <form class="panel">
  29.                    <section class="panel-head">
  30.                        <span class="form-logo"></span>
  31.                        <h1 class="panel-title">Identifíquese</h1>
  32.                    </section>
  33.                    <section class="panel-body">
  34.                        <section class="form-group-hoz">
  35.                            <label for="user">Usuario o email:</label>
  36.                            <input type="text" id="user" name="user" class="textfield"/>
  37.                        </section>
  38.                        <section class="form-group-hoz">
  39.                            <label for="pass">Contraseña:</label>
  40.                            <input type="password" id="pass" name="pass" class="textfield"/>
  41.                        </section>
  42.                        <section id="error-message" class="error-message-inactive">
  43.                         <p class="error-message-text"></p>
  44.                        </section>
  45.                        <section class="form-btn-group">
  46.                            <button type="submit" class="btn btn-primary">Aceptar</button>
  47.                            <button type="button" class="btn btn-danger">Cancelar</button>
  48.                        </section>
  49.                    </section>
  50.                </form>
  51.            </section>
  52.  
  53.        </section>
  54.  
  55.        <script src="resources/vendor/jquery-2.1.1.min.js"></script>
  56.        <script src="resources/js/util.js"></script>
  57.        <script src="resources/js/call_servlet_ajax.js"></script>
  58.    </body>
  59. </html>
  60.  

Lo que nos interesa es solamente el formulario. El resto, es solo para darle estilos a la página y se vea formal. En el formulario, no especificamos el método ni el archivo que procesará la acción. Esto lo haremos posteriormente con AJAX.

Podemos ver 2 elementos tipo input, uno para el usuario y otro para la contraseña y le colocamos un id cada uno para poder identificarlos:

Código
  1. <input type="text" id="user" name="user" class="textfield"/>
  2. <input type="password" id="pass" name="pass" class="textfield"/>

También contiene dos botones: Uno para enviar los datos del formulario y otro para limpiar.

Código
  1. <button type="submit" class="btn btn-primary">Aceptar</button>
  2. <button type="reset" class="btn btn-danger">Limpiar</button>


CREACIÓN DEL ARCHIVO javascript (AJAX)

Creamos un archivo javascript llamado call_servlet_ajax.js dentro del folder resources/js. Éste archivo tendrá el siguiente código.

Código
  1. /**
  2.  * @author Gus
  3.  */
  4. $("form").on("submit", function(e)
  5. {
  6. // previene el submit normal del formulario
  7. e.preventDefault();
  8.  
  9. var username = $("#user").val();
  10. var pass = $("#pass").val();
  11.  
  12. var dataToSend = '{"username": "'+username+'", "password": "'+pass+'"}';
  13.  
  14. $.ajax(
  15. {
  16. url: "LoginController",
  17. data: { data: dataToSend },
  18. dataType: "json",
  19. type: "post"
  20. })
  21. .done(function(data)
  22. {
  23. console.log("SUCCESS");
  24. $("#error-message > p").html(data["message"]);
  25. $("#error-message > p").removeClass("error-message-text").addClass("success-message-text");
  26. $("#error-message > p").css("padding",".25rem");
  27. if($("#error-message").hasClass("error-message-inactive")) {
  28. $("#error-message").removeClass("error-message-inactive").addClass("error-message-active");
  29. }
  30. })
  31. .fail(function(jqXHR, textStatus, errorThrown)
  32. {
  33. var response = JSON.parse(jqXHR.responseText);
  34. console.log("FAIL");
  35. $("#error-message > p").html(response["message"]);
  36. $("#error-message > p").removeClass("success-message-text").addClass("error-message-text");
  37. $("#error-message > p").css("padding",".25rem");
  38. $("#error-message").removeClass("error-message-inactive").addClass("error-message-active");
  39. })
  40. .always(function(jqXHR, textStatus, errorThrown) {
  41. $("#loading-icon").removeClass("fa fa-circle-o-notch fa-spin");
  42. });
  43. });
  44.  

Primero obtenemos los valores escritos en los input. Seguidamente creamos un objeto JSON con dichos valores.

En la llamada AJAX, en la url deben poner el nombre del servlet (sin la barra al inicio), para indicar que dicho servlet manejará la petición. En data, creamos un JSON con un parámetro “data” y su valor el String con forma de JSON “dataToSend”, especificamos que se va a trabajar con json y que el método será POST.


CREACIÓN DEL SERVLET

Creamos un paquete llamado com.mycompany.controllers.servlets. Dentro de éste paquete crearemos un Servlet:

Código:
New -> Servlet

Y lo pondremos el nombre LoginController. Damos finish. Colocamos el siguiente código en el Servlet:

Código
  1. package com.mycompany.control.servlets;
  2.  
  3. import java.io.IOException;
  4. import java.io.PrintWriter;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7.  
  8. import javax.servlet.ServletException;
  9. import javax.servlet.annotation.WebServlet;
  10. import javax.servlet.http.HttpServlet;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13.  
  14. import org.json.JSONObject;
  15. import org.mindrot.jbcrypt.BCrypt;
  16.  
  17. import com.mycompany.models.entities.User;
  18. import com.mycompany.util.JSONUtil;
  19. import com.mycompany.util.UserUtil;
  20.  
  21.  
  22. @WebServlet(asyncSupported = true, urlPatterns = { "/LoginController" })
  23. public class LoginController extends HttpServlet {
  24. private static final long serialVersionUID = 1L;
  25.  
  26.  
  27.    public LoginController() {
  28.        super();    
  29.    }
  30.  
  31. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  32.  
  33. }
  34.  
  35. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  36.  
  37. response.setContentType("application/json");
  38.        PrintWriter writer = response.getWriter();
  39.  
  40.        Map<String,Object> data = JSONUtil.jsonToMap(request.getParameter("data"));
  41.        Map<String,Object> operationInfo; // contiene mensajes de los sucesos de la operación
  42. User user = UserUtil.getUser4login((String)data.get("username")); // obtiene el usuario si existe
  43.  
  44. if(user != null)
  45. {
  46. operationInfo = new HashMap<>();
  47. // si el password ingresado coincide con el password del usuario hasheado -> Login correcto
  48. if(BCrypt.checkpw((String)data.get("password"), user.getPassword()))
  49. {
  50. operationInfo.put("message", "Login correcto");
  51. }
  52. else
  53. {
  54. response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  55. operationInfo.put("message", "Contreña incorrecta");
  56. }
  57. writer.print(new JSONObject(operationInfo));
  58. }
  59. else
  60. {
  61. response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  62. operationInfo = new HashMap<>();
  63. operationInfo.put("message", "Usuario no encontrado");
  64.  
  65. writer.print(new JSONObject(operationInfo));
  66. }
  67. writer.flush();
  68.  
  69. }
  70.  
  71. }

Lo primero que hacemos es establecer una cabecera para el tipo de respuesta. Como vamos a devolver un archivo JSON, establecemos el content type como “application/json”. Luego obtenemos el objeto PrintWriter para mostrar datos en pantalla.

Aquí viene algo interesante. Hacemos una llamada al método estático jsonToMap de la clase JSONUtil que hemos creado y le pasamos lo siguiente:
Código
  1. Request.getAttribute(“data”)

¿Qué significa esto? Bien, si recuerdas, en el archivo call_servlet_ajax, en la data a enviar enviamos un JSON que tenía la llave “data” y el valor “dataToSend” que era un String con forma de JSON.

Bien, request.getAtributte(“data”), obtiene el valor de la llave “data” que le hemos enviado mediante AJAX. Entonces, ¿qué hace el método jsonToMap con éste JSON en formato String que hemos obtenido?

Lo convierte a JSON con:

Código
  1. jsonObj = new JSONObject(stringifyJSON);
  2.  
E itera el JSON para guardar las llaves y los valores en un Map (array asociativo) y lo devuelve. Entonces, el objeto Map data del Servlet, recibe el objeto Map que retorna el método jsonToMap.

Crea un objeto Map para guardar los mensajes dependiendo de los sucesos y enviarlos de vuelta por AJAX. Crea un objeto User y utiliza el método estático getUser4login de UserUtil, pasándole el nombre de usuario para que el método consulte si existe y devuelva el Usuario encontrado o null si no lo encuetra.

Evaluamos, si el usuario ha sido encontrado, comparamos las contraseñas. Para esto, hacemos uso del método estático checkpw de la librería jBCrypt para verificar la contraseña enviada por el formulario con la contraseña que está hasheada en la BBDD.

Si la contraseña coincide, guardamos en el mapa el mensaje “Login correcto”. Si la contraseña no coincide, guardamos en el mapa el mensaje “Contraseña incorrecta”.

Finalmente devolvemos el mapa convertido en JSON pasándole el mapa al constructor de la clase JSONObject. Una vez devuelto el objeto JSON al archivo javascript, podremos mostrar los mensajes que le hemos enviado desde el Servlet.

En el método success, simplemente se le asigna al elemento p hijo de #error-message el texto enviado desde el Servlet. Además de un cambio de clases CSS para mostrar el mensaje con un efecto de “fade”, en el método fail, exactamente lo mismo.


RESULTADO FINAL



Logueo exitoso:


Logueo con error de validación:


23  Programación / PHP / [Resuelto] Obtener dentro de función referencia a llamante en: 20 Enero 2015, 17:09 pm
Supongamos que tengo una interface:

Código
  1. <?php
  2.  
  3. interface ActiveRecord
  4. {
  5. public function save();
  6. public function update();
  7. public function delete();
  8. public function find($id);
  9. public function all();
  10. }

Y una clase que lo implementa:

Código
  1. <?php
  2.  
  3. abstract class ActiveRecordImpl implements ActiveRecord
  4. {
  5.  
  6. public function save()
  7. {
  8.  
  9. }
  10.  
  11. public function update()
  12. {
  13.  
  14. }
  15.  
  16. public function delete()
  17. {
  18.  
  19. }
  20.  
  21. public function find($id)
  22. {
  23.  
  24. }
  25.  
  26. public function all()
  27. {
  28.  
  29. }
  30.  
  31. }
  32.  

Además dos clases: Customer y User (con sus __get y __set) que heredan de Active Record.

Bien, supongamos que el método save() de ActiveRecord necesita guardar un objeto en la BD de la siguiente manera:

Código
  1. <?php
  2.  
  3. public function save()
  4. {
  5. $caller = REFERENCIA_AL_OBJETO_LLAMANTE;
  6. $callerClass = get_class($caller); // 'Customer' o 'User'
  7.  
  8. // obtiene el servicio dinámicamente, de acuerdo al objeto llamante
  9. $service = ServiceFactory::create($callerClass);
  10.  
  11. // le envía el objeto llamante al servicio Customer para que
  12. // lo guarde en la BD
  13. $service->save($caller);
  14. }

El método save() obtiene una referencia al método que lo llamó (que puede ser un objeto tipo Customer o User), y obtiene la clase del objeto llamante para poder crear su servicio respectivo. Posteriormente, el servicio guarda el objeto por medio del método save()

La clase ServiceFactory solo crea y devuelve un servicio para el tipo de clase indicado:

Código
  1. <?php
  2.  
  3. public static function create($class)
  4. {
  5. switch ($class) {
  6. case 'Customer':
  7. return new CustomerService();
  8.  
  9. case 'User':
  10. return new UserService();
  11. }
  12. }
  13.  

Y los servicios de 'Customer' y 'User' hacen uso de sus DAOs respectivos:

Código
  1. <?php
  2.  
  3. class CustomerService
  4. {
  5. public function save(Customer $customer)
  6. {
  7. new CustomerDAO()->save($customer);
  8. }
  9. }

Código
  1. <?php
  2.  
  3. class UserService
  4. {
  5. public function create(User $user)
  6. {
  7. new UserDAO()->save($user);
  8. }
  9. }

Ahora, para guardar un objeto 'Customer' y 'User' se haría lo siguiente:

Código
  1. <?php
  2.  
  3. $customer = new Customer();
  4. $customer->username = $username;
  5. $customer->email = $email;
  6. $customer->password = $password;
  7. $customer->dni = $dni;
  8. $customer->address = $address;
  9.  
  10. $user = new User();
  11. $user->username = $username;
  12. $user->email = $email;
  13. $user->password = $password;

¿Es ésto posible? Si es es así, ¿cómo podría obtener la referencia del objeto llamante?
Páginas: 1 2 [3]
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines