2
votes

I have a page-scoped component, which has an instance variable List with data, which I display in a datatable. This datatable has pagination, sorting and filtering.

The first time gate into the page, I get this appended in my URL: ?conversationId=97. The page works correctly, and when I change datatable pages no now component is created.

After a minute or two, and at seamingly random time, I get an exception saying that there is no context. I have not used @Create in my code or my navigation files.

So, I have two questions:

  • Why do I get this suffix in my URL? Why did a conversation start?
  • Why the exception? The component is scoped to PAGE. If I received an exception, it should not be related to a conversation. Right? Or is the conversation the exception is referring a temporary conversation?

Cheers!

UPDATE I:

The project is an Ear.

This is the page:

<!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"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">

<body>
<ui:composition template="/WEB-INF/facelets/templates/template.xhtml">


<ui:define name="content">

    <!--  This method returns focus on the filter -->
    <script type="text/javascript">

    function submitByEnter(event){
        if (event.keyCode == 13) {
            if (event.preventDefault) {
                // Firefox
                event.preventDefault(); 
            } else {
                // IE 
                event.returnValue = false; 
            }
            document.getElementById("refreshButton").click();
        } 
    }

    </script>

    <h:form prependId="false">

        <h:commandButton action="Back" value="Back to home page" />
        <br />

        <p><h:outputText
            value="Applicants and Products (experimentation page)"
            class="page_title" /></p>


        <h:commandButton
            action="#{applicantProductListBean.showCreateApplicant}"
            value="Create Applicant" id="createApplicantButton">
        </h:commandButton>

        <a4j:commandButton value="Refresh" id="refreshButton"
            action="#{applicantProductListBean.refreshData}"
            image="/images/icons/refresh48x48.gif"
            reRender="compositeTable, compositeScroller">
<!--                <f:setPropertyActionListener-->
<!--                    target="# {pageScrollerBean.applicantProductListPage}" value="1" />-->
        </a4j:commandButton>

        <rich:toolTip for="createApplicantButton" value="Create Applicant" />

        <rich:dataTable styleClass="composite2DataTable" id="compositeTable"
            rows="1" columnClasses="col"
            value="#{applicantProductListBean.dataModel}" var="pageAppList">
            <f:facet name="header">
                <rich:columnGroup>
                    <rich:column colspan="3">
                        <h:outputText styleClass="headerText" value="Applicants" />
                    </rich:column>
                    <rich:column colspan="3">
                        <h:outputText styleClass="headerText" value="Products" />
                    </rich:column>
                    <rich:column breakBefore="true">
                        <h:outputText styleClass="headerText" value="Applicant Name" />



                        <a4j:commandButton id="sortingApplicantNameButton"
                            action="#{applicantProductListBean.toggleSorting('applicantName')}"
                            image="/images/icons/sorting/#{sortingFilteringBean.applicantProductListSorting.sortingValues['applicantName']}.gif"
                            reRender="sortingApplicantNameButton, sortingApplicantEmailButton, compositeTable, compositeScroller">
<!--                                    <f:setPropertyActionListener-->
<!--                                    target="#{pageScrollerBean.applicantProductListPage}" value="1" />-->
                        </a4j:commandButton>




                        <br />
                        <h:inputText
                            value="#{sortingFilteringBean.applicantProductListFiltering.filteringValues['applicantName']}"
                            id="applicantNameFilterValue"
                            onkeypress="return submitByEnter(event)">
                        </h:inputText>
                    </rich:column>
                    <rich:column>
                        <h:outputText styleClass="headerText" value="Applicant Email" />
                        <a4j:commandButton id="sortingApplicantEmailButton"
                            action="#{applicantProductListBean.toggleSorting('applicantEmail')}"
                            image="/images/icons/sorting/#{sortingFilteringBean.applicantProductListSorting.sortingValues['applicantEmail']}.gif"
                            reRender="sortingApplicantNameButton, sortingApplicantEmailButton, compositeTable, compositeScroller">
<!--                                 <f:setPropertyActionListener-->
<!--                                    target="#{pageScrollerBean.applicantProductListPage}" value="1" />-->
                        </a4j:commandButton>
                        <br />
                        <h:inputText
                            value="#{sortingFilteringBean.applicantProductListFiltering.filteringValues['applicantEmail']}"
                            id="applicantEmailFilterValue"
                            onkeypress="return submitByEnter(event)">
                        </h:inputText>
                    </rich:column>
                    <rich:column>
                        <h:outputText styleClass="headerText" value="Applicant Actions" />
                    </rich:column>
                    <rich:column>
                        <h:outputText styleClass="headerText" value="Product Name" />

                        <a4j:commandButton id="sortingProductNameButton"
                            action="#{applicantProductListBean.toggleSorting('productName')}"
                            immediate="true"
                            image="/images/icons/sorting/#{sortingFilteringBean.applicantProductListSorting.sortingValues['productName']}.gif"
                            reRender="sortingProductNameButton, compositeTable, compositeScroller">
                        </a4j:commandButton>



                        <br />
                        <h:inputText
                            value="#{sortingFilteringBean.applicantProductListFiltering.filteringValues['productName']}"
                            id="productNameFilterValue"
                            onkeypress="return submitByEnter(event)">
                        </h:inputText>
                    </rich:column>
                    <rich:column>
                        <h:outputText styleClass="headerText" value="Product Email" />
                        <br />
                        <h:inputText
                            value="#{sortingFilteringBean.applicantProductListFiltering.filteringValues['productEmail']}"
                            id="productEmailFilterValue"
                            onkeypress="return submitByEnter(event)">
                        </h:inputText>
                    </rich:column>
                    <rich:column>
                        <h:outputText styleClass="headerText" value="Product Actions" />
                    </rich:column>
                </rich:columnGroup>
            </f:facet>
            <rich:subTable rowClasses="odd_applicant_row, even_applicant_row"
                value="#{pageAppList}" var="app">
                <rich:column
                    styleClass=" internal_cell
                    composite2TextContainingColumn"
                    valign="top">
                    <h:outputText value="#{app.name}" />
                </rich:column>

                <rich:column
                    styleClass="internal_cell composite2TextContainingColumn"
                    valign="top">
                    <h:outputText value="#{app.receiptEmail}" />
                </rich:column>

                <rich:column valign="top" styleClass="buttonsColumn">
                    <h:commandButton
                        action="#{applicantProductListBean.showUpdateApplicant(app)}"
                        image="/images/icons/edit.jpg">
                    </h:commandButton>
                    <!--                    <rich:toolTip for="editApplicantButton" value="Edit Applicant" />-->
                    <h:commandButton
                        action="#{applicantProductListBean.showDeleteApplicant(app)}"
                        image="/images/icons/delete.png">
                    </h:commandButton>
                    <!--                    <rich:toolTip for="deleteApplicantButton" value="Delete Applicant" />-->
                </rich:column>



                <rich:column colspan="3">
                    <table class="productsTableTable">
                        <tbody>
                            <tr>
                                <td class="createProductButtonTableCell"><h:commandButton
                                    action="#{applicantProductListBean.showCreateProduct(app)}"
                                    value="Create Product">
                                </h:commandButton>     
<!--                    <rich:toolTip for="createProductButton" value="Create Product" />-->
                                </td>
                            </tr>
                            <tr>
                                <td><rich:dataTable value="#{app.products}" var="prod"
                                    rowClasses="odd_product_row, even_product_row">
                                    <rich:column
                                        styleClass="internal_cell composite2TextContainingColumn">
                                        <h:outputText value="#{prod.inventedName}" />
                                    </rich:column>

                                    <rich:column
                                        styleClass="internal_cell composite2TextContainingColumn">
                                        <h:outputText value="#{prod.receiptEmail}" />
                                    </rich:column>

                                    <rich:column styleClass="buttonsColumn">
                                        <h:commandButton
                                            action="#{applicantProductListBean.showUpdateProduct(prod)}"
                                            image="/images/icons/edit.jpg">
                                        </h:commandButton>
                                           <!--                         <rich:toolTip for="editProductButton" value="Edit Product" />-->
                                        <h:commandButton
                                            action="#{applicantProductListBean.showDeleteProduct(prod)}"
                                            image="/images/icons/delete.png">
                                            <f:setPropertyActionListener target="#{productBean.product}"
                                                value="#{prod}" />
                                        </h:commandButton>
                                          <!--                          <rich:toolTip for="deleteProductButton" value="Delete Product" />-->
                                    </rich:column>
                                </rich:dataTable></td>
                            </tr>
                        </tbody>
                    </table>
                </rich:column>
            </rich:subTable>
            <f:facet name="footer">
                <h:panelGrid columns="1" styleClass="applicantProductListFooter">
                    <h:outputText value="#{msgs.no_results}" rendered="#{(empty applicantProductListBean.dataModel) || (applicantProductListBean.dataModel.rowCount==0)}"/>

                    <rich:datascroller align="center" for="compositeTable"
                        page="#{pageScrollerBean.applicantProductListPage}"
                        id="compositeScroller" reRender="compositeTable"
                        renderIfSinglePage="false" fastControls="hide">
                        <f:facet name="first">
                            <h:outputText value="#{msgs.first}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="first_disabled">
                            <h:outputText value="#{msgs.first}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="last">
                            <h:outputText value="#{msgs.last}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="last_disabled">
                            <h:outputText value="#{msgs.last}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="next">
                            <h:outputText value="#{msgs.next}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="next_disabled">
                            <h:outputText value="#{msgs.next}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="previous">
                            <h:outputText value="#{msgs.previous}" styleClass="scrollerCell" />
                        </f:facet>
                        <f:facet name="previous_disabled">
                            <h:outputText value="#{msgs.previous}" styleClass="scrollerCell" />
                        </f:facet>
                    </rich:datascroller>
                </h:panelGrid>

            </f:facet>
        </rich:dataTable>



    </h:form>
</ui:define>

This is the backing bean:

@Name("applicantProductListBean")
@Scope(ScopeType.PAGE)
public class ApplicantProductListBean extends
    BasePagedSortableFilterableListBean {

/**
 * Public field for ad-hoc injection to work.
 */
@EJB(name = "FacadeService")
public ApplicantFacadeService applicantFacadeService;
@Logger
private static Log logger;
private final int pageSize = 10;
@Out(scope = ScopeType.CONVERSATION, required = false)
Applicant currentApplicant;
@Out(scope = ScopeType.CONVERSATION, required = false)
Product product;

@Create
public void onCreate() {
    System.out.println("Create");
}

@Override
protected DataModel initDataModel(int pageSize) {

    // get filtering and sorting from session
    sorting = getSorting();
    filtering = getFiltering();
    // System.out.println("Initializing a Composite3DataModel");
    // System.out.println("Pagesize: " + pageSize);
    // System.out.println("Filtering: " + filtering.getFilteringValues());
    // System.out.println("Sorting: " + sorting.getSortingValues());
    return new Composite3DataModel(1, sorting, filtering);
}

// Navigation methods
/**
 * Navigation-returning method, returns the action to follow after pressing
 * the "Create Applicant" button
 * 
 * @return the action to be taken
 */
public Navigation.ApplicantProductList showCreateApplicant() {

    return Navigation.ApplicantProductList.SHOW_CREATE_APPLICANT;
}

/**
 * Navigation-returning method, returns the action to follow after pressing
 * the "Edit Applicant" button
 * 
 * @return the action to be taken
 */
public Navigation.ApplicantProductList showUpdateApplicant(
        Applicant applicant) {
    this.currentApplicant = applicant;
    return Navigation.ApplicantProductList.SHOW_UPDATE_APPLICANT;
}

/**
 * Navigation-returning method, returns the action to follow after pressing
 * the "Delete Applicant" button
 * 
 * @return the action to be taken
 */
public Navigation.ApplicantProductList showDeleteApplicant(
        Applicant applicant) {
    this.currentApplicant = applicant;
    return Navigation.ApplicantProductList.SHOW_DELETE_APPLICANT;
}

/**
 * Navigation-returning method, returns the action to follow after pressing
 * the "Create Product" button
 * 
 * @return the action to be taken
 */
public Navigation.ApplicantProductList showCreateProduct(Applicant app) {

    this.product = new Product();
    this.product.setApplicant(app);
    return Navigation.ApplicantProductList.SHOW_CREATE_PRODUCT;
}

/**
 * Navigation-returning method, returns the action to follow after pressing
 * the "Edit Product" button
 * 
 * @return the action to be taken
 */
public Navigation.ApplicantProductList showUpdateProduct(Product prod) {
    this.product = prod;
    return Navigation.ApplicantProductList.SHOW_UPDATE_PRODUCT;
}

/**
 * Navigation-returning method, returns the action to follow after pressing
 * the "Delete Product" button
 * 
 * @return the action to be taken
 */
public Navigation.ApplicantProductList showDeleteProduct(Product prod) {
    this.product = prod;
    return Navigation.ApplicantProductList.SHOW_DELETE_PRODUCT;
}

/**
 * */
@Override
public Sorting getSorting() {

    if (sorting == null) {
        return (getSortingFilteringBeanFromSession()
                .getApplicantProductListSorting());
    }
    return sorting;
}

/**
 * 
 */
@Override
public void setSorting(Sorting sorting) {

    getSortingFilteringBeanFromSession().setApplicantProductListSorting(
            sorting);
}

/**
 * 
 */
@Override
public Filtering getFiltering() {

    if (filtering == null) {
        return (getSortingFilteringBeanFromSession()
                .getApplicantProductListFiltering());
    }
    return filtering;
}

/**
 * 
 */
@Override
public void setFiltering(Filtering filtering) {

    getSortingFilteringBeanFromSession().setApplicantProductListFiltering(
            filtering);
}

/**
 * @return the currentApplicant
 */
public Applicant getCurrentApplicant() {
    return currentApplicant;
}

/**
 * @param currentApplicant
 *            the currentApplicant to set
 */
public void setCurrentApplicant(Applicant applicant) {
    this.currentApplicant = applicant;
}

/**
 * The model for this page
 * 
 */
private class Composite3DataModel extends
        PagedSortableFilterableDataModel<List<Applicant>> {

    public Composite3DataModel(int pageSize, Sorting sorting,
            Filtering filtering) {

        super(pageSize, sorting, filtering);
    }

    @Override
    protected DataPage<List<Applicant>> fetchPage(int fakeStartRow,
            int fakePageSize) {

//          if (logger.isTraceEnabled()) {
            System.out.println("Getting page with fakeStartRow: " + fakeStartRow
                    + " and fakePageSize " + fakePageSize);
//          }
        // to find the page size multiply the startRow and the fakePageSize
        // (which is 1) to the actual page size
        int startRow = fakeStartRow
                * ApplicantProductListBean.this.pageSize;
        int pageSize = fakePageSize
                * ApplicantProductListBean.this.pageSize;
//          if (logger.isTraceEnabled()) {
            System.out.println("Getting page with startRow: " + startRow
                    + " and pageSize " + pageSize);
//          }
        List<Applicant> pageApplicants = applicantFacadeService
                .findPagedWithCriteria(startRow, pageSize, filtering,
                        sorting);
        // List<Applicant> pageApplicants = applicantFacadeService
        // .findPagedWithDynamicQuery(startRow, pageSize, filtering,
        // sorting, true);
//          if (logger.isTraceEnabled()) {
            System.out.println("Set of applicants: " + pageApplicants.size());
//          }
        List<List<Applicant>> pageApplicantsListContainer = new ArrayList<List<Applicant>>();
        pageApplicantsListContainer.add(pageApplicants);
        DataPage<List<Applicant>> dataPage = new DataPage<List<Applicant>>(
                this.getRowCount(), fakeStartRow,
                pageApplicantsListContainer);
        return dataPage;
    }

    @Override
    protected int getDatasetSize() {

        // int size = getServiceFacade().countWithCriteria(filtering,
        // sorting);
        // int size =
        // applicantFacadeService.countWithDynamicQuery(filtering, sorting,
        // false);
        int size = (int) Math.ceil((double) applicantFacadeService
                .countWithCriteria(filtering, sorting, false)
                / pageSize);
        if (logger.isTraceEnabled()) {
            logger.trace("Got Dataset Size: " + size);
        }
        return size;
    }
}

/**
 * @return the product
 */
public Product getProduct() {
    return product;
}

/**
 * @param product
 *            the product to set
 */
public void setProduct(Product product) {
    this.product = product;
}
}

And this is the page file (Notice that as long as I don't leave the page no conversation is created):

<?xml version="1.0" encoding="UTF-8"?>
<page>
<navigation>
    <rule if-outcome="Back">
        <redirect view-id="/index.xhtml" />
    </rule>
    <rule if-outcome="SHOW_CREATE_PRODUCT">
        <begin-conversation join="true" />
        <redirect view-id="/pages/product/createProduct.xhtml" />
    </rule>
    <rule if-outcome="SHOW_UPDATE_PRODUCT">
        <begin-conversation join="true" />
        <redirect view-id="/pages/product/editProduct.xhtml" />
    </rule>
    <rule if-outcome="SHOW_DELETE_PRODUCT">
        <begin-conversation join="true" />
        <redirect view-id="/pages/product/deleteProduct.xhtml" />
    </rule>
    <rule if-outcome="SHOW_CREATE_APPLICANT">
        <begin-conversation join="true" />
        <redirect view-id="/pages/applicant/createApplicant.xhtml" />
    </rule>
    <rule if-outcome="SHOW_UPDATE_APPLICANT">
        <begin-conversation join="true" />
        <redirect view-id="/pages/applicant/editApplicant.xhtml" />
    </rule>
    <rule if-outcome="SHOW_DELETE_APPLICANT">
        <begin-conversation join="true" />
        <redirect view-id="/pages/applicant/deleteApplicant.xhtml" />
    </rule>
</navigation>
</page>

UPDATE II:

the stacktrace is here

2
@Markos Fragkakis Can you show your Seam component and your page ? ear or war ?Arthur Ronald
@Arthur Ronald F D Garcia I added the code. Please take another look.Markos Fragkakis
add the code (at least the relevant parts) of the superclass of your beanBozho
@Markos Fragkakis you'd better provide the relevant code I requested :)Bozho
@Bozho Based on StackTrace, i think it has to do with Ajax4j. Take a lookArthur Ronald

2 Answers

4
votes

Well, Let's see

The first time gate into the page, I get this appended in my URL: ?conversationId=97

ok. As said by Seam in Action book

Seam, by default, creates a Temporary conversation to serve the current request. A Temporary conversation is initialized immediately following restore view phase of the JSF life cycle and is destroyed after the render response phase.

And

After a minute or two, and at seamingly random time, I get an exception saying that there is no context.

Each conversation can have its own Timeout period, which defaults to the global Timeout setting

/WEB-INF/components.xml

<core:manager conversation-timeout="1000000"/>

Or page-specific

<page view-id="/app.xhtml" timeout="1000000"/>

Its value is specified in milliseconds. But it must exceed session Timeout defined in web.xml

/WEB-INF/web.xml

<!--specified in minutes-->
<session-config>
    <session-timeout>30</session-timeout>
</session-config>

Maybe it explains why you get your exception.

But if you really want to know whether you have long-running conversation, Seam stores a built-in conversation-scoped component named conversation. So inside your Managed bean, do as follows to know whether you have a long-running conversation

org.jboss.seam.core.Conversation conversation = (Conversation) Component.getInstance("conversation");

System.out.println(conversation.isLongRunning());

If you see true, someway you have started a long-running conversation. You can even see inside your page

#{conversation.longRunning}

I hope it can be useful to you.

0
votes

Try making the component scoped conversation instead of page. It sounds like what you want to do is larger than page scoped, which holds the context for only a single request.

I'm not sure why you got that conversationId parameter appended to your url. However, all communication in seam happens inside of a conversation. If its page scoped, the conversation is a temporary conversation. To promote the conversation to a long running conversation, you need to a begin annotation or to start the conversation in pages.xml.

I think the seamBooking example application has some sample code that should be similar to what you are trying to do.