diff --git a/.travis.yml b/.travis.yml index c9e3e7d9..67378f68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,8 @@ language: java jdk: - oraclejdk8 - - oraclejdk13 + - oraclejdk11 + - openjdk17 cache: directories: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..ebb47e1f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,87 @@ +[//]: # " Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. " +[//]: # " " +[//]: # " This program and the accompanying materials are made available under the " +[//]: # " terms of the Eclipse Public License v. 2.0, which is available at " +[//]: # " http://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: GNU General Public License, " +[//]: # " version 2 with the GNU Classpath Exception, which is available at " +[//]: # " https://www.gnu.org/software/classpath/license.html. " +[//]: # " " +[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 " + +# Contributing to Eclipse Tyrus + +Thanks for your interest in this project. + +## Project description + +Eclipse Tyrus is the open source +JSR 356 - Java API for WebSocket +reference implementation for easy development of WebSocket applications. Eclipse Tyrus is also +a Jakarta WebSocket 2.0 compatible implementation. + +WebSocket protocol defined by IETF +provides bi-directional communication between the server and the remote host. The +pros are mainly the ability to communicate both ways, low latency and small +communication overhead. Therefore Tyrus and WebSocket in general are suitable for web +applications that require sending a huge volume of relatively small messages like +online games or market ticker broadcasting. + +Goals of Eclipse Tyrus project can be summarized in the following points: +* Track the Eclipse WebSocket API and provide regular releases of production quality implementations that ships with GlassFish; + +* Provide APIs to extend Tyrus & Build a community of users and developers; and finally + +* Make it easy to build websocket based web services utilising Java and the Java Virtual Machine. + +## Developer resources + +Information regarding source code management, builds, coding standards, and +more. + +* https://projects.eclipse.org/projects/ee4j.tyrus/developer + +The project maintains the following source code repositories + +* https://github.com/eclipse-ee4j/tyrus +* https://github.com/eclipse-ee4j/tyrus-project.github.io + +## Eclipse Development Process + +This Eclipse Foundation open project is governed by the Eclipse Foundation +Development Process and operates under the terms of the Eclipse IP Policy. + +## Specifications + +This specification project operates under the terms of the Eclipse Foundation +Specification process. + +* https://eclipse.org/projects/dev_process +* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf +* https://www.eclipse.org/projects/efsp/ +* https://www.eclipse.org/legal/efsp_non_assert.php + +## Eclipse Contributor Agreement + +Before your contribution can be accepted by the project team contributors must +electronically sign the Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +Commits that are provided by non-committers must have a Signed-off-by field in +the footer indicating that the author is aware of the terms by which the +contribution has been provided to the project. The non-committer must +additionally have an Eclipse Foundation account and must have a signed Eclipse +Contributor Agreement (ECA) on file. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Contact + +Contact the project developers via the project's "dev" list. + +* https://dev.eclipse.org/mailman/listinfo/tyrus-dev diff --git a/README.md b/README.md index f3a95f11..56d10a04 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,25 @@ [![Build Status](https://travis-ci.org/eclipse-ee4j/tyrus.svg?branch=master)](https://travis-ci.org/eclipse-ee4j/tyrus) -# Tyrus +# Eclipse Tyrus -Tyrus is the open source +Eclipse Tyrus is the open source JSR 356 - Java API for WebSocket reference implementation -for easy development of WebSocket applications. WebSocket protocol defined by IETF +for easy development of WebSocket applications.Eclipse Tyrus is also +a Jakarta WebSocket 2.0 compatible implementation. + + WebSocket protocol defined by IETF provides bi-directional communication between the server and the remote host. The pros are mainly the ability to communicate both ways, low latency and small communication overhead. Therefore Tyrus and WebSocket in general are suitable for web applications that require sending a huge volume of relatively small messages like online games or market ticker broadcasting. +## Building Eclipse Tyrus + +Building Tyrus can be done using `mvn clean install`, but sometimes (such as for building 2.x from a tag) +`mvn clean install -Pstaging` would be required. + ## Licensing - [Eclipse Public License 2.0](https://projects.eclipse.org/license/epl-2.0) diff --git a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/ClientFilter.java b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/ClientFilter.java index 430dac27..2f75252d 100644 --- a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/ClientFilter.java +++ b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/ClientFilter.java @@ -210,6 +210,9 @@ public void close(CloseReason reason) { @Override public void processConnectionClosed() { LOGGER.log(Level.FINE, "Connection has been closed by the server"); + if (connectCompletionHandler != null) { + connectCompletionHandler.failed(new IOException("Connection closed by server")); + } if (wsConnection == null) { return; diff --git a/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletContainerInitializer.java b/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletContainerInitializer.java index 4638d4cf..5e4ab080 100755 --- a/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletContainerInitializer.java +++ b/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletContainerInitializer.java @@ -83,34 +83,9 @@ public void onStartup(Set> classes, final ServletContext ctx) throws Se DebugContext.TracingThreshold.TRACE); final ApplicationEventListener applicationEventListener = createApplicationEventListener(ctx); - final TyrusServerContainer serverContainer = new TyrusServerContainer(classes) { - - private final WebSocketEngine engine = - TyrusWebSocketEngine.builder(this) - .applicationEventListener(applicationEventListener) - .incomingBufferSize(incomingBufferSize) - .maxSessionsPerApp(maxSessionsPerApp) - .maxSessionsPerRemoteAddr(maxSessionsPerRemoteAddr) - .parallelBroadcastEnabled(parallelBroadcastEnabled) - .tracingType(tracingType) - .tracingThreshold(tracingThreshold) - .build(); - - @Override - public void register(Class endpointClass) throws DeploymentException { - engine.register(endpointClass, ctx.getContextPath()); - } - - @Override - public void register(ServerEndpointConfig serverEndpointConfig) throws DeploymentException { - engine.register(serverEndpointConfig, ctx.getContextPath()); - } - - @Override - public WebSocketEngine getWebSocketEngine() { - return engine; - } - }; + final TyrusServerContainer serverContainer = new TyrusServerContainerImpl(classes, applicationEventListener, + incomingBufferSize, maxSessionsPerApp, maxSessionsPerRemoteAddr, parallelBroadcastEnabled, tracingType, + tracingThreshold, ctx.getContextPath()); ctx.setAttribute(ServerContainer.class.getName(), serverContainer); Boolean wsadlEnabled = getBooleanContextParam(ctx, TyrusWebSocketEngine.WSADL_SUPPORT); if (wsadlEnabled == null) { @@ -224,4 +199,55 @@ private ApplicationEventListener createApplicationEventListener(final ServletCon } return null; } + + private static class TyrusServerContainerImpl extends TyrusServerContainer { + private final ApplicationEventListener applicationEventListener; + private final Integer incomingBufferSize; + private final Integer maxSessionsPerApp; + private final Integer maxSessionsPerRemoteAddr; + private final Boolean parallelBroadcastEnabled; + private final DebugContext.TracingType tracingType; + private final DebugContext.TracingThreshold tracingThreshold; + private final String contextPath; + private final WebSocketEngine engine; + + public TyrusServerContainerImpl(Set> set, ApplicationEventListener applicationEventListener, + Integer incomingBufferSize, Integer maxSessionsPerApp, Integer maxSessionsPerRemoteAddr, + Boolean parallelBroadcastEnabled, DebugContext.TracingType tracingType, + DebugContext.TracingThreshold tracingThreshold, String contextPath) { + super(set); + this.applicationEventListener = applicationEventListener; + this.incomingBufferSize = incomingBufferSize; + this.maxSessionsPerApp = maxSessionsPerApp; + this.maxSessionsPerRemoteAddr = maxSessionsPerRemoteAddr; + this.parallelBroadcastEnabled = parallelBroadcastEnabled; + this.tracingType = tracingType; + this.tracingThreshold = tracingThreshold; + this.contextPath = contextPath; + this.engine = TyrusWebSocketEngine.builder(this) + .applicationEventListener(applicationEventListener) + .incomingBufferSize(incomingBufferSize) + .maxSessionsPerApp(maxSessionsPerApp) + .maxSessionsPerRemoteAddr(maxSessionsPerRemoteAddr) + .parallelBroadcastEnabled(parallelBroadcastEnabled) + .tracingType(tracingType) + .tracingThreshold(tracingThreshold) + .build(); + } + + @Override + public void register(Class endpointClass) throws DeploymentException { + engine.register(endpointClass, contextPath); + } + + @Override + public void register(ServerEndpointConfig serverEndpointConfig) throws DeploymentException { + engine.register(serverEndpointConfig, contextPath); + } + + @Override + public WebSocketEngine getWebSocketEngine() { + return engine; + } + } } diff --git a/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletFilter.java b/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletFilter.java index 755dfe5b..ad0cf57a 100644 --- a/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletFilter.java +++ b/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusServletFilter.java @@ -65,7 +65,7 @@ class TyrusServletFilter implements Filter, HttpSessionListener { private static final Logger LOGGER = Logger.getLogger(TyrusServletFilter.class.getName()); - private final TyrusWebSocketEngine engine; + private TyrusWebSocketEngine engine; private final boolean wsadlEnabled; // I don't like this map, but it seems like it is necessary. I am forced to handle subscriptions @@ -303,5 +303,7 @@ private synchronized JAXBContext getWsadlJaxbContext() throws JAXBException { public void destroy() { serverContainer.stop(); engine.getApplicationEventListener().onApplicationDestroyed(); + serverContainer = null; + engine = null; } } diff --git a/core/src/main/java/org/glassfish/tyrus/core/MessageHandlerManager.java b/core/src/main/java/org/glassfish/tyrus/core/MessageHandlerManager.java index 912c78cf..3a4cb40c 100644 --- a/core/src/main/java/org/glassfish/tyrus/core/MessageHandlerManager.java +++ b/core/src/main/java/org/glassfish/tyrus/core/MessageHandlerManager.java @@ -301,6 +301,10 @@ public void removeMessageHandler(MessageHandler handler) { } } + Map, MessageHandler> getRegisteredHandlers() { + return new HashMap<>(registeredHandlers); + } + /** * Get all successfully registered {@link MessageHandler}s. * @@ -308,14 +312,14 @@ public void removeMessageHandler(MessageHandler handler) { */ public Set getMessageHandlers() { if (messageHandlerCache == null) { - messageHandlerCache = Collections.unmodifiableSet(new HashSet(registeredHandlers.values())); + messageHandlerCache = Collections.unmodifiableSet(new HashSet<>(registeredHandlers.values())); } return messageHandlerCache; } public List, MessageHandler>> getOrderedWholeMessageHandlers() { - List, MessageHandler>> result = new ArrayList, MessageHandler>>(); + List, MessageHandler>> result = new ArrayList<>(); for (final Map.Entry, MessageHandler> entry : registeredHandlers.entrySet()) { if (entry.getValue() instanceof MessageHandler.Whole) { result.add(entry); @@ -325,7 +329,7 @@ public List, MessageHandler>> getOrderedWholeMessageHandlers( return result; } - static Class getHandlerType(MessageHandler handler) { + private static Class getHandlerType(MessageHandler handler) { Class root; if (handler instanceof AsyncMessageHandler) { return ((AsyncMessageHandler) handler).getType(); diff --git a/core/src/main/java/org/glassfish/tyrus/core/TyrusSession.java b/core/src/main/java/org/glassfish/tyrus/core/TyrusSession.java index 739c18b4..48c790b4 100755 --- a/core/src/main/java/org/glassfish/tyrus/core/TyrusSession.java +++ b/core/src/main/java/org/glassfish/tyrus/core/TyrusSession.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.Map.Entry; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -61,7 +62,7 @@ * @author Martin Matula (martin.matula at oracle.com) * @author Pavel Bucek (pavel.bucek at oracle.com) */ -public class TyrusSession implements Session, DistributedSession { +public class TyrusSession implements DistributedSession { private static final Logger LOGGER = Logger.getLogger(TyrusSession.class.getName()); @@ -587,9 +588,10 @@ MessageHandler.Whole getMessageHandler(Class c) { void notifyMessageHandlers(Object message, boolean last) { boolean handled = false; - for (MessageHandler handler : getMessageHandlers()) { + for (Entry, MessageHandler> e : this.handlerManager.getRegisteredHandlers().entrySet()) { + MessageHandler handler = e.getValue(); if ((handler instanceof MessageHandler.Partial) - && MessageHandlerManager.getHandlerType(handler).isAssignableFrom(message.getClass())) { + && e.getKey().isAssignableFrom(message.getClass())) { if (handler instanceof AsyncMessageHandler) { checkMessageSize(message, ((AsyncMessageHandler) handler).getMaxMessageSize()); @@ -615,11 +617,9 @@ void notifyMessageHandlers(Object message, boolean last) { } void notifyPongHandler(PongMessage pongMessage) { - final Set messageHandlers = getMessageHandlers(); - for (MessageHandler handler : messageHandlers) { - if (MessageHandlerManager.getHandlerType(handler).equals(PongMessage.class)) { - ((MessageHandler.Whole) handler).onMessage(pongMessage); - } + final MessageHandler.Whole handler = getMessageHandler(PongMessage.class); + if (handler != null) { + handler.onMessage(pongMessage); } } diff --git a/core/src/test/java/org/glassfish/tyrus/core/TyrusSessionTest.java b/core/src/test/java/org/glassfish/tyrus/core/TyrusSessionTest.java index f479809d..e9dba619 100644 --- a/core/src/test/java/org/glassfish/tyrus/core/TyrusSessionTest.java +++ b/core/src/test/java/org/glassfish/tyrus/core/TyrusSessionTest.java @@ -294,7 +294,6 @@ public void onMessage(InputStream message, boolean last) { public void multiplePongHandlersAsync() { Session session = createSession(endpointWrapper); - session.addMessageHandler(new MessageHandler.Partial() { @Override public void onMessage(PongMessage message, boolean last) { @@ -312,7 +311,6 @@ public void onMessage(PongMessage message, boolean last) { public void multipleBasicDecodableAsync() { Session session = createSession(endpointWrapper); - session.addMessageHandler(new MessageHandler.Partial() { @Override public void onMessage(TyrusSessionTest message, boolean last) { @@ -359,7 +357,6 @@ public void onMessage(PongMessage message) { public void removeHandlers() { Session session = createSession(endpointWrapper); - final MessageHandler.Partial handler1 = new MessageHandler.Partial() { @Override public void onMessage(String message, boolean last) { @@ -408,6 +405,44 @@ public void idTest() { assertFalse(session2.getId().equals(session3.getId())); } + @Test + public void getLambdaHandlers() { + Session session = createSession(endpointWrapper); + + final MessageHandler.Partial handler1 = this::stringPartialHandler; + final MessageHandler.Whole handler2 = this::bytesHandler; + final MessageHandler.Whole handler3 = this::pongHandler; + + session.addMessageHandler(String.class, handler1); + session.addMessageHandler(ByteBuffer.class, handler2); + session.addMessageHandler(PongMessage.class, handler3); + + assertTrue(session.getMessageHandlers().contains(handler1)); + assertTrue(session.getMessageHandlers().contains(handler2)); + assertTrue(session.getMessageHandlers().contains(handler3)); + + session.removeMessageHandler(handler3); + + assertTrue(session.getMessageHandlers().contains(handler1)); + assertTrue(session.getMessageHandlers().contains(handler2)); + assertFalse(session.getMessageHandlers().contains(handler3)); + + session.removeMessageHandler(handler2); + + assertTrue(session.getMessageHandlers().contains(handler1)); + assertFalse(session.getMessageHandlers().contains(handler2)); + assertFalse(session.getMessageHandlers().contains(handler3)); + } + + private void stringPartialHandler(String message, boolean last) { + } + + private void bytesHandler(ByteBuffer message) { + } + + private void pongHandler(PongMessage message) { + } + @ServerEndpoint(value = "/echo") private static class EchoEndpoint extends Endpoint { diff --git a/docs/pom.xml b/docs/pom.xml index 06cffb03..bb89aeff 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -1,7 +1,7 @@