]
Shane Bryzak updated JBSEAM-3368:
---------------------------------
Fix Version/s: 2.1.0.GA
(was: 2.1.0.CR1)
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
Assignee: Shane Bryzak
Priority: Critical
Fix For: 2.1.0.GA
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>
     
<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: