SeamFramework.orgCommunity Documentation

Web Beans: Java Contexts and Dependency Injection

The new standard for dependency injection and contextual state management


Note
I. Uso de objetos contextuales
1. Comenzando a escribir Web Beans
1.1. Su primer Web Bean
1.2. ¿Qué es un Web Bean?
1.2.1. Tipos API, tipos de enlace e inyección de dependencia
1.2.2. Tipos de despliegue
1.2.3. Ámbito
1.2.4. Nombres de Web Beans y EL unificado
1.2.5. Tipos de interceptor de enlace
1.3. ¿Qué clase de objetos pueden ser Web Beans?
1.3.1. Web Beans sencillos
1.3.2. Web Beans de empresa
1.3.3. Métodos de productor
1.3.4. endpoints de JMS
2. ejemplo de aplicación de red JSF
3. La implementación de referencia de Web Beans
3.1. El ejemplo numberguess
3.2. Ejemplo de traductor
4. Inyección de dependencia
4.1. Anotaciones de Enlace
4.1.1. Anotaciones de enlace con miembros
4.1.2. Combinaciones de anotaciones de enlace
4.1.3. Anotaciones de enlace y métodos de productor
4.1.4. El tipo de enlace predeterminado
4.2. Tipos de despliegue
4.2.1. Habilitar tipos de despliegue
4.2.2. Prioridad de tipo de despliegue
4.2.3. Ejemplo de tipos de despliegue
4.3. Corregir dependencias insatisfechas
4.4. Los proxy de cliente
4.5. Obtención de un Web Bean por búsqueda programática
4.6. El ciclo de vida de los callbacks, @Resource, @EJB y @PersistenceContext
4.7. El objeto InjectionPoint
5. Ámbitos y contextos
5.1. Tipos de ámbito
5.2. Ámbitos incorporados
5.3. El ámbito de conversación
5.3.1. Demarcación de conversación
5.3.2. Propagación de conversación
5.3.3. Pausa de conversación
5.4. El seudo ámbito dependiente
5.4.1. La anotación @New
6. Métodos de productor
6.1. Ámbito de un método de productor
6.2. Inyección dentro de métodos de productor
6.3. Uso de @New con métodos de productor
II. Desarrollo de código de acoplamiento flexible
7. Interceptores
7.1. Enlaces de interceptor
7.2. Implementación de interceptores
7.3. Habilitar Interceptores
7.4. Enlaces de interceptor con miembros
7.5. Anotaciones de enlace de múltiples interceptores
7.6. Herencia del tipo de interceptor de enlace
7.7. Uso de @Interceptors
8. Decoradores
8.1. Atributos de delegado
8.2. Habilitar decoradores
9. Eventos
9.1. Observadores de evento
9.2. Productores de Evento
9.3. Registro dinámico de observadores
9.4. Enlaces de evento con miembros
9.5. Enlaces de evento múltiples
9.6. Observadores transaccionales
III. Aprovechar al máximo un tecleado fuerte
10. Estereotipos
10.1. El ámbito predeterminado y el tipo de despliegue para un estereotipo
10.2. Restricción de ámbito y tipo con un estereotipo
10.3. Enlaces de interceptor para esterotipos
10.4. Predeterminación de nombre con estereotipos
10.5. Estereotipos estándar
11. Specialization
11.1. Uso de Specialization
11.2. Ventajas de Specialization
12. Definición de Web Beans utilizando XML
12.1. Declaración de clases de Web Bean
12.2. Declaración de metadatos de Web Bean
12.3. Declaración de miembros de Web Bean
12.4. Declaración de Web Beans en línea
12.5. Uso de un esquema
IV. Web Beans en el ecosistema de Java EE
13. Integración Java EE
13.1. Inyección de recursos de Java EE en un Web Bean
13.2. Llamando a Web Bean desde un Servlet
13.3. Llamada a un Web Bean desde un Message-Driven Bean
13.4. endpoints JMS
13.5. Empaquetamiento y despliegue.
14. Extensión de Web Beans
14.1. El objeto Manager
14.2. La clase Bean
14.3. La interfaz Contexto
15. Siguientes pasos
A. Integración de la IR de Web Beans en otros entornos
A.1. SPI RI de Web Beans
A.1.1. Web Bean Discovery
A.1.2. EJB Discovery
A.1.3. @EJB, @PersistenceContext and @Resource resolution
A.1.4. Transaction Services
A.1.5. The application context
A.1.6. Bootstrap and shutdown
A.1.7. JNDI
A.1.8. Carga de recurso
A.1.9. Servlet injection
A.2. El contrato con el contenedor

JSR-299 has recently changed its name from "Web Beans" to "Java Contexts and Dependency Injection". The reference guide still refers to JSR-299 as "Web Beans" and the JSR-299 Reference Implementation as the "Web Beans RI". Other documentation, blogs, forum posts etc. may use the new nomenclature, including the new name for the JSR-299 Reference Implementation - "Web Beans".

You'll also find that some of the more recent functionality to be specified is missing (such as producer fields, realization, asynchronous events, XML mapping of EE resources).

La especificación de Web Beans (JSR-299) define una serie de servicios para el entorno de Java EE que facilitan el desarrollo de aplicaciones. Web Beans entrecruza un ciclo de vida mejorado y un modelo de interacción en tipos de componentes existentes de Java incluyendo los componentes JavaBeans y Enterprise Java Beans. Como complemento para el modelo tradicional de programación Java EE, el servicio de Web Beans proporciona servicios:

Inyección de dependencia, junto con la administración de ciclo de vida contextual, ahorra al usuario de un API desconocido el tener que hacer y contestar las siguientes preguntas:

Un Web Bean especifica únicamente el tipo y la semántica de otros Web Beans de los que depende. No se necesita conocer el ciclo de vida real, la implementación concreta, el modelo de hilos u otros clientes de cualquier Web Bean a la que dependa. Mejor aún, la implementación concreta, el ciclo de vida y el modelo de hilos de un Web Bean al que éste depende pueden variar según el escenario de despliegue, sin afectar a ningún cliente.

Los eventos, los interceptores y los decoradores mejoran el acoplamiento-flexibleinherente en este modelo:

Más importante, Web Beans ofrece todos los servicios en formatypesafe. Web Beans nunca utiliza identificadores de cadena para determinar cómo los se ajustan entre sí objetos de colaboración. Además, aunque XML sigue siendo una opción, muy rara vez se utiliza. En su lugar, Web Beans usa la información de teclado disponible en el modelo de objeto Java junto con un nuevo patrón, llamado anotaciones de enlace, para conectar a Web Beans, las dependencias, los interceptores y decoradores y sus consumidores de evento.

Los servicios Web Beans son generales y se aplican a los siguientes tipos de componentes existentes en el entorno de Java EE.

Web Beans incluso provee los puntos de integración necesarios para que otras clases de componentes definidos por especificaciones de Java EE futuras, hagan uso de los servicios de Web Beans e interactúen con otra clase de Web Bean.

Una gran cantidad de marcos existentes de Java, incluyendo Seam, Guice y Spring, influyeron en Web Beans. No obstante, Web Beans tiene su propio carácter distintivo: más typesafe que Seam, más con estado y menos centrada en XML que Spring, red y aplicación empresarial más capaz que Guice.

Lo más importante es que Web Beans es un JCP estándar que se integra sin problemas con Java EE y con cualquier entorno SE dónde EJB Lite incrustado esté disponible.

Tabla de contenidos

1. Comenzando a escribir Web Beans
1.1. Su primer Web Bean
1.2. ¿Qué es un Web Bean?
1.2.1. Tipos API, tipos de enlace e inyección de dependencia
1.2.2. Tipos de despliegue
1.2.3. Ámbito
1.2.4. Nombres de Web Beans y EL unificado
1.2.5. Tipos de interceptor de enlace
1.3. ¿Qué clase de objetos pueden ser Web Beans?
1.3.1. Web Beans sencillos
1.3.2. Web Beans de empresa
1.3.3. Métodos de productor
1.3.4. endpoints de JMS
2. ejemplo de aplicación de red JSF
3. La implementación de referencia de Web Beans
3.1. El ejemplo numberguess
3.2. Ejemplo de traductor
4. Inyección de dependencia
4.1. Anotaciones de Enlace
4.1.1. Anotaciones de enlace con miembros
4.1.2. Combinaciones de anotaciones de enlace
4.1.3. Anotaciones de enlace y métodos de productor
4.1.4. El tipo de enlace predeterminado
4.2. Tipos de despliegue
4.2.1. Habilitar tipos de despliegue
4.2.2. Prioridad de tipo de despliegue
4.2.3. Ejemplo de tipos de despliegue
4.3. Corregir dependencias insatisfechas
4.4. Los proxy de cliente
4.5. Obtención de un Web Bean por búsqueda programática
4.6. El ciclo de vida de los callbacks, @Resource, @EJB y @PersistenceContext
4.7. El objeto InjectionPoint
5. Ámbitos y contextos
5.1. Tipos de ámbito
5.2. Ámbitos incorporados
5.3. El ámbito de conversación
5.3.1. Demarcación de conversación
5.3.2. Propagación de conversación
5.3.3. Pausa de conversación
5.4. El seudo ámbito dependiente
5.4.1. La anotación @New
6. Métodos de productor
6.1. Ámbito de un método de productor
6.2. Inyección dentro de métodos de productor
6.3. Uso de @New con métodos de productor

Entonces, ¿está preparado para empezar a escribir su primer Web Bean? O quizás está escéptico, preguntándose por qué tipos de arcos le hará ¡saltar la especificación de Web Beans! La buena noticia es que probablemente ya ha escrito y utilizado cientos, quizás miles de Web Beans. Podría no recordar incluso el primer Web Bean que escribió.

Con determinadas excepciones, muy especiales, toda clase de Java con un constructor que no acepte parámetros es un Web Bean. Esto incluye cada JavaBean. Además, cada sesión estilo EJB 3 es un Web Bean. Por supuesto, los JavaBeans y EJB que usted ha escrito a diario no han podido aprovechar los nuevos servicios definidos por la especificación de Web Beans, pero podrá utilizar cada uno de ellos como Web Beans — inyectándolos en otros Web Beans, configurándolos a través de los servicios de configuración, incluso agregándoles interceptores y decoradores — sin tocar su código existente.

Suponga que tenemos dos clases existentes de Java, las cuales hemos estado utilizando por años en varias aplicaciones. La primera clase analiza una cadena en un lista de oraciones:

public class SentenceParser {

    public List<String
> parse(String text) { ... }
}

La segunda clase existente es un bean de front-end sin estado de sesión capaz de traducir oraciones de un idioma a otro:

@Stateless

public class SentenceTranslator implements Translator {
    public String translate(String sentence) { ... }
}

Donde Translator es la interfaz local:

@Local

public interface Translator {
    public String translate(String sentence);
}

Lamentablemente, no tenemos una clase preexistente que traduzca todos los documentos de texto. Entonces, escribamos un Web Bean que realice esta tarea:

public class TextTranslator {

    
    private SentenceParser sentenceParser;
    private Translator sentenceTranslator;
    
    @Initializer
    TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
        this.sentenceParser = sentenceParser;
        this.sentenceTranslator = sentenceTranslator;
    }
    
    public String translate(String text) {
        StringBuilder sb = new StringBuilder();
        for (String sentence: sentenceParser.parse(text)) {
            sb.append(sentenceTranslator.translate(sentence));
        }
        return sb.toString();
    }
    
}

Podemos obtener una instancia de TextTranslator inyectándola en una Web Bean, Servlet o EJB:

@Initializer

public setTextTranslator(TextTranslator textTranslator) {
    this.textTranslator = textTranslator;
}

De modo alterno, podemos obtener una instancia llamando directamente un método del administrador de Web Bean:

TextTranslator tt = manager.getInstanceByType(TextTranslator.class);

Pero espere: ¡TextTranslator no tiene un constructor sin parámetros! ¿Es éste aún un Web Bean? Bueno, una clase que no tiene un constructor sin parámetros aún puede ser un Web Bean si tiene un constructor anotado @Initializer.

Como pudo adivinar, la anotación @Initializer tiene algo que ver con la ¡inyección de dependencia! @Initializer puede aplicarse a un constructor o método de un Web Bean, y pide a un administrador de Bean llamar a ese constructor o método cuando inicia el Web Bean. El administrador de Web Bean inyectará otros Web Beans a los parámetros del constructor o método.

En el momento de inicialización, el administrador de Web Bean debe confirmar que exista exactamente un Web Bean que complete cada punto de inyección. En nuestro ejemplo, si no estaba disponible ninguna implementación de Translator — si el EJB de SentenceTranslator no estaba desplegado — el administrador de Web Bean produciría una UnsatisfiedDependencyException. Si más de una implementación de Translator estuviera disponible, el administrador de Web Bean produciría una AmbiguousDependencyException.

Entonces, ¿qué es, exactamente un Web Bean?

Un Web Bean es una clase de aplicación que contiene lógica de negocios. Un Web Bean puede llamarse directamente desde el código de Java, o invocarse a través de Unified EL. Un Web Bean puede acceder recursos transaccionales. Las dependencias entre Web Beans son manejadas automáticamente por el administrador de Web Bean. La mayoría de Web Beans son con estado y contextuales. El ciclo de vida de un Web Bean siempre es manejado por el administrador de Web Bean.

Volvamos atrás por un segundo. ¿Qué significa "contextual"? Puesto que Web Beans puede tener estados, es importante saber qué instancia de bean se tiene. A diferencia de un modelo de componente sin estado (por ejemplo, beans sin estado de sesión) o un modelo de componente singleton (como servlets, o beans singleton), clientes diferentes de un Web Bean ven el Web Bean en estados diferentes. El estado cliente-visible depende de la instancia de Web Bean a la que se refiere el cliente.

No obstante, como un modelo sin estado o un modelo singleton, pero a diferencia de los beans con estado de sesión, el cliente no controla el ciclo de vida de la instancia explícitamente creando y destruyéndolo. En su lugar, el ámbito del Web Bean determina:

Para un subproceso dado en una aplicación de Web Beans, puede haber un contexto activo asociado con el ámbito del Web Bean. Este contexto puede ser único para el subproceso (por ejemplo, si el Web Bean tiene un ámbito de petición), o puede compartirse con algunos subprocesos (por ejemplo, si el Web Bean tiene un ámbito de sesión) o incluso con todos los otros subprocesos (si es el ámbito de la aplicación).

Los clientes (por ejemplo, otros Web Beans) ejecutando en el mismo contexto verán la misma instancia del Web Bean. Pero los clientes en un contexto diferente verán una instancia diferente.

Una gran ventaja del modelo contextual es que permite a los Web Beans con estado ser tratados como ¡servicios! El cliente no necesita preocuparse por manejar el ciclo de vida del Web Bean que está utilizando, ni necesita saber qué ciclo de vida es. Los Web Beans interactúan pasando mensajes, y las implementaciones del Web Bean definen el ciclo de vida de su propio estado. Los Web Beans están en parejas sueltas porque:

Podemos remplazar un Web Bean por un Web Bean diferente que implemente la misma API y tenga un ciclo de vida diferente (un ámbito diferente) sin afectar la otra implementación de Web Bean. De hecho, Web Beans define una facilidad altamente desarrollada para anular las implementaciones de Web Bean en el momento del despliegue, como también ver en Sección 4.2, “Tipos de despliegue”.

Observe que todos los clientes de una Web Bean son Web Beans. Otros objetos tales como Servlets o Message-Driven Beans — los cuales son por naturaleza no inyectables, objetos contextuales — también pueden obtener referencias a Web Beans por inyección.

Más formalmente, de acuerdo con la especificación:

Un Web Bean comprende:

  • Conjunto (no vacío) de Tipos API

  • Un conjunto (no vacío) de tipos de anotación

  • Un ámbito

  • Un tipo de despliegue

  • Alternativamente, un nombre de Web Bean

  • Un conjunto de tipos de interceptor de enlace

  • Una implementación de Web Bean

Veamos lo que significan algunos de estos términos, para el desarrollador de Web Bean.

Los Web Beans suelen adquirir referencias a otros Web Beans a través de la inyección de dependencia. Cualquier atributo inyectado especifica un "contrato" que debe cumplir el Web Bean que va a ser inyectado. El contrato es:

Una API es una clase o interfaz de usuario-definida. (Si el Web Bean es un bean EJB de sesión, el tipo API es la vista de bean de interfaz @Local o de clase). Un tipo de enlace representa alguna semántica visible de cliente cumplida por algunas implementaciones de API y no por otras.

Los tipos de enlace están representados por anotaciones de usuario-definidas hechas por ellas mismas @BindingType. Por ejemplo, el siguiente punto de inyección tiene un tipo de PaymentProcessor de API y un tipo de enlace @CreditCard:

@CreditCard PaymentProcessor paymentProcessor

Si no está explícito ningún tipo de enlace en el punto de inyección, se asumirá el tipo de enlace predeterminado @Current.

Para cada punto de inyección, el administrador de Web Bean busca un Web Bean que cumpla el contrato (implemente el API, y tenga todos los tipos de enlace), e inyecta ese Web Bean.

El siguiente Web Bean tiene el tipo de enlace @CreditCard e implementa el tipo API PaymentProcessor. Podría por lo tanto ser inyectado en el punto de inyección de ejemplo:

@CreditCard

public class CreditCardPaymentProcessor 
    implements PaymentProcessor { ... }

Si un Web Bean no especifica explícitamente un conjunto de tipos de enlace, tiene exactamente un tipo de enlace: el tipo de enlace predeterminado @Current.

Web Beans define un algoritmo de resolución altamente desarrollado e intuitivo que ayuda al contenedor a decidir qué debe hacer si hay uno más de un Web Beans que cumpla un contrato determinado. Veremos esta información en detalle en Capítulo 4, Inyección de dependencia.

Ya hemos visto que JavaBeans, EJB y algunas otras clases de Java pueden ser Web Beans. Pero, exactamente, ¿qué clase de objetos son los Web Beans?

La especificación dice que todos los beans de sesión estilo EJB 3- y singleton son empresariales. Los mensajes de beans no son Web Beans — porque no están diseñados para ser inyectados en otros objetos — pero pueden aprovechar la mayoría de las funcionalidades de los Web Beans, incluyendo la inyección de dependencia y los interceptores.

No toda interfaz local de un Web Bean empresarial tiene un parámetro de tipo comodín o tipo variable, cada una de sus superinterfaces, es un tipo API del Web Bean de empresa. Si el bean EJB tiene una vista local de clase de bean, la clase de bean, y cada una de sus superclases, también es un tipo API.

Los beans con estado de sesión deben declarar un método de eliminación sin parámetros o un método de eliminación anotado @Destructor. El administrador de Web Bean llama a este método para destruir la instancia de bean con estado de sesión al final del ciclo de vida. Este método se llama el métododestructor del Web Bean empresarial.

@Stateful @SessionScoped

public class ShoppingCart {
    ...
    
    @Remove
    public void destroy() {}
}

¿Entonces deberíamos utilizar un Web Bean empresarial en lugar del Web Bean sencillo? Bueno, cada vez que necesitemos los servicios de empresa avanzados ofrecidos por EJB, tales como:

deberíamos utilizar un Web Bean empresarial. Cuando no necesitemos ninguna de estas cosas, bastará con un Web Bean sencillo.

Muchos Web Beans (incluyendo toda sesión o ámbito de aplicación Web Bean) están disponibles para acceso concurrente. Por lo tanto, la administración de concurrencia proporcionada por EJB3.1 es especialmente útil. La mayor parte de la sesión y el ámbito de la aplicación WebBeans debe ser EJB.

Los Web Beans que guardan referencias a recursos pesados, o mantienen un montón de estado interno se benefician del ciclo de vida de contenedor avanzado - administrado definido por el modelo EJB @Stateless/@Stateful/@Singleton, con el soporte para pasivación y grupo de instancia.

Por último, suele ser evidente cuando la administración de transacción nivel-método, seguridad nivel-método, temporizadores o métodos remotos o asíncronos se requieren.

Suele ser fácil iniciar con un Web Bean sencillo y luego cambiar a un EJB, con sólo añadir una anotación: @Stateless, @Stateful o @Singleton.

Un método de productor es un método llamado por el administrador de Web Bean para obtener una instancia del Web Bean cuando no exista en el actual contexto. Un método de productor permite a la aplicación tomar el control total del proceso de iniciación, en lugar de dejar la instanciación al administrador de Web Bean. Por ejemplo:

@ApplicationScoped

public class Generator {
    private Random random = new Random( System.currentTimeMillis() );
    
    @Produces @Random int next() {
        return random.nextInt(100);
    }
}

El resultado de un método de productor es inyectado como cualquier otro Web Bean.

@Random int randomNumber

El método de tipo de retorno y todas las interfaces que extiende/implementa directa o indirectamente son tipos API del método del productor. Si el tipo de retorno es una clase, todas las superclases tienen también tipos API.

Algunos métodos de productor retornan objetos que requieren destrucción explícita:

@Produces @RequestScoped Connection connect(User user) {

    return createConnection( user.getId(), user.getPassword() );
}

Estos métodos de productor pueden definir métodos desechables:

void close(@Disposes Connection connection) {

    connection.close();
}

Este método desechable es llamado automáticamente por el administrador de Web Bean al final de la petición.

Hablaremos mucho más acerca de métodos del productor en Capítulo 6, Métodos de productor.

Ilustremos estas ideas con un ejemplo. Vamos a implementar inicio/cierre de sesión de usuario para una aplicación que utiliza JSF. Primero, definiremos un Web Bean para mantener el nombre de usuario y contraseña escritos durante el inicio de sesión:

@Named @RequestScoped

public class Credentials {
        
    private String username;
    private String password;
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    
}

Este Web Bean está vinculado al intérprete de comandos de inicio de sesión en el siguiente formulario JSF:

<h:form>

    <h:panelGrid columns="2" rendered="#{!login.loggedIn}">
        <h:outputLabel for="username"
>Username:</h:outputLabel>
        <h:inputText id="username" value="#{credentials.username}"/>
        <h:outputLabel for="password"
>Password:</h:outputLabel>
        <h:inputText id="password" value="#{credentials.password}"/>
    </h:panelGrid>
    <h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/>
    <h:commandButton value="Logout" acion="#{login.logout}" rendered="#{login.loggedIn}"/>
</h:form
>

El trabajo real está hecho por una sesión de ámbito Web Bean que mantiene información acerca del usuario actualmente conectado y expone la entidad del Usuario a otras Web Beans:

@SessionScoped @Named

public class Login {
    @Current Credentials credentials;
    @PersistenceContext EntityManager userDatabase;
    private User user;
    
    public void login() {
            
        List<User
> results = userDatabase.createQuery(
           "select u from User u where u.username=:username and u.password=:password")
           .setParameter("username", credentials.getUsername())
           .setParameter("password", credentials.getPassword())
           .getResultList();
        
        if ( !results.isEmpty() ) {
           user = results.get(0);
        }
        
    }
    
    public void logout() {
        user = null;
    }
    
    public boolean isLoggedIn() {
       return user!=null;
    }
    
    @Produces @LoggedIn User getCurrentUser() {
        return user;
    }
}

Obviamente, @LoggedIn es una anotación de enlace:

@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD})
@BindingType
public @interface LoggedIn {}

Ahora, cualquier otro Web Bean puede fácilmente inyectar al usuario actual:

public class DocumentEditor {


    @Current Document document;
    @LoggedIn User currentUser;
    @PersistenceContext EntityManager docDatabase;
    
    public void save() {
        document.setCreatedBy(currentUser);
        docDatabase.persist(document);
    }
    
}

Esperamos que este ejemplo de una idea del modelo de programación de Web Bean. En el próximo capítulo, estudiaremos más a fondo la inyección de dependencia de Web Beans.

La implementación de referencia (IR) de Web Beans se desarrolla en the Seam project. La última versión de Web Beans se puede descargar desde the downloads page.

La IR de Web Beans viene con dos ejemplos de aplicaciones de despliegue: webbeans-numberguess, un ejemplo WAR, que contiene sólo beans sencillos y un ejemplo EAR webbeans-translator que contiene beans empresariales. Para ejecutar los ejemplos necesitará lo siguiente:

  • la última versión de la IR de Web Beans,

  • JBoss AS 5.0.0.GA, y

  • Ant 1.7.0.

Currently, the Web Beans RI only runs on JBoss Application Server 5. You'll need to download JBoss AS 5.0.1.GA from jboss.org, and unzip it. For example:

$ cd /Applications
$ unzip ~/jboss-5.0.1.GA.zip

Luego, descargue la IR de Web Beans desde seamframework.org, y descomprímalo. Por ejemplo:

$ cd ~/
$ unzip ~/webbeans-$VERSION.zip

Después necesitaremos decirle a Web Beans en dónde está localizado JBoss. Editar jboss-as/build.properties y establecer la propiedad jboss.home. Por ejemplo:

jboss.home=/Applications/jboss-5.0.1.GA

Nota

A new deployer, webbeans.deployer is added to JBoss AS. This adds supports for Web Bean archives to JBoss AS, and allows the Web Beans RI to query the EJB3 container and discover which EJBs are installed in your application.

Web Beans is bundled with JBoss AS 5.1 and above.

To install Web Beans, you'll need Ant 1.7.0 installed, and the ANT_HOME environment variable set. For example:

$ unzip apache-ant-1.7.0.zip
$ export ANT_HOME=~/apache-ant-1.7.0

Then, you can install the update. The update script will use Maven to download Web Beans automatically.

$ cd webbeans-$VERSION/jboss-as
$ ant update

Ahora, ¡está listo para desplegar su primer ejemplo!

Sugerencia

Los scripts creados para los ejemplos ofrecen una cantidad de objetivos, a saber:

  • ant restart - despliega el ejemplo en formato explotado

  • ant explode - actualiza un ejemplo explotado, sin reiniciar el despliegue

  • ant deploy - despliega el ejemplo en formato JAR comprimido

  • ant undeploy - quita el ejemplo del servidor

  • ant clean - borra el ejemplo

Para desplegar el ejemplo numberguess:

$ cd examples/numberguess
ant deploy

Start JBoss AS:

$ /Application/jboss-5.0.0.GA/bin/run.sh

Sugerencia

If you use Windows, use the run.batscript.

Espere que despliegue la aplicación, y diviértase en http://localhost:8080/webbeans-numberguess!

La IR de Web Beans incluye un segundo ejemplo que traducirá su texto en Latín. El ejemplo numberguess es un ejemplo WAR, y sólo utiliza beans sencillos; el ejemplo de traductor es un ejemplo EAR e incluye beans empresariales, empaquetados en un módulo EJB. Para probar:

$ cd examples/translator
ant deploy

Espere a que despliegue la aplicación, y ¡visite http://localhost:8080/webbeans-translator!

En la aplicación numberguess se le dan 10 intentos para adivinar un número entre 1 y 100. Después de cada intento, se le dirá si es mayor o menor a su número.

El ejemplo de numberguess consta de una cantidad de Web Beans, archivos de configuración y páginas Facelet JSF, empaquetadas como WAR. Empecemos con los archivos de configuración.

Todos los archivos de configuración para este ejemplo están localizados en WEB-INF/, el cual está almacenado en WebContent en el árbol fuente. Primero, tenemos faces-config.xml, en donde le pedimos a JSF que utilice Facelets:


<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
    
    <application>
        <view-handler
>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>

</faces-config
>

Hay un archivo web-beans.xml vacío, el cual marca esta aplicación como una aplicación de Web Beans.

Por último, está web.xml:

Observemos una vista de Facelet:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:s="http://jboss.com/products/seam/taglib">

  <ui:composit(1)ion template="template.xhtml">
    <ui:define name="content">
       <h1
>Adivine un número...</h1>
       <h:form(2) id="NumberGuessMain">
          <div style="color: red">
             <h:messages id="messages" globalOnly="false"/>
             <h:outputText id="Higher" value="Higher!" rendered="#{game.number gt game.guess and game.guess ne 0}"/>
             <h:outputText id="Lower" value="Lower!" rendered="#{game.number lt game.guess and game.guess ne 0}"/>
          </div>
   
          <div(3)>
             Estoy pensando un número entre #{game.smallest} y #{game.biggest}.
             Le quedan #{game.remainingGuesses} de intentos.
          </div>
     
          <div>
             S(4)u número: 
             <h:inputText id="inputGuess" 
                          value="#{game.guess}" 
                          required="true" 
                          size="3" 
              (5)            disabled="#{game.number eq game.guess}">
                <f:validateLongRange maximum="#{game.biggest}" 
                                     minimum="#{game.smallest}"/>
             <(6)/h:inputText>
            <h:commandButton id="GuessButton"  
                             value="Guess" 
                             action="#{game.check}" 
                             disabled="#{game.number eq game.guess}"/>
          </div>
          <div>
            <h:commandButton id="RestartButton" value="Reset" action="#{game.reset}" immediate="true" />
          </div>
       </h:form>
    </ui:define>
  </ui:composition>
</html
>
1

Facelets es un lenguaje de plantilla para JSF, aquí se delimita la página en una plantilla que define el encabezado.

2

Hay una cantidad de mensajes que pueden ser enviados al usuario, "Mayor!", "Menor!" y "Correcto!"

3

Mientras el usuario adivina, el rango de números que puede adivinar se vuelve cada vez más pequeño-esta oración cambia para estar seguros de que el usuario sabe en qué rango debe adivinar.

4

Este campo de entrada está vinculado a un Web Bean, utilizando la expresión de valor.

5

Un rango de validador se utiliza para garantizar que el usuario por accidente no entre un número fuera del rango en el que se puede adivinar - si el validador no estuviera aquí, el usuario podría tratar de adivinar fuera del rango.

6

Y, obviamente, debe haber una forma para que el usuario pueda enviar el número al servidor. Aquí nos vincularnos a un método de acción en el Web Bean.

El ejemplo existe de 4 clases, las primeras dos son tipos de enlace. Primero, hay un tipo de enlace @Random, utilizado para inyectar un número aleatorio:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface Random {}

También hay un tipo de enlace @MaxNumber, utilizado para inyectar el número máximo posible:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface MaxNumber {}

La clase Generator es responsable de crear el número aleatorio, a través de un método de productor. También expone el número máximo posible a través del método de productor:

@ApplicationScoped

public class Generator {
   
   private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
   
   private int maxNumber = 100;
   
   java.util.Random getRandom()
   {
      return random;
   }
   
   @Produces @Random int next() { 
      return getRandom().nextInt(maxNumber); 
   }
   
   @Produces @MaxNumber int getMaxNumber()
   {
      return maxNumber;
   }
}

Notará que el Generador es una aplicación en ámbito por lo tanto no obtenemos un número aleatorio diferente cada vez.

El Web Bean final en la aplicación es la sesión en ámbito Juego.

Notará que hemos utilizado la anotación @Named, para poder utilizar el bean a través de EL en la página JSF. Por último, hemos utilizado la inyección de constructor para inicializar el juego con un número aleatorio. Y, claro está, necesitamos decirle al jugador cuando haya ganado, por lo tanto le damos retroalimentación con FacesMessage.

package org.jboss.webbeans.examples.numberguess;



import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.webbeans.AnnotationLiteral;
import javax.webbeans.Current;
import javax.webbeans.Initializer;
import javax.webbeans.Named;
import javax.webbeans.SessionScoped;
import javax.webbeans.manager.Manager;
@Named
@SessionScoped
public class Game
{
   private int number;
   
   private int guess;
   private int smallest;
   private int biggest;
   private int remainingGuesses;
   
   @Current Manager manager;
   
   public Game()
   {
   }
   
   @Initializer
   Game(@MaxNumber int maxNumber)
   {      
      this.biggest = maxNumber;
   }
   public int getNumber()
   {
      return number;
   }
   
   public int getGuess()
   {
      return guess;
   }
   
   public void setGuess(int guess)
   {
      this.guess = guess;
   }
   
   public int getSmallest()
   {
      return smallest;
   }
   
   public int getBiggest()
   {
      return biggest;
   }
   
   public int getRemainingGuesses()
   {
      return remainingGuesses;
   }
   
   public String check()
   {
      if (guess
>number)
      {
         biggest = guess - 1;
      }
      if (guess<number)
      {
         smallest = guess + 1;
      }
      if (guess == number)
      {
         FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
      }
      remainingGuesses--;
      return null;
   }
   
   @PostConstruct
   public void reset()
   {
      this.smallest = 0;
      this.guess = 0;
      this.remainingGuesses = 10;
      this.number = manager.getInstanceByType(Integer.class, new AnnotationLiteral<Random
>(){});
   }
   
}

El ejemplo de traductor tomará las oraciones que entre y las traducirá en Latín.

El ejemplo de traductor está incorporado como un EAR, y contiene EJB. Como resultado, su estructura es más compleja que el ejemplo de Numberguess.

Primero, demos una mirada al agregador EAR, el cual está localizado en el módulo webbeans-translator-ear. Maven genera automáticamente la application.xml:


<plugin>
   <groupId
>org.apache.maven.plugins</groupId>
   <artifactId
>maven-ear-plugin</artifactId>
   <configuration>
      <modules>
         <webModule>
            <groupId
>org.jboss.webbeans.examples.translator</groupId>
            <artifactId
>webbeans-translator-war</artifactId>
            <contextRoot
>/webbeans-translator</contextRoot>
         </webModule>
      </modules>
   </configuration>
</plugin
>

Aquí establecemos la ruta de contexto, la cual nos da una url interesante (http://localhost:8080/webbeans-translator).

Sugerencia

Si no está utilizando Maven para generar estos archivos, usted necesitaría META-INF/application.xml:


<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"
             version="5">
  <display-name
>webbeans-translator-ear</display-name>
  <description
>Ejemplo Ear para la implementaci�n de referencia de JSR 299: Web Beans</description>
  
  <module>
    <web>
      <web-uri
>webbeans-translator.war</web-uri>
      <context-root
>/webbeans-translator</context-root>
    </web>
  </module>
  <module>
    <ejb
>webbeans-translator.jar</ejb>
  </module>
</application
>

Next, lets look at the war. Just as in the numberguess example, we have a faces-config.xml (to enable Facelets) and a web.xml (to enable JSF) in WebContent/WEB-INF.

Más interesante aún es el facelet utilizado para traducir texto. Al igual que en el ejemplo de Numberguess tenemos una plantilla, la cual rodea el formulario (omitido aquí por razones de brevedad):


<h:form id="NumberGuessMain">
            
   <table>
      <tr align="center" style="font-weight: bold" >
         <td>
            Your text
         </td>
         <td>
            Translation
         </td>
      </tr>
      <tr>
         <td>
            <h:inputTextarea id="text" value="#{translator.text}" required="true" rows="5" cols="80" />
         </td>
         <td>
            <h:outputText value="#{translator.translatedText}" />
         </td>
      </tr>
   </table>
   <div>
      <h:commandButton id="button" value="Translate" action="#{translator.translate}"/>
   </div>
   
</h:form
>

El usuario puede entrar texto en el área de texto a mano izquierda y pulsar el botón de traducir para ver el resultado a la derecha.

Por último, veamos el módulo EJB, webbeans-translator-ejb. En src/main/resources/META-INF sólo hay un web-beans.xml vacío, utilizado para marcar el archivo como si contuviera Web Beans.

Hemos guardado la parte más interesante para el final, ¡el código! El proyecto tiene dos beans sencillos, SentenceParser y TextTranslator y dos beans empresariales, TranslatorControllerBean y SentenceTranslator. Por ahora, debe comenzar a familiarizarse con el aspecto de Web Bean, por lo tanto sólo destacaremos aquí las partes más interesantes.

Tanto SentenceParser como TextTranslator son beans dependientes, y TextTranslator utiliza inicialización de constructor:

public class TextTranslator { 

   private SentenceParser sentenceParser; 
   private Translator sentenceTranslator; 
   
   @Initializer
   TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) 
   { 
      this.sentenceParser = sentenceParser; 
      this.sentenceTranslator = sentenceTranslator;

TextTranslator es un bean con estado (con una interfaz local de negocios), donde lo mágico sucede - claro está, que no desarrollaramos un traductor completo, ¡pero le dimos una buena luz!

Por último, hay un controlador orientado a UI que recoge el texto desde el usuario y lo envía al traductor. Esta es una petición en ámbito, llamada bean con estado de sesión que inyecta el traductor.

@Stateful

@RequestScoped
@Named("translator")
public class TranslatorControllerBean implements TranslatorController
{
   
   @Current TextTranslator translator;

El bean también tiene capturadores y configuradores para todos los campos en la página.

Como este es un bean de sesión con estado, tenemos que tener un método de eliminación:

   @Remove

   public void remove()
   {
      
   }

El administrador de Web Beans llamará al método de eliminación cuando el bean sea destruido, en este caso al final de la petición.

That concludes our short tour of the Web Beans examples. For more on Web Beans , or to help out, please visit http://www.seamframework.org/WebBeans/Development.

Necesitamos ayuda en todas las áreas - corrección de errores, escritura de nuevas funciones, escritura de ejemplos y traducción de esta guía de referencia.

Web Beans soporta tres mecanismos primarios para inyección de dependencia:

Constructor de inyección de parámetro:

public class Checkout {

        
    private final ShoppingCart cart;
    
    @Initializer
    public Checkout(ShoppingCart cart) {
        this.cart = cart;
    }
}

Inyección de parámetro del método Inicializador:

public class Checkout {

        
    private ShoppingCart cart;
    @Initializer 
    void setShoppingCart(ShoppingCart cart) {
        this.cart = cart;
    }
    
}

E inyección directa de campo:

public class Checkout {


    private @Current ShoppingCart cart;
    
}

La inyección de dependencia siempre se presenta cuando la instancia de Web Bean es instanciada primero:

  • Primero, el administrador de Web Bean llama al constructor de Web Bean, para obtener una instancia del Web Bean.

  • Luego, el administrador de Web Bean inicializa los valores de los campos inyectados del Web Bean.

  • Más tarde, el administrador de Web Bean llama a todos los métodos inicializadores del Web Bean.

  • Por último, se llama al método de Web Bean @PostConstruct, si existe.

La inyección de parámetro constructor no es admitida por beans de EJB, porque EJB es instanciado por el contenedor de EJB, no por el administrador de Web Bean.

Los parámetros de constructores y métodos de inicializador no necesitan ser anotados explícitamente cuando se aplique el tipo de enlace predeterminado @Current. Los campos inyectados, sin embargo, deben especificar un tipo de enlace, cuando se aplique el tipo de enlace predeterminado. Si el campo no especifica ningún tipo de enlace, no será inyectado.

Los métodos de productor también admiten inyección de parámetro:

@Produces Checkout createCheckout(ShoppingCart cart) {

    return new Checkout(cart);
}

Por último, los métodos de observador (que encontraremos en Capítulo 9, Eventos), los métodos desechables y los métodos destructores, admiten inyección de parámetro.

La especificación de Web Beans define un procedimiento, llamado algoritmo de resolución de typesafe que el administrador de Web Bean sigue al identificar el Web Bean para inyectar a un punto de inyección. Este algoritmo parece complejo en un principio, pero una vez que lo entienda, es en realidad muy intuitivo. La resolución de Typesafe se realiza al inicializar el sistema, lo que significa que el administrador informará al usuario inmediatamente si se pueden cumplir las dependencias de un Web Bean, produciendo una UnsatisfiedDependencyException o una AmbiguousDependencyException.

El propósito de este algoritmo es permitir a múltiples Web Beans implementar el mismo tipo API ya sea:

  • permitiendo al cliente seleccionar la aplicación requerida mediante anotaciones de enlace,

  • permitiendo al desplegador de aplicación seleccionar la aplicación apropiada para una despliegue particular, sin cambios en el cliente, habilitando o inhabilitando los tipos de despliegue, o

  • permitiendo que una implementación de una API remplace otra implementación de la misma API en el momento del despliegue, sin cambios al cliente, mediante prioridad de tipo de despliegue.

Exploremos cómo el administrador de Web Beans determina una Web Bean para ser inyectado.

Si tenemos más de un Web Bean que implemente un tipo determinado de API, el punto de inyección puede especificar el Web Bean que debe ser inyectado mediante una anotación de enlace. Por ejemplo, deberían haber dos aplicaciones del PaymentProcessor:

@PayByCheque

public class ChequePaymentProcessor implements PaymentProcessor {
    public void process(Payment payment) { ... }
}
@PayByCreditCard

public class CreditCardPaymentProcessor implements PaymentProcessor {
    public void process(Payment payment) { ... }
}

Donde @PayByCheque y @PayByCreditCard son anotaciones de enlace:

@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayByCheque {}
@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayByCreditCard {}

Un desarrollador de cliente de Web Bean utiliza la anotación de enlace para especificar exactamente el Web Bean que debe inyectarse.

Uso de inyección de campo:

@PayByCheque PaymentProcessor chequePaymentProcessor;

@PayByCreditCard PaymentProcessor creditCardPaymentProcessor;

Uso de inyección de método inicializador:

@Initializer

public void setPaymentProcessors(@PayByCheque PaymentProcessor chequePaymentProcessor, 
                                 @PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
   this.chequePaymentProcessor = chequePaymentProcessor;
   this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}

O uso de inyección de constructor:

@Initializer

public Checkout(@PayByCheque PaymentProcessor chequePaymentProcessor, 
                @PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
   this.chequePaymentProcessor = chequePaymentProcessor;
   this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}

Todos los Web Beans tienen un tipo de despliegue. Cada tipo de despliegue identifica un conjunto de Web Beans que debería ser instalado bajo condiciones en algunos despliegues del sistema.

Por ejemplo, podríamos definir un tipo de despliegue llamado @Mock, el cual identificaría Web Beans que deben ser instalados sólo cuando el sistema se ejecute dentro de un entorno de pruebas de integración:

@Retention(RUNTIME)

  @Target({TYPE, METHOD})
  @DeploymentType
  public @interface Mock {}

Supongamos que tenemos algunos Web Bean que interactuaron con un sistema externo para procesar pagos:

public class ExternalPaymentProcessor {

        
    public void process(Payment p) {
        ...
    }
    
}

Como este Web Bean no especifica explícitamente un tipo de despliegue, tiene el tipo de despliegue predeterminado @Production.

Para prueba de integración o de unidad, el sistema externo está lento o no está disponible. Por lo tanto, creamos el objeto mock:

@Mock 

public class MockPaymentProcessor implements PaymentProcessor {
    @Override
    public void process(Payment p) {
        p.setSuccessful(true);
    }
}

Pero, ¿cómo determina el administrador de Web Bean la aplicación que debe utilizar en un despliegue determinado?

Si ha prestado atención, probablemente se estará preguntando cómo escoge Web Bean la aplicación — ExternalPaymentProcessor o MockPaymentProcessor — . Piense en lo que sucede cuando el administrador encuentra este punto de inyección:

@Current PaymentProcessor paymentProcessor

Ahora hay dos Web Beans que cumplen el contrato PaymentProcessor. Claro está que no podemos utilizar una anotación de enlace para explicar, porque las anotaciones de enlace están codificadas dentro de la fuente en el punto de inyección, y queremos que el administrador pueda decidir en el ¡momento de despliegue!

La solución a este problema es que cada tipo de despliegue tiene una prioridad diferente. La prioridad de los tipos de despliegue es determinada por el orden de aparición en web-beans.xml. En nuestro ejemplo, @Mock es posterior a @Production por lo tanto tiene mayor prioridad.

Cada vez que el administrador descubre que más de un Web Bean cumple el contrato (tipo API más anotaciones de enlace) especificado por un punto de inyección, considera la prioridad relativa de los Web Beans. Se escoge el Web Bean que tiene prioridad respecto de los otros para inyectar. Por lo tanto, en nuestro ejemplo, el administrador de Web Bean inyectará MockPaymentProcessor al ejecutar en nuestro entorno de prueba de integración (que es precisamente lo que queremos).

Es interesante comparar esta facilidad con las arquitecturas populares del administrador de hoy. Varios contenedores "ligeros" también permiten el despliegue condicional de clases existentes en el classpath, pero las clases que van a ser desplegadas deben ser explícitamente, listadas de modo individual en el código de configuración o en algún archivo de configuración XML. Web Beans no admite definición de Web Bean ni configuración vía XML, pero en el común de los casos donde no se requiere una configuración compleja, los tipos de despliegue permiten habilitar un conjunto completo de Web Beans con una sóla línea de XML. Mientras tanto, un desarrollador que esté navegando el código puede fácilmente identificar en qué escenarios de despliegue se utilizará el Web Bean.

Los clientes de un Web Bean inyectado no suelen mantener una referencia directa a una instancia de Web Bean.

Imagine que un Web Bean vinculado al ámbito de aplicación mantiene una referencia directa a un Web Bean vinculado al ámbito de petición. La aplicación en el ámbito de Web Bean es compartida entre muchas peticiones diferentes. No obstante, cada petición ¡debe ver una instancia diferente de la petición en el ámbito de WebBean!

Ahora imaginemos que un enlace de Web Bean a la sesión mantiene una referencia directa a un Web Bean enlazado al ámbito de la aplicación. De vez en cuando, el contexto de sesión se serializa al disco con el fin de utilizar la memoria de un modo más eficiente. Sin embargo, la aplicación en el ámbito de la instancia de Web Bean ¡no debe serializarse junto con la sesión en el ámbito de Web Bean!

Por lo tanto, a menos que un Web Bean tenga un ámbito predeterminado @Dependent, el administrador de Web Bean deberá direccionar indirectamente todas las referencias inyectadas al Web Bean a través del objeto de proxy. Este proxy de cliente responsable de garantizar que la instancia de Web Bean reciba un método de invocación es la instancia asociada con el contexto actual. El proxy de cliente también permite a los Web Beans vincularse a contextos tales como el contexto de sesión que debe serializarse al disco sin serializar de modo recursivo a otros Web Beans inyectados.

Lamentablemente, debido a limitaciones del lenguaje de Java, el administrador de Web Bean no puede utilizar proxy en algunos tipos de Java. Por lo tanto, el administrador de Web Bean produce un UnproxyableDependencyException si no se puede aplicar proxy al tipo de un punto de inyección.

El administrador de Web Bean no puede aplicar proxy en los siguientes tipos de Java:

Suele ser muy fácil corregir una UnproxyableDependencyException. Basta con añadir un constructor sin parámetros a la clase inyectada, introducir una interfaz, o cambiar el ámbito del Web Bean inyectado a @Dependent.

La aplicación puede obtener una instancia de la interfaz Manager a través de inyección:

@Current Manager manager;

El objeto Manager proporciona un grupo de métodos para obtener una instancia de Web Bean en forma programática.

PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class);

Las anotaciones de enlace se pueden especificar a través de subclasificaciones de la clase auxiliar AnnotationLiteral, porque de otra manera es difícil instanciar un tipo de anotación en Java.

PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class, 

                                               new AnnotationLiteral<CreditCard
>(){});

Si el tipo de vinculación tiene un miembro de anotación, no podemos utilizar una subclase anónima de AnnotationLiteral — en su lugar, necesitaremos crear una subclase llamada:

abstract class CreditCardBinding 

    extends AnnotationLiteral<CreditCard
> 
    implements CreditCard {}
PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class, 

                                               new CreditCardBinding() { 
                                                   public void value() { return paymentType; } 
                                               } );

Hay algunas clases de objetos — con ámbito @Dependent — que necesitan saber algo acerca del objeto o punto de inyección dentro del cual son inyectados para poder hacer lo que hacen. Por ejemplo:

Un Web Bean con ámbito @Dependent puede inyectar una instancia de InjectionPoint y acceder a metadatos relativos al punto de inyección al que pertenezca.

Veamos un ejemplo. El código siguiente es detallado, y vulnerable a problemas de refactorización:

Logger log = Logger.getLogger(MyClass.class.getName());

Este método inteligente de productor permite inyectar un Logger JDK sin especificar explícitamente la categoría de registro:

class LogFactory {


   @Produces Logger createLogger(InjectionPoint injectionPoint) { 
      return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); 
   }
}

Ahora podemos escribir:

@Current Logger log;

¿No está convencido? Entonces, veamos un segundo ejemplo. Para inyectar parámetros, necesitamos definir el tipo de vinculación:

@BindingType

@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface HttpParam {
   @NonBinding public String value();
}

Utilizaríamos este tipo de enlace en puntos de inyección, como a continuación:

@HttpParam("username") String username;

@HttpParam("password") String password;

El siguiente método de productor sí funciona:

class HttpParams


   @Produces @HttpParam("")
   String getParamValue(ServletRequest request, InjectionPoint ip) {
      return request.getParameter(ip.getAnnotation(HttpParam.class).value());
   }
}

(Observe que el miembro del valor() de la anotación HttpParam es ignorado por el administrador de Web Bean porque está anotado como @NonBinding.)

El administrador de Web Bean proporciona un Web Bean incorporado que implementa la interfaz InjectionPoint:

public interface InjectionPoint { 

   public Object getInstance(); 
   public Bean<?> getBean(); 
   public Member getMember(): 
   public <extends Annotation
> T getAnnotation(Class<T
> annotation); 
   public Set<extends Annotation
> getAnnotations(); 
}

Hasta ahora, hemos visto algunos ejemplos de anotaciones de tipo ámbito. El ámbito de un Web Bean determina el ciclo de vida de instancias del Web Bean. El ámbito también determina qué clientes se refieren a qué instancias del Web Bean. Según la especificación de Web Beans, un ámbito determina:

  • Cuándo se crea una nueva instancia de un Web Bean con ese ámbito

  • Cuándo se destruye una instancia existente de cualquier Web Bean con ese ámbito

  • Qué referencias se refieren a una instancia de un Web Bean con ese ámbito

Por ejemplo, si tenemos una sesión con ámbito Web Bean, UsuarioActual, todos los Web Beans que son llamados en el contexto de la misma HttpSession verán la misma instancia del UsuarioActual. Dicha instancia se creará automáticamente la primera vez que se necesite un UsuarioActual en esa sesión, y se destruirá automáticamente al terminar la sesión.

El ámbito de conversación de Web Beans es un poco parecido al ámbito de sesión tradicional en que mantiene el estado asociado con el usuario del sistema y abarca varias peticiones al servidor. Sin embargo, a diferencia del ámbito de sesión, el ámbito de conversación:

Una conversación representa una tarea, una unidad de trabajo desde el punto de vista del usuario. El contexto de conversación mantiene un estado asociado con lo que el usuario está actualmente trabajando. Si el usuario está trabajando en varias tareas al mismo tiempo, habrá múltiples conversaciones.

El contexto de conversación está activo durante cualquier petición de JSF. Sin embargo, la mayoría de las conversaciones se destruyen al final de la petición. Si una conversación debe mantener un estado a través de múltiples peticiones, debe ser explícitamente promovida a conversación-larga.

Web Beans ofrece un Web Bean incorporado para controlar el ciclo de vida de conversaciones en una aplicación JSF. Dicho Web Bean puede obtenerse por inyección:

@Current Conversation conversation;

Para iniciar la conversación asociada con la petición actual a una conversación larga, llame al método begin() desde el código de aplicación. Para programar que el actual contexto de conversación larga se destruya al final de la petición actual, llame a end().

En el ejemplo a continuación, un Web Bean de conversación en ámbito controla la conversación con la que está asociada.

@ConversationScoped @Stateful

public class OrderBuilder {
    private Order order;
    private @Current Conversation conversation;
    private @PersistenceContext(type=EXTENDED) EntityManager em;
    
    @Produces public Order getOrder() {
        return order;
    }
    public Order createOrder() {
        order = new Order();
        conversation.begin();
        return order;
    }
    
    public void addLineItem(Product product, int quantity) {
        order.add( new LineItem(product, quantity) );
    }
    public void saveOrder(Order order) {
        em.persist(order);
        conversation.end();
    }
    
    @Remove
    public void destroy() {}
    
}

Este Web Bean puede controlar su propio ciclo de vida mediante la API de Conversación. No obstante, algunos otros Web Beans tienen un ciclo de vida que depende totalmente de otro objeto.

Además de los cuatro ámbitos incorporados, Web Beans ofrece el ámbito seudo dependiente. Este es el ámbito para el Web Bean que no declare explícitamente un tipo de ámbito.

Por ejemplo, este Web Bean tiene el ámbito de tipo @Dependent:

public class Calculator { ... }

Cuando un punto de inyección de un Web Bean apunta a un Web Bean dependiente, una nueva instancia del Web Bean dependiente es creada cada vez que el primer Web Bean sea instanciado. Las instancias de Web Beans dependientes nunca se comparten entre Web Beans o puntos diferentes de inyección. Ellas son objetos dependientes de alguna otra instancia de Web Bean.

Las instancias dependientes de Web Bean se destruyen cuando la instancia de la que dependen es destruida.

Web Beans facilita la obtención de una instancia dependiente de una clase de Java o bean EJB, incluso si la clase o el bean EJB ya se declaró como Web Bean con algún otro tipo de ámbito.

Los métodos de productor nos permiten sobrepasar algunas limitaciones que se presentan cuando el administrador de Web Bean, en lugar de la aplicación, es responsable de instanciar objetos. También son la forma más fácil de integrar objetos que no son Web Beans dentro del entorno de Web Beans. (Veremos un segundo método en Capítulo 12, Definición de Web Beans utilizando XML.)

Según las especificaciones:

Un productor de Web Beans actúa como fuente de objetos a ser inyectados, donde:

  • los objetos que van a ser inyectados no necesitan ser instancias de Web Beans,

  • el tipo concreto de objetos a ser inyectado puede variar en tiempo de ejecución o

  • los objetos requieren alguna especificación de inicialización que no es realizada por el constructor de Web Bean

Por ejemplo, los métodos de productor nos permiten:

  • exponer una entidad JPA como un Web Bean,

  • exponer cualquier clase JDK como un Web Bean,

  • definir múltiples Web Beans, con diferentes ámbitos o inicialización para la misma clase de implementación, o

  • variar la implementación de un tipo API en tiempo de ejecución.

En particular, los métodos de productor nos permiten utilizar polimorfismo de tiempo de ejecución con Web Beans. Como hemos visto, los tipos de despliegue son una solución potente para el problema del polimorfismo de tiempo de despliegue. Sin embargo, una vez que el sistema es desplegado, la implementación de Web Bean es corregida. El método de productor no tiene dicha limitación:

@SessionScoped

public class Preferences {
    
    private PaymentStrategyType paymentStrategy;
    
    ...
    
    @Produces @Preferred 
    public PaymentStrategy getPaymentStrategy() {
        switch (paymentStrategy) {
            case CREDIT_CARD: return new CreditCardPaymentStrategy();
            case CHEQUE: return new ChequePaymentStrategy();
            case PAYPAL: return new PayPalPaymentStrategy();
            default: return null;
        } 
    }
    
}

Consider an injection point:

@Preferred PaymentStrategy paymentStrat;

Este punto de inyección tiene el mismo tipo de anotaciones de enlace que el método de productor, por lo tanto apunta al método de productor mediante las reglas de inyección usuales de Web Beans. El método del productor será llamado por el administrador de Web Bean a fin de obtener una instancia para servir a este punto de inyección.

.

No hay un problema en potencia con el código anterior. Las implementaciones de CreditCardPaymentStrategy son instanciadas mediante el operador nuevo de Java. Los objetos instanciados directamente por la aplicación no pueden hacer uso de la inyección de dependencia y no tienen interceptores.

Si esto no es lo que deseamos podemos utilizar la inyección de dependencia dentro del método del productor para obtener las instancias de Web Bean:

@Produces @Preferred @SessionScoped

public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          ChequePaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    } 
}

Espere, ¿qué sucede si CreditCardPaymentStrategy es una petición en ámbito de Web Bean? Entonces el método del productor tiene el efecto de "promover" la instancia de petición en ámbito actual dentro del ámbito de sesión. ¡Esto casi seguro es un error! El objeto en ámbito de petición será destruido por el administrador de Web Bean antes de finalizar la sesión, pero la referencia al objeto se dejará "colgando" en el ámbito de sesión. Este error no será detectado por el administrador de Web Bean, entonces, ¡por favor tenga un cuidado especial al retornar instancias de Web Bean desde métodos de productor!

Hay por lo menos tres formas de corregir este error. Podemos cambiar el ámbito de la implementación CreditCardPaymentStrategy, pero podría afectar a otros clientes de ese Web Bean. Una mejor opción sería cambiar el ámbito del método del productor a @Dependent o @RequestScoped.

Pero una solución más común es utilizar la anotación especial de enlace @New.

El primer tema importante de Web Beans es el acoplamiento flexible. Ya hemos visto tres medios para lograr dicho acoplamiento:

Estas técnicas sirven para habilitar el acoplamiento flexible de cliente y servidor. El cliente ya no está estrechamente ligado a una implementación de un API, ni tiene que administrar el ciclo de vida del objeto del servidor. Este método permite interactuar a los objetos con estado como si fueran servicios.

El acoplamiento flexible hace más dinámico a un sistema. El sistema puede responder al cambio de una manera bien definida. En el pasado, los marcos que trataban de ofrecer los servicios listados arriba invariablemente lo hacían sacrificando la seguridad. Web Beans es la primera tecnología que logra este nivel de acoplamiento flexible en una forma typesafe.

Web Beans ofrece tres servicios adicionales importantes que amplían el objetivo del acoplamiento flexible:

En primer lugar, exploremos los interceptores.

Web Beans reutiliza el interceptor de arquitectura básico de EJB 3.0, extendiendo la funcionalidad en dos direcciones:

  • Cualquier Web Bean puede tener interceptores, no sólo beans de sesión.

  • Web Beans ofrece un método de anotación más sofisticado para interceptores de enlace a Web Beans.

La especificación EJB define dos clases de puntos de interceptación:

  • la interceptación de método de negocios y

  • la interceptación de devolución de llamadas de ciclo de vida

Un interceptor de método de negocios se aplica a invocaciones de métodos del Web Bean por clientes del Web Bean:

public class TransactionInterceptor {

    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

Un interceptor de devolución de llamadas de ciclo de vida se aplica a invocaciones de devolución de llamadas de ciclo de vida por el contenedor:

public class DependencyInjectionInterceptor {

    @PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}

Una clase de interceptor puede interceptar métodos de devolución de llamadas de ciclo de vida y métodos de negocios.

Los interceptores son una forma potente de capturar y distinguir cuestiones que son ortogonales al tipo de sistema. Cualquier interceptor puede interceptar invocaciones de cualquier tipo de Java. Esto los hace perfectos para resolver cuestiones técnicas como la administración de transacción y la seguridad. Sin embargo, por naturaleza, los interceptores desconocen la semántica real de los eventos que interceptan. Por lo tanto, los interceptores no son una herramienta apropiada para distinguir cuestiones relacionadas con negocios.

En cambio, los decoradores interceptan invocaciones únicamente para una determinada interfaz de Java y por lo tanto, conocen toda la semántica asociada a la interfaz. Esto los hace una herramienta perfecta para representar algunas clases de cuestiones de negocios. También significa que los decoradores no tienen la generalidad de un interceptor. Los decoradores no pueden resolver problemas técnicos que atraviesan muchos tipos dispares.

Supongamos que tenemos una interfaz que representa cuentas:

public interface Account {

    public BigDecimal getBalance();
    public User getOwner();
    public void withdraw(BigDecimal amount);
    public void deposit(BigDecimal amount);
}

Varios Web Beans diferentes en nuestro sistema implementan la interfaz de Cuenta. No obstante, tenemos un requisito legal que, para cualquier clase de cuenta, las transacciones grandes deben ser registradas por el sistema en un registro especial. Este es el trabajo perfecto para un decorador.

Un decorador es un Web Bean sencillo que implementa el tipo que decora y es anotado @Decorator.

@Decorator

public abstract class LargeTransactionDecorator 
        implements Account {
    
    @Decorates Account account;
    
    @PersistenceContext EntityManager em;
    
    public void withdraw(BigDecimal amount) {
        account.withdraw(amount);
        if ( amount.compareTo(LARGE_AMOUNT)
>0 ) {
            em.persist( new LoggedWithdrawl(amount) );
        }
    }
    
    public void deposit(BigDecimal amount);
        account.deposit(amount);
        if ( amount.compareTo(LARGE_AMOUNT)
>0 ) {
            em.persist( new LoggedDeposit(amount) );
        }
    }
    
}

A diferencia de otros Web Beans sencillos, un decorador puede ser una clase abstracta. Si no hay nada especial que el decorador tenga que hacer para un método particular de la interfaz decorada, usted no necesita implementar ese método.

La notificación de eventos de Web Beans permite a Web Beans interactuar de una manera completamente disociada. Los productores crean eventos que son enviados luego a observadores de evento por el administrador de Web Beans. Este esquema básico podría parecerse al patrón conocido observador/observable, pero hay un par de cambios:

  • no solamente los productores de eventos están disociados de los observadores; los observadores están completamente disociados de los productores.

  • los observadores pueden especificar una combinación de "selectores" para limitar el conjunto de notificaciones de eventos que recibirán y

  • los observadores pueden ser notificados inmediatamente o pueden especificar que la entrega del evento sea retrasada hasta el final de la transacción actual

El productor de evento puede obtener un objeto que notifica el eventopor inyección:

@Observable Event<Document

> documentEvent

La anotación @Observable define implícitamente un Web Bean con ámbito @Dependent y tipo de despliegue @Standard, con una implementación provista por el administrador de Web Bean.

Un productor crea eventos llamando al método fire() de la interfaz del Evento, pasando un objeto de evento:

documentEvent.fire(document);

Un objeto de evento puede ser una instancia de una clase de Java que no tiene variables de tecla o parámetros de comodines. El evento será entregado a cada método de observador que:

El administrador de Web Beans simplemente llama a todos los métodos de observador, pasando el objeto del evento como el valor de un parámetro de evento. Si cualquier método de observador produce una excepción, el administrador de Web Beans se detiene llamando a los métodos de observador y la excepción es reenviada por el método fire().

Para especificar un "selector", el productor del evento puede pasar una instancia del tipo de enlace del evento al método fire():

documentEvent.fire( document, new AnnotationLiteral<Updated

>(){} );

La Anotación Literal clase auxiliar hace posible crear una instancia de tipos de enlaces en línea, ya que de otra manera es difícil hacerlo en Java.

El evento será entregado a cada método de observador que:

De modo alternativo, se pueden especificar eventos de enlaces anotando el punto de inyección de notificador de eventos:

@Observable @Updated Event<Document

> documentUpdatedEvent

Luego cada evento disparado vía esta instancia de Evento tiene el enlace de evento anotado. El evento será enviado a cada método de observador que:

Los observadores transaccionales reciben las notificaciones de sus eventos antes o después de la fase de finalización de la transacción en la se creó el evento. Por ejemplo, el siguiente método de observador necesita actualizar un conjunto de resultados de petición almacenado en caché en el contexto de aplicación, pero sólo cuando las transacciones que actualizan el árbol de Categoría tengan éxito:

public void refreshCategoryTree(@AfterTransactionSuccess @Observes CategoryUpdateEvent event) { ... }

Hay tres clases de observadores transaccionales:

Los observadores transaccionales son muy importantes en un modelo de objetos con estado como Web Beans, porque el estado suele ser mantenido para más de una transacción atómica.

Imagine que hemos almacenado en caché una serie de resultados de petición JPA en el ámbito de la aplicación:

@ApplicationScoped @Singleton

public class Catalog {
    @PersistenceContext EntityManager em;
    
    List<Product
> products;
    @Produces @Catalog 
    List<Product
> getCatalog() {
        if (products==null) {
            products = em.createQuery("select p from Product p where p.deleted = false")
                .getResultList();
        }
        return products;
    }
    
}

De vez en cuando, se crea o borra un Producto. Cuando esto ocurre, necesitamos refrescar el catálogo del Producto. No obstante, deberíamos esperar hasta después de que la transacción finalice exitosamente antes de ¡actualizar!

El Web Bean que crea y borra Productos podría crear eventos, por ejemplo:

@Stateless

public class ProductManager {
    @PersistenceContext EntityManager em;
    @Observable Event<Product
> productEvent;
    public void delete(Product product) {
        em.delete(product);
        productEvent.fire(product, new AnnotationLiteral<Deleted
>(){});
    }
    
    public void persist(Product product) {
        em.persist(product);
        productEvent.fire(product, new AnnotationLiteral<Created
>(){});
    }
    
    ...
    
}

Ahora el Catálogo puede observar los eventos después de finalizar la transacción exitosamente:

@ApplicationScoped @Singleton

public class Catalog {
    ...
    
    void addProduct(@AfterTransactionSuccess @Observes @Created Product product) {
        products.add(product);
    }
    
    void addProduct(@AfterTransactionSuccess @Observes @Deleted Product product) {
        products.remove(product);
    }
    
}

El segundo tema importante de Web Beans es el tecleado fuerte. La información acerca de dependencias, interceptores y decoradores de Web Bean y la información sobre consumidores de eventos para un productor de eventos, está contenida en construcciones de Java typesafe que pueden ser validadas por el compilador.

No necesita ver los identificadores de cadena en código de Web Beans, no porque el marco los esté escondiendo del uso inteligente de las reglas — llamadas"configuración por convención" — sino porque simplemente ¡no hay cadenas con qué comenzar!

El beneficio evidente de este método es que cualquier IDE puede proporcionar autofinalización, validación y refactorización sin necesitar herramientas especiales. Pero hay un segundo beneficio menos evidente. Resulta que cuando empieza a pensar en identificación de objetos, eventos o interceptores a través de anotaciones en lugar de nombres, tendrá la oportunidad de aumentar el nivel semántico de su código.

Web Beans le anima a desarrollar anotaciones que modelen conceptos, por ejemplo,

en lugar de utilizar nombres compuestos como

Las anotaciones son reutilizables. Ayudan a describir cualidades comunes de partes dispares del sistema. Nos ayudan a categorizar y entender nuestro código. Nos ayudan a tratar las cuestiones comunes en una forma común. Hacer nuestro código más leíble y entendible.

Los estereotipos de Web Beans van más allá de este paso. Un estereotipo modela un rol común en su arquitectura de aplicación. El estereotipo encapsula varias propiedades del rol, incluyendo ámbito, enlaces de interceptor, tipo de despliegue, etc, en un sólo paquete reutilizable.

Incluso metadatos XML de Web Beans es tecleado ¡fuertemente! No hay compilador para XML, por eso Web Beans aprovecha los esquemas XML para validar los tipos de Java y los atributos que aparecen en XML. Este enfoque hace que el archivo XML sea más leíble, así como las anotaciones lo hicieron con nuestro código de Java.

Ahora estamos listos para conocer otras funciones más avanzadas de Web Beans. Tenga en cuenta que estas funciones hacen a nuestro código más fácil de validar y más entendible. La mayoría del tiempo no se necesita realmente utilizarlas, pero si se utilizan de modo inteligente, se llegará a apreciar su poder.

Según la especificación de Web Beans:

En muchos sistemas, el uso de patrones arquitecturales produce una serie de roles de Web Beans recurrentes. Un estereotipo permite al desarrollador de marco identificar dicho rol y declarar algunos metadatos comunes para Web Beans con ese rol en un lugar central.

Un estereotipo encapsula cualquier combinación de:

  • un tipo de despliegue predeterminado,

  • un tipo de ámbito predeterminado,

  • una restricción en el ámbito de Web Bean,

  • un requisito que implementa el Web Bean o extiende un cierto tipo y

  • una serie de anotaciones de enlace del interceptor.

Un estereotipo puede también especificar que todos los Web Beans con el estereotipo tengan nombres de Web Beans predeterminados.

Un Web Bean puede declarar cero, uno o múltiples estereotipos.

Un estereotipo es un tipo de anotación Java. Dicho estereotipo identifica clases de acción en algún marco MVC:

@Retention(RUNTIME)

@Target(TYPE)
@Stereotype
public @interface Action {}

Utilizamos el estereotipo aplicando la anotación al Web Bean.

@Action 

public class LoginAction { ... }

Hemos visto cómo el modelo de inyección de dependencia de Web Beans nos permite omitir la implementación de un API en el momento del despliegue. Por ejemplo, la siguiente Web Bean empresarial provee una implementación del Procesador de Pago de API en producción:

@CreditCard @Stateless

public class CreditCardPaymentProcessor 
        implements PaymentProcessor {
    ...
}

Pero en nuestro entorno, omitimos esa implementación de PaymentProcessor con un Web Bean diferente:

@CreditCard @Stateless @Staging

public class StagingCreditCardPaymentProcessor 
        implements PaymentProcessor {
    ...
}

Lo que hemos tratado de hacer con StagingCreditCardPaymentProcessor es remplazar totalmente a AsyncPaymentProcessor en un despliegue particular del sistema. En ese despliegue, el tipo de despliegue @Staging tendría una prioridad más alta que el tipo de despliegue predeterminado @Production, y por ende los clientes con el siguiente punto de inyección:

@CreditCard PaymentProcessor ccpp

Recibirían una instancia de StagingCreditCardPaymentProcessor.

Lamentablemente, hay varias trampas en las que se puede caer fácilmente:

  • el Web Bean de prioridad más alta puede que no implemente todos los tipos de API del Web Bean que intenta omitir,

  • el Web Bean de prioridad más alta puede que no declare todos los tipos de enlace del Web Bean que intenta omitir,

  • el Web Bean de prioridad más alta puede que no tenga el mismo nombre que el Web Bean que intenta omitir, o

  • el Web Bean que intenta omitir podría declarar un método de productor, método desechable o método de observador.

En cada uno de estos casos, el Web Bean que hemos tratado de omitir se podría llamar aún en el tiempo de ejecución. Por lo tanto, la omisión de alguna manera tiende a desarrollar error.

Web Beans ofrece una función especial, llamada Specialization, la cual ayuda al desarrollador a evitar estas trampas. Specialization parece un poco esotérica al comienzo, pero es fácil de utilizar en la práctica y realmente apreciará la seguridad adicional que proporciona.

Hasta ahora hemos visto varios ejemplos de Web Beans declarados mediante anotaciones. No obstante, hay un par de ocasiones en que no podemos utilizar anotaciones para definir el Web Bean.

  • cuando la clase de implementación viene de alguna biblioteca preexistente, o

  • cuando debe haber múltiples Web Beans con la misma clase de implementación.

En estos casos, Web Beans nos ofrece dos opciones:

  • escribir un método de productor, o

  • declarar el Web Bean utilizando XML

Muchos marcos utilizan XML para proporcionar metadatos relacionados con clases de Java. Sin embargo, Web Beans usa un método muy diferente para especificar los nombres de clases de Java, campos o métodos para la mayoría de otros marcos. En lugar de escribir nombres de clase y miembro como valores de cadena de elementos XML y atributos, Web Beans le permite utilizar el nombre de clase o miembro como el nombre del elemento XML.

La ventaja de este enfoque es que se puede escribir un esquema de XML que evita errores de ortografía en su documento de XML. Es incluso posible que una herramienta genere automáticamente el esquema XML desde el código de Java compilado. O, un entorno de desarrollo integrado podría realizar la misma validación sin la necesidad de una etapa de generación intermedia explícita.

El tercer tema de Web Beans es la integración. Web Beans fue diseñado para trabajar junto con otras tecnologías, ayudando al desarrollador de la aplicación a encajar en otras tecnologías.Web Beans es una tecnología abierta. Forma parte de un ecosistema de Java EE y es por si mismo una base para un nuevo ecosistema de extensiones portátiles e integración con marcos y tecnologías existentes.

Ya hemos visto cómo Web Beans ayuda a integrar EJB y JSF, permitiendo a los EJB enlazarse directamente a páginas JSF. Esto es apenas el comienzo. Web Beans ofrece el mismo potencial a otras tecnologías, tales como motores de administración de proceso de negocios, otros marcos de red y modelos de componentes de terceras partes. La plataforma de Java EE nunca podrá estandarizar todas las tecnologías interesantes utilizadas en el mundo del desarrollo de la aplicación Java, pero Web Beans facilita el uso de las tecnologías que aún no hacen parte completamente de la plataforma dentro del entorno Java EE.

Ya estamos a punto de ver cómo aprovechar completamente la plataforma de Java EE en una aplicación que utiliza Web Beans. También veremos brevemente una serie de SPI provistas para soportar extensiones a Web Beans. Puede que nunca las tenga que utilizar directamente, pero es conveniente saber que están allí si se necesitan. Lo más importante, es que podrá aprovecharlas indirectamente cada vez que utilice una extensión de terceras partes.

Los Web Beans están totalmente integrados en un entorno de Java EE. Los Web Beans tienen acceso a recursos de Java EE y a contextos persistentes de JPA. Se pueden ser utilizar en expresiones Unificadas EL en páginas JSF y JSP. Pueden ser inyectados en algunos objetos, tales como Servlets y Message Driven Beans, los cuales no son Web Beans.

Enviar mensajes mediante JMS puede ser bastante complejo, debido al número de objetos diferentes que se tienen que manejar. Para colas tenemos Queue, QueueConnectionFactory, QueueConnection, QueueSession y QueueSender. Para temas tenemos Topic, TopicConnectionFactory, TopicConnection, TopicSession y TopicPublisher. Cada uno de estos objetos tiene su propio ciclo de vida y modelo de hilos de los cuales tenemos que preocuparnos.

Los Web Beans se encargan de eso por nosotros. Todo lo que se necesita es reportar la cola o tópico en web-beans.xml, especificando un tipo de enlace y conexión de fábrica.


<Queue>
    <destination
>java:comp/env/jms/OrderQueue</destination>
    <connectionFactory
>java:comp/env/jms/QueueConnectionFactory</connectionFactory>
    <myapp:OrderProcessor/>    
</Queue
>

<Topic>
    <destination
>java:comp/env/jms/StockPrices</destination>
    <connectionFactory
>java:comp/env/jms/TopicConnectionFactory</connectionFactory>
    <myapp:StockPrices/>    
</Topic
>

Ahora podemos inyectar Queue, QueueConnection, QueueSession o QueueSender para una cola, o Topic, TopicConnection, TopicSession oTopicPublisher para un tema.

@OrderProcessor QueueSender orderSender;

@OrderProcessor QueueSession orderSession;
public void sendMessage() {
    MapMessage msg = orderSession.createMapMessage();
    ...
    orderSender.send(msg);
}
@StockPrices TopicPublisher pricePublisher;

@StockPrices TopicSession priceSession;
public void sendMessage(String price) {
    pricePublisher.send( priceSession.createTextMessage(price) );
}

El ciclo de vida de objetos JMS inyectados es controlado por el administrador deWeb Bean.

Web Beans pretende ser una plataforma para marcos, extensiones e integración con otras tecnologías. Por lo tanto, Web Beans expone una serie de SPI para el uso de desarrolladores de extensiones portátiles para Web Beans. Por ejemplo, las siguientes clases de extensiones fueron previstas por los diseñadores de Web Beans:

  • integración con motores de Gestión de Proceso de Negocios,

  • integración con marcos de terceras partes tales como Spring, Seam, GWT o Wicket, y

  • nueva tecnología basada en el modelo de programación de Web Beans.

El centro nervioso para extender Web Beans es el objeto Manager.

La interfaz Manager nos permite registrar y obtener Web Beans, interceptores, decoradores, observadores y contextos en forma programada.

public interface Manager

{
   public <T
> Set<Bean<T
>
> resolveByType(Class<T
> type, Annotation... bindings);
   public <T
> Set<Bean<T
>
> resolveByType(TypeLiteral<T
> apiType,
         Annotation... bindings);
   public <T
> T getInstanceByType(Class<T
> type, Annotation... bindings);
   public <T
> T getInstanceByType(TypeLiteral<T
> type,
         Annotation... bindings);
   public Set<Bean<?>
> resolveByName(String name);
   public Object getInstanceByName(String name);
   public <T
> T getInstance(Bean<T
> bean);
   public void fireEvent(Object event, Annotation... bindings);
   public Context getContext(Class<? extends Annotation
> scopeType);
   public Manager addContext(Context context);
   public Manager addBean(Bean<?> bean);
   public Manager addInterceptor(Interceptor interceptor);
   public Manager addDecorator(Decorator decorator);
   public <T
> Manager addObserver(Observer<T
> observer, Class<T
> eventType,
         Annotation... bindings);
   public <T
> Manager addObserver(Observer<T
> observer, TypeLiteral<T
> eventType,
         Annotation... bindings);
   public <T
> Manager removeObserver(Observer<T
> observer, Class<T
> eventType,
         Annotation... bindings);
   public <T
> Manager removeObserver(Observer<T
> observer,
         TypeLiteral<T
> eventType, Annotation... bindings);
   public <T
> Set<Observer<T
>
> resolveObservers(T event, Annotation... bindings);
   public List<Interceptor
> resolveInterceptors(InterceptionType type,
         Annotation... interceptorBindings);
   public List<Decorator
> resolveDecorators(Set<Class<?>
> types,
         Annotation... bindings);
}

Podemos obtener una instancia de Manager vía inyección:

@Current Manager manager

Actualmente la IR de Web Beans sólo se ejecuta en JBoss AS 5; integrando la IR dentro de otros entornos EE (por ejemplo otro servidor de aplicación como Glassfish), dentro de un contenedor de servlet (como Tomcat), o con una implementación incrustada EJB3.1 es bastante fácil. En este apéndice veremos brevemente los pasos requeridos.

El SPI de Web Beans está ubicado en el módulo webbeans-ri-spi, empaquetado como webbeans-ri-spi.jar. Algunos SPI son opcionales, si necesita anular la conducta predeterminada, se requerirán otros.

Todas las interfaces en el patrón decorador SPI soportan y proporcionan una clase Forwarding.

La IR de Web Beans también delega el descubrimiento de bean EJB3 al contenedor para no tener que examinar las anotaciones EJB3 o analizar ejb-jar.xml. Para cada EJB en la aplicación se debe descubrir un EJBDescriptor:

public interface EjbDiscovery

{
   public static final String PROPERTY_NAME = EjbDiscovery.class.getName();
   
   /**
    * Gets a descriptor for each EJB in the application
    * 
    * @return The bean class to descriptor map 
    */
   public Iterable<EjbDescriptor<?>
> discoverEjbs();
   
}
public interface EjbDescriptor<T

> {
   
   /**
    * Gets the EJB type
    * 
    * @return The EJB Bean class
    */
   public Class<T
> getType();
   /**
    * Gets the local business interfaces of the EJB
    * 
    * @return An iterator over the local business interfaces
    */
   public Iterable<BusinessInterfaceDescriptor<?>
> getLocalBusinessInterfaces();
   
   /**
    * Gets the remote business interfaces of the EJB
    * 
    * @return An iterator over the remote business interfaces
    */
   public Iterable<BusinessInterfaceDescriptor<?>
> getRemoteBusinessInterfaces();
   
   /**
    * Get the remove methods of the EJB
    * 
    * @return An iterator over the remove methods
    */
   public Iterable<Method
> getRemoveMethods();
   /**
    * Indicates if the bean is stateless
    * 
    * @return True if stateless, false otherwise
    */
   public boolean isStateless();
   /**
    * Indicates if the bean is a EJB 3.1 Singleton
    * 
    * @return True if the bean is a singleton, false otherwise
    */
   public boolean isSingleton();
   /**
    * Indicates if the EJB is stateful
    * 
    * @return True if the bean is stateful, false otherwise
    */
   public boolean isStateful();
   /**
    * Indicates if the EJB is and MDB
    * 
    * @return True if the bean is an MDB, false otherwise
    */
   public boolean isMessageDriven();
   /**
    * Gets the EJB name
    * 
    * @return The name
    */
   public String getEjbName();
   
   
}

El EjbDescriptor es bastante auto explicativo y debería devolver los metadatos pertinentes como se define en la especificación de EJB. Además de estas dos interfaces, está BusinessInterfaceDescriptor, la cual representa una interfaz de negocios local (encapsulando la clase de interfaz y el nombre de jndi a la búsqueda de una instancia del EJB).

The Web Beans RI must delegate JTA activities to the container. The SPI provides a couple hooks to easily achieve this with the TransactionServices interface.

public interface TransactionServices

{
   /**
    * Possible status conditions for a transaction. This can be used by SPI
    * providers to keep track for which status an observer is used.
    */
   public static enum Status
   {
      ALL, SUCCESS, FAILURE
   }
   /**
    * Registers a synchronization object with the currently executing
    * transaction.
    * 
    * @see javax.transaction.Synchronization
    * @param synchronizedObserver
    */
   public void registerSynchronization(Synchronization synchronizedObserver);
   /**
    * Queries the status of the current execution to see if a transaction is
    * currently active.
    * 
    * @return true if a transaction is active
    */
   public boolean isTransactionActive();
}

The enumeration Status is a convenience for implementors to be able to keep track of whether a synchronization is supposed to notify an observer only when the transaction is successful, or after a failure, or regardless of the status of the transaction.

Any javax.transaction.Synchronization implementation may be passed to the registerSynchronization() method and the SPI implementation should immediately register the synchronization with the JTA transaction manager used for the EJBs.

To make it easier to determine whether or not a transaction is currently active for the requesting thread, the isTransactionActive() method can be used. The SPI implementation should query the same JTA transaction manager used for the EJBs.

Hay una serie de requisitos que la IR de Web Beans ubica en el contenedor para el funcionamiento correcto que está fuera de la implementación de las API.

Aislamiento de classloader

Si se está integrando la IR de Web Beans dentro de un entorno que admite despliegue de varias aplicaciones, debe habilitar el aislamiento de classloader para cada aplicación de Web Beans, de forma automática o a través de la configuración del usuario.

Servlet listener and filters

Si usted está integrando el Web Beans en un entorno de Servlet debe registrar org.jboss.webbeans.servlet.WebBeansListener como oyente de Servlet, ya sea automáticamente o a través de la configuración de usuario, para cada aplicación Web Beans que utiliza Servlet.

If you are integrating the Web Beans into a JSF environment you must register org.jboss.webbeans.servlet.ConversationPropagationFilter as a Servlet listener, either automatically, or through user configuration, for each Web Beans application which uses JSF. This filter can be registered for all Servlet deployment safely.

Intercepción de sesión de Bean

Si está integrando los Web Beans en un entorno EJB debe registrar org.jboss.webbeans.ejb.SessionBeanInterceptor como un interceptor EJB para todos los EJB en la aplicación, automáticamente o a través de la configuración de usuario, para cada aplicación que utilice Web Beans empresariales.

El webbeans-ri.jar

Si está integrando el Web Beans dentro de un entorno que admite despliegue de aplicaciones, debe insertar el webbeans-ri.jar dentro de las aplicaciones del classloader aislado. No se puede cargar desde el classloader compartido.