Generando clientes de servicios web JAX-WS desde Java.

Hace poco publiqué una entrada donde comentaba la generación de clientes de servicios web JAX-WS desde .NET. Ha llegado el momento de hacer una entrada paralela para explicar el mismo procedimiento pero desde el punto de vista Java.

Existen varios métodos para generar clientes para un servicio web basado en tecnología JAX-WS 2.x, yo me voy a centrar en la herramienta wsimport que viene incluida en la implementación de referencia que Glassfish/Metro proporciona de la especificación.

La implementación de referencia.

La especificación JSR 224 – Java API for XML-Based Web Services establece las bases para trabajar con servicios web que utilizan XML para comunicarse. Como pasa con todas las especificaciones puede haber varias implementaciones, pero siempre hay una que se elige como la implementación de referencia (RI – Reference Implementation).

En el caso de ésta especificación la implementación de referencia la aporta Metro. La RI que yo voy a utilizar es la versión JAX-WS 2.1.9, por que se acerca mucho a la versión 2.1.5 que es la que incluye de saque el servidor de aplicaciones Weblogic 11g (parche 10.3.6), que es el que estoy utilizando para realizar estas pruebas.

En teoría, se podría utilizar cualquier versión de la implementación de referencia teniendo en cuenta que si difiere de la que incorpora weblogic habrá que incluir las librerías correctas en la aplicación y tal vez marcar la opción prefer-web-inf-classes en el descriptor weblogic.xml de nuestra aplicación.

La implementación de referencia se descarga de la página jax-ws.java.net (ver sección Links) y va empaquetada en un fichero JAR que si ejecutamos nos muestra un acuerdo de licencia y descomprime en la carpeta donde se ejecuta el contenido de la implementación.

La sintaxis de la herramienta wsimport

Los ejecutables de esta herramienta son insustancialmente diferentes en windows y en linux. En windows la herramienta está en \bin\wsimport.bat y en linux se puede encontrar en /bin/wsimport.sh.

La sintaxis en ambos sistemas es la misma:

wsimport [options] <wsdl>

<wsdl> indica una url que permita acceder al WSDL del servicio web para el que vamos a generar el cliente. Esta url puede ser tanto un recurso local, como un recurso obtenido mediante protocolo http.

[options] es el apartado donde se pueden incluir diferentes opciones que variarán el comportamiento de la herramienta. A continuación se presenta una lista completa de estas opciones:

  • -d <directory> : Indica el directorio de salida donde se dejan las clases compiladas. Si no se utiliza esta opción las clases compiladas se dejarán en el mismo directorio desde el que se llama a wsimport.
  • -b <path> : Añade ficheros XSD adicionales que se puedan necesitar en los binding jaxws/jaxb del servicio web.
  • -B <jaxbOption>
  • -catalog <file> : Especifica un fichero de catálogo que resuelve las referencias a entidades externas. Los formatos de catálogo soportados son: TR9401, XCatalog y OASIS XML Catalog.
  • -extension : Permite extensiones de terceros. Esta funcionalidad no está soportada por la especificación, por lo que el cliente generado puede no ser portable o permitir la interoperabilidad entre plataformas.
  • -help : Muestra una ayuda con el listado de las opciones.
  • -httpproxy:<host>:<port> : Si para acceder a la URL del WSDL que queremos generar hay que viajar a través de un proxy, con esta opción podremos indicar su configuración. Si no se rellena el puerto, por defecto será el 8080.
  • -keep : Si se incluye esta opción los fuentes que generan los compilados del cliente no se borran.
  • -p : Especifica el paquete java de las clases del cliente generado. Si se indica esta opción no se tendrán en cuenta; ni el nombre de paquete que puede incluirse en el wsdl, ni el nombre de paquete por defecto que se genera cuando no se indica esta opción.
  • -s <directory> : Especifica un directorio donde se guardan los ficheros de código fuente generados.
  • -verbose : Muestra los mensajes del compilador indicando las tareas que está realizando.
  • -version : Muestra un mensaje informativo con la versión de la implementación de referencia que se está utilizando.
  • -wsdllocation <location>
  • -target : Genera el código para la versión JAX-WS indicada. La versión 2.0 genera código compatible con la especificación JAX-WS 2.0.
  • -quiet: Elimina cualquier salida que se pueda generar. Útil para generaciones automatizadas de clientes.

Clases generadas

La herramienta wsimport genera las clases necesarias para poder invocar a las operaciones del servicio web de forma correcta. Las clases que se generan siguen siempre el siguiente criterio:

  • Clase PortType. Una clase que lleva el mismo nombre que el atributo name del elemento porttype del wsdl y contiene un método por cada operación definida con los elementos operation.
  • Clase Service. Una clase que lleva el mismo nombre que el atributo name del elemento service del wsdl. Esta clase accede al servicio web y permite instanciar la clase PortType.
  • Por cada operación definida en el portType
    • Tantas clases como sean necesarias para rellenar los Input
    • Tantas clases como sean necesarias para devolver el resultado de la operación
  • Clase ObjectFactory. Esta clase facilita la instanciación interna de las clases input y response.
  • Clase package-info. Anota el paquete java para que los objetos generados a partir del xsd del wsdl estén correctamente ubicados.

Una generación de ejemplo.

En la entrada Creando un servicio web mediante anotaciones JAX-WS utilizando un enfoque ascendente (bottom-up) generaba un servicio web calculadora que permitía hacer las operaciones aritméticas básicas: suma, resta, multiplicación y división. Este servicio web está publicado en un servidor de aplicaciones weblogic que tengo para pruebas, el WSDL de acceso es: http://localhost:7001/JaxWSEjemploWAR/CalculadoraService?WSDL. El ejemplo que voy a generar se basa en las operaciones que proporciona este servicio web.
Mediante Eclipse creo un proyecto java básico que servirá para contener las clases que van a hacer uso del cliente y las propias clases generadas con la herramienta wsimport. Este proyecto se encuentra en una carpeta local de mi ordenador, E:\srv\entorno\aplic\wk-pruebas-jaxws\JaxWSClientEjemplo\src. Añado la opción -d  informada con este directorio.
A la hora de realizar la generación del cliente es interesante guardar el código fuente generado. Incluyo la opción -keep.
Las clases generadas para mantener un orden quiero que se creen bajo el paquete java egv.jaxws.clientes.calculadora. Añado la opción -p con el paquete indicado.
Finalmente, me gustaría que el compilador me vaya indicando información sobre las operaciones que va realizando. Añado la opción -verbose.
Ejecución del comando wsimport para el servicio web CalculadoraService
Tras la ejecución se puede ver en el proyecto de Eclipse las clases generadas a través de la herramienta.
  • CalculadoraPortType. La clase que contiene los métodos con las operaciones del servicio web.
  • CalculadoraService. La clase que comunica con el servicio web y que permite instanciar la clase CalculadoraPortType.
  • Division y DivisionResponse. Las clases de entrada y salida para la operación division().
  • Multiplicacion y MultiplicacionResponse. Las clases de entrada y salida para la operación multiplicacion().
  • Suma y SumaResponse. Las clases de entrada y salida para la operación suma().
  • Resta y RestaResponse. Las clases de entrada y salida para la operación resta().
  • ObjectFactory. Clase que permite utilizar internamente las clases de entrada y salida.
  • package-info. Clase que indica el paquete sobre el que se han generado las clases del cliente.
Cliente generado por wsimport en proyecto de Eclipse

Finalmente, he generado una clase de ejemplo CalculadoraCompra.java que calcula mediante operaciones aritméticas el resultado de la lista de la compra. Como el servicio web solo permite manejar enteros, las operaciones no manejan decimales y el resultado no es muy exacto, pero es ilustrativo del uso del servicio web.

public static void main(String[] args) {
  Integer pan = new Integer (1);
  Integer leche = new Integer (2);
  Integer carne = new Integer (6);
  Integer lentejas = new Integer (3);

  Integer totalCompra = new Integer(0);

  CalculadoraService cs = new CalculadoraService();
  CalculadoraPortType cpt = cs.getCalculadoraPortTypePort();
  totalCompra = cpt.suma(pan, leche);
  totalCompra = cpt.suma(totalCompra, carne);
  totalCompra = cpt.suma(totalCompra, lentejas);

  Integer ivaDeLaCompra = new Integer(0);
  Integer totalCompraConIva = new Integer(0);
  Integer iva = new Integer(21);

  ivaDeLaCompra = cpt.multiplicacion(totalCompra, iva);
  ivaDeLaCompra = cpt.division(ivaDeLaCompra, new Integer(100));
  totalCompraConIva = cpt.suma(totalCompra, ivaDeLaCompra);

  System.out.println("Pan: " + pan.toString() + "€");
  System.out.println(" + ");
  System.out.println("Leche: " + leche.toString() + "€");
  System.out.println(" + ");
  System.out.println("Carne: " + carne.toString() + "€");
  System.out.println(" + ");
  System.out.println("Lentejas: " + lentejas.toString() + "€");
  System.out.println(" = ");
  System.out.println("Total SIN IVA: " + totalCompra.toString() + "€");
  System.out.println("Total PVP (" + iva.toString() + "%): " + totalCompraConIva.toString() + "€");
}

El único punto en el que voy a hacer incapié es para subrayar los pasos necesarios para invocar a las operaciones del servicio web que se encuentran en la clase CalculadoraPortType. Esta clase es un interfaz por lo que no se puede crear una instancia, la forma correcta de obtenerla es instanciar la clase del servicio web CalculadoraService y posteriormente obtener el porttype con el método correspondiente getCalculadoraPortTypePort().

CalculadoraService cs = new CalculadoraService();
CalculadoraPortType cpt = cs.getCalculadoraPortTypePort();

El resultado de la ejecución del ejemplo se puede ver en la siguiente imagen.

Resultado de la ejecución del ejemplo que hace uso del cliente de servicios web.

Links

Internacionalizando aplicaciones con Java. Visión básica.

La internacionalización, también conocida como i18n (es curioso, pero en inglés hay 18 letras entre la letra «i» y la letra «n» de la palabra internacionalization), es la habilidad de una aplicación para adaptarse a distintos idiomas sin tener que realizar cambios en el código fuente.

Las características principales que debe cumplir una aplicación internacionalizada son:

  • con un mismo ejecutable de la aplicación se debe poder ejecutar la aplicación en distintos idioma simplemente cambiando la información de localización regional del usuario.
  • los textos susceptibles de cambiar de idioma no pueden incluirse directamente en el código. A cambio en el código aparecerá una variable que servirá para identificar el verdadero texto a mostrar.
  • soportar nuevos idiomas no debe requerir la re-compilación de la aplicación.
  • la información que pueda depender de la configuración regional del usuario aparecerá con el formato adecuado. Por ejemplo, moneda, números, fechas, etcétera.
  • el diseño de la aplicación (desde un inicio) debe estar orientado a la internacionalización.

En un alto porcentaje de los proyectos en los que participo conviven Euskera y Castellano, por lo que es muy necesario tener claros los conceptos básicos de la internacionalización.

Mi intención en esta entrada es, a través de un ejemplo, explorar los conceptos más básicos de i18n, entendiendo las ventajas que proporciona y la manera en la que lo hace.

La aplicación de ejemplo.

La aplicación de ejemplo que voy a utilizar para ilustrar el uso de la internacionalización recoge un nombre por teclado y lo escribe en la consola. Es un ejemplo muy simple pero muy útil para mostrar el funcionamiento de i18n.

La aplicación en funcionamiento.

El código fuente de la aplicación, sin internacionalizar es una clase verdaderamente simple; BasicConsoleLogin.java. Consta de dos métodos, uno privado y otro publico, los dos estáticos. El método privado readInput() es el encargado de leer el nombre introducido por el usuario a través del teclado. El método publico main(String[] args) contiene el código funcional de la aplicación.

/**
 * <p>Recoge la entrada del usuario vía teclado. </p>
 * @return java.lang.String la entrada insertada por teclado.
 */
 private static String readInput()
 {
   BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
   String s = new String("");
   try {
     s = bufferRead.readLine(); 
   } catch (IOException e) {
     s = getStackTrace(e);
   }
   return s;
 }

 public static void main(String[] args) {
    System.out.println("Introduce tú nombre:");
    String nombre= readInput();
    System.out.println("Tu nombre es " + nombre);
    System.out.println("Muchas gracias por darme uso");
 
 }

Paso 1:  creación de los bundles de internacionalización.

A través de la internacionalización, el texto de la aplicación que es susceptible de ser traducido a diferentes idiomas se organiza en ficheros de propiedades, uno por idioma.

Estos ficheros se conocen como ‘bundles’ y contienen múltiples filas que constan de una estructura clave=mensaje_traducido. La clave aparecerá en todos los bundles y el mensaje_traducido variará conteniendo la cadena de texto que corresponde con el idioma.

Para entender lo anterior lo mejor es explicarlo con los ‘bundles’ que voy a utilizar en la aplicación de ejemplo:

Bundle: BasicConsoleLoginMessages_es_ES.properties

LanguageSelection = El idioma elegido es:
InputMessage = Introduce tú nombre:
OutputMessage = Tu nombre es 
AcknowledgeMessage = Muchas gracias por darme uso.

Bundle: BasicConsoleLoginMessages_eu_ES.properties

LanguageSelection = Hizkuntza hau aukeratu duzu:
InputMessage = Idatzi zure izena:
OutputMessage = Zure izena ... da: 
AcknowledgeMessage = Eskerrik asko erabiltzeagatik!

Bundle: BasicConsoleLoginMessages_en_UK.properties

LanguageSelection = The Language chosen is:
InputMessage = Please, type your name:
OutputMessage = Your name is 
AcknowledgeMessage = Thanks for using me!

La aplicación de ejemplo está traducida a tres idiomas: castellano (es_ES), euskera (eu_ES) e inglés (en_UK).

Por cada idioma hay un ‘bundles’ que contienen pares de valores clave = traducción. La clave es igual en todos los ‘bundles’ independientemente del idioma, sin embargo, la traducción es propia de cada uno. Por ejemplo, el mensaje que se usa para animar al usuario a teclear su nombre se llama InputMessage. Este nombre será el que se utilice en la aplicación y en cada ‘bundle’ tendrá una traducción distinta.

Paso 2: Recoger el idioma en el que se ejecuta la aplicación.

Para establecer el idioma en el que el usuario quiere ver la aplicación, inicialmente se pregunta por el idioma. El usuario podrá elegir entre castellano, euskera e inglés, y con esa información la aplicación continuará ejecutándose en el idioma elegido.

La aplicación en euskera.

Normalmente, las aplicaciones suelen recoger la configuración de idioma de la configuración regional del sistema operativo o pueden incorporar un icono en pantalla para el cambio de idioma. Existen múltiples métodos.

En nuestra aplicación de ejemplo, el usuario teclea el idioma que desea. La información que teclea el usuario sirve para crear una instancia de la clase java.util.Locale que usaremos para acceder al ‘bundle’ correspondiente del idioma elegido. El método getLocale(String appIdioma) muestra esta manera de proceder.

private static Locale getLocale(String appIdioma)
 {
   Locale locale;
   if ("es_ES".equals(appIdioma)) {
     locale = new Locale("es", "ES");
   } else if ("eu_ES".equals(appIdioma)) {
     locale = new Locale("eu", "ES");
   } else if ("en_UK".equals(appIdioma)) {
     locale = new Locale("en", "UK");
   } else {
     locale = new Locale("eu", "ES");
   }
   return locale;
 }

Paso 3. Internacionalizar la aplicación

El último paso es adaptar la aplicación para ser internacionalizada. El código fuente internacionalizado quedaría así:

public static void main(String[] args) {
  System.out.println("Sartu aplikazioaren hizkuntza / Introduce el idioma de la aplicación / Tell us application's language (eu_ES|es_ES|en_UK): ");
 
  String appIdioma = readInput();
 
  Locale miLocale = getLocale(appIdioma);
  ResourceBundle rb = ResourceBundle.getBundle("BasicConsoleLoginMessages", miLocale);
 
  System.out.println(rb.getString("LanguageSelection") + miLocale.getLanguage() + "_" + miLocale.getCountry());
  System.out.println(rb.getString("InputMessage"));
  String nombre= readInput();
 
  System.out.println(rb.getString("OutputMessage") + nombre);
  System.out.println(rb.getString("AcknowledgeMessage"));
 
 }

El punto clave es la recuperación del bundle correspondiente al idioma que elija el usuario. Esta recuperación se hace a través de la clase java.util.ResourceBundle, el único requisito es que los ficheros de los ‘bundles’ sean accesibles desde el classloader de la aplicación.

 ResourceBundle rb = ResourceBundle.getBundle("BasicConsoleLoginMessages", miLocale);

Una vez accedido el bundle, recuperar los mensajes de texto es una sencilla llamada al método getString(String clave);

rb.getString("OutputMessage")

Conclusión

La internacionalización no aporta excesiva dificultad adicional al código y permite traducir las aplicaciones a diferentes idiomas de forma muy sencilla.

Esta aplicación que he utilizado en el ejemplo es una aplicación excesivamente sencilla y no ilustra la dificultad que puede llegar a ser internacionalizar una aplicación no preparada para ello. En las aplicaciones complejas se hace necesario sino obligatorio un diseño inicial i18n, ya que incorporarlo posteriormente puede darnos más de un quebradero de cabeza.

Links

Creando un servicio web mediante anotaciones JAX-WS utilizando un enfoque ascendente (bottom-up)

Para la programación de servicios web, independientemente de la tecnología, ya sea Java, ya sea .NET, se pueden utilizar dos enfoques distintos; crear el servicio web a partir de un WSDL ya existente (enfoque top-down o descendente), o generar el correspondiente WSDL a partir de código fuente ya existente (enfoque bottom-up o ascendente).

El objetivo de esta entrada es generar un servicio web mediante el enfoque bottom-up o ascendente y utilizando la tecnología JAXWS. En concreto, en esta entrada se utilizan las anotaciones de la especificación JSR-181.

¿Qué necesitas para seguir esta entrada?

Me gustaría aclarar que no voy a utilizar para la generación de este servicio web ninguna ayuda (wizard) de las que proporcionan las IDEs Java que hay en el mercado, por tanto, cualquiera de éstas herramientas sirve para desarrollar el ejemplo.

En este caso, y porque ya lo tengo instalado, voy a utilizar Eclipse 3.7.1 (Helios) con el plugin de Oracle (OEPE) para realizar un rápido despliegue en un dominio básico de Weblogic que he creado para esta ocasión.

El servidor de aplicaciones sobre el que voy a desplegar y publicar el servicio web va a ser el parche 10.3.6 de Weblogic 11g.

El dominio básico de Weblogic creado para estas pruebas tiene una única instancia que nos servirá tanto para publicar la consola administrativa del servidor, como la aplicación de ejemplo que voy a generar.

La JDK instalada es la versión 1.6.0_29 del Hotspot de Oracle.

Los componentes de la prueba

Los servicios web, en java, se publican en aplicaciones web dinámicas y se empaquetan en ficheros WAR. Teniendo esto en cuenta, para esta prueba voy a crear un proyecto Web Dinámico que a su vez voy a incluir en una aplicación JEE empaquetada en un EAR.

El motivo de incluir el módulo WAR en un EAR es simplemente una decisión personal del grupo «porquemeapetece«. En realidad, simplemente con el módulo WAR ya nos bastaría para desplegar y publicar el servicio web en el servidor de Weblogic.

La aplicación EAR la voy a llamar AppEjemploEAR y el módulo WAR lo voy a llamar JaxWSEjemploWAR.

La clase java de ejemplo.

La lógica de negocio que quiero convertir en servicio web es una calculadora muy básica compuesta por cuatro métodos: suma, resta, multiplicacion y division, que realizan las operaciones aritméticas correspondientes entre dos operandos.

La implementación de este servicio es la clase CalculadoraService.java que se encuentra en el directorio de fuentes de la aplicación WAR.

package ejemplo.jaxws; 
/**
 * @author EGV
 * <p>Clase calculadora que proporciona las cuatro operaciones aritméticas 
 * básicas: suma, resta, multiplicación y división.</p> 
 */
public class CalculadoraService {

  /**
   * Realiza la suma de los operandos.
   * @param operando1 Operando uno.
   * @param operando2 Operando dos.
   * @return Operando1 + Operando2.  
   */
  public Integer suma (Integer operando1, Integer operando2)
  {
    return operando1 + operando2;
  }

  /**
   * Realiza la resta de los dos operandos.
   * @param operando1 Operando uno.
   * @param operando2 Operando dos.
   * @return Operando1 - Operando2
   */
  public Integer resta (Integer operando1, Integer operando2)
  {
    return operando1 - operando2; 
  }

  /**
   * Realiza la multiplicación de los dos operandos.
   * @param operando1 Operando uno.
   * @param operando2 Operando dos.
   * @return Operando1 * Operando2
   */
  public Integer multiplicacion (Integer operando1, Integer operando2)
  {
    return operando1 * operando2;
  }

  /**
   * Realiza la división entre los operandos.
   * @param operando1 Operando1
   * @param operando2 Operando2
   * @return Operando1 / Operando2   
   */
  public Integer division (Integer operando1, Integer operando2)  
  {
    return operando1 / operando2;
  }

  /**
  * @param args
  */
  public static void main(String[] args) {
    Integer operando1 = new Integer(10);
    Integer operando2 = new Integer(3);
    Integer resultado;
    CalculadoraService calculadora = new CalculadoraService();
    StringBuffer bsTraza;

    resultado = calculadora.suma(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("+").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

    resultado = calculadora.resta(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("-").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

    resultado = calculadora.multiplicacion(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("*").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

    resultado = calculadora.division(operando1, operando2);
    bsTraza = new StringBuffer();
    bsTraza.append(operando1).append("/").append(operando2).append("=").append(resultado);
    System.out.println(bsTraza.toString());

  }

}

Las anotaciones básicas para definir Servicios Web.

La especificación JSR-181 define una serie de anotaciones que permiten generar, a partir de una clase java, un servicio web. Gracias a estas anotaciones el desarrollo del servicio web se simplifica enormemente, además, las anotaciones de esta especificación están incluidas en la JDK a partir de la versión 1.5 y sucesoras, por tanto, incluidas en mi versión 1.6.0_29.

Las anotaciones que voy a utilizar son las siguientes:

  • @WebService –> javax.jws.WebService
  • @WebMethod –> javax.jws.WebMethod
  • @WebParam –> javax.jws.WebParam
  • @WebResult –> javax.jws.WebResult
  • @SOAPBinding –> javax.jws.soap.SOAPBinding

Las anotaciones @WebParam, @WebResult y @SOAPBinding las incluyo para dejar constancia de su funcionamiento, en verdad para este ejemplo tan básico no serían necesarias ya que la configuración por defecto, es la más adecuada.

La clase java de ejemplo anotada.

La clase CalculadoraService.java con las anotaciones quedaría de la siguiente forma:

package ejemplo.jaxws;
import javax.jws.WebMethod;
 import javax.jws.WebParam;
 import javax.jws.WebResult;
 import javax.jws.WebService;
 import javax.jws.soap.SOAPBinding;
/**
* @author EGV
* <p>Clase calculadora que proporciona las cuatro operaciones aritméticas
* básicas: suma, resta, multiplicación y división.</p>
* <p>Esta clase se anota con la especificación JSR-181 para convertirla
* en un servicio web.</p>
*/
@WebService(serviceName="CalculadoraService",
  name="CalculadoraPortType",
  targetNamespace="http://egv.com/ejemplo-jaxws/calculadora")
@SOAPBinding( style=SOAPBinding.Style.DOCUMENT,
  use=SOAPBinding.Use.LITERAL, 
  parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
public class CalculadoraService {
  /**
  * Realiza la suma de los operandos.
  * @param operando1 Operando uno.
  * @param operando2 Operando dos.
  * @return Operando1 + Operando2.
  */
  @WebMethod( operationName="suma",
    action="sumaAction")
  @WebResult( name="sumaResponse")
  public Integer suma (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 + operando2;
  }

  /**
  * Realiza la resta de los dos operandos.
  * @param operando1 Operando uno.
  * @param operando2 Operando dos.
  * @return Operando1 - Operando2
  */
  @WebMethod( operationName="resta",
    action="restaAction")
  @WebResult( name="restaResponse")
  public Integer resta (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 - operando2;
  }

  /**
  * Realiza la multiplicación de los dos operandos.
  * @param operando1 Operando uno.
  * @param operando2 Operando dos.
  * @return Operando1 * Operando2
  */
  @WebMethod( operationName="multiplicacion",
    action="multiplicacionAction")
  @WebResult( name="multiplicacionResponse")
  public Integer multiplicacion (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 * operando2;
  }

  /**
  * Realiza la división entre los operandos.
  * @param operando1 Operando1
  * @param operando2 Operando2
  * @return Operando1 / Operando2
  */
  @WebMethod( operationName="division",
    action="divisonAction")
  @WebResult( name="divisionResponse")
  public Integer division (
    @WebParam(name="operando1", mode=WebParam.Mode.IN) Integer operando1,
    @WebParam(name="operando2", mode=WebParam.Mode.IN) Integer operando2)
  {
    return operando1 / operando2;
  }

Las anotaciones @WebService y @SOAPBinding son generales al servicio web generado, por lo que se asocian a la clase. Las anotaciones @WebMethod y @WebResult están relacionadas con el método y por tanto hay una por cada método a publicar. La anotación @WebParam está relacionada a los parámetros, por lo que habrá una por cada parámetro que haya en los métodos.

Desplegando, publicando y probando el servicio web.

Una vez que la clase java que queremos publicar como servicio web está anotada, desplegamos la aplicación EAR en el servidor de Weblogic.

Si hemos hecho correctamente la anotación, el servidor desplegará AppEjemploEAR con el módulo JaxWSEjemploWAR dentro y el servicio web CalculadoraService publicado.

Vista Despliegue AppEjemploEAR

Para probar el servicio web se puede hacer de varias maneras; generando un cliente de web service, utilizando la herramienta de test de servicios web proporcionada por Weblogic, utilizando herramientas externas como SOAPUi, etcétera.

En la imagen se puede ver el cliente que proporciona el servidor de Weblogic. Si en la vista de despliegue se pincha en el servicio web a probar, en este caso CalculadoraService, nos abrirá la pantalla con la configuración propia de este servicio. En la pestaña de Prueba existe un Punto de Prueba que se llama Test 

Client que nos llevará a una página web que nos permitirá probar las diferentes operaciones de CalculadoraService.

Cliente Test de CalculadoraService

El WSDL generado

El WSDL del servicio web es generado a partir de la clase anotada.

Podemos observar como las anotaciones que hemos incluido en el código Java se reflejan en los diferentes apartados del documento WSDL.

<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. -->
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. -->
<definitions 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://egv.com/ejemplo-jaxws/calculadora" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://egv.com/ejemplo-jaxws/calculadora" name="CalculadoraService">
  <types>
    <xsd:schema>
      <xsd:import namespace="http://egv.com/ejemplo-jaxws/calculadora" schemaLocation="http://172.16.204.140:7001/JaxWSEjemploWAR/CalculadoraService?xsd=1"/>
    </xsd:schema>
  </types>
  <message name="suma">
    <part name="parameters" element="tns:suma"/>
  </message>
  <message name="sumaResponse">
    <part name="parameters" element="tns:sumaResponse"/>
  </message>
  <message name="resta">
    <part name="parameters" element="tns:resta"/>
  </message>
  <message name="restaResponse">
    <part name="parameters" element="tns:restaResponse"/>
  </message>
  <message name="multiplicacion">
    <part name="parameters" element="tns:multiplicacion"/>
  </message>
  <message name="multiplicacionResponse">
    <part name="parameters" element="tns:multiplicacionResponse"/>
  </message>
  <message name="division">
    <part name="parameters" element="tns:division"/>
  </message>
  <message name="divisionResponse">
    <part name="parameters" element="tns:divisionResponse"/>
  </message>
  <portType name="CalculadoraPortType">
    <operation name="suma">
      <input message="tns:suma"/>
      <output message="tns:sumaResponse"/>
    </operation>
    <operation name="resta">
      <input message="tns:resta"/>
      <output message="tns:restaResponse"/>
    </operation>
    <operation name="multiplicacion">
      <input message="tns:multiplicacion"/>
      <output message="tns:multiplicacionResponse"/>
    </operation>
    <operation name="division">
      <input message="tns:division"/>
      <output message="tns:divisionResponse"/>
    </operation>
  </portType>
  <binding name="CalculadoraPortTypePortBinding" type="tns:CalculadoraPortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="suma">
      <soap:operation soapAction="sumaAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="resta">
      <soap:operation soapAction="restaAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="multiplicacion">
      <soap:operation soapAction="multiplicacionAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="division">
      <soap:operation soapAction="divisonAction"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="CalculadoraService">
    <port name="CalculadoraPortTypePort" binding="tns:CalculadoraPortTypePortBinding">
      <soap:address location="http://172.16.204.140:7001/JaxWSEjemploWAR/CalculadoraService"/>
    </port>
  </service>
</definitions>

Los XML Request y Response de la operación Suma.

Para invocar a la operación Suma del servicio web CalculadoraService se envía el XML de petición de servicio. Como respuesta, el servicio web enviará otro XML con la información de la operación. Estos XMLs también están influidos por las anotaciones de la clase Java anotada.

XML Request

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header />
  <env:Body>
    <suma xmlns="http://egv.com/ejemplo-jaxws/calculadora">
      <!--Optional:-->
      <operando1 xmlns="">10</operando1>
      <!--Optional:-->
      <operando2 xmlns="">13</operando2>
    </suma>
  </env:Body>
</env:Envelope>

XML Response

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <S:Body>
    <ns2:sumaResponse xmlns:ns2="http://egv.com/ejemplo-jaxws/calculadora">
      <sumaResponse>23</sumaResponse>
    </ns2:sumaResponse>
  </S:Body>
</S:Envelope>

Conclusión

Si disponemos de una clase java, bien sea un POJO, o bien sea un Entreprise Java Bean, que contiene lógica de negocio que quisiéramos publicar para su utilización por otras aplicaciones en una arquitectura SCA (orientada a componentes), una buena idea es convertir de manera muy sencilla esa clase en un servicio web mediante las anotaciones que proporciona la especificación JSR-181.
Esta especificación se incluyó por primera vez en la JDK 1.5 y permite que una aplicación EAR automáticamente despliegue y publique servicios web.
Estas anotaciones se incluyen en la clase Java y definen el contrato WSDL del servicio web.
En siguientes entradas iré comentando con más en detalle el uso de las anotaciones JAX-WS para manipular el WSDL generado, haciendo un uso avanzado además de las anotaciones relacionadas con JAXB para la manipulación de los XML de las operaciones.

Links

Eclipse Juno y la configuración fallida del proxy

Este post, cuyo título puede sonar a película de ciencia ficción, es un problema que todos aquellos que trabajamos con Eclipse, más tarde o más temprano nos encontramos a la hora de conectarnos a internet en aquellos sitios donde la conexión no se realiza directamente sino que se hace a través de un proxy.

Este es el típico problema de conexión que deriva en una excepción InvocationTargetException al acceder al MarketPlace cuando queremos instalar aquellos plugins necesarios para los desarrollos a llevar a cabo. En ese momento comienzan un sin fin de pruebas de configuración del proxy que puede que consigan, o no, solucionar la conectividad.

La configuración del proxy de Eclipse en el arranque.

Tras mucho renegar con las preferencias de la conexión vía proxy, probar las configuraciones manual, nativa, directa y combinaciones varias de las anteriores; después de modificar la propia configuración del sistema operativo y jugar con las variables de entorno de manera infructuosa; por fin he encontrado un método que hace que Eclipse funcione sí o sí a través del proxy sin quebraderos de cabeza.

La solución se realiza añadiendo la configuración del proxy en el fichero eclipse.ini.

Este fichero se encuentra en el mismo directorio del ejecutable, en la carpeta home de la instalación, y en él se pueden añadir parámetros que modifican en el arranque la funcionalidad de Eclipse. Los parámetros que determinan la configuración del proxy son algunos de los que se pueden establecer en el arranque, las líneas concretas son las siguientes:

-Dhttp.proxyPort=<puerto_del_proxy>
-Dhttp.proxyHost=<nombre_dns_o_dir_ip_del_proxy>
-Dhttp.proxyUser=<nombre_usuario_proxy>
-Dhttp.proxyPassword=<password_usuario_proxy>
-Dhttp.nonProxyHosts=<excepciones_sin_proxy_separadas_por_|_pipe>

La contra de utilizar este solución es que en aquellas instalaciones en las que es necesario acceder al proxy de forma autenticada, al incluir el nombre del usuario y la contraseña como parámetros, si esa instalación se utiliza en una máquina virtual y se distribuye a un montón de desarrolladores, cada uno tiene que cambiar esos dos parámetros para incluir su usuario y contraseña propios.

Si lo anterior no es un problema, ésta solución funciona siempre, termina con los quebraderos de cabeza de las preferencias de Eclipse y sirve para cualquier plataforma, ya sea windows, linux o macintosh.

Links

Documentación Eclipse Juno – Network Connections how to. http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.platform.doc.user%2Freference%2Fref-net-preferences.htm

Tuning de Weblogic 11g (I)

Históricamente, y desde que migramos, en nuestra plataforma Linux, de Weblogic 8.1 a Weblogic 11g, el servidor en el que desplegamos nuestras aplicaciones tarda bastante en alcanzar el estado RUNNING.

Dentro de las tareas de optimización teníamos claro que había que aligerar el arranque de nuestro dominio en weblogic 11g. Descartando eliminar del dominio alguna de las aplicaciones desplegadas, nos pusimos a investigar donde se estaban perdiendo los tiempos.

Un estudio minucioso del arranque del dominio examinando las trazas nos mostró que durante un tiempo, más o menos largo dependiendo de la potencia de la máquina, weblogic quedaba parado aparentemente sin realizar ninguna operación. La última traza que se escribía, antes de la espera, era la siguiente:

<31-mar-2011 14H57’ CEST> <[STANDBY] ExecuteThread: ‘2’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1301576265940>

Las siguientes trazas, pasado el tiempo de espera, eran las siguientes:

<31-mar-2011 15H00’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589143>
 <31-mar-2011 15H00’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589205>
 <31-mar-2011 15H00’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589257>

Las trazas revelaban que entre el inicio del subsistema IIOP y el subsistema Security pasaba un tiempo de tres minutos en los que el servidor no mostraba ninguna traza. Así pues, un buen punto de partida era investigar en que se estaban empleando esos minutos de espera.

Consultando información sobre este problema en internet surgieron varios casos similares con sus respectivas soluciones.

La información corrupta de ldap.

El primer camino que investigué hablaba de corrupción en la información almacenada referente a ldap en el dominio. Esta información, supuestamente incorrecta, habría sido generada en un arranque previo.

La solución propuesta estaba basada en renombrar o directamente eliminar el directorio ldap almacenado en la instancia o instancias del servidor en el dominio. Este directorio ldap se puede encontrar en la ruta /servers//data.

Seguí este intento de solución, ya que las trazas escritas después de la parada mostraban el arranque de los recursos JPA (Java Persistence API) y JDO (Java Data Objects) cuya autenticación parecía estar basada en ldap.

Sin embargo, y tras eliminar el directorio y reiniciar el dominio, los tiempos ni mejoraron ni empeoraron, se mantuvieron similares.

Quedaba claro, por tanto, que esta no era la solución requerida.

La semilla de seguridad que germinaba tiempos de espera.

En esta bifurcación de soluciones, el siguiente camino a seguir hablaba de mal rendimiento provocado por el sistema de seguridad de java, cuya solución estaba basada en modificar el fichero java.security de la JRE con la que se arrancan los dominios.

La modificación a realizar en este fichero era sustituir la línea:

securerandom.source=file:/dev/urandom

por:

securerandom.source=file:/dev/./urandom

No entendía yo muy bien en que podía afectar este cambio al arranque de weblogic, así que escepticismo es el sentimiento que mejor definía mi estado. Sin embargo, como el subsistema que parecía estar dando los problemas era el de Security, me decidí a intentarlo.

Sorprendentemente, tras realizar el cambio y reiniciar el dominio, el tiempo de espera entre el arranque de los dos subsistemas desapareció.

<31-mar-2011 18H39’ CEST> <[STANDBY] ExecuteThread: ‘2’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1301576265940>
 <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589143>
 <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589205>
 <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589257>
 <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589510>

En las trazas se puede observar que el arranque de los dos subsistemas se realiza dentro del mismo minuto, un estudio más detenido demuestra que lo hace casi en el mismo segundo.

Por tanto, estaba claro que ésta era la solución para este retraso concreto.

Sin embargo, y pese a tener la solución, no me quedaba claro el por qué de la misma, así que seguí investigando.

/dev/urandom

De esta forma me surgieron preguntas para las que no tenía respuesta. Algunas de estás preguntas eran: ¿qué era /dev/urandom? ¿para qué lo estaba usando el sistema de seguridad de java? ¿por qué ralentizaba el proceso de autenticación?

Así que comencé a buscar respuestas.

La primera respuesta que encontré fue la utilidad de /dev/urandom, un archivo especial que se puede encontrar en los sistemas unix, que basándose en los bits que genera el ruido ambiental recogido de dispositivos obtiene números pseudoaleatorios. Estos números pseudoaleatorios son ideales para utilizar en la generación de claves. Si queréis saber más podéis leer la entrada sobre /dev/random de la wikipedia. Para comprobar que era eso de la generación de números aleatorios a partir del ruido de los dispositivos, se me ocurrió hacer un cat del fichero /dev/urandom. El resultado fue el siguiente: wpid-captura_de_pantalla_2011-04-05_a_las_20-thumb-51-37_sepia_sepia-2012-02-27-19-29.png



java.security.SecureRandom

La siguiente pregunta era saber para que estaba utilizando java el fichero /dev/urandom.

La respuesta la encontré en la clase java.security.SecureRandom. Esta es la clase que proporciona la generación de números aleatorios adecuados para implementar potentes sistemas de cifrado.

Cuando desde un desarrollo se utiliza esta clase, desde la variable securerandom.source del fichero //jre/lib/security/java.security, se recoge el sistema de generación de números aleatorios que java utilizará internamente para su generación.

La configuración, que por defecto se crea con la instalación de la jdk, es el fichero /dev/urandom como se puede comprobar en la jdk 1.6.0_21que tengo instalada en mi entorno de desarrollo.

Select the source of seed data for SecureRandom. By default an 
attempt is made to use the entropy gathering device specified by
the securerandom.source property. If an exception occurs when
accessing the URL then the traditional system/thread activity
algorithm is used.
 On Solaris and Linux systems, if file:/dev/urandom is specified and it
exists, a special SecureRandom implementation is activated by default.
This “NativePRNG” reads random bytes directly from /dev/urandom.
 On Windows systems, the URLs file:/dev/random and file:/dev/urandom
enables use of the Microsoft CryptoAPI seed functionality.
 securerandom.source=file:/dev/urandom

Entonces, ¿qué es lo que funciona mal?

Hasta aquí todo parece normal, ¿qué es entonces lo que no funciona?. La respuesta a esta pregunta la encontré en la base de datos de bugs que Sun (ahora Oracle) comparte en el portal bugs.sun.com. Al parecer y reportado desde la versión Java 5.0 existe el bug 6202721.

Este bug dice que si se utilizan las siguientes líneas de código en cualquier desarrollo java

 SecureRandom.getInstance(“SHA1PRNG”).nextLong());

la clase SecureRandom lee las semillas del fichero /dev/random en vez del fichero /dev/urandom configurado en la opción securerandom.source del fichero java.security.

Esta casuística fue introducida como corrección de otro bug (4705093) que no he sido capaz de encontrar en la base de datos. Al parecer, este bug le daba algún tipo de significado especial a la cadena /dev/urandom que impedía utilizarla siendo sustituida por /dev/random. La utilización de éste fichero es un problema porque proporciona un rendimiento muchísimo peor.

La solución.

Existen dos soluciones propuestas a este problema.

Si somos nosotros los que hemos incorporado en nuestro desarrollo las líneas de código que obtienen un número aleatorio basado en el algoritmo SHA1PRNG, bastará con sustituir las líneas:

 Long numeroAleatorio = SecureRandom.getInstance(“SHA1PRNG”).nextLong());

por las siguientes:

 Long numeroAleatorio = new SecureRandom().nextLong();

Sin embargo, si no tenemos acceso a los fuentes del código para cambiarlo, tal y como ocurre en el arranque del subsistema security en Weblogic; la solución es modificar el valor de la opción securerandom.source en el fichero java.securityde la instalación de la JRE con el siguiente valor:

 securerandom.source=file:/dev/./urandom

De esta forma, la JRE no detectará la cadena /dev/urandom y por tanto no procederá a sustituirla por /dev/random. Sin embargo, en unix, la ruta /dev/./urandom hace referencia al mismo fichero que la ruta /dev/urandom, por lo que el funcionamiento será el mismo y el rendimiento será el óptimo.

Y así, es como finaliza esta investigación que viene a demostrar las infinitas complejidades que surgen en los desarrollos basados en Java.

Espero que os haya sido útil e interesante.

Minientrada

Error increible de Java con Windows 7

En el mundillo Java los fallos a veces pasan, y a veces pasan sin ser culpa de Java. Y esas veces hay que celebrarlo, porque son las menos.

Coincidiendo con la migración de Windows XP a Windows 7 en los entornos de usuario, una de nuestras aplicaciones java realizada con tecnología RCP (Rich Client Platform), decidió sin previo aviso que no quería arrancar. Los síntomas eran claros, al pinchar sobre el ejecutable, en nuestra recién estrenada instalación de Windows 7, la aplicación comenzaba el arranque y se cerraba sin llegar a mostrar la ventana inicial. Lo que nos extrañaba es que la misma versión de la aplicación en Windows XP funcionara correctamente.

El error.

En una aplicación cliente, relativamente «compleja», como es ésta; con accesos a base de datos mediante JDBC, con clientes EJB que se conectan a EJBs publicados en un servidor J2EE, e informes generados con BIRT, muchas variables podían influir en el mal funcionamiento. Lo que no sospechaba, ni por asomo, es que algo tan tonto como el tipo de letra pudiera influir en una aplicación java, sin embargo la ejecución de la aplicación RCP era clara al respecto ya que las trazas dejaban el siguiente rastro:

java.util.MissingResourceException: Wrong font data format. Value is: «MS Sans Serif-negrita-12 «

at org.eclipse.jface.resource.FontRegistry.makeFontData(FontRegistry.java:767)

at org.eclipse.jface.resource.FontRegistry.readResourceBundle(FontRegistry.java:860)

at org.eclipse.jface.resource.FontRegistry.readResourceBundle(FontRegistry.java:342)

at org.eclipse.jface.resource.FontRegistry.(FontRegistry.java:286)

at org.eclipse.jface.resource.FontRegistry.(FontRegistry.java:308)

at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:342)

 

Windows 7 y los DPI de las fuentes.

 

Los monitores planos (LCD, TFT…) tienen un tamaño fijo de pixel o «resolución» con la que muestran los elementos en pantalla. Cuando un usuario, desde el sistema operativo, cambia a una resolución menor para ver más grande el tamaño de la letra, el monitor combina y ajusta los pixeles de tamaño fijo para conseguir la nueva resolución. Este proceso degrada la calidad de la imagen vista en pantalla haciendo que todo, no solo el texto, se vea peor.

Para evitar esto, Windows permite el aumento de tamaño de las fuentes y de las ventanas manteniendo la resolución nativa del monitor. Gracias a este método se obtienen imágenes más nítidas al poder utilizar el tamaño original de los píxeles para componerlas.

En Windows 7, para hacer que la resolución del sistema operativo corresponda con la resolución nativa de la pantalla y en ciertas resoluciones altas, para que las letras se muestren a un tamaño aceptable establece por defecto la configuración 120 DPI (dots per inch) de las fuentes. La configuración normal de las fuentes es 96 DPI. Esto implica que en 120DPI se usa un 125% de DPI más grande que lo habitual.

Si el usuario vuelve a 96 DPI, es decir, vuelve al tamaño de fuente normal, las fuentes TrueType, que permiten escalado, se ajustan automáticamente. Sin embargo, las fuentes basadas en bitmaps (imágenes), como MS Sans Serif, deben utilizar el fichero de fuentes adecuado a la resolución. Por tanto, en la instalación de Windows 7 debería existirá un fichero de fuentes para 96DPI y otro para 120DPI.

Sin embargo, si a la hora de instalar Windows 7 la resolución de pantalla es una resolución alta (por ejemplo, 1900×1200) se establece las fuentes por defecto a 120 DPI y no se instalan las fuentes 96 DPI ya que se considera que no se van a utilizar.

Por tanto, si se utiliza el tamaño 96DPI, se encontrará con que para ciertos tipos de letra los ficheros no existen, y como en nuestro caso, dará un error. La aplicación RCP utiliza un sistema de ventanas que lo independiza del sistema operativo. Este sistema de ventanas es conocido como JFace y está basado en SWT (Standar Widget Tookit). Este sistema utiliza las fuentes normales de 96DPI y todo junto provoca el fallo.

Las soluciones. Considerando las particularidades de nuestro entorno se puede optar por varias soluciones. A continuación se muestran en orden de prioridad:

 

  • Incluir las fuentes 96DPI en la instalación de Windows 7.
  • Cambiar el tipo de fuente que utiliza la aplicación RCP para que use una fuente TrueType (que no dan problemas en el escalado).
  • Parchear Windows 7 para que utilice las fuentes 120DPI cuando de fuentes bitmap se trata.

 

Incluir las fuentes 96DPI en la instalacion de Windows 7 Las fuentes en Windows 7 se instalan de diferentes maneras. El método más sencillo es descargar la fuente, descomprimirla, hacer doble click en el archivo .FON y finalmente hacer click en el botón «Instalar».

Las fuentes se pueden descargar desde los siguentes links:

Cambiar el tipo de fuente de la aplicación RCP

La aplicación RCP utiliza jFace para la implementación de la interfaz de usuario. Es en esta interfaz donde se configuran los tipos de fuente que se utilizan en la aplicación. La configuración se realiza en el fichero jfacefonts_es.properties que se encuentra en la ruta \\plugins\org.eclipse.jface.nl_es_0.2.0.v20080615043401\org\eclipse\jface\resource

Estos ficheros de propiedes contienen el tipo de fuentes que se utilizan en diferentes tipos de controles pertenecientes a la interfaz de usuario. Es en este fichero donde estará especificado el tipo de letra Sans Serif que tendremos que sustituir por un tipo de letra TrueType.

En nuestro caso el cambio realizado es el siguiente:

org.eclipse.jface.bannerfont.0=Tahoma-bold-8

org.eclipse.jface.headerfont.0=Tahoma-bold-12

Parchear Windows 7 para que utilice las fuentes 120DPI.

Parchear Windows 7 implica tocar el registro. Esta opción, aunque válida, no es recomendable pues lo que se hace es trampear el sistema. La trampa consiste en que para determinados casos, en vez de utilizar el fichero de fuentes que debería utilizarse (96DPI), que es el correcto pero no está instalado, se utilice el fichero de fuentes incorrecto pero existente en el sistema (el de 120DPI).

Aún así, dejo aquí la solución por si alguien tiene prisa en sacar adelante su programa y no tiene tiempo de aplicar las soluciones anteriormente recomendadas.

Lo primero que hay que hacer es acceder al registro de Windows 7 utilizando la herramienta regedit.

Una vez dentro del editor del registro se busca la clave MS Sans Serif 8,10,12,14,18,24 que se encuentra en la ruta HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts\.

El truco es cambiar el valor existente en esa entrada, SSERIFF.FON por el valor SSERIFE.FON

is set to SSERIFF.FON on a system which started at 125% DPI. The setting is set to SSERIFE.FON on a system which started at 100% DPI. Notice that one character of the file name changes from F to E.

The actual font files used might be different on Windows systems for other languages or code pages. See the table below for the file names:

While we are fixing the MS Sans Serif font, we can also fix the MS Serif and Courier fonts. These are the Registry settings MS Serif 8,10,12,14,18,24 and Courier 10,12,15 values in the same registry key. See the table below for the file names:

To fix the system you need to change the settings to new file names (changing the appropriate letter from F to E) and reboot.

Note: The registry changes do not take effect until the system has been restarted. You MUST reboot after making the registry changes.

Below are the contents of Font Fix.reg file to make the changes for an English system:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts] «MS Sans Serif 8,10,12,14,18,24″=»SSERIFE.FON» «MS Serif 8,10,12,14,18,24″=»SERIFE.FON» «Courier 10,12,15″=»COURE.FON»

Links. Windows 7, bitmap fonts and Microsoft Dynamics GP