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