miércoles, mayo 25, 2011

Encriptacion utilizando el algoritmo RSA

En esta ocacion veremos un ejemplo de encriptacion sobre RSA, para entenderlo mejor ps date una leida del algoritmo

1.- Crearemos una clase RSAKeys
public class RSAKeys {
/**
RSAPrivateKey esta en java.security.interfaces
*/
 private RSAPrivateKey privateKey;
 private String exponente;
 private String modulus;
//sus metodos get y set
}
2.- Agregamos la libreria commons-codec
En mi caso utilizare commons-codec-1.5

3.- Creamos la clase RSAEncriptacion
public class RSAEncriptacion {
 
 private final String ALGORITMO="RSA";
 private final String CODIFICACION = "UTF-8";
 private final Integer LONGITUD=512;
 private final Integer BASE = 16;

}
4.- Creamos el metodo generarKeys
/**
Obtenemos una instancia de un keyPairGenerator (GENERADOR DE PAR DE LLAVES)
basado en el algoritmo RSA 

Inicializamos al generador de llaves, dandole el tamaño de la llave

En una clase KeyPair guardamos las llaves generadas

En una llave publica y privada guardamos los resultados del keyPair

En nuestra clase RSAKeys guardamos la llave privada, y el modulo y exponente
de la llave publica, esto para generarla despues. 

Esto lo puedes hacer con la llave privada
*/
public RSAKeys generarKeys() throws Exception{
 KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITMO);
 kpg.initialize(LONGITUD);
 KeyPair kp = kpg.genKeyPair();
 RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
 RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();  
 RSAKeys keys = new RSAKeys();
 keys.setExponente(publicKey.getPublicExponent().toString(BASE));
 keys.setModulus(publicKey.getModulus().toString(BASE));
 keys.setPrivateKey(privateKey);  
 return keys;
}
5.- Realizamos el metodo obtenPublicKey
/**
Delego las exepciones a una capa superior

Realizo una instancia de un keyFactory en base al algorimo

Creo una RSAPublicKeySpec (es diferente de un RSAPublicKey) en base al modulo 
y al exponente

Obtengo una llave publica a partir de mi fabrica de keys dandole como parametro
mi publicKeySpec

Finalmente hago un cast de mi publicKey a un RSAPublicKey
*/
public RSAPublicKey obtenPublicKey(String modulo, String exponente) 
throws NoSuchAlgorithmException, InvalidKeySpecException
{
 KeyFactory fact = KeyFactory.getInstance(ALGORITMO);
 RSAPublicKeySpec publicKey = new RSAPublicKeySpec( new BigInteger(modulo 
, BASE) , new BigInteger (exponente , BASE) );
 PublicKey pubKey = fact.generatePublic(publicKey);
 return (RSAPublicKey) pubKey;
}
6.- Realizamos el metodo Encriptar
/**
Obtengo un RSAPublicKey a partir de mi metodo antes construido

Creo una instancia de cipher en base a mi algoritmo (Recuerda que este me ayuda a
encriptar y desencriptar)

Configuro mi cipher en modo encriptador y le paso como algoritmo mi llave publica

Cifro mi texto con el metodo doFinal de cipher

Ahora por medio de la clase Hex convierto a char mi arreglo de bytes, en ves 
de utilizar el metodo HexToString del post pasado (Mas facil nop  ¬¬ )

Finalmente convierto mi char[] a String
*/
public String encriptar(String texto, String modulo, String exponente) 
throws Exception {
 RSAPublicKey publicKey = obtenPublicKey(modulo, exponente);
 Cipher cipher = Cipher.getInstance(ALGORITMO);
 cipher.init(Cipher.ENCRYPT_MODE,  publicKey);
 byte[] cifrado = cipher.doFinal(texto.getBytes());
 char [] charEncriptado = Hex.encodeHex(cifrado);
 String textoEncriptado = new String(charEncriptado);
 return textoEncriptado;
}
7.- Realizamos el metodo Desencriptar
/**
Inicializamos una instancia de cipher ...

Lo configuramos en modo Decript

Obtenemos en arreglo de bytes desencriptado

Finalmente creamos un String en base a nuestro arreglo de bytes y la codificacion
*/
public String desencriptar(String decript, RSAPrivateKey privateKey) 
 throws Exception {
 Cipher cipher = Cipher.getInstance(ALGORITMO);
 cipher.init(Cipher.DECRYPT_MODE, privateKey);
 byte[] desencriptado= Hex.decodeHex(decript.toCharArray()); 
 byte[] desci = cipher.doFinal(desencriptado);
 return new String(desci, CODIFICACION);
}
8.- Nuestro Metodo Main
public static void main(String[] args) throws Exception {
 RSAEncriptacion encriptacion = new RSAEncriptacion();  
 RSAKeys keys = encriptacion.generarKeys(); 

 String textoOriginal = "Mi Texto Original";  
 String textoEncriptado = encriptacion.encriptar(textoOriginal , keys.getModulus() , keys.getExponente());
 String textoDesencriptado = encriptacion.desencriptar(textoEncriptado, keys.getPrivateKey());
  
 System.out.println("T Encriptado: " + textoEncriptado);
 System.out.println("T Desencriptado: " + textoDesencriptado);
}

martes, mayo 24, 2011

Encriptacion utilizando el algoritmo AES

En esta ocacion haremos un pequeño ejemplo de una clase que encripte y desencripte mediante el algoritmo AES (Advanced Encryption Standard)
  
1.- Creamos una clase AESKey
Esta clase solamente contendra una variable que sera nuestra key, que nos servira para encriptar y desencriptar


public class AESKey {
 private String encoded;

 public String getEncoded() {
  return encoded;
 }

 public void setEncoded(String encoded) {
  this.encoded = encoded;
 }
}

2.- Primero crearemos nuestra clase encriptacion

public class AESEncriptacion {
 //el resto del codigo ira aqui :)
}

3.- Crearemos nuestro metodo crearKey

private final String ALGORITMO = "AES";//algoritmo (si cambia la imprementacion tambien)
private final int LONGITUD = 128;//longitud de la llave ()
private final String CODIFICACION = "UTF-8";//como se convertira a byte, esto sera mas adelante

/**
Agrego un constructor que recibe como parametros un AESKey
*/
public AESEncriptacion(AESKey aesKey) throws Exception {  
 this.aesKey = aesKey;
}

public AESKey generaKey() throws Exception {
 KeyGenerator kgen = KeyGenerator.getInstance(ALGORITMO);
 kgen.init(LONGITUD);
 SecretKey skey = kgen.generateKey(); 
 AESKey aesKey = new AESKey();
 aesKey.setEncoded(StringtoHex(skey.getEncoded()));
 return aesKey;
}

4.- Crearemos nuestro metodo Encriptacion

/**
Estoy delegando las exepciones a el metodo que ocupe a encripta.

Convierto una cadena, q en este caso es mi key a un arreglo de bytes
el cual se lo asigno junto con el algoritmo a la clase SecretKeySpec.

Obtengo una instancia de Chiper en base al algoritmo, esta me ayudara
a encriptar y desencriptar mis codigos

A mi variable chiper la pongo en modo encripcion y le paso mi key secreta

Obtengo un arreglo de bytes que representa a mi cadena encriptada (resultado
de la encripcion)

Para que pueda guardar por ejemplo en bd mi cadena encriptada, paso el arreglo
de bytes a una cadena, esto lo hago con ayuda del metodo hexToString que veremos
mas adelante
*/
public String encripta(String cadena) 
  throws NoSuchAlgorithmException,
  NoSuchPaddingException, InvalidKeyException,
  IllegalBlockSizeException, BadPaddingException,
  UnsupportedEncodingException 
{  
  byte[] raw = StringToHex(aesKey.getEncoded());
  SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITMO);
  Cipher cipher = Cipher.getInstance(ALGORITMO);
  cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
  byte[] encrypted = cipher.doFinal(cadena.getBytes(CODIFICACION));
  String encriptado = HexToString(encrypted);
  return encriptado;
}

5.- Crearemos nuestro metodo HexToString

/**
Por medio de un for recorro cada posicion del arreglo de bytes

En la variable aux obtengo la posicion deseada, indicando que es un hexadecimal
por medio de 0xff

Como un hexadecimal lo representare con 2 poficiones ff, 0f, 12... etc
en caso de que el resultado fuera menor a 16 solo obtendria 1 posicion
lo que me daria problemas al momento de desencriptar

En caso de ser menor a 16, le concateno un 0 a mi cadena, luego simplemente 
concateno mi variable aux, indicando que la guardate en un formato String
que representara un hexadecimal > Integer.toHexString(aux)

Al Final retorno una cadena que representa a mi arreglo de bytes, de esta forma
podre guardar la informacion en una bd, o imprimirla de una forma entendible
*/
private String HexToString(byte[] arregloEncriptado) {
 String textoEncriptado = "";
 for (int i = 0; i < arregloEncriptado.length; i++) {
  int aux = arregloEncriptado[i] & 0xff;
  if (aux < 16) {
   textoEncriptado = textoEncriptado.concat("0");
  }
  textoEncriptado = textoEncriptado.concat(Integer.toHexString(aux));
 }
 return textoEncriptado;
}
 
6.- Crearemos nuestro metodo StringToHex
/**
Creo un arreglo de bytes, del tamaño de mi cadena / 2
(por eso el agregar un 0 si hera menor a 16 en el metodo de HexToString)

Por medio de un for recorro a mi arreglo

Dentro del fofr en la variable index guardare la posicion * 2, esto para siempre
tomar pares 2, 4, 6, etc...

En la variable aux guardo un subString de index a index+2 (0-2,2-4) siempre sera en pares, no hay fallas por que el metodo HexToString me retorno una cadena par

En una variable int, guardo el parseo del segmento de cadena, con base 16 (Hexadecimal)

Para finalizar en la posocion correspondiente de mi arreglo de bytes hago un cast
para guardar mi variable entera

Finalmente retornaria un arreglo de bytes identico al que recibio el metodo
HexToString, por tanto obtengo mi arreglo encriptado :)
*/
private byte[] StringToHex(String encriptado) {
 byte[] enBytes = new byte[encriptado.length() / 2];
 for (int i = 0; i < enBytes.length; i++) {
  int index = i * 2;
  String aux = encriptado.substring(index, index + 2);
  int v = Integer.parseInt(aux, 16);
  enBytes[i] = (byte) v;
 }
 return enBytes;
}
7.- Crearemos nuestro metodo Desencripcion
/**
Del mismo modo delego las excepciones a un nivel superior

Obtengo un arreglo de bytes a partir de mi key

Se lo asigno junto con el algoritmo a la clase SecretKeySpec.

Obtengo una instancia de Chiper en base al algoritmo

A mi variable chiper la pongo en modo desencripcion y le paso mi key secret

Obtengo un arreglo de bytes a partir de mi cadena encriptada

Al final creo un String a partir de mi arreglo de bytes

Esto no lo hice antes, como algunos ejemplos de la red, por que ? 
por que de este modo si lo hiciera desde el metodo encriptar, habria algunos
caracteres que no se podrian representar, por lo tanto se perderia
la integridad de la informacion.

Es mas pueden intentar realizar el programa sin utilizar HexToString y StringToHex
haciendo las conversiones del tipo new String(byte[]) y no trabajaria su codigo
*/
public String desencriptar(String encriptado) throws InvalidKeyException,
  IllegalBlockSizeException, BadPaddingException,
  UnsupportedEncodingException, NoSuchAlgorithmException,
  NoSuchPaddingException 
{
 byte[] raw = StringToHex(aesKey.getEncoded());
 SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITMO);
 Cipher cipher = Cipher.getInstance(ALGORITMO);  
 cipher.init(Cipher.DECRYPT_MODE, skeySpec);
 byte[] original = cipher.doFinal(StringToHex(encriptado));
 String originalString = new String(original);
 return originalString;
}
8.- Crearemos nuestro Implementacion
public static void main(String args[]) throws Exception {
 AESKey aesKey = new AESKey();
  
 AESEncriptacion tmp = new AESEncriptacion();
 aesKey = tmp.generaKey();
 
 AESEncriptacion ejemplo = new AESEncriptacion(aesKey);

 String encriptado = ejemplo.encripta("123456789012345678");
 String desencriptado = ejemplo.desencriptar(encriptado);

 System.out.println(encriptado);
 System.out.println(desencriptado);
 }



9.- Se me olvidava, te coloco los importes por si las moscas :)

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

NOTA: Al final De una Forma Ligeramente Simple podemos encriptar utilizando el Algoritmo AES, En posteriores post colocare otras formas de encripcion

Sin miedo ! ! !

martes, mayo 17, 2011

Lectura de Archivos, y Guardado En BD (SQLLOADER DE ORACLE)

En la entrada anterior vimos como leer un archivo por medio de flatworm, siendo una forma util de guardara informacion sobre una bd.

Oracle nos proporciona una herramienta que nos permite guardar la informacion de un archivo, directamente en las tablas que deceemos.

Algunas ventajas que tenemos son:
* No necesitamos tener a java como intermediario
* No necesitamos configurar nada en oracle, solo tener el cliente completo (tener en el cliente slq loader), o en su defecto ejecutar la aplicacion desde el server
* Automaticamente se crean los archivos de log
* Podemos indicar el numero maximo de errores antes de terminar una aplicacion


Desventajas
* Al igual que cuando modificamos una tabla, esta queda desabilitada hasta que termine de realizarse modificaciones sobre ella


1.- Para hacer uso de esta herramienta, primero crearemos un archivo de configuracion donde indicamos en que tablas guardaremos la informacion del archivo y lo guardaremos con una extension .ctl


load data
replace
into table PRODUCTOS(
EnExistencia                    POSITION(1:5)              char NULLIF EnExistencia=BLANKS
          "TRIM(:EnExistencia)"
Nombre                            POSITION(6:25)            char NULLIF Nombre=BLANKS
          "TRIM(:Nombre)"
Codigo                             POSITION(26:30)           char NULLIF Codigo=BLANKS
          "TRIM(:Codigo)"
FechaInventario                 POSITION(31:40)          char NULLIF FechaInventario=BLANKS
          "TRIM(:FechaInventario)"
Vendidas                          POSITION(26:30)          char NULLIF Vendidas=BLANKS
          "TRIM(:Vendidas)"
)


2.-Ahora Crearemos un archivo con extension .par, el cual contendra la ruta del archivo log, los errores maximos, tambien datos de conexion

USERID=usuarioBD/passwordBD
CONTROL=rutaArchivoCTL
LOG=archivoLog.log
ERRORS=100


3.- Ahora para ejecutarlo, solo abrimos una consola de sql, ya sea del lado del server o en el cliente (recuerda q tiene que tener instalado sql loader) y escribimos los siguiente


sqlldr PARFILE=rutaArchivoPar DATA=archivoALeer


***LISTO, SE EJECUTA LA APLICACION, SE GUARDAN LOS DATOS EN LA BD Y SE CREA EL ARCHIVO DE LOG


ESTA ES UNA FORMA MUY SENSILLA DE GUARDAR DATOS EN UNA BD, SIN PASAR POR JAVA, EL DETALLE ES TENER SQLLDR EN NUESTRO SERVER O EN EL CLIENTE

Lectura de Archivos Mediante FlatWorm

Vamos a Reutilizar el Framework FlatWorm para poder ahora leer datos de un archivo, siempre y cuando sabemos la longitud de cada campo

Para tal motivo realizaremos lo siguiente:


1.- creamos un proyecto y le agregamos las siguientes librerias:
            commons-beanutils.jar
            commons-collections-3.0.jar
            commons-logging.jar
            flatworm-2.0.1.jar
****Recuerda, no es obligatorio utilizar estas versiones de librerias, pero las pongo para que te sea mas facil seguir el ejemplo

2.- Creamos 4 paquetes
            com.archivos.bean
            com.archivos.configurador
            com.archivos.lectura
            com.archivos.main

3.- En el paquete bean, agregamos el bean que Productos que utilizamos en la entrada Creacion De Archivos III

4.- En el paquete Configurador, del mismo modo copiamos la clase DatosIniciales que tenemos en Creacion De Archivos III

5.- En el paquete lectura creamos una clase llamada LecturaArchivo agregando el siguiente codigo

            private DatosIniciales datos;
            private FileParser parser;
            private BufferedReader br;
            private FileFormat ff;
            //agregar sus metodos set y get


public void cerrar(){
try {
             if(br!=null){            
                          br.close();            
             }        
} catch (IOException e) {
             System.out.println("C 1");
}
} 

/*
Creamos un parceador para poder pasar de un xml a un formato de archivo
en un stream obtenemos el archivo a leer
el stream lo guardamos en un buffer para poder ser leido
*/
public void configurar(){
try {
             ConfigurationReader parserC = new ConfigurationReader();       
             ff = parserC.loadConfigurationFile(getDatos().getArchivoWarm());
             InputStream in;       
             in = new FileInputStream(
                          getDatos().getRutaSubidaArchivo()+getDatos().getNombreArchivo());
             br = new BufferedReader(new InputStreamReader(in));        
} catch (FileNotFoundException e) {
             System.out.println("Archivo no encontrado");
} catch (FlatwormUnsetFieldValueException e) {
             System.out.println("Error de Archivo Warm");
} catch (FlatwormConfigurationValueException e) {
             System.out.println("Error en la Configuracion del Archivo Warm");
}         
}


/*
Guardamos en un campoMach un record obtenido del buffer
preguntamos si el nombre del record obtenido es igual a nuestro beanWarm q tenemos mapeado en nuestro xml
si es asi obtenemos el bean Productos, obteniendolo del record . get bean (de un mismo record se pueden obtener multiples beans)
solo nos resta imprimir la informacion
*/
public void leer(){
 try {
             MatchedRecord results;       
             while ((results = ff.getNextRecord(br)) != null){
                          if (results.getRecordName().equals(getDatos().getRecordWarm())){
                          Productos prod = (Productos) results.getBean(getDatos().getBeanWarm());
                          System.out.println("******************");
                          System.out.println("Codigo: "+prod.getCodigo());
                          System.out.println("En Existencia: "+prod.getEnExistencia());

                          System.out.println("Fecha Inventario: "+prod.getFechaInventario());
                          System.out.println("Nombre: "+prod.getNombre());
                          System.out.println("Vendidas: "+prod.getVendidas());
                          }
             }
} catch (FlatwormInvalidRecordException e) {

              System.out.println("Record Invalido");
} catch (FlatwormInputLineLengthException e) {
              System.out.println("Longitud de la Linea invalida");
} catch (FlatwormConversionException e) {
              System.out.println("No se pudo realizar una conversion de datos");
} catch (FlatwormUnsetFieldValueException e) {
              System.out.println("Error de Campo Vacio");
} catch (FlatwormCreatorException e) {
              System.out.println("Error de Creacion del Bean");
} 

}

Como se puede observar, el leer puede ser un poco engorroso en comparacion con otras tecnicas como la tradicional de lectura de archivos, pero de este modo podemos modificar la longitud de los campos a leer sin recompilar codigo y es mas facil guardarlo en una bd, por ejemplo :)

En otra entrada veremos como realizar esto a traves de Oracle, sin pasar por java :)

 



lunes, mayo 16, 2011

Conexion a un SFTP

Ahora modificaremos el proyecto que realizamos en la entrada anterior.

Segimos utilizando la libreria jscape.

1.- Modificamos la interfaz agregando

           public Sftp getSftp();

2.- La anterior implementacion que teniamos nos pedira que la modifiquemos agregando el metodo, para hacerlo facil que solo retorne un null

3.- Creamos la siguiente implementacion que llamaremos SFTPImpl y sustituiremos nuesta variable ftp por una sftp

          private Sftp sftp;
          //agregamos sus metodos set y get

4.- El resto del codigo queda de la siguiente casi igual siendo el metodo desconectar en ves de que haga referencia a el ftp sera al sftp, el que cambia y por poco es el metodo conectar


          public boolean conectar() {
          try {
          SshParameters parametros = new 
SshParameters(
                    getDatosConexion().getDominio(), 
                    getDatosConexion().getUsuario(), 
                    getDatosConexion().getPassword()
          );
          sftp = new Sftp(parametros);
          sftp.connect();
          sftp.setBinary();
          System.out.println("Conectado al SFTP...");
          return true;
          } catch (SftpException e) {
          System.out.println("Error al conectar al SFTP");
         
          return false;
          }
         
          public boolean desconectar() {
          if(sftp!=null){
                     if(sftp.isConnected()){

                             sftp.disconnect();
                             return true;
                     }
          }
          return false;
          }


5.- En el metodo main solo hay que cambiar la referencia a la implementacion y listo ya podemos conectar a un servidor sftp


***una opcion para servidor sftp gratis puede ser: core FTP mini-sftp-server

Conexion a un FTP

Esta ocacion hablare un poco de como conectarnos a un ftp,sftp, y ftps

Empesaremos con la conexion a un ftp

1.- Crearemos un proyecto con 3 paquetes
          com.conexion.bean
          com.conexion.configuracion
          com.conexion.main


2.- Utilizaremos la libreria jscape.jar para realizar el uso del ftp, podremos utilziar otra libreria, pero utilizare esta por la facilidad de crear una conexion a un sftp


3.- En el paquete bean crearemos uno llamado DatosConexion con los siguientes datos
          dominio
          usuario
          password
           //sus metodos get y set

4.- En el paquete com.conexion.configuracion crearemos una Interfaz, q en mi caso llamare IConfiguracionConexion que tendra los siguientes metodos
          public boolean conectar();
          public boolean desconectar();
          public void setDatosConexion getDatosConexion();
          public DatosConexion getDatosConexion();
          public Ftp getFtp();
**Ftp proviene de la libreria jscape y en especifico com.jscape.inet.ftp.Ftp

5.- En el mismo paquete crearemos la prime implementacion, que llamare ConexionFTPImpl y contendra
          private DatosConexion datosConexion;
          private Ftp ftp;
          //sus metodos get y set

/*
creamos un nuevo ftp con los datos de conexion que tenemos
le agregamos un escuchador a nuestro ftp
intentamos establecer la conexion con el ftp
le indicamos que los datos que enviaremos o recibiremos seran tipop binario
recordemos que un ftp puede convertir la informacion a binario o a codigo ascii
para enviarla o recibirla
*/
          public boolean conectar(){
          try{
                    ftp = new Ftp(
                              getDatosConexion().getDominio() , 
                              getDatosConexion().getUsuario() , 
                              getDatosConexion().getPassword()
                              );
                     ftp.addFtpListener(this); 
                     ftp.connect();
                     ftp.setBinary();
                     System.out.println("Conectado al FTP..."); 
                     return true;

          }catch(FtpException e){
                    System.out.println("Error al conectar al ftp");
          }
          return false;

           

          public boolean desconectar(){
          if(ftp!=null){
                    if(ftp.isConnected()){
                              ftp.disconnect();
                              System.out.println("Desconectado del ftp...");

                    }
                    return true;
          }
          return false;
          }  

6.- En nuestro paquete main crearemos una clase Principal donde pondremos nuestro metodo main agregando nuestro codigo
          IConfiguracionConexion conexion = new ConexionFTPImpl();
          conexion.setDatosConexion(new DatosConexion());   
          conexion.getDatosConexion().setDominio("127.0.0.1");
          conexion.getDatosConexion().setPassword("java-limos-pass");
          conexion.getDatosConexion().setUsuario("java-limos");          conexion.conectar();          conexion.desconectar();


7.- Para el ejemplo utilize FileZilla Server, que es mi servidor ftp, dando un usuario "java-limos" y un pass "java-limos-pass"

lunes, mayo 09, 2011

Creacion De Archivos III (Archivos para Batch, con el Framework flatworm)

Esta es la tercera y ultima forma que posteare para poder crear archivos con informacion masiva

esta ocacion utilizaremos un framework llamafo "FLATWORM" el cual tiene la caracteristica de mapear en un archivo xml una o mas clases (de aqui en adelante Beans), en este archivo xml definiremos:

1.- Longitudes de campos
2.- Tipo de dato del campo
3.- Posicion del campo sobre el archivo
4.- Caracteres de relleno
5.- Formato a convertir un dato (conversion de fechas, alineacion de la informacion)


Iniciemos la construccion de archivos con flatworm


1. Crearemos un proyecto java, y le agregaremos 4 jars

     commons-beanutils.jar 
     commons-collections.jar (para mi caso utilizare la version 3.0) 
     commons-logging.jar
     flatworm.jar (para mi caso utiliare la version 2.0.1)  

*siendo los primeros 3 de apache 

2. procedamos a crear 4 paquetes

     com.archivos.bean
     com.archivos.configurador
     com.archivos.escritura
     com.archivos.main

3.- en el paquete com.archivos.bean crearemos un bean llamado Productos con las siguientes variables

     enExistencia           int
     nombre                   String
     codigo                    String
     fechaInventario        String
     vendidas                 int

con sus metodos get y set

4.- sobre escribiremos el metodo toString del bean quedando lo siguiente

public String toString(){
    return enExistencia + nombre + codigo + fechaInventario + vendidas;


5.- crearemos en el paquete com.archivos.configurador una clase llamada DatosIniciales que contendra las siguientes variables
     nombreArchivo
     rutaSubidaArchivo
     archivoWarm
     beanWarm
     recordWarm

6.- en el paquete com.archivos.escritura crearemos una clase llamada EscrituraArchivo quedando de la siguiente forma

    private FileCreator archivo; //clase encargada de crear y escribir sobre el archivo
    private DatosIniciales datos; 
 
     
     // Metodo que crea el archivo, el constructor de FileCreator recibe como primer parametro
     //la ruta y nombre del archivo xml y como segundo parametro el nombre y ruta del archivo
     //que se creara
     public void abrirArchivo(){
        try {
            archivo = new FileCreator(getDatos().getArchivoWarm(), getDatos().getRutaSubidaArchivo() + getDatos().getNombreArchivo());
            archivo.open();           
        } catch (FlatwormCreatorException e) {
            System.out.println("Error al crear el archivo");
        } catch (UnsupportedEncodingException e) {
            System.out.println("Codificacion del archivo no aceptada");
        }
    }
    

     //clase que se encargara de cerrar el archivo creado
    public void cerrarArchivo(){       
        try {
            if(archivo != null){
            archivo.close();
            }
        } catch (IOException e) {
            System.out.println("Error al cerrar el archivo");
        }
    }
    

     //clase encargada de escribir sobre el archivo, recibe como parametros el bean
     //el metodo setBean recibe como parametros el nombre de nuestro bean, pero como esta
     //mapeado en nuestro arhivo xml, como segundo parametro nuestro bean
     //le agregamos un recordSeparator para que haga saltos de linea(esto es por que bloc de      //notas no reconocera los saltos de linea, cualquier otro editor si)
     //el metodo write se encarga de escribir y recibe como parametros el recordWarm que
     //hemos mapeado en nuestro xml
    public void escribir(Productos bean){       
        try {
            archivo.setBean(getDatos().getBeanWarm(), bean);
            archivo.setRecordSeperator("\r\n");       
            archivo.write(getDatos().getRecordWarm());
        } catch (FlatwormCreatorException e) {
            System.out.println("Error en el FlatWormCreator");
        } catch (IOException e) {
            System.out.println("Error de IO");
        }
    } 



7.- en el paquete com.archivos.main crearemos un metodo ArmarArchivos
que quedara de la siguiente forma


    EscrituraArchivo escritura;
    DatosIniciales datos;
   
    public ArmarArchivos(){
        escritura = new EscrituraArchivo();
        datos = new DatosIniciales();
    }



    //este metodo hara las veces de una consulta a una bd, solo para el ejemplo :)
    public List<Productos> consultar(){
        List<Productos> productos = new ArrayList<Productos>();
        Productos producto1 = new Productos();
        Productos producto2 = new Productos();
        Productos producto3 = new Productos();
       
        producto1.setCodigo("aaxd");
        producto1.setEnExistencia(10);
        producto1.setFechaInventario("09/05/2011");
        producto1.setNombre("aspirina");
        producto1.setVendidas(5);
       
        producto2.setCodigo("zzsd");
        producto2.setEnExistencia(20);
        producto2.setFechaInventario("05/05/2011");
        producto2.setNombre("melox");
        producto2.setVendidas(1);
       
        producto3.setCodigo("xxxxxxxxxx");
        producto3.setEnExistencia(50);
        producto3.setFechaInventario("09/04/2011");
        producto3.setNombre("desodorante");
        producto3.setVendidas(10);
       
        productos.add(producto1);
        productos.add(producto2);
        productos.add(producto3);
       
        return productos;
    }



    public static void main(String[] args) {
        ArmarArchivos armar = new ArmarArchivos();       
        

        // asignacion de informacion a la clase datos
        System.out.println("Asignando Datos ...");       
        armar.datos.setArchivoWarm("D:\\ejemplosJavaLimos\\ProductosXML.xml");
        armar.datos.setBeanWarm("productosBean");//esta en el xml
        armar.datos.setNombreArchivo("EjemploFlatWorm.txt");
        armar.datos.setRecordWarm("productosBeanW");//esta en el xml
        armar.datos.setRutaSubidaArchivo("D:\\
ejemplosJavaLimos\\");
        armar.escritura.setDatos(armar.datos);
       
        List<Productos> resultados = armar.consultar();
 

        System.out.println("Armando Archivos ...");        
        armar.escritura.abrirArchivo();
        for(Productos producto : resultados){
            armar.escritura.escribir(producto);
        }
        armar.escritura.cerrarArchivo();

    }
     
 8.- hemos hablado de un bendito archivo xml, pues ahora lo presento, se llamara ProductosXML.xml 

< ?xml version="1.0" encoding="ISO-8859-1"? >
< file-format >
   
 < converter name="char" class="com.blackbear.flatworm.converters.CoreConverters" method="convertChar" return-type="java.lang.String"/ >
      < converter name="int" class="com.blackbear.flatworm.converters.CoreConverters" method="convertInteger" return-type="java.lang.Integer"/ >
     < record name="productosBeanW" >
         < record-ident >
            < length-ident minlength="45" maxlength="45" / >
        < /record-ident >
        < record-definition >
            < bean name="productosBean" class="com.archivos.bean.Productos"/ >
            < line >
                < record-element length="5" beanref="productosBean.enExistencia" type="int" >
                    < conversion-option name="justify" value="left"/ > 
                    < conversion-option name="pad-character" value=" " / > 
                < /record-element >
                < record-element length="20" beanref="productosBean.nombre" type="char" >
                    < conversion-option name="justify" value="right"/> 
                    < conversion-option name="pad-character" value="-" / >
                < /record-element >
                < record-element length="5" beanref="productosBean.codigo" type="char" >
                    < conversion-option name="justify" value="right"/ > 
                    < conversion-option name="pad-character" value="@" / >
                < /record-element>
                < record-element length="10" beanref="productosBean.fechaInventario" type="char" >
                    < conversion-option name="justify" value="right"/ > 
                    < conversion-option name="pad-character" value=" " / > 
                < /record-element >
                < record-element length="5" beanref="productosBean.vendidas" type="int" >
                    < conversion-option name="justify" value="right"/ > 
                    < conversion-option name="pad-character" value=" "  />
                < /record-element >               
            < /line >
        < /record-definition >

    < /record >
< /file-format > 


**DONDE: 

*Las Etiquetas converter, definiran el tipo de variable, en este caso recibire int y String, por lo tanto declarare una del tipo in y una del tipo char(no String)  

*Nuestro record se llamara productosBeanW, dentro de el podemos tener multiples beans, por eso la diferencia

*Dentro del bloque record-ident definimos la longitud maxima y minima de cada linea a escribir, esto mas que nada sirve a la hora de lectura, para diferenciar por ejemplo entre lineas de encabezado, pie de pagina, etc...

*Definimos nuestro bean, dandole nombre y la ruta dentro de nuestro proyecto

*Dentro del bloque linea, definimos nuestros elementos (variables del bean), dandole una longitud y un tipo de dato

*Entre el bloque record-element definimos las caracteristicas que tendra el campo como, el alineado, por ejemplo tenemos alineaciones a la derecha y a la izquierda, tambien podemos definir caracteres de relleno, es decir si el valor de un campo es menor a la longitud establecida, el resto se rellenara con los caracteres, tenemos 3 casos para el ejemplo, un espacion, un guion medio y un @

*Podemos definir valores por default formatear fechas entre otras opciones por medio de la etiqueta conversion-option



*Esto nos ayudara a no recompilar nuestro codigo, si las longitudes cambian, u otra cosa que no afecte al tipo de variable que se esta utilizando


*Al definir una longitud para cada campo, en caso de que sea rebasado por alguna variable, el valor de la variable sera cortado para adecuarse a la longitud


9.- Al final la salida de nuestro archivo queda:


10   ------------aspirina@aaxd09/05/2011    5
20   ---------------melox@zzsd05/05/2011    1
50   ---------desodorantexxxxx09/04/2011   10


si observas bien, el codigo del tercer producto fue cortado, la variable enExistencia se alineo a la izquierda y la variable vendidas se alineo a la derecha, la variable nombres se relleno con guines medios y la variable codigo con @

Perdon si describi poco, solo q estoy en el trabajo, pero cualquier cosa dejen sus comentarios y con gusto haremos algo al respecto