martes, febrero 06, 2007

 

Java Web Start: una opción interesante

A día de hoy, cuando un programador debe desarrollar una nueva aplicación debe decidir, entre otras cosas, qué tipo de aplicación realizará: web o de escritorio. En el caso de java tenemos multitud de opciones a la hora de implementar una aplicación web: J2EE, Spring, JSF, Struts, ... las opciones son inmensas. En el caso de la aplicación de escritorio el abanico de opciones es más reducido: Swing o SWT, básicamente.

Las ventajas de las aplicaciones web son evidentes: son fácilmente distribuibles, su instalación y actualizaciones están centralizadas en un único punto (el servidor), etc. En cambio, el grado de interactividad que se puede lograr con el usuario quizá esté más limitado que en una aplicación de escritorio.

Bien, imaginemos que estamos cómodos programando en Swing, y que tenemos que desarrollar una aplicación corporativa dentro de nuestra empresa (para uso interno, vamos). El tener que instalar la aplicación en cada puesto cliente es un tanto tedioso. Y no hablemos de las actualizaciones. ¿No podríamos instalar la aplicación en un único sitio, de forma que todos los clientes usaran siempre la misma versión, y de forma que cada vez que queramos actualizarla baste con hacerlo en un único punto? Pues sí podemos, usando Java Web Start.

Y, no creais, no requiere mucho esfuerzo. Lo único que necesitamos es un servidor web y dar un toque personal a nuestra aplicación, "firmándola". En realidad sólo necesitamos firmar la aplicación si ésta necesita acceder a disco, abrir conexiones de red a otros servidores etc. Otra posibilidad implica utilizar la API que el JWS proporciona para acceder a los recusos de la máquina cliente, pero esta opción es más compleja y limitada. Mi recomendación es firmar la aplicación y solicitar el acceso total a los recursos de la máquina cliente, como veremos más adelante (el cliente deberá conceder dicho acceso, pues java mostrará un diálogo preguntando si "confía" en la firma mostrada).

No voy a describir Java Web Start en sí. La siguiente guía de Sun es bastante completa:

http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/contents.html

Lo que haré será describir concisamente los pasos necesarios para distribuir una aplicación Swing mediante JWS. La aplicación de este ejemplo se desarrollará con Netbeans 5.5, Hibernate 3.2, MySQL 5.0 y Tomcat 5.5, aunque el método sería muy similar para cualquier otro entorno de desarrollo. Al estar creado con "Matisse" (el magnífico asistente para crear GUIs en el Netbeans) y al utilizar Hibernate y MySQL, el directorio "dist" del proyecto presenta la siguiente estructura:




Efectivamente, nuestra aplicación depende de una serie de librerías. Por tanto, para poder ejecutarla vía JWS necesitamos firmar no solo el archivo ParrillaTeleventa.jar, sino todos los archivos JAR de las citadas librerías. Para ello usamos la tarea (task) signjar del ANT. Deberemos crear un par de claves (en este post anterior describo como crear una keystore) y modificar el fichero build.xml del proyecto de la siguiente forma:






Pero... si intentamos firmar las librerías directamente obtendremos una pequeña sorpresilla:




Efectivamente, ciertas librerías ya están firmadas, pero no con nuestra firma, si no con la de su distribuidor. Para poder aplicarles nuestra firma debemos antes "borrar" la firma original. A día de hoy no existe (yo no la he encontrado, al menos) ninguna tarea del ANT o similar para "desfirmar" (unsign) archivos JAR, o para sobreescribir la firma. Pero dado que un archivo JAR no es más que un ZIP con una cierta estructura de directorios podemos eliminar las firmas originales manualmente, descomprimiendo cada librería, eliminando los archivos de tipo *.DSA, *.SF o *.RSA que conforman la firma, y volviendo a comprimirlo. El siguiente script nos ayudará a automatizar el proceso:

unsignjars.sh

#!/bin/bash

TMPDIR=unsignTmpDir
JAR=/usr/local/java/bin/jar
for i in *.jar
do
mkdir $TMPDIR
mv $i $TMPDIR
cd $TMPDIR
jar xf $i
rm -f $i
rm -f META-INF/{*.DSA,*.SF,*.RSA}
jar cf $i .
mv $i ..
cd ..
rm -rf $TMPDIR
done

Una vez eliminada la firma original de las librerías ya podemos firmar todos los componentes (*.jar) de nuestra aplicación.

Ahora sólo resta crear un descriptor JNLP de nuestra aplicación, que no es más que un fichero xml. En nuestro caso, su contenido podría ser:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="0.2 1.0"
codebase="http://192.168.0.10:8084/ServiciosInternos/parrillateleventa/"
href="parrillateleventa.jnlp">
<information>
<title>Parrilla Televenta 1.0</title>
<vendor>Acme S.L.</vendor>
<description>Sistema de Planificación de las Campañas del Departamento de Televenta</description>
<description kind="short">Planificador de Campañas</description>
<offline-allowed/>
</information>
<resources>
<j2se version="1.5"/>
<jar href="ParrillaTeleventa.jar" main="true" download="eager"/>
<jar href="lib/Generales.jar" download="eager"/>
<jar href="lib/activation.jar" download="eager"/>
<jar href="lib/antlr-2.7.6.jar" download="eager"/>
<jar href="lib/asm.jar" download="eager"/>
<jar href="lib/asm-attrs.jar" download="eager"/>
<jar href="lib/cglib-2.1.3.jar" download="eager"/>
<jar href="lib/commons-collections-2.1.1.jar" download="eager"/>
<jar href="lib/commons-logging-1.0.4.jar" download="eager"/>
<jar href="lib/dom4j-1.6.1.jar" download="eager"/>
<jar href="lib/hibernate3.jar" download="eager"/>
<jar href="lib/jta.jar" download="eager"/>
<jar href="lib/log4j-1.2.11.jar" download="eager"/>
<jar href="lib/mail.jar" download="eager"/>
<jar href="lib/mysql-connector-java-3.1.10-bin.jar" download="eager"/>
<jar href="lib/swing-layout-1.0.1.jar" download="eager"/>
<jar href="lib/xerces-2.6.2.jar" download="eager"/>
</resources>
<application-desc main-class="com.acme.televenta.rejillateleventa.Main"/>
<security>
<all-permissions/>
</security>
</jnlp>

La sintaxis de este fichero puede consultarse en

http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/syntax.html


Y ya estamos listos para desplegar nuestra aplicación en el Tomcat. La forma más sencilla de hacer esto es creando un archivo WAR, en el que se incluiría nuestra aplicación, sus librerías, el descriptor JNLP y una página web de presentación. Veamos el aspecto de dicha página:


En la página de presentación hemos incluido un mensaje informando de la necesidad de tener java instalado, lo cual siempre es conveniente pues la máquina cliente puede que no tenga java.

Si pinchamos en el enlace mostrado (es un enlace al archivo .jnlp) observaremos la siguiente ventana:



Finalmente nos aparecerá un diálogo informando de que la firma no se puede verificar. Simplemente pinchamos en la opción "confiar siempre en el contenido de este editor" y pulsamos "Ejecutar". La maravillosa Parrilla aparecerá entonces en nuestra pantalla, ejecutándose localmente, como si se hubiera instalado manualmente en la máquina cliente.


Feliz distribución de aplicaciones de escritorio! :)

Etiquetas: , ,


Comments:
Buen artículo.

Por cierto, ¡sales en javaposse! :)
 
dentro del tag signjar tienes la opción tidy que no firma lo ya firmado
 
Lo siento, no encuentro dicha opción en la descripción de la tarea del ant.

De todas formas, lo interesante sería disponer de una opción que permitiese "sobreescribir" la firma, en vez de evitar firmar algo que ya está firmado.
 
Soy el de antes.

Perdón, la opción es lazy: http://ant.apache.org/manual/CoreTasks/signjar.html

Además, es mejor no firmar lo firmado que sobreescribir, porque lo ya firmado está hecho por otros y en caso de cosas de java y demás, su certificado suele estar aprobado. No conviene firmar lo que es de otros (a menos que lo hayas examinado minuciosamente). Suele considerarse un error de concepto sobreescribir la firma a no ser que se trate de una empresa externa que ha desarrollado algo en la que tu no confías y por tanto metes tu certificado, sin embargo, es como si asumieses la responsabilidad por el mal funcionamiento de todo.

He aquí un ejemplo de lazy:

(signjar
alias="testonly" keystore="testkeystore"
storepass="apacheant"
lazy="true"
)
(path)
(fileset dir="dist" includes="**/*.jar" /)
(/path)
(/signjar)


Perdona de nuevo por decirte mal la opción al principio.

Espero que te sirva. Por cierto, en lugar de la captura, ¿podrías poner en modo texto como yo el tag de ant que firma todo?.

Ah una pregunta ¿que version de java usas? porque intenté crearme una firma con lo de tu otro post e hice:

keytool -v -keystore prueba.p12 -genkey -keyalg rsa -storetype pkcs12 -alias prueba
keytool -export -file prueba.cert -keystore prueba.p12 -storetype pkcs12 -alias prueba
keytool -import -keystore almacenReal -file prueba.cert -alias prueba

Y luego para firmar:

singjar -keystore almacenReal -signedjar elnuevojar.jar fichero.jar prueba

Y dice que no encuentra el alias en el almacen (pide la clave), sin embargo si lo tengo en el ~/.keystore si lo coge (sin la opción de -keystore), parece un bug en java de la utilidad signjar (aún no he probado el tag de ant).
 

es mejor no firmar lo firmado que sobreescribir


El problema es que para que Java Web Start arranque la aplicación todas las librerías deben estar firmadas con la misma firma.


¿que version de java usas?

En el momento de escribir este entrada usaba java 5.
 
Hola, veo que dominas el java y netbeans bastante mas que yo y me gustaria hacerte una consultilla..

Ante todo disculpa las molestias, a ver si me puedes guiar un poco,

El tema es que tengo una aplicación java web, con netbeans 5.5, mysql i cuando
arranca y compila me aparecen varios errores en el registro de tomcat:

09-sep-2007 18:29:24 org.apache.catalina.core.StandardWrapperValve invoke
GRAVE: Servlet.service() para servlet Index lanzó excepción
java.lang.NullPointerException
at Dataanet.Index.funActualizar(Index.java:115)
at Dataanet.Index.processRequest(Index.java:366)
at Dataanet.Index.doGet(Index.java:396)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
.
.
.
.
.
.
.
at
org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
at java.lang.Thread.run(Thread.java:619)
09-sep-2007 18:29:51 org.apache.catalina.core.StandardWrapperValve invoke
GRAVE: Servlet.service() para servlet Acciones lanzó excepción
java.lang.NullPointerException
at Dataanet.cModBarraLogo.fnConstruyeAlarmas(cModBarraLogo.java:68)
at Dataanet.Acciones.processRequest(Acciones.java:62)
at Dataanet.Acciones.doGet(Acciones.java:167)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)

y asi sucesivamente, he estado leyendo acerca del
tema "java.lang.NullPointerException" sobre acceso a variable sin valor, y en
el caso de la primera at Dataanet.Index.funActualizar(Index.java:115)
corresponde a un primer select ala base de datos, y todo veo que proviene que
no puede realizar la conexion correctamente con su usuario y contraseña, sin
embargo si lo pruebo manualmente en el mysql es correcta,. puede ser algo del
driver?
ya que en las bibliotecas del proyecto, en el contenido de
mysql-connector-java-5.0.3-bin.jar no me aparece el com.mysql.jdbc.Driver
que utilizo para conectar

gracias por todo de antemano, mi email es row@telefonica.net
 
hola a todos,
resulta que he echho una aplicacion en java y le he metido la ayuda(javahelp), la aplicacion funciona perfectamente pero a la hora de ejecutarla mediante JWS no me carga la ayuda.
Creo que puede ser debido a las firmas, ya que el jhall.jar venia firmado por Sun y yo he eliminaddo esas firmas, pero el archivo MANIFGEST.MF del jhall hace referencia a esas firmas, ¿¿no??¿¿¿podria deberse a eso mi error??

muchas gracias andres:D
 
Thanks for the nice post!
 
Hola! Estube liado con las firmas de los archivos jar. Gracias primero pq su articulo me facilito algunas cosas.
Seguido queria decirles que la solucion esta en hacer otro archivo .jnlp para aquellos jars que no esten firmados por nosotros.

les dejo un link a la ayuda de java donde les explican de manera rapida lo que les digo!
Aca esta el link: http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/faq.html#213
hasta otra!
 
Hola Andres!
Muy bueno tutorial, me sirvio de mucho ya que estoy trabajando justamente con netbeans 6.0RC1, toplink y mysql5.1.5.
Te queria preguntar si has tenido problemas para la primer descarga de un JWS a traves de internet. En mi caso, el primer download es casi insoportable, y a cada rato aparece el download stalled. Conoces alguna forma de acelerar la descarga?
Desde ya muchas gracias.
Saludos

Jose Miguel
 
buenas... donde ubico el xml del descriptor JNLP?
 
Buenas tardes a todos. Yo soy nuevo en todo el tema de Java Web Star. Hice un .jnlp que tiene una aplicacion q al ejecutarla, este le manda una peticion a un PINPAD de que lea la tarjeta. Bueno el punto es que yo quiero obtener la informacion que capturo del pinpad y devolverla al .jnlp.
 
tengo un problema!!!, CREE UN CHAT EN JAVA Y LO PUSE EN UN SERVIDOR, Y TODOS LOS CLIENTES LO USAN MEDIANTE JNLP, LA VERDAD QUE ANDA MUY BIEN....PERO TENGO VARIOS USUARIOS A LOS CUALES SE LES ABRE LA APLICACION, EN LOS PROCESOS DEL SISTEMA CORRE EL JAVAW, PERO NO SE VE LA APLICACION!!! URGENTE!!! PORQUE!!!! YA SE ME QUEMARON LAS IDEAS!, PROBE DE TODO!
 
Hola a todos, para empesar exelente manual, muy bueno, bueno mi problema es
mi programa se conecta a una base de datos Firebird y lo hace perfectamente en modo escritorio, el problema es cuando lo pongo como aplicaccion web start, no se conecta para nada, aqui les pongo el codigo de la conexion


package interfaz;
/*
* Data.java
*
* Created on 20 de febrero de 2007, 06:31 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
import java.util.*;

import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Connection;
/**
*
* @author servidor
*/
public class Data {
public int conectado = 0;
public Connection conexion = null;
//---------------------------------------------------------------
public int estableceConexion(
String lca,
String lcb,
String lcc,
String lcd)
{
if (conexion != null){
return 0;
}
try
{
Properties props = new Properties();
props.setProperty("user", lcc);
props.setProperty("password", lcd);
props.setProperty("encoding", "ISO8859_1");


DriverManager.registerDriver(new org.firebirdsql.jdbc.FBDriver());
conexion = DriverManager.getConnection("jdbc:firebirdsql:"+lca+"/3050:"+lcb,props);

conectado=1;
return 1;
} catch (Exception e)
{
return 0;
}
}
//---------------------------------------------------------------
public void cierraConexion()
{
try
{
conexion.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
}


e llegado a la conclusion de q el problema es en esta parte

DriverManager.registerDriver(new org.firebirdsql.jdbc.FBDriver());

pero no tengo idea, por q en modo web start ocurre eso, ya q en modo escrito corre perfectamente, alguien tiene idea de q es lo q esta mal, tal vez el modo de conexionado, ademas no le puse las firmas, ya q use lo q me jenera el Netbeans, lo hace perfectamente, muestra todo menos la conexion a firebird, espero q alguien pueda ayudarme, gracias ???
 
Las aplicaciones JWS que accesan a Bases de datos funcionan como los hacen las aplicaciones web JSP ??

o es que solamente se publica en un servidor web para bajar la aplicacion y actualizarla ?
 
Hola, soy informatico y he visto tu articulo, ya que estoy trabajando en lo mismo y esta muy bueno, yo tengo una aplicacion sencilla en la cual la parte de java web start funciona perfecto, es decir la he probado en localhost y desde otro host en una red LAN y la aplicacion se levanta bien, pero tengo un problema con mi base de datos que es MySql, ya que cuando ejecuto la aplicacion por la web, los datos no se muestran, es como si no se conectara a la base de datos, pero cuando la ejecuto de forma normal, es dcir ejecutando el .Jar directamente, si muestra todos los datos, necesito que me ayuden con ese problema, muchas gracias.
 
Gracias, me ha servido mucho este tema
 
Publicar un comentario en la entrada



<< Home

This page is powered by Blogger. Isn't yours?