Páginas

martes, 13 de diciembre de 2016

Primeros pasos con SQLite

Para pequeñas pruebas o arquetipos solemos utilizar una BBDD SQLite, en esta entrada dejo un pequeño trozo de código con algunas de las operaciones mas comunes:
  • Conectar y desconectar de la BBDD
  • Crear y borrar tablas
  • Insertar, actualizar y borrar registros
  • Lanzar diferentes consultas
  • Commit y rollback de transacciones
  • Uso de  PreparedStatement

En el pom.xml de nuestro proyecto deberemos incluir la dependencia:

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.15.1</version>
</dependency>

El código sería:

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSQLite {

 private static final String URL = "jdbc:sqlite:test.db";
 
 public static void main(String[] args) throws SQLException {
  
  final Connection conexion = DriverManager.getConnection(URL);
  final DatabaseMetaData metadatos = conexion.getMetaData();
  System.out.println("------------------------------------------------------------");
  System.out.println("Conectados al motor de BBDD: " + metadatos.getDriverName());
  System.out.println("DriverVersion: " + metadatos.getDriverVersion());
  System.out.println("DatabaseProductVersion: " + metadatos.getDatabaseProductVersion());
  System.out.println("JDBCMajorVersion: " + metadatos.getJDBCMajorVersion());
  
        String sql = "DROP TABLE IF EXISTS CLIENTE;";
        Statement stmt = conexion.createStatement();
        stmt.execute(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Borrada la tabla CLIENTE");
  
        sql = "CREATE TABLE IF NOT EXISTS CLIENTE (ID integer PRIMARY KEY, NOMBRE text NOT NULL, DIRECCION text NOT NULL, PRIORIDAD integer NOT NULL);";
        stmt = conexion.createStatement();
        stmt.execute(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Creada la tabla CLIENTE");
  
  conexion.setAutoCommit(false);
        sql = "INSERT INTO CLIENTE(NOMBRE,DIRECCION,PRIORIDAD) VALUES(?,?,?);";
        PreparedStatement pstmt = conexion.prepareStatement(sql);
        pstmt.setString(1, "cliente1");
        pstmt.setString(2, "direccion1");
        pstmt.setString(3, "1");
        pstmt.executeUpdate();
        pstmt.setString(1, "cliente2");
        pstmt.setString(2, "direccion2");
        pstmt.setString(3, "1");
        pstmt.executeUpdate();
        pstmt.setString(1, "cliente3");
        pstmt.setString(2, "direccion3");
        pstmt.setString(3, "2");
        pstmt.executeUpdate();
        conexion.commit();
  System.out.println("------------------------------------------------------------");
  System.out.println("Insertados 3 registros en tabla CLIENTE");
  
  sql = "SELECT count(*) as NUMREG FROM CLIENTE";
  ResultSet rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados " + rs.getInt("NUMREG") + " registros en tabla CLIENTE");
  
  sql = "SELECT count(*) as NUMREG FROM CLIENTE WHERE NOMBRE ='cliente1'";
  rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados " + rs.getInt("NUMREG") + " registros en tabla CLIENTE con NOMBRE ='cliente1'");
  
  sql = "SELECT count(*) as NUMREG FROM CLIENTE WHERE NOMBRE LIKE 'cliente%'";
  rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados " + rs.getInt("NUMREG") + " registros en tabla CLIENTE con NOMBRE like 'cliente%'");
  
  sql = "SELECT * FROM CLIENTE WHERE PRIORIDAD=1";
  rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados los siguientes registros en tabla CLIENTE con PRIORIDAD=1");
  System.out.println("ID NOMBRE DIRECCION PRIORIDAD");
  while (rs.next()) {
   System.out.println(rs.getInt("ID") + " " + rs.getString("NOMBRE") + " " + rs.getString("DIRECCION") + " " + rs.getInt("PRIORIDAD"));
  }
  
  sql = "UPDATE CLIENTE SET PRIORIDAD=2 WHERE PRIORIDAD=1";
  int numRecAct = stmt.executeUpdate(sql);
  conexion.commit();
  System.out.println("------------------------------------------------------------");
  System.out.println("Actualizados "+numRecAct+" registros de PRIORIDAD=1 a PRIORIDAD=2");
  System.out.println("ID NOMBRE DIRECCION PRIORIDAD");
  
  sql = "SELECT * FROM CLIENTE";
  rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados los siguientes registros en tabla CLIENTE");
  System.out.println("ID NOMBRE DIRECCION PRIORIDAD");
  while (rs.next()) {
   System.out.println(rs.getInt("ID") + " " + rs.getString("NOMBRE") + " " + rs.getString("DIRECCION") + " " + rs.getInt("PRIORIDAD"));
  }
  
  sql = "DELETE FROM CLIENTE WHERE NOMBRE='cliente1'";
  numRecAct = stmt.executeUpdate(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Borrados los registros con NOMBRE='cliente1'");
  
  sql = "SELECT * FROM CLIENTE";
  rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados los siguientes registros en tabla CLIENTE");
  System.out.println("ID NOMBRE DIRECCION PRIORIDAD");
  while (rs.next()) {
   System.out.println(rs.getInt("ID") + " " + rs.getString("NOMBRE") + " " + rs.getString("DIRECCION") + " " + rs.getInt("PRIORIDAD"));
  }
  
  sql = "DELETE FROM CLIENTE";
  numRecAct = stmt.executeUpdate(sql);
  conexion.rollback();
  System.out.println("------------------------------------------------------------");
  System.out.println("Intento cancelado de borrado completo de la tabla CLIENTE");
  
  sql = "SELECT * FROM CLIENTE";
  rs = stmt.executeQuery(sql);
  System.out.println("------------------------------------------------------------");
  System.out.println("Encontrados los siguientes registros en tabla CLIENTE");
  System.out.println("ID NOMBRE DIRECCION PRIORIDAD");
  while (rs.next()) {
   System.out.println(rs.getInt("ID") + " " + rs.getString("NOMBRE") + " " + rs.getString("DIRECCION") + " " + rs.getInt("PRIORIDAD"));
  }
  
  conexion.close();
 }
}

Un saludo

Comparativa librerías JSON: Gson y Jackson

Para manipular datos en formato JSON existen dos librerías: Jackson y Gson. La primera es muy conocida por ser el 'motor' usado por el framework Jersey de Web Services RESTfull, la segunda no lo es tanto.

El presente ejemplo tiene como finalidad comparar el rendimiento de ambas. Tras unas primeras pruebas con POJOS java de diversos tamaños me da la sensación de que Jackson es un poquitín mas rápida.

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class TestMotoresJSON {

 private static final int NUMCLIENTES = 5;
 private static final int NUMFACTURAS = 100000;
 
 private static final Gson motorGSON = new GsonBuilder().create();
 private static final ObjectMapper motorJACKSON = new ObjectMapper();
 
 public static void main(String[] args) throws IOException {

  //Montamos un pojo Java con la lista de clientes y facturas
  final List lstClientes = montaListaClientes();

  //Serializo GSon
  long tmp = System.currentTimeMillis();
  String lstClientesJSON = serializoGSon(lstClientes);
  System.out.println("Tiempo serialización GSon:"+(System.currentTimeMillis()-tmp));

  //Deserializo GSon
  tmp = System.currentTimeMillis();
  deserializoGSon(lstClientesJSON);
  System.out.println("Tiempo deserialización GSon:"+(System.currentTimeMillis()-tmp));
  
  //Serializo Prueba
  tmp = System.currentTimeMillis();
  lstClientesJSON = serializoJackson(lstClientes);
  System.out.println("Tiempo serialización Jackson:"+(System.currentTimeMillis()-tmp));
  
  //Deserializo Prueba
  tmp = System.currentTimeMillis();
  deserializoJackson(lstClientesJSON);
  System.out.println("Tiempo deserialización Jackson:"+(System.currentTimeMillis()-tmp));
 }
 
 private static String serializoGSon(final List lstClientes){
   
  return motorGSON.toJson(lstClientes);
 }
 
 private static void deserializoGSon(final String lstClientesJSON){
  
  motorGSON.fromJson(lstClientesJSON, new TypeToken>(){}.getType());
 }
 
 private static String serializoJackson(final List lstClientes) throws IOException {

  return motorJACKSON.writeValueAsString(lstClientes);
 }
 
 private static void deserializoJackson(final String lstClientesJSON) throws IOException {
  
  motorJACKSON.readValue(lstClientesJSON, motorJACKSON.getTypeFactory().constructCollectionType(ArrayList.class, Cliente.class));
 }
 
 private static List montaListaClientes(){
  
  final List lst = new ArrayList();
  Cliente cliente = null;
  Factura fac = null;
  for (int c=0;c());
   for (int f=0;f
Necesitaremos las clases de apoyo:
import java.util.List;

public class Cliente {

 private String CIF;
 private String nombre;
 private String direccion;
 private String email;
 private String telefono;
 private String fax;
 private List facturas;
 
 public String getCIF() {
  return CIF;
 }
 public void setCIF(String cIF) {
  CIF = cIF;
 }
 public String getNombre() {
  return nombre;
 }
 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
 public String getDireccion() {
  return direccion;
 }
 public void setDireccion(String direccion) {
  this.direccion = direccion;
 }
 public String getEmail() {
  return email;
 }
 public void setEmail(String email) {
  this.email = email;
 }
 public String getTelefono() {
  return telefono;
 }
 public void setTelefono(String telefono) {
  this.telefono = telefono;
 }
 public String getFax() {
  return fax;
 }
 public void setFax(String fax) {
  this.fax = fax;
 }
 public List getFacturas() {
  return facturas;
 }
 public void setFacturas(List facturas) {
  this.facturas = facturas;
 }
}

public class Factura {

 private String id;
 private String fecha;
 private long bruto;
 private long neto;
 private long iva;
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getFecha() {
  return fecha;
 }
 public void setFecha(String fecha) {
  this.fecha = fecha;
 }
 public long getBruto() {
  return bruto;
 }
 public void setBruto(long bruto) {
  this.bruto = bruto;
 }
 public long getNeto() {
  return neto;
 }
 public void setNeto(long neto) {
  this.neto = neto;
 }
 public long getIva() {
  return iva;
 }
 public void setIva(long iva) {
  this.iva = iva;
 }
}

Necesitaremos importar en el pom.xml las dependencias:

     <dependencies>
          <dependency>
               <groupId>com.fasterxml.jackson.core</groupId>
               <artifactId>jackson-databind</artifactId>
               <version>2.8.5</version>
          </dependency>
          <dependency>
               <groupId>com.google.code.gson</groupId>
               <artifactId>gson</artifactId>
               <version>2.6.2</version>
          </dependency>
     </dependencies>

miércoles, 7 de diciembre de 2016

Añadir un fichero nuevo a un fichero zip previamente existente

Hola

Se nos presentó la necesidad de añadir contenido a ciertos ficheros ZIP preexistentes, para abordar el problema podíamos usar la librería Commons Compress de Apache (https://commons.apache.org/proper/commons-compress/) o directamente las clases del paquete 'java.util.zip' del JDK.

El problema es que ambas librerías no añaden contenido a un zip preexistente, sino que sobrescriben el contenido del ZIP original por el nuevo contenido. Debe ser por alguna cuestión 'conceptual' relativa al formato ZIP.

Por tanto la forma de abordar el problema es generar un nuevo fichero zip temopral, volcar en dicho fichero el contenido del fichero zip original, añadir al fichero zip temporal el nuevo fichero, borrar el fichero zip original y renombrar el fichero temporal con el nombre del original.

Ya puestos a documentar el tema, dejo el código con la clase generada:

package org.casa.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class TestZip {

 private static final int BUFFER_SIZE = 1024;

 /**
  * Crea un fichero zip a partir del fichero recibido con el nombre
  * de dicho fichero y extensión zip, el zip se genera en la carpeta
  * que contiene el fichero recibido.
  * 
  * @param fichero
  * @throws IOException
  */
 public static void comprimeFichero(final File fichero) throws IOException {

  //Generamos un fichero zip con nombre del fichero recibido y extensión zip
  final String nombreFichZip = fichero.getName().substring(0, fichero.getName().lastIndexOf("."))+".zip";

  //Creamos el nuevo fichero zip en la ubicación del fichero recibido
  final File zf = new File(fichero.getParentFile()+File.separator+nombreFichZip);
  final FileOutputStream fos = new FileOutputStream(zf);
  final ZipOutputStream zos = new ZipOutputStream(fos);

  //Comprimimos el fichero recibido en el nuevo zip
  final ZipEntry ze = new ZipEntry(fichero.getName());
  zos.putNextEntry(ze);
  final FileInputStream fisEntry = new FileInputStream(fichero);
  copyToZip(fisEntry, zos);

  //Cerramos los manejadores
  fisEntry.close();
  zos.flush();
  zos.closeEntry();
  zos.close();
  fos.close();
 }

 /**
  * Crea un fichero zip con todos los ficheros contenidos en la carpeta
  * recibida, el zip tendrá el nombre del directorio con extensión zip y 
  * se ubicará en el directorio padre del directorio recibido.
  * 
  * @param directorio
  * @throws IOException 
  */
 public static void comprimeDirectorio(final File carpeta) throws IOException {

  //Generamos un fichero zip con nombre de la carpeta recibida y extensión zip
  final String nombreFichZip = carpeta.getName()+".zip";

  //Creamos el nuevo fichero zip en la carpeta padre de la carpeta recibida
  final File zf = new File(carpeta.getParentFile()+File.separator+nombreFichZip);
  final FileOutputStream fos = new FileOutputStream(zf);
  final ZipOutputStream zos = new ZipOutputStream(fos);

  //Comprimimos cada fichero de la carpeta en el nuevo zip
  ZipEntry ze = null;
  FileInputStream fisEntry = null;
  for (final File fich : carpeta.listFiles()) {
   ze = new ZipEntry(fich.getName());
   zos.putNextEntry(ze);
   fisEntry = new FileInputStream(fich);
   copyToZip(fisEntry, zos);
   fisEntry.close();
  }

  //Cerramos los manejadores
  zos.flush();
  zos.closeEntry();
  zos.close();
  fos.close();
 }

 /**
  * Añade un nuevo fichero comprimido al fichero zip recibido por parámetro.
  * 
  * Creamos un zip temporal intermedio donde copiamos el contenido del zip
  * recibido y en este zip temporal añadimos el fichero recibido. Finalmente
  * reemplazamos el fichero zip original con el fichero zip generado.
  * 
  * @param zipFile
  * @param fichero
  * @throws IOException
  */
 public static void addFicheroZip(final File zipFile, final File fichero) throws IOException {

  //Guardamos el nombre y el path del fichero zip recibido
  final String nombreFicheroZip = zipFile.getName();
  final String pathFicheroZip = zipFile.getParent();
  
  //Creamos un nuevo fichero zip temporal en la ubicación del zip recibido
  final File ftemp = File.createTempFile(nombreFicheroZip,null,zipFile.getParentFile());
  final FileOutputStream fostemp = new FileOutputStream(ftemp);
  final ZipOutputStream zostemp = new ZipOutputStream(fostemp);
  
  //Abrimos el fichero zip recibido por parametro
  final FileInputStream fis = new FileInputStream(zipFile);
  final ZipInputStream zis = new ZipInputStream(fis);
  
  //Sacammos el contenido del zip recibido por parametro y lo volcamos al zip temporal
  ZipEntry ze = zis.getNextEntry();
  while (ze!= null) {
   ze.getName();
   zostemp.putNextEntry(ze);
   copyToZip(zis, zostemp);
   ze = zis.getNextEntry();
  }
  
  //Añadimos el fichero nuevo al fichero zip temporal
  final ZipEntry zenuevo = new ZipEntry(fichero.getName());
  zostemp.putNextEntry(zenuevo);
  final FileInputStream fisNuevo = new FileInputStream(fichero);
  copyToZip(fisNuevo, zostemp);

  //Cerramos los manejadores
  fisNuevo.close();
  zis.close();
  fis.close();
  zostemp.flush();
  zostemp.closeEntry();
  zostemp.close();
  fostemp.close();

  //Borramos el fichero zip recibido
  zipFile.delete();
  
  //Renombramos el fichero temporal nuevo
  ftemp.renameTo(new File(pathFicheroZip+File.separator+nombreFicheroZip));
 }

 /**
  * Extrae el contenido del zip recibido por parámetro en la carpeta
  * que contiene el fichero zip recibido.
  * 
  * 
  * @param zipFile
  * @throws IOException
  */
 public static void descomprimeZip(final File zipFile) throws IOException {
  
  //Abrimos el fichero zip recibido por parametro
  final FileInputStream fis = new FileInputStream(zipFile);
  final ZipInputStream zis = new ZipInputStream(fis);

  //Extraemos cada fichero del zip en la carpeta del ZIP recibido
  File fe = null;
  FileOutputStream fose = null;
  ZipEntry ze = zis.getNextEntry();
  while (ze!= null) {
   //Procesamos cada fichero del zip
   fe = new File(zipFile.getParentFile().getCanonicalPath()+File.separator+ze.getName());
   fose = new FileOutputStream(fe);
   copyFromZip(zis, fose);
   fose.flush();
   fose.close();
   //Saltamos al siguiente fichero del zip
   ze = zis.getNextEntry();
  }
  
  //Cerramos los manejadores
  zis.close();
  fis.close();  
 }

 private static void copyToZip(final InputStream is, final ZipOutputStream zos) throws IOException {

  byte[] buffer = new byte[BUFFER_SIZE];
  int length;
  while ((length = is.read(buffer)) >= 0) {
   zos.write(buffer, 0, length);
  }
 }
 
 private static void copyFromZip(final ZipInputStream zis, final OutputStream os) throws IOException {

  byte[] buffer = new byte[BUFFER_SIZE];
  int length;
  while ((length = zis.read(buffer)) >= 0) {
   os.write(buffer, 0, length);
  }
 }
}


Un saludo

miércoles, 30 de noviembre de 2016

Instalar y desinstalar VMware Workstation Player en Ubuntu

Para instalar 'VMware Workstation Player':

1) Nos descargamos 'VMware Workstation Player' en el enlace: http://www.vmware.com/go/tryplayerpro-linux-64 ofrecido en la página: http://www.vmware.com/products/player/playerpro-evaluation.html y lo

2) Lo instalamos ejecutando:

    sudo ./VMware-Player-12.5.1-4542065.x86_64.bundle

Para desinstalar 'VMware Workstation Player':

1) Ejecutamos:

    sudo vmware-installer -u vmware-player

Esto nos levantará un asistente de desinstalación que deberemos seguir.

Monitorización y afinamiento del rendimiento de aplicaciones en Tomcat con JVisualVM

Para monitorizar el estado de un servidor Tomcat podemos utilizar dos herramientas que vienen incluídas en el JDK. JConsole y la mas moderna JVisualVM. Esta segunda agrupa las funcionalidades de varias herramientas habituales: jstat, jconsole, jstack, jmap e jinfo, acercándose mucho a la utilidad de una herramienta comercial de perfilado.

Vamos a centrarnos en comentar JConsole y JVisualVM.

Lo mas correcto a la hora de utilizar cualquiera de las dos herramientas es conectar con el servidor mediante JMX (Java Management Extensions) que es una especificación que define un mecanismo para ajustar aplicaciones y servicios en caliente. La monitorización mediante JMX es la menos intrusiva, se estima que ronda el 5% de sobrecarga.

Para monitorizar nuestro servidor mediante JMX tendremos que realizar los siguientes ajustes:

1) Paramos nuestro servidor Tomcat:

    $ /{tomcat-folder}/bin/shutdown.sh

2) Creamos (si no existe ya) el fichero /{tomcat-folder}/bin/setenv.sh con el siguiente contenido:

export JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

3) Rearrancamos nuestro servidor Tomcat:

    $ /{tomcat-folder}/bin/shutdown.sh

A la hora de monitorizar el servidor tomcat desde nuestro PC podremos levantar JConsole o JVisualVM, ambas herramientas están disponibles en el JDK:

1) Levantamos cualquiera de las herramientas


    $ /{jdk-folder}/bin/jconsole
    $ /{jdk-folder}/bin/jvisualvm

2) Definimos la conexión remota JMX, para lo que necesitaremos conocer la IP y el puerto del servidor, sería siempre algo así "127.0.0.1:9999".

  • JConsole. Connection / New Connection / Remote Process / <hostname>:<port>
  • JVisualVM. File /Add JMX Connection / <hostname>:<port>

Con esto ya deberíamos poder monitorizar nuestra JVM en producción o en pruebas de carga (lanzadas por ejemplo con JMeter) en preexplotación y observando la evolución de:

     Memoria Heap
     Memoria PermGen
     Threads
     Garbage Collector
     CPU utilizada
     Clases cargadas en memoria
     MBeans
     etc

Aunque para una monitorización básica de la JVM ambas herramientas son válidas, JVisualVM es mucho mas potente, dado que ofrece muchas de las características de herramientas comerciales de "profiling" (que se puede traducir como "perfilado", "afinamiento" o "puesta a punto") como JProfiler y YourKitdado. Con JVisualVM podremos "afinar" el rendimiento de nuestra aplicación:
  • Monitorizando el tiempo de CPU consumido por nuestras clases/paquetes durante una tanda de pruebas, detectando cuellos de botella o problemas de programación que impliquen tiempos de respuesta muy elevados.
  • Monitorizando la Memoria utilizada por nuestra aplicación, detectando que objetos aparecen con un frecuencia inesperada (posibles memory leaks), cual es el consumo de memoria de dichos objetos (posibles pool), etc.

Nota. JVisualVM se puede ampliar con muchos plugins, se puede integrar con Eclipse, es gratuíta, ...




miércoles, 16 de noviembre de 2016

Herramienta de cifrado RSA para Windows

Hola

Buscando en Internet una herramienta que permitiese a usuáriosde Windows  enviarse ficheros cifrados con RSA me he topado con Kleopatra (https://www.gpg4win.org/download.html), así que lo he instalado y probado en una virtualización con Windows 7 y he verificado que funciona sin problemas y no es dificil de manejar.

Esta sería la secuencia de pasos básicos que deberán llevar a cabo dos personas que necesiten intercambiar ficheros cifrados con RSA:

1) Tareas previas del receptor.

1.1) Instalar Kleopatra lanzando el instalador 'gpg4win-2.3.3.exe' de Kleopatra. Durante la instalación  pregunta qué componentes instalar, dejamos marcadas las opciones por defecto.
1.1) El receptor deberá sacar la clave pública del certificado a distribuir con su cadena de confianza, desde  un navegador donde tengamos instalado el certificado a utilizar se hace fácilmente: Administrador de  certificados/Sus certificados/Ver/Detalles/Exportar/Certifiacdo X.509 con cadena (PEM)
1.2) El receptor deberá hacer llegar al emisor dicha clave pública. Se generará un fichero xxxxxxxxxxx.cer
1.3) Arrancamos Kleopatra
1.4) El receptor deberá tener su certificado completo (clave pública+clave privada) de su certificado en Kleopatra. Para obtener dicho certificado lo mas sencillo es sacarlo del navegador donde lo tengamos instalado: Administrador de certificados/Sus certificados/Hacer copia, nos pedirá una password  y generará un fichero xxxxxxxxxxxxxx.p12
1.5) Importamos el certificado completo del receptor (xxxxxxxxxxxxxx.p12) en Kleopatra, pulsamos el botón  'Import certificates' y seleccionamos el fichero xxxxxxxxxxx.p12 obtenido del navegador. Nos pedirá  la password del almacen P12 y que definamos una password interna para el almacen interno de Kleopatra

2) Tareas del emisor de documentos cifrados

2.1) Instalar Kleopatra lanzando el instalador 'gpg4win-2.3.3.exe' de Kleopatra.
2.2) Importar la clave pública del certificado del receptor: pulsan el botón 'Import certificates' y  seleccionan el fichero xxxxxxxxxxx.cer recibido desde el receptor
2.3) Ajustar la config de Kleopatra marcando el check 'Never consult CRL' de: Settings/Configure Kleopatra/S/MIME Validation/
2.4) Para cifrar un fichero lo localizamos con el Explorador de Archivos y tras pulsar sobre el mismo con el botón derecho del ratón seleccionaremos la opción 'Mas opciones de GpgEX/Cifrar', pulsaremos Next,  seleccionamos la clave pública del INE con la que deseamos cifrar (xxxxxxxxxxx.cer), pulsamos el botón Add y  el botón Encrypt.
2.5) Enviamos al receptor el fichero generado

3) Tareas en el receptor por cada documento cifrado

3.1) Recibimos un fichero cifrado con nuestra clave pública por parte de cierto organismo por lo  que a priori solo nosotros podemos ver su contenido. Ojo como no está firmado cualquiera podría haber generado ese fichero.
3.2) Pulsamos con el botón derecho del ratón sobre el fichero, seleccionamos 'Mas opcioens de GpgEX/Descifrar' nos pedirá la password interna de la herramienta (definida al introducir el certificado completo del receptor en Kleopatra) y sobreescribirá el fichero cifrado con el fichero en claro.

Notas. He probado con ficheros desde 1KB, 2KB, 4KB,... hasta 1MB y el tiempo es inapreciable, luego he probado con un fichero de 17MB y ha taraddo un par de segundos, por último he probado con un fichero de 54MB y ha tardado 7 segundos en cifrarlo. Las operaciones de descifrado vienen a ser igual de rápidas.

Un saludo

jueves, 10 de noviembre de 2016

Problemas en Apache '/opt/apache/bin/httpd -k start'

Hola

He revisando cierto servidor Apache de desarrollo donde apache no respondía ni por el puerto 80 ni por el puerto 443 (timeout siempre), verifiqué que el Apache estaba aparentemente levantado mirando si había un proceso escuchando el 80 y el 443:

[usuarioxxxxxx@desa11 logs]$ sudo netstat -tulpn

   tcp        0      0 :::443        :::*           LISTEN      1662/httpd
   tcp        0      0 :::80          :::*           LISTEN      1662/httpd

Como sí que lo había, intenté pararlo/arrancarlo del modo habitual obteniendo un error relativo a que los puertos estaban siendo utilizados:

[usuarioxxxxxx@desa11 logs]$ sudo /etc/init.d/apache stop
[usuarioxxxxxx@desa11 logs]$ sudo /etc/init.d/apache start

Saqué toda la info posible del proceso 1662:

   [usuarioxxxxxx@desa11 logs]$ ps -Af | grep 1662
   root      1662  1660  0 09:54 ?        00:00:00 /opt/apache/bin/httpd -k start

Y me quedé rallado con '/opt/apache/bin/httpd -k start', de donde salía ese '-k' que nunca había observado.

Maté el proceso sin mas:

   [usuarioxxxxxx@desa11 logs]$ sudo kill 1662

Volví a levantar el apache del modo habitual:

   [usuarioxxxxxx@desa11 logs]$ sudo /etc/init.d/apache start
   Apache/2.2.23 mod_ssl/2.2.23 (Pass Phrase Dialog)
   Some of your private key files are encrypted for security reasons.
   In order to read them you have to provide the pass phrases.
   Server www.example.com:443 (RSA)
   Enter pass phrase:
   OK: Pass Phrase Dialog successful.

Tras el rearranque anterior del Apache de ya funcionaba todo sin problemas, pero revisando de nuevo los puertos de nuevo SORPRESA!!!:

[usuarioxxxxxx@desa11 ~]$ sudo netstat -tulpn

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name  
tcp        0      0 :::443                      :::*                       LISTEN      2417/httpd        
tcp        0      0 :::80                       :::*                        LISTEN      2417/httpd        
[usuarioxxxxxx@desa11 ~]$ ps -Af | grep 2417
root      2417     1  0 10:33 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2419  2417  0 10:33 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2420  2417  0 10:33 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2421  2417  0 10:33 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2422  2417  0 10:33 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2423  2417  0 10:33 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2500  2417  0 10:48 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2501  2417  0 10:48 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2502  2417  0 10:48 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2503  2417  0 10:48 ?        00:00:00 /opt/apache/bin/httpd -k start
apache    2529  2417  0 10:52 ?        00:00:00 /opt/apache/bin/httpd -k start

Resulta que tenemos 'N threads' de Apache corriendo. Tiene pinta de que el proceso padre PID=2417 hace un fork de sí mismo por cada peticion recibida, creánmdose un proceso hijo (PIDs=2419...2529). Esto lo he verificado levantando un navegador y atacando uno de los portales del server.

Antes por lo que fuese, el proceso padre (1662) estaba tostado y no se dejaba parar del modo habitual, ahora el proceso padre (2417) lo paro sin problemas con:

   sudo /etc/init.d/apache/start
   sudo /etc/init.d/apache/stop

Como siempre usamos estos scripts no habíamos visto nunca que Apache se para/arranca/etc (a pelo) con la opción '-k':

   /opt/apache/bin/httpd -k start|stop| ...

REFERENCIAS

http://httpd.apache.org/docs/current/programs/httpd.html

martes, 18 de octubre de 2016

Ionic 2. Instalación, ajuste y subida de una aplicación híbria al movil en 5 min

Introducción

En unos pocos minutos podemos tener listo nuestro entorno de desarrollo Ionic 2 (sin plataforma), montar una aplicación de ejemplo y visualizarla en nuestro dispositivo movil.

Los pasos a seguir son los siguientes:

Instalación y configuración

1) Descargamos Node.js desde https://nodejs.org/es/, para Linux debemos bajar 'node-v4.6.0-linux-x64.tar.xz' y lo llevamos a nuestra carpeta $HOME

2) Lo descomprimimos mediante el comando:

tar -xvf node-v4.6.0-linux-x64.tar.xz

3) Lo renombramos a algo mas intuitivo como 'node-v4.6.0'

mv node-v4.6.0-linux-x64 node-v4.6.0

4) Ajustamos el fichero $HOME/.profile añadiendo:

#programacion moviles
export NODE_HOME=/home/miusuario/node-v4.6.0
export PATH="$NODE_HOME/bin:$PATH"

5) Recargamos las variables de configuración relanzando el fichero .profile con el comando:

$ . ./.profile

Aclaración. Este ajuste vale para la terminal actual, lo que nos permite seguir trabajando, si
queremos que el cambio sea permanente lo mejor es reiniciar sesión.

6) Verificamos que se ha instalado correctamente y que las variables de entorno están bien definidas con:

$ node -v
$ npm -v

7) Ajustamos la configuración de NPM para que use el proxy del INE mediante el comando:

$ npm config set proxy "http://usuario:password@ip-server-proxy:puerto-proxy/"

8) Verificamos que la configuración de NPM ha quedado bien ajustada con el comando:

npm config list

9) Instalamos Apache Cordova

$ npm install -g cordova

10) Verificamos que la instalación haya sido correcta:

$ cordova -v

11) Instalamos Ionic

$ npm install -g ionic

12) Verificamos que la instalación haya sido correcta:

$ ionic -v

Aclaración. Si se desea una versión expecifica de los mismos se pude poner por ejemplo:

$ npm install -g cordova@5.0.0
$ npm install -g ionic@1.5.0

13) Instalación de Git. Software de control de versiones

$ sudo apt-get install git
$ git --version

14) Instalación de Bower. Administrador de los paquetes (dependencias) de nuestro proyecto.

$ npm install -g bower
$ bower -v

15) Instalación de Gulp. Herramienta que permite automatizar tareas comunes de desarrollo.

$ npm install -g gulp
$ gulp -v

Creación de un aplicativo y visualización en el dispositivo

El entorno ya estaría listo, ahora vamos a generar una app de ejemplo y subirla a nuestro movil, para ello deberemos crear una cuenta en https://apps.ionic.io/login (con una dirección de correo y una contraseña que usaremos mas tarde).

1) Ionic nos ofrece toda una serie de plantillas para generar el esqueleto inicial de nuestros proyectos, podemos conocer la lista completa mediante el comando:

$ ionic start --list

2) Nos colocamos en la carpeta $HOME y creamos el proyecto 'ejemplo1' usando la plantilla tabs

$ cd $HOME
$ ionic start -a "ejemplo1" -i es.ine.sgtic.ejemplo1 ejemplo1 tabs --v2

  -a ejemplo1              -> Nombre del proyecto (aplicación)
  -i es.casa.ejemplo1      -> Nombre del dominio/id de la aplicación
  ejemplo1                 -> Nombre del directorio donde generará el proyecto
  blank                    -> Nombre de la plantilla que hemos usado
  --v2                     -> Parámetro con el que indicamos que queremos usar Ionic 2 (lo que implica: Angular 2, Typescript, etc).

Aclaración. Ionic 2 se basa en Angular 2, el cual trabaja por defecto con Typescript (aunque podría hacerlo con Javascript clásico ES5). Los Proyectos con Ionic 2 tienen una estructura interna completamente diferente. Con Angular 1 la estructura sería:

    |-www/
    |
    |--js/
    |--|-app.js
    |--|-HomeCtrl.js
    |--|-DetailCtrl.js
    |
    |--templates/
    |--|-Home.html
    |--|-Detail.html
    |
    |-index.html

Mientras con Angular 2 la estructura es:

    |-www/
    |
    |--Home/
    |--|-HomeCtrl.js
    |--|-Home.html
    |
    |--Detail/
    |--|-DetailCtrl.js
    |--|-Detail.html
    |
    |-index.html
    |-app.js

3) Subimos el app a nuestro movil (donde habremos instalado el app Ionic View):

$ cd ejemplo1
$ ionic upload  (Nos pedirá la cuenta de correo y la password de nuestra cuenta en Ionic)

4) Con ésto ya tendríamos la aplicación disponible en Ionic View para verla funcionar en el dispositivo movil. Arrancaríamos Ionic View en el movil y en el área 'My apps' podremos ver la aplicación 'ejemplo1' que podremos descargar y ejecutar ejecutar pulsando 'View app'.

Trabajo en local

Ya hemos visto como generar y subir al dispositivo una aplicación de ejemplo, ahora vamos a ver cómo probarla/modificarla en el entorno de desarrollo para posteriormente volver a subirla:

1) Nos movemos a la carpeta $HOME/ejemplo1

$ cd $HOME/ejemplo1

2) Levantamos el servidor embebido en Ionic con:

$ ionic serve --address localhost --port 8100

Aclaración. El comando podría no tener parámetros. Habitualmente funciona sin problemas con 'ionic serve', pero con las opciones indicadas podemos configurar tanto la dirección IP como el puerto por el que se despacharán peticiones. Estos ajustes son esenciales en máquinas con varias IP y/o otros servidores (Apache, Tomcat, etc) levantados.

3) Probamos la app en local en la URL: http://localhost:8100

Se nos abrirá el navegador con la siguiente URL: http://localhost:8100/

4) Podemos acceder a cualquiera de las páginas del aplicativo para ajustar tanto la página en sí (fichero *.html) como el controlador (fichero *.ts), tras efectuar el cambio simplemente refrescamos la página en nuestro navegador.

5) Al depurar con Chrome hemos visto que no podemos acceder a los scripts en la pestañan Sources, dado que no se ve la carpeta /top/localhost:8100/src/app/, para apañar este bug hemos tenido que lanzar el comando:

$ npm install @ionic/app-scripts@latest

Referencias:

http://ionicframework.com/docs/v2/

Recarga del .profile (bash) en Linux

En casi todas las distribuciones de Linux actuales tenemos Bash como intérprete de comandos.

El intérprete de comandos se cargará cuando iniciamos una sesión, por ello es importante conocer donde y cómo ajustar las variables de entorno de forma correcta.

Bash puede configurarse en varios ficheros, que sabemos que será ejecutados al iniciar la sesión en el siguiente orden (si es que existen):



/etc/profile
$HOME/.bash_profile
$HOME/.bash_login
$HOME/.profile

Si necesitamos ajustar varibles de entorno únicamente para las sesiones de nuestro usuario, típicamente $PATH, $JAVA_HOME, $M2_HOME, etc, lo mejor es ajustar el fichero .profile que encontraremos en $HOME.

Una vez ajustadas las variables tendríamos que reiniciar la sesión para que dichos ajustes sean recargados, pero hay una forma más rápida de  recarga, consistente en ejecutar el comando:

$ . ./.profile

Tras ésto podríamos ver que los cambios en nuestra variable XXX ya están cargados lanzando el comando

$ echo $XXX

Un saludo


lunes, 17 de octubre de 2016

Instalación y configuración GetSimple CMS

Los pasos a dar para la intalación del gestor de contenido getsimplecms son:

1) Instalar los siguientes paquetes:

   sudo apt-get install apache2
   sudo apt-get install php
   sudo apt-get install libapache2-mod-php
   sudo apt-get install php-xml
   sudo apt-get install sendmail

2) Descargamos getsimplecms de la página: http://get-simple.info/

3) Descargamos el soporte español de la página: http://get-simple.info/extend/all_languages.php

4) Descomprimimos 'GetSimpleCMS-3.3.12.zip' en /var/www/html/ el contenido del zip en esta carpeta, deben por tanto quedar aquí las carpetas admin, backup, data, ...

5) Descomprimimos 'spanish-language-for-getsimple.zip' en /var/www/html/admin/lang el contenido del zip en esta carpeta, deben por tanto quedar aquí los ficheros en_US.php y es_ES.php

6) Ajustamos los permisos de acceso a /var/www por parte de apache/php
   sudo chmod -R 777 /var/www

7) Ajustamos la visibilidad del apache en el fichero /etc/apache2/apache2.conf

<Directory /var/www/>
  Options Indexes FollowSymLinks
  AllowOverride All
  Require all granted
  Allow from all
</Directory>

8) Ajustamos el tamaño máximo de los ficheros a subir editando /etc/php/7.0/apache2/php.ini

   upload_max_filesiza=10M

8) Paramos el apache y lo arrancamos:

  /etc/init.d/apache2 stop
  /etc/init.d/apache2 start

9) Accedemos a http://localhost/admin y seguimos el asistente:

  - Ajustamos el idioma al Español
  - Ajustamos la cuenta del administrador: admin/password

10) Acceso usuarios: http://localhost

11) Acceso administradores: http://localhost/admin

viernes, 30 de septiembre de 2016

Añadir nuevos lanzadores en Gnome Shell

Cuando queremos añadir neuvos lanzadores en la barra de aplicaciones preferidas de Gnome Shell podemos encontrarnos con que la aplicación que nos interesa no aparece en la lista de aplicaciones 'registradas'.

Para disponer de dicha aplicación en el buscador y poder por tanto arrastrar su icono a la barra de aplicaciones preferidas deberemos generar un fichero *.desktop en cualquiera de los siguientes directorios:

/usr/share/applications --> Todos los usuarios del sistema tendrían el lanzador

/.local/share/applications --> Sólo lo tendríamos nosotros

Si por ejemplo acabamos de descaergar e instalar el Apache Directory Studio generaremos el fichero:

[Desktop Entry]
Name=apache-directory-studio
Exec=/home/egdepedro/ApacheDirectoryStudio/ApacheDirectoryStudio
Type=Application
Icon=/home/egdepedro/ApacheDirectoryStudio/icon.xpm

Ya sólo nos queda resetear Gnome Shell para que encuentre el nuevo lanzador y lo ofrezca en el buscador de aplciaciones. Para ello pulsaremos ALT+F2, tecleamos el comando r y pulsamos ENTER

Un saludo

miércoles, 18 de mayo de 2016

Crear accesos directos en Gnome shell

Para crear el acceso directo a una nueva aplicación que no tenía lanzador, en mi caso el sqldeveloper de Oracle hemos de realizar los siguientes pasos:

1) Averiguar donde está el lanzador de la herramienta:

$whereis sqldeveloper
sqldeveloper: /usr/local/bin/sqldeveloper

2) Nos movemos a la carpeta $HOME/.local/share/applications

3) Creamos un fichero lanzador llamado sqldeveloper.desktop con el contenido:

[Desktop Entry]
Encoding=UTF-8
Name=sqldeveloper
Exec=/usr/local/bin/sqldeveloper
Icon=/opt/sqldeveloper/icon.png
Type=Application
Categories=Development;

4) Hacemos que Gnome cargue el nuevo lanzador para todos los usuarios:

desktop-file-install sqldeveloper.desktop

Y eso es todo



miércoles, 23 de marzo de 2016

Creación de mi primera app con Ionic

Introducción

Vamos a ver los pasos necesarios para crear unas aplicaciones de ejemplo con Ionic en base a las plantillas que se ofrece.

1) Creamos una carpeta 'apps-ionic' y nos movemos a ella.

2) Lanzamos el siguiente comando para crear un app llamado ejemplo1:

   ionic start -a "ejemplo1" -i es.ine.sgtic.ejemplo1 ejemplo1 blank

    -a ejemplo1              -> Nombre de la aplicación
    -i es.ine.sgtic.ejemplo1 -> Nombre del dominio/id de la aplicación
    ejemplo1                 -> Nombre del directorio donde generará el proyecto
    blank                    -> Nombre de la plantilla que hemos usado

   Nota. En mi caso durante la creación del proyecto base ha saltado el error:

     Error Initializing app: Error: connect ETIMEDOUT 192.30.252.131:443

Googleando el problema he visto que hay que definir una variable PROXY a nivel de sistema dado que Ionic no usa la config del proxy definida en NPM. Por tanto creamos una variable de entorno llamada PROXY y le metemos el valor http://usuario:password@xxx.xxx.xxx.xxx:pppp

Tras el ajuste he tenido que reabrir la ventana de comandos para que pille el cambio y ejecute el comando con normalidad. La traza obtenida ha sido:

C:\Users\IEUser\Desktop\apps-ionic>ionic start -a "ejemplo1" -i es.ine.sgtic.ejemplo1 ejemplo1 blank
Creating Ionic app in folder C:\Users\IEUser\Desktop\apps-ionic\ejemplo1 based on blank project
    Downloading: https://github.com/driftyco/ionic-app-base/archive/master.zip
    [=============================]  100%  0.0s
    Downloading: https://github.com/driftyco/ionic-starter-blank/archive/master.zip
    [=============================]  100%  0.0s
    Updated the hooks directory to have execute permissions
    Update Config.xml
    Initializing cordova project

    Your Ionic project is ready to go! Some quick tips:

     * cd into your project: $ cd ejemplo1

     * Setup this project to use Sass: ionic setup sass

     * Develop in the browser with live reload: ionic serve

     * Add a platform (ios or Android): ionic platform add ios [android]
       Note: iOS development requires OS X currently
       See the Android Platform Guide for full Android installation instructions:
       https://cordova.apache.org/docs/en/edge/guide_platforms_android_index.md.html

     * Build your app: ionic build <PLATFORM>

     * Simulate your app: ionic emulate <PLATFORM>

     * Run your app on a device: ionic run <PLATFORM>

     * Package an app using Ionic package service: ionic package <MODE> <PLATFORM>

    For more help use ionic --help or ionic docs

    Visit the Ionic docs: http://ionicframework.com/docs


    New! Add push notifications to your Ionic app with Ionic Push (alpha)!
    https://apps.ionic.io/signup
    +---------------------------------------------------------+
    + New Ionic Updates for March 2016
    +
    + The View App just landed. Preview your apps on any device
    + http://view.ionic.io
    +
    + Invite anyone to preview and test your app
    + ionic share EMAIL
    +
    + Generate splash screens and icons with ionic resource
    + http://ionicframework.com/blog/automating-icons-and-splash-screens/
    +
    +---------------------------------------------------------+


3) Ya tendríamos montado el proyecto con su esqueleto, para ver funcionando el app deberemos seguir los pasos que nos indica la propia traza de Ionic:

   cd ejemplo1         --> Nos movemos a la carpeta de la app
   ionic setup sass  --> Ajustamos hojas de estilo
   ionic serve           --> Arranca un server http por el puerto 8100 para pruebas

4) Ahora podemos repetir los pasos anteriores con otras plantillas de ejemplo, hay algunas mas. Para sacar la lista completa de plantillas de ionic deberemos ejecutar el comando

   ionic start --list
 
Nota. No debemos olvidar colocarnos en el directorio padre 'apps-ionic' para tener los distintos ejemplos
organizados en una misma carpeta:

   ionic start -a "ejemplo2" -i es.ine.sgtic.ejemplo2 ejemplo2 tabs
   ionic start -a "ejemplo3" -i es.ine.sgtic.ejemplo3 ejemplo3 sidemenu
   ionic start -a "ejemplo4" -i es.ine.sgtic.ejemplo4 ejemplo4 complex-list
   ionic start -a "ejemplo5" -i es.ine.sgtic.ejemplo5 ejemplo5 maps
   ionic start -a "ejemplo6" -i es.ine.sgtic.ejemplo6 ejemplo6 salesforce


Nota. En el ejemplo4 (complex-list) se ve lo mismo que en el ejemplo1 ¿?

En la siguiente entrada nos dedicaremos a 'destripar' el contenido de la estructura de carpetas generada para cada proyecto, veremos cómo visualizar la aplicación directamente en el emulador de un dispositivo con Android y empaquetaremos el app para instalarlo en un app real.

Un saludo

Instalación del entorno de desarrollo Ionic

Introducción

Vamos a resumir los pasos necesarios para instalar el entorno de desarrollo de aplicaciones híbridas basadas en Ionic en una virtualización con Windows-7 e IExplorer 11.

Instalaremos las herramientas: Node.js, Git, Bower, Gulp, Cordova, Ioni, JDK y Android Studio.

Proceso de instalación. Parte I

1) Instalamos node.js bajando el instalador de la página web oficial (https://nodejs.org/en/) y ejecutándolo. La página detecta el sistema operativo que tengamos instalado y nos ofrecerá el instalador adecuado.

2) Al instalar Node.js se nos habrá descargado también el gestor de paquetes NPM de Node.js. Verificamos que ambos componentes están correctamente instalados lanzando los comandos:

   node -v   --> v4.4.0
   npm -v    --> 2.14.20

Nota. Si usamos NPM detrás de un proxy deberemos configurarlo mediante el comando:

   npm config set proxy http://usuario@password@xxx.xxx.xxx.xxx:pppp

Con este comando se nos creará el fichero .npmrc en el home del usuario con la config del proxy

3) Seguidamente instalamos GIT (un sistema de control de versiones) que vamos a necesitar después para Bower y para Ionic-cli. Instalamos GIT bajando el instalador de la página oficial (https://git-scm.com/downloads) y ejecutándolo. La página detecta el sistema operativo que tengamos instalado y nos ofrecerá el instalador adecuado.

4) Añadimos la ruta de instalación de la carpeta 'c:\Program Files\Git\cmd' (carpeta donde se ha instalado por defecto GIT) a la variabla de entorno PATH de nuestro sistema.

5) Verificamos que el componente está correctamente instalado lanzando el comando:

   git --version  --> git version 2.7.4.windows.1

6) Seguidamente instalaremos Bower, que es la herramienta que usaremos como administrador de las dependencias de librerías de nuestra aplicación. Para instalarlo usaremos NPM, el comando será:

   npm install bower -g

Nota. La opción 'g' indica al instalador que queremos una instalación global para poder acceder a bower desde cualquier directorio.

7) Verificamos que el componente está correctamente instalado lanzando el comando:

   bower -v   --> 1.7.7

8) Lo siguiente es instalar GULP, un sistema de construcción que automatiza tareas habituales del entorno de desarrollo: minificación de código JavaScript, compresión de imágenes, validación de sintaxis, etc. Para instalarlo usaremos NPM, el comando será:

   npm install gulp -g

9) Verificamos que el componente está correctamente instalado lanzando el comando:

   gulp -v   --> [06:31:50] CLI version 3.9.1

10) Por último instalamos Ionic y Apache Cordova desde el NPM. En este caso vamos a ser estrictos con la versión, para tener definidos las versiones que nos interesan. Para instalarlos usaremos NPM y los comandos serán:

   npm install cordova -g
   npm install ionic -g

Nota. Si necesitamos una versión concreta de cualquiera de los componentes que instalamos con npm podemos usar comandos de tipo:

     npm install cordova@5.0.0 -g

Durante la instalación se mostrarán warnings en caso de conflictos con las versiones.

11) Verificamos que ambos componentes están correctamente instalados lanzando los comandos:

   cordova -v       --> 6.0.0
   ioniccordova -v  --> 1.7.14

Con esto ya tendríamos instalado todo lo necesario para desarrollar aplicaciones con Ionic.

Las aplicaciones construidas se podrían probar directamente sobre el movil o la tablet elegida, pero si queremos trabajar con varios SSOO de varias versiones, siempre es mas cómodo instalar en el PC emuladores de dichas plataformas: Android Studio, Visual Studio, etc, que nos permitirán probar nuestra app en emulaciones de diferentes dispositivos físicos y/o con distintas versiones del SO objetivo (Android, IOs, Windows Phone, etc).

Proceso de instalación. Parte II

Para poder probar en distintos emuladores de dispositivos con diferentes versiones de los sistemas operativos necesitaremos instalar el JDK y un  emulador de la plataforma, en nuestro caso vamos a realizar las pruebas con Android, por lo que instalaremos el SDK de Android (Android Studio) y el JDK de Java.

12) Descargamos el JDK (ojo no del JRE) desde la URL (http://www.oracle.com/technetwork/es/java/javase/downloads/index.html). Como en nuestro caso estamos trabajando en una imagen de Windows 7 de 32 bits, bajaremos el fichero 'jdk-8u73-windows-i586.exe'. Lanzaremos el instalador y punto.

13) Descargamos Android Studio desde la URL (http://developer.android.com/sdk/index.html). Tras la descarga lanzaremos el instalador 'android-studio-bundle-141.2456560-windows' y seguiremos el asistente definiendo la instalación por defecto.

Un Saludo

martes, 22 de marzo de 2016

Servicio web RESTful

Introducción a REST

REST (Representational State Transfer) es un tipo de arquitectura software en el que las interfaces entre sistemas usan directamente HTTP para obtener datos o indicar la ejecución de operaciones sobre los datos, en cualquier formato (XML, JSON, etc) sin las abstracciones adicionales de los protocolos basados en patrones de intercambio de mensajes, como por ejemplo SOAP.

Los sistemas que siguen los principios REST se llaman con frecuencia RESTful.

Para poder crear/consumir servicios web REST, tenemos el API JAX-RS (Java API for RESTful Web Services) en el JRE/JDK de java

Algunos conceptor básicos

1) Programar un servicio web RESTfull debemos cambiar el enfoque, los conceptos básicos de los servicios web tradicionales (RPC) basados en la invocación de métodos cambian completamente.Ya no invocamos métodos del tipo addEmpleado(emp), removeEmpleado(emp), updateEmpleado(emp), deleteEmpleado(emp),  ahora el 'recurso' empleado se expone en una URL (http://localhost:8080/rest-ws/rest/empleado) sobre la quepodemos realizar operaciones básicas de tipo create, read, update y delete invocando la URL mediante los métodos:POST, GET, PUT, DELETE de HTTP.

Algunos ejemplo:
  • Para dar de alta un empleado llamaríamos la siguiente URL por POST: 
http://localhost:8080/rest-ws/rest/empleado/33/juan/español/español
  • Para dar de baja un empleado llamaríamos la siguiente URL por DELETE:
http://localhost:8080/rest-ws/rest/empleado/1
  • Para recuperar un empleado llamaríamos la siguiente URL por GET:
http://localhost:8080/rest-ws/rest/empleado/1
  • Para actualizar un empleado llamaríamos la siguiente URL por PUT:
http://localhost:8080/rest-ws/rest/empleado/33/Juan/Español/Español
  • Para obtener la lista de empleados llamaríamos la siguiente URL por GET:
http://localhost:8080/rest-ws/rest/empleado/list
  • Para obtener el número de empleados llamaríamos la siguiente URL por GET:
http://localhost:8080/rest-ws/rest/empleado/count

2) Destacar que la misma URI puede tener varias finalidades, en los ejemplos anteriores usamos la misma URI para el alta de un empleado y para su actualización, sólo se diferencian en el método HTTP por el que se invoca y en este ejemplo en los parámetros
  
3) Destacar también que los parámetros se añaden en la URI del recurso, es en servicio web quien tiene mapeados cada uno de ellos
  
4) Destacar que se trabaja principalmente cadenas de texto, en envío de cualquier binario deberá ser codificado
  
5) En RESTfull existe el wadl, que es equivalente a los wsdl de los servicios web basados en XML.

7) Generar/Encontrar el wadl

Si usamos el framework Jersey, la implementación de referencia de JAX-RS, como en el presente ejemplo, podremos acceder al fichero wadl con la URL: http://localhost:8080/rest-ws/rest/application.wadl

8) Generar cliente Java desde wadl

Hay una herramienta llamada WALD que utiliza Jersey 1.x and JAX-RS 2.0 para generar un cliente Java de un determinado fichero *.wadl. Hay un plugin disponible para Maven.


Servicio web ejemplo


Fichero web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>es.ine.sgtic.rest</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>jersey-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>


Fichero EmpleadoRest.java


package es.ine.sgtic.rest;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import es.ine.sgtic.model.pojo.Empleado;
import es.ine.sgtic.model.pojo.Empleados;
import es.ine.sgtic.servicios.EmpleadoManager;

@Path("/empleado")
public class EmpleadoRest {
    
    private EmpleadoManager empMngr = new EmpleadoManager();
    
    @GET
    @Path("{id: [0-9]+}")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * http://localhost:8080/rest-ws/rest/empleado/{id}
     */
    public Empleado readEmpleado(@PathParam("id")String id) {
        
        return empMngr.readEmpleado(id);
    }
    
    @POST
    @Path("{id: [0-9]+}/{nombre}/{ape1}/{ape2}/{email}")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * http://localhost:8080/rest-ws/rest/empleado/{id}/{nombre}/{ape1}/{ape2}/{email}
     */
    public Empleado createEmpleado(@PathParam("id")String id, 
                                   @PathParam("nombre")String nombre, 
                                   @PathParam("ape1")String ape1, 
                                   @PathParam("ape2")String ape2,
                                   @PathParam("email")String email) {
        
        return empMngr.createEmpleado(id, nombre, ape1, ape2, email);
    }
    
    @PUT
    @Path("{id: [0-9]+}/{nombre}/{ape1}/{ape2}/{email}")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * http://localhost:8080/rest-ws/rest/empleado/{id}/{nombre}/{ape1}/{ape2}/{email}
     */
    public Empleado updateEmpleado(@PathParam("id")String id, 
                                   @PathParam("nombre")String nombre, 
                                   @PathParam("ape1")String ape1, 
                                   @PathParam("ape2")String ape2,
                                   @PathParam("email")String email) {
        
        return empMngr.updateEmpleado(id, nombre, ape1, ape2, email);
    }
    @DELETE
    @Path("{id: [0-9]+}")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * http://localhost:8080/rest-ws/rest/empleado/{id}
     */
    public Empleado deleteEmpleado(@PathParam("id")String id) {
        
        return empMngr.deleteEmpleado(id);
    }
    
    @GET
    @Path("/count")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * http://localhost:8080/rest-ws/rest/empleado/count
     */
    public String getCount() {
        
        return empMngr.getNumeroEmpleados();
    }
    
    @GET
    @Path("/list")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * http://localhost:8080/rest-ws/rest/empleado/list
     */
    public Empleados getEmpleados() {

        return empMngr.getListaEmpleados();
    }
    
    @GET
    @Path("/find")
    @Produces(MediaType.APPLICATION_JSON)
    /*
     * Debería responder a cualquiera de estas combinaciones
     * http://localhost:8080/rest-ws/rest/empleado/find?id=2
     * http://localhost:8080/rest-ws/rest/empleado/find?nombre=pepe
     * http://localhost:8080/rest-ws/rest/empleado/find?nombre=pepe&ape1=garcia
     */
    public Empleados findEmpleado(@QueryParam("id")String id, 
                                     @QueryParam("nombre")String nombre, 
                                  @QueryParam("ape1")String ape1, 
                                  @QueryParam("ape2")String ape2,
                                  @QueryParam("email")String email) {
        
        return empMngr.getListaEmpleadosCoincidentes(id, nombre, ape1, ape2, email);
    }
}


Fichero EmpleadoManager.java


package es.ine.sgtic.servicios;

import es.ine.sgtic.model.dao.EmpleadoDao;
import es.ine.sgtic.model.pojo.Empleado;
import es.ine.sgtic.model.pojo.Empleados;

public class EmpleadoManager {

    private static final EmpleadoDao empDao = EmpleadoDao.getInstance();

    public Empleado createEmpleado(final String id, final String nombre, final String ape1, final String ape2, final String email){

        final Empleado emp = new Empleado();
        emp.setId(id);
        emp.setNombre(nombre);
        emp.setApe1(ape1);
        emp.setApe2(ape2);
        emp.setEmail(email);

        return empDao.createEmpleado(id, emp);
    }

    public Empleado readEmpleado(String id){

        return empDao.readEmpleado(id);
    }

    public Empleado updateEmpleado(final String id, final String nombre, final String ape1, final String ape2, final String email){

        final Empleado emp = new Empleado();
        emp.setId(id);
        emp.setNombre(nombre);
        emp.setApe1(ape1);
        emp.setApe2(ape2);
        emp.setEmail(email);

        return empDao.updateEmpleado(id, emp);
    }

    public Empleado deleteEmpleado(String id){

        return empDao.deleteEmpleado(id);
    }

    /**
     * Ojo aquí el truco está en devolver el objeto 'empleados' que contiene 
       internamente la lista de empleados, de otro modo estaríamos devolviendo
       una lista con N objetos empleado pero sin un 'nodo raiz', dos horas de 
       pelea para enterder el error que daba.
     * @return
     */
    public Empleados getListaEmpleados(){

        final Empleados empleados = new Empleados();
        empleados.getLista().addAll(empDao.getListaEmpleados().values());
        return empleados;
    }

    public Empleados getListaEmpleadosCoincidentes(final String id, final String nombre, final String ape1, final String ape2, final String email){

        final Empleados empleados = new Empleados();
        empleados.getLista().addAll(empDao.getListaEmpleadosCoincidentes(id, nombre, ape1, ape2, email).values());
        return empleados;
    }
    
    public String getNumeroEmpleados(){

        return empDao.getNumeroEmpleados();
    }
}


Fichero EmpleadoDao.java


package es.ine.sgtic.model.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;

import es.ine.sgtic.model.pojo.Empleado;

@XmlRootElement
/**
 * Clase de acceso a datos, la montamos para evitar tener que acceder a 
 * una BBDD de verdad. Al ser un Singleton hay una única instancia de la 
 * clase y tendremos persistencia entre llamada y llamada de la lista de
 * empleados.
 */
public class EmpleadoDao {

    private static EmpleadoDao instance = null;
    final Map<String, Empleado> empleados;

    public static EmpleadoDao getInstance() {
        if(instance == null) {
            instance = new EmpleadoDao();
        }
        return instance;
    }
    
    /**
     * Constructor donde metemos algunos empleados de prueba
     */
    protected EmpleadoDao(){
        
        empleados = new HashMap<String, Empleado>();
        Empleado emp = new Empleado();
        emp.setId("1");
        emp.setNombre("nombre1");
        emp.setApe1("ape11");
        emp.setApe2("ape12");
        emp.setEmail("nombre1.ape11@test.com");
        empleados.put("1", emp);
        emp = new Empleado();
        emp.setId("2");
        emp.setNombre("nombre2");
        emp.setApe1("ape21");
        emp.setApe2("ape22");
        emp.setEmail("nombre2.ape21@test.com");
        empleados.put("2", emp);
        emp = new Empleado();
        emp.setId("3");
        emp.setNombre("nombre3");
        emp.setApe1("ape31");
        emp.setApe2("ape32");
        emp.setEmail("nombre3.ape31@test.com");
        empleados.put("3", emp);
        emp = new Empleado();
        emp.setId("4");
        emp.setNombre("antonio");
        emp.setApe1("garcia");
        emp.setApe2("gonzalez");
        emp.setEmail("antonio.garcia.gonzalez@test.com");
        empleados.put("4", emp);
        emp = new Empleado();
        emp.setId("5");
        emp.setNombre("antonio");
        emp.setApe1("perez");
        emp.setApe2("gonzalez");
        emp.setEmail("antonio.perez.gonzalez@test.com");
        empleados.put("5", emp);
        emp = new Empleado();
        emp.setId("6");
        emp.setNombre("luis");
        emp.setApe1("garcia");
        emp.setApe2("ruiz");
        emp.setEmail("luis.garcia.ruiz@test.com");
        empleados.put("6", emp);
    }
    
    public Map<String, Empleado> getListaEmpleados(){

        return empleados;
    }

    /**
     * Devolvemos la lista de empleados coincidentes con TODOS los parámetros recibidos 
     * @return
     */
    public Map<String, Empleado> getListaEmpleadosCoincidentes(final String id, final String nombre, final String ape1, final String ape2, final String email){

        //Clonamos la lista original de emplados
        final List<Empleado> lstEmpleadosCoincidentes = new ArrayList<Empleado>();
        lstEmpleadosCoincidentes.addAll(empleados.values());
        
        //Vamos eliminando de la lista clonada aquellos que no coincidan con alguno de los criterios recibidos
        for (Iterator<Empleado> iterator = lstEmpleadosCoincidentes.iterator(); iterator.hasNext();) {
            Empleado emp = iterator.next();
            if (id!=null && !emp.getId().equals(id)) {
                iterator.remove();
            } else if (nombre!=null && !emp.getNombre().equals(nombre)) {
                iterator.remove();
            } else if (ape1!=null && !emp.getApe1().equals(ape1)) {
                iterator.remove();
            } else if (ape2!=null && !emp.getApe2().equals(ape2)) {
                iterator.remove();
            } else if (email!=null && !emp.getEmail().equals(email)) {
                iterator.remove();
            }
        }
        
        //Generamos el Map de empleados coincidentes
        Map<String, Empleado> mapEmpleadosCoincidentes = new HashMap<String, Empleado>();
        for (Empleado emp : lstEmpleadosCoincidentes) {
            mapEmpleadosCoincidentes.put(emp.getId(),emp);
        }
        
        //Devolvemos el Map de empleados coincidentes
        return mapEmpleadosCoincidentes;
    }
    
    public String getNumeroEmpleados(){
        //TODO: Parece que le molesta si devuelves un Integer...
        return Integer.toString(empleados.size());
    }
    
    public Empleado createEmpleado(final String id, final Empleado empleado){
        
        return empleados.put(id, empleado);
    }
    
    public Empleado readEmpleado(String id){
        
        return empleados.get(id);
    }
    
    public Empleado updateEmpleado(final String id, final Empleado empleado){
        
        final Empleado emp = empleados.get(id);
        emp.setNombre(empleado.getNombre());
        emp.setApe1(empleado.getApe1());
        emp.setApe2(empleado.getApe2());
        emp.setEmail(empleado.getEmail());
        return emp;
    }
    
    public Empleado deleteEmpleado(String id){
        
        return empleados.remove(id);
    }
}


Ejemplo de cliente java


Un pequeño ejemplo Java que hace uso del servicio web.

Clase AppCliente.java con el main:


package es.ine.sgtic;

import java.util.List;

import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;

import es.ine.sgtic.model.pojo.Empleado;
import es.ine.sgtic.model.pojo.Empleados;

public class AppCliente {

    private static ClientConfig clienteRestConfig;
    private static Client clienteRest;
    
    public static void main(String[] args) {

        //Instanciamos el cliente y lo configuramos para que acepte JSON 
        clienteRestConfig = new DefaultClientConfig();
        clienteRestConfig.getClasses().add(JacksonJaxbJsonProvider.class);
        clienteRestConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
        clienteRest = Client.create(clienteRestConfig);

        try {
           
            //Obtenemos el número de empleados
            obtenerNumeroEmpleados();
           
            //Obtenemos la lista de empleados
            obtenerListaEmpleados();
           
            //Realizamos diferentes búsquedas
            buscarEmpleadosCoincidentes(null,"antonio",null,null,null);
            buscarEmpleadosCoincidentes(null,"antonio",null,"gonzalez",null);
            buscarEmpleadosCoincidentes(null,null,"garcia",null,null);
            //TODO:  Como se peta porque la lista Empleados recibida sólo tiene un elemento
            //hemos tenido que ajustar en el server el parámetro de inicio siguiente:
            //<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            buscarEmpleadosCoincidentes(null,"antonio","garcia","gonzalez",null);
           
            //Recuperamos el empleado 1
            obtenerEmpleado("1");
           
            //Añadimos un empleado
            crearEmpleado("7","nombre7","ape71","ape72", "nombre7.ape71@test.com");
           
            //Obtenemos la lista de empleados por segunda vez
            obtenerListaEmpleados();
           
            //Actualizamos empleado
            actualizarEmpleado("7","nombre7","ape71","apeCAMBIADO", "nombre7.ape71@test.com");
           
            //Obtenemos la lista de empleados por segunda vez
            obtenerListaEmpleados();
           
            //Borramos un empleado
            borrarEmpleado("7");
           
            //Obtenemos la lista de empleados por segunda vez
            obtenerListaEmpleados();
           
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    private static void obtenerEmpleado(final String id) throws Exception {
       
        //Preparamos la URI
        final String EmpleadoURI = "http://localhost:8080/rest-ws/rest/empleado/1";
        
        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.get(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta
        if (respuesta.getStatus() == 200) {
            Empleado emp = respuesta.getEntity(Empleado.class);
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> obtenerEmpleado("+id+")");
            System.out.println(emp.getNombre() + " " + emp.getApe1() + " " + emp.getApe2() + " " + emp.getEmail());
            System.out.println("-----------------------------------------------------------");
        } else {
            throw new RuntimeException("HTTP Error: " + respuesta.getStatus());
        }
    }
    
    private static void obtenerNumeroEmpleados() throws Exception {
       
        //Preparamos la URI
        final String EmpleadoURI = "http://localhost:8080/rest-ws/rest/empleado/count";

        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.get(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta y la tratamos
        if (respuesta.getStatus() == 200) {
            String numEmp = respuesta.getEntity(String.class);
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> obtenerNumeroEmpleados()");
            System.out.println("Hay un total de " + numEmp + " empleados.");
            System.out.println("-----------------------------------------------------------");
        } else {
            throw new Exception("HTTP Error: " + respuesta.getStatus());
        }
    }
    
    private static void obtenerListaEmpleados() throws Exception {
       
        //Preparamos la URI
        final String EmpleadoURI = "http://localhost:8080/rest-ws/rest/empleado/list";

        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.get(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta y la tratamos
        if (respuesta.getStatus() == 200) {
            final Empleados empleados = respuesta.getEntity(Empleados.class);
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> obtenerListaEmpleados()");
            final List<Empleado> lstEmpleados = empleados.getLista();
            for (int c=0; c<empleados.getLista().size(); c++){
                System.out.println(lstEmpleados.get(c).getNombre() + " " + lstEmpleados.get(c).getApe1() + " " + lstEmpleados.get(c).getApe2()+ " " + lstEmpleados.get(c).getEmail() );
            }
            System.out.println("-----------------------------------------------------------");
           
        } else {
            throw new Exception("HTTP Error: " + respuesta.getStatus());
        }
    }
    
    private static void buscarEmpleadosCoincidentes(final String id, final String nombre, final String ape1, final String ape2, final String email) throws Exception {
       
        //Preparamos la URI
        final StringBuilder sbCriterios = new StringBuilder();
        if (id!=null){
            sbCriterios.append("id="+id);
        }
        if (nombre!=null){
            if(sbCriterios.length()>0) {
                sbCriterios.append("&nombre="+nombre);
            } else {
                sbCriterios.append("nombre="+nombre);
            }
        }
        if (ape1!=null){
            if(sbCriterios.length()>0) {
                sbCriterios.append("&ape1="+ape1);
            } else {
                sbCriterios.append("ape1="+ape1);
            }
        }
        if (ape2!=null){
            if(sbCriterios.length()>0) {
                sbCriterios.append("&ape2="+ape2);
            } else {
                sbCriterios.append("ape2="+ape2);
            }
        }
        if (email!=null){
            if(sbCriterios.length()>0) {
                sbCriterios.append("&email="+email);
            } else {
                sbCriterios.append("email="+email);
            }
        }

        System.out.println("http://localhost:8080/rest-ws/rest/empleado/find?"+sbCriterios.toString());
       
        final String EmpleadoURI = String.format("http://localhost:8080/rest-ws/rest/empleado/find?%s", sbCriterios.toString());

        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.get(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta y la tratamos
        if (respuesta.getStatus() == 200) {
            final Empleados empleados = respuesta.getEntity(Empleados.class);
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> buscarEmpleadosCoincidentes("+id+","+nombre+","+ape1+","+ape2+","+email+")");
            final List<Empleado> lstEmpleados = empleados.getLista();
            for (int c=0; c<empleados.getLista().size(); c++){
                System.out.println(lstEmpleados.get(c).getNombre() + " " + lstEmpleados.get(c).getApe1() + " " + lstEmpleados.get(c).getApe2()+ " " + lstEmpleados.get(c).getEmail() );
            }
            System.out.println("-----------------------------------------------------------");
           
        } else {
            throw new Exception("HTTP Error: " + respuesta.getStatus());
        }
    }
    
    private static void crearEmpleado(final String id, final String nombre, final String ape1, final String ape2, final String email) throws Exception {
       
        //Preparamos la URI
        final String EmpleadoURI = String.format("http://localhost:8080/rest-ws/rest/empleado/%s/%s/%s/%s/%s", id, nombre, ape1, ape2, email);
        
        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.post(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta
        if (respuesta.getStatus() == 200 || respuesta.getStatus() == 204) {
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> crearEmpleado("+id+","+nombre+","+ape1+","+ape2+","+email+") --> " + respuesta.getStatus());
            System.out.println("-----------------------------------------------------------");
        } else {
            throw new RuntimeException("HTTP Error: " + respuesta.getStatus());
        }
    }
    
    private static void actualizarEmpleado(final String id, final String nombre, final String ape1, final String ape2, final String email) throws Exception {
       
        //Preparamos la URI
        final String EmpleadoURI = String.format("http://localhost:8080/rest-ws/rest/empleado/%s/%s/%s/%s/%s", id, nombre, ape1, ape2, email);
        
        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.put(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta
        if (respuesta.getStatus() == 200 || respuesta.getStatus() == 204) {
            Empleado emp = respuesta.getEntity(Empleado.class);
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> actualizarEmpleado("+id+","+nombre+","+ape1+","+ape2+","+email+") --> " + respuesta.getStatus());
            System.out.println(emp.getNombre() + " " + emp.getApe1() + " " + emp.getApe2() + " " + emp.getEmail());
            System.out.println("-----------------------------------------------------------");
        } else {
            throw new RuntimeException("HTTP Error: " + respuesta.getStatus());
        }
    }
    
    private static void borrarEmpleado(final String id) throws Exception {
       
        //Preparamos la URI
        final String EmpleadoURI = String.format("http://localhost:8080/rest-ws/rest/empleado/%s", id);
        
        //Invocamos al WS obteniendo la respuesta
        final WebResource webResource = clienteRest.resource(EmpleadoURI);
        final ClientResponse respuesta = webResource.delete(ClientResponse.class);

        //Verificamos si tenemos una respuesta correcta
        if (respuesta.getStatus() == 200 || respuesta.getStatus() == 204) {
            Empleado emp = respuesta.getEntity(Empleado.class);
            System.out.println("-----------------------------------------------------------");
            System.out.println(">> borrarEmpleado("+id+") --> " + respuesta.getStatus());
            System.out.println("-----------------------------------------------------------");
        } else {
            throw new RuntimeException("HTTP Error: " + respuesta.getStatus());
        }
    }
}


Pojo empleados.java


package es.ine.sgtic.model.pojo;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
//JAX-RS mapea automáticamente las clases anotadas con JAXB a JSON
public class Empleados {

    List<Empleado> lista = new ArrayList<Empleado>();

    public List<Empleado> getLista() {
        return lista;
    }

    public void setLista(List<Empleado> lista) {
        this.lista = lista;
    }
} 


Pojo empleado.java


package es.ine.sgtic.model.pojo;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
//JAX-RS mapea automáticamente las clases anotadas con JAXB a JSON
public class Empleado {

    private String id;
    private String nombre;
    private String ape1;
    private String ape2;
    private String email;
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getNombre() {
        return nombre;
    }
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    public String getApe1() {
        return ape1;
    }
    public void setApe1(String ape1) {
        this.ape1 = ape1;
    }
    public String getApe2() {
        return ape2;
    }
    public void setApe2(String ape2) {
        this.ape2 = ape2;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
}