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. Creemos que compartir un proyecto es mucho más interesante que guardarlo en un cajón secreto.

blog


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

menéame

17 Responses to “JSON-RPC-Java: Capa de abstracción para comunicaciones AJAX”

  1. Rafael Says:

    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

  2. Caminews » Atenea tech blog » Blog Archive » JSON-RPC-Java: Capa de abstracción para comunicaciones AJAX Says:

    […] Fonte: http://www.ateneatech.com/blog/json-rpc-java-capa-de-abstraccion-para-comunicaciones-ajax […]

  3. luis Says:

    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:

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

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

    Muchas grácias por tu comentario :)

  4. rubenagui Says:

    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

  5. luis Says:

    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 :)

  6. amarmol Says:

    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

  7. luis Says:

    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 :)

  8. becario_precario Says:

    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

  9. amarmol Says:

    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

  10. yrene Says:

    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


    function ejemplo(f){
    //–var jsonrpc = new JSONRpcClient(”/PROYECTO_LPII_2007/JSON-RPC”);
    var jsonrpc = new JSONRpcClient_getHTTPRequest(”//JSON-RPC”);
    //–alert(”hola”);
    alert(jsonrpc.ejemplo.foo());

    }

    <!–
    .Estilo1 {…

    bueno y en el metodo onclick del boton coloco la funcion :
    <input type=”button” onclick=”ejemplo(this.form)” name=”bt…..

    Bueno disculpen la ignorancia…pero la verdad me gustaria mucho poder usar esto.

    ah por cierto en el webContent tengo que colocar el archivo “jsonrpc.js”?…porque asi lo he estado haciendo, bueno dentro de el encuentro varios metodos

    GRACIAS …y disculpen tanta molestia espero alguien pueda orientarme.

  11. yrene Says:

    perdon ……..la funcion va asi:

    function ejemplo(f){

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

    alert(jsonrpc.ejemplo.foo());

    }

    <!–
    …………….

    bueno tambien cree la clase Ejmemplo..todo identico al ejemplo que se ha publicado aki ..gracias

  12. luis Says:

    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í:

    <script type=”text/javascript” src=”{path al archivo}/jsonrpc.js”>

    Espero que te sea de ayuda!

    Saludos y gracias por el post! :)

  13. Erick Says:

    Hola Luis,
    Tengo un codigo en mi jsp de la siguiente forma:

    .
    .
    .

    .
    .

    jsonrpc = new JSONRpcClient(”JSON-RPC”);

    function refrescar(result,exception){
    document.getElementById(”nombre”).innerHTML = “”+result.nombre + “”;
    document.getElementById(”descripcion”).innerHTML = “”+result.descripcion + “”;

    padre = “”;
    if (result.hijoDe != null)
    padre = “” + result.relacionHijoDe + “: “+result.hijoDe + “”;

    if (result.padreDe != null)
    padre += “” + result.relacionPadreDe + “: ” + result.padreDe + “”;

    document.getElementById(”padres”).innerHTML = padre + “”;

    // Arreglo que contiene las propiedades de la clase
    lista=result.propiedades.list;
    prop = “”;

    if (lista.length>0) {
    prop = “Propiedades: “;
    for (var i=0;i<lista.length;i++){
    prop+=”  ” + lista[i]+”";
    }
    }
    document.getElementById(”propiedades”).innerHTML = prop;

    // Arreglo que contiene las instancias de la clase
    lista2=result.instancias.list;
    inst=”";

    if (lista2.length>0){
    inst = ” ”

    for (var i=0;i<lista2.length;i+=2){

    if (i%4 == 0)
    inst += “”;
    inst += “”;
    inst += “    ”;
    inst += “”;
    inst += “”;
    inst += “ ” + lista2[i+1] + ” “;
    inst += “”;

    if (i%2 == 1)
    inst += “”;
    }

    inst += “”;
    }

    document.getElementById(”instancias”).innerHTML = inst;
    img = “”;
    document.getElementById(”imagen”).innerHTML = img;
    document.getElementById(”imagen”).value=result.id;
    }

    function buscar(id){
    jsonrpc.busc.buscarOntologia(refrescar,id,”");
    }

    el llamado de la función buscar lo hago cuando hago clic en un elemento x de la pagina…
    Ok el problema que tengo es en la linea :
    jsonrpc.busc.buscarOntologia(refrescar,id,”");

    y en la consola de error me sale:
    JSONRpcException: Not Found

    pero el script jsonrpc.js existe y esta localizado donde lo indico, ademas tengo registrado el servlet en el web.xml
    lo extraño del asunto es que en algunas instalaciones hechas en Linux y Windows me sirve, pero en algunas de linux no (Apache/2.2.3 (Ubuntu) mod_jk/1.2.18).

    Podrias ayudarme al respecto, no se porque tan errático este comportamiento.
    Gracias.

  14. Erick Says:

    las primeras lineas del codigo son basicamente estas

    1
    2

    3

    4
    5
    .
    .
    .
    6

  15. Erick Says:

    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”>

  16. Isra Says:

    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();
    }
    }

  17. Marcos Says:

    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.

Leave a Reply