Blog

En nuestro blog queremos explicar las cosas que nos pasan, los proyectos que realizamos, compartir nuestra experiencia como empresa para aprender de la experiencia de nuestros lectores.

JSON-RPC-Java: Capa de abstracción para comunicaciones AJAX

Desde hace un tiempo, en diferentes proyectos, utilizamos esta librería, que simplifica enormemente el desarrollo de aplicaciones Web que hacen uso del archiconocido Ajax. Os explicaremos e este articulo como instalarlo y empezar a usarlo.

JSON-RPC-Java permite al programador JavaScript acceder de forma transparente a las funciones de servidor de una aplicación Web J2EE.

Para empezar a usar JSON-RPC-Java debemos definir cuales son las clases que deseamos que se puedan acceder desde JavaScript. Usaremos la siguiente, por ejemplo:

package com.ateneaTech.jsonrpcjava;


public class Ejemplo {

public String foo() {

return "Hola, foo!";

}

}

El siguiente paso es registrar esta clase. Solo es necesario registrar una clase una vez en cada sesión. Aunque existen formas de registrar clases desde un JSP, nosotros recomendamos, por claridad en el código, hacerlo en un servlet. Para ello utilizamos la clase JSONRPCBridge que nos permite registrar nuestra clase en el RPC. Mantendremos una instancia de JSONRPCBridge por cada sesión, con el código:

HttpSession session = request.getSession();
JSONRPCBridge json_bridge = null;
json_bridge = (JSONRPCBridge) session.getAttribute("JSONRPCBridge");
if(json_bridge == null) {

json_bridge = new JSONRPCBridge();
session.setAttribute("JSONRPCBridge", json_bridge);

}

Para registrar nuestra clase debemos añadir una sola linea al serlvet:

json_bridge.registerObject("ejemplo", new Ejemplo());

Para comunicar JavaScript con Java se utiliza un servlet que se debe registrar:

<servlet>

<servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
<servlet-class>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-class>

</servlet>
<servlet-mapping>

<servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
<url-pattern>/JSON-RPC</url-pattern>

</servlet-mapping>

Ya tenemos configurado nuestro servidor, vamos ahora a por el código de cliente, que es sumamente sencillo. Primero tenemos que enlazar el script a nuestra página:

<script type="text/javascript" src="jsonrpc.js">

En el código JavaScript se debe crear una instancia del objeto JSONRpcClient:
var jsonrpc = new JSONRpcClient("/<nombre de la aplicación>/JSON-RPC");
y llamar a una de las funciones definidas en las clases registradas de forma síncrona:

alert(jsonrpc.ejemplo.foo());

o de forma asíncrona:

jsonrpc.ejemplo.foo(function(result, exception) {

alert(result);

});

Como podéis ver hemos realizado una conexión AJAX y hemos interaccionado con una clase Java de forma transparente.

Otras las ventajas de este sistema és la serialización de objetos Java a objetos JavaScript. Si devolvemos un objeto Java podremos acceder como si se tratara de un objeto JavaScript a sus propiedades publicas.

La comunicación y el serialización se realizan a través de Json que permite ahorrar ancho de banda y tiempo para su lectura en cliente en contraposición a Java.

Nada más por ahora, si tenéis alguna pregunta no dudéis en comentar

Comentarios

Hola, he intentado aplicar

Hola,

he intentado aplicar éste método a mi aplicación implementada en JSF.Pero me encuentro con un error de javascript a la hora de la ejecución.
Mi código del servidor es el siguiente:

json_bridge=new JSONRPCBridge();
locale=new Locale("es","ES");
this.setLocale(locale);
json_bridge.registerObject("idioma",this.getLocale());

Éste código se encuentra en un backing bean de sesión.Y el código javascript insertado en la págona es el siguiente:

var jsonrpc = new JSONRpcClient("/GestorIncidencias/JSON-RPC");
alert(jsonrpc.idioma.toString());

He añadido la declaración del servlet al web.xml como indicas en el articulo.
El error de javascript que me da al abrir la página es el siguiente:

JSONRpcClient is not defined
[Break on this error] var jsonrpc = new JSONRpcClient("/GestorIncidencias/JSON-RPC");

Un saludo.Gracias

Hola Rafael, El error que me

Hola Rafael,

El error que me comentas viene dado porque seguramente has olvidado importar el archivo jsonrpc.js a tu página. Este archivo contiene el cliente JavaScript para JSON-RPC y el código para incluirlo es:

Este archivo lo encontrarás en la distribución de JSON-RPC-Java que seguramente ya has bajado.

Muchas grácias por tu comentario :)

Hola, La idea me parece muy

Hola,

La idea me parece muy buena y el primer ejemplo muy didáctico. Podrías hacer un segundo ejemplo que devuelva algo más complejo que un simple String???

Gracias

Hola rubenagi, Estamos

Hola rubenagi,

Estamos preparando un artículo para explicaros como se trabaja con este sistema para devolver Beans y colecciones de objetos (List, Set y HashMap) de forma transparente.

Gracias por el comentario :)

Sería posible combinar esto

Sería posible combinar esto con JPA?
Es decir meter en el Bridge una entidad cuyos atributos sean otras entidades en la base de datos mapeados JPA

Hola amarmol, La verdad es

Hola amarmol,

La verdad es que no he probado la posibilidad que apuntas.

Pero, aun sin probarlo, entendiendo el mecanismo de comunicación, no hay ningún problema en combinar JPA con JSON-RPC-Java. Esta librería lo que hace es mapear las llamadas a funciones Java con funciones JavaScript de forma transparente, eso y solo eso. Esta sencillez es su punto fuerte, pues no interfiere en el resto del desarrollo, siendo posible, en teoría, combinarlo con cualquier otra tecnología sin problemas!

Si realizas una prueba, no dudes en comentar el resultado! Gracias por comentar :)

WOWWWWW!!!! que pedazo de

WOWWWWW!!!! que pedazo de tecnología!!! Necesito aplicarla ya mi proyecto para sorprender al jefe y que me quite de becario. Me podeis para cuándo más o menos teneis pensado tener el ejemplo con los Beans y los HashMap. Mil gracias

Hola luis Supongo que no debe

Hola luis
Supongo que no debe de haber problemas si las relaciones entre los beans son simples. Pero me preguntaba como se comportaria en una base de datos con muchas relaciones entre las entidades (mapeadas con JPA) , porque suponte que (exagerando un poco) tirando de las relaciones, cargas en la memoria del navegador toda la base de datos...

Lo que realmente me interesa saber es si JSON-RPC crea en memoria del navegador todos los atributos (ya sean otros beans, o tipos basicos) o por el contrario, alli donde se encuentra un atributo que es un bean te crea el codigo AJAX necesario para recuperar dicho atributo, de manera que en memoria del cliente solo estan los beans que estar utilizando...

Sera cuestion de probarlo aunque me parece que JSON-RPC te recupera todos los atributos de un bean y los carga en el cliente.

Saludos

hola que tal, de verdad que

hola que tal, de verdad que esto se ve muy interesante, pero lo he querido probar y no consigo el resultado, estoy haciendo la aplicacion en java - Eclipse en mi servlet estoy poniendo lo siguiente:

HttpSession session = request.getSession();
JSONRPCBridge json_bridge = null;
json_bridge = (JSONRPCBridge) session.getAttribute("JSONRPCBridge");
if(json_bridge == null) {

json_bridge = new JSONRPCBridge();
session.setAttribute("JSONRPCBridge", json_bridge);
json_bridge.registerObject("ejemplo", new Ejemplo());

}

hago la conexion en el jsp y coloco la funcion

perdon ........la funcion va

perdon ........la funcion va asi:

function ejemplo(f){

var jsonrpc = new JSONRpcClient("/PROYECTO_LPII_2007>/JSON-RPC");

alert(jsonrpc.ejemplo.foo());

}

Hola yrene, Bueno, el

Hola yrene,

Bueno, el archivo jsonrpc.js es la clave, tienes que ponerlo en el WebContent (que supongo que es la carpeta raiz del .war final) o donde quieras dentro de ella. Y también deberás enlazarla desde tu archivo HTML, como explico arriba, como un archivo JavaScript, haciendo así:

Espero que te sea de ayuda!

Saludos y gracias por el post! :)

las primeras lineas del

las primeras lineas del codigo son basicamente estas

1
2

3

4
5
.
.
.
6

jsp:useBean

jsp:useBean id="JSONRPCBridge" scope="session" class="com.metaparadigm.jsonrpc.JSONRPCBridge" />
jsp:useBean id="busc" scope="session" class="com.ontologia.als.service.impl.OntologiaManagerImpl" />

% JSONRPCBridge.registerObject("busc", busc); %>

html:html locale="true">
head>
.
.
.
script type="text/javascript" src="js/jsonrpc.js">

Hola, Tengo problemillas

Hola,

Tengo problemillas utilizando JSON-RPC-Java con objetos JPA's.

Me da el siguiente error:

uncaught exception: JSONRpcClientException: bean es.orange.scoring.db.spi.jpa.datamodel.TInfBloque110JPA bean es.orange.scoring.db.spi.jpa.datamodel.TInformaInfoJPA bean org.apache.openjpa.util.java$sql$Timestamp$proxy bean kodo.kernel.ProfilingStateManager bean org.apache.openjpa.util.LongId bean java.lang.Class bean java.lang.reflect.Constructor circular reference

con un plugin del mozilla:

{"error":{"code":593,"msg":"bean es.orange.scoring.db.spi.jpa.datamodel.TInfBloque110JPA bean es.orange
.scoring.db.spi.jpa.datamodel.TInformaInfoJPA bean org.apache.openjpa.util.java$sql$Timestamp$proxy bean
kodo.kernel.ProfilingStateManager bean org.apache.openjpa.util.LongId bean java.lang.Class bean java
.lang.reflect.Constructor circular reference"},"id":3}

En principio apunta a que tengo en el objeto TInfBloque110JPA un objeto TInformaInfoJPA que tambien apunta a este. En JPA se realiza para establecer las relaciones bidireccionales.

¿Como podría solucionar o saltar esta referencia ciclica?

Gracias y un saludo.

**************************************************
CODIGO TInfBloque110JPA

package es.orange.scoring.db.spi.jpa.datamodel;
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import es.orange.scoring.db.datamodel.InformaInfo;
import es.orange.scoring.db.spi.jpa.abstractmodel.InfBloque110Abstract;

import javax.persistence.*;

/**
* The persistent class for the T_INFBLOQUE110 database table.
*
* @author BEA Workshop
*/
@Entity()
@Table(name="T_INFBLOQUE110", schema="SCORINGD_OWN")
public class TInfBloque110JPA extends InfBloque110Abstract implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;

@Id()
@GeneratedValue(strategy=GenerationType.TABLE)
@Column(name="BLOQUE110_ID", unique=true, nullable=false, precision=20)
private Long bloque110IdJPA;

@Basic()
@Column(name="SOLICITUD_ID",precision=20)
private Long solicitudIdJPA;

@Basic()
@Column(name="UNIDAD_BALANCE", length=1)
private String unidadBalanceJPA;

//bi-directional many-to-one association to InformaInfo
@ManyToOne(targetEntity=TInformaInfoJPA.class, fetch=FetchType.LAZY)
@JoinColumn(name="SOLICITUD_ID", referencedColumnName="SOLICITUD_ID")
private InformaInfo InformaInfoJPA;

public TInfBloque110JPA()
{
super();
}

public InformaInfo getInformaInfoJPA() {
return this.InformaInfoJPA;
}

public void setInformaInfoJPA(InformaInfo informaInfoJPA) {
this.InformaInfoJPA = informaInfoJPA;
}

public Long getBloque110IdJPA() {
return this.bloque110IdJPA;
}
public void setBloque110IdJPA(Long bloque110IdJPA) {
this.bloque110IdJPA = bloque110IdJPA;
}

public Long getSolicitudIdJPA() {
return this.solicitudIdJPA;
}
public void setSolicitudIdJPA(Long solicitudIdJPA) {
this.solicitudIdJPA = solicitudIdJPA;
}

public String getUnidadBalanceJPA() {
return this.unidadBalanceJPA;
}
public void setUnidadBalanceJPA(String unidadBalanceJPA) {
this.unidadBalanceJPA = unidadBalanceJPA;
}

public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof TInfBloque110JPA)) {
return false;
}
TInfBloque110JPA castOther = (TInfBloque110JPA)other;
return new EqualsBuilder()
.append(this.getSolicitudId(), castOther.getSolicitudId())
.isEquals();
}

public int hashCode() {
return new HashCodeBuilder()
.append(getSolicitudId())
.toHashCode();
}

public String toString() {
return new ToStringBuilder(this)
.append("solicitudId", getSolicitudId())
.toString();
}
}

**************************************************
CODIGO TInformaInfoJPA

package es.orange.scoring.db.spi.jpa.datamodel;
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import es.orange.scoring.db.datamodel.InfBloque010;
import es.orange.scoring.db.datamodel.InfBloque020;
import es.orange.scoring.db.datamodel.InfBloque030;
import es.orange.scoring.db.datamodel.InfBloque040;
import es.orange.scoring.db.datamodel.InfBloque050;
import es.orange.scoring.db.datamodel.InfBloque061;
import es.orange.scoring.db.datamodel.InfBloque110;
import es.orange.scoring.db.datamodel.InfBloque120;
import es.orange.scoring.db.datamodel.InfBloque130;
import es.orange.scoring.db.datamodel.InfBloque170;
import es.orange.scoring.db.spi.jpa.abstractmodel.InformaInfoAbstract;

import javax.persistence.*;

/**
* The persistent class for the T_INFORMA_INFO database table.
*
* @author BEA Workshop
*/
@Entity()
@Table(name="T_INFORMA_INFO", schema="SCORINGD_OWN")
public class TInformaInfoJPA extends InformaInfoAbstract implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;

@Id()
@SequenceGenerator(name="informainfoIdGenerator", sequenceName="SEQ_INFORMAINFO")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="informainfoIdGenerator")
@Column(name="INFORMAINFO_ID", unique=true, nullable=false, precision=20)
private Long informaInfoIdJPA;

@Basic()
@Column(name="SOLICITUD_ID", precision=20)
private Long solicitudIdJPA;

@Basic()
@Column(name="B10FECCONST_FC", length=11)
private java.sql.Timestamp b10fecconstFc;

//bi-directional many-to-one association to TInfbloque110
@OneToMany(targetEntity=TInfBloque110JPA.class, mappedBy="InformaInfoJPA", cascade={CascadeType.REFRESH, CascadeType.MERGE}, fetch=FetchType.LAZY)
private java.util.List InfBloque110JPA;

public TInformaInfoJPA() {
}

public Long getInformaInfoIdJPA() {
return this.informaInfoIdJPA;
}
public void setInformaInfoIdJPA(Long informaInfoIdJPA) {
this.informaInfoIdJPA = informaInfoIdJPA;
}

public Long getSolicitudIdJPA() {
return this.solicitudIdJPA;
}
public void setSolicitudIdJPA(Long solicitudIdJPA) {
this.solicitudIdJPA = solicitudIdJPA;
}

public java.sql.Timestamp getB10fecconstFc() {
return this.b10fecconstFc;
}
public void setB10fecconstFc(java.sql.Timestamp b10fecconstFc) {
this.b10fecconstFc = b10fecconstFc;
}

public java.util.List getInfBloque110JPA() {
return this.InfBloque110JPA;
}
public void setInfBloque110JPA(java.util.List InfBloque110JPA) {
this.InfBloque110JPA = InfBloque110JPA;
}

public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof TInformaInfoJPA)) {
return false;
}
TInformaInfoJPA castOther = (TInformaInfoJPA)other;
return new EqualsBuilder()
.append(this.getInformaInfoIdJPA(), castOther.getInformaInfoIdJPA())
.isEquals();
}

public int hashCode() {
return new HashCodeBuilder()
.append(getInformaInfoIdJPA())
.toHashCode();
}

public String toString() {
return new ToStringBuilder(this)
.append("solicitudId", getInformaInfoIdJPA())
.toString();
}
}

Hola buenas. Disculpa pero

Hola buenas.

Disculpa pero he intentado hacer que funcione una y otra vez sin consegirlo.

¿Te imponrtaria dejar un pequeño proyecto en el que se muestre bien las clases, los servlet, js etc, para cojer como referencia?

Un saludo y gracias de antemano.

Me da gusto saber que con

Me da gusto saber que con pyxser solucione el problema de referencias circulares en XML. El algoritmo y modelo de serializacion de pyxser permiten referencias cruzadas y circulares, pero esta implementado en C para Python. Vere si lo implemento en mas lenguajes.

Enviar un comentario nuevo

El contenido de este campo se mantiene privado y no se mostrará públicamente.
  • Las direcciones de las páginas web y las de correo se convierten en enlaces automáticamente.
  • Etiquetas HTML permitidas: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Saltos automáticos de líneas y de párrafos.

Más información sobre opciones de formato

Al enviar éste formulario, usted acepta la política de privacidad de Mollom.