DWR

RECU-0138 (Recurso Referencia)

Descripción

DWR (Direct Web Remoting) también conocido como "AJAX fácil para Java" es una librería open source que facilita el acceso mediante JavaScript a métodos de clases Java que se ejecutan en el servidor. DWR consta de dos partes principales:

  • Un Servlet Java ejecutándose en el servidor, que procesa las peticiones y manda respuestas al navegador.
  • Código JavaScript ejecutándose en el navegador, que envía peticiones y puede actualizar la página dinámicamente.

DWR trabaja generando dinámicamente código Javascript basado en clases Java. Esta forma de trabajar con funciones remotas de Java en Javascript proporciona un mecanismo RPC, como RMI o SOAP, pero sin los inconvenientes de tener que instalar plugins en el navegador.

Como Java trabaja de forma síncrona mientras que Javascript lo hace de forma asíncrona, al hacer una llamada a un método remoto tenemos que proporcionar la función que será llamada cuando los datos estén disponibles. DWR genera dinámicamente una clase JavaScript que se corresponde con la clase Java en el servidor.

Integración con JSF

DWR contiene dos puntos de extensión para JSF:

  • JSF Creator: Consiste en añadir al fichero de configuración dwr.xml un creator de tipo “jsf”:
<allow>
  ...
  <create creator="jsf" javascript="ScriptName">
    <param name="managedBeanName" value="beanName"/>
    <param name="class" value="your.class"/>
  </create>
  ...
</allow>
  • Servlet Filter: El filtro DWR/Faces nos permite acceder a los beans desde nuestro FacesContext sin ser parte oficialmente del ciclo de vida de JSF. Para usar el JsfCreator se debe añadir el filtro DWR/Faces a nuestro fichero web.xml:
<filter>
  <filter-name>DwrFacesFilter</filter-name>
  <filter-class>uk.ltd.getahead.dwr.servlet.FacesExtensionFilter
  </filter-class>
</filter>

<filter-mapping>
  <filter-name>DwrFacesFilter</filter-name>
  <url-pattern>/dwr/*</url-pattern>
</filter-mapping>

Características

DWR tiene un gran número de características entre las que podemos destacar:

  • Es una librería RPC (Remote Procedure Call – Llamada a Procedimientos Remotos).
  • Soporta Reverse Ajax a partir de la version 2.0, lo que permite enviar datos de forma asíncrona desde el servidor web al navegador. Ésto se puede realizar de tres formas distintas:
    • Polling: el navegador hace peticiones al servidor en intervalos regulares y frecuentes (por ejemplo cada 3 segundos) para comprobar si se han realizado actualizaciones en la página.
    • Comet: el servidor responde a la petición del navegador lentamente, en cualquier momento, y no sólo en respuesta a un user input. Esto permite mantener un canal de comunicación abierto entre cliente y servidor.
    • PiggyBack: en este caso, cuando el servidor tiene una actualización para enviar al cliente, espera a la próxima vez que el navegador le hace una petición, enviándole entonces la respuesta a dicha petición y la actualización pendiente.
  • DWR selecciona el mejor método en cada momento de forma transparente al programador.
  • Puede dinámicamente generar JavaScript desde una API Java. Esto se hace en tiempo de ejecución y no de compilación, lo que nos permite usarlo para un control remoto de muchos navegadores. Esto permite crear cualquier aplicación dinámica, como aplicaciones de chat. Los mensajes se enviarán a los clientes usando Reverse Ajax.
  • Permite el manejo de excepciones.
  • Incluye librerías JavaScript como:
    • engine.js: gestiona todas las comunicaciones con el servidor.
    • util.js: para la manipulación de código HTML.
    • gi.js: ayuda a la integración de DWR con TIBCO GI.
  • Ayuda a proteger nuestro sitio web contra los ataques CSRF (Cross-Site Request Forgery).
  • Profunda integración con tecnologías Java del lado del servidor como Spring.

Buenas prácticas y recomendaciones de uso

Se recomienda seguir los siguientes ejemplos de buenas prácticas:

Mejorar la carga de mensajes

No está recomendado emplear la función useLoadingMessage(): En su lugar, se recomienda usar el siguiente código como plantilla y personalizarlo según nuestros propósitos

function useLoadingMessage(message) {
  var loadingMessage;
  if (message) loadingMessage = message;
  else loadingMessage = "Loading";

  dwr.engine.setPreHook(function() {
    var disabledZone = $('disabledZone');
    if (!disabledZone) {
      disabledZone = document.createElement('div');
      disabledZone.setAttribute('id', 'disabledZone');
      disabledZone.style.position = "absolute";
      disabledZone.style.zIndex = "1000";
      disabledZone.style.left = "0px";
      disabledZone.style.top = "0px";
      disabledZone.style.width = "100%";
      disabledZone.style.height = "100%";
      document.body.appendChild(disabledZone);
      var messageZone = document.createElement('div');
      messageZone.setAttribute('id', 'messageZone');
      messageZone.style.position = "absolute";
      messageZone.style.top = "0px";
      messageZone.style.right = "0px";
      messageZone.style.background = "red";
      messageZone.style.color = "white";
      messageZone.style.fontFamily = "Arial,Helvetica,sans-serif";
      messageZone.style.padding = "4px";
      disabledZone.appendChild(messageZone);
      var text = document.createTextNode(loadingMessage);
      messageZone.appendChild(text);
    }
    else {
      $('messageZone').innerHTML = loadingMessage;
      disabledZone.style.visibility = 'visible';
    }
  });

  dwr.engine.setPostHook(function() {
    $('disabledZone').style.visibility = 'hidden';
  });
}

Debemos llamar a este método después de que la página se haya cargado (por ejemplo, no llamarlo antes de que el evento onload() se haya disparado) ya que crea un div oculto que contiene el mensaje cargado.La forma más fácil de hacer ésto es llamando al evento onload() como se muestra a continuación

<head>
  <script>
  function init() {
    dwr.util.useLoadingMessage();
  }
  </script>
  ...
</head>
<body onload="init();">
...

Hay casos en los que no podemos modificar fácilmente las etiquetas <head> o <body> (como ocurre por ejemplo en los CMS). En estos casos podemos usar el siguiente código:

<script>
    function init() {
          dwr.util.useLoadingMessage();
    }
    if (window.addEventListener) {
          window.addEventListener("load", init, false);
    }
    else if (window.attachEvent) {
          window.attachEvent("onload", init);
    }
    else {
          window.onload = init;
    }
</script>

La mayoría de estas funciones crean dinámicamente un div (con id=”disabledZone”) que contiene el mensaje. El código que hace que aparezca y desaparezca cada vez que ocurre una actividad Ajax es el siguiente:

dwr.engine.setPreHook(function() {
  $('disabledZone').style.visibility = 'visible';
});

dwr.engine.setPostHook(function() {
  $('disabledZone').style.visibility = 'hidden';
});

De forma similar, podemos cargar las imágenes:

function useLoadingImage(imageSrc) {
  var loadingImage;
  if (imageSrc) loadingImage = imageSrc;
  else loadingImage = "ajax-loader.gif";
  dwr.engine.setPreHook(function() {
    var disabledImageZone = $('disabledImageZone');
    if (!disabledImageZone) {
      disabledImageZone = document.createElement('div');
      disabledImageZone.setAttribute('id', 'disabledImageZone');
      disabledImageZone.style.position = "absolute";
      disabledImageZone.style.zIndex = "1000";
      disabledImageZone.style.left = "0px";
      disabledImageZone.style.top = "0px";
      disabledImageZone.style.width = "100%";
      disabledImageZone.style.height = "100%";
      var imageZone = document.createElement('img');
      imageZone.setAttribute('id','imageZone');
      imageZone.setAttribute('src',imageSrc);
      imageZone.style.position = "absolute";
      imageZone.style.top = "0px";
      imageZone.style.right = "0px";
      disabledImageZone.appendChild(imageZone);
      document.body.appendChild(disabledImageZone);
    }
    else {
      $('imageZone').src = imageSrc;
      disabledImageZone.style.visibility = 'visible';
    }
  });
  dwr.engine.setPostHook(function() {
    $('disabledImageZone').style.visibility = 'hidden';
  });
}

 

 

Pasar información extra a los callbacks

Un ejemplo del uso del "cierre" JavaScript para pasar parámetros a un método callback se muestra en el siguiente código. El método callback sería similar al siguiente código:

function callbackFunc(dataFromServer, dataFromBrowser) {
   ...
}

Se puede llamar al método de la siguiente forma:

var dataFromBrowser = ...;

// define an erasure function to store a reference to
// dataFromBrowser and to call dataFromServer
var callbackProxy = function(dataFromServer) {
  callbackFunc(dataFromServer, dataFromBrowser);
};

var callMetaData = { callback:callbackProxy };

Remote.method(params, callMetaData);

De esta forma, la función que estamos pasando como callback, no es el callback real sino un cierre que actúa como un proxy, para pasar los datos que se han añadido en el cliente. Podríamos escribirlo de forma más abreviada:

var dataFromBrowser = ...;
Remote.method(params, {
  callback:function(dataFromServer) {
    callbackFunc(dataFromServer, dataFromBrowser);
  }
});

Ejemplos

  • Estan incluidos dentro de las caracteristicas del recurso

Enlaces externos