onzinsky
onzinsky

Reputation: 591

Websocket + Pretty Faces = AbstractMethodError

I'm building a simple JSF 2.3 app in tomcat 8.5. I got the websocket working, and then I added Pretty Faces.

If I use them separately, they both work. I mean, if I remove Pretty Faces' dependencies from the pom, my websocket works. And if I keep the dependencies, and remove the <f:websocket> from my view, Pretty Faces work (redirects ok)!

But, if I try to use Pretty Faces, while I have a <f:websocket> in a view, AbstractMethodError appears when I navigate to it. Literally, if I comment the tag, everything else works.

I use mojarra. In my pom, I'm using the dependecies from https://github.com/javaserverfaces/mojarra/blob/master/README.md, and the default ones for Pretty Faces.

I'm guessing the problem is either that I have conflicting dependencies, from what I read in this BalusC answer, or that Pretty Faces and Websocket conflict somehow.

Any help would be appreciated. Thanks in advance.

Here the dependencies

<!-- Java EE containers -->
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>8.0</version>
    <scope>provided</scope>
</dependency>

<!-- Servlet Containers -->
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.faces</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet-shaded</artifactId>
    <version>3.0.0.Final</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency> <!-- Optional, only when <f:websocket> is used. -->
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.1</version>
</dependency>

<!-- Pretty Faces -->
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-servlet</artifactId>
    <version>3.4.2.Final</version>
</dependency>
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-config-prettyfaces</artifactId>
    <version>3.4.2.Final</version>
</dependency>

Here the exception

Nov 06, 2019 1:49:39 AM com.sun.faces.context.ExceptionHandlerImpl log
FATAL: JSF1073: java.lang.AbstractMethodError caught during processing of RENDER_RESPONSE 6 : UIComponent-ClientId=, Message=null
Nov 06, 2019 1:49:39 AM com.sun.faces.context.ExceptionHandlerImpl log
FATAL: No associated message
java.lang.AbstractMethodError
    at javax.faces.application.ViewHandlerWrapper.getWebsocketURL(ViewHandlerWrapper.java:328)
    at javax.faces.application.ViewHandlerWrapper.getWebsocketURL(ViewHandlerWrapper.java:328)
    at com.sun.faces.push.WebsocketChannelManager.register(WebsocketChannelManager.java:151)
    at com.sun.faces.push.WebsocketChannelManager.register(WebsocketChannelManager.java:142)
    at com.sun.faces.push.WebsocketChannelManager$Proxy$_$$_WeldClientProxy.register(Unknown Source)
    at com.sun.faces.renderkit.html_basic.WebsocketRenderer.encodeEnd(WebsocketRenderer.java:115)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:949)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1912)
    at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:918)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1905)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:491)
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:194)
    at org.ocpsoft.rewrite.faces.RewriteViewHandler.renderView(RewriteViewHandler.java:196)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:151)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:151)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:126)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:223)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:671)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.ocpsoft.rewrite.servlet.RewriteFilter.doFilter(RewriteFilter.java:226)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:470)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:395)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316)
    at org.ocpsoft.rewrite.servlet.impl.HttpRewriteResultHandler.handleResult(HttpRewriteResultHandler.java:42)
    at org.ocpsoft.rewrite.servlet.RewriteFilter.rewrite(RewriteFilter.java:297)
    at org.ocpsoft.rewrite.servlet.RewriteFilter.doFilter(RewriteFilter.java:198)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:660)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:798)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

Upvotes: 0

Views: 378

Answers (2)

Selaron
Selaron

Reputation: 6184

Update:

As per Lincoln's comment the new release PrettyFaces 3.4.3 supportes JSF 2.3. Beside other changes it now makes the RewriteViewHandler extend ViewHandlerWrapper as suggested by BalusC.


Background:

It looks like PrettyFaces 3.4.2 is not fully compatible with JSF 2.3.

The non abstract class org.ocpsoft.rewrite.faces.RewriteViewHandler.RewriteViewHandler extends javax.faces.application.ViewHandler but does not override the abstract method getWebsocketURL.

You can easily reproduce this with pretty faces and JSF 2.3 API on class path:

package my.pkg;

import org.ocpsoft.rewrite.faces.RewriteViewHandler;

public class PrettyFacesTest {
    public static void main(String[] args) {
        new RewriteViewHandler(null).getWebsocketURL(null, null);
    }
}

Result:

Exception in thread "main" java.lang.AbstractMethodError: org.ocpsoft.rewrite.faces.RewriteViewHandler.getWebsocketURL(Ljavax/faces/context/FacesContext;Ljava/lang/String;)Ljava/lang/String;
    at my.pkg.PrettyFacesTest.main(PrettyFacesTest.java:7)

The RewriteViewhandler is registered in the rewrite-integration-faces-3.4.2.Final.jar/META-INF/faces-config.xml:

<application>
    <navigation-handler>org.ocpsoft.rewrite.faces.RewriteNavigationHandler</navigation-handler>
    <view-handler>org.ocpsoft.rewrite.faces.RewriteViewHandler</view-handler>
</application>

I'm not sure if there's chance to prevent this registration. If so you could create a CustomRewriteViewHandler that extends RewriteViewHandler, overrides getWebsocketURL and delegates that call to super.parent:

import javax.faces.application.ViewHandler;
import javax.faces.context.FacesContext;

import org.ocpsoft.rewrite.faces.RewriteViewHandler;

public class CustomRewriteViewHandler extends RewriteViewHandler {
    public CustomRewriteViewHandler(ViewHandler viewHandler) {
        super(viewHandler);
    }

    @Override
    public String getWebsocketURL(FacesContext context, String channel) {
        return super.parent.getWebsocketURL(context, channel);
    }
}

You'd then register this CustomRewriteViewHandler instead of the original one:

<application>
    <view-handler>my.pkg.CustomRewriteViewHandler</view-handler>
</application>

As BalusC describes here you cannot block parts of faces-config.xml from the included jar. Instead you can set <faces-config ... metadata-complete="true"> which results in none faces-config.xml nor annotations in any included jars being processed so coming at the cost that you'd need to redefine all the 3d party stuff needed in your own faces.config.xml.

Upvotes: 4

onzinsky
onzinsky

Reputation: 591

As @Selaron detected, the problem was that PrettyFaces does not support JSF 2.3's Websocket, since the class RewriteViewHandler does not implement the method getWebsocketURL. Here's his solution to the problem.

Once he pointed it out, I forked PrettyFaces (ocpsoft/rewrite) and discovered there was no support for JSF 2.3 at all. So I tried and upgrade to JSF 2.3, which seemed cleaner (at least to me) than @Selaron's answer.

Here's the commit of the thing working. I won't get into the specifics of what I had to change (which are explained in the commit).

One last thing. As I mentioned in my question, the only two dependencies I used to include in my pom.xml for PrettyFaces are the default ones.

But when I build the project in my local environment, I needed to add some more.

Here they are

<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-impl-servlet</artifactId>
    <version>3.4.3-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-config-prettyfaces</artifactId>
    <version>3.4.3-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-api-servlet</artifactId>
    <version>3.4.3-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-annotations-impl</artifactId>
    <version>3.4.3-SNAPSHOT</version>
</dependency>

Cloning/downloading from my branch of ocpsoft/rewrite, installing the project locally, and adding those dependencies into your pom.xml should allow you to make PrettyFaces + JSF 2.3's websocket to work.

PS - I am reticent to mark any of the answers as accepted right now. (I'll give a try to @Selaron's and decide then).

Upvotes: 1

Related Questions