Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - Gus Garsaky

#1
Java / Timer & TimerTask
Julio 29, 2015, 05:24:47 PM

La clase Timer es una clase muy útil para situaciones particulares. Estas situaciones pueden ser por ejemplo, cuando necesitamos hacer algo cada X tiempo. Quizás hayas visto en algunas webs que aparecen ofertas o anuncios cada X tiempo, pues bien, esto se hace gracias a los Timers.

Vamos a realizar un pequeño timer que hace un conteo regresivo imprimiendo en pantalla la hora actual. Primero hagamos una clase que muestre la hora actual.

Código: java

public class Display {

    public void printTime(int hour, int minute, int second) {
        String fullHour = "";

        fullHour += (hour > 9) ? ":" + hour : "0" + hour;
        fullHour += (minute > 9) ? ":" + minute : ":0" + minute;
        fullHour += (second > 9) ? ":" + second : ":0" + second;

        System.out.println(fullHour);
    }
}


Nada del otro mundo, solo le da formato a una hora recibida como 3 parámetros: hora, minuto y segundo. Esta clase la usaremos en el Timer para mostrar la hora actual.

Código: java

import java.util.Timer;
import java.util.TimerTask;

public class Timeout {

    private int hour = 0;
    private int minute = 0;
    private int second = 11;
    private Timer timer;
    private boolean isTimerRunning;
    private Display display;

    public Timeout() {
        timer = new Timer();
        display = new Display();
    }

    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            isTimerRunning = true;
            if(second > 0) {
                second--;
            } else {
                second = 59;
                if(minute > 0) minute--;
                else {
                    minute = 59;
                    if(hour > 0) hour--;
                    // si segundo = 0, minuto = 0 y hora = 0,
                    // cancelamos el timer
                    else {
                        isTimerRunning = false;
                        timer.cancel();
                        timer.purge();
                    }
                }
            }
            if(isTimerRunning)
                display.printTime(hour, minute, second);
        }
    }; // fin timertask

    public void start(int timeout, int interval) {
        timer.schedule(task, timeout, interval);
    }

} // fin clase


El código es autoexplicado. En lo que debemos de fijarnos es el TimerTask. La TimerTask nos permite realizar tareas en un thread separado. Esto es así, porque por lo general, cuando usamos un Timer es para que se esté ejecutando cada cierto tiempo un código de forma paralela, esto eso, multithreading programming (programación multi hilo). El código del TimerTask solo hace una serie de comprobaciones para disminuir la hora, minuto y segundo. Luego, llama al método printTime del objeto Display, pasándole la hora, minuto y segundo y que este método dará forma e imprimirá.

Para empezar un timer, debemos de llamar al método schedule el cual recibe 3 parámetros:

  • Un objeto TimerTask
  • Un timeout (tiempo de espera para que empiece a ejecutarse)
  • Un intervalo (la tarea se ejecutará cada X tiempo)

    Los dos últimos parámetros se debe especificar en milisegundos, así, 1000 es equivalente a 1 segundo. Por último, hagamos nuestra clase principal.

    Código: java

    public class TimerTest {

        public static void main(String[] args) {
            Timeout timeout = new Timeout();
            timeout.start(0, 1000);
        }
    }


    En la clase principal decimos que el timer tendrá un delay de 0s y un intervalo de 1s. Si compilamos y corremos:

    Código: dos
    javac Display.java
    javac Timeout.java
    javac TimerTest.java

    java TimerTest


    Obtendremos lo siguiente:



    ¿Posibilidades? La que puedas imaginar, así que piensa como sacarle provecho en tu próximo proyecto.
#2
Java / Re:Conversor de temperatura
Julio 29, 2015, 05:10:14 PM
Buen aporte, rush.
#3
El hecho que no lance una Exception ya de por sí es raro. Si dices que has probado el procedure en el Workbench, el error se puede deber que estás pasando un id incorrecto al procedure.

Si hubiese algún tipo de excepción lo capturarías y como dices, no se lanza ninguna y teniendo en cuenta que tu procedure está testeada, solo se me ocurre que el error pudiese ser un id incorrecto.
#4
Java / Re:Mini curso Java [En progreso]
Julio 25, 2015, 11:54:42 AM
Sobrecarga de constructores y métodos (Overloading)

Como vimos anteriormente, un método nos sirve para agrupar lógica en pequeños módulos para luego ser usados cuando los necesitemos. Vimos también que un método tiene cierta estructura:

Código: php
<visibilidad> <modificador> <nombre> (<parámetros>) { ... }


Supongamos que tengo el siguiente método:

Código: java

public String createCar(String brand) {
    return "Auto " + brand + " creado";
}


El método solo recibe un parámetro que representa a la marca del automóvil a crear y retorna un String informando del auto creado. Pero, ¿que pasa si quiero crear un carro con una marca y un color? Una opción podría ser crear otro método:

Código: java

public String createCarWithColor(String brand, String color) {
    return "Auto " + brand + " color " + color + " creado";
}


Y si quisiese crear un carro con marca, color y además de X HP (caballos de fuerza), tendría que crear otro método llamado createCarWithColorAndHP. Afortunadamente, no necesitas hacer eso, solo tienes que sobrecargar el método createCar.

Código: java

public String createCar(String brand) {
    return "Auto " + brand + " creado";
}

public String createCar(String brand, String color) {
    return "Auto " + brand + " color " + color + " creado";
}

public String createCar(String brand, String color, String hp) {
    return "Auto " + brand + " color " + color + " y " + hp + " caballos de fuerza creado";
}


A esto se le llama sobrecarga. Estamos sobrecargando un método para que se comporte diferente de acuerdo a los parámetros recibidos. Para sobrecargar un método debemos de cumplir la siguiente regla:

  • Un método sobrecargado debe cambiar los parámetros del método a sobrecargar(menos o más).

    El siguiente ejemplo muestra una sobrecarga errónea de un método:

    Código: java

    public int sumar(int x, int y) { ... }

    public float sumar(int x, int y) { ... } // error, debe cambiar parámetros



    SOBRECARGA DE CONSTURCTORES

    La sobrecarga de constructores es igual que la de los métodos. Veamos un ejemplo para más detalle:

    Código: java

    public class User {

        private String id;
        private String name;

        public User() {
    this("", "");
        }

        public User(String id) {
            this(id, "");
        }

        public Usuer(String id, String name) {
    this.id = id;
    this.name = name;
        }

    }


    Vemos que hemos sobrecargado el constructor por defecto User(), el primero pasándole un String id, y el segundo un String id y un String name. Lo curioso aquí es la instrucción this(...). Veremos más adelante el concepto de this, pero como adelanto les diré. La palabra reservada this hace referencia a la instancia. Es decir, si hago: this(id, ""), -> this = objeto User -> objeto User(id, ""). Por lo tanto, como this hace referencia a la instancia, podemos acceder tanto a los contructores, métodos y atributos. Ya veremos más acerca de this un poco más adelante.
#5
Java / CDI 1.1 - Context and Dependecy Injection
Julio 14, 2015, 10:49:24 PM
CDI

Context and Dependency Injection o Contexto e inyección de depencias en español, es un poderoso elemento de la plataforma Java EE el cual nos provee un poderoso mecanismo de inyección de depencias. Además, nos provee de muchas utilidades, como interceptores, calificadores, eventos, decoradores, productores y trituradores, entre otros.

CDI es un concepto incomprendido por gran parte de los programadores Java EE. En el presente documento, intentaremos despejarnos las dudas. Empecemos.


Dependency Injection

Dependency Injection (DI) es un patrón de diseño que desacopla componentes depenientes. Es parte de inversion of control (IoC) donde el objetivo que es invertido es el proceso de obtención de dependencias necesarias. El término fue originalmente dicho por Martin Fowler. Una manera de pensar en DI en un entorno manejado como Java EE es pensar en JNDI al revés. En lugar que un objeto busque por otros, el contenedor inyecta su dependencia por ti. Esto es llamado Principio Hollywood, "Don't call us (buscar objetos), we'll call you (inyectar objetos)".


Gestión del ciclo de vida

El ciclo de vid de un POJO es muy simple, creas una instancia de una clase usando la palabra reservada new y esperas a la recolección de basura para que la elimine y libere algo de memoria. En su lugar, necesitas inyectar el bean y el contenedor hace el resto, esto quiere decir que el contenedor es el único responsable de gestionar el ciclo de vida de un bean: este lo crea, este lo destruye. Así que, ¿como inicializas un bean si tu no llamas a un constructor? El contenedor te brinda un manejo luego de construir la instancia y antes de destruirla. Estas anotaciones son:


  • @PostConstruct: un método anotado con esta anotación es llamado inmediatamente después que el contenedor haya inyectado el bean (instanciado).
  • @PreDestroy: un método anotado con esta anotación es llamado justo antes que el recolector de basura elimine el bean.


CDI Bean

Un bean CDI puede ser cualquier tipo de clase que contenga lógica de negocio. Este puede ser llamado directamente por código Java vía inyección o puede ser invocado vía El desde una página JSF. Un bean es un POJO que no extiende de nada, puede inyectar referencias en otros beans (@Inject), tiene su ciclo de vida manejado por el contenedor y puede tener sus propios metodos incerptores:

Código: java
public class BankLoan {
    @Inject
    private AccountChecker accountChecker;
    private Date dateOfLoan;

    @PostConstruct
    private void initializeDate() {
        dateOfLoan = new Date();
    }
}


En el ejemplo anterior, se está inyectando la dependencia del bean accountChecker, esto lo hace el contenedor. El método initializeDate anotado con @PostConstruct será llamado automáticamente luego de que se instancie la clase BankLoan. Los CDI beans tienen ciertas características que deben cumplirse de manera obligatoria. Estas son:

  • No debe ser una interna no estática.
  • Tiene un constructor por defecto sin parámetros o debe declarar un constructor anotando @Inject.
  • Es una clase concreta (no debe extender) o  es anotada con @Decorator

    Para mayor comprensión, veamos el siguiente diagrama de clases:


    ¿Cómo conectamos CDStore y CreditCard? La forma que todos conocemos es mediante el operador new:

    Código: java
    public class CDStore {
        private CreditCard creditCard;
       
        public CDStore() {
            creditCard = new Visa();
        }
        public void buyItems() {
            // pagar con la tarjeta
      creditCard.pay();
        }
    }


    El código anterior es bien simple  y es lo que nosotros hacemos y conocemos. Pero, ¿que pasaría si quisieras pagar con MasterCard?, ¿Necesitarías crear una (otra) instancia de MasterCard? Una solución pasar su dependencia por medio del constructor de una clase externa que haga uso de la clase CDStore:

    Código: java
    public class CDStore {
        private CreditCard creditCard;

        public CDStore(CreditCard creditCard) {
            this.creditCard = creditCard;
        }
        ...
    }


    En otra clase:

    Código: java
    CDStore cdStore = new CDStore(new MasterCard());


    Ésto es lo que se conoce como inversion of control (IoC) o inversión de control (en español). La dependencia se inyecta desde una tercera clase, de ésta manera no creamos un vínculo entre CDStore y CreditCard.

    Inyección (@Inject)

    Java EE es un entorno gestionado, así que quizás no necesitas construir las dependencias tú mismo, en su lugar, puedes dejar que el contenedor inyecte las dependencias de los beans por tí. CDI tiene la habilidad de inyectar dependencias de prácticamente lo que sea. Veamos un ejemplo:

    Código: java
    public class CDStore {
        @Inject
        private CreditCard creditCard;
       
        public void buyItems() {
            creditCard.pay();
        }
        ...
    }


    Como se puede observar, no necesitamos construir la dependencia de creditCard, se lo dejamos al contenedor. La implementación se vería así:

    Código: java
    public class Visa implements CreditCard {
        @Override
        public void pay() {
             // logica de negocio
        }
    }


    Nota que solo hemos especificado una implementación de la interface CreditCard. Si hubiéramos creado otra implementación, la inyección no se podría realizar porque el contenedor no sabría qué dependencia inyectar. Esto, lo resolveremos más adelante con calificadores (@Qualifier).




    ANEXO

    Puntos de inyección

    Una inyección puede llevarse a cabo en tres puntos:

  • Por propiedad:

       
    Código: java
    @Inject
    private CreditCard creditCard;


       Nota que no es necesario especificar setter para la propiedad.
  • Por constructor:

       
    Código: java
    @Inject
             private CDStore (CreditCard creditCard) {
                 this.creditCard = creditCard;
            }


       La regla aquí es que la inyección por constructor se puede realizar solo una vez (por    clase).

  • Por setter:
       
    Código: java
    @Inject
            public void setCreditCard(CreditCard creditCard) {
                this.creditCard = creditCard;
            }



    Ésto es un anexo, porque en realidad no importa en qué punto hagas la inyección. Por propiedad, constructor o setter, para el contenedor le da igual. Por lo tanto, escoger uno u otro, es simplemente opción personal.




    Injección por defecto (@Default)

    Tu puedes tener una dependencia para inyectar por defecto. Por ejemplo, nosotros tenemos solo una implementación, aquella, tendrá implícitamente el modificador @Default. Así, ésto:

    Código: java
    @Inject
    private CreditCard creditCard;


    Es igual que:

    Código: java
    @Inject @Default
    private CreditCard creditCard;


    Podemos apreciarlo en una imagen:


    Si tienes una implementación, el contenedor inyectará la única dependencia existente. Si por otro lado, tienes muchas implementaciones de una interface, el contenedor no sabrá cuál implementación debe inyectar, y aquí es cuando toman tanta importancia los calificadores.


    Calificadores (@Qualifier)

    Cuando el sistema inicia, el contenedor CDI debe validar que todos los beans satisfasgan cada punto de inyección existente. Si alguna dependencia de un bean no puede ser satisfecha por el contenedor CDI, entonces la aplicación no se podrá desplegar informando de una insatisfacción de dependencia. Si hay una sola implementación disponible, la inyección funcionará aplicando la dependencia única, que es marcada implícitamente con @Default.

    Si hay más de usa implementación y tratamos de desplegar, la aplicación no desplegará porque dectectará ambiguación en las dependencias, es decir, si hay 2 o más implementaciones, ¿cómo sabe el contenedor qué dependencia inyectar? Aquí es donde toman importancia los calificadores. Veamos el siguiente diagrama de clases que ilustra la situación actual:


    Podemos ver que hemos creado una nueva implementación, llamada MasterCard, la misma está anotada bajo el calificador MasterCardPay. Además, vemos como una nueva clase, CDStoreWithQualifier inyecta la nueva dependencia. Esto lo hará mediante el calificador. Veamos el ejemplo en código (solo las nuevas clases):

    Calificador MasterCardPay:

    Código: java
    @Qualifier
    @Retention(RUNTIME)
    @Target({FIELD, TYPE, PARAMETER, METHOD})
    public @interface MasterCardPay {}


    Implementación MasterCard:

    Código: java
    @MasterCardPay
    public class MasterCard implements CreditCard {
        private Logger logger = Logger.getLogger(MasterCard.class.getName());
       
        @Override
        public void pay() {
            logger.log(Level.INFO, "Pagando con MasterCard");
        }
    }


    La anotación @Retention, especifica el nivel de retención de la anotación. Existen 3 tipos:

  • SOURCE: la anotación será visible en el código fuente, una vez que se compila la aplicación, es descartada. La anotación no será escrita en bytecode. Por ejemplo, @Override.
  • CLASS: La anotación se descarta durante la carga de la clase por parte del ClassLoader. Sí se escribe en bytecode.
  • RUTNIME: No se descarta. Ésta anotación estará siempre disponible por reflection en tiempo de ejecución.

    Las primeras dos anotaciones no podrán ser inspeccionadas en tiempo de ejecución, mientras que la última sí. Esta es la diferencia entre las 3.
    La anotación @Target, especifica que elementos podrán ser afectados por la anotación. Hay varias opciones:

  • ElementType.ANNOTATION_TYPE
  • ElementType.CONSTRUCTOR
  • ElementType.FIELD
  • No tienes permitido ver los links. Registrarse o Entrar a mi cuenta_VARIABLE
  • ElementType.METHOD
  • ElementType.PACKAGE
  • ElementType.PARAMETER
  • ElementType.TYPE

    Creo que la mayoría se explican solas. Las dos anotaciones que son un poco confusas son ANNOTATION_TYPE y TYPE. ANNOTATION_TYPE significa que la anotación solo podrá ser usada para anotar otras anotaciones, como @Retention. TYPE significa cualquier tipo de elemento (clases, interfaces, enums, anotaciones, etc).

    Comprendidos ya estos conceptos, vamos a ver la clase que inyecta la nueva implementación:

    Código: java
    public class CDStoreWithQualifier {
        @Inject @MasterCardPay
        private CreditCard masterCard;
       
        public void buyItems() {
            masterCard.pay();
        }
    }


    Observa lo sencillo que es inyectar múltiples dependencias, solo basta con indicar el calificador de la dependencia que debe ser inyectada. Es posible también anotar un elemento con múltiples anotaciones. Java no nos limita en este aspecto. Por ejemplo:

    Código: java
    @Inject @Visa @Gold
    private CreditCard creditCard;



    Productores (@Produces)

    Como ya hemos visto, podemos inyectar CDI beans dentro de otros CDI beans. Pero, si quisiéramos inyectar POJOs, o tipo de datos primitivos, ¿sería eso posible? La respuesta corta es no y la razón es la siguiente.

    Por defecto, no puedes inyectar clases como String o Date y la razón, es porque ellas están empaquetadas en el archivo rt.jar (Java runtime enviroment classes) y este no contiene un descriptor de beans beans.xml. Esto es importante tenerlo presente, si un archivo no tiene un descriptor beans.xml bajo el directorio META-INF, CDI no activará el descrubimiento de beans (bean discovery) y los POJOs no podrán ser tratados como beans y por ende, no podrán ser inyectados. Afortunadamente, podemos revertir esta situación con ayuda de los productores. Veamos un ejemplo:


    Veamos que la clase CDSerialProducer tiene 3 anotaciones, las cuales pasamos a describir:

  • @Produces: utilizada para producir un bean listo para inyección.
  • @CDSerialPrefix: anotación propia.
  • @RandomNumber: anotación propia.

    Primer veamos nuestras anotaciones, serán igual que los calificadores que ya hemos visto:

    Código: java
    @Retention(RUNTIME)
    @Target({FIELD, TYPE, PARAMETER, METHOD})
    public @interface CDSerialPrefix { }


    Código: java
    @Retention(RUNTIME)
    @Target({FIELD, TYPE, PARAMETER, METHOD})
    public @interface RandomNumber { }


    Ahora, veamos como producimos beans para inyectar de tipo String e int:

    Código: java
    public class CDSerialProducer {
        @Produces @CDSerialPrefix
        private final String prefix = "T9-SK534";
       
        @Produces @RandomNumber
        public int generateNumber() {
            return new Random().nextInt(10000);
        }
    }


    La anotación @Produces, producirá una propiedad y un método para inyectar. Seguido de @Produces, debemos de especificar el calificador con el cual se va a inyectar la propiedad y el método. En este caso son @CDSerialPrefix y @RandomNumber. Ahora veamos como los inyectamos:

    Código: java
    public class CDService {
        @Inject @CDSerialPrefix
        private String prefix;
        @Inject @RandomNumber
        private int number;
        private Logger logger = Logger.getLogger(getClass().getName());
       
        public void createCD() {
            logger.log(Level.INFO, "Creando CD con serial: {0}", (prefix + number));
        }
    }


    Si ejecutamos el proyecto, podemos ver que se inyectan satisfactoriamente los valores de la propiedad y el método dentro de las propiedades de CDService. Gracias a los productores, podemos inyectar prácticamente lo que sea.




    ANEXO

    API de InjectionPoint

    Los atributos y propidades producidad por productores (@Produces) no necesitan saber ningún tipo de información acerca de donde ellos son inyectados. Sin embargo, hay ciertos casos  donde podría ser de utilidad que el bean a inyectar sepa en qué punto de inyección se ha llevado a cabo la inyección. Un ejemplo común de ésto, es generar una dependencia dinámica de Logger (para crear logs). Generalmente, siempre creamos un Logger así:

    Código: java
    Logger logger = Logger.getLogger(TuClase.class.getName());


    Lo anterior funciona correctamente, pero también podríamos generar depenencias dinámicamente. Veamos primero la API de InyectionPoint:


    Podemos usar esta API para generar una dependencia dinámica para Logger. Veamos un ejemplo:

    Código: java
    public class LoggerProducer {
        @Produces
        private Logger createLogger(InyectionPoint ip) {
            return Logger.getLogger(ip.getMember().getDeclaringClass().getName());
        }
    }


    Perfecto. Ahora tan solo debemos inyectar al logger de ésta manera:

    Código: java
    @Inject Logger log;


    ¿Cómo ocurre la magia? Bien, el contenedor CDI buscará cualquier bean de tipo Logger listo para inyectar, y lo encuentra gracias a que hemos anotado el método con @Produces, lo que deja ese método listo para su inyección (como con @CDSerialPrefix y @RandomNumber del ejemplo anterior).





    Trituradores (@Disposes)

    Ahora sabemos como producir beans o valores para inyectar, pero algunos beans podrían necesitar ser cerrados, como es el caso de conexiones JDBC o un EntityManager. Por lo tanto, algunos beans producidos deberían asegurarse de ser cerrados. Aquí es donde toman importancia los Disposers. Los Disposers son lo  contrario a Producers, éste último crea beans o valores para inyectar, mientras que el primero se encarga de destruirlo.
    Veamos un ejemplo con una clásica conexión JDBC:

    Código: java
    public class DerbyJdbcProducer {
        private final static String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
        private final static String URL = "jdbc:derby://localhost:1527/CDI";
        private final static String USER = "duke";
        private final static String PASS = "1234";
        @Inject
        private Logger logger;
       
        @Produces
        public Connection createConnection() {
            Connection con = null;
            try {
                Class.forName(DRIVER).newInstance();
                con = DriverManager.getConnection(URL, USER, PASS);
                logger.log(Level.INFO, "Creando conexión JDBC con Derby");
            } catch(InstantiationException | IllegalAccessException |
                    ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
            return con;
        }
        public void closeConnection(@Disposes Connection con) {
            try {
                con.close();
                logger.log(Level.INFO, "Cerrando conexión JDBC con Derby");
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    Un método disposer debe obligatoriamente recibir un parámetro @Disposes y el tipo del parámetro debe ser el mismo que el tipo devuelto con el método productor (@Produces) y su calificador (@Qualifier) en caso haya más métodos productores con el mismo tipo de retorno. Dado que el ciclo de vida de un bean inyectado depende del contexto en donde se inyecta, cuando se elimina el bean que contiene el bean inyectado, éste se destruirá también. Pero si no hubiésemos escrito el método disposer, la conexión nunca se hubiese cerrado por ser ésta independiente del contexto. Cuando el bean que contiene el bean inyectado es eliminado, antes que eso ocurra, se ejecutará el método disposer del bean inyectado, en este caso, cerrando la conexión JDBC.




    ANEXO

    Alcances (scopes)

    Todo objeto cuyo ciclo de vida es manejado por CDI tiene un alcance definido y un ciclo de vida que está ligado a un contexto específico. En Java, el alcance de un POJO es muy simple: tu creas una instancia de una clase usando la palabra reservada new y tu confías en el recolección de basura para deshacerse  de él y liberar alguna memoria. Con CDI, un bean es ligado al contexto y este lo mantiene en ese contexto hasta que el bean es destruido por el contenedor. No hay manera para remover manualmente un bean desde un contexto.

    CDI define los siguientes alcances y permite puntos de extesión para crear tu propio alcance:

  • Application scope(@ApplicationScoped): Se extiende por la duración entera de una aplicación. El bean es creado solo una vez por duración de la aplicación y es descartado cuando la aplicación se apaga. Este alcance es util para cases utilitarias o helpers, u objetos que guardan datos compartidos por la aplicación entera (pero deberías ser cuidadoso con los problemas de concurrencia cuando los datos tienen que ser accesados por muchos threads).

  • Session scope(@SessionScoped): Se extiene a través de muchas peticiones HTTP o muchas invocaciones de métodos por una sola sesión de usuario. El bean es creado por la duración de una sesión HTTP y es descartado cuando la sesión termina. Este ámbito es para objectos que son necesarios a lo largo de la sesión como preferencias de usuario o credenciales de login.

  • Request scope(@RequestScoped): Corresponde a una simple petición HTTP o a una invocación de un método. El bean es creado para la duración de una invocación a un métod y es descartado cuando el método termina. Esto es usado para clases de servicio o beans JSF que son solo necesitados durante la duración de una petición HTTP.

  • Conversation scope(@ConversationScoped): Se extiende entre múltiples invocaciones dentro de los límites de una sesión con puntos de inicio y fin determinados por la aplicación. Este alcance es usado a través de múltiples páginas como parte de un flujo de trabajo de múltiplos pasos.

  • Dependent pseudo-scope(@Dependent): El ciclo de vida es el mismo que del cliente. Un bean @Dependent es creado cada vez que este es injectado y la referencia es eliminada cuando el objetivo de la inyección es eliminado. Éste es el alcance por defecto para CDI.

    Como puedes ver, todos los alcances tienen una anotación que tu puedes usar en tus beans CDI (todas ellas están en el paquete javax.enterprise.context). Los primeros tres alcances son bien conocidos. Por ejemplo, si tu tienes un bean de alcance de sesión como de un carro de compras, el bean será automáticamente creado cuando la sesión inicia (primera vez que un usuario se loguea) y es  destruido automáticamente cuando la sesión termina.

    CitarNotas: El ciclo de vida de un bean @Dependent está ligado al ciclo de vida del bean que lo contiene. Se crea cuando el bean que lo contiene es instanciado y es eliminado cuando el mismo es eliminado. Es el scope por defecto.


    Conversation (@ConversationScoped)

    El alcande conversation es un poco diferente al resto. Este alcance mantiene estados asociados en un intérvalo de tiempo, a través de múltiples peticiones y es eliminado programáticamente (explícitamente) por la aplicación. Este alcance es usado mayormente en procesos  donde se requiere mantener valores durante múltiples peticiones HTTP pero sin extenderse demasiado, como un alcance @SessionScoped.

    API de Conversation:


    La principal diferencia con otros alcances, es que ellos son eliminados automáticamente por el contenedor al cabo de un tiempo cuando termina la petición, sesión, aplicación, etc. En Conversation, nosotros debemos encargarnos terminar su ciclo de vida. Por ejemplo, un bean @ConversationScoped puede ser usado para un asistence de registro de usuario. Deseamos mantener los datos que se van registrando del usuario a través de distintas páginas y que elimine el bean al terminar el registro:

    Código: java
    @Named
    @ConversationScoped
    public class UserRegistrationWizard implements Serializable {
        @Inject
        private Conversation conversation;
        @Inject
        private User user;
        @Inject
        private transient Logger logger;
       
        @PostConstruct
        public void init() {
            conversation.begin();
        }
        public User getUser() {
            return user;
        }
        public void register() {
            conversation.end();
            logger.log(Level.INFO, "Usuario registrado:");
            logger.log(Level.INFO, "Email: {0}", user.getEmail());
            logger.log(Level.INFO, "Password: {0}", user.getPassword());
            logger.log(Level.INFO, "Fecha Nacimiento: {0}", user.getBirthDate());
        }
    }


    Para probarlo, crea distintas páginas JSF y en cada una pon un formulario con un input text para el email, un input secret para el password y un input text para la fecha de cumpleaños:

    start.xhtml

    Código: html5
    <h3>Inicio de registro</h3>
            <h:form>
                <h:outputLabel for="email" value="Email:"/>
                <h:inputText id="email" value="#{userRegistrationWizard.user.email}"/>
                <h:commandButton value="Siguiente" action="step2"/>
            </h:form>


    step2.xhtml

    Código: html5
    <h3>Paso 2</h3>
            <h:form>
                <h:outputLabel for="password" value="Contraseña"/>
                <h:inputSecret id="password" value="#{userRegistrationWizard.user.password}"/>
                <h:commandButton value="Siguiente" action="end"/>
            </h:form>


    end.xhtml

    Código: html5
    <h3>Paso 3</h3>
            <h:form>
                <h:outputLabel for="birthDate" value="F. Nacimiento:"/>
                <h:inputText id="birthDate" value="#{userRegistrationWizard.user.birthDate}">
                    <f:convertDateTime pattern="dd-MM-yyyy"/>
                </h:inputText>
                <h:commandButton value="Registrar" action="#{userRegistrationWizard.register()}">
                    <f:ajax execute="@form" />
                </h:commandButton>
            </h:form>


    Al llegar a la últma página y pulsar el botón registrar, se ejecuta el método registrar(), donde termina la conversación y se imprime toda la información del usuario recolectada durante la misma.


    Interceptores (@Interceptor)

    Los interceptores te permiten agregar conceptos a través de tus beans. Cuando un cliente invoca a un método en un Managed Bean (y por lo tanto a CDI beans, EJB, RESTful, ...), el contenedor puede interceptar la invocación y procesar lógica de negocio antes que el método del bean sea invocado. Los interceptores vienen en cuatro tipos:

  • Interceptores a nivel de constructor: el interceptor es asociado con un constructor de la clase objetivo (@AroundConstruct)
  • Interceptores a nivel  de método: el interceptor es asociado con un método de negocio específico (@AroundInovke)
  • Interceptores por tiempo límite: el interceptor se interpone en el tiempo límite de los métodos (@AroundTimeout (usado solo con EJB timer)
  • Interceptores de ciclo de vida: el interceptor se interpone en las llamadas de eventos del ciclo de vida del objetivo (@PostConstruct y @PreDestroy).

    Un método anotado con @AroundInvoke, debe tener el siguiete patrón de firma:

    Código: java
    @AroundInvoke
    Object <Método> (InvocationContext ic) throws Exception;


    Las siguientes reglas aplican a un método @AroundInvoke:

  • El método puede tener cualquier modificador de visibilidad pero no debe ser static o final.
  • El método debe tener un parámeto InvocationContext y debe retornar Object, el cual es el resultado del método objetivo invocado.
  • El método debe lanzar una excepción comprobada.
  • El objeto InvocationContext permite a los interceptores controlar el comportamiento de la cadena de invocaciones. Si muchos interceptores son encadenados, la misma instancia de InvocationContext es pasada a cada interceptor, quien puede agregar datos contextuales para ser procesador por otros interceptores.

    Echememosle un ojo a la API de InvocationContext:


    Otra forma de trabajar con interceptores, es que estos pueden estar en una clase y luego ser referenciada por la clase que los necesite:

    Código: java
    @Interceptors(MisInterceptores.class)
    public class MiClase { ... }


    O en algún método:

    Código: java
    @Interceptors(MisInterceptores.class)
    public void doSomething() { ... }


    Si queremos que un método no sea interceptado, lo anotamos con @ExcludeClassInterceptors.

    Veamos un ejemplo:


    Vamos con nuestro interceptor:

    Código: java
    public class LoginInterceptor {
        private @Inject Logger logger;

        @AroundConstruct
        private Object init(InvocationContext ic) throws Exception {
            logger.log(Level.INFO, "--- ConstructInterceptor ---");
            logger.log(Level.INFO, "Entrando al constructor");
            try {
                return ic.proceed();
            } finally {
                logger.log(Level.INFO, "Saliendo del constructor");
            }
        }
        @AroundInvoke
        private Object interceptMethod(InvocationContext ic) throws Exception {
            logger.entering(ic.getTarget().toString(), ic.getMethod().getName());
            try {
                logger.log(Level.INFO, "método interceptado");
                return ic.proceed();
            } finally {
                logger.exiting(ic.getTarget().toString(), ic.getMethod().getName());
            }
        }   
    }


    Y nuestra clase que se verá afectada por nuestro inteceptor:

    Código: java
    @Interceptors(LoginInterceptor.class)
    public class LoginService {
        private @Inject Logger logger;
       
        public void login() {
            logger.log(Level.INFO, "Haciendo login");
        }
    }


    Cuando se instancie ésta clase y cuando llamemos al método login(), se ejecutarán los interceptores que hemos definido en LoginInterceptor, que son uno a nivel de método y otro a nivel de constructor.

    CitarTambién se pueden proporcionar más de un interceptor, deben separarse por comas:

    Código: java
    @Interceptors(LoginInterceptor.class, AccountcheckerInterceptor.class)
    public class LoginService { ... }


    Los interceptores serán ejecutados en el orden que son escritos.


    Vinculación de interceptores (@InterceptorBinding)

    Una manera más práctica y que es la más aconsejada por su bajo acoplamiento, es Interceptor Binding. Es muy fácil de hacer, solo basta crear un Interceptor Binding que es muy parecido a un calificador:

    Código: java
    @InterceptorBinding
    @Retention(RUNTIME)
    @Target({METHOD, TYPE})
    public @interface Loggable { }


    El interceptor cambia muy poco, solo debemos anotar con @Interceptor y el interceptor binding que hemos creado, en nuestro caso, @Loggable:

    Código: java
    @Interceptor
    @Loggable
    public class LoginInterceptorBinding { ... }


    Ahora, en nuestra clase objetivo (donde se interceptarán los métodos), solo debemos anotar la clase o los métodos/constructor que queremos interceptar. Por ejemplo:

    Código: java
    public class LoginServiceBinding {
        @Loggable
        public void Login() {
            ...
        }
    }


    Como se puede observar, es mucho más práctico de esta forma y además, más agradable a la vista. Para poder utilizar ésta manera, debemos de agregar nuestro interceptor binding en el descriptor de beans (beans.xml):

    Código: xml
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
           bean-discovery-mode="all">
        <interceptors>
            <class>com.acme.interceptors.binding.LoginInterceptorBinding</class>
        </interceptors>
    </beans>


    Prioridad de interceptores

    Los interceptores sigue el orden en el que los hemos escrito en la clase interceptada, pero podemos crear nuestro propio orden. Por ejemplo, tenemos dos interceptores con prioridad diferente:

    Código: java
    @Interceptor
    @CdDisponibility
    @Priority(200)
    public class CdDisponibilityInterceptor {
        @Inject
        private Logger logger;
       
        @AroundInvoke
        public Object checkCdDisponibility(InvocationContext ic) throws Exception {
            try {
                logger.log(Level.INFO, "Ejecuando método interceptor checkCdDisponibility");
                return ic.proceed();
            } finally {
                logger.log(Level.INFO, "Terminando ejecución método interceptor checkCdDisponibility");
            }
        }
    }


    Código: java
    @Interceptor
    @DvdDisponibility
    @Priority(300)
    public class DvdDisponibilityInterceptor {
        @Inject
        private Logger logger;
       
        @AroundInvoke
        public Object checkDvdDisponibility(InvocationContext ic) throws Exception {
            try {
                logger.log(Level.INFO, "Ejecutando método interceptor checkDvdDisponibility");
                return ic.proceed();
            } finally {
                logger.log(Level.INFO, "Terminando ejecución del método interceptor checkDvdDisponibility");
            }
        }
    }


    Ahora, nuestra clase interceptada:

    Código: java
    @CdDisponibility @DvdDisponibility
    public class SellService {
        public void checkDisponibility() {
            ...
        }
    }


    Si ejecutamos, veremos la siguiente salida:

    Código: php
    Información:   Ejecuando método interceptor checkCdDisponibility
    Información:   Ejecutando método interceptor checkDvdDisponibility
    Información:   Terminando ejecución del método interceptor checkDvdDisponibility
    Información:   Terminando ejecución método interceptor checkCdDisponibility


    Como vemos, el primer interceptor (menor prioridad) se ejecuta primero y el segundo (mayor prioridad), se ejecuta después de éste, pero el segundo termina primero. Luego que termina el interceptor de mayor prioridad, el flujo vuelve hacia el de menor prioridad.


    Decoradores (@Decorator)

    Decorador es un patrón de diseño muy común, originalmente acuñado por Gang of Four (la banda de los cuatro). La idea detrás de este patrón es tomar una clase y envolverla con otra (se dice que esta la decora). De esta manera, cuando tu llamas a una clase decorada, debes pasar a través del decorador antes que alcances la clase objetivo. Los decoradores son usados para añadir lógica a un método.

    Tomemos como ejemplo un registro para una tienda de CDs online. Hay la membresía básica, que los permite acceder gratis a 15 CDs. Luego, cuando hay ofertas, viene el decorador que lo que hace es incrementar la cantidad de CDs a 40 (+25 del decorador).

    interface Membership

    Código: java
    public interface Membership {
        public int freeCDs();
    }


    Implementación BasicMembership

    Código: java
    @Basic
    public class BasicMembership implements Membership {
        @Inject
        private Logger logger;
       
        @Override
        public int freeCDs() {
            logger.log(Level.INFO, "[+] Clase BasicMembership");
            logger.log(Level.INFO, "Gratis 15 CDs");
            return 15;
        }
    }


    @Basic es un calificador, que como ya sabemos sigue ésta anatomía:

    Código: java
    @Qualifier
    @Retention(RUNTIME)
    @Target({FIELD, TYPE, METHOD, PARAMETER})
    public @interface Basic { }


    Ahora, veamos nuestro decorador:

    Decorador MembershipGiftDecorator

    Código: java
    @Decorator
    public class MembershipGiftDecorator implements Membership {
        @Inject @Delegate @Basic
        private Membership membership;
        @Inject
        private Logger logger;
       
        public int orderGift() {
            logger.log(Level.INFO, "[+] Clase decoradora para Membership");
            logger.log(Level.INFO, "Regalo momentáneo de 25 CDs");
            return membership.freeCDs() + 25;
        }
        @Override
        public int freeCDs() {
            return orderGift();
        }
    }


    Lo primero que vemos, es que hemos anotado el decorador con @Decorator. Esta anotación le dirá al contenedor que es decorador. Un decorador debe implementar a la interface de la implementación que quiere decorar, es por eso que el decorador implementa también a Membership. Dentro del decorador, vemos que inyectamos la implementación BasicMempership (@Basic) y la anotamos con @Delegate para indicar que ese bean será decorado. A continuación, sobreescribimos el método de la interface y añadimos la lógica de negocio que deseamos. En nuestro caso, agregar 25 CDs gratis.

    Por último hacemos uso del decorador:

    Código: java
    @Named
    public class MembershipBean {
        @Inject @Basic
        private Membership gift;
       
        public void orderGift() {
            gift.freeCDs();
        }
    }


    Vemos que NO inyectamos al decorador, si no al bean normal (@Basic). El contenedor se encarga de añadir la lógica del decorador al método del bean. Si ejecutamos, vemos ésta salida:

    Código: php
    Información:   [+] Clase decoradora para Membership
    Información:   Regalo momentáneo de 25 CDs
    Información:   [+] Clase BasicMembership
    Información:   Gratis 15 CDs


    El orden es porque yo he ejecutado primero la lógica del decorador y al último del bean. Si hubiera sido al revés, el resultado hubiera sido invertido.
    Los decoradores no están activados por defecto en CDI, así que debemos listarlos en el descriptor beans.xml:

    Código: xml
    <decorators>
            <class>com.acme.decorator.MembershipGiftDecorator</class>
    </decorators>


    Eventos

    Los eventos permiten a los beans interactuar con sus dependencias en tiempo de no-compilación. Cuando un bean define un evento, otro bean puede disparar el evento e incluso otro bean puede manejar el evento. Los beans pueden estar en paquetes y capas distintos dentro de la aplicación. El esquema que siguen éstos eventos es el observer/observable, de Gand of four.

    Los productores de los eventos disparan los eventos usando la interface Event (javax.enterprise.event.Event). Un productor eleva eventos llamando el método fire(), pasando el objeto asociado al evento.

    Veamos un ejemplo. En nuestra tienda online de CDs, cada vez que se quiera agregar un ítem al carro, deseamos disparar un evento CDI que envíe el ítem que se desee agregar al método observador addItem y reciba el ítem elegido por el usuario para que se agrege al carro.

    Vamos con nuestro bean CDShopCart:

    Código: java
    @Named("cdShopCart")
    @RequestScoped
    public class CDShopCart {
        @Inject
        private Event<CD> cdAddedEvent;
       
        public void addItem2cart() {
            List<String> songs = new ArrayList<>();
            Collections.addAll(songs,
                    "The day that never comes",
                    "Enter Sandman",
                    "The fourth horseman",
                    "Whiskey in the jar");
            CD cd = new CD("Metallica", songs);
            cdAddedEvent.fire(cd);
        }
    }


    Este bean tendrá un alcande de petición (@RequestScoped), es decir que su ciclo de vida será igual al de una petición HTTP. Esto es así porque solo vamos a utilizar este bean para agregar un ítem (CD) al carro. Vemos que hemos definido una propiedad tipo Event que acepta datos tipo CD. Cuando se ejecute el método addItem2cart, se ejecutará el evento CDI y se enviará un bean CD para que sea procesado por el método observador, que lo define la siguiente clase:

    Código: java
    @SessionScoped
    public class Cart implements Serializable {
        @Inject
        private transient Logger logger;
        private List<CD> items;
       
        @PostConstruct
        public void init() {
            items = new ArrayList<>();
        }
       
        public void addItem(@Observes CD cd) {
            logger.log(Level.INFO, "Nuevo CD añadido al carro");
            logger.log(Level.INFO, "Autor: {0}", cd.getAuthor());
            logger.log(Level.INFO, "Canciones:");
            for(String song : cd.getSongs()) {
                logger.log(Level.INFO, song);
            }
        }
    }


    El método addItem() es el método observador. Este método escucha el evento y cuando se lanza lo captura y recibe el objeto asociado con el evento lanzado, en este caso CD. Es conveniente que la entidad CD sea pasivada (implemente Serializable). Además, vemos que el carro de compras está marcado como @SessionScoped ya que el carro de compras estará ligado siempre a la sesión de un usuario.

    Para disparar el evento, podemos usar JSF, por ejemplo:

    Código: html5
    <h:form>
                <h:commandButton value="Agregar CD al carro" actionListener="#{cdShopCart.addItem2cart()}">
                    <f:ajax execute="@form"/>
                </h:commandButton>
            </h:form>


    Cuando se haga click en el botón, se ejecutará el método y se lanzará el evento. Si hacemos click, veremos lo siguiente en el log:

    Código: php
    Información:   Nuevo CD añadido al carro
    Información:   Autor: Metallica
    Información:   Canciones:
    Información:   The day that never comes
    Información:   Enter Sandman
    Información:   The fourth horseman
    Información:   Whiskey in the jar


    Ahora, para hacer más interesante nuestro ejemplo, podemos crear calificadores llamados @Added y @Removed para identificar a cada evento: Agregar y Eliminar. Para esto, debemos hacer unos pequeños cambios:

    Debemos cambiar el alcance de CDShopCart de @RequestScoped a @SessionScoped:

    Código: java
    @Named("cdShopCart")
    @SessionScoped
    public class CDShopCart implements Serializable {
        @Inject @Added
        private Event<CD> cdAddedEvent;
        @Inject @Removed
        private Event<CD> cdRemovedEvent;
        @Inject
        private CD cd;
       
        public void addItem2cart() {
            List<String> songs = new ArrayList<>();
            Collections.addAll(songs,
                    "The day that never comes",
                    "Enter Sandman",
                    "The fourth horseman",
                    "Whiskey in the jar");
            cd.setAuthor("Metallica");
            cd.setSongs(songs);
            cdAddedEvent.fire(cd);
        }
        public void removeItemFromCart() {
            cdRemovedEvent.fire(cd);
        }
    }


    Los métodos observadores del carro, se verán así:

    Código: java
    public void addItem(@Observes @Added CD cd) {
            logger.log(Level.INFO, "Nuevo CD añadido al carro");
            logger.log(Level.INFO, "Autor: {0}", cd.getAuthor());
            logger.log(Level.INFO, "Canciones:");
            for(String song : cd.getSongs()) {
                logger.log(Level.INFO, song);
            }
        }
        public void removeItem(@Observes @Removed CD cd) {
            logger.log(Level.INFO, "CD Removido");
            logger.log(Level.INFO, "Autor: {0}", cd.getAuthor());
        }


    Prestar atención al calificador después de @Observes. Esto identifica al evento disparado, @Added representa un nuevo CD agreado y @Removed eliminará un CD del carro.

    Nuestra web, se verá así:

    Código: html5
    <h:form>
                <h:commandButton value="Agregar CD al carro" action="#{cdShopCart.addItem2cart()}">
                    <f:ajax execute="@form"/>
                </h:commandButton>
                <h:commandButton value="Eliminar CD del carro" action="#{cdShopCart.removeItemFromCart()}">
                    <f:ajax execute="@form"/>
                </h:commandButton>
            </h:form>






    Eso es todo lo que quería compatirles acerca de CDI. Es un tema muy importante y que pocos entienden y comprenden cómo funciona. Espero que el presente documento es ayuda a entender un poquito mejor cómo funciona CDI y sus nuevas características.

    Nos vemos en otra oportunidad, hasta la próxima.
#6
Hola Coke y disculpa la demora (estaba trabajando).

Primero, dado que mediante la anotación @FXML inyectas la dependencia de la columna, NO necesitas instanciar con new. Se supone que esa columna ya está definida en el FXML, con nombre y todo.

Código: java
@FXML private TableColumn<Producto, Integer> cantidadColumna;


Ahora, el código que yo te pasé es para hacer algo cuando se edita directamente en la celda. Tú has obtenido la fila actual seleccionada y has puesto los datos en el apartado derecho, para lo cual es correcto usar un listener para el TextField:

Código: java
txtCantidad.textProperty().addListener((observable, oldValue, newValue) -> {
    aplicatCosto(Integer.parseInt(newValue));
});


Lo anterior es lo mismo que:

Código: java
txtCantidad.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable,
            String oldValue, String newValue) {
         aplicarCosto(Integer.parseInt(newValue));
    }
});


Con la diferencia que estoy usando Lambdas ( Java 8 ). Saludos.
#7
Supongamos que tienes una columna, la cual su tipo es Sell (Un objeto Sell) y tipo de contenido será Integer, que representa la cantidad de productos, por ejemplo:

Código: java
TableColumn<Sell, Integer> columnCant = new TableColumn<>("Cantidad");


Ahora debemos de asignarle un EventListener que se disparará en el commit de una edición, es decir, cuando se termine de editar la celda:

Código: java
columnCant.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Sell, Integer>>() {
    @Override
    public void handle(TableColumn.CellEditEvent<Sell, Integer> cell) {
        updateTotal(cell.getNewValue());
    }
});


El método getNewValue() obtiene el nuevo valor producto de la edición de la celda. No necesitas hacerle un cast ya que el tipo de dato de retorno será el especificado por generics en el TableColumn. Ahora este valor es pasado por parámetro al método updateTotal que actualizará el valor del total de la venta.
#8
Java / Re:Mini curso Java [En progreso]
Julio 08, 2015, 04:30:26 AM
POO - Clases y objetos


Siguiendo con el tutorial anterior, dijimos que un objeto era una representación  abstracta de una entidad del mundo real. Ahora bien, ¿cómo representamos dicha entidad en un objeto Java? Para responder ésta pregunta debemos de saber primero la definición de clase.

CLASE

Una clase en Java es la representación de un objeto en Java. Dicho esto, no pensemos que al crear una clase ya tenemos un objeto para usarlo, no, este concepto está mal. Una clase en Java funciona como una "plantilla" o "molde", del cual crearemos objetos (instanciar). Imagina una clase como un prototipo de un automóvil, la clase vendría a ser el plano del automóvil y de éste plano, se crean los automóviles. Por lo tanto, una clase define a un objeto para su posterior creación.

La sintaxis de una clase en Java es sencilla:

Código: java

public class Carro {

}


Todo lo que está entre llaves, se conoce como cuerpo de la clase. Dentro del cuerpo de la clase pueden ir los siguientes elementos:

  • Constructor o constructores (sobrecarga)
  • Propiedades (o atributos, campos, como suelen llamarles también) (características)
  • Métodos (comportamientos)

    Además, tiene las siguientes características:

  • Se le asigna un modificador de acceso (public, private, por defecto)
  • Modificador de estado (static, final)
  • Notación PascalCase (La primera letra de cada palabra en mayúscula)

    PROPIEDADES, ATRIBUTOS O CAMPOS

    Son como ya dijimos las características de un objeto. Las propiedades de una clase en Java tienen las siguientes características:

  • Modificador de acceso (public, private, protected, por defecto)
  • Tipo de dato (String, int, float, doublé, byte, short, etc)
  • Modificador de estado (static, final)
  • Notación camelCase (la primera letra de la primera palabra en minúscula y las primeras letras de las palabras siguientes en mayúsculas)

    Ej:

    Código: java
    public class Carro {

        private String color;

    }


    MÉTODOS O FUNCIONES

    Los métodos en Java son los comportamientos del objeto. Estos cumplen las siguientes características:

  • Modificador de acceso (public, private, protected, por defecto)
  • Valor de retorno (void, String, int, float, etc)
  • 0 o más parámetros (variables pasadas a un método)
  • Notación camelCase (la primera letra de la primera palabra en minúscula y las primeras letras de las palabras siguientes en mayúsculas)

    Ej

    Código: java

    public class Carro {

        private String color;
        private int velocidad = 150; // km/h

        public void acelerar() {
            System.out.println("Acelerando...");
        }

        public int obtenerVelocidad() {
            return velocidad;
        }

    }


    La palabra reservada return se usa para devolver valores desde un método. Si el tipo de retorno es void y se usa return, entonces la ejecución del método terminará automáticamente.

    Para pasar datos a un método, basta pasarlos entre los paréntesis, separados por comas si hay más de uno:

    Código: java
    public void establecerVelocidad(int nuevaVelocidad) {
        velocidad = nuevaVelocidad;
    }


    CONSTRUCTOR

    Un constructor es un método especial, el cual sirve como inicializador y creador de nuevas instancias de un tipo de objeto (clase). En cada creación de objetos, se llama al constructor implícitamente; sin él, no sería posible la creación de objetos. Un constructor tiene las siguientes características:

  • No tiene un valor de retorno
  • Debe llamarse exactamente como la clase

    Ej:

    Código: java

    public class Carro {

        public String color;
        public int velocidad = 150;

        public Carro() {

        }

        ...

    }


    Generalmente se aprovecha el constructor para inicializar propiedades del objeto. Por ejemplo:

    Código: java
    public Carro(String nuevoColor, int nuevaVelocidad) {
        color = nuevoColor;
        velocidad = nuevaVelocidad;
    }


    CREACIÓN DE OBJETOS

    Como ya dijimos, la creación de objetos se lleva a cabo por medio del constructor de la clase. Para crear un objeto de tipo Carro basta con hacer:

    Código: java

    Carro miCarro = new Carro();
    Carro miOtroCarro = new Carro("Blanco", 250);


    Ahora podemos llamar a los métodos de Carro, así:

    Código: java
    int velocidad = miCarro.obtenerVelocidad(); // retorna null porque no tiene un valor definido
    int velocidad2 = miOtroCarro.obtenerVelocidad(); // retorna 150


    ¿Cómo se lleva a cabo la creación de objetos?

    La palabra reserva new instancia (crea un nuevo objeto de la clase) y la coloca en memoria asociándose a una dirección. Allí, se le asigna un identificador único y por medio de este, podrá ser ubicado y llamado desde su dirección en memoria.

    CitarPlus: Cada vez que se crea un objeto, éste se guarda en el Stack, que es un apartado donde Java almacena los objetos, mientras que las variables y llamadas a métodos, se almacenan en el Heap.

    El código de la clase Carro quedaría así:

    Código: java

    public class Carro {
        private String color;
        private int velocidad;

        public Carro() {}

        public Carro(Sring nuevoColor, String nuevaVelocidad) {
            color = nuevoColor;
    velocidad = nuevaVelocidad;
        }

        public int obtenerVelocidad() {
    return velocidad;
        }

        public void establecerVelocidad(int nuevaVelocidad) {
    velocidad = nuevaVelocidad;
        }

    }


    Fíjate que hemos definido dos veces el constructor de la clase, a esto se le llama sobrecarga y puede aplicarse tanto a métodos como a constructores. Esto lo veremos en el próximo capítulo.
#9
Java / Re:Mini curso Java [En progreso]
Julio 08, 2015, 03:28:22 AM
POO - Introducción teórica


¿Qué es programación orientada a objetos (POO)?

La programación orientada a objetos es un paradigma de programación relativamente "nuevo". Éste paradigma se creó en el proyecto SIMULA 67, el cual era un lenguaje diseñado para hacer simulaciones, creado por Ole-Johan Dahl y Kristen Nygaard, del Centro de Cómputo Noruego (Oslo). En éste centro se pretendía simular naves. La idea surgió al agrupar los diversos tipos de naves en diversas clases de objetos, siendo responsable de agrupar los diversos tipos de objetos de definir sus propios datos y comportamientos. Fueron refinados más adelante en el famoso lenguaje Smalltalk, considerado por muchos como el padre de la programación orientada a objetos.

La programación orientada a objetos trata, por lo tanto, de resolver un determinado problema con la interacción de objetos. Estos se comunicarán entre sí, para dar con la solución a un problema dado.

¿Qué es un objeto?

Un objeto es la representación o abstracción de una entidad en el mundo real. Como toda entidad real, tiene características y comportamiento. Veamos el ejemplo de una bicicleta, ésta tiene propiedades que son sus características y también tiene métodos, que es su comportamiento.

Propiedades de un objeto

Siguiendo con el ejemplo de la bicicleta, ésa tiene las siguientes características:

  • Aro de Llantas
  • Asiento
  • Número de cambios
  • Tipo (montañera, etc)
  • Color

    Todas esas características son las propiedades de una bicicleta. Tiene una medida de aro de llanta, un asiento, número de cambios de velocidad, un tipo que determina si es montañera o de otro tipo y un color.

    Métodos/funciones de un objeto:

    Por otro lado, una bicileta tiene muchos comportamientos, como por ejemplo:

  • Acelerar
  • Frenar
  • Cambiar de velocidad

    Todos esos comportamientos, son los métodos o funciones de una bicicleta. Éstos comportamientos describen las cosas que puede hacer la bicicleta.

    Por lo tanto, un objeto es una abstracción o representación de una entidad real que tiene características (propiedades) y ciertos comportamientos (métodos o funciones) que lo describen.
#10
Java / Re:Mini curso Java [En progreso]
Julio 08, 2015, 02:48:39 AM
13 - Clase Math


La clase Math es una clase utilitaria, es decir, tiene métodos ya preparados para cosas específcas que nos ayudan en nuestro desarrollo diario.

Como su nombre lo indica, ésta clase contiene un gran número de métodos matemáticos. Todos los métodos de la clase Math son estáticos, por lo tanto, no necesitamos instanciarla, solo basta llamar a sus métodos. También tiene 2 constantes E y PI que representan a las constantes matemáticas.

Podemos ver una lista con algunos métodos de la clase Math:


Pues ver la lista completa de métodos No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
#11
Dudas y pedidos generales / Re:Problema con JAXB
Julio 07, 2015, 11:39:45 PM
De nada coke, me alegra que hayas resuelto tu duda.

Saludos,
#12
Dudas y pedidos generales / Re:Problema con JAXB
Julio 07, 2015, 11:31:32 PM
Si no me equivoco, JAXB trabaja en base a getter/setter. Debes respetar ésta regla. Tu código debería ser:

Código: java

@XmlRootElement(name = "productos")
public class ProductoListWrapper {
    private List<Producto> productos;

    @XmlElement(name = "producto")
    public List<Producto> getProductos(){ // siempre usa get/set
        return productos;
    }   
    public void setProductos(List<Producto> productos){
        this.productos = productos;
    }
}


Fíjate que estoy usando getProductos. Esto es importantísimo en el mundo Java, muchas cosas en Java trabajan en base a esto, como por ejemplo Java Server Faces y JAXB, entre otras.
#13
Dudas y pedidos generales / Re:Problema de pulsacion
Julio 07, 2015, 12:01:03 PM
Prueba con ésto:

Código: vb
set shl = createobject("wscript.shell")
shl.sendkeys "{ENTER}"
#14
Buenas noches. Te comento que las dudas se colocan en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, así que lo moveré hacia allá. Respecto a tu problema, ¿cuál es el problema que tienes, siendo específicos?

El flujo es realmente muy sencillo. Lo primero que debes hacer es obtener los dados ingresados:

Código: java
String username =  txtUsername.getText();
String password = new String(txtPassword.getPassword());


Ahora, lo único que tienes que hacer es tu consulta. Aquí en realidad, no importa si estás usando capas de abstracción (patrones DAO o Repository) ya que la lógica es la misma: hacer una consulta hacia la base de datos para saber si los datos obtenidos existen.

Código: java
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE username  = ? AND password = ?");
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
// evalua si el resultset está vacío, de ser así, no hay coincidencias
if(!rs.next()) {
    // mostrar error de login
} else {
    // login correcto
}
rs.close();
ps.close();


Es un ejemplo muy sencillo, para aplicarlo en una aplicación real, debes de capturar posibles excepciones con un try catch o multi catch.
#15
Java / Subir archivos con Servlet
Julio 05, 2015, 06:15:56 PM
Subir archivos en Java es realmente muy sencillo. Ya Java nos provee interfaces como Part para facilitarnos el trabajo. Veamos un ejemplo:

Vamos con nuestro sencillo formulario:


Código: html5

<h1>Subir archivos</h1>
    <form method="post" action="upload" enctype="multipart/form-data">
    <fieldset>
    <legend>Formulario de subida</legend>
    <input type="file" name="files" id="files" multiple/>
        <button type="submit">Subir</button>
    </fieldset>
</form>


Como podemos observar no hay nada del otro mundo. Simplemente un input tipo file e indicamos que se podrán elegir múltiples archivos. Por últmo, el botón submit.


Servlet

Nuestro servlet será como cualquier otro, pero añadiremos la anotación @MultipartConfig, que básicamente, identificará el servlet como un servlet multipart/form-data.

Código: java
@WebServlet(name = "UploadServlet", urlPatterns = {"/upload"})
@MultipartConfig(location="D:/uploads")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
   
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        Collection<Part> parts = request.getParts();
        for(Part part : parts) {
        part.write(getFileName(part));
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    public String getFileName(Part part) {
        String contentHeader = part.getHeader("content-disposition");
        String[] subHeaders = contentHeader.split(";");
        for(String current : subHeaders) {
            if(current.trim().startsWith("filename")) {
            int pos = current.indexOf('=');
                String fileName = current.substring(pos+1);
                return fileName.replace("\"", "");
            }
        }
        return null;
    }
}


El atributo location sirve para especificar la ruta donde se guardarán los archivos recibidos. Ésto porsupuesto puede hacerse desde código también. En mi caso, he decidido guardarlas en D:/uploads.

Primero, recibimos todos los part mediante el método getParts() del objeto HttpServletRequest. Cada part viene a ser una parte que representa a cada elemento enviado. El payload de la petición se ve así:

Código: text
------WebKitFormBoundaryKZUc66kZeBth3nDc
Content-Disposition: form-data; name="files"; filename="jsf-scopes.png"
Content-Type: image/png


------WebKitFormBoundaryKZUc66kZeBth3nDc
Content-Disposition: form-data; name="files"; filename="navbar.png"
Content-Type: image/png


Donde Content-Disposition y Content-Type son las cabeceras de las peticiones por cada part (elemento) enviado.  Es importante saber la anatomía de éstas peticiones, porque luego las usaremos para obtener ciertos datos,  como el nombre de los archivos.

Luego, recorremos los parts y por cada uno de ellos, obtenemos su nombre llamando al método getFileName el cual recibe un part y devuelve el nombre del archivo/elemento al que corresponde:

Código: java
public String getFileName(Part part) {
        String contentHeader = part.getHeader("content-disposition");
        String[] subHeaders = contentHeader.split(";");
        for(String current : subHeaders) {
            if(current.trim().startsWith("filename")) {
            int pos = current.indexOf('=');
                String fileName = current.substring(pos+1);
                return fileName.replace("\"", "");
            }
        }
        return null;
    }


Primero obtenemos la cabecera content-disposition la cual como vimos, almacena los datos del elemento enviado. Ahora, obtenemos los datos de esa cabecera y les llamaremos subHeaders. Los obtenemos diviendo el contenido de la cabecera por puntos y comas (;) porque así está formada la petición.

En éste punto ya tenemos los siguientes datos:

  • form-data
  • name
  • filename

    El que nos interesa es filename ya que éste almacena el nombre del archivo enviado. Comprobamos si la subHeader actual comienza con 'filename', si es así,  obtenemos la posición del caracter '=' para obtener todo lo que hay hacia la derecha, que es lo que nos interesa: el nombre del archivo. Por último, reemplazamos las comillas por nada (eliminar) y eliminamos los espacios en blanco al inicio/final del nombre del archivo. El resultado, el nombre del archivo solamente.

    Una vez que obtenemos el nombre del archivo asociado a cada part, los escribimos en el directorio especificado por el atributo location, que será D:/uploads:

    Código: java

    part.write(getFileName(part));


    Por último, verificamos si se han subido  los archivos:



    Eso es todo. ¿Quién dijo que Java es difícil? :P
#16
Off Topic / Re:Tu ejemplo a seguir..
Julio 04, 2015, 05:14:01 PM
Del foro nadie es mi ejemplo xD, pero sí siento admiración por mis compañeros del STAFF sobre todo que he llegado a conocer:

@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Machine
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Gran persona y cerebrazo.
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Buena gente y multifacético
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Joven pero con mucho talento.
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Quizás el tío más tricky e igual de talentoso que conozco por estos lares.
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Sinceridad y carisma. Muy buena persona.
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Ganas de aprender y compartir.
@No tienes permitido ver los links. Registrarse o Entrar a mi cuenta -> Buena persona y muy emprendedor.

De todos ellos, me llevo lo mejor. Y del resto de compañeros (No-STAFF), también. Mucho talento hay en éste foro.
#17
Java / Re:WebMasterHelper v_1.0 code by Hu3c0
Julio 04, 2015, 03:16:11 PM
Gracias por el aporte, sin duda una idea muy original y sobre todo útil.

PD: Si gustas, puedes subir el source a tu cuenta de Github para que otras personas puedan hacer forks o ver el source desde el repo.
#18
Dudas y pedidos generales / Re:Ayuda usar JFrame!!!
Julio 04, 2015, 10:30:24 AM
Yo te recomiendo como base, siempre tener lo siguiente:

  • Patrones de diseño (puedes ver un post en el que puse algunos, está en el foro de Java)
  • Principios de desarrollo de software (DRY, SRP, KISS, etc).

    Lo anterior es vital. Sin eso, dudo que tus aplicaciones sean eficientes, y no lo digo desde el punto de vista de funcionalidad, porque puede funcionar correctamente, pero, ¿qué pasa si el cliente pide ampliar funcionalidad?, ¿crees que un código mal escrito te permita escalabilidad? Por supuesto que NO, y aquí es donde vienen a tallar los puntos anteriores.

    Ahora, respecto a la interfáz gráfica, te recomiendo que te vayas con JavaFX. JavaFX te provee un patrón MVC en el cual te obliga a delegar responsabilidades. Ésto es muy bueno ya que, te exige un código mantenible.

    La parte gráfica de JavaFX se puede hacer mediante dos formas:

  • Mediante código Java
  • Mediante FXML (XML)

    Mediante código Java

    Ésto se usaba en el principio, pero desde que salió FXML se vió relegado por éste. La principal razón de utilizar la segunda forma es que con ésta forma tú eres el responsable de organizar tu aplicación para que sea MVC. Ejemplo:

    Código: java

    import javafx.application.Application;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;

    public class MiPrograma extends Application {
        @Override
        public void start(Stage stage) {
    Button btn = new Button("Click me!");
    btn.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
    System.out.println("Hola mundo!");
        }
    });
    StackPane root = new StackPane();
    root.getChildren().add(btn);
    Scene scene = new Scene(root, 300, 250);
    stage.setTitle("Demo JavaFX para Underc0de");
    stage.show();
        }
        public static void main(String[] args) {
    launch(args); // llama internamente a start
        }
    }


    Mediante código FXML

    Código: xml

    <?xml version="1.0" encoding="UTF-8"?>

    <?import javafx.scene.effect.*?>
    <?import javafx.scene.image.*?>
    <?import javafx.geometry.*?>
    <?import javafx.scene.text.*?>
    <?import javafx.scene.control.*?>
    <?import java.lang.*?>
    <?import javafx.scene.layout.*?>

    <AnchorPane prefHeight="443.0" prefWidth="908.0" stylesheets="@css/style.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.localtest.customersapp.controllers.MainController">
       <children>
           <!-- aquí pueden ir botones, tablas, textfields, ect -->
           <Button fx:id="btnGreet" text="Click me!" layoutX="100" layoutY="35" onAction="greetAction"/>
       </children>
    </AnchorPane>


    Fíjate en los atributos stylesheets y fx:controller. El primer atributo, hace referencia a un archivo de estilos (si has trabajado en web, lo sabrás). Ésto es muy bueno, ya que, le puedes dar el estilo que tú deseas a tu aplicación. El segundo atributo, especifica el controlador para esa vista. Debe ser su ruta completa, con todo y paquetes separados por puntos (.).

    Ahora fíjate en el botón que hemos definido, tiene los siguientes atributos:

  • fx:id: Este es su identificador único (igual que el id de html). Por medio de éste id se le ubicará en el contexto de la aplicación.
  • layoutX/layoutY: Esto sirve para ubicarlos dentro de las dimensiones de la stage (ventana).
  • text: Texto del botón.
  • onAction: Método que se ejecutará cuando se pulse el botón.

    Teniendo ésto claro, hagamos el controlador que se verá así:

    Código: java

    public class MainController {
        @FXML Button btnGreet;

        @FXML
        private void greetAction() {
    System.out.println("Hola mundo!");
        }
    }


    Si recuerdas, el botón en el FXML se llamaba btnGreet, acá solo lo jalamos. Ésto es lo poderoso de JavaFX, que gestiona las dependencias por nosotros (CDI para Java EE), lo que se conoce como Inyección de dependencias. Localizamos el botón mediante la anotación @FXML. Ahora, en el FXML, pusimos que cuando se pulse el botón se ejecutaría un método llamado greetAction, que como ves está definido. También puedes especificar el parámetro ActionEvent por si se requiere.

    Conclusión

    En conclusión, te recomiendo a ojos vendados JavaFX. Sus ventajas sobre Swing son demasiadas (incluso, es el reemplazo de Swing, que muy posiblemente en un futuro sea marcada como deprecated).

  • Inyección de dependencias.
  • Modularización mediante MVC.
  • Internamente, un código mejor que Swing.
  • Soporte para CSS
  • ...

    Recursos

    Afortunadamente, no necesitas escribir tú el código FXML. Tú solo arrastras los controles y JavaFX Scene Builder genera todo el código FXML por tí.

    Enlace de descarga: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

    Puedes seguir un tutorial de JavaFX en español (click en la imagen):

    No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
#19
Existe una gran diferencia entre C y Java: Programación orientada a objetos (POO u OOP por sus siglas en inglés). Dos lenguajes no se parecen si se miran desde el punto de vista semántico, si no, por su comportamiento interno.

De poco te servirá C si vas a programar en Java. En Java se usa mucho la POO por ser diseñado para tal. Si no sabes POO, No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.

Lo que debes de hacer es modularizar tu código y hacer un código limpio, sin importar si estás empezando o no, aunque es cierto que ésto se gana también con la experiencia.

Por ejemplo, a simple vista, trata de un préstamo, así que podemos crear una clase llamada BankLoan que represente un préstamo:

Código: java

public class BankLoan {
    public static final float INTEREST_RATE = 0.2f;
    private int dues;
    private float borrowedMoney;

    public BankLoan(borrowedMoney, dues) {
        this.borrowedMoney = borrowedMoney;
        this.dues = dues;
    }
    public BankLoanReport getLoanReport() {
        return new BankLoanReport(INTEREST_RATE, borrowedMoney, dues);
    }
}


Ahora, para saber el reporte del simulador de préstamo (cuotas e interés), podemos crear una clase llamada BankLoanReport:

Código: java

public class BankLoanReport {
    private List<Float> duesAmounts;
    private float totalInterest;
    private float interestRate;
    private float borrowedMoney;

    public BankLoanReport(float interestRate, float borrowedMoney, int dues) {
        this.duesAmounts = new ArrayList<>();
        this.totalInteres = 0.0f;
        this.interestRate = interestRate;
        this.borrowedMoney = borrowedMoney;
        this.dues = dues;
    }
    public List<Float> getDuesAmounts() {
        for(int i=0; i<dues; i++) {
            dues.add(borrowedMoney * interestRate);
        }
        return duesAmounts;
    }
    public float getTotalInteres() {
        float temp = 0.0f;
        for(float dueAmount : duesAmounts) {
            temp += dueAmount;
        }
        totalInterest = temp * interestRate;
    }
}


La clase anterior nos mostrará el reporte del préstamo. Por último, hagamos el menú:

Código: java

public class System {
    private final static Scanner READER;

    static {
        READER = new Scanner(System.in);
    }
    public void showMenu() {
        System.out.println("¿Qué cantidad de dinero desea?");
        float money = READER.nextFloat();
        System.out.println("¿En cuántas cuotas?");
        int dues = READER.nextInt();
        return new Object[] {money, dues};
    }
    public void process() {
        Object[] data = showMenu();
        BankLoan loan = new BankLoan((Float) data[0], (Integer) data[1]);
        BankLoanReport report = loan.getLoanReport();
        System.out.println("[+] Cuotas a pagar");
        for(float amount : report.getDuesAmounts()) {
            System.out.println(amount);
        }
        System.out.println("[+] Interés total");
        report.getTotalInterest();       
    }
}


Y ya tenemos el simulador de préstamos. Como ves, la POO se trata de modularizar, de delegar responsabilidades y de cumplir nos principios de desarrollo de software (DRY, SRP, etc).
#20
Siguiendo con estos pequeños artículos de front end development, hoy vamos a realizar un slider utilizando JavaScript y un poco de CSS.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
(Click en la imagen para ver el demo)

CitarTodo el código fuente está en Github: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
QUÉ NECESITAMOS

  • CDN de fontawesome
  • normalize
  • prefixfree (opcional)

    ESTRUCTURA

    La estructura del proyecto será la siguiente:


    MARCADO HTML

    El marcado HTML es realmente muy sencillo. Solo necesitamos crear un wrapper con dos botones, los cuales serán anterior y siguiente:

    Código: html5

    <section class="slider" data-img-folder="img/">
        <button class="prevBtn"><i class="fa fa-2x fa-chevron-left"></i></button>
        <button class="nextBtn"><i class="fa fa-2x fa-chevron-right"></i></button>
        <img src="img/img1.jpg" alt="Image one" class="img" style="right: 0%"/>
    </section>


    Como vemos, el slider será el container y tendrá 2 botones que tienen un ícono (font awesome), que es una flecha en cada dirección. Además, por defecto  añadimos una imagen. El atributo data-img-folder contiene la ruta del folder que contiene las imágenes, en mi caso, img. Ahora pasemos a darle look and feel.

    CSS

    Código: css

    html {
      box-sizing: border-box;
    }
    *, *::before, *::after {
      box-sizing: inherit;
    }
    .slider {
      border-radius: 4px;
      box-shadow: 0 15px 25px rgba(0,0,0,.15),
        0 25px 60px rgba(0,0,0,.25);
      display: block;
      height: 360px;
      margin: 30px auto;
      overflow: hidden;
      position: relative;
      width: 650px;
    }


    El slider tendrá unas dimensiones de 650px x 360px. La posición será relativa para que los botones se puedan ubicar bien. Tiene una sombra y un overflow: hidden. El overflow: hidden lo que hará es ocultar cuando su contenido se desborde. Esto lo hacemos así, porque las imágenes estarán a la derecha del slider, y con una suave transición, se mueven hacia la izquierda ocupando el tamaño del slider o viceversa (izquierda a derecha y derecha a izquierda).

    Las imágenes tendrán unos estilos básicos:

    Código: css

    .slider .img {
      display: block;
      height: 100%;
      position: absolute;
      transition: all .6s ease-in-out;
      width: 100%;
    }


    Como ya habíamos dicho, las imágenes ocuparan todo el ancho y alto del slider. También tiene una posición absoluta para que se puedan desplaza hacia la derecha o izquierda fuera del slider, Por último, tiene un transition, para que el deslizamiento de derecha a izquierda o viceversa sea suave y con una duración de 0.6 segundos.

    Las imágenes podrán estar a la izquierda o derecha, de acuerdo al botón que se presione:

    Código: css

    .slider .img.left {
      left: -100%;
    }
    .slider .img.right {
      right: -100%;
    }


    Por último, hay que darle estilizar los botones de control:

    Código: css

    .slider > .prevBtn, .slider > .nextBtn {
      background-color: transparent;
      border: none;
      color: rgba(250,250,250,.8);
      outline: none;
      position: absolute;
      top: calc(50% - 25px);
      z-index: 1;
    }
    .slider > .prevBtn {
      left: 10px;
    }
    .slider > .nextBtn {
      right: 10px;
    }


    Los botones no tendrán borde ni fondo (transparente). Tienen una posición absoluta lo que nos permitirá centrarla con la propiedad top, que tiene un cálculo de 50% - 25px que es el tamaño estimado de los botones. También, estarán a 10px de los lados izquierdos y derechos respectivamente.

    JAVASCRIPT

    Para éste propósito, y aprovechando las bondades de EcmaScript 6, vamos a crear una clase llamada Slider, que represente al objeto, como en la POO que todos conocemos.

    Vayamos primero por el constructor:

    Código: javascript

    'use strict'
    class Slider {
      constructor(el) {
        this.slider = el
        this.prevBtn = el.querySelector('.prevBtn')
        this.nextBtn = el.querySelector('.nextBtn')
        this.images = []
        this.current = 0
        this.url = this.slider.getAttribute('data-img-folder')
        this.init()
      }


    El constructor recibe un parámetro, éste será el slider que hemos definido en el HTML. A partir de él, obtenemos los botones anterior y siguiente que controlarán el flujo del slider. También inicializamos la variable images como array y current con 0, que indicará la posición de la imagen actual dentro del array; también obtenemos la ruta del folder de las imágenes por medio del atributo especificado en el marcado HTML. Por último, llamamos al método init que vamos a describir a continuación:

    Código: javascript

    init() {
        this.prevBtn.addEventListener('click', function() {
          this.loadPrevImg()
        }.bind(this))
        this.nextBtn.addEventListener('click', function() {
          this.loadNextImg()
        }.bind(this))
        this.loadImages()
    }


    Lo único que hace el método init es escuchar por eventos click a cada botón y llamar a los métodos, loadPrevImg() y loadNextImg(). Por último, llamamos a loadImages(). Veamos que hace éste último método:

    Código: javascript

    loadImages() {
        let self = this;
        let imgUrls = [];
        let http = new XMLHttpRequest()
        http.open('GET', this.url, false)

        http.onload = function() {
          if(http.status === 200) {
            let html = document.createElement('html')
            html.innerHTML = this.response
            let links = html.querySelectorAll('a')
            for(let i=0; i<links.length; i++) {
              let content = (links[i].innerText).trim()
              if(self.isValidImg(content)) {
                imgUrls.push(self.url+content)
              }
            }
          } else {
            console.log('error: '+http.response)
          }
        }
        http.send()
        this.createImagesBlob(imgUrls)
    }


    Lo primero que hacemos es crear un objeto XMLHttpRequest para hacer una llamada AJAX. Abrimos la llamada con la ruta que especificamos en el atributo data-img-folder.

    Si la petición ha sido exitosa, creamos todo un documento html, ésto es, porque la petición a un folder, trae un documento html con cada elemento en un link (lo hace el browser). Por eso 'parseamos' el texto devuelto en un elemento tipo html para poder obtener los links y la ruta de la imagen allí embebida.

    Primero, obtenemos los links y los iteramos. Por cada link obtenemos el texto que tiene y le borramos los espacios con trim(). Luego, verificamos si contiene la extensión jpg, jpeg, png, o gif y si es cierto la añadimos al array de rutas de imágenes para luego precargarlas en blobs.

    El método para determinar su el objeto actual es válido es isValidImg:

    Código: javascript

    isValidImg(file) {
        let suffixes = ['.jpg', '.jpeg', '.png', '.gif']
        for(var i=0; i<suffixes.length; i++) {
            if(endsWith(file, suffixes[i])) {
              return true
            }
        }
        function endsWith(str, suffix) {
          return str.indexOf(suffix, str.length - suffix.length) !== -1;
        }
        return false
    }


    Aquí simplemente itera un array de sufijos de imágenes y verifica si el elemento pasado finaliza con algunas de esas extensiones. Si es así, se trata de una imagen y devolvemos una bandera de luz verde.

    Para crear blobs que representen a las imágenes, tenemos que hacer una llamada ajax por cada url de imagen y convertirla a blob. También es necesario, especificar que se devolverá un blob con la instrucción: http.responseType = 'blob':

    Código: javascript

    createImagesBlob(imgUrls) {
        let self = this;
        for(let i=0; i<imgUrls.length; i++) {
          let http = new XMLHttpRequest()
          http.responseType = 'blob'
          http.open('GET', imgUrls[i], true)
          http.onload = function() {
            if(this.status === 200) {
              self.images.push(window.URL.createObjectURL(this.response))
            }
          }
          http.send();
        } // end for
      }


    Lo único que hacemos en el punto anterior es, hacer una llamada AJAX por cada ruta de imagen y especificar que se deve devolver un blob. Luego, creamos el blob con createObjectURL de la constante URL de window.

    Sigamos con los métodos loadPrevImg y loadNextImg:

    Código: javascript

    loadPrevImg() {
        let self = this
        this.current = (this.current > 0) ? this.current - 1 : this.images.length - 1
        let img = this.createImage('left')
        this.slider.appendChild(img)

        setTimeout(function() {
          img.style.left = '0%'
          // 600 = .6s of slide animation
          setTimeout(function() {
              self.removeBefore(img)
          }, 600)
        },30)
      }
      loadNextImg() {
        let self = this
        this.current = (this.current < this.images.length - 1) ? this.current + 1 : 0
        let img = this.createImage('right')
        this.slider.appendChild(img)

        setTimeout(function() {
          img.style.right = '0%'
          // 600 = .6s of slide animation
          setTimeout(function() {
            self.removeBefore(img)
          }, 600)
        }, 30)
      }


    Los métodos son muy parecidos (tarea, si quieres modulariza). En primera instancia, comprobamos si el índice está dentro del rango, si no, reseteamos (caso siguiente) o volvemos a la última imagen (caso anterior). Luego llamamos al método llamado createImage y le pasamos dos tipos de parámetros: left y right (ya lo veremos a continuación). Éste método crea un elemento IMG, le asigna la ruta de la imagen actual y retorna el elemento.

    Una vez que ha retornado la imagen, le asignamos la propiedad left o right a 0%. Ésto lo que hará es mover la imagen de afuera hacia dentro del slider con la duración y transición que le hemos dado via CSS (¿recuerdas el transition: all .6s ease-in-out?). Por último, una vez que la imagen se ha mostrado, eliminamos la anterior creada. Importante, si no, agregarías la misma imagen una y otra vez y el HTML se haría enorme.

    Veamos el método createImage:

    Código: javascript

    createImage(side) {
        let img = document.createElement('img')
        img.src = this.images[this.current]
        img.classList.add('img')
        if (side === 'right') {
          img.classList.add('right')
        } else {
          img.classList.add('left')
        }
        return img
      }


    Simplemente creamos un elemento IMG y le asignamos la ruta de la imagen a mostrar. Aquí viene algo importante:

  • El parámetro es left: la imagen se deslizará de izquierda a derecha.
  • El parámetro es right: la imagen se deslizará de derecha a izquierda.

    Ahora veamos el método removeBefore:

    Código: javascript

    removeBefore() {
        let child = this.slider.querySelector('img');
        this.slider.removeChild(child);
    }


    Este método es sencillo. Obtenemos el primer elemento tipo img del slider (que será el elemento img creado antes del nuevo) y lo removemos. De ésta manera siempre habrá solo un elemento img. Problemas de rendimiento, creo que ninguno, porque el manejo del DOM será de 2 cambios para 5 segundos.

    Por último, una función para ponerlo en modo automático en caso quiera el usuario:

    Código: javascript

    setModeAuto() {
        setInterval(function() {
          this.loadNextImg()
        }.bind(this), 5000)
    }





    No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

    PD: Disculpen el delay al cargar las imágenes al principio. Lo que pasa es que Github no permite CORS y no pude obtener las imágenes como blob. En local, works like a charm.