SeamFramework.orgCommunity Documentation

Chapitre 4. Le modèle de composant contextuel

4.1. Les contextes de Seam
4.1.1. Le contexte sans état
4.1.2. Le contexte évenementiel
4.1.3. Le contexte de Page
4.1.4. Le contexte conversationnel
4.1.5. Le contexte de Session
4.1.6. Le contexte de processus métier
4.1.7. Le contexte d'Application
4.1.8. Les variables de contexte
4.1.9. Priorité dans la recherche de contexte
4.1.10. Modèle de concurrence
4.2. Les composants de Seam
4.2.1. Les beans de session sans état
4.2.2. Les Beans de session avec état
4.2.3. Les beans entité
4.2.4. JavaBeans
4.2.5. Les beans conducteur-de-message
4.2.6. L'intercepteur
4.2.7. Les noms de component
4.2.8. Définition de l'étendue de composant
4.2.9. Les composants avec des rôles multiples
4.2.10. Les composants livrés
4.3. La bijection
4.4. Les méthode du cycle de vie
4.5. Installation conditionelle
4.6. Mettre des traces
4.7. Les interfaces Mutable et @ReadOnly
4.8. Fabrique et composants gestionnaire

Les deux concepts centraux dans Seam sont la notion de contexte et la notion de composant. Les composants sont des objets avec état, habituellement des EJBs, et une instance d’un composant est associé avec un contexte, et défini par un nom dans ce contexte. La Bijection fourni un mécanisme pour nommer les composants par un alias interne (les variables d’instances) dans les noms du contexte, autorisant des arbres de composants a être dynamiquement assemblés et réassemblés par Seam.

Commençons par la description des contextes existant dans Seam.

Les contextes de Seam sont créés et détruit par le serveur de squelette d’application (framework) via des appels explicite à l’API Java. Les contextes sont habituellement implicites. Dans certains cas, malgré tout, les contextes sont spécifiés via des annotations.

Les contextes de base de Seam

Nous pouvons reconnaître certain de ces contexte de part les spécifications des servlets autour des servlets. Malgré cela, deux d’entre elles devraient être nouvelle pour vous : contexte conversationnel, et contexte de processus métier. Une des raisons pour que le gestionnaire d’état qui est dans les applications web, soit si fragile et un nid à erreurs c’est que trois des contextes livrés (requête, session et application) ne sont pas particulièrement utile du point de vue de la logique métier. Une session d’authentification d’un utilisateur, par exemple, est construite presque arbitrairement dans une application de flot de travail. Ainsi, les composants Seam sont bornés à la conversation ou aux contextes des processus métier, ainsi ils sont les contextes qui ont le plus de sens en termes d’application.

Ayons un regard sur chacun de ces contextes l'un après l'autre.

Le contexte de conversation est un vraiment un concept central dans Seam. Une conversation est une unité de travail du point de vue de l’utilisateur. Elle peut s’étendre sur plusieurs interactions de l’utilisateur, plusieurs requêtes, et plusieurs transactions de base de données. Mais pour l’utilisateur, une conversation résout un seul problème. Par exemple, "réserver un hôtel", "valider un contact", "créer un bon de commande" sont toutes des conversations. Vous devriez apprécier de penser en termes d’implémentation de la conversation comme un seul "cas d’utilisation" ou une "exemple d’utilisation" mais la relation n’est pas nécessairement aussi directe.

Une conversation retient un état associé avec "ce que l’utilisateur est en train de faire maintenant dans cette fenêtre". Un simple utilisateur peut avoir de multiples conversations en cours à tout moment dans le temps, habituellement dans de multiples fenêtres. Le contexte de conversation nous permet de nous assurer que cet état provenant de différentes conversations ne peut se caramboler et causer des bugs.

Il est possible que cela vous prennes du temps de maitriser la façon de penser l’application en termes de conversations. Mais une fois que vous avez l’habitude de le faire, nous pensons que vous allez adorer cette notion, et que vous ne serez plus capable de ne jamais plus penser en termes de conversations!

Quelques conversations perdurent dans une simple requête. Les conversations qui s’étendent sur plusieurs requêtes doivent être bien démarquées en utilisant les annotations fournies par Seam.

Quelques conversations sont aussi des tâches. une tache est une conversation ce qui a un sens en termes de processus métier à exécution longue et a le potentiel de déclencher une transition d’état pour un processus métier quand elle réussit à se terminer. Seam fournis une série d’annotations spéciales pourdifférencie cette tâche.

Les conversations doivent être reliées, avec une conversation prenant sa place "à l’intérieur" d’une plus grande conversation. Ceci est une fonctionnalité avancée.

Habituellement, l’état de la conversation est actuellement maintenu par Seam dans la session servlet entre les requêtes. Seam implémente un delais de péremption configurable, détruisant automatiquement les conversations inactives et ainsi s’assurant que l’état maintenu par une session de connexion d’un seul utilisateur ne grandi hors limitation si l’utilisateur abandonne les conversations.

Seam sérialise les requêtes de processus concurent qui ont lieu dans le même contexte de conversation à exécution longue, dans le même processus.

Autre alternative, Seam peut être configuré pour converser l’état conversationnel dans le navigateur du client.

Ni les spécifications servlet ni EJB ne définissent la moindre indications pour l’organisation des requêtes originaire du même client. Le container de servlet simplement laisse tous les threards s’exécuter de manière concurrente et laisse la sureté des threads se renforcer dans le code applicatif. Le container EJB autorise les composants sans état à être accédés de manière concurrente et déclenche des exceptions si de multiples threads accèdent à un bean session avec état.

Cette fonctionnalité devrait être ok dans les applications web au style ancien qui sont basées autour de requêtes bien dimensionnées et synchrones. Mais les applications modernes qui font une utilisation lourde de nombreuses requêtes bien dimensionnées et asynchrones (AJAX), de manière concurrente est un état de fait doivent être supporté par le modèle de programmation. Seam fourni une couche de gestion concurrente dans son modèle de contexte.

La session Seam et les contextes d’application sont multi threadées. Seam va nous autoriser des requêtes concurrentes dans un contexte pouvant être exécuté en concurrence. Les contextes page et d'évènements sont par nature un simple thread. Le contexte de processus métier est strictement parlant multi threadé, mais en pratique la concurrence est suffisamment rare pour que ce fait puisse être assez peu controlé la plus part du temps. Finalement, Seam renforce le modèle d'un seul thread par conversation par processus pour le contexte de conversation en sérialisant les requêtes concurrentes dans un même contexte de conversation d’exécution longue.

Depuis que le contexte de session est multi-threadé et souvent il contient un état volatile, les composants de l’étendue session sont toujours protégés par Seam d’accès concurrents aussi longtemps que les intercepteurs de Seam ne sont pas désactivés par ce composant. Si les intercepteurs sont désactivés, alors tout accès sécurisé au thread doit être implémenté par le concurent lui-même. Seam sérialise les requêtes dans les beans de session l’étendue de session et dans les JavaBeans par défaut (et détecte et brise toute étreinte mortele qui apparait). Ceci n’est pas la fonctionnalité par défaut pour les composants de l’étendue application, car les composants de l’étendue application ne conservent pas habituellement un état volatile et tout cela à cause de la synchronisation au niveau global qui est extrèmement couteuse. Malgré tout, vous pouvez forcer le modèle de threads en sérialisation sur n’imoporte quel bean session ou composant JavaBean en ajoutant l’annotation @Synchronized.

Le modèle concurrent signifie que les clients AJAX peuvent de manière sure utiliser la session volatile et un état conversationnel, sans le besoin d’un travail particulier de la part du développeur.

Les composants de Seam sont ds POJOs (Plain old java Objects). En particulier, s'ils sont des JavaBeans ou bean entreprise EJB3.0. Tant que Sean ne requière pas que ces composants soient des EJBs et ainsi qu’il puisse même être utilisés sans un container compatible EJB3, Seam a été conçu avec EJB3.0 comme état d’esprit et il inclus une intégration profonde avec EJB3.0. Seam supporte les types de composants suivants.

Les composants beans de session avec état sont capables de retenir un état pas seulement au travers de multiples invocations du bean mais aussi au travers de multiples requêtes. L’état de l’application ne doit pas s’attarder dans la base de données pour être éventuellement retenu par des beans de sessions avec état. Ceci est une différence principale entre Seam et d’autres serveurs d’application web. Plutôt que de coller de l’information à propos de la conversation courante directement dans le HttpSession, vous devriez conserver ces variables d’instance d’un bean session avec état qui va être relié au contexte de conversation. Ceci autorise Seam à gérer le cycle de vie de cet état pour vous et vous assure qu’il n’y aura pas de collision entre les états dans différentes conversations concurrentes.

Les beans de session avec état sont souvent utilisés comme écouteur d’action JSF, et comme beans de soutient qui fournissent des propriétés aux composants JSF pour l’affichage ou la soumissions de formulaire.

Par défaut, les beans de session avec état sont reliés au contexte de conversation. Ils ne peuvent jamais reliés aux contextes de page ou sans état.

Les requêtes concurrentes pour les sessions avec état dans l’étendue de session sont toujours sérialisées par Seam si les intercepteurs de Seam ne sont pas désactivés par le bean.

Les beans session avec états peuvent être instanciés en utilisant Component.getInstance() ou @In(create=true). Ils ne devraient pas être instanciés via l'observateur JNDI ou par l'opérateur new.

Les beans entité peuvent être reliés à une variable de contexte et fonctionne comme un composant de Seam. Avec des entitées ayant un identité persistante en plus de leur identité contextuelle, les instance d’entité sont habituellement reliées explicitement par le code Java, plutôt qu’être instanciées implicitement par Seam.

Les composants bean entité ne supportent pas la bijection ou la démarcation du contexte. Aucune invocation d’un bean entité ne déclenchera la validation.

Les beans entité ne sont habituellement pas utilisés comme écouteur d’action JSF, mais ont aussi la fonction d’un bean de soutient qui fourni des propriétés aux composants JSF pour l’affichage et la soumission de formulaire. En particulier, il est commun d’utiliser une entité comme un bean de soutient, avec un écouteur d’action de bean de session sans état pour implémenter les fonctionnalités de type création/mise à jour/effacement.

Par défaut, les beans entités sont reliés au contexte de conversation. Ils ne devraient jamais être reliés au contexte sans état.

Notez que dans un environement en cluster il est parfois moins efficace de relier un bean entité directement à une conversation ou à une variable de contexte de Seam d'étendue de session que de maintenir une reference au bean entité dans une bean de session avec état. C'est pour cette raison que toutes les applications Seam ne définissent pas des beans entité a être des composants Seam.

Les composant beans entité de Seam peuvent être instanciés en utilisant Component.getInstance() ou @In(create=true) ou directement en utilisant l'opérateur new.

Tous les composants de Seam au besoin d’un nom. Nous pouvons assigner un nom à un composant en utilisant l’annotation @Name:

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    ... 
}

Ce nom est un nom de composant seam et n’est pas lié a un tout autre nom défini par la spécification EJB. Malgré tout, les noms de composant Seam fonctionnent tous comme les noms des beans de gestion JSF et vous pouvez penser que les deux concepts sont identiques.

@Name n'est pas la seule façon de définir un nom de composant, mais nous avons toujours besoin de spécifier ce nom quelque part. Si nous ne le faisons pas, alors aucune autre annotation de Seam ne va fonctionner.

A chaque fois que Seam instancie un composant, il relie cette nouvelle instance à une variable dans l'étendue configuré pour ce composant qui correspondant au nom du composant. Cette fonctionnalité est identique à comment JSF fait travailler les beans qu'il gère, exception que Seam vous permet de configurer cette relation en utilisant des annotation au lieu de XMM. Par exemple, le connecté courant dans User peut être relié à une variable de contexte de session currentUser tant que le User dont il est sujet à quelques fonctionnalités administratives qui peuvent être reliées à la variable de contexte de conversation user. Soyez prudent, pensez-y, parce qu'au traver d'une affectation programmée, il est possible de surcharger une variable de contexte qui a une référence sur un composant de Seam, avec de potentiel problèmes de confusion.

Pour de plus grande applications, et pour les composants de Seam livrés, les noms qualifiés sont souvent utilisés.

@Name("com.jboss.myapp.loginAction")

@Stateless
public class LoginAction implements Login { 
    ... 
}

Nous devrions utiliser le nom de composant qualifié à la fois dans le code Java et dans le langage d’expression de JSF:


<h:commandButton type="submit" value="Login"
                 action="#{com.jboss.myapp.loginAction.login}"/>

Comme c’est perturbant, Seam fourni aussi une manière pour renommer un nom qualifié en nom simple. Ajouter une ligne comme celle-ci dans le fichier components.xml:


<factory name="loginAction" scope="STATELESS" value="#{com.jboss.myapp.loginAction}"/>

Tous les composants Seam livrés ont un nom qualifié, mais la plus part d’entre eux sont renommés vers un nom simple grâce à la fonctionnalité d'importation de l'espace de nommage de Seam. Le fichier components.xml inclus dans le JAR de Seam définie les espaces de nommage suivant.

<components xmlns="http://jboss.com/products/seam/components">
    
    <import>org.jboss.seam.core</import>
    <import>org.jboss.seam.cache</import>
    <import>org.jboss.seam.transaction</import>
    <import>org.jboss.seam.framework</import>
    <import>org.jboss.seam.web</import>
    <import>org.jboss.seam.faces</import>
    <import>org.jboss.seam.international</import>
    <import>org.jboss.seam.theme</import>
    <import>org.jboss.seam.pageflow</import>
    <import>org.jboss.seam.bpm</import>
    <import>org.jboss.seam.jms</import>
    <import>org.jboss.seam.mail</import>
    <import>org.jboss.seam.security</import>
    <import>org.jboss.seam.security.management</import>  
    <import>org.jboss.seam.security.permission</import>
    <import>org.jboss.seam.captcha</import>
    <import>org.jboss.seam.excel.exporter</import>
    <!-- ... --->
</components>

Pour essayer de résoudre un nom non-qualifié, Seam va vérifier chacun de ces espace de nommage, dans l'odre. Vous pouvez inclure des espaces de nommages additionnels dans votre fichier components.xml de votre application pour des espaces de nommages spécifiques à votre application.

L'injection de dépendance ou l'inversion de contrôle est maintenant un concept familier pour la plus part des développeurs Java. L’injection de dépendance permet à un composant d'obtenir une référence sur un autre composant en passant par le containeurs qui "injecte" l'autre composant par une méthode assesseur ou par une variable d'instance. Dans toutes les implémentations d’injection de dépendances que nous avons vu, l’injection intervient quand le composant est construit, et la référence ne change pas postérieurement pour le cycle de vie de l’instance du composant. Pour les composants sans état, ceci est raisonnable. Du point de vue d’un client, toutes les instances d’un composant sans état particulier sont interchangeables. Dans un autre côté, Seam accentue l’utilisation de composants avec état. Donc l’injection de dépendance traditionnelle n’est plus une construction vraiment utilisable. Seam introduit la notion de bijection comme une généralisation de l’injection. A la différence de l’injection, la bijection est :

Par essence, la bijection vous permet de renommer une variables de contexte dans une variables d’instance de composant en spécifiant que la valeur de la variable d’instance est injectée, extradée ou les deux. Bien sûr, nous utilisons des annotation pour activer la bijection.

L'annotation @In indique que la valeur devrait être injectée, aussi dans une variable d’instance.

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    @In User user;
    ... 
}

ou dans une méthode assesseur :

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    User user;
    
    @In
    public void setUser(User user) {
        this.user=user;
    }
    
    ... 
}

Par défaut, Seam va donner une priorité de recherche dans tous les contextes en utilisant le nom de la propriété ou de la variable d’instance qui a été injectée. Vous devriez vouloir spécifier le nom de la variable de contexte explicitement, en utilisant, par exemple, @In("currentUser").

Si vous voulez que Seam puisse créer une instance du composant quand il n’y a pas d’instance de composant existante reliée à une variable de contexte nommée, vous pouvez spécifier @In(create=true). Si la valeur est optionnel (elle peut être null) spécifiez @In(required=false).

Pour quelques composants, il peut être répétitif d’avoir à spécifier @In(create=true) partout où ils sont utilisés . Dans ce genre de cas, vous pouvez annoter le composant @AutoCreate, et alors il va toujours être créé, n’important quand, même sans l’utilisation explicite de create=true.

Vous pouvez même injecter la valeur d’une expression :

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    @In("#{user.username}") String username;
    ... 
}

Les valeurs injectées sont désalouées (autrement dit, définie à null) immédiatement après la réalisation de la méthode et sont extraction.

(Il y a beaucoup plus d'information sur le cicle de vie d'un composant et l'injection dans le chapitre suivant).

L'annotation @Out indique qu'un attribue devrait être extradé, tout comme une variable d'instance:

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    @Out User user;
    ... 
}

ou comme une méthode assesseur:

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    User user;
    
    @Out
    public User getUser() {
        return user;
    }
    
    ... 
}

Un attribut peut être à la fois injecté et extradé:

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    @In @Out User user;
    ... 
}

ou:

@Name("loginAction")

@Stateless
public class LoginAction implements Login { 
    User user;
    
    @In
    public void setUser(User user) {
        this.user=user;
    }
    
    @Out
    public User getUser() {
        return user;
    }
    
    ... 
}

Le bean de session et les composants Seam de bean entité supportent tous les rappels du cycle de vie EJB3.0 usuel (@PostConstruct, @PreDestroy, etc). Mais Seam étend tous ces rappels aux composant JavaBean. Cependant tant que ces annotation ne sont pas disponible dans un environement J2EE, Seam défini deux composants additionnel pour le rappel dans le cycle de vie, équivalent à @PostConstruct et à @PreDestroy.

La méthode @Create est appelée après que Seam instancie un composant. Les composants ne peuvent définir seulement qu'une seule méthode @Create.

La méthode @Destroy est appelé quand le contexte qui lie le composant de Seam se termine. Les composants ne peuvent définir qu'une seule méthode @Destroy.

De plus, les composants bean avec état doivent définir une méthode sans aucun paramètre annotée @Remove. Cette méthode est appelé par Seam quand le contexte se termine.

Finalement, une annotation liée est l’annotation @Startup qui peut être appliquée à toutes l’application ou un composant d’étendue session. L’annotation @Startup indique à Seam d’instancier le composant immédiatement, quand le contexte commence, au lieu d’attendre avant son premier référencement par un client. Il est possible de controler l’ordre d’instanciation du démarrage des composants en spécifiant @Startup(depends={....}).

L'annotation @Install vous permet de contrôler l’installation conditionnelle du composant qui sont requis dans certains scénarios de déploiement et pas dans d’autres. C'est utile si :

@Install fonctionne en vous laissant spécifier la précédence et les dépendances.

La précédence d’un composant est un numéro que Seam utilise pour décider quel composant est à installer quand il a plusieurs classes avec le même nom de composant dans le classpath. Seam va choisir le composant avec la précédence la plus haute. Il y a quelques valeurs de précédences prédéfinie (dans l’ordre ascendant) :

Supposez que nous avons un composant appelé messageSender qui parle à la queue de JMS.

@Name("messageSender") 

public class MessageSender {
    public void sendMessage() {
        //do something with JMS
    }
}

Si dans vos tests unitaires, nous n’avons pas de queue de JMS disponible, mais que nous aimerions simuler cette méthode. Nous allons créer un composant le singeant et qui existe dans le classpath quand les tests unitaires sont exécutés mais il n’est jamais déployé avec l’application:

@Name("messageSender") 

@Install(precedence=MOCK)
public class MockMessageSender extends MessageSender {
    public void sendMessage() {
        //do nothing!
    }
}

La precedence aide Seam à décider quel version à utiliser quand il trouve deux composants dans la classpath.

C’est bien si vous étiez capable de contrôler exactement quelles classes sont dans le classpath. Mais si j’ai écrit un serveur d’application réutilisable avec beaucoup de dépendances, je ne veux pas à avoir à briser ce serveur d’applications au travers de nombreux jars. Je veux d’être capable de décider quel composants sont à installer selon quel autres composants sont déjà installés et selon quelles classes sont disponibles dans le classpath. L’annotation @Install contrôle aussi cette fonctionnalité. Seam utilise ce mécanisme interne pour activer l’installation conditionnelle de beaucoup des composants livrés. Malgré tout, vous n’aurez probablement pas besoin de l’utiliser dans votre application.

Qui n’en n’a pas assez de voir du code aussi touffu que celui-ci ?

private static final Log log = LogFactory.getLog(CreateOrderAction.class);

        
public Order createOrder(User user, Product product, int quantity) {
    if ( log.isDebugEnabled() ) {
        log.debug("Creating new order for user: " + user.username() + 
            " product: " + product.name() 
            + " quantity: " + quantity);
    }
    return new Order(user, product, quantity);
}

Il est difficile d’imaginer comment le code pour un simple message de log peut devenir beaucoup verbeux. Il y a beaucoup plus de lignes de code dédié à l'affichage des traces plus que de la réelle logique métier! Je reste totalement abasourdie que la communauté Java n’a pas encore trouvé mieux depuis 10 ans.

Seam fourni un API d'affichage des traces qui simplifie significativement le code :

@Logger private Log log;

        
public Order createOrder(User user, Product product, int quantity) {
    log.debug("Creating new order for user: #0 product: #1 quantity: #2", user.username(), product.name(), quantity);
    return new Order(user, product, quantity);
}

Il n’est pas utile si vous déclarez la variable log statique ou non— elle va fonctionner dans tous les cas, à l’exception des composants bean entité qui demande à la variable de log d'être statique.

Notez que nous n’avons pas besoin d’être touffu si ( log.isDebugEnabled() ) conserver, depuis la concaténation des chaines de caractères apparaissent dans la méthode debug().Notez aussi que nous n’avons habituellement pas besoin de spécifier la catégorie de log explicitement, car Seam connaît quel composant au sein du quel il va injecter le Log.

Si User et Product sont des composants Seam disponible dans les contextes courant, il n’y a pas mieux :

@Logger private Log log;

        
public Order createOrder(User user, Product product, int quantity) {
    log.debug("Creating new order for user: #{user.username} product: #{product.name} quantity: #0", quantity);
    return new Order(user, product, quantity);
}

Seam logue auto-magiquement choisi s’il faut envoyer la sortie vers log4j ou vers le logging JDK. Si log4j est dans le classpath, Seam va l’utiliser. Si ce n’est pas le cas, Seam va utiliser le logging JDK.

Plusieurs serveurs d’applications fonctionnent comme d’incroyable implémentation brissant le clustering HttpSession quand les modifications d’état d’objet mutables relié à une session sont seulement répliqués quand l’application appelle setAttribute() explicitement. Ceci est la source de bugs qui ne peuvent pas effectivement être testé pendant le temps de développement, jusqu’à qu’ils se manifestent quand les erreurs surviennent. En outre, le message actuel répliqué contient le graphe d’objet intégralement sérialisé relié à l’attribut de la session, ce qui est inefficace.

Bien sur, les beans de session avec état EJB peuvent réaliser automatiquement la sale vérification et la réplication des états mutés et un containeur EJB sophistiqué peut introduire des optimisations comme la réplication au niveau attribut. Malheureusement, tous les utilisateurs de Seam n’ont la bonne surprise de travailler dans un environnement qui supporte EJB3.0. Donc, pour la session et le JavaBean d’étendue conversationnelle et les composants bean entité, Seam propose une couche additionnelle pour le gestionnaire d’état sécurisé pour le cluster au dessus du clustering de session du container web.

Pour la session ou des composants de JavaBeans d’étendue conversationnel, Seam automatiquement force la réplication qui survient en appelantsetAttribute() une fois sur chaque requète quand le composant est invoqué par l'application.Bien sur, cette stratégie est inéficace pour les composants principalement en lecture seul. Vous pouvez controler cette fonctionnalité en implémentant l'interface org.jboss.seam.core.Mutable et écrire votre propre logique de sale-vérification dans votre composant. Par exemple :

@Name("account")

public class Account extends AbstractMutable
{
    private BigDecimal balance;
    
    public void setBalance(BigDecimal balance)
    {
        setDirty(this.balance, balance);
        this.balance = balance;
    }
    
    public BigDecimal getBalance()
    {
        return balance;
    }
    
    ...
    
}

Ou, vous pouvez utiliser l’annotation @ReadOnly pour obtenir un effet similaire :

@Name("account")

public class Account
{
    private BigDecimal balance;
    
    public void setBalance(BigDecimal balance)
    {
        this.balance = balance;
    }
    
    @ReadOnly
    public BigDecimal getBalance()
    {
        return balance;
    }
    
    ...
    
}

Pour la session ou pour des composants bean entité d’étendue conversation, Seam automatiquement forces la réplication qui intervient en appelant setAttribute() une fois par requête, à moins que l'entité (d'étendue conversationnelle) ne soit actuellement associée avec le contexte de persistance gérée par Seam, dans ce cas aucune réplication n'est requise. Cette stratégie n'est pas nécéssairement efficace, donc une session ou des beans entité d'édendue conversationnelle devraient l’utiliser avec prudence. Vous devez toujours écrire un bean session avec état ou un composant JavaBean pour "gérer" l’instance du bean entité. Par exemple,

@Stateful

@Name("account")
public class AccountManager extends AbstractMutable
{
    private Account account; // an entity bean
    
    @Unwrap
    public Account getAccount()
    {
        return account;
    }
    
    ...
    
}

Notez que la classe EntityHome dans le Seam Application Framework fourni un bel exemple de gestion d’une instance bean entité utilisant un composant Seam.

Nous avons souvent besoin de travailler avec des objets qui ne sont pas des composants Seam. Mais nous continuons à vouloir être capable de les injecter dans notre composants en utilisant @In et de les utiliser en tant que valeur et méthode reliées aux expressions, etc. De temps en temps, nous avons même besoin de les attacher dans le cycle de vie du contexte de Seam (@Destroy, par exemple). Donc les contextes de Seam peuvent obtenir des objets qui ne sont pas des composants Seam et Seam fourni quelques fonctionnalités sympa qui rendent plus facile le travail avec les objets non-composants reliés aux contextes.

Le modèle de conception fabrique de composant autorise un composant Seam à agir comme l’instanciateur pour un objet non-composant. Une méthode fabrique va être appelée quand une variable du contexte est référencée mais n’a pas de valeur reliée à elle. Nous définissons des méthodes fabrique en utilisant l’annotation @Factory. La méthode fabrique relie une valeur à une variable de contexte et détermine l’étendue de la valeur liée. Il y a deux types de méthode fabrique. La première méthode retourne une valeur, qui est relié au contexte par Seam :

@Factory(scope=CONVERSATION)

public List<Customer
> getCustomerList() { 
    return ... ;
} 

Le second style est une méthode de type void qui relie cette valeur à la variable de contexte elle-même :

@DataModel List<Customer

> customerList;
@Factory("customerList")
public void initCustomerList() { 
    customerList = ...  ;
} 

Dans les deux cas, la méthode fabrique est appelée quand nous référençons la variable de contexte customerList et cette valeur est null, et alors n’a absolument pas à intervenir dans le cycle de vie de la valeur. Et même un patron de conception plus puisssant est le pattron de conception gestionnaire de composant. Dans ce cas, nous avons un composant Seam qui est relié à une variable de contexte qui gère la valeur de la variable de contexte restant invisible aux clients.

Un composant gestionnaire est n’importe quel composant avec une méthode @Unwrap. Cette méthode retourne la valeur qui va être visible pour les clients, et elle est appeléeà chaque fois qu'une variable de contexte est référencée.

@Name("customerList")

@Scope(CONVERSATION)
public class CustomerListManager
{
    ...
    
    @Unwrap
    public List<Customer
> getCustomerList() { 
        return ... ;
    }
}

Le patron de conception gestionnaire est particulièrement utile si nous avons un objet où nous avons besoin de contrôler le cycle de vie du composant. Par exemple, si vous avez des objets très lourds qui ont besoin d’opération de nettoyage quand le contexte s’arrête, vous pouvez @Unwrap l'objet et réaliser un nettoyage dans la méthode @Destroy du gestionnaire.

@Name("hens")

@Scope(APPLICATION) 
public class HenHouse
{
    Set<Hen
> hens;
    
    @In(required=false) Hen hen;
    
    @Unwrap
    public List<Hen
> getHens()
    {
        if (hens == null)
        {
            // Setup our hens
        }
        return hens;
    }
    
    @Observer({"chickBorn", "chickenBoughtAtMarket"})
    public addHen()
    {
        hens.add(hen);
    }
    
    @Observer("chickenSoldAtMarket")
    public removeHen()
    {
        hens.remove(hen);
    }
    
    @Observer("foxGetsIn")
    public removeAllHens()
    {
        hens.clear();
    }
    ...
} 

Ici le composant gestionnaire observe tous les évènement qui modifie l'objet sous-jacent. Le composant gère ces actions lui-même, et parce que l'objet est empaqueté donc à chaque accès, une vue cohérante est fournie.