miércoles, 26 de octubre de 2016

Integración Java+Azure utilizando REST

Cuando escuchamos de Azure, inmediatamente se nos viene a la mente el SDK de esta interesante plataforma de Microsoft, sin embargo algunos desarrolladores preferimos invocar a los servicios de Azure directamente desde  su API REST, sin utilizar bibliotecas específicas. En este artículo encontrarás un ejemplo completo de cómo comunicarnos con Azure Blob Storage.


A continuación, se muestra la clase java que se encarga de realizar la comunicación:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

/**
 *
 * @author Rolando
 */
public class Util {

    private static Base64 base64 = new Base64();

    public static void signRequestSK(HttpURLConnection request, String account, String key) throws Exception {
        SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";

        StringBuilder sb = new StringBuilder();
        sb.append("GET\n"); // method
        sb.append("\n"); // content encoding
        sb.append("\n"); // content language
        sb.append("\n"); // content length
        sb.append("\n"); // md5 (optional)
        sb.append("\n"); // content type
        sb.append("\n"); // legacy date
        sb.append("\n"); // if-modified-since
        sb.append("\n"); // if-match
        sb.append("\n"); // if-none-match
        sb.append("\n"); // if-unmodified-since
        sb.append("\n"); // range
        sb.append("x-ms-date:" + date + "\n"); // headers
        sb.append("x-ms-version:2015-12-11\n");
        sb.append("/" + account + request.getURL().getPath() + "\ncomp:list");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256"));
        String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8"))));
        String auth = "SharedKey " + account + ":" + authKey;
        request.setRequestProperty("x-ms-date", date);
        request.setRequestProperty("x-ms-version", "2015-12-11");
        request.setRequestProperty("Authorization", auth);
        request.setRequestProperty("Accept-Charset", "UTF-8");
        request.setRequestProperty("Accept", "application/atom+xml,application/xml");
        request.setRequestMethod("GET");
    }

    public static void main(String args[]) throws Exception {
        //To use fiddler
        System.setProperty("http.proxyHost", "127.0.0.1");
        System.setProperty("https.proxyHost", "127.0.0.1");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyPort", "8888");
        //--
        //Inputs
        String account = "<Nombre_Cuenta>";
        String key = "<Access_Key>";
        //--
        HttpURLConnection connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection();
        signRequestSK(connection, account, key);
        connection.connect();
        StringBuffer text = new StringBuffer();
        InputStreamReader in = new InputStreamReader((InputStream) connection.getContent());
        BufferedReader buff = new BufferedReader(in);
        String line;
        do {
            line = buff.readLine();
            text.append(line + "\n");
        } while (line != null);
        System.out.println(text);
    }
}
Entonces, si utilizamos Fiddler, podremos ver que este sería el Request:
GET http://"<Nombre_Cuenta>".blob.core.windows.net/?comp=list HTTP/1.1
x-ms-date: Wed, 26 Oct 2016 18:35:49 GMT
x-ms-version: 2015-12-11
Authorization: SharedKey "<Nombre_Cuenta>":psyT2FlwpU3BU7/kKqtfi+ZBTuUpEX8bnsZ45O/1rUk=
Accept-Charset: UTF-8
Accept: application/atom+xml,application/xml
User-Agent: Java/1.8.0_92
Host: "<Nombre_Cuenta>".blob.core.windows.net
Connection: keep-alive
Y el response quedaría de la siguiente manera:
<?xml version="1.0" encoding="UTF-8" ?>
<EnumerationResults ServiceEndpoint="http://"<Nombre_Cuenta>".blob.core.windows.net/">
    <Containers>
        <Container>
            <Name>sas</Name>
            <Properties>
                <Last-Modified>Tue, 25 Oct 2016 19:19:46 GMT</Last-Modified>
                <Etag>"0x8D3FD0BE1F7E8D0"</Etag>
                <LeaseStatus>unlocked</LeaseStatus>
                <LeaseState>available</LeaseState>
            </Properties>
        </Container>
    </Containers>
    <NextMarker />
</EnumerationResults>

viernes, 21 de octubre de 2016

Servicios Web REST y PHP

En este artículo les mostraré cómo implementar una aplicación CRUD con PHP y servicios Web RESTFul. Los requisitos son los siguientes:
  • PHP 4.0 o superior
  • MySQL 4.0 o superior
  • Integración PHP+MySQL realizada correctamente.

Y la estructura de archivos es como se muestra a continuación:

rest_api
|-- dao.php
|-- index.php
|-- model.php
|-- service.php


Como primer paso, crear una base de datos y crear la tabla test con la estructura que se muestra a continuación.


Crear el archivo model.php, en donde se especificará una "clase entidad" que corresponderá con la tabla test de nuestra base de datos [Lo sé, no es java =(]. El hecho de no utilizar el concepto de encapsulación es debido a la versión de PHP que se utilizó para este post (4.0).
<?php

class Test {

    var $id;
    var $nombres;
    var $apellidos;

}
También tendremos una clase PHP exclusivamente para el acceso a base de datos. En el archivo dao.php, se implementarán todas las operaciones sobre base de datos.
<?php

class DAO {

    var $server;
    var $db;
    var $dblog;
    var $dbpass;

    function DAO() {
        $this->server = 'localhost';
        $this->db = 'test';
        $this->dblog = 'root';
        $this->dbpass = '123456';
    }

    function connect() {
        $this->cnn = new MySQL(array('host' => $this->server, 'user' => $this->dblog, 'password' => $this->dbpass, 'database' => $this->db));
    }

    function insert($test) {
        $this->connect();
        $sql = "INSERT INTO test(nombres, apellidos) VALUES ('" . $test->nombres . "','" . $test->apellidos . "'" . ")";
        $rs = $this->cnn->query($sql);
    }

    function findAll() {
        $this->connect();
        $sql = "SELECT * FROM test";
        $result = $this->cnn->query($sql);
        $rs = $result->fetchall(MYSQL_ASSOC);
        return $rs;
    }

    function find($id) {
        $this->connect();
        $sql = "SELECT * FROM test WHERE id=" . $id;
        $result = $this->cnn->query($sql);
        $rs = $result->fetchRow(MYSQL_ASSOC);
        return $rs;
    }

    function update($test) {
        $this->connect();
        $sql = "UPDATE test SET nombres ='" . $test->nombres . "', apellidos='" . $test->apellidos . "' WHERE id='" . $test->id . "'";
        $this->cnn->query($sql);
    }

    function delete($id) {
        $this->connect();
        $sql = "DELETE FROM test WHERE id='" . $id . "'";
        $this->cnn->query($sql);
    }

}
Las funciones de la clase DAO serán invocadas a través de una capa de servicio, la cual se encargará de procesar las peticiones y determinar qué tipo de servicio REST es el consultado. El archivo tendrá el nombre service.php.
<?php

require_once("dao.php");
require_once("model.php");

$method = $_SERVER['REQUEST_METHOD'];

$dao = new DAO();
$json_service = new Services_JSON();

//call DAO based on HTTP method
switch ($method) {
    case 'GET':
        $result = $dao->findAll();
        $json_response = $json_service->encode($result);
        echo $json_response;
        break;
    case 'POST':
        $test = new Test();
        $test->nombres = $_POST['nombres'];
        $test->apellidos = $_POST['apellidos'];
        $dao->insert($test);
        break;
    case 'PUT':
        parse_str(file_get_contents("php://input"), $post_vars);
        $test = new Test();
        $test->id = $post_vars['id'];
        $test->nombres = $post_vars['nombres'];
        $test->apellidos = $post_vars['apellidos'];
        $dao->update($test);
        break;
    case 'DELETE':
        parse_str(file_get_contents("php://input"), $post_vars);
        $id = $post_vars['id'];
        $dao->delete($id);
        break;
}
Finalmente, escribimos la parte de código correspondiente al lado del cliente en el archivo index.php. A través de una URL, accedemos a los servicios publicados y utilizamos sus funcionalidades dependiendo de la manera en cómo los invoquemos. (GET/POST/PUT/DELETE).
<?php

$url = "http://localhost/res_api/service.php";

if (!function_exists('http_build_query')) {

    function http_build_query($data, $prefix = null, $sep = '', $key = '') {
        $ret = array();
        foreach ((array) $data as $k => $v) {
            $k = urlencode($k);
            if (is_int($k) && $prefix != null) {
                $k = $prefix . $k;
            }

            if (!empty($key)) {
                $k = $key . "[" . $k . "]";
            }

            if (is_array($v) || is_object($v)) {
                array_push($ret, http_build_query($v, "", $sep, $k));
            } else {
                array_push($ret, $k . "=" . urlencode($v));
            }
        }

        if (empty($sep)) {
            $sep = ini_get("arg_separator.output");
        }

        return implode($sep, $ret);
    }

}

/* GET Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
$curl_response = curl_exec($client);
echo $curl_response;

/* POST Client */
$curl_post_data = array(
    'nombres' => 'Roger Manuel',
    'apellidos' => 'Rodriguez Alfaro'
);
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($client, CURLOPT_POST, 1);
curl_setopt($client, CURLOPT_POSTFIELDS, $curl_post_data);
$curl_response = curl_exec($client);
echo $curl_response;

/* PUT Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_CUSTOMREQUEST, "PUT");
$curl_put_data = array(
    'id' => '14',
    'nombres' => 'Enma',
    'apellidos' => 'Cruz Ticle'
);
curl_setopt($client, CURLOPT_POSTFIELDS, http_build_query($curl_put_data));
$curl_response = curl_exec($client);
if ($response === false) {
    $info = curl_getinfo($client);
    curl_close($client);
    die('error occured during curl exec. Additioanl info: ' . var_export($info));
}
curl_close($client);
echo $curl_response;


/* DELETE Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_CUSTOMREQUEST, "DELETE");
$curl_post_data = array(
    'id' => '15'
);
curl_setopt($client, CURLOPT_POSTFIELDS, http_build_query($curl_post_data));
$curl_response = curl_exec($client);
curl_close($client);
echo $curl_response;
Evidentemente, estaría restando la parte de la vista, pero eso ya será tema de otro post. Recuerden que es mi primer post de PHP, por lo que les pido comprensión si algunos conceptos o separaciones de capas no las hago correctamente, la idea es aportar no? :D

martes, 27 de septiembre de 2016

Columnas virtuales con MySQL

Las columnas virtuales son una de las características más resaltantes de MySQL 5.7. Estas columnas tienen la característica de ser definidas en la tabla como el resultado de funciones de otras columnas, por ejemplo:
create database demo;
use demo;
create table tabla(campo1 int, campo2 int, campo3 int generated always as (campo1*campo2) virtual);
insert into tabla values (10, 20, default);
select * from tabla;
+--------+--------+--------+
| campo1 | campo2 | campo3 |
+--------+--------+--------+
|     10 |     20 |    200 |
+--------+--------+--------+

La tabla creada con estas sentencias SQL tendrá dos columnas que almacenarán números enteros y una tercera columna que será el resultado de la multiplicación de las dos columnas anteriores, tal como se muestra en la siguiente figura:


Dichas columnas virtuales son cálculadas sobre la marcha, cada vez que los campos son recuperados. Una de las limitantes es que no podemos utilizar columnas virtuales definidas después de la actual, esto debido a que las columnas virtuales se calculan en el orden en que se definen.

Existe otro tipo de columnas generadas, llamadas columnas almancenadas, pero éstas serán revisadas en otro artículo.

lunes, 19 de septiembre de 2016

Secure Admin must be enabled to access the DAS remotely

En este corto artículo les presento la solución al error:

Secure Admin must be enabled to access the DAS remotely

Que ocurre cuando queremos acceder remotamente a un servidor glassfish recientemente instalado. La solución es tan sencilla como ejecutar el siguiente comando:

asadmin --host [host] --port [port] enable-secure-admin


Recuerden reiniciar el servicio para que los cambios tomen efecto. Saludos!

martes, 13 de septiembre de 2016

How to Install and Configure MySQL Cluster on Windows

MySQL Clúster es una versión de alta disponibilidad, alta redundancia de MySQL adaptada para el entorno de computación distribuida. Usa el motor de almacenamiento NDB Cluster para permitir la ejecución de varios servidores MySQL en un clúster. Este motor de almacenamiento está disponible en las distribuciones binarias de MySQL 5.0, Los sistemas en los que está disponible son Linux, Solaris y Windows. En este artículo vamos a revisar la manera cómo configurarlo.

La infraestructura que utilizaremos será la siguiente:


Los requerimientos son los siguientes:


Las recomendaciones son estas:

  • Desactivar firewall
  • Desactivar antivirus
  • Desinstalar y limpiar versiones anteriores de MySQL

Una vez descargada la versión, descomprimir el contenido en una carpeta en el disco C, a la cual llamaremos mysqlc.

En cada uno de los tres nodos de la figura mostrada anteriormente, crear la estructura de carpetas y su contenido a partir del comando:
C:\>mkdir my_cluster my_cluster\ndb_data my_cluster\mysqld_data my_cluster\conf
C:\>mkdir my_cluster\mysqld_data\mysql my_cluster\mysqld_data\ndbinfo
C:\>copy c:\mysqlc\data\mysql my_cluster\mysqld_data\mysql 
C:\>copy c:\mysqlc\data\ndbinfo my_cluster\mysqld_data\ndbinfo

Nodo Administrador
Crear el archivo config.ini en la carpeta c:\my_cluster\conf con el siguiente contenido:
[ndb_mgmd]
HostName=192.168.1.100
DataDir=c:\my_cluster\ndb_data
Nodeid=1

[Ndbd default]
NoOfReplicas=2

[Ndbd]
HostName=192.168.1.101
Nodeid=3

[Ndbd]
HostName=192.168.1.102
Nodeid=4
[Mysqld]
[Mysqld]
Iniciar el nodo administrador ejecutando el siguiente comando y dejando la consola abierta.
C:\my_cluster>/mysqlc/bin/ndb_mgmd -f conf/config.ini --initial --configdir=c:\my_cluster\conf\
En otra consola de comandos del nodo administrador, ejecutar el programa para verificar el estado de nuestro cluster.
C:\>C:\mysqlc\bin\ndb_mgm -e show
El resultado deberá ser similar al mostrado a continuación:


Nodos de Datos
En cada uno de los nodos de datos, crear el archivo my.cnf en la carpeta c:\my_cluster\conf. El puerto debe ser diferente para cada nodo de dato y de preferencia mayor a 5000.
[mysqld]
ndbcluster
datadir=c:\my_cluster\mysqld_data
port=15002
ndb-connectstring = 192.168.1.100

[mysql_cluster]
ndb-connectstring = 192.168.1.100
Ahora nos conectamos con el nodo administrador ejecutando el siguiente comando en cada uno de los dos nodos de datos
C:\>c:\mysqlc\bin\ndbd -c 192.168.1.100:1186
Sin embargo, al ejecutar en el nodo administrador el siguiente comando.
C:\>C:\mysqlc\bin\ndb_mgm -e show
Podemos notar que, a pesar que la conexión al nodo administrador ha sido hecha, los nodos SQL aún no están habilitados (not connected, accepting connect from any host).


Para levantar las API's, ejecutar en cada nodo de datos el siguiente comando en una consola nueva:
C:\my_cluster>c:\mysqlc\bin\mysqld --defaults-file=conf/my.cnf --console
Al volver a ejecutar el comando de visualización de conexiones en el nodo administrador, la salida será la siguiente:


Ahora sólo resta conectarnos al motor de base de datos de cada nodo utilizando una nueva consola para ejecutar el siguiente comando, teniendo en cuenta el puerto establecido en el archivo my.cnf. En este caso, mostraremos la conexión en el nodo que utiliza el puerto 15002.
c:\my_cluster>c:\mysqlc\bin\mysql -P15002 -u root
Al levantar los dos motores, cualquier sentencia SQL ejecuta en uno de ellos deberá verse automáticamente reflejada en la otra instancia. La siguiente imagen ilustra esto:


Inicialmente, en la consola azul habían las mismas base de datos que en la consola blanca, sin embargo al crear la base de datos clusterdb en esta última, la instancia azul reflejó automáticamente los cambios.

Para finalizar, les dejo los comandos para finalizar el servicio de manera segura:
#Ejecutar por cada nodo de datos, teniendo en cuenta el puerto
C:\my_cluster>c:\mysqlc\bin\mysqladmin -u root -h 127.0.0.1 -P15000 shutdown
#Nodo administrador
C:\my_cluster>c:\mysqlc\bin\ndb_mgm -e shutdown
#Ver puertos
netstat -oan

lunes, 12 de septiembre de 2016

How to use Java 8 predicate

Las expresiones lambda son funciones anónimas, es decir que no necesitan una clase. Su sintaxis básica se muestra a continuación:

parámetros → {Cuerpo lambda}

En caso de que el cuerpo lambda presenta más de una línea de código, será necesario incluir las llaves. En caso devuelva un valor, será necesario incluir también la cláusula return.

A continuación, mostraré algunos ejemplos del uso de predicados utilizando funciones lambda. Recuerden que estas características sólo están disponibles en la versión 8 del JDK.

Predicado simple
Representa una función con un único argumento cuyo valor de retorno será de tipo booleano. Aplicaremos este caso para verificar si un número es primo o no.

import java.util.function.Predicate;
import java.util.stream.IntStream;

/**
 *
 * @author Rolando
 */
public class Main {

    public static void main(String[] args) {
        System.out.println(isPrimo(1));
        System.out.println(isPrimo(2));
        System.out.println(isPrimo(3));
        System.out.println(isPrimo(4));
    }

    private static boolean isPrimo(final int numero) {
        Predicate<Integer> isDivisible = divisor -> numero % divisor == 0;
        return numero > 1 && IntStream.range(2, numero).
                noneMatch(index -> isDivisible.test(index));
    }
}

Encadenamiento de predicados
Los predicados también puede ser encadenados entre sí utilizando conectores de tipo AND, OR y de negación como si de cualquier expresión lógica se tratase. A continuación, se muestra un ejemplo sencillo en donde se verifica, a través de dos predicados, que un número se encuentra en un rango determinado.
import java.util.function.Predicate;

/**
 *
 * @author Rolando
 */
public class Main {

    public static void main(String[] args) {
        System.out.println(isInRange(7, 2, 8));
        System.out.println(isInRange(9, 11, 15));
    }

    private static boolean isInRange(final int numero, final int limiteInferior, final int limiteSuperior) {
        Predicate<Integer> mayorNumero = i -> i >= limiteInferior;
        Predicate<Integer> menorNumero = i -> i <= limiteSuperior;
        return limiteSuperior >= limiteInferior && mayorNumero.and(menorNumero).test(numero);
    }
}

Predicados como argumentos de funciones
Java 8 también nos permite pasar predicados como argumentos de funciones. En el siguiente ejemplo, sólo vamos a permitir números que cumplan con la regla que es establecida como parámetro de la función procesar.
import java.util.function.Predicate;

/**
 *
 * @author Rolando
 */
public class Main {

    public static void main(String[] args) {
        procesar(11, (i) -> i > 10);
        procesar(9, (i) -> i % 3 == 0);
    }

    /**
     * Función que recibe un predicado como parámetro
     *
     * @param number
     * @param predicate
     */
    static void procesar(int number, Predicate<Integer> predicate) {
        if (predicate.test(number)) {
            System.out.println("Número " + number + " ha sido aceptado!");
        }
    }
}

También podemos filtrar elementos de una lista utilizando predicados. El siguiente ejemplo lo ilutra:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 *
 * @author Rolando
 */
public class Main {

    public static void main(String[] args) {
        List<Usuario> users = new ArrayList<>();
        users.add(new Usuario("John", "admin"));
        users.add(new Usuario("Peter", "member"));
        List<Usuario> lstAdministradores = process(users, (u) -> u.getUsuario().equals("admin"));
        System.out.println(lstAdministradores.size());
    }

    static List<Usuario> process(List<Usuario> users, Predicate<Usuario> predicate) {
        List<Usuario> result = new ArrayList<>();
        for (Usuario user : users) {
            if (predicate.test(user)) {
                result.add(user);
            }
        }
        return result;
    }
}

class Usuario {

    private String nombre;
    private String usuario;

    public Usuario(String nombre, String usuario) {
        this.nombre = nombre;
        this.usuario = usuario;
    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getUsuario() {
        return usuario;
    }

    public void setUsuario(String usuario) {
        this.usuario = usuario;
    }
}

lunes, 29 de agosto de 2016

How to set up a JDBC Connection Pool on Glassfish

Un pool de conexiones es una clase java que tiene abiertas varias conexiones a base de datos. Cada vez que un programa cliente necesita comunicarse con una base de datos, establece una conexión, generalmente utilizando un protocolo especializado. Esta conexión, generalmente se mantiene abierta el tiempo que dura la ejecución del programa y sólo es cerrada al finalizar el trabajo de la aplicación con la base de datos.

Este artículo muestra la secuencia de pasos a seguir para poder configurar un pool de conexiones en un servidor de aplicaciones Glassfish. Los pasos a seguir son los siguientes:


Luego de configurado el nombre y seleccionado el motor de base de datos, debemos especificar ciertos parámetros de conexión. Si alguno de ellos no existe, se podrá agregar haciendo clic en el botón Add Property.


Podemos hacer una prueba del funcionamiento de nuestro pool de conexiones haciendo clic en el botón Ping.


Si al hacer clic en Ping les aparece el siguiente error: "Ping Connection Pool failed for SakilaPool. Class name is wrong or classpath is not set for : com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource Please check the server.log for more details.", no desesperen. Este error se debe a que no se encuentra la biblioteca correspondiente al conector MySQL en el dominio del servidor.

La solución es muy sencilla, sólo debemos colocar el archivo de extensión jar en su lugar correspondiente y reiniciar el servicio.



Volvemos a hacer ping al JDB Connection Pool y la respuesta debería ser como la que se muestra a continuación.


Ahora sólo basta configurar el acceso al pool como recurso nombrado. Para esto, hacer clic en el botón New de la pantalla que se muestra a continuación:


Y luego hacer clic en OK. Con esto tendremos configurado todo y sólo restaría hacer consultas de prueba a la base de datos a través del pool de conexiones.


Para realizar las pruebas debemos crear un proyecto Java Web y en la página de inicio deberá ser similar a:

<%@page import="java.sql.ResultSet"%> 
<%@page import="java.sql.Statement"%> 
<%@page import="javax.sql.DataSource"%> 
<%@page import="java.sql.Connection"%> 
<%@page import="javax.naming.InitialContext"%> 
<%@page import="javax.naming.Context"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <%
            Context ctx = new InitialContext();
            DataSource ds = (DataSource) ctx.lookup("jdbc/sakila");
            Connection con = ds.getConnection();
            if (con != null) {
                Statement st = con.createStatement();
                ResultSet rs = st.executeQuery("SELECT * FROM actor");
                out.print("<table>");
                while (rs.next()) {
                    out.print("<tr>");
                    out.print("<td>" + rs.getString("first_name") + "</td>");
                    out.print("<td>" + rs.getString("last_name") + "</td>");
                    out.print("</tr>");
                }
                out.print("</table>");
                rs.close();
                st.close();
                con.close();
            }%>
    </body>
</html>
La salida deberá verse así:


Espero este artículo les sea de utilidad. Cualquier pregunta, no duden en comentarla.

jueves, 25 de agosto de 2016

Authentication with JAX-WS SOAP header

Una de las formas más comunes de autenticación en servicios Web es a través de credenciales del tipo usuario y contraseña, agregados como cabeceras en una solicitud SOAP. En este artículo les voy a mostrar la manera cómo enviar estos parámetros desde el cliente hacia el servidor, en donde serán recuperados para posteriormente ejecutar la lógica de autenticación requerida.

Como primer paso, se debe crear en Netbeans un proyecto Java Web para luego crear la estructura de paquetes que se muestra a continuación.


Se debe crear una Interface Notificador en donde se declarará el método que será implementado por nuestro servicio Web. En este caso, la funcionalidad a implementar será la de responder con un saludo a un nombre que se recibirá como parámetro.
package com.rolandopalermo.blog.interfaces;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public interface Notificador {

    @WebMethod
    public String saludar(String nombre);
}

Como se puede apreciar, dos anotaciones fueron agregadas. La primera es @WebService y se utiliza para indicar que una clase está implementando un servicio Web o que una Service Endpoint Interface está implementando una interfaz de servicio Web. La segunda anotación, @WebMethod, indica que un método es una operación de servicio Web.

Ahora toca implementar la interfaz creada anteriormente. Dicha implementación se realizará a través de un servicio Web, tal como se muestra a continuación:



/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rolandopalermo.blog.ws;

import com.rolandopalermo.blog.interfaces.Notificador;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;

@WebService(serviceName = "NotificadorImpl")
public class NotificadorImpl implements Notificador {

    @Resource
    private WebServiceContext wsc;

    @Override
    public String saludar(String nombre) {
        /*Lectura de parámetros de cabecera SOAP*/
        MessageContext mc = wsc.getMessageContext();
        Map requestHeader = (Map) mc.get(MessageContext.HTTP_REQUEST_HEADERS);
        List usuarioList = (List) requestHeader.get("usuario");
        List passwrdList = (List) requestHeader.get("password");
        //***************************************************
        String usuario = "";
        String password = "";
        if (usuarioList != null && passwrdList != null) {
            usuario = (String) usuarioList.get(0);
            password = (String) passwrdList.get(0);
            if (usuario.equals("admin") && password.equals("123456")) {
                if (nombre != null && nombre.length() > 0) {
                    return "Hola " + nombre;
                }
                return "Error";
            } else {
                return "Error de autenticación";
            }
        } else {
            return "Se requiere campos de autenticación";
        }
    }
}
El último paso que resta, en el lado del servidor, sería el despliegue del proyecto. Para esto, hacer lo siguiente:


Y para finalizar, sólo resta escribir el código correspondiente a los clientes. Para el caso de Netbeans, se tendría que crear un servicio Web cliente con el WSDL del servicio Web. Si no enviamos los parámetros de autenticación, la respuesta del servicio será la siguiente:


Por otra parte, si enviamos los parámetros requeridos, tal como se muesta a continuación, la respuesta debe ser la correspondiente a un usuario correctamente autenticado.
package com.rolandopalermo.blog.cliente;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;

public class Main {
    
    private static final String WS_URL = "http://localhost:8080/SOAPAuthentication/NotificadorImpl?wsdl";
    
    public static void main(String[] args) {
        
        com.rolandopalermo.blog.ws.NotificadorImpl_Service service = new com.rolandopalermo.blog.ws.NotificadorImpl_Service();
        com.rolandopalermo.blog.ws.NotificadorImpl port = service.getNotificadorImplPort();
        
        Map<String, Object> req_ctx = ((BindingProvider)port).getRequestContext();
        req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WS_URL);

        Map<String, List<String>> headers = new HashMap<String, List<String>>();
        headers.put("usuario", Collections.singletonList("admin"));
        headers.put("password", Collections.singletonList("123456"));
        req_ctx.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
        
        System.out.println(port.saludar("Rolando"));
        
    }
}

Al ejecutar dicho código, la respuesta será la siguiente:


Código fuente del proyecto:


Espero que este post les sea de utilidad. Cualquier consulta, no duden de escribirla en la caja de comentarios, saludos!

viernes, 30 de octubre de 2015

Enviar correo con Matlab y Gmail


UserName = 'emisor@gmail.com';
passWord = '';
setpref('Internet','E_mail',UserName);
setpref('Internet','SMTP_Server','smtp.gmail.com');
setpref('Internet','SMTP_Username',UserName);
setpref('Internet','SMTP_Password',passWord);
props = java.lang.System.getProperties;
props.setProperty('mail.smtp.auth','true');
props.setProperty('mail.smtp.socketFactory.class', ...
                  'javax.net.ssl.SSLSocketFactory');
props.setProperty('mail.smtp.socketFactory.port','465');
emailto = 'receptor@gmail.com'; % recipient's email
sendmail(emailto, 'My Subject', 'My message');

miércoles, 23 de septiembre de 2015

Obtener imagen de Cámara IP con Matlab


% Obtener imagen de cámara IP
clc;
clear;
figure;
while(1)
%--Definición de variables
url='http://192.168.2.157/image/jpeg.cgi'; 
snapshot1='image_cam_1.jpg';
user='admin';
pass='admin';
%--
urlwrite(url,snapshot1,'Authentication','Basic','Username',user,'Password',pass);
I1 = imread(snapshot1);
imshow(I1);
delete(snapshot1);
end