[jbossseam-issues] [JBoss JIRA] Created: (JBSEAM-3368) conversation id evaluated to null (natural conversations)

Arron Ferguson (JIRA) jira-events at lists.jboss.org
Tue Sep 2 12:27:38 EDT 2008


conversation id evaluated to null (natural conversations)
---------------------------------------------------------

                 Key: JBSEAM-3368
                 URL: https://jira.jboss.org/jira/browse/JBSEAM-3368
             Project: Seam
          Issue Type: Bug
          Components: Core
    Affects Versions: 2.0.3.CR1
         Environment: Linux Ubuntu 7.10, Intel Pentium 4, 3GHz, 1 GB RAM, Tomcat 6.0.16
            Reporter: Arron Ferguson
            Priority: Blocker


If the user attempts to go to a page that is restricted based on what is in the pages.xml file, two things go wrong: 1) an exception is thrown (java.lang.IllegalStateException: conversation id evaluated to null: [name of natural conversation]), 2) the user can still view the page that is supposed to be restricted (i.e., using the restrict element in the pages.xml file).

Setup Instructions:
1.) Set up Tomcat 6.x
2.) Create web app project structure
3.) Use the following for your pages.xml file (copy/paste):

<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
       http-port="8080" https-port="8443"
       no-conversation-view-id="/index.xhtml"
                 login-view-id="/index.xhtml">

  <conversation name="regmem"
    parameter-name="memberID"
    parameter-value="#{memberreg.tempKey}"/>

  <page view-id="*" scheme="http" />

  <page view-id="/register.xhtml">
    <description>Register Member: #{memberreg}</description>
    <navigation from-action="#{memberreg.start}">
      <begin-conversation join="true" conversation="regmem"/>
      <redirect view-id="/register1.xhtml"/>
    </navigation>
    <navigation>
      <rule if="#{identity.loggedIn}">
        <redirect view-id="/memberadmin.xhtml"/>
      </rule>
      <rule if="#{memberreg.acceptedInformation}">
        <redirect view-id="/register3.xhtml"/>
      </rule>
      <rule if="#{memberreg.agree}">
        <redirect view-id="/register2.xhtml"/>
      </rule>
      <rule if="#{memberreg.started}">
        <redirect view-id="/register1.xhtml"/>
      </rule>
    </navigation>
  </page>

  <page view-id="/register1.xhtml" conversation-required="true"
    conversation="regmem">
    <description>Register Member: #{memberreg}</description>
    <restrict>#{memberreg.started}</restrict>
    <navigation from-action="#{memberreg.agreeToLicense}">
      <redirect view-id="/register2.xhtml"/>
    </navigation>
    <navigation from-action="#{memberreg.cancel}">
      <end-conversation/>
      <redirect view-id="/index.xhtml"/>
    </navigation>
    <navigation>
      <rule if="#{identity.loggedIn}">
        <redirect view-id="/memberadmin.xhtml"/>
      </rule>
      <rule if="#{memberreg.acceptedInformation}">
        <redirect view-id="/register3.xhtml"/>
      </rule>
      <rule if="#{memberreg.agree}">
        <redirect view-id="/register2.xhtml"/>
      </rule>
    </navigation>
  </page>

  <page view-id="/register2.xhtml" conversation-required="true"
    conversation="regmem">
    <description>Register Member: #{memberreg}</description>
    <restrict>#{memberreg.agree}</restrict>
    <navigation from-action="#{memberreg.next}">
      <rule if="#{memberreg.readyToConfirm}">
        <redirect view-id="/register3.xhtml"/>
      </rule>
      <rule if="#{not memberreg.readyToConfirm}">
        <redirect view-id="/register2.xhtml"/>
      </rule>
    </navigation>
    <navigation>
      <rule if="#{identity.loggedIn}">
        <redirect view-id="/memberadmin.xhtml"/>
      </rule>
      <rule if="#{memberreg.acceptedInformation}">
        <redirect view-id="/register3.xhtml"/>
      </rule>
    </navigation>
  </page>

  <page view-id="/register3.xhtml" conversation-required="true"
    conversation="regmem">
    <description>Register Member: #{memberreg}</description>
    <restrict>#{memberreg.readyToConfirm}</restrict>
    <navigation from-action="#{memberreg.acceptInformation}">
      <redirect view-id="/register4.xhtml"/>
    </navigation>
    <navigation from-action="#{memberreg.editInformation}">
      <redirect view-id="/register2.xhtml"/>
    </navigation>
    <navigation>
      <rule if="#{identity.loggedIn}">
        <redirect view-id="/memberadmin.xhtml"/>
      </rule>
    </navigation>
  </page>

  <page view-id="/register4.xhtml" conversation-required="true"
    conversation="regmem">
    <description>Register Member: #{memberreg}</description>
    <restrict>#{memberreg.acceptedInformation}</restrict>
    <navigation from-action="#{memberreg.end}">
      <end-conversation before-redirect="false"/>
      <redirect view-id="/index.xhtml"/>
    </navigation>
  </page>

  <!-- START EXCEPTIONS -->
  <exception class="org.jboss.seam.security.NotLoggedInException" log="false">
    <redirect view-id="/index.xhtml">
      <message severity="warn">Your session ended or expired.</message>
    </redirect>
  </exception>

  <exception class="org.jboss.seam.security.AuthorizationException" log="false">
    <end-conversation/>
    <redirect view-id="/index.xhtml">
      <message severity="warn">You are not authorized.</message>
    </redirect>
  </exception>
  <!-- END EXCEPTIONS -->

</pages>

4.) Create two Java classes (registration bean and POJO):
Registration Bean:
package com.test;

import javax.ejb.Remove;
import javax.persistence.Column;
import javax.persistence.NoResultException;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.EntityManager;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.faces.Renderer;
import static org.jboss.seam.ScopeType.CONVERSATION;

@Scope(CONVERSATION)
@Name("memberreg")
public class MemberRegistrationAction implements Serializable
{
    private static long tempKey = 0;
    @Out(required = false) @In(required = false)
    private Member newMember;

    @In(create = true)
    private Renderer renderer;

    private boolean started = false;
    private boolean agree = false;
    private boolean acceptedInformation = false;
    private boolean readyToConfirm = false;
    private boolean stillNeedsToEdit = false;
    private String confirm = "";

    public String getConfirm() { return confirm; }

    public void setConfirm(String confirm) { this.confirm = confirm; }

    public long getTempKey() { return tempKey; }

    public void setTempKey(long tempKey) { this.tempKey = tempKey; }

    public Member getNewMember() { return newMember; }

    /////////////////////////////////////////////
    // CONVERSATION METHODS
    /////////////////////////////////////////////
    public void start()
    {
        newMember = new Member();
        if(tempKey >= Long.MAX_VALUE)
        {
            tempKey = 0;
        }
        tempKey++;
        started = true;
    }

    public void agreeToLicense() { agree = true; }

    public void cancel()
    {
      started = false;
      agree = false;
      acceptedInformation = false;
      readyToConfirm = false;
      stillNeedsToEdit = false;
    }

    public void next() { readyToConfirm = this.verify(); }

    private boolean verify() {
        return true; // test purposes, always true
    }

    public void acceptInformation()
    {
        acceptedInformation = true;
        stillNeedsToEdit = false;
        this.complete();
    }

    public void editInformation()
    {
        stillNeedsToEdit = true;
        acceptedInformation = false;
    }

    private void complete() {
        ; // write to JPA/Hibernate
    }

    public void end() {
        ; // something, something, something, dark side
    }
    /////////////////////////////////////////////

    public boolean isStarted() { return started; }

    public boolean isAgree() { return agree; }

    public boolean isAcceptedInformation() { return acceptedInformation; }

    public boolean isReadyToConfirm() { return readyToConfirm; }

    public boolean isStillNeedsToEdit() { return stillNeedsToEdit; }

    public void destroy()
    {
      started = false;
      agree = false;
      acceptedInformation = false;
      readyToConfirm = false;
      stillNeedsToEdit = false;
    }
}

POJO:
package com.test;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;
import org.jboss.seam.annotations.Name;

@Entity @Name("member") @Table(name="member")
public class Member implements Serializable {
    @Id
    @GeneratedValue
    @Column(name = "id")
    private long id = -1;

    @NotNull
    @Length(min=2, max=20, message="Must be between 2 and 20 chars long")
    @Column(name = "lastname")
    private String lastName;

    @NotNull
    @Length(min=2, max=20, message="Must be between 2 and 20 chars long")
    @Column(name = "firstname")
    private String firstName;

    public long getId() { return id; }

    public void setId(long id) { this.id = id; }

    public String getLastName() { return lastName; }

    public void setLastName(String lastName) { this.lastName = lastName; }

    public String getFirstName() { return firstName; }

    public void setFirstName(String firstName) { this.firstName = firstName; }

   @Override
   public String toString()  { return firstName + " " + lastName; }

}

5.) Create XHTML files (template, index, register1 - register4):
///////////////////////////////////
template.xhtml:
<!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" lang="en"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core" >
  <head>
    <title>something.com</title>
  </head>
  <body>
    <div>
      <h:messages globalOnly="true"/><br/><br/>
      <ui:insert name="maincontent">
      </ui:insert>
    </div>
  </body>
</html>

///////////////////////////////////
index.html:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:s="http://jboss.com/products/seam/taglib">
    <!-- <ui:composition template="./template.xhtml"> -->
  <ui:composition template="./template.xhtml">
    <ui:define name="maincontent">
      <s:link view="/register.html" value="click here to register" propagation="none"/>
    </ui:define>
  </ui:composition>
</html>

///////////////////////////////////
register.html
<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:s="http://jboss.com/products/seam/taglib">
  <ui:composition template="./template.xhtml">
    <ui:define name="maincontent">
        <p>Click to start natural conversation:</p>
        <h:form id="testform">
          <h:outputText value="Click: "/>
          <h:commandButton value="Register" action="#{memberreg.start}">
            <s:conversationName value="regmem"/>
            <s:conversationPropagation type="join"/>
          </h:commandButton>
        </h:form>
    </ui:define>
  </ui:composition>
</html>

///////////////////////////////////
register1.xhtml
<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:s="http://jboss.com/products/seam/taglib" >
    <ui:composition template="./template.xhtml">
      <ui:define name="maincontent">
        <h:form id="agreeform">
          <h:commandButton id="agreebut1" value="Agree"
            action="#{memberreg.agreeToLicense}">
            <s:conversationName value="regmem"/>
            <s:conversationPropagation type="join"/>
          </h:commandButton>
          <br/><p>Click cancel to kill the natural conversation early</p>
          <h:commandButton id="disagreebut1" value="Disagree"
            action="#{memberreg.cancel}">
            <s:conversationName value="regmem"/>
            <s:conversationPropagation type="end"/>
          </h:commandButton>
        </h:form>
      </ui:define>
    </ui:composition>
</html>

///////////////////////////////////
register2.xhtml
<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:s="http://jboss.com/products/seam/taglib" >
  <ui:composition template="./template.xhtml">
     <ui:define name="maincontent">
     <h:form id="formfields">
        <p>Content inserted for testing purposes when this button clicked:</p>
        <h:commandButton id="next" value="Next" action="#{memberreg.next}">
          <s:conversationName value="regmem"/>
          <s:conversationPropagation type="join"/>
        </h:commandButton>
      </h:form>
    </ui:define>
  </ui:composition>
</html>

///////////////////////////////////
register3.xhtml
<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:rich="http://richfaces.org/rich"
      xmlns:s="http://jboss.com/products/seam/taglib" >
  <ui:composition template="./template.xhtml">
    <ui:define name="maincontent">
      <h:form id="formfields" rendered="#{not identity.loggedIn}">
        <h:commandButton id="editinfo" value="Edit Information"
          action="#{memberreg.editInformation}">
          <s:conversationName value="regmem"/>
          <s:conversationPropagation type="join"/>
        </h:commandButton>
        &#160;&#160;&#160;&#160;&#160;
        <h:commandButton id="confirmreg" value="Confirm Registration"
          action="#{memberreg.acceptInformation}">
          <s:conversationName value="regmem"/>
          <s:conversationPropagation type="join"/>
        </h:commandButton> 
      </h:form>
    </ui:define>
  </ui:composition>
</html>

///////////////////////////////////
register4.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:s="http://jboss.com/products/seam/taglib" >
  <ui:composition template="./template.xhtml">
    <ui:define name="maincontent">
      <p>Click back to finish natural conversation.</p>
      <s:link value="Back to home." action="#{memberreg.end}"
        propagation="end"/>
    </ui:define>
  </ui:composition>
</html>

6.) Use the following JARs for your web app (for running inside of Tomcat 6):
activation.jar
antlr.jar
asm.jar
cglib.jar
commons-beanutils.jar
commons-codec-1.3.jar
commons-collections.jar
commons-digester.jar
commons-lang.jar
commons-logging.jar
DA.jar
dom4j.jar
ejb-api.jar
filelist.txt
hibernate-annotations.jar
hibernate-commons-annotations.jar
hibernate-entitymanager.jar
hibernate.jar
hibernate-search.jar
hibernate-validator.jar
javassist.jar
jboss-archive-browsing.jar
jboss-el.jar
jboss-seam-debug.jar
jboss-seam-ioc.jar
jboss-seam.jar
jboss-seam-mail.jar
jboss-seam-ui.jar
jsf-api.jar
jsf-facelets.jar
jsf-impl.jar
jstl.jar
jta.jar
lucene-core.jar
mail.jar
persistence-api.jar
richfaces-api.jar
richfaces-impl.jar
richfaces-ui.jar
spring.jar
urlrewrite-3.1.0.jar

7.) (Optional) Set up facelets but you may also want to make the default extension html:
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>

Steps to reproduce bug:
1.) Start web app inside of Tomcat 6
2.) using your browser's URL text field, type in the name of a register1.html, register2.html, or register3.html. At this point your web app will generate an exception stack which will be caused by:
java.lang.IllegalStateException: conversation id evaluated to null:

The conversation is evaluated to null. Since conversations are a layer on top of sessions, the same behavior is expected which is a faces message purporting of no conversation existing and a redirect to the URL specified in the pages.xml attribute no-conversation-view-id="/index.xhtml" (so in this case index.xhtml page). It should not allow the user to see the page and generate exceptions.


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: https://jira.jboss.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        



More information about the seam-issues mailing list