From 4e69b483446f6ce6214c2f0d4ab001b0fd204aff Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 8 Apr 2020 15:31:42 +1000 Subject: [PATCH] Issue #4747 - filter out synthetic annotated methods Signed-off-by: Lachlan Roberts --- .../javax/tests/SyntheticOnMessageTest.java | 112 ++++++++++++++++++ .../jetty/websocket/util/ReflectUtils.java | 21 ++-- 2 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java new file mode 100644 index 00000000000..8130905ee42 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java @@ -0,0 +1,112 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.javax.tests; + +import java.lang.reflect.Method; +import java.net.URI; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.websocket.CloseReason; +import javax.websocket.ContainerProvider; +import javax.websocket.OnMessage; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; +import javax.websocket.server.ServerEndpoint; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SyntheticOnMessageTest +{ + private Server server; + private URI serverUri; + private ServerConnector connector; + private WebSocketContainer client; + + @BeforeEach + public void before() throws Exception + { + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setContextPath("/"); + JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> + container.addEndpoint(ServerSocket.class)); + server.setHandler(contextHandler); + server.start(); + serverUri = URI.create("ws://localhost:" + connector.getLocalPort()); + client = ContainerProvider.getWebSocketContainer(); + } + + @AfterEach + public void after() throws Exception + { + LifeCycle.stop(client); + server.stop(); + } + + public static class AnnotatedEndpoint + { + public void onMessage(T message) + { + } + } + + @ServerEndpoint("/") + public static class ServerSocket extends AnnotatedEndpoint + { + @OnMessage + public void onMessage(String message) + { + } + } + + @Test + public void syntheticOnMessageTest() throws Exception + { + // ServerSocket has two annotated onMessage methods, one is a synthetic bridge method generated + // by the compiler and shouldn't be used. + List annotatedOnMessages = Stream.of(ServerSocket.class.getDeclaredMethods()) + .filter(method -> method.getAnnotation(OnMessage.class) != null) + .collect(Collectors.toList()); + assertThat(annotatedOnMessages.size(), is(2)); + + // We should correctly filter out all synthetic methods so we should not get an InvalidSignatureException. + EventSocket clientSocket = new EventSocket(); + Session session = client.connectToServer(clientSocket, serverUri); + assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS)); + session.close(); + assertTrue(clientSocket.closeLatch.await(5, TimeUnit.SECONDS)); + assertThat(clientSocket.closeReason.getCloseCode(), is(CloseReason.CloseCodes.NORMAL_CLOSURE)); + } +} diff --git a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java index c038b45ead2..bb1a33e6e99 100644 --- a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java +++ b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java @@ -29,6 +29,7 @@ import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Stream; public class ReflectUtils { @@ -220,27 +221,19 @@ public class ReflectUtils public static Method[] findAnnotatedMethods(Class pojo, Class anno) { - List methods = null; Class clazz = pojo; - + List methods = new ArrayList<>(); while ((clazz != null) && Object.class.isAssignableFrom(clazz)) { - for (Method method : clazz.getDeclaredMethods()) - { - if (method.getAnnotation(anno) != null) - { - if (methods == null) - methods = new ArrayList<>(); - methods.add(method); - } - } + Stream.of(clazz.getDeclaredMethods()) + .filter(method -> !method.isSynthetic() && (method.getAnnotation(anno) != null)) + .forEach(methods::add); clazz = clazz.getSuperclass(); } - if (methods == null) + if (methods.isEmpty()) return null; - int len = methods.size(); - return methods.toArray(new Method[len]); + return methods.toArray(new Method[0]); } /**