Fórumok

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Jon Gilmore, módosítva 11 év-val korábban

LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
Hello,

I receive a NullPointerException when calling getPortletInstanceId(). I am using liferay-faces-portal version 3.0.0-BETA1.

LiferayFacesContext liferayFacesContext = LiferayFacesContext.getInstance();
liferayFacesContext.getPortletInstanceId();

java.lang.NullPointerException
at com.liferay.faces.portal.context.LiferayPortletHelperImpl.getPortletInstanceId(LiferayPortletHelperImpl.java:147)
at com.liferay.faces.portal.context.LiferayFacesContextImpl.getPortletInstanceId(LiferayFacesContextImpl.java:743)

Thanks for any suggestions.
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Hi Jon,

What version of Liferay Portal are you using?

Neil
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
We're using liferay 6.1
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Ah, you need to use Liferay Faces 3.1.x (currently 3.1.0-BETA2) for Liferay 6.1

The 3.0.x version that you're using is for Liferay 6.0
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Fixed in FACES-1212. emoticon
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
Hi Neil,
Thanks for your help. I've updated my portlet to use liferay-faces-bridge-impl-3.1.0-BETA2.jar and liferay-faces-portal-3.1.0-BETA2.jar.

Unfortunately I'm still getting the following exception when running in Liferay 6.1. Can I provide you with any more information?

java.lang.NullPointerException
at com.liferay.faces.portal.context.LiferayPortletHelperImpl.getPortletInstanceId(LiferayPortletHelperImpl.java:147)
at com.liferay.faces.portal.context.LiferayFacesContextImpl.getPortletInstanceId(LiferayFacesContextImpl.java:743)


/**
 * Fades out/in a portlet out from the view when visible is set to false/true. Uses the portlet id and jquery to alter the portlets encapsulating dom.
 */
 public final class FTSVisibilityRenderer extends DomBasicRenderer
{
    @Override
    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
    {
        ValueExpression valueExpression = uiComponent.getValueExpression("visibility");
        FTSLiferayPortlet liferayPortlet = (FTSLiferayPortlet) valueExpression.getValue(facesContext.getELContext());

        if (liferayPortlet == null)
            LOG.warn("Unable to determine visibility from value expressions, defaulting to true.");

        Boolean isVisible = ( liferayPortlet != null ? liferayPortlet.isPortletVisible() : true );

        LiferayFacesContext portletFacesContext = LiferayFacesContext.getInstance();
        if (LOG.isDebugEnabled())
            LOG.debug("Visibility for portlet: '" + portletFacesContext.getPortletName() + "' is: " + isVisible);
        
        String portletId = "p_p_id_" + portletFacesContext.getPortletInstanceId() + "_";

        String javascript = MessageFormat.format(FADEOUT, new Object[] {portletId});
        if (isVisible)
            javascript = MessageFormat.format(FADEIN, new Object[] {portletId});

        JavascriptContext.addJavascriptCall(facesContext, javascript);
    }

    private static final String FADEOUT = "jQuery(''#{0}'').fadeOut(''fast'');";
    private static final String FADEIN  = "jQuery(''#{0}'').fadeIn(''fast'');";
Hasika Pamunuwa, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 11 Csatlakozás dátuma: 2010.06.21. Legújabb bejegyzések
I think it'll be fixed in 3.1.0 RC1.

Following worked for me.


LiferayFacesContext liferayFacesContext = LiferayFacesContext.getInstance();
PortletRequestImpl portletRequest = (PortletRequestImpl) liferayFacesContext.getExternalContext().getRequest();
Portlet portlet = portletRequest.getPortlet();
String portletInstanceId = portlet.getPortletId();
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Hasika is correct -- fixed in 3.1.0-RC1. You can grab the source from GitHub and build 3.1.0-RC1-SNAPSHOT locally for now if you like.
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
3.1.0-RC1 has certainly helped. LiferayFacesContext.getPortletInstanceId() now works from within a JSF thread.

Unfortunately, I'm performing a render request via PortableRenderer.render() !! i.e. from outside a JSF thread I get the following stack trace:

2012-05-11 12:07:27,721 [http-nio-8080-exec-17] DEBUG com.liferay.faces.portal.el.ExtELResolver:debug:39 - Unable to resolve variable [liferay] value=null
2012-05-11 12:07:27,737 [http-nio-8080-exec-17] DEBUG com.liferay.faces.portal.el.ExtELResolver:debug:39 - Unable to resolve variable [liferay] value=null
java.lang.NullPointerException
at com.liferay.faces.portal.context.LiferayPortletHelperImpl.getPortletInstanceId(LiferayPortletHelperImpl.java:163)
at com.liferay.faces.portal.context.LiferayFacesContextImpl.getPortletInstanceId(LiferayFacesContextImpl.java:743)

This was certainly possible using edoarasframework 1.3.0 together with liferay 5.2. Do you think it will be made possible in liferay-faces?
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Under the hood, Liferay Faces Portal has a managed-bean named Liferay that uses the JSF2 ViewScoped annotation. This has the effect of keeping data (like the current com.liferay.portal.model.Portlet instance) in-memory. For example, here's the constructor of the bean:
public Liferay() {
	// FACES-1212: Cache the "RENDER_PORTLET" request attribute here in the constructor since it is only
	// available during the RENDER_PHASE of the portlet lifecycle.
	FacesContext facesContext = FacesContext.getCurrentInstance();
	PortletRequest portletRequest = (PortletRequest) facesContext.getExternalContext().getRequest();
	portlet = (Portlet) portletRequest.getAttribute(WebKeys.RENDER_PORTLET);
}


Back in the days of PortletFaces-Tools (edoarasframework 1.3.0) this bean was kept in memory with ICEfaces Extended-Request scope. This has been replaced by JSF2 ViewScoped, which apparently doesn't last long enough for your needs. If you want to use the Liferay managed bean outside of a JSF thread (and outside of a PortletRequest) like you're doing with the PortableRenderer, then my guess would be that the Liferay managed-bean would have to be kept around longer than ViewScoped permits.

I suppose one thing you could try, would be to have the following in your portlet's WEB-INF/faces-config.xml descriptor:
<managed-bean>
<managed-bean-name>liferay</managed-bean-name>
<managed-bean-class>com.liferay.faces.portal.bean.Liferay</managed-bean-class>
<managed-bean-scope>#{window}</managed-bean-scope>
</managed-bean>


The WEB-INF/faces-config.xml is designed to "win" when there is a conflict with annotations. So by using the managed-bean syntax above, I would *think* it would cause the Liferay managed-bean to be placed in the ICEfaces WindowScope which might live long enough for your needs.
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
Your suggestion does not seem to override as hoped. I tried your code sample and received the same error as before, and then even tried changing the liferay bean scope to session, which I'm sure is not advised, but it didn't help either.
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Please checkout the source from GitHub and change the annotation in Liferay.java from @ViewScoped to @SessionScoped and let me know if that works.
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
Hi again, with the liferay bean session scope set session it now complains about the headManagedBean being null:


2012-05-14 14:42:28,556 [http-nio-8080-exec-11] DEBUG com.liferay.faces.portal.el.ExtELResolver:debug:39 - Unable to resolve variable [headManagedBean] value=null
2012-05-14 14:42:28,556 [Message Mediator Thread] ERROR com.ftsinfo.portal.model.event.FTSPortletEventMulticastor:handle
SessionMessage:199 - (8437da) Problem encountered message.
java.lang.NullPointerException
        at com.liferay.faces.portal.context.LiferayPortletHelperImpl.getPortletRequest(LiferayPortletHelperImpl.java:168)
        at com.liferay.faces.portal.context.LiferayPortletHelperImpl.getPortlet(LiferayPortletHelperImpl.java:150)
        at com.liferay.faces.portal.context.LiferayPortletHelperImpl.getPortletInstanceId(LiferayPortletHelperImpl.java:163)
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
The stacktrace you posted doesn't seem to mention the "Caused By" issue regarding the headManagedBean. Can you look more deeply into your stacktrace and see if it mentions a "Caused By" exception regarding HeadPhaseListener or HeadRendererBridgeImpl? Those are the two classes that call HeadManagedBean.getInstance(FacesContext).
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
There is no other log output I'm afraid. I have DEBUG switched on, I could try TRACE if you think that will help.
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
Also, a call to JavascriptContext.addJavascriptCall(liferayFacesContext, javascript) from a non jsf thread also results in a NullPointerException. I'm guessing this is a very similar issue.


java.lang.NullPointerException
        at com.liferay.faces.portal.context.LiferayFacesContextImpl.getExternalContext(LiferayFacesContextImpl.java:515)
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Line 515 of LiferayFacesContextImpl currently looks like this:

return FacesContext.getCurrentInstance().getExternalContext();


I'd like to try and make LiferayFacesContext work for you in this non-JSF-thread situation, but if the call to FacesContext.getCurrentInstance().getExternalContext() is returning null, then this may be opening a can of worms. Please put this line of code in your non-JSF-thread and let me know the output:

System.err.println("facesContext=" + FacesContext.getCurrentInstance());


Thanks,

Neil
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
FacesContext.getCurrentInstance() returns null
thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
I spoke to my contact at ICEsoft about this, and I think I have a better understanding of what's going on now.

It makes sense that FacesContext.getCurrentInstance() returns null in a non-JSF-thread in an ICEfaces 2.x/3.x portlet. The reason why your code worked with ICEfaces 1.8 Ajax Push is because ICEfaces would create a pseudo FacesContext in a non-JSF-thread.

The reason why your code is not porting to ICEfaces 2.0/3.0 is because it doesn't create the pseudo FacesContext anymore. Instead the ICEpush mechanism is purely a notification system now, and so you will probably have to refactor your code a little. Basically, your non-JSF-thread should not be changing JSF model managed-bean values or calling anything that relies on FacesContext. When your code calls PortableRenderer.render() it will simply instruct the browser to execute a new XmlHttpRequest() that will run the Faces lifecycle. At that time, your JSF model managed-beans should determine the latest values, so that the browser's DOM will be updated accordingly.

For more information, see: http://wiki.icesoft.org/display/ICE/Ajax+Push+-+APIs
Jon Gilmore, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

New Member Bejegyzések: 12 Csatlakozás dátuma: 2010.08.17. Legújabb bejegyzések
Thanks for that, it gave us a handy idea. I implemented the same code within a component for which we've defined a bespoke renderer, on the theory that it will be called within the Faces lifecycle following the PortableRenderer.render() call. Using this method we find that FacesContext.getCurrentInstance() does not return null when called from within the renderer AND JavascriptContext.addJavascriptCall works! The only issue I have within the renderer is that LiferayFacesContext.getPortletInstanceId() still throws the NullPointerException as mentioned in previous posts, although we can get around this issue quite easily.

I'm a bit worried about your statement that the non-JSF-thread should not be changing JSF model managed-bean values. I see little use in using the PortableRenderer if one cannot alter values in the managed bean and propagate them to the user, maybe I've misunderstood.


public final class FTSVisibilityRenderer extends DomBasicRenderer
{
    @Override
    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
    {
        ValueExpression valueExpression = uiComponent.getValueExpression("visibility");
        FTSLiferayPortlet liferayPortlet = (FTSLiferayPortlet) valueExpression.getValue(facesContext.getELContext());
        if (liferayPortlet == null)
            LOG.warn("Unable to determine visibility from value expressions, defaulting to true.");

        Boolean isVisible = ( liferayPortlet != null ? liferayPortlet.isPortletVisible() : true );

        String portletId = "";
        String javascript = "";
        try {
            //this returns a not null object
            System.err.println("faces context get current instance: " + FacesContext.getCurrentInstance());
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        try {
            LiferayFacesContext lfc = LiferayFacesContext.getInstance();
            //this throws a NullPointerException
            portletId = "p_p_id_" + lfc.getPortletInstanceId() + "_";
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        try {
            javascript = MessageFormat.format(FADEOUT, new Object[] {portletId});
            if (isVisible)
                javascript = MessageFormat.format(FADEIN, new Object[] {portletId});

            //this works
            JavascriptContext.addJavascriptCall(facesContext, javascript);

        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

    private static final String FADEOUT = "jQuery(''#{0}'').fadeOut(''fast'');";
    private static final String FADEIN  = "jQuery(''#{0}'').fadeIn(''fast'');";

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

thumbnail
Neil Griffin, módosítva 11 év-val korábban

RE: LiferayFacesContext.getPortletInstanceId() throws null pointer

Liferay Legend Bejegyzések: 2655 Csatlakozás dátuma: 2005.07.27. Legújabb bejegyzések
Hi Jon,

I'm glad it's working for you now. emoticon

Regarding what you said here:
Jon Gilmore:
I'm a bit worried about your statement that the non-JSF-thread should not be changing JSF model managed-bean values. I see little use in using the PortableRenderer if one cannot alter values in the managed bean and propagate them to the user, maybe I've misunderstood.


Let me clarify -- What I meant was that, since the non-JSF-thread doesn't have the ability to do JSF things like resolve JSF managed-bean instances, it is not possible to update any bean values from your non-JSF-thread.

However, there are two things I can think of that you can do:

1. If your managed-beans are @SessionScoped, then you can call PortletRequest.getPortletSession() in order to get the session scope. You can then call PortletSession.getAttribute(String name) from your non-JSF-thread in order to find the managed-bean instance.

2. If your managed-beans are @RequestScoped or @ViewScoped, then you can inject a @SessionScoped (see #1) managed-bean into it with a @ManagedProperty annotation.

3. You can use Spring to define some spring-beans that contain data. You can then have your non-JSF-thread get the spring-bean instance from the Spring ApplicationContext and when it comes time for your JSF managed-beans values to get called during the subsequent JSF lifecycle, the JSF managed-beans could then get values from the spring-beans. I used this in a project once and it worked great. Or you could just use Spring to manage all your beans. That would work too for everything except @ViewScoped.