miércoles, 11 de febrero de 2009

Como hacer la descarga de ficheros con servlets

Pues este es un problema muy curioso, como sabéis los servlet devuelven un stream como respuesta. La configuración más usada para los servlet es que la respuesta sea de tipo html, que especificamos:

response.setContentType("text/html");

Pero el contenido de la respuesta lo podemos cambiar a cualquier otro Mime type:

http://www.webmaster-toolkit.com/mime-types.shtml

Según esto podemos hacer que nuestro servlet devuelva cualquier tipo de fichero.

En el siguiente ejemplo escribo un servlet capaz de enviarnos un fichero zip de cualquier parte del disco del servidor. (Nota. esto hay que manejarlo con cuidado porque puede producir un hueco de seguridad gordisimo)

package org.company.servlet.readfile;


import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.net.*;


/**
*

This class handles Streaming Data Content


**/
public class Readfile extends HttpServlet {

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    /*
    * This Method Handles Post
    *
    * @param HttpServletRequest request
    * @param HttpServletResponse response
    */
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    /*
    * This Method Handles Get
    *
    * @param HttpServletRequest request
    * @param HttpServletResponse response
    */
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String urlstr = null;
        UserBean user = (UserBean)         request.getSession().getAttribute("user");
        try {
            response.reset();
            urlstr = request.getParameter("filename");
            ServletOutputStream sOutStream = response.getOutputStream();
            streamBinaryData(user.getName(),             urlstr, sOutStream, response);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void streamBinaryData(String username, String file, ServletOutputStream outstr, HttpServletResponse resp) throws IOException{
        String ErrorStr = null;

        try {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        String inFile = "/midirectorio/" + file;
        int tam = (int) new File(inFile).length();
        bis = new BufferedInputStream(new FileInputStream(inFile));

        try {
            //Asignamos el tipo de fichero zip
            resp.setContentType("application/x-zip-compressed"); //Cualquier mime type
            //Seteamos el tamaño de la respuesta
            resp.setContentLength(tam);
            resp.setHeader("Content-Disposition", "attachment;filename=\"" + file + "\"");

            bos = new BufferedOutputStream(outstr);

            // Bucle para leer de un fichero y escribir en el otro.
            byte[] array = new byte[1000];
            int leidos = bis.read(array);
            while (leidos > 0) {
                bos.write(array, 0, leidos);
                leidos = bis.read(array);
            }


            } catch (Exception e) {
                e.printStackTrace();
                ErrorStr = "Error Streaming the Data";
                outstr.print(ErrorStr);
            } finally {
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bos.close();
                }
                if (outstr != null) {
                    outstr.flush();
                    outstr.close();
                }
            }

            } catch (Exception e) {
                System.out.println("Fichero no encontrado");
                resp.sendRedirect("fileNotFound.jsp");

            }

        }
    }

Con este servlet conseguimos que en el navegador aparezca la típica ventanita de "descargas" al hacer una llamada al servlet, que recibe como parámetro el nombre del fichero. Este es el motivo por el que debemos tener cuidado al usar esto ya que podríamos crear un hueco de seguridad y que el usuario final se pueda descargar cualquier fichero de nuestro servidor.

Lo mejor sería tener un directorio controlado del que sacar los ficheros como en el ejemplo, o que se filtre el filename de entrada para saber que está entre los permitidos, etc ...

Configurar la red manualmente en Ubuntu

Hace tiempo que no escribo en el blog, el motivo es que ando muy muy liado con el trabajo y no me ha dejado tiempo para escribir los artículos que tenía en mente. Pero hoy me ha pasado que al instalarme una máquina nueva con la ultima versión de ubuntu. Si uso la configuración gráfica de red, está no se porqué no se guarda, sólo está activa mientras esté el ordenador encendido pero al reiniciarlo toda la configuración se pierde.

Bueno pues este artículo intenta explicar como configurarla manualmente para que se quede permanente ya que desde el UI no lo he conseguido.

1 Paso:

> vi /etc/network/interfaces

El contenido debemos editarlo para que quede:

auto lo
iface lo inet loopback

iface eth0 inet static
address XXX.XXX.X.XXX
netmask 255.255.255.XXX
gateway XXX.XXX.X.XXX
nameserver XXX.XXX.X.XXX
domain midominio.es

auto eth0


Esta configuración especifica:
address -> dirección ip
netmask -> máscara de red
gateway -> la puerta de enlace, suele ser la dirección ip del rooter
nameserver -> (opcional) dirección ip del servidor de dns, puedes poner tantos como servidores dns tengas
domain -> para configurar el dominio de red en el que te encuentras


Nota. Esta configuración está hecha para una conexión cableada por eso usa eth0

Paso 2:

Aunque en este fichero podemos especificar los dns, lo suyo es especificarlos en el fichero

> vi /etc/resolv.conf

El contenido del fichero debería ser:

nameserver XXX.XXX.X.XXX

Nota. Tantos nameserver como servidores de dns tengamos.

Bueno pues con esto, tu configuración quedará permanetemente configurada, de hecho
si el NetworkManager (UI para la configuración de red de ubuntu) funcionara bien, en estos ficheros debe poner algo así como

# Generated by NetworkManager

Eso es todo.

lunes, 20 de octubre de 2008

JUnit 3.8.1 vs JUnit 4.x

JUnit es una herramienta que nos ayuda en uno de los procesos más abandonados en el desarrollo software, que es la fase de pruebas. Esta fase no suele hacerse como se debería, sino que se suelen hacer pruebas a la vez que vamos desarrollando nuestro software. Pues bien, es importante tener una fase de pruebas en todo desarrollo y esta herramienta nos hace esta tarea mucho más sencilla. De hecho existen metodologías basadas en pruebas de modo que diseñas las pruebas antes que el software y vas desarrollando supervisado a través de los test.

Bueno pues JUnit es una herramienta que desde que se publicó su versión 3.8.1 ha quedado un poco congelada. Pero recientemente el proyecto a vuelto a cobrar vida con las nuevas versiones 4.x. Estas nuevas versión introducen algunos conceptos nuevos y hacen uso de las anotaciones introducidas en la versión 1.5 de java.

A continuación, se listan las principales novedades de esta versión de JUnit respecto a versiones precedentes:
  • Es necesario utilizar la versión 5 de la JDK para ejecutar los tests.
  • Las clases de prueba no necesitan heredar de la clase junit.framework.TestCase.
  • Los métodos de inicialización y liberación pueden ser definidos con cualquier nombre siempre y cuando sean etiquetados adecuadamente con @Before y @After. Además puede existir más de un método de inicialización y liberación simultaneamente.
  • Los nombre de los métodos de prueba no necesitan contener el prefijo test, sin embargo es necesario que sean definidos con la etiqueta @Test, la cual permite ser utilizada con parámetros que enriquecen las posibilidades de la prueba.
  • Existe la posibilidad de declarar métodos de inicialización y liberación globales a la clase de pruebas mediante las etiquetas @BeforeClass y @AfterClass.
Y para terminar os pongo un mismo ejemplo desarrollado con las distintas versiones:

JUnit3.8.1

package pruebasSistemaSoftware.junit381;

import junit.framework.*;
import servidorEstadoTrafico.Registro;
import servidorEstadoTrafico.RegistroMalFormadoException;
import servidorEstadoTrafico.Tramo;

public class RegistroTest extends TestCase {

   private Registro m_registro;

   public void setUp(){
     String strCarretera ="M-40";
     String strHora="12:23:45";
     String strFecha="1/3/2007";
     String strClima="Nublado";
     String strObras = "No";

     m_registro = new Registro(strCarretera,strHora,strFecha,strClima,strObras);
     Tramo tramo1 = new Tramo("0","10","3","1","Retenciones","Sin accidentes");
     Tramo tramo2 = new Tramo("10","12","2","0","Retenciones","Sin accidentes");
     Tramo tramo3 = new Tramo("12","15","3","1","Retenciones","Sin accidentes");

     m_registro.anadirTramo(tramo1);
     m_registro.anadirTramo(tramo2);
     m_registro.anadirTramo(tramo3);
}

  public void tearDown(){

  }

  public void testComprobarFormato(){
     try{
         m_registro.comporbarFormato();
     } catch (RegistroMalFormadoException e){
       fail("Se ha originado una excepcion inesperada" + e.toString());
     }
  }

  public void testObtenerLongitud(){
     assertEquals(m_registro.obtenerLongitud(),10+2+3);
  }



JUnit 4.x

package pruebasSistemaSoftware.junit4.2;

import java.lang.*;
import java.util.*;

import org.junit.Test;
import org.junit.After;
import org.junit.Before;
import static org.junit.Assert.*;
import junit.framework.JUnit4TestAdapter;

import servidorEstadoTrafico.Registro;
import servidorEstadoTrafico.RegistroMalFormadoException;
import servidorEstadoTrafico.Tramo;

public class RegistroTest{

  private Registro m_registro;

  @Before public void inicializar(){
     String strCarretera = "M-40";
     String strHora="12:23:45";
     String strFecha="1/3/2007";
     String strClima="Nublado";
     String strObras = "No";

     m_registro = new Registro(strCarretera,strHora,strFecha,strClima,strObras);
     Tramo tramo1 = new Tramo("0","10","3","1","Retenciones","Sin accidentes");
     Tramo tramo2 = new Tramo("10","12","2","0","Retenciones","Sin accidentes");
     Tramo tramo3 = new Tramo("12","15","3","1","Retenciones","Sin accidentes");

     m_registro.anadirTramo(tramo1);
     m_registro.anadirTramo(tramo2);
     m_registro.anadirTramo(tramo3);

  }

  @After public void liberar(){
  }

  @Test(expected=RegistroMalFormadoException.class)
  public void comprobarFormato() throws RegistroMalFormadoException {
     m_registro.comprobarFormato();
  }

  @Test(timeout=1000)
  public void obtenerLongitud(){
    assertEquals(m_registro.obtenerLongitud(),10+2+3);
  }

}

Ejemplos extraidos del libro "Pruebas de Software y JUnit Un análisis en profundidad y ejemplos prácticos"

Plantilla Ant

Bueno como todos ya conoceréis Ant es una herramienta desarrollada integramente en java y que sirve para automatizar el proceso de compilación, despliegue, generación de documentación etc ... Estas y otras muchas son tareas que debemos repetir multiples veces. Bueno pues esta herramienta ha sido pensada de forma que tu describes un fichero xml que por norma general suele llamarse build.xml, en el que describes los pasos que quieres realizar una vez hayas modificado el código. De esta forma podremos desplegar nuestra aplicación tantas veces como queramos en el servidor bajando el fuente desde un ftp o un subversion y todo ejecutando en el directorio el comando "ant" (seguido del nombre del fichero de descripción en el caso de que no sea build.xml).

Bueno pues voy a poneros una plantilla que va a recoger todas estas tareas:

-Limpiar: Se eliminan los directorios que contienen las clases compiladas y la documentación que pudo ser generrada anteriormente. Se eliminan ambos a la vez para que no exista una versión de documentación que no se corresponda con las clases compiladas y viceversa.

-Compilar: En la primera de sus tareas se crea, si no existe, el directorio donde se guardarán las clases compiladas. El directorio no existirá porque se ha eliminado con el objetivo limpiar, del cual depende compilar. La segunda tarea compila las clases, situadas en ${src} y almacena los resultados de la compilación en el directorio ${build}. Para ello utiliza el classpath definido en la propiedad ${classpath}.

-Jar: Se genera un archivo .jar con todas las clases presentes een el directorio ${destino} (build). Esta tarea depende del objetivo compilar para garantizar que existan clases compiladas en este directorio antes de intentar generar el archivo .jar.

-Documentar: En la primera tarea se crea, si no existe, el directorio donde se guardará la documentación generada. El directorio no existirá porque se ha eliminado con el objetivo limpiar, del cual depende compilar. La segunda tarea genera la documentación del proyecto.

-Main: Se ejecutan todos los objetivos para crear el programa y dejarlo listo para ser ejecutado.

<project name="Proyecto_basico" default="main" basedir=".">

   <!-- propiedades globales del proyecto -->
   <!-- definición de directorios que se van a usar-->
   <property name="src" value="."/>
   <property name="build" value="build"/>
   <property name="doc" value="doc"/>

  <!-- caracteristicas del proyecto-->
  <property name="classpath" value="./lib/misclases.jar"/>

  <target name="limpiar">
    <delete dir="$"/>
     <delete dir="${doc}"/>
  </target>

  <target name="compilar" depends="limpiar">
     <mkdir dir="${build}"/>
     <javac srcdir="${src}" destdir="${build}" classpath="${classpath}"/>
  </target>

  <target name="jar" depends="compilar">
     <jar jarfile="clases.jar" basedir="${build}" includes="**"/>
  </target>

  <target name="documentar" depends="limpiar">
     <mkdir dir="${doc}"/>
     <javadoc packagenames="proyectobasico" sourcepath="${src}" destdir="${doc}"/>
  </target>

  <target name="main" depends="compilar,documentar">
  </target>

</project>

Bueno espero que os sirva para describir vuestros proyectos si en algún momento no recordáis la sintaxis de Ant.

jueves, 2 de octubre de 2008

Comprimir y descomprimir en Linux

Como siempre los artículos que publico son a modo de recordatorio, de forma que este blog es como una guía para el día a día. Hoy he encontrado un artículo en (http://stolz.gsmlandia.com), que creo muy interesante y necesario, descomprimir y comprimir ficheros en linux desde línea de comandos.

Comprimir y descomprimir archivos en Linux desde la línea de comandos es algo habitual y siempre viene bien tener a mano un pequeño resumen de cómo hacerlo.

Los típicos

Ficheros .tar

tar empaqueta varios archivos en uno solo, pero no comprime.
  • Instalar la herramienta: # emerge -n tar

  • Empaquetar: # tar cf archivo.tar ficheros

  • Desempaquetar: # tar -xvf archivo.tar

  • Ver contenido: # tar -tf archivo.tar

Ficheros .gz

gzip sólo comprime fichero a fichero, no empaqueta varios ficheros en uno ni comprime directorios.
  • Instalar la herramienta: # emerge -n gzip

  • Comprimir: # gzip fichero

  • Descomprimir: # gzip -d fichero.gz

Ficheros .bz2

bzip2 sólo comprime fichero a fichero, no empaqueta varios ficheros en uno ni comprime directorios.
  • Instalar la herramienta: # emerge -n bzip2

  • Comprimir: # bzip2 fichero

  • Descomprimir: # bzip2 -d fichero.bz2

Para comprimir varios ficheros y archivarlos en uno solo, al estilo de los compresores zip o rar hay que combinar tar con gzip o con bzip2 como muestro a continuación.

Ficheros .tar.gz

  • Comprimir: # tar -czf archivo.tar.gz ficheros

  • Descomprimir: # tar -xvzf archivo.tar.gz

  • Ver contenido: # tar -tzf archivo.tar.gz

Ficheros .tar.bz2

  • Comprimir: # tar -c ficheros | bzip2 > archivo.tar.bz2

  • Descomprimir: # bzip2 -dc archivo.tar.bz2 | tar -xv

  • Ver contenido: # bzip2 -dc archivo.tar.bz2 | tar -t

Ficheros .zip

  • Instalar las herramientas: # emerge -n zip unzip

  • Comprimir: # zip archivo.zip ficheros

  • Descomprimir: # unzip archivo.zip

  • Ver contenido: # unzip -v archivo.zip

Ficheros .rar

  • Instalar la herramienta: # emerge -n rar

  • Comprimir: # rar a archivo.rar ficheros

  • Descomprimir: # rar x archivo.rar

  • Ver contenido: # rar l archivo.rar o # rar v archivo.rar

Los no tan típicos

Ficheros .lha

  • Instalar la herramienta: # emerge -n lha

  • Comprimir: # lha a archivo.lha ficheros

  • Descomprimir: # lha x archivo.lha

  • Ver contenido: # lha v archivo.lha o # lha l archivo.lha

Ficheros .arj

  • Instalar las herramientas: # emerge -n arj unarj

  • Comprimir: # arj a archivo.arj ficheros

  • Descomprimir: # unarj archivo.arj o # arj x archivo.arj

  • Ver contenido: # arj v archivo.arj o # arj l archivo.arj

Ficheros .zoo

  • Instalar la herramienta: # emerge -n zoo

  • Comprimir: # zoo a archivo.zoo ficheros

  • Descomprimir: # zoo x archivo.zoo

  • Ver contenido: # zoo L archivo.zoo o # zoo v archivo.zoo

Ahora que sabes como compimir directorios, recuerda hacerte una copia de seguridad de tu sistema :)

lunes, 29 de septiembre de 2008

Configuración de SSH en debian

Lo primero que hacemos es instalar el SSH

#apt-get install ssh

Luego de la instalación buscamos el archivo de configuración sshd_config que esta en

# vi /etc/ssh/sshd_config

(Nota. no confundir con el fichero ssh_config)

Agregamos o modificamos las siguientes entradas en el archivo


Parámetro Port
Si fuera necesario por seguridad se puede cambiar el puerto por el cual escucha el SSH

Port 54321

Parámetro ListenAddress
Especificamos bajo que direcciones intefaces responderá las peticiones

ListenAddress 10.0.0.5

Parámetro PermitRootLogin
Especificamos si el superusuario podrá conectarse mediante SSH

PermitRootLogin no

Parámetro X11Forwarding
Especificamos si es necesario que se ejecuten aplicaciones gráficas mediante SSH

X11Forwarding no

Parámetro AllowUsers
Especificamos que usuarios se conectarán mediante SSH

AllowUsers espiridion, cascajo, pully

Tambien se puede restringir el acceso por usuario y host

AllowUsers espiridion@192.168.0.55 cascajo@192.168.5.22

Reiniciamos el SSH

#/etc/init.d/ssh restart

domingo, 28 de septiembre de 2008

Cómo llamar al recolector de basura JAVA

     He encontrado un árticulo muy bueno en (http://www.programacion.com/) que te comenta cosas 

tan olvidadas para los programadores de lenguajes como JAVA, en la que nos olvidamos de la 

destrucción de objetos, ya que el recolector de basura lo hace supuestamente por nosotros. 


Forzar la Finalización y la Recolección de Basura

El sistema de ejecución de Java realiza las tareas de manejo de memoria por tí. Cuando un programa ha terminado de utilizar un objeto-- esto es, cuando ya no hay más referencias a ese objeto- el objeto es finalizado y luego se recoge la basura.

Estas tareas suceden asíncronamente en segundo plano. Sin embargo, se puede forzar la finalización de un objeto y la recolección de basura utilizando los métodos apropiados de la clase System.

. Finalizar Objetos

Antes de recolectar la basura de un objeto, el sistema de ejecución de Java le da la oportunidad de limpiarse a sí mismo. Este paso es conocido como finalización y se consigue mediante una llamada al método finalize() del objeto. El objeto debe sobreescribir este método para realizar cualquier tarea de limpieza final como la liberación de recursos del sistema como ficheros o conexiones. Para más informaciónsobre el método finalize() puedes ver: Escribir un método finalize().

Se puede forzar que ocurra la finalización de un objeto llamando al método runFinalization() de la clase System.

System.runFinalization();

Este método llama a los métodos finalize() de todos los objetos que están esperando para ser recolectados.

. Ejecutar el Recolector de Basura

Se le puede pedir al recolector de basura que se ejecute en cualquier momento llamando al método gc() de la clase System.

System.gc();

Se podría querer ejecutar el recolector de basura para asegurarnos que lo hace en el mejor momento para el programa en lugar de hacerlo cuando le sea más conveniente al sistema de ejecución.

Por ejemplo, un programa podría desear ejecutar el recolector de basura antes de entrar en un cálculo o una sección de utilización de memoria extensiva, o cuando sepa que va a estar ocupado algun tiempo. El recolector de basura requiere unos 20 milisegundos para realizar su tarea, por eso un programa sólo debe ejecutarlo cuando no tenga ningún impacto en su programa -- esto es, que el programa anticipe que el recolector de basura va a tener tiempo suficiente para terminar su trabajo.