4 Replies Latest reply on Feb 12, 2019 8:31 AM by amontobin

    CDI Conversations

    amontobin

      Hi,

      I have an old seam application that i need to migrate.

      My application uses a lot of conservations and i'm a bit dissappointed with CDI conservations (not as powerful as seam ones).

      I need to pass (parameters, entities, ...) from one conversation to the old or to the new one.

       

      For Example,

       

      Query Page (Conversation 1) => Choose Element => Edit Page (Conversation 2 retrieve entity parameter and merge with current persistent context)

       

      In seam, the pseudo algorithm was :

       

      // Get Parameters From current context

      Entity entity1 = ...;

      Conversation.instance().leave();

      Conversation.instance().begin();

      // Pass parameters to the new conversation

      ...

       

      How can i do that in CDI ? I have no access to the old conversation.

       

      I try to do in a jsf action:

       

      String contextId = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(Container.CONTEXT_ID_KEY);

      LazyHttpConversationContextImpl httpConversationContext = (LazyHttpConversationContextImpl) Container.instance(contextId).deploymentManager().instance().select(Context.class).select(HttpConversationContext.class).get();

      httpConversationContext.activate(oldConversationId);

      ((MyBean) httpConversationContext.get(bean))

       

      @Inject @Http

      ConversationContext conversationContext;

       

      String newConversationId = conversation.getId();

      conversationContext.desactivate();

      conversationContext.activate(oldConversationId);

      ((MyBean) conversationContext.get(bean))

      conversationContext.desactivate();

      conversationContext.activate(newConversationId );

       

      I manage to get rid of warnings :

      WELD-000335: Conversation context is already active, most likely it was not cleaned up properly during previous request processing: HttpServletRequestImpl [ POST /xxxx/conversation1.xhtml ]

       

      I can retrieve for basic properties the value used in the previous conversation, but i have got a lot of warnings and i'm not sure that's a good solution at the end.

       

      Maybe should i need to create a custom CDI scope ?

       

      Thanks for sharing your own experience

       

      Sebastien

        • 1. Re: CDI Conversations
          manovotn

          Hello

           

          I haven't used or developed Seam, so I don't know how things worked there.

           

          I am not sure I am following what you do there.

          You basically want to temporarily activate previous conversation, grab a bean and use it in new conversation instead of new bean instance created there?

          That would kind of beat the purpose of having new conversation in the first place - why not use the prior conversation instead?

          Also, speaking of custom scopes, try looking at JSF's @FlowScoped if that's something you could use.

          • 2. Re: CDI Conversations
            amontobin

            You basically want to temporarily activate previous conversation, grab a bean and use it in new conversation instead of new bean instance created there?

            Not Exactly, grab old bean properties (basic properties, entities, ...) and pass them to the new bean.

            I need to have differents conversations ; each conversation represents a "state" of UI (Query, Edit, Create)

            For example :

             

            1. Display Query UI

            2. List Entities corresponding to criteria

            3. Select Entity to edit and start new conversation (we pass entity to the new conversation)

            4. Display Edit UI

            5. Change entity properties

            6. Save

            7. Return to Query and return to previous conversation (we pass entity to the old conversation and merge entity so that the attributes displayed in the query mode reflected those who have changed)

             

            @Flowscoped seems not appropriate because my application is only in one page (All JSF components are dynamics).

             

            • 3. Re: CDI Conversations
              manovotn

              Are you operating on transient or long running conversations?

              You can @Inject Conversation built-in bean into your beans (which are active when conversation is) and you can call begin() method.

              That turns conversation from transient to long running. E.g. from being bound to a single request to being able to span across multiple requests, up to the the duration of whole HTTP session.

              Obviously this long running conversation needs to be manually ended (Conversation.end()) once you are done with it.

               

              Theoretically, you can have your first conversation long running and your second transient.

              But then you would need to control which propagates via HTTP request through the cid parameter and I am still not sure you could reach both of them in the same time...

               

              The more I ponder over it, the more fitting it seems to have a custom scope for your scenario.

              • 4. Re: CDI Conversations
                amontobin

                Hi,

                 

                Sorry for my late answer, i've been a little ill in the meantime.

                 

                Here is my little example to deal with conversations :

                 

                <h:body>

                <h:form>

                <h:panelGrid columns="2">

                        <h:outputLabel value="Id"/>

                        <h:outputLabel value="#{javax.enterprise.context.conversation.id}"/>

                        <h:outputLabel value="Transient"/>

                        <h:outputLabel value="#{javax.enterprise.context.conversation.transient}"/>

                        <h:outputLabel value="timeout (ms)"/>

                        <h:outputLabel value="#{javax.enterprise.context.conversation.timeout}"/>

                  </h:panelGrid>

                  <h:panelGrid columns="1">

                    <h:commandButton value="Start No Propagation" action="#{conversation1Controller.startNoPropagation}">

                    <f:param name="conversationPropagation" value="none"/>

                    </h:commandButton>

                        <h:commandButton value="Start With Propagation" action="#{conversation1Controller.startWithPropagation}">

                        </h:commandButton>

                        <h:commandButton value="Info" action="#{conversation1Controller.info}">

                        <f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/>

                        </h:commandButton>

                        <h:commandButton value="Stop Current" action="#{conversation1Controller.stop}">

                        <f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/>

                        </h:commandButton>

                    </h:panelGrid>  

                </h:form>

                <a4j:log />

                </h:body>

                 

                 

                @Named

                @ConversationScoped

                public class Conversation1Bean implements Serializable {

                  private String value;

                 

                  public String getValue() {

                    if (value == null) {

                      generateValue();

                    }

                    return value;

                  }

                 

                  public void setValue(String value) {

                    this.value = value;

                  }

                 

                  public void generateValue() {

                    setValue(new Timestamp(System.currentTimeMillis()).toString());

                  }

                }

                 

                 

                @Named

                @SessionScoped

                public class Conversation1Controller implements Serializable {

                 

                  @Inject

                  Conversation1Bean conversation1Bean;

                 

                  @Inject

                  Conversation conversation;

                 

                  @Inject

                  @Http

                  ConversationContext conversationContext;

                 

                  public String startWithPropagation() {

                    conversation.begin();

                    conversation1Bean.generateValue();

                    return null;

                  }

                 

                  public String startNoPropagation() {

                    conversation.begin();

                    conversation1Bean.generateValue();

                    info();

                    return null;

                  }

                 

                  public String info() {

                    String conversationId = conversation.getId();

                    BeanManager bm = CDI.current().getBeanManager();

                    Bean bean = bm.getBeans("conversation1Bean").iterator().next();

                    List<String> list = new ArrayList<String>();

                    for (ManagedConversation current : conversationContext.getConversations()) {

                      list.add(current.getId());

                    }

                    for (String current : list) {

                      conversationContext.deactivate();

                      conversationContext.activate(current);

                      System.out.println("Faces cid : " + current + " value " + ((Conversation1Bean) getExpressionValue("conversation1Bean")).getValue());

                      System.out.println("BeanManager cid : " + current + " value " + ((Conversation1Bean) conversationContext.get(bean)).getValue());

                    }

                    conversationContext.deactivate();

                    conversationContext.activate(conversationId);

                    return null;

                  }

                 

                  public String stop() {

                    conversation.end();

                    return null;

                  }

                 

                  public static Object getExpressionValue(String expression) {

                    ValueExpression ve = FacesContext.getCurrentInstance().getApplication().getExpressionFactory().createValueExpression(FacesContext.getCurrentInstance().getELContext(), quoteBinding(expression), Object.class);

                    Object value = (Object) ve.getValue(FacesContext.getCurrentInstance().getELContext());

                    return value;

                  }

                 

                  public static String quoteBinding(String binding) {

                    if (binding != null) {

                      if (binding.indexOf("#{") == -1) {

                        return "#{" + binding + "}";

                      }

                    }

                    return binding;

                  }

                }

                 

                Two click on Start With Propagation => Attempt to call begin() on a long-running conversation. Normal / Good

                10 click on Start No Propagation + Info =>

                - Retrieve value via EL => Not Good values

                - Retrieve value via conversationContext => OK We see all 10 different values

                 

                I will continue to improve my example