EJB, @WebService & interfaces (JAXB)

Introducción

JEE 5 permite exponer como webservices, de manera sencilla, los métodos de stateless EJBs mediante anotaciones.

Cuando algún tipo de la signatura de los métodos anteriores es una interfaz, el mecanismo "out-of-the-box" anterior no funciona, y requiere la intervención del programador.

Un ejemplo concreto de la situación anteriormente descrita sería la siguiente:

·Se tiene que implementar, mediante diferentes clases, una interfaz denominada "Animal" previamente proporcionada y que no se puede modificar de manera alguna (ni siquiera para añadir una anotación).

·Las clases que se codificarán implementando la interfaz "Animal" serán "Cat" y "Dog"

·Finalmente, se desarrollará un mètodo web en un stateless EJB que retorne un tipo "Animal" (Nótese que 'Animal' es una interfaz, y por lo tanto puede ser tanto un Cat como un Dog).

Esta situación tiene deferentes formas de solucionarse. La propuesta en este artículo es la más genérica posible, es decir:

·No es necesario modificar de manera alguna la interfaz a implementar

·Permite múltiples implementaciones de la interfaz, sin que unas necesiten saber de la existencia de las otras.

Entorno de desarrollo

NetBeans 6.7 RC2 (IDE)

Glassfish 2.1 (JEE 5 server)

Solución A (si se puede anotar la interfaz)

En total se tendrá:

·Animal: La interfaz a implementar

·Cat: Una clase que implementa Animal

·Dog: Otra clase que implementa Animal

·ZooLocal i ZooBean: La interfaz i el bean del EJB cuyo método se expondrá como webservice

== Animal.java =====================================

Nota: Si en lugar de una interfaz, fuera una classe abstracta, entonces no sería necesaria la anotación "@XmlTransient"

package org.dom;

import javax.xml.bind.annotation.XmlTransient;

@XmlTransient //Para que JAXB no tenga en cuenta la interface

public interface Animal {

}

== Cat.java =====================================

package org.dom;

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="cat")

public class Cat implements Animal {

@XmlElement

private double height;

public Cat() {

this.height= 72;

}

}

== Dog.java =====================================

package org.dom;

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="dog")

public class Dog implements Animal {

@XmlElement

private double weight;

public Dog() {

this.weight= 2727;

}

}

== ZooLocal.java =====================================

package org.dom;

import javax.ejb.Local;

@Local

public interface ZooLocal {

}

== ZooBean.java =====================================

package org.dom;

import javax.ejb.Stateless;

import javax.jws.WebMethod;

import javax.jws.WebService;

import javax.xml.bind.annotation.XmlElementRef;

import javax.xml.bind.annotation.XmlRootElement;

import javax.xml.bind.annotation.XmlSeeAlso;

@Stateless

@WebService

@XmlSeeAlso({Dog.class, Cat.class}) //Classes that are not otherwise statically reachable will become reachable for the JAXB runtime (separated by comma)

@XmlRootElement(name = "zoo")

public class ZooBean implements ZooLocal {

//@XmlAnyElement

//@XmlElementRef(type = Object.class)

//@XmlAnyElement(lax=false)

//@XmlElementRefs({

// @XmlElementRef(type = Dog.class)

//})

//List<Animal> animals;

public ZooBean() {

//this.animals = new ArrayList<Animal>();

System.out.println("***JJJ*** Contructor");

}

@WebMethod()

@XmlElementRef(type = Object.class)

public org.dom.Animal getAnimal(int catOrDog) {

org.dom.Animal ret;

if (catOrDog % 2 == 0) {

ret = new Cat();

} else {

ret = new Dog();

}

return (ret);

}

}

== Testeo =====================================

Llamando al webservice con un número par, retornará un Cat (que es un Animal).

Llamando al webservice con un número impar, retornará un Cat (que es un Animal).

Solución B (si NO se puede anotar la interfaz)

En total se tendrá:

·Animal: La interfaz a implementar

·AnimalAbstract: Clase abstracta que implementa la interfaz Animal

·Cat: Una clase que extiende AnimalAbstract

·Dog: Otra clase que extiende AnimalAbstract

·ZooLocal i ZooBean: La interfaz i el bean del EJB cuyo método se expondrá como webservice

== Animal.java =====================================

package org.dom;

public interface Animal {

}

== AnimalAbstract.java =====================================

package org.dom;

public class AnimalAbstract implements Animal {

}

== Cat.java =====================================

package org.dom;

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="cat")

public class Cat extends AnimalAbstract {

@XmlElement

private double height;

public Cat() {

this.height= 72;

}

== Dog.java =====================================

package org.dom;

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="dog")

public class Dog extends AnimalAbstract {

@XmlElement

private double weight;

public Dog() {

this.weight= 2727;

}

}

== ZooLocal.java =====================================

package org.dom;

import javax.ejb.Local;

@Local

public interface ZooLocal {

}

== ZooBean.java =====================================

package org.dom;

import javax.ejb.Stateless;

import javax.jws.WebMethod;

import javax.jws.WebService;

import javax.xml.bind.annotation.XmlElementRef;

import javax.xml.bind.annotation.XmlRootElement;

import javax.xml.bind.annotation.XmlSeeAlso;

/**

* EJB web method with interfaces

* Solution B (dom4 project); Note: The Solution A is on project dom3

* In this solution, it is NOT possible to annotate the interface,

* so a new abstract class is build on top of it.

*

* https://jaxb.dev.java.net/guide/Mapping_interfaces.html

*

*/

@Stateless

@WebService

@XmlSeeAlso({AnimalAbstract.class, Dog.class, Cat.class}) //classes that are not otherwise statically reachable will become reachable for the JAXB runtime (separated by comma)

@XmlRootElement(name = "zoo")

public class ZooBean implements ZooLocal {

public ZooBean() {

}

@WebMethod()

@XmlElementRef(type = Object.class)

public org.dom.AnimalAbstract getAnimal(int catOrDog) {

org.dom.AnimalAbstract ret;

if (catOrDog % 2 == 0) {

ret = new Cat();

} else {

ret = new Dog();

}

return (ret);

}

}

== Testeo =====================================

Llamando al webservice con un número par, retornará un Cat (que es un Animal).

Llamando al webservice con un número impar, retornará un Cat (que es un Animal).