掲示板

RE: JSF Bridge Portlet unable to do redirect to other page

thumbnail
11年前 に samuel tian によって更新されました。

JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 10 参加年月日: 12/06/14 最新の投稿
I've recently moved our primefaces project into liferay portlet using jsf faces (bridge). One of the problems we've find is, we were able to do:


try {
 context.getExternalContext().redirect("edit-survey.xhtml");
 } catch (IOException ex) {
  log.error(ex);
 }
context.responseComplete();


inside the @PostConstructor of our JSF viewscoped beans. User gets redirected to another JSF view when some criteria are not meet.

Instead, we got the following error:

Caused By: Java.Lang.IllegalStateException: 6.1.3.1: Unable To Redirect To A Non-Faces View During The RENDER_PHASE.
At Com.Liferay.Faces.Bridge.Context.BridgeContextImpl.Redirect(BridgeContextImpl.Java:497)
At Com.Liferay.Faces.Bridge.Context.ExternalContextImpl.Redirect(ExternalContextImpl.Java:149)


The question is, how do we redirect to another jsf view, during render phase? if not, could we rather redirect to a liferay page?
thumbnail
11年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
It should be possible to redirect to another JSF view during the RENDER_PHASE of the portlet lifecycle. Here is the relevant code from BridgeContextImpl.redirect(String):


					// Otherwise, if currently executing the RENDER_PHASE of the portlet lifecycle, then
					else if (portletPhase == Bridge.PortletPhase.RENDER_PHASE) {

						// If the specified URL is for a JSF viewId, then prepare for a render-redirect.
						if (bridgeRedirectURL.isFacesViewTarget()) {
							renderRedirect = true;
							renderRedirectURL = bridgeRedirectURL;
						}

						// Otherwise,
						else {
							...
						}
					}


So the key is to make sure that the URL that you specify actually targets a JSF viewId.

I would recommend that you try putting a leading slash character like "/edit-survey.xhtml"

Also, make sure that you have the path correct. You may need to use "/views/edit-survey.xhtml" if your Facelet views are in a sub-folder under src/main/webapp
thumbnail
11年前 に samuel tian によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 10 参加年月日: 12/06/14 最新の投稿
Hi Neil, your clue helped, thanks for you reply.

We've dug a bit more about this redirection issue those days. In our project, when we want redirect to another page, what we using before were those navigation rules defined in the face config xml file.

After moving to portlet environment, If we add <redirect/> to <navigation-case>, all the values in request map and flash map become null after page redirection.

I'm currently have no idea what caused this problem, our project & portlet setup is pretty much the same as the "primefaces 3" portlet inside "liferay faces demos". The only difference I could think is the SpringBeanFacesELResolver also configured.

However, I found if we use the target file name(without .xhtml extension) as the outcome for redirection, everything is fine. For example, if we use "edit-question?redirect-faces=true" as the outcome for an action, after redirection, request map and flash worked as expected.

Same thing happens for "ExternalContext().redirect()".

When you do ExternalContext().redirect(edit-question.xhtml), you get that exception saying redirect is not allowed in render phase. But ExternalContext().redirect(edit-question) worked.

BTW: because of reading this balusC's post. We've upgraded our jsf mojarra api into 2.1.14. What i described above is under JSF 2.1.14 with Liferay Faces 3.1.0-ga1
thumbnail
11年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
When you get a chance:

1. Please some debugger breakpoints inside of the bridge's BridgeContextImpl.redirect(String) method

2. For each of these cases you mentioned, please let me know the value of the specified URL, as well as the flow of the method, so that I can better understand how the method is handling your cases.

Thanks,

Neil
thumbnail
10年前 に Dámaris Suárez によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 14 参加年月日: 12/08/28 最新の投稿
Hi Neil, thanks for your tips.

I am having the same problem using liferay-faces-bridge-impl-3.1.1-ga2. I am facing the following issues:

1. I want to redirect to a url, not a xhtml in the same project, since the xhtml that I want to go to is on a different war.
2. I am always in the render phase when redirecting, because my portlet calls login and redirect as soon as it is accessed.
3. Looking at redirect method I noticed something:


A) BridgeRedirectURL bridgeRedirectURL = bridgeURLFactory.getBridgeRedirectURL(url, null, currentFacesViewId,this);
//Because of this call, and null on the second parameter, i am not sure if the lines after will ever return something that is not null
String viewIdRenderParameterValue = bridgeRedirectURL.getParameter(viewIdRenderParameterName);
If that is the case, that means that in RENDER PHASE and when not redirecting to the same project, it always leads to an Exception.

Is there any way that I can get inside this, when debugging?

if (viewIdRenderParameterValue != null) {

// TCK TestPage 179: redirectRenderPRP1Test
renderRedirect = true;
viewIdRenderParameterValue = URLDecoder.decode(viewIdRenderParameterValue,
StringPool.UTF8);
bridgeRedirectURL = bridgeURLFactory.getBridgeRedirectURL(viewIdRenderParameterValue,
null, currentFacesViewId, this);
renderRedirectURL = bridgeRedirectURL;
}

An option were to put my code in the same project i am trying to redirect to, but not sure if that is the best idea.

Thanks in advance,

Dámaris.
thumbnail
10年前 に Atif Hussain によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Junior Member 投稿: 47 参加年月日: 12/04/03 最新の投稿
You can do redirecting in two ways.
One is using faces-config.xml and second is using FacesContext.getCurrentInstance().getExternalContext().redirect("some url");

Case 1: Suppose you are at localhost:8080/web/liferay/home . And you have a portlet at this page.


Java class method :
public String forwardToDetailPage()
{
  return "forward_to_details";
}

<navigation-rule>
		<from-view-id>/xhtml/folder/main_page.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>forward_to_details</from-outcome>
			<to-view-id>/xhtml/folder/details.xhtml</to-view-id>
			<redirect />
		</navigation-case>
	</navigation-rule>

in this case you Main URL dose not change and you are able to switch from main.xhtml to details.xhtml (in the same portlet)

Case 2: Suppose you are at localhost:8080/web/liferay/home and you have a portlet at this page.

Java class method :
public String forwardToDetailPage()
{
 FacesContext.getCurrentInstance().getExternalContext().redirect("forward_to_details");
}

Your Main URL will chagne from
localhost:8080/web/liferay/home to localhost:8080/web/liferay/forward_to_details
thumbnail
10年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
I would also recommend that you read this other post that talks about the render-redirect feature of JSR 329.
thumbnail
10年前 に Dámaris Suárez によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 14 参加年月日: 12/08/28 最新の投稿
Hi Neil, i´m just finding the same error when trying your example. I don´t use "managedBean" annotation and a constructor, but "named" annotation (java.inject) and a postconstruct annotated method. I try to redirect to a url which contains portlets that are inside another war.

The behaviour it´s always the same and it´s more understandable it I just paste your code here:

@Override
public void redirect(String url) throws IOException {

if (url != null) {
logger.debug("redirect url=[{0}]", url);

String currentFacesViewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
//Question: Why is the second parameter null? Is it not the parameters that I try to store in viewIdRenderParameterValue at the end of this method?
//How can I avoid that viewIdRenderParameterValue will be null, and therefore that I get an Exception?
BridgeRedirectURL bridgeRedirectURL = bridgeURLFactory.getBridgeRedirectURL(url, null, currentFacesViewId,
this);

// If currently executing the ACTION_PHASE, EVENT_PHASE, or RENDER_PHASE of the portlet lifecycle, then
if ((portletPhase == Bridge.PortletPhase.ACTION_PHASE) ||
(portletPhase == Bridge.PortletPhase.EVENT_PHASE) ||
(portletPhase == Bridge.PortletPhase.RENDER_PHASE)) {

// If the specified URL starts with a "#" character, is external to this application, or has a
// "javax.portlet.faces.DirectLink" parameter value of "true", then
if ((portletPhase == Bridge.PortletPhase.ACTION_PHASE) &&
(url.startsWith(StringPool.POUND) || bridgeRedirectURL.isExternal() ||
BooleanHelper.isTrueToken(bridgeRedirectURL.getParameter(Bridge.DIRECT_LINK)))) {

bridgeRequestScope.setRedirectOccurred(true);

// TCK NOTE: The TCK does not appear to have a test that invokes this condition.
portletContainer.redirect(bridgeRedirectURL.toString());
}

// Otherwise,
else {

// If currently executing the ACTION_PHASE or EVENT_PHASE of the portlet lifecycle, then
if ((portletPhase == Bridge.PortletPhase.ACTION_PHASE) ||
(portletPhase == Bridge.PortletPhase.EVENT_PHASE)) {

// TCK NOTE: The TCK will invoke this condition during the
// TestPage039-requestNoScopeOnRedirectTest and TestPage176-redirectActionTest.
FacesContext facesContext = FacesContext.getCurrentInstance();
String oldViewId = facesContext.getViewRoot().getViewId();
String newViewId = bridgeRedirectURL.getContextRelativePath();

// NOTE: Since the Portlet API assumes that the portlet container implements the
// post-redirect-get pattern, need to treat this as a navigation from one Faces view to another.
// In this case the client does not redirect, rather the new Faces View is rendered. Note that
// Liferay Portal does not implement the post-redirect-get pattern, rather, the entire portlet
// lifecycle occurs within the POST operation. So currently, for Liferay, the <redirect/> is
// effectively ignored. :-(
if (!oldViewId.equals(newViewId)) {

// Create the new view and place it into the FacesContext.
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
UIViewRoot newViewRoot = viewHandler.createView(facesContext, newViewId);
facesContext.setViewRoot(newViewRoot);

// Update the PartialViewContext.
partialViewContextRenderAll(facesContext);

// Set the response as "complete" in the FacesContext.
facesContext.responseComplete();

// Set a flag on the {@link BridgeRequestScope} indicating that a <redirect />
// occurred which means that the request attributes should not be preserved.
getBridgeRequestScope().setRedirectOccurred(true);

// Apply the PortletMode, WindowState, etc. that may be present in the URL to the response.
try {
StateAwareResponse stateAwareResponse = (StateAwareResponse) portletResponse;
bridgeRedirectURL.applyToResponse(stateAwareResponse);
}
catch (PortletModeException e) {
logger.error(e.getMessage());
}
catch (WindowStateException e) {
logger.error(e.getMessage());
}
}
}

// Otherwise, if currently executing the RENDER_PHASE of the portlet lifecycle, then
else if (portletPhase == Bridge.PortletPhase.RENDER_PHASE) {

// If the specified URL is for a JSF viewId, then prepare for a render-redirect.
//I cannot come inside, since I try to reach a url, not a view inside the same project.
if (bridgeRedirectURL.isFacesViewTarget()) {
renderRedirect = true;
renderRedirectURL = bridgeRedirectURL;
}

// Otherwise,
else {

// If there is a URL parameter specifying a JSF viewId, then prepare for a render-redirect.
String viewIdRenderParameterName = bridgeConfig.getViewIdRenderParameterName();
String viewIdRenderParameterValue = bridgeRedirectURL.getParameter(
viewIdRenderParameterName);
//I have the suspicion, this parameter will always be null when I get to this point, is that right?
if (viewIdRenderParameterValue != null) {

// TCK TestPage 179: redirectRenderPRP1Test
renderRedirect = true;
viewIdRenderParameterValue = URLDecoder.decode(viewIdRenderParameterValue,
StringPool.UTF8);
bridgeRedirectURL = bridgeURLFactory.getBridgeRedirectURL(viewIdRenderParameterValue,
null, currentFacesViewId, this);
renderRedirectURL = bridgeRedirectURL;
}

// Otherwise, throw an IllegalStateException according to Section 6.1.3.1 of the Spec.
//I get this exception
else {
throw new IllegalStateException(
"6.1.3.1: Unable to redirect to a non-Faces view during the RENDER_PHASE.");
}
}
}
}
}

// Otherwise, since executing the RESOURCE_PHASE of the portlet lifecycle:
else {

// NOTE: The Bridge Spec indicates that the redirect is to be ignored, but JSF 2 has the ability to
// redirect during Ajax.
portletContainer.redirect(bridgeRedirectURL.toString());
}
}
else {
logger.error("redirect url=null");
}
}


The problem inside this method is that I am always at the RENDER_PHASE (log in and redirect are supposed to happen as the portlet is shown), but because of not accesing to a xhtml in the same project, but to a url in a different war, and I suspect because of a setting of null of certain parameters, I will always get an exception at this point. I might have some holes in my understanding of JSF and its phases, so maybe showing a non-faces view (what is that actually?) at the render phase is at all not possible.

Thanks in advance,

Dámaris.
thumbnail
10年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
Hi Dámaris,

Thanks for mentioning that you are using the java.inject.Named annotation. Are you trying to use Google Guice or perhaps JBoss Weld for dependency injection?

Kind Regards,

Neil
thumbnail
10年前 に Dámaris Suárez によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 14 参加年月日: 12/08/28 最新の投稿
Hi Neil,

I am using a Jboss 7.1 with Liferay integrated, but i am not using these jboss libraries (or google) for dependency injection. I am using, and don´t know if this information is useful, a service to translate request and session scope annotation from java to spring notation.

As far as I am concerned i use javax.enterprise.context.RequestScoped and java.inject.Named in my controller class.

Do you have an idea what i might do at this point?

Thanks,

Dámaris.
thumbnail
10年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
Hi Dámaris,

If your beans are annotated with java.inject.Named and javax.enterprise.context.RequestScoped, then I think you are probably using CDI in some way.

Do you have a WEB-INF/beans.xml descriptor in your portlet project? If so, then since you are using JBoss 7.1, the JBoss Weld implementation (bundled with JBoss AS) would get activated.

But you mentioned that you are using Spring -- are you using some type of CDI+Spring integration like this blog post by Matthias Wessendorf?

Kind Regards,

Neil
thumbnail
10年前 に Dámaris Suárez によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 14 参加年月日: 12/08/28 最新の投稿
Hi Neil,

Thanks for your answer, yes, I do have a CdiScopeMetadataResolver class as you mention, and no beans.xml descriptor in my portlet (there are some beans.xml in other services and middleware projects used by my portlets, but empty).

Does it bring some clarity on why I cannot redirect to pages outside my portlet in the render phase?

Best regards,

Dámaris.
thumbnail
10年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
Hi Dámaris,

OK, thank you for clarifying that Weld is not activating in your application. I took a closer look and I think I understand the problem now. The javax.portlet.RenderResponse interface of the Portlet API does not support the ability to redirect. That's why the bridge is reporting "6.1.3.1: Unable to redirect to a non-Faces view during the RENDER_PHASE" when you try to perform the render-redirect to a non-Faces view.

One way you can work around this, is to render a small <script>...</script> that performs the redirect to the non-Faces URL via JavaScript.

Kind Regards,

Neil
thumbnail
10年前 に Dámaris Suárez によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

New Member 投稿: 14 参加年月日: 12/08/28 最新の投稿
Hi Neil,

Thanks for your answer, this workaround works fine.


Best regards,

Dámaris.
thumbnail
10年前 に Neil Griffin によって更新されました。

RE: JSF Bridge Portlet unable to do redirect to other page

Liferay Legend 投稿: 2655 参加年月日: 05/07/27 最新の投稿
It's my pleasure Dámaris. So glad to hear that it is working for you. Thanks for using Liferay Faces. emoticon
thumbnail
8年前 に Kyle Joseph Stiemann によって更新されました。

Thread Split

Liferay Master 投稿: 760 参加年月日: 13/01/14 最新の投稿