scopescontexts.xml 18.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"  [ ]>
<chapter id="scopescontexts">

   <title>Scopes and contexts</title>

   <para>
      So far, we've seen a few examples of <emphasis>scope type annotations</emphasis>. The scope of a bean determines
      the lifecycle of instances of the bean. The scope also determines which clients refer to which instances of the
      bean. According to the CDI specification, a scope determines:
   </para>

   <blockquote>
      <itemizedlist>
         <listitem>
           <para>When a new instance of any bean with that scope is created</para>
         </listitem>
         <listitem>
           <para>When an existing instance of any bean with that scope is destroyed</para>
         </listitem>
         <listitem>
           <para>Which injected references refer to any instance of a bean with that scope</para>
         </listitem>
      </itemizedlist>
   </blockquote>

   <para>
      For example, if we have a session-scoped bean, <literal>CurrentUser</literal>, all beans that are called in the
      context of the same <literal>HttpSession</literal> will see the same instance of <literal>CurrentUser</literal>.
      This instance will be automatically created the first time a <literal>CurrentUser</literal> is needed in that
      session, and automatically destroyed when the session ends.
   </para>

      <tip>
         <para>
            JPA entities aren't a great fit for this model. Entities have their whole own lifecycle and identity model
            which just doesn't map naturally to the model used in CDI. Therefore, we recommend against treating entities
            as CDI beans. You're certainly going to run into problems if you try to give an entity a scope other than
            the default scope <literal>@Dependent</literal>. The client proxy will get in the way if you try to pass
            an injected instance to the JPA <literal>EntityManager</literal>.
         </para>
      </tip>

   <section>
      <title>Scope types</title>

      <para>
         CDI features an <emphasis>extensible context model</emphasis>. It's possible to define new scopes by creating
         a new scope type annotation:
      </para>

<programlisting role="JAVA"><![CDATA[@ScopeType
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface ClusterScoped {}]]></programlisting>

      <para>
         Of course, that's the easy part of the job. For this scope type to be useful, we will also need to define a
         <literal>Context</literal> object that implements the scope!  Implementing a <literal>Context</literal> is
         usually a very technical task, intended for framework development only. You can expect an implementation of the
         business scope, for instance, in a future version of Seam.
      </para>

      <para>
         We can apply a scope type annotation to a bean implementation class to specify the scope of the bean:
      </para>

      <programlisting role="JAVA"><![CDATA[@ClusterScoped
public class SecondLevelCache { ... }]]></programlisting>

      <para>Usually, you'll use one of CDI's built-in scopes.</para>

   </section>

   <section>
      <title>Built-in scopes</title>

      <para>CDI defines four built-in scopes:</para>

      <itemizedlist>
         <listitem>
            <para><literal>@RequestScoped</literal></para>
         </listitem>
         <listitem>
            <para><literal>@SessionScoped</literal></para>
         </listitem>
         <listitem>
            <para><literal>@ApplicationScoped</literal></para>
         </listitem>
         <listitem>
            <para><literal>@ConversationScoped</literal></para>
         </listitem>
      </itemizedlist>

      <para>For a web application that uses CDI:</para>

      <itemizedlist>
         <listitem>
            <para>
               any servlet request has access to active request, session and application scopes, and, additionally
            </para>
        </listitem>
        <listitem>
            <para>any JSF request has access to an active conversation scope.</para>
        </listitem>
      </itemizedlist>

      <note>
         <para>A CDI extension can implement support for the conversation scope in other web frameworks.</para>
      </note>

      <para>The request and application scopes are also active:</para>

      <itemizedlist>
         <listitem>
            <para>during invocations of EJB remote methods,</para>
         </listitem>
         <listitem>
            <para>during invocations of EJB asynchronous methods,</para>
         </listitem>
         <listitem>
            <para>during EJB timeouts,</para>
         </listitem>
         <listitem>
            <para>during message delivery to a message-driven bean,</para>
         </listitem>
         <listitem>
            <para>during message delivery to a <literal>MessageListener</literal>, and</para>
         </listitem>
         <listitem>
            <para>during web service invocations.</para>
         </listitem>
      </itemizedlist>

      <para>
         If the application tries to invoke a bean with a scope that does not have an active context, a
         <literal>ContextNotActiveException</literal> is thrown by the container at runtime.
      </para>
      
      <para>
         Managed beans with scope <literal>@SessionScoped</literal> or <literal>@ConversationScoped</literal> must be
         serializable, since the container passivates the HTTP session from time to time.
      </para>

      <para>
         Three of the four built-in scopes should be extremely familiar to every Java EE developer, so let's not waste
         time discussing them here. One of the scopes, however, is new.
      </para>

   </section>

   <section>
      <title>The conversation scope</title>
  
      <para>
         The conversation scope is a bit like the traditional session scope in that it holds state associated with a
         user of the system, and spans multiple requests to the server. However, unlike the session scope, the
         conversation scope:
      </para>
  
      <itemizedlist>
         <listitem>
            <para>is demarcated explicitly by the application, and</para>
         </listitem>
         <listitem>
            <para>
               holds state associated with a particular web browser tab in a JSF application (browsers tend to share
               domain cookies, and hence the session cookie, between tabs, so this is not the case for the session scope).
            </para>
         </listitem>
      </itemizedlist>
  
      <para>
         A conversation represents a task&#8212;a unit of work from the point of view of the user. The conversation context
         holds state associated with what the user is currently working on. If the user is doing multiple things at the
         same time, there are multiple conversations.
      </para>
  
      <para>
         The conversation context is active during any JSF request. Most conversations are destroyed at the end of the
         request. If a conversation should hold state across multiple requests, it must be explicitly promoted to a
         <emphasis>long-running conversation</emphasis>.
      </para>
  
      <section>
         <title>Conversation demarcation</title>
  
         <para>
            CDI provides a built-in bean for controlling the lifecycle of conversations in a JSF application. This bean
            may be obtained by injection:
         </para>
  
         <programlisting role="JAVA">@Inject Conversation conversation;</programlisting>
  
         <para>
            To promote the conversation associated with the current request to a long-running conversation, call the
            <literal>begin()</literal> method from application code. To schedule the current long-running conversation
            context for destruction at the end of the current request, call <literal>end()</literal>.
         </para>

         <para>
            In the following example, a conversation-scoped bean controls the conversation with which it is associated:
         </para>
  
<programlisting role="JAVA"><![CDATA[@ConversationScoped @Stateful
public class OrderBuilder {
   private Order order;
   private @Inject 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() {}
}]]></programlisting>

         <para>
            This bean is able to control its own lifecycle through use of the <literal>Conversation</literal> API. But
            some other beans have a lifecycle which depends completely upon another object.
         </para>
         
      </section>
  
      <section>
         <title>Conversation propagation</title>
    
         <para>
            The conversation context automatically propagates with any JSF faces request (JSF form submission) or redirect. 
            It does  not automatically propagate with non-faces requests, for example, navigation via a link.
         </para>
    
         <para>
            We can force the conversation to propagate with a non-faces request by including the unique identifier of
            the conversation as a request parameter. The CDI specification reserves the request parameter named
            <literal>cid</literal> for this use. The unique identifier of the conversation may be obtained from the
            <literal>Conversation</literal> object, which has the EL bean name <literal>conversation</literal>.
         </para>
    
         <para>
            Therefore, the following link propagates the conversation:
         </para>
    
         <programlisting role="XML"><![CDATA[<a href="/addProduct.jsp?cid=#{conversation.id}">Add Product</a>]]></programlisting>

         <para>
            It's probably better to use one of the link components in JSF 2:
         </para>

         <programlisting role="XML"><![CDATA[<h:link outcome="/addProduct.xhtml value="Add Product">
   <f:param name="cid" value="#{conversation.id}"/>
</h:link>]]></programlisting>
         
         <tip>
         <para>
            The conversation context propagates across redirects, making it very easy to implement the common 
            POST-then-redirect pattern, without resort to fragile constructs such as a "flash" object. The container 
            automatically adds the conversation id to the redirect URL as a request parameter.
         </para>
         </tip>

      </section>
  
      <section>
         <title>Conversation timeout</title>
    
         <para>
            The container is permitted to destroy a conversation and all state held in its context at any time in order
            to conserve resources. A CDI implementation will normally do this on the basis of some kind of 
            timeout&#8212;though this is not required by the specification. The timeout is the period of inactivity before
            the conversation is destroyed (as opposed to the amount of time the conversation is active).
         </para>
    
         <para>
            The <literal>Conversation</literal> object provides a method to set the timeout. This is a hint to the
            container, which is free to ignore the setting.
         </para>
    
         <programlisting role="JAVA">conversation.setTimeout(timeoutInMillis);</programlisting>
    
      </section>

   </section>
   
   <section>
      <title>The singleton pseudo-scope</title>
      
      <para>
         In addition to the four built-in scopes, CDI also supports two <emphasis>pseudo-scopes</emphasis>. The first 
         is the <emphasis>singleton pseudo-scope</emphasis>, which we specify using the annotation <literal>@Singleton</literal>.
      </para>
      
      <note>
         <para>
            Unlike the other scopes, which belong to the package <literal>javax.enterprise.context</literal>, the 
            <literal>@Singleton</literal> annotation is defined in the package <literal>javax.inject</literal>.
         </para>
      </note>
      
      <para>
         You can guess what "singleton" means here. It means a bean that is instantiated once. Unfortunately, there's 
         a little problem with this pseudo-scope. Beans with scope <literal>@Singleton</literal> don't have a proxy
         object. Clients hold a direct reference to the singleton instance. So we need to consider the case of a client 
         that can be serialized, for example, any bean with scope <literal>@SessionScoped</literal> or 
         <literal>@ConversationScoped</literal>, any dependent object of a bean with scope <literal>@SessionScoped</literal>
         or <literal>@ConversationScoped</literal>, or any stateful session bean.
      </para>
      
      <para>
         Now, if the singleton instance is a simple, immutable, serializable object like a string, a number or a date, 
         we probably don't mind too much if it gets duplicated via serialization. However, that makes it no stop being a 
         true singleton, and we may as well have just declared it with the default scope.
      </para>

      <para>There are several ways to ensure that the singleton bean remains a singleton when its client gets serialized:</para>
      
      <itemizedlist>
         <listitem>
            <para>
               have the singleton bean implement <literal>writeResolve()</literal> and <literal>readReplace()</literal> 
               (as defined by the Java serialization specification),
            </para>
         </listitem>
         <listitem>
            <para>
               make sure the client keeps only a transient reference to the singleton bean, or
            </para>
         </listitem>
         <listitem>
            <para>
               give the client a reference of type <literal>Instance&lt;X&gt;</literal> where <literal>X</literal> is the
               bean type of the singleton bean.
            </para>
         </listitem>
      </itemizedlist>
      
      <para>A fourth, better solution is to instead use <literal>@ApplicationScoped</literal>, allowing the container to
      proxy the bean, and take care of serialization problems automatically.</para>
   
   </section>

   <section>
      <title>The dependent pseudo-scope</title>

      <para>
         Finally, CDI features the so-called <emphasis>dependent pseudo-scope</emphasis>. 
         This is the default scope for a bean which does not explicitly declare a scope type.
      </para>

      <para>
         For example, this bean has the scope type <literal>@Dependent</literal>:
      </para>

      <programlisting role="JAVA"><![CDATA[public class Calculator { ... }]]></programlisting>

      <para>
         An instance of a dependent bean is never shared between different clients or different injection points. It is 
         strictly a <emphasis>dependent object</emphasis> of some other object. It is instantiated when the object it 
         belongs to is created, and destroyed when the object it belongs to is destroyed.
      </para>
      
      <para>
          If a Unified EL expression refers to a dependent bean by EL name, an instance of the bean is instantiated
          every time the expression is evaluated. The instance is not reused during any other expression evaluation.
      </para>
      
      <tip>
         <para>
            If you need to access a bean directly by EL name in a JSF page, you probably need to give it a scope other
            than <literal>@Dependent</literal>. Otherwise, any value that gets set to the bean by a JSF input will be 
            lost immediately. That's why CDI features the <literal>@Model</literal> stereotype; it lets you give a bean
            a name, and set its scope to <literal>@RequestScoped</literal> in one stroke. If you need to access a bean
            that really <emphasis>has</emphasis> to have the scope <literal>@Dependent</literal> from a JSF page,
            inject it into a different bean, and expose it to EL via a getter method.
         </para>
      </tip>
      
      <para>
         Beans with scope <literal>@Dependent</literal> don't need a proxy object. The client holds a direct reference
         to its instance.
      </para>

      <para>
         CDI makes it easy to obtain a dependent instance of a bean, even if the bean is already declared as a bean with
         some other scope type.
      </para>
      
   </section>

      <section>
         <title>The <literal>@New</literal> qualifier</title>

         <para>
            The built-in qualifier <literal>@New</literal> allows us to obtain a dependent object of a specified class.
         </para>

         <programlisting role="JAVA"><![CDATA[@Inject @New Calculator calculator;]]></programlisting>
         
         <para>The class must be a valid managed bean or session bean, but need not be an enabled bean.</para>
         
         <para>
            This works even if <literal>Calculator</literal> is <emphasis>already</emphasis> declared with a different
            scope type, for example:
         </para>

         <programlisting role="JAVA"><![CDATA[@ConversationScoped
public class Calculator { ... }]]></programlisting>

         <para>
            So the following injected attributes each get a different instance of <literal>Calculator</literal>:
         </para>

         <programlisting role="JAVA"><![CDATA[public class PaymentCalc {
   @Inject Calculator calculator;
   @Inject @New Calculator newCalculator;
}]]></programlisting>

         <para>
            The <literal>calculator</literal> field has a conversation-scoped instance of <literal>Calculator</literal>
            injected. The <literal>newCalculator</literal> field has a new instance of <literal>Calculator</literal>
            injected, with a lifecycle that is bound to the owning <literal>PaymentCalc</literal>.
         </para>

         <para>
            This feature is particularly useful with producer methods, as we'll see in the next chapter.
         </para>

      </section>

<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>