Forums de discussion

Problem with f:ajax listener sometimes not called in a portlet

Benjamin Cassan, modifié il y a 10 années.

Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
I have a jsf page with a link for calling a search with Ajax. The link is automatically clicked with javascript. The form is in several instances of a JSF 2 portlet in liferay. Usually, everything works fine, but sometimes, the listener is not called.

In the portlet:
<h:form id="result">
...
  <h:commandlink id="launch">
    <f:ajax event="click" render="errors content debug" listener="#{resultBackingBean.doSearch}" />
  </h:commandlink>
...
</h:form>


When it works, I have the following traces:

12:55:10,476 DEBUG AFTER phaseId=[RESTORE_VIEW 1] viewId=[/views/search_result.xhtml]
12:55:10,476 DEBUG BEFORE phaseId=[APPLY_REQUEST_VALUES 2] viewId=[/views/search_result.xhtml]
12:55:10,476 DEBUG AFTER phaseId=[APPLY_REQUEST_VALUES 2] viewId=[/views/search_result.xhtml]
12:55:10,476 DEBUG BEFORE phaseId=[PROCESS_VALIDATIONS 3] viewId=[/views/search_result.xhtml]
12:55:10,476 DEBUG AFTER phaseId=[PROCESS_VALIDATIONS 3] viewId=[/views/search_result.xhtml]
12:55:10,486 DEBUG BEFORE phaseId=[UPDATE_MODEL_VALUES 4] viewId=[/views/search_result.xhtml]
12:55:10,486 DEBUG AFTER phaseId=[UPDATE_MODEL_VALUES 4] viewId=[/views/search_result.xhtml]
12:55:10,486 DEBUG BEFORE phaseId=[INVOKE_APPLICATION 5] viewId=[/views/search_result.xhtml]
12:55:10,486 DEBUG doSearch called
12:55:10,486 DEBUG AFTER phaseId=[INVOKE_APPLICATION 5] viewId=[/views/search_result.xhtml]
12:55:10,486 DEBUG BEFORE phaseId=[RENDER_RESPONSE 6] viewId=[/views/search_result.xhtml]
12:55:10,496 DEBUG AFTER phaseId=[RENDER_RESPONSE 6] viewId=[/views/search_result.xhtml]


I can see my own debug trace doSearch called. But sometimes, after page reloading, I do not see my trace, the listener is never called. I always have the Detected Ajax ResourceRequest trace, but there is nothing between BEFORE INVOKE_APPLICATION and AFTER INVOKE_APPLICATION.

If I want the page to work again, I have to reload it. As if something wrong was sometimes occurring during some loading, as the behavior can change only after the page is reloaded.

I tried to change the scope of the bean with doSearch (from viewScope to sessionScope), I tried to set immediate="true" for the f:ajax, I tried to use a4j:ajax, but I still have the same behavior that I don't understand... Why? emoticon
thumbnail
Neil Griffin, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 2655 Date d'inscription: 27/07/05 Publications récentes
When you get a chance, please enable the Developer Tools in Chrome (or FireBug in FireFox) and see if there are any JavaScript errors reported by the browser when the error is being reproduced. Since you don't see any output, I am wondering if the Ajax request is even getting submitted?
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
I always get a log trace "Detected Ajax ResourceRequest", before the beginning of the lifecycle, and with findbugs, I see the ajax request, with a response:
<partial-response>
  <changes>
    <update id="javax.faces.ViewState">6791417547372597471:4319149946307207422</update>
  </changes>
</partial-response>


As I understand it, the ajax request is processed, but as my listener is not called, nothing is returned by the request. On a working request, the response has several updates for the items to render.
I have no javascript errors, not even warnings.
thumbnail
Juan Gonzalez, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 3089 Date d'inscription: 28/10/08 Publications récentes
Plase can can you send the complete .xhtml, managed beans and faces-config.xml?

Thanks.
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
Here it is.

xhtml:
<!--?xml version="1.0"?-->

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets">
	<h:head>
	</h:head>
	<h:body>

		<h:form id="result">
			<h:panelgroup id="errors">
				<ui:repeat var="error" value="#{resultModelBean.errorsList}">
					<ul class="error">
						<li>
							<h:outputtext value="#{error}" />
						</li>
					</ul>
				</ui:repeat>
			</h:panelgroup>
	
			<h:commandlink id="launch" binding="#{resultBackingBean.launchSearch}">
				<f:ajax event="click" render="errors content debug" listener="#{resultBackingBean.doSearch}" />
			</h:commandlink>
	
			<h:panelgroup id="content">
				<h:panelgroup rendered="#{(resultModelBean.feed == null) and (!resultModelBean.searchDone)}" layout="block" style="padding: 1em;text-align: center;">
					<h:graphicimage value="/images/ajax-loader.gif" width="32" height="32" longdesc="#{i18n['result-wait']}" />
				</h:panelgroup>
	
	
				<h:panelgroup rendered="#{(resultModelBean.feed == null) and (resultModelBean.searchDone)}">
					<h:outputlabel value="#{i18n['result-noValue']}" />
				</h:panelgroup>
	
				<h:panelgroup rendered="#{resultModelBean.feed != null}">
					<h2>
						<h:panelgroup rendered="#{(resultModelBean.feed.link != null) and (!resultModelBean.feed.link.isEmpty())}">
							<h:outputlink target="_blank" value="#{resultModelBean.feed.link}">
								<h:outputtext value="#{resultModelBean.feed.title}" />
							</h:outputlink>
						</h:panelgroup>
						<h:panelgroup rendered="#{(resultModelBean.feed.link == null) or (resultModelBean.feed.link.isEmpty())}">
							<h:outputtext value="#{resultModelBean.feed.title}" />
						</h:panelgroup>
					</h2>
		
					<h:outputlabel value="#{resultModelBean.feed.description}" />

					<ui:repeat var="category" value="#{resultModelBean.entries.entrySet().toArray()}">
						<h:panelgroup rendered="#{null != category.key}">
							<ul>
								<li><h:outputtext value="#{category.key.name}" /></li>
									<ul>
										<ui:repeat var="entry" value="#{category.value}">
											<li>
												<!-- Display with link -->
												<h:panelgroup rendered="#{(entry.link != null) and (!entry.link.isEmpty())}">
													<h:outputlink target="_blank" value="#{entry.link}">
														<h:outputtext value="#{entry.title}" />
													</h:outputlink>
												</h:panelgroup>
												<!-- Display without link -->
												<h:panelgroup rendered="#{(entry.link == null) || (entry.link.isEmpty())}">
													<h:outputtext value="#{entry.title}" />
												</h:panelgroup>
											</li>
										</ui:repeat>
									</ul>
							</ul>
						</h:panelgroup>
						<h:panelgroup rendered="#{null == category.key}">
							<ui:repeat var="entry" value="#{category.value}">
								<ul>
									<li>
										<!-- Display with link -->
										<h:panelgroup rendered="#{(entry.link != null) and (!entry.link.isEmpty())}">
											<h:outputlink target="_blank" value="#{entry.link}">
												<h:outputtext value="#{entry.title}" />
											</h:outputlink>
										</h:panelgroup>
										<!-- Display without link -->
										<h:panelgroup rendered="#{(entry.link == null) || (entry.link.isEmpty())}">
											<h:outputtext value="#{entry.title}" />
										</h:panelgroup>
									</li>
								</ul>
							</ui:repeat>
						</h:panelgroup>
					</ui:repeat>
				</h:panelgroup>
			</h:panelgroup>
	
			<h:panelgroup id="debug">
				<h:panelgroup rendered="#{resultModelBean.debugRendered}" style="color: #CCCCCC; font-size: x-small;" layout="block">
					<h:outputformat value="Namespace: {0} {1}">
						<f:param value="#{facesContext.externalContext.encodeNamespace(':result:launch')}" />
						<f:param value="#{resultBackingBean.launchSearch.clientId}" />
					</h:outputformat><br>
					<h:outputformat value="Product: {0}">
						<f:param value="#{searchDataModelBean.searchCriteria.product}" />
					</h:outputformat>
					<br>
					<h:outputformat value="Search: {0}">
						<f:param value="#{searchDataModelBean.searchCriteria.wordSearch}" />
					</h:outputformat>
					<br>
					<h:outputformat value="URL: {0}">
						<f:param value="#{resultModelBean.url}" />
					</h:outputformat>
					<br>
					<br>
					<h:outputtext value="#{resultModelBean.feed}" />
				</h:panelgroup>
			</h:panelgroup>
		</h:form>

		<script>
			<!-- Automatic launch of the search -->
			document.getElementById('#{resultBackingBean.launchSearch.clientId}').click();
		</script>
	</h:body>
</f:view>


Managed bean that has the listener function:
/**
 * 
 */
package cg.portal.search.bean;

import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ComponentSystemEvent;
import javax.portlet.PortletPreferences;
import javax.portlet.PortletRequest;

import org.apache.log4j.Logger;

import com.sun.syndication.feed.synd.SyndCategory;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.FeedException;

import cg.common.exception.ApplicativeException;
import cg.portal.search.dto.LoginData;
import cg.portal.search.dto.SearchCriteria;
import cg.portal.search.dto.WebApplicationConfig;
import cg.portal.search.service.ISearchService;

/**
 * Class used to call the search and retrieve the result.
 *
 */
@ManagedBean(name = "resultBackingBean")
@ViewScoped
public class ResultBackingBean implements Serializable {

    /**
     * ID
     */
    private static final long serialVersionUID = -350181546053439898L;

    private static final Logger LOG = Logger.getLogger(ResultBackingBean.class);

    // Injections
    @ManagedProperty(name = "searchDataModelBean", value = "#{searchDataModelBean}")
    private SearchDataModelBean searchDataModelBean;

    // Injections
    @ManagedProperty(name = "resultModelBean", value = "#{resultModelBean}")
    private ResultModelBean resultModelBean;

    // Injections
    @ManagedProperty(name = "webApplicationModelBean", value = "#{webApplicationModelBean}")
    private WebApplicationModelBean webApplicationModelBean;

    // Injections
    @ManagedProperty(name = "searchService", value = "#{searchService}")
    private ISearchService searchService;

    /** the link to activate.
     */ 
    private UIComponent launchSearch;

    public void doSearch(AjaxBehaviorEvent event) {
        LOG.debug("doSearch called");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.trace("@PostConstruct annotation worked");
    }

    @PreDestroy
    public void preDestroy() {
        LOG.trace("@PreDestroy annotation worked");
    }

    /**
     * Setter for injecting the model bean
     * 
     * @param searchModelBean
     */
    public void setSearchDataModelBean(SearchDataModelBean searchDataModelBean) {

        // Injected via ManagedProperty annotation
        this.searchDataModelBean = searchDataModelBean;
    }

    /**
     * Setter for injecting the model bean
     * 
     * @param ResultModelBean
     */
    public void setResultModelBean(ResultModelBean resultModelBean) {

        // Injected via ManagedProperty annotation
        this.resultModelBean = resultModelBean;
    }

    /**
     * Setter for injecting the web application bean
     * 
     * @param webApplicationBean
     */
    public void setWebApplicationModelBean(WebApplicationModelBean webApplicationModelBean) {

        // Injected via ManagedProperty annotation
        this.webApplicationModelBean = webApplicationModelBean;
    }

    /**
     * Setter for injecting the service
     * 
     * @param searchService
     */
    public void setSearchService(ISearchService searchService) {

        // Injected via ManagedProperty annotation
        this.searchService = searchService;
    }

    /**
     * @return the launchSearch
     */
    public UIComponent getLaunchSearch() {
        return launchSearch;
    }

    /**
     * @param launchSearch the launchSearch to set
     */
    public void setLaunchSearch(UIComponent launchSearch) {
        this.launchSearch = launchSearch;
    }
}


faces-config.xml:
<!--?xml version="1.0"?-->

<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
	<navigation-rule>
		<navigation-case>
			<from-outcome>ipc.searchLaunched</from-outcome>
			<to-view-id>/views/search_result.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>
	<lifecycle>
		<phase-listener>com.liferay.faces.util.lifecycle.DebugPhaseListener</phase-listener>
	</lifecycle>
</faces-config>
thumbnail
Juan Gonzalez, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 3089 Date d'inscription: 28/10/08 Publications récentes
Hi Benjamin,

Guess you're hitting this issue:

http://java.net/jira/browse/JAVASERVERFACES-1492

by your #{resultBackingBean.launchSearch} binding value. I wouldn't recommend to use binding, if it can be avoided.

To solve it you have these options:

* Change your managed bean to be @RequestScope (and solve all problems that could arise).
* Change javax.faces.PARTIAL_STATE_SAVING to false
* Get the JSF 2.2 jars and overwrite previous ones from your portlet. Don't know if this have been solved in 2.1.x. If you're using JSF 2.2 remember to use the proper Liferay Faces version as you can read here http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+Faces+Version+Scheme
thumbnail
Neil Griffin, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 2655 Date d'inscription: 27/07/05 Publications récentes
I agree with Juan. If possible, avoid the binding attribute, backing beans should be @RequestScoped, and @ViewScoped should be limited to model beans. If the backing bean needs to access model data, then model beans can be injected into the backing bean with @ManagedProperty (JSF) or with @Inject (CDI).

We haven't released a version of Liferay Faces that is compatible with JSF 2.2 yet, so upgrading to JSF 2.2 isn't an option at this time.
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
I changed to request scope, but the problem is still there. It was request scope before, and I changed it due to the binding, mostly for accessing the ID with javascript.

I'm not sure for this issue. According to the description, the effect of the bug is to lose the binding value. I use the binding only to retrieve the ID of the link for the javascript side, for clicking the link. The fact is that I don't have problem with that, it works all the time, the link is activated, and the ajax is activated. So it seems that I don't lose the binding.

I will try to find another solution for the binding, even if it does not seem to this issue. Maybe there are other side effects.
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
I also tried to put the function in a session scope managed bean, with the same behavior: the click was ok, ajax was processed, but sometimes my function is not called.
thumbnail
Neil Griffin, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 2655 Date d'inscription: 27/07/05 Publications récentes
I see that you have the DebugPhaseListener registered in faces-config.xml which is good. Do you have the level set to debug in your log4j.properties file? If yes, then what phases of the JSF lifecycle execute when you click the button when it works, and when it does not work?
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
The level is set to DEBUG. Maybe my post was not clear enough, I see the same phases in both situation:

When working, I can see my own debug trace set in the listener method:
DEBUG AFTER phaseId=[RESTORE_VIEW 1] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[APPLY_REQUEST_VALUES 2] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[APPLY_REQUEST_VALUES 2] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[PROCESS_VALIDATIONS 3] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[PROCESS_VALIDATIONS 3] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[UPDATE_MODEL_VALUES 4] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[UPDATE_MODEL_VALUES 4] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[INVOKE_APPLICATION 5] viewId=[/views/search_result.xhtml]
DEBUG doSearch called
DEBUG AFTER phaseId=[INVOKE_APPLICATION 5] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[RENDER_RESPONSE 6] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[RENDER_RESPONSE 6] viewId=[/views/search_result.xhtml]


When not working, I have the same phases, but without my log (and without any processing that could be in the method):
DEBUG AFTER phaseId=[RESTORE_VIEW 1] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[APPLY_REQUEST_VALUES 2] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[APPLY_REQUEST_VALUES 2] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[PROCESS_VALIDATIONS 3] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[PROCESS_VALIDATIONS 3] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[UPDATE_MODEL_VALUES 4] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[UPDATE_MODEL_VALUES 4] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[INVOKE_APPLICATION 5] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[INVOKE_APPLICATION 5] viewId=[/views/search_result.xhtml]
DEBUG BEFORE phaseId=[RENDER_RESPONSE 6] viewId=[/views/search_result.xhtml]
DEBUG AFTER phaseId=[RENDER_RESPONSE 6] viewId=[/views/search_result.xhtml]


Except the fact that the method is not called, I see no difference in logs, so I really don't have a clue, no error or warning, or trace that could indicate why the behavior is different sometimes.
thumbnail
Neil Griffin, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 2655 Date d'inscription: 27/07/05 Publications récentes
When you get a chance, please try your test as a plain webapp outside of Liferay Portal. If it works as a webapp, but not as a portlet then that might indicate that we have a bug in the bridge.
thumbnail
Vernon Singleton, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Expert Publications: 315 Date d'inscription: 14/01/13 Publications récentes
Hi Benjamin,

I notice that you keep saying that _sometimes_ it does not work. Could your issue be session related? Could it be that it does not work after _say_ 30 minutes when your session times out? But then it works again when you have a new session? How would you measure the intermittent nature of this bug?

Other than those questions, I agree with Neil and Juan, please test your webapp outside of the liferay portal, to help us determine if the bug is related to the bridge.
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
No, the "sometimes" is not time dependent or session related. It can happen very quickly after logging in, and when it is not working, refreshing the page can make it work again. And the page with ajax is loaded only after verification of some data in session.
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
I followed your advice, to avoid using bindings. I removed it, using another way for getting the client ID of the link (looking all the UIComponents until I find the good one, not very nice comparing to bindings, but it works). And for the moment, I cannot reproduce the issue. Maybe it is the bug you were talking about, or something else that could be broken by the binding.

So I don't know if it is this bug, but thank for the advice to avoid bindings emoticon
thumbnail
Neil Griffin, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 2655 Date d'inscription: 27/07/05 Publications récentes
Glad to hear that it is working. Juan's advice about avoiding the binding attribute is very good. The UIComponent.findComponent(String) method is an efficient way to get the reference to a component in the tree, so that you don't have to search the tree yourself. There is an example of how to do this at line 150 of ApplicantBackingBean.postalCodeListener(ValueChangeEvent) in the icefaces3-compat-portlet. If you want to search the tree from the top, then you can call FacesContext.getViewRoot() and then call uiViewRoot.findComponent(String).
Benjamin Cassan, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

New Member Publications: 15 Date d'inscription: 02/01/13 Publications récentes
Thanks a lot, this is a better solution than the one I found (that was implementing a recursive search from the UIViewRoot). It is better to use the existing findComponent emoticon

From an external point of view, it seemed cleaner to bind to the component than searching in the tree, this is why I choose the binding. Now I know it is better to avoid it ;)
thumbnail
Neil Griffin, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 2655 Date d'inscription: 27/07/05 Publications récentes
I agree with you -- the binding attribute is a nice dependency-injection type of mechanism, whereas findComponent is more of a dependency-pull. But historically there have been technical challenges with using the binding attribute in JSF applications, so I'm sad to say that it should be avoided.
thumbnail
Juan Gonzalez, modifié il y a 10 années.

RE: Problem with f:ajax listener sometimes not called in a portlet

Liferay Legend Publications: 3089 Date d'inscription: 28/10/08 Publications récentes
Great it worked.

Probably that binding clientId get lost during GET or after postback (that's why you said it worked sometimes). So probably is the same issue we were talking about.

Yep, bindings need to be used with care. I don't like it at all, and I didn't find any case which I really need it (and don't have any other alternative). I prefer more "stateless" apps, and binding doesn't help much. But that's another story.. emoticon