sábado, 30 de noviembre de 2013

Cómo escribir un archivo con C++

Para escribir un archivo con C solo tenemos que usar la función putc(caracter, fichero), la cual irá colocando cada caracter ingresado por consola en el archivo que le indiquemos. Esto se va a repetir hasta que ingresemos el indicador de fin de archivo (EOF).
#include "stdio.h"

void escribirArchivo(char*);

int main()
{
    char nombreArchivo[70];
    printf("Nombre del archivo: ");
    scanf("%s",nombreArchivo);
    escribirArchivo(nombreArchivo);
    return 0;
}

void escribirArchivo(char* nombreArchivo)
{
    FILE *pFichero;
    char caracter;
    pFichero = fopen(nombreArchivo, "wt");
    if(pFichero == NULL)
    {
        printf("No se puede abrir el archivo");
        return;
    }
    caracter = getchar();
    while(caracter != EOF)
    {
        putc(caracter, pFichero);
        caracter = getchar();
    }
    fclose(pFichero);
}
El resultado sería el siguiente:



Saludos y hasta una próxima oportunidad.

jueves, 28 de noviembre de 2013

Cómo leer un archivo con C++

Ahora es el turno a C/C++. Lo que hace este ejemplo es tomar cada caracter del archivo e ir mostrándolo por pantalla utilizando la función getc(), la cual devuelve -1 cuando detecta el fin del archivo. Para esto importamos la biblioteca stdio.h la cual ya define el final de un archivo con la constante EOF.

#include "stdio.h"

void leerArchivo(char*);

int main() {
    char nombreArchivo[70];
    printf("Nombre del archivo: ");
    scanf("%s",nombreArchivo);
    leerArchivo(nombreArchivo);
    return 0;
}

void leerArchivo(char* nombreArchivo) {
    FILE *pArchivo;
    char caracter;
    pArchivo = fopen(nombreArchivo, "rt");
    if(pArchivo==NULL) {
        printf("No se puede abrir el archivo.");
        return;
    }
    caracter = getc(pArchivo);
    while(caracter != EOF) {
        printf("%c", caracter);
        caracter = getc(pArchivo);
    }
    fclose(pArchivo);
}
Y al ejecutar el programa tendríamos lo que se muestra a continuación:


Espero que este sencillo ejemplo les sea de utilidad. Próximamente estaré publicando más artículos relacionados al manejo de archivos con C/C++.

lunes, 18 de noviembre de 2013

Transacciones con SQLite

Un saludo a todos los lectores de mi blog. En esta oportunidad quiero contarles acerca del manejo de transacciones en SQLite, pues aunque no lo parezca, este pequeño motor de base de datos sí cuenta con mecanismos para ejecutar los conocidos rollback y commit. Hace algún tiempo publiqué una entrada (que les recomiendo leer) en donde mostraba la manera de cómo trabajar con este motor sin embargo no comenté acerca del manejo de transacciones, tema que es de vital importancia pues aseguramos la atomicidad de nuestras operaciones sobre la base de datos y también reducimos el tiempo de ejecución de nuestras consultas.

Para poder entender mejor les voy a mostrar el siguiente ejemplo. Tenemos una pequeña aplicación la cual se encarga de grabar registros de productos en una base de datos. El precio de cada producto es generado por un método que para este caso le llamaremos doStuff y que recibe dos parámetros.
public static double doStuff(int precioTotal, int valor) {
    double precioUnitario = precioTotal / valor;
    return precioUnitario;
}
Como vemos, el método anterior es sensible a generar excepciones, por ejemplo la conocida ArithmeticException. Ahora supongamos que tenemos una lista de productos que queremos insertar en nuestra base de datos tal como se ve en la siguiente sección de código:
Producto producto1 = new Producto();
producto1.setNombre("Ariel");
producto1.setStock(50);
// --
Producto producto2 = new Producto();
producto2.setNombre("Pinol");
producto2.setStock(25);
// --
Producto producto3 = new Producto();
producto3.setNombre("Nivea");
producto3.setStock(50);

// --
Producto producto4 = new Producto();
producto4.setNombre("Colgate");
producto4.setStock(10);
// --
List<Producto> listaProductos = new ArrayList<Producto>();
listaProductos.add(producto1);
listaProductos.add(producto2);
listaProductos.add(producto3);
listaProductos.add(producto4);
Ahora vamos a recorrer nuestra lista de la siguiente manera:
for (Producto producto : listaProductos) {
    try {
        if (producto.getNombre().compareTo("Nivea") == 0) {
            // (1)Aquí vamos a generar un error a propósito
     producto.setPrecioUnitario(doStuff(0, 0));
 } else {
     producto.setPrecioUnitario(doStuff(1, 1));
 }
 //(2) Crear producto en la base de datos
    } catch (Exception e) {
        error = true;
    }
}
En el punto (2) vamos a crear el producto en la base de datos y no vamos a tener ningún problema hasta llegar al producto Nivea ya que en este objeto, al enviar como parámetros 0 y 0 se lanzará la excepción java.lang.ArithmeticException: / by zero la cual impedirá que se inserte en la base de datos. Sin embargo el problema real radica en el hecho de que ya hemos insertado antes dos productos y si vemos la inserción de los cuatro productos como una sola transacción, una operación atómica, entonces debemos eliminar los productos Ariel y Pinol de nuestra base de datos. Al no implementar un mecanismo de rollback y ejecutar el código anterior tendremos en nuestra base de datos lo siguiente:

Como vemos esto realmente no estaría bien pues si fueran datos que dependieran entre ellos, esto generaría datos inconsistentes en nuestra base de datos. Para esto es que implementamos el manejo de transacciones mediante una clase a la que llamaremos SqlProductoFactory y que se encargará de crear las conexiones, cerrarlas, comitearlas o hacer rollback de ser necesario.
package com.blogspot.rolandopalermo.sql;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import com.blogspot.rolandopalermo.bean.Producto;
import com.blogspot.rolandopalermo.util.ConexionJDBC;

public class SqlProductoFactory {

 private ConexionJDBC conn;
 private Connection connection;
 private Statement stmt;

 public void crearConexion() {
  System.out.println("Creamos la conexión");
  try {
   conn = new ConexionJDBC();
   connection = conn.getConnection();
   connection.setAutoCommit(false);
   stmt = connection.createStatement();
  } catch (SQLException e) {
   System.err.println("Error: " + e.getMessage());
  }
 }

 public void rollback() {
  System.err.println("Hacemos rollback");
  try {
   if (connection != null) {
    connection.rollback();
   }
  } catch (SQLException ex) {
   System.err.println("Error: " + ex.getMessage());
  }
 }

 public void commit() {
  System.out.println("Hacemos commit");
  try {
   if (connection != null && conn != null) {
    connection.commit();
    connection.setAutoCommit(true);
   }
  } catch (SQLException ex) {
   System.err.println("Error: " + ex.getMessage());
  }
 }

 public void close() {
  System.out.println("Cerramos la conexión");
  if (stmt != null) {
   try {
    stmt.close();
   } catch (Exception e) {
    System.err.println("Error: " + e.getMessage());
   }
  }
  if (conn != null) {
   try {
    conn.close();
   } catch (Exception e) {
    System.err.println("Error: " + e.getMessage());
   }
  }
 }

 public void crearProducto(Producto producto) throws SQLException {
  System.out.println("Creamos el producto");
  String sql = "INSERT INTO TB_PRODUCTOS (txtNombre, txtPrecioUnitario, txtStock) VALUES ('"
    + producto.getNombre()
    + "',"
    + producto.getPrecioUnitario()
    + "," + producto.getStock() + ");";
  stmt.execute(sql);
 }
}
Ahora utilizando nuestra clase manejadora de transacciones tendremos algo como esto:
for (Producto producto : listaProductos) {
    try {
        if (producto.getNombre().compareTo("Nivea") == 0) {
            // Aquí vamos a generar un error a propósito
            producto.setPrecioUnitario(doStuff(0, 0));
        } else {
            producto.setPrecioUnitario(doStuff(1, 1));
        }
        sql.crearProducto(producto);
    } catch (Exception e) {
        error = true;
        sql.rollback();
        break;
    }
}
if (!error) {
    sql.commit();
}
sql.close();
Y luego de volver a ejecutar nuestro método de inserción de nuestra lista de productos nuestra base de datos quedará así:


Si ocurre un problema al insertar un producto entonces no se insertará ninguno. De todos modos les adjunto el proyecto que ha sido hecho en Eclipse y no se olviden de colocar la base de datos en el disco D: para poder ejecutar la aplicación.
La base de datos pueden descargarla del siguiente enlace: base de datos. Un saludo a todos y hasta una próxima oportunidad.

jueves, 14 de noviembre de 2013

Solución al error ORA-06508

El día de hoy les traigo una solución para el error de código ORA-06508. Al parecer todo parte del hecho de no tener bien mapeadas las librerías y objetos que necesitamos para desplegar una forma ya sea mediante el Forms Builder o en un servidor Weblogic. La descripción es bastante clara: PL/SQL: could not find program unit being called. Y es por ahí por donde debemos empezar, indicándole a nuestro Weblogic de dónde debe obtener las librerías (*.pll). Bueno, vayamos al punto.


Los pasos que seguí fueron los siguientes:

1.- Verificar que todas las rutas del FORMS_PATH estén bien establecidas.


Del registro anterior debemos verificar que todas las rutas establecidas existan.


2.- Buscar todos los archivos con extensión ENV pues puede haber más de uno tal como se muestra en la siguiente imagen:


3.- Asegurarse que todos los archivos de extensión ENV contengan en el CLASSPATH las mismas rutas hacia las librerías requeridas para el despliegue de nuestras formas en un ambiente específico.


Adicionalmente al adjuntar librerías (*.pll) a nuestras formas, al aparecer el cuadro de diálogo donde no solicita remover la ruta, seleccionar la opción: No


Y si alguien encontró otra solución sería bueno que la compartan. Saludos.

miércoles, 13 de noviembre de 2013

Solución al error ORA-17079

Un saludo a todos, el día de hoy en la oficina nos tocó hacer frente a un problema no muy usual con el conocido IDE de Oracle, JDeveloper. Este entorno, a mi parecer no muy agradable, nos estaba presentando una excepción, la cual decía lo siguiente: Las credenciales del usuario no coinciden con las existentes o en inglés: User credentials doesn't match the existing ones. Esta excepción se encuentra asociada al error de Oracle ORA-17079 y en nuestro caso ocurrió cuando estabamos intentando conectarnos desde una aplicación web despegada en un Tomcat a un pool de conexiones de una base de datos Oracle11g.

Bueno pues, investigando un poco dimos con la solución la cual les comparto a continuación:


Hacemos clic en el botón Refresh Now.


Haciendo esto deberíamos tener nuestro problema resuelto. Si alguien ha encontrado otra manera de resolverlo, no duden en comentarlo. Un saludo a todos.

martes, 12 de noviembre de 2013

Contar repeticiones en una lista en Java

En este breve artículo les mostraré la manera de cómo contar la cantidad de ocurrencias de cada elemento de una lista Java utilizando las clases HashSetCollections, Set y List. Bueno, para ser directos vayamos a un ejemplo que ilustrará esto.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Quipu {

    public static void main(String... agrs) {
        List<String> list = new ArrayList<String>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("c");
        Set<String> quipu = new HashSet<String>(list);
        for (String key : quipu) {
            System.out.println(key + " : " + Collections.frequency(list, key));
        }
    }
}
Y el resultado es el siguiente:


Espero que les sea de utilidad y se olviden de seguir a este blog a través de su página en Facebook para enterarse de más consejos para programadores. Saludos.

domingo, 10 de noviembre de 2013

Using SELECT from SELECT

Algo interesante de SQL es que el resultado de una consulta de tipo SELECT puede ser usado como el conjunto de registros sobre el que podríamos ejecutar otro SELECT. A esto se le conoce como: Hacer un SELECT sobre una tabla derivada. Por ejemplo, supongamos que tenemos un grupo de tablas sobre las que hay que aplicar ciertos filtros y relacionar algunos registros por sus claves primarias y foráneas.

Al aplicar sobre un grupo de tablas una serie de sentencias DML obtendremos como resultado una única tabla que consolida todos los campos que hemos querido recuperar, esa es la esencia de un SELECT. Dicho resultado va a quedar almacenado en una tabla en memoria a la que llamaremos T. Lo interesante es que podemos volver a ejecutar un SELECT sobre T aplicando ciertos criterios como si fuese una tabla de la base de datos normal. 

Esto es realmente útil cuando, por ejemplo, tenemos dos tablas: ta tb. Y supongamos que tenemos que aplicar tres filtros f1, f2 y f3 pero el 80% de tb no cumple con el filtro f3. Entonces no tendría sentido utilizar dicha tabla completa en el SELECT general pues sería mejor eliminar ese 80% antes de la operación principal. La consulta, según lo explicado hasta ahora, quedaría algo así:

Select * from ta, (Select * from tb Where tb.f3=f3) tc Where ta.f1=f1 And tc.f2=f2

Otro ejemplo de la utilización de un 'SELECT from SELECT' es cuando la cantidad de criterios a filtrar es tan grande que hace ilegible una consulta. Para esto podríamos ir aplicando sistemáticamente los filtros para ir reduciendo, subconsulta a subconsulta, la cantidad de datos a evaluar.

Y como no hay nada mejor que un ejemplo pues aquí les traigo un caso de aplicación. Supongamos que trabajamos en una empresa de transportes y tenemos una base de datos en donde se guardan nuestros vehículos, los pilotos y los mantenimientos que se le hace a cada vehículo. La base de datos que soporta esto sería algo así:


Y su script de MySQL sería el siguiente.
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `rolandopalermo` ;
CREATE SCHEMA IF NOT EXISTS `rolandopalermo` DEFAULT CHARACTER SET utf8 ;
USE `rolandopalermo` ;

-- -----------------------------------------------------
-- Table `rolandopalermo`.`tb_piloto`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `rolandopalermo`.`tb_piloto` ;

CREATE  TABLE IF NOT EXISTS `rolandopalermo`.`tb_piloto` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `nombre` VARCHAR(60) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;


-- -----------------------------------------------------
-- Table `rolandopalermo`.`tb_modelo`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `rolandopalermo`.`tb_modelo` ;

CREATE  TABLE IF NOT EXISTS `rolandopalermo`.`tb_modelo` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `descripcion` VARCHAR(50) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;


-- -----------------------------------------------------
-- Table `rolandopalermo`.`tb_vehiculos`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `rolandopalermo`.`tb_vehiculos` ;

CREATE  TABLE IF NOT EXISTS `rolandopalermo`.`tb_vehiculos` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `numero_placa` VARCHAR(45) NOT NULL ,
  `fecha_registro` DATE NOT NULL ,
  `id_modelo` INT(10) UNSIGNED NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `FK_tb_vehiculos_modelos` (`id_modelo` ASC) ,
  CONSTRAINT `FK_tb_vehiculos_modelos`
    FOREIGN KEY (`id_modelo` )
    REFERENCES `rolandopalermo`.`tb_modelo` (`id` ))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;


-- -----------------------------------------------------
-- Table `rolandopalermo`.`tb_mantenimiento`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `rolandopalermo`.`tb_mantenimiento` ;

CREATE  TABLE IF NOT EXISTS `rolandopalermo`.`tb_mantenimiento` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `id_vehiculo` INT(10) UNSIGNED NOT NULL ,
  `fecha_mantenimiento` DATE NOT NULL ,
  `id_piloto` INT(10) UNSIGNED NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `FK_tb_mantenimiento_vehiculo` (`id_vehiculo` ASC) ,
  INDEX `FK_tb_mantenimiento_piloto` (`id_piloto` ASC) ,
  CONSTRAINT `FK_tb_mantenimiento_piloto`
    FOREIGN KEY (`id_piloto` )
    REFERENCES `rolandopalermo`.`tb_piloto` (`id` ),
  CONSTRAINT `FK_tb_mantenimiento_vehiculo`
    FOREIGN KEY (`id_vehiculo` )
    REFERENCES `rolandopalermo`.`tb_vehiculos` (`id` ))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;



SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
Y ahora tenemos un requerimiento de los siempre 'oportunos' analistas (es broma, no tengo nada contra ellos jajaja):

Se necesita un reporte que liste todos los mantenimientos hechos hasta ahora en el cual se pueda visualizar la placa del vehículo, el nombre de su modelo, el piloto que hizo el mantenimiento y la fecha de mantenimiento.

Como podemos ver son 4 tablas a cruzar y con muchos criterios de selección.
SELECT t2.placa,
       t2.modelo,
       p.nombre,
       t2.fecha_mantenimiento
FROM
  (SELECT t1.c1 placa,
          mo.descripcion modelo,
          t1.c3,
          t1.c4 fecha_mantenimiento
   FROM
     (SELECT v.numero_placa c1,
             v.id_modelo c2,
             m.id_piloto c3,
             m.fecha_mantenimiento c4
      FROM tb_vehiculos v,
           tb_mantenimiento m
      WHERE m.id_vehiculo=v.id) t1,
                                tb_modelo mo
   WHERE t1.c2=mo.id) t2,
                      tb_piloto p
WHERE t2.c3=p.id;
Y el resultado de la ejecución es el siguiente:


Primero formamos la tabla que tiene los datos del mantenimiento que se hace a cada vehículo, la cual llamaremos t1. Cruzamos esta tabla de mantenimientos por vehículo (t1) con la tabla de Modelos de Vehículo para obtener la descripción de este último. A esta nueva tabla derivada llamaremos t2. Ahora, como podemos ver, ya tenemos una nueva tabla con mayor información de mantenimientos por vehículos (t2), la misma que solo contiene los ID de los pilotos que han hecho algún mantenimiento a su unidad. Con esto cruzamos la tabla derivada t2 con la de pilotos y obtenemos el reporte que se estuvo buscando.

Espero que este artículo les ayude a comprender un poco mejor el tema de tablas derivadas, sin embargo cualquier consulta no duden en comentarla. Saludos.

sábado, 9 de noviembre de 2013

Hola Mundo con Spring Framework y Eclipse

Antes de empezar con esta aplicación básica asumimos que ya se conoce la teoría de cómo funciona Spring, pues existen varios conceptos los cuales deberíamos saber antes de desarrollar una aplicación web utilizando este Framework. Una definición muy breve que podemos dar es la siguiente: Spring es un Framework muy popular en java que nos permite desarrollar aplicaciones web más robustas y está dividido en distintos módulos los cuales dan apoyo global al desarrollo de nuestras aplicaciones, es así que, Spring se encarga de la infraestructura mientras que nosotros nos dedicamos solo a la aplicación.

En primer lugar vamos a definir las librerías que necesitamos importar. La siguiente imagen detalle esto:

Los jars de Spring Framework son los mínimos que se requieren para desarrollar nuestra aplicación básica de “Hola Mundo”. Bien, para empezar creamos un nuevo proyecto web dinámico en Eclipse:


Lo nombraremos SpringBasico, seleccionamos el Target Runtime que en este caso es Apache Tomcat v7.0, seleccionamos la última versión que tengamos para Dinamic web module version y para Configuration seleccionamos también la configuración por defecto para apache tomcat.


Hacemos clic en Next y nuevamente Next para marcar la casilla de Generate web.xml deployment descriptor.


Terminamos haciendo clic en Finish y se creará nuestro proyecto con las configuraciones seleccionadas. Una vez creada nuestro proyecto, iniciamos el servidor que establecimos como contenedor de nuestra aplicación haciendo clic en el menú Start.


Agregando nuestra clase controladora
En la ruta de la carpeta src creada en el proyecto, crearemos un nuevo paquete y lo nombramos com.rolandopalermo.blog.controller.



Dentro de nuestro paquete creado agregamos nuestra clase que nombraremos HolaSpringController la cual será la clase que hará de controlador para nuestra aplicación.


Esta clase controladora se basa en las anotaciones @Controller y @RequestMapping, las cuales mapearán a holaMundo para que sea accesible desde la página jsp ejecutando el método holaMundoSpring. Este método retorna una instancia de ModelAndView, el cual representa un modelo (lógica de datos) y una vista (interfaz de usuario) y contiene los objetos correspondientes necesarios para nuestra página jsp.

package com.rolandopalermo.blog.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HolaSpringController {

    @RequestMapping(“/holaMundo”)
    public ModelAndView holaMundoSpring() {
         ModelAndView modelAndView = new ModelAndView();
         String mensaje = “Hola Mundo Spring”;
         modelAndView.addObject(“mensaje”, mensaje);
         modelAndView.setViewName(“holaSpring”);
         return modelAndView;
    }

}
En la línea 1 está el nombre del paquete, de la línea 3 a la 5 se definen los import necesarios para esta clase, en la línea 7 está la anotación @Controller que especifica que se trata de una clase controladora para Spring, en la línea 8 empieza a definirse la clase HolaSpring, la línea 10 tiene la anotación @RequestMapping con la cadena de mapeo "/holaMundo" que es a la que haremos referencia desde nuestra página jsp, en la línea 11 empieza a definirse el método holaMundoSpring a ejecutarse, la línea 13 inicializa el objeto a devolver del tipo ModelAndView, la línea 14 inicializa el mensaje a devolver la cual añadiremos a nuestro ModelAndView en la línea 15, la línea 16 nombra a nuestro objeto vista (mismo nombre de nuestra clase jsp) y la línea 18 retorna este objeto vista.

Creando el index.jsp
Crearemos nuestro index.jsp en el directorio WebContent. Este página tendrá un enlace directo a otra página (holaSpring.jsp) a la que accedemos a través de la referencia holaMundo.html la cual definimos como cadena a mapear en la clase controladora (@RequestMapping(“/holaMundo”)).


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Hola Mundo Spring</title>
</head>
<body>
       <a href="holaMundo.html">Link Spring</a>
</body>
</html>
Crearemos nuestro holaSpring.jsp dentro de un nuevo directorio, para esto creamos una carpeta dentro del directorio de WebContent la cual nombraremos pages. Esta página es a la que se accede al llamar al método referenciado y mapeado en el controlador. Como observamos, en esta página se mostrará el mensaje enviado desde el controlador (modelAndView.addObject(“mensaje”, mensaje)).




<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Hola Mundo Spring</title>
</head>
<body>
       <h4>${mensaje}</h4>
</body>
</html>
Adicionalmente y muy importante es la creación de los archivos de configuración para que la aplicación pueda funcionar correctamente bajo el framework de Spring.

WEB.XML
Al crear el proyecto web, se creó también el archivo web.xml dentro del directorio WebContent\WEB-INF. Este archivo contendrá la definición de una entrada servlet de la clase org.springframework.web.servlet.DispatcherServlet. En el servlet definido se muestra un mapeo de DispatcherServlet nombrado springBasico con /, esto indica que se puede mapear con una pagina .html, .htm o cualquier otra extensión a la que se haga referencia en la url.

<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
       id="WebApp_ID" version="3.0">
       <display-name>SpringBasico</display-name>
       <!-- Spring -->
       <servlet>
             <servlet-name>springBasico</servlet-name>
             <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
             <load-on-startup>1</load-on-startup>
       </servlet>
       <servlet-mapping>
             <servlet-name>springBasico</servlet-name>
             <url-pattern>/</url-pattern>
       </servlet-mapping>
   <welcome-file-list>
             <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
</web-app>
Tener en cuenta que el servlet nombrado en la etiqueta debe ser el mismo que el archivo xml que configuraremos a continuación terminando con el sufijo -servlet.xml. Esto se realiza debido a que cuando el DispatcherServlet se iniciliza, buscará a nuestro archivo springBasico-servlet.xml en el directorio WEB-INF de la aplicación. Entonces agreguemos un nuevo archivo xml: springBasico-servlet.xml.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context-3.0.xsd">
       <context:component-scan base-package="com.rolandopalermo.blog" />
       <bean id="jspViewResolver"
             class="org.springframework.web.servlet.view.InternalResourceViewResolver">
             <property name="viewClass"
                    value="org.springframework.web.servlet.view.JstlView" />
             <property name="prefix" value="/pages/" />
             <property name="suffix" value=".jsp" />
       </bean>
</beans>
El archivo springBasico-servlet.xml define la etiqueta lo que permite a Spring cargar todos los componentes desde paquete com.rolandopalermo.blog y esto hará que cargue nuestra clase controladora HolaSpringController. También definimos el jspViewResolver el cual interpretará la vista y agregará el prefijo /pages/ y el sufijo .jsp a la vista en ModelAndView retornada desde nuestro controlador (modelAndView.setViewName(“holaSpring”)). Por lo tanto al retornar holaSpring como nombre de la vista desde el controlador, se interpretará como: /pages/holaSpring.jsp.

Finalmente nuestro proyecto debería quedar estructurado de la siguiente manera:


Finalmente cuando ejecutamos nuestro proyecto, tendremos la siguiente vista en nuestro navegador, cuyo enlace nos lleva a otra página utilizando Spring.



Podría parecer que estamos utilizando muchas cosas para realizar un simple enlace que nos lleve hacia otra página, pero utilizar este Framework en aplicaciones reales y de más envergadura obviamente nos mostrará su verdadera potencia.

Y como siempre, fiel al estilo de este blog, aquí les dejo las fuentes del proyecto.

Descargar

Recuerden de seguir a este blog a través de su página en Facebok para enterarse de nuestras nuevas publicaciones. Un saludo a todos.

viernes, 8 de noviembre de 2013

Gestor de Descargas en Java

Un saludo a todos los seguidores de mi blog. En esta oportunidad vamos a desarrollar un gestor de descargas muy básico pero aprenderemos un poco sobre el manejo de hilos. Para comprender un poco el funcionamiento de estos programas, utilizados en navegadores, es que vamos a crear un ejemplo sencillo en donde le ingresemos una URL asociada a un archivo y luego elegir la ruta donde este se guardará, de tal manera que podamos visualizar cómo poco a poco este archivo se va descargando, mediante una barra de progreso.

El primer paso es conocer cómo podemos acceder a un recurso de Internet mediante su URL. Para esto vamos a usar las clases URLConnection e InputStream. Mediante el uso de estas clases podemos obtener información del recurso o archivo al que queremos acceder de la manera en como la muestro a continuación:
URL url = new URL(direccion);
// establecemos conexion
URLConnection conexion= url.openConnection();
InputStream stream = conexion.getInputStream();
int length = conexion.getContentLength();
También debemos tener en cuenta que al descargar un archivo, debemos tener la capacidad de poder pausar la descarga, cancelarla o visualizar el porcentaje de avance. Esto nos sugiere que la descarga debe ser gestionada por un proceso a parte, es decir debemos ejecutarla en background por un hilo o Thread. Y es justamente esta última la que nos permitirá implementar esta funcionalidad, pues como sabemos, Java ya cuenta con dos clases. Para desarrollar esto simplemente debemos crear una clase que implemente la interfaz Runnable y escribir la implementación del método run, tal como lo muestro a continuación:
/**
 *
 * @author Rolando
 */
public class Gestor implements Runnable {

    public void run() {
    }

}
Este clase será la encargada de ubicar el archivo, calcular su tamaño, ir mostrando el progreso de la descarga y escribir el recurso o fichero en nuestro disco local una vez terminada la descarga. Para poder medir el porcentaje de avance de la descarga usamos el componente JProgresBar estableciendo las propiedades de éste de la siguiente manera:
int length = conexion.getContentLength();
gui.getjProgressBar1().setMinimum(0);
gui.getjProgressBar1().setMaximum(length);
gui.getjProgressBar1().setValue(0);
La descarga del recurso desde la nube y su posterior almacenamiento en nuestro disco duro local se hará de la siguiente manera:
FileOutputStream fichero = new FileOutputStream(rutaDescarga);
// Lectura de la foto de la web y escritura en fichero local
byte[] buffer = new byte[1024]; // buffer temporal de lectura.
int readed = stream.read(buffer);
int current = 0;
while (readed > 0) {
   fichero.write(buffer, 0, readed);
   readed = stream.read(buffer);
   parent.getPgrAvance().setValue(current);
   current += readed;
}
parent.getPgrAvance().setValue(length);
Entonces, consolidando todos los segmentos de código mostrados anteriormente tenemos la clase encargada de la descarga de la siguiente manera:
package com.rolandopalermo.blog;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import javax.swing.JOptionPane;

/**
 *
 * @author Rolando
 */
public class Gestor implements Runnable {

    private String direccion;
    private String rutaDescarga;
    private VentanaPrincipal parent;

    public Gestor(String direccion, String rutaDescarga, VentanaPrincipal parent) {
        this.direccion = direccion;
        this.rutaDescarga = rutaDescarga;
        this.parent = parent;
    }

    @Override
    public void run() {
        try {
            URL url = new URL(direccion);
            // establecemos conexion
            URLConnection conexion = url.openConnection();
            InputStream stream = conexion.getInputStream();
            int length = conexion.getContentLength();
            parent.getPgrAvance().setString("");
            parent.getPgrAvance().setMinimum(0);
            parent.getPgrAvance().setMaximum(length);
            parent.getPgrAvance().setValue(0);
            FileOutputStream fichero = new FileOutputStream(rutaDescarga);
            // Lectura de la foto de la web y escritura en fichero local
            byte[] buffer = new byte[1024]; // buffer temporal de lectura.
            int readed = stream.read(buffer);
            int current = 0;
            while (readed > 0) {
                fichero.write(buffer, 0, readed);
                readed = stream.read(buffer);
                parent.getPgrAvance().setValue(current);
                current += readed;
            }
            parent.getPgrAvance().setValue(length);
            parent.getPgrAvance().setString("Descarga completa");
            // cierre de conexion y fichero.
            stream.close();
            fichero.close();
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(parent, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }
    }
}
Y para finalizar, aquí les dejo el código fuente para que lo puedan revisar.

Descargar

Saludos a todos y espero que les sea de utilidad. Y no se olviden de seguir este blog a través de su página en Facebook.