Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PrimeFaces-served icons CSS fails to load after changing design #436

Closed
jesse-gallagher opened this issue Jun 27, 2023 · 7 comments
Closed
Labels
bug Something isn't working jsf
Milestone

Comments

@jesse-gallagher
Copy link
Member

If the NSF is left alone, this generally works (as in the riekpil.de example), but opening the NSF or making a change that initializes a full app reload (e.g. re-saving faces-config.xml or a Java file) makes the resource fail until a server restart or, presumably, the app expires.

Screenshot 2023-06-27 at 9 36 42 AM

There's no error message in the response or on the console, though there's a logged message (without a trace) about trying to get a writer after already getting an output stream for the request - that implies that it could be squelching some other problem.

The fact that it only happens on a "full" app refresh makes me wonder if there's some aspect of it sticking around that causes trouble, like caches resources or the like. It'll be worth investigating whether the ServletContext and related elements really come in clean after a refresh.

@jesse-gallagher jesse-gallagher added bug Something isn't working jsf labels Jun 27, 2023
@jesse-gallagher
Copy link
Member Author

jesse-gallagher commented Jun 27, 2023

It looks like this specifically happens when you have jakarta.faces.PROJECT_STAGE=Development, though I'm not even sure why that matters - from what I can tell, the Servlet and the special ClassLoader it uses should be discarded entirely on design change anyway, specified stage or not.

@jesse-gallagher
Copy link
Member Author

In some initial investigation, it seems like the problem may be down to the way classes are loaded from the NSF. Currently, it's done via module.getModuleClassLoader().loadClass(className), but switching it to Class.forName(className, true, module.getClassLoader()) appears to avoid the trouble.

@jesse-gallagher
Copy link
Member Author

Turns out that wasn't the problem, but it's good to fix anyway.

The actual exception is currently being squelched by the exception handler in NsfJsfServlet, which doesn't log the exception anywhere when it can't get a Writer (which is where the only log line is coming from). Changing the exception handler to log the full trace reveals:

java.lang.ClassCastException: org.primefaces.context.PrimeRequestContext incompatible with org.primefaces.context.PrimeRequestContext
	at org.primefaces.context.PrimeRequestContext.getCurrentInstance(PrimeRequestContext.java:83)
	at org.primefaces.application.resource.PrimeResource.<init>(PrimeResource.java:46)
	at org.primefaces.application.resource.PrimeResourceHandler.wrapResource(PrimeResourceHandler.java:106)
	at org.primefaces.application.resource.PrimeResourceHandler.createResource(PrimeResourceHandler.java:72)
	at org.apache.myfaces.el.unified.resolver.ResourceResolver.getValue(ResourceResolver.java:141)
	at jakarta.el.CompositeELResolver.getValue(CompositeELResolver.java:136)
	at com.sun.el.parser.AstValue.getValue(AstValue.java:114)
	at com.sun.el.parser.AstValue.getValue(AstValue.java:177)
	at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:183)
	at org.apache.myfaces.shared.resource.ValueExpressionFilterInputStream.read(ValueExpressionFilterInputStream.java:128)
	at java.base/java.io.InputStream.read(InputStream.java:293)
	at java.base/java.io.InputStream.read(InputStream.java:218)
	at org.apache.myfaces.application.ResourceHandlerImpl.pipeBytes(ResourceHandlerImpl.java:701)
	at org.apache.myfaces.application.ResourceHandlerImpl.handleResourceRequest(ResourceHandlerImpl.java:638)
	at jakarta.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:72)
	at org.primefaces.application.resource.PrimeResourceHandler.handleResourceRequest(PrimeResourceHandler.java:87)
	at jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:196)
	at org.openntf.xsp.jsf.nsf.NSFJsfServlet.lambda$0(NSFJsfServlet.java:178)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:748)
	at org.openntf.xsp.jsf.nsf.NSFJsfServlet.service(NSFJsfServlet.java:155)
	(snip)

@jesse-gallagher
Copy link
Member Author

My general theories have revolved around needing to close out older ClassLoaders at the right time, though that hasn't borne fruit. Still, it seems plausible: the Servlet#destroy method in NSFJsfServlet isn't called when modifying a force-reload resource and going to the page again, and so lingering bits remain. On the other hand, keeping a cache of CLs based on DB path and then closing an old one out didn't fix the trouble.

In general, the problem could come from the fact that PrimeFaces uses its own FacesContextFactory, and this lingers around after the refresh. However, my initial test in that direction wasn't fruitful either: in the same check where I look for an orphaned ClassLoader, I also tried calling FactoryFinder.releaseFactories(), but the problem remained.

@jesse-gallagher
Copy link
Member Author

Similarly, calling FactoryFinder.releaseFactories() after each request (so it's in the same ClassLoader) doesn't change the problem either.

@jesse-gallagher
Copy link
Member Author

This is weirdly fiddly. I've had no luck so far tracking down what might remain between discarded ClassLoaders. Looking at the FacesServlet and the objects it gets from the factory, they're all new instances, including the Prime ones.

I also don't see anything stashed in the ServletContext (which is presumably reset) or in the HttpSession.

@jesse-gallagher
Copy link
Member Author

Found the trouble: the CDI container isn't re-initialized with a Faces request post-refresh. Faces uses CDI internally to provide these objects, so they're being cached across requests within Weld.

The root of this trouble is that CDI doesn't currently have a mechanism for invalidating containers outside of WeldApplicationListener, which won't be called because JSF doesn't participate in the XPages lifecycle.

@jesse-gallagher jesse-gallagher added this to the 2.13.0 milestone Jun 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working jsf
Projects
None yet
Development

No branches or pull requests

1 participant