Help

Controls

PermLinkWikiLink
Switch Workspace

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Forum: Seam Users Forum ListTopic List
11. Mar 2010, 19:06 America/New_York | Link

Hi all!

This is a newbie question. I a page using a PAGE-scoped component. I would expect that the constructor of the component to be executed once, upon creation of the page, then the component would be stored in the PAGE context, and everybody would be happy.

But what happens is that my constructor runs 4 times and the @Create method runs 2 times. Why is this happening? If someone could take a look I would be grateful.

Cheers!

The output:

Constructor
Constructor
@Create
Constructor
Constructor
@Create

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">

		<a4j:loadScript src="/js/focus.js" />

		<h:form prependId="false">

			<!-- Hotkeys -->
			<rich:hotKey key="ctrl+s"
				handler="$('createButton').click();return false;" />

			<h:commandButton action="#{applicantBean.cancelCreateApplicant}"
				value="#{msgs.back_to_search_results}" immediate="true" />
			<br />
			<br />

			<h:outputText value="#{msgs.create_applicant}" class="page_title" />

			<table width="100%">
				<tbody>
					<tr>
						<td width="20%"><h:outputText value="#{msgs.name}" /></td>
						<td width="30%"><h:inputText id="applicantName"
							style="width:100%" value="#{applicantBean.applicant.name}"
							required="true" requiredMessage="#{val.a_value_is_required}" /></td>
						<td width="50%"><h:message for="applicantName"
							styleClass="errorMessage" /></td>
					</tr>
					<tr>
						<td><h:outputText value="#{msgs.email}" /><h:outputText
							value=" #{msgs.optional_in_parenthesis}" styleClass="optional" /></td>
						<td><h:inputText id="applicantEmail" style="width:100%"
							value="#{applicantBean.applicant.receiptEmail}">
							<f:validator validatorId="EmailValidator" />
						</h:inputText></td>
						<td><h:message for="applicantEmail" styleClass="errorMessage" /></td>
					</tr>
					<tr>

						<td colspan="2" align="right" valign="middle">

						<p><h:commandButton id="createButton"
							action="#{applicantBean.createApplicant}" value="#{msgs.create}">
						</h:commandButton> <h:commandButton action="#{applicantBean.cancelCreateApplicant}"
							value="#{msgs.cancel}" immediate="true">
						</h:commandButton></p>
						</td>

					</tr>
					<tr>
						<td><h:messages errorClass="errorMessage"
							infoClass="errorMessage" layout="table" globalOnly="true"
							showDetail="false" showSummary="true" /></td>
					</tr>
				</tbody>
			</table>



		</h:form>

		<script type="text/javascript">
			setFocus("#{(focus != null) ? focus : 'applicantName'}");
			setHighlight('${highlight}');
		</script>

	</ui:define>
</ui:composition>
</body>
</html>

and the component:



import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

/**
 * Managed bean to handle product instances.
 * 
 * 
 */
@Name("productBean")
@Scope(ScopeType.PAGE)
public class ProductBean extends BasePageManagedBean {

	/**
	 * The logger.
	 */
	private final static Logger logger = Logger.getLogger(ProductBean.class);
	/**
	 * Public field for ad-hoc injection to work.
	 */
	@EJB(name = "ProductFacadeService")
	public ProductFacadeService productFacadeService;
	private Product product;

	public ProductBean() {
		System.out.println("Constructor");
	}
	
	@Create
	public void onCreate() {
		product = new Product();
		System.out.println("@Create");
	}

	/**
	 * This method creates a product
	 * 
	 * @return the action to be taken
	 */
	public Navigation.EditProduct createProduct() {

		if (!productFacadeService.canCreate(product)) {
			setMessage("val", "product_name_already_exists", null,
					FacesMessage.SEVERITY_ERROR, "productName");
			return null;
		}
		productFacadeService.create(product);
		if (logger.isTraceEnabled()) {
			logger.trace("Created product: " + product);
		}
		return Navigation.EditProduct.SHOW_APPLICANT_PRODUCT_LIST;
	}


	/**
	 * Getter for the product
	 * 
	 * @return the product
	 */
	public Product getProduct() {

		return product;
	}

	/**
	 * Setter for the product
	 * 
	 * @param product
	 *            the product to set
	 */
	public void setProduct(Product product) {

		this.product = product;
	}

	/**
	 * Setter for the applicant of the product
	 * 
	 * @param applicant
	 *            the applicant to be set
	 */
	public void setApplicant(Applicant applicant) {

		getProduct().setApplicant(applicant);
	}

	/**
	 * Navigation-returning method, returns the action to follow after canceling
	 * creating a product.
	 * 
	 * @return the action to be taken
	 */
	public Navigation.CreateProduct cancelCreateProduct() {

		return (Navigation.CreateProduct.SHOW_APPLICANT_PRODUCT_LIST);
	}

}

7 Replies:
11. Mar 2010, 21:48 America/New_York | Link
I dont see "productBean" referenced in the view.

How your component is instantiated?

Regards.
12. Mar 2010, 09:08 America/New_York | Link

Sorry, you are right. I pasted a similar page. This is the correct one, which uses the productBean component.

<!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">

		<a4j:loadScript src="/js/focus.js" />

		<!-- Hotkeys -->
		<rich:hotKey key="ctrl+s"
			handler="$('createButton').click();return false;" />

		<h:form prependId="false">

			<a4j:keepAlive beanName="productBean" />

			<h:commandButton action="#{productBean.cancelCreateProduct}"
				value="#{msgs.back_to_search_results}" immediate="true" />
			<br />
			<br />

			<h:outputText value="#{msgs.create_product}" class="page_title" />

			<table width="100%">
				<tbody>
					<tr>
						<td width="20%"><h:outputText value="#{msgs.name}" /></td>
						<td width="30%"><h:inputText id="productName"
							style="width:100%" value="#{productBean.product.inventedName}"
							required="true" requiredMessage="#{val.a_value_is_required}" /></td>
						<td width="50%"><h:message styleClass="errorMessage"
							for="productName" /></td>
					</tr>
					<tr>
						<td><h:outputText value="#{msgs.email}" /><h:outputText
							value=" #{msgs.optional_in_parenthesis}" styleClass="optional" /></td>
						<td><h:inputText id="productEmail" style="width:100%"
							value="#{productBean.product.receiptEmail}">
							<f:validator validatorId="EmailValidator" />
						</h:inputText></td>
						<td><h:message styleClass="errorMessage" for="productEmail" /></td>
					</tr>
					<tr>
						<td><h:outputText value="#{msgs.applicant}" /></td>
						<td><h:inputText
							style="width:100%" value="#{productBean.product.applicant.name}" disabled="true" />
						</td>
					</tr>

					<tr>
						<td colspan="2" align="right" valign="middle">

						<p><h:commandButton id="createButton" action="#{productBean.createProduct}"
							value="#{msgs.create}" /> <h:commandButton
							action="#{productBean.cancelCreateProduct}"
							value="#{msgs.cancel}" immediate="true" /></p>
						</td>

					</tr>
				</tbody>
			</table>

		</h:form>

		<script type="text/javascript">
			setFocus("#{(focus != null) ? focus : 'productName'}");
			setHighlight('${highlight}');
		</script>

	</ui:define>
</ui:composition>
</body>
</html>
12. Mar 2010, 16:13 America/New_York | Link
I think your problem is this:

<a4j:keepAlive beanName="productBean" />

There is two instances of the same bean, one in the page tree and other in wherever Rich Faces stores the bean for keep alive implementation.

Regards.
12. Mar 2010, 16:47 America/New_York | Link

Sergio, thanks for the reply.

Indeed, a4j:keepalive was responsible for half the initialization. Now that I have removed that line, I get this:

Constructor Constructor @Create

Why does the constructor run twice? Is it perhaps related to the JSF lifecycle underneath Seam, and it is normal?

If it is, it is very restricting of someone wants to do some datamodel initialization in the constructor...

Cheers!

12. Mar 2010, 16:59 America/New_York | Link

The constructor may run twice as a result of some proxy creation. I think Seam does this for conversation-scoped components, I am not sure about page-scoped. Placing a breakpoint in the constructor and observing both the stack (who called it?) and the exact type of this object (is this a real object or a proxy?) could provide some insight...

12. Mar 2010, 17:02 America/New_York | Link

By the way I believe that the @Create methods are the prefered way of initializing a component.

12. Mar 2010, 17:11 America/New_York | Link

Kalispera Niko!

Well, the way I see it, that could very well be the case.

I did run the debugger, and the results are enlightening.

First of all, the first invocation is on the component itself (ProductBean), the second one is on its proxy (ProductBean$$javassistseam5@18669ac), so you are right.

Both request come from the HtmlInputText (the first of the two, as both beans will be available for the remaining JSF components).

Cheers,

Markos