diff --git a/src/main/java/org/java_websocket/AbstractWebSocket.java b/src/main/java/org/java_websocket/AbstractWebSocket.java index 4a2a0f6cb..d9f75fdba 100644 --- a/src/main/java/org/java_websocket/AbstractWebSocket.java +++ b/src/main/java/org/java_websocket/AbstractWebSocket.java @@ -128,7 +128,7 @@ protected void startConnectionLostTimer() { */ private void restartConnectionLostTimer() { cancelConnectionLostTimer(); - connectionLostTimer = new Timer(); + connectionLostTimer = new Timer("WebSocketTimer"); connectionLostTimerTask = new TimerTask() { /** @@ -138,7 +138,7 @@ private void restartConnectionLostTimer() { @Override public void run() { connections.clear(); - connections.addAll( connections() ); + connections.addAll( getConnections() ); long current = (System.currentTimeMillis()-(connectionLostTimeout * 1500)); WebSocketImpl webSocketImpl; for( WebSocket conn : connections ) { @@ -169,7 +169,7 @@ public void run() { * @return the currently available connections * @since 1.3.4 */ - protected abstract Collection connections(); + protected abstract Collection getConnections(); /** * Cancel any running timer for the connection lost detection diff --git a/src/main/java/org/java_websocket/client/WebSocketClient.java b/src/main/java/org/java_websocket/client/WebSocketClient.java index be1829ec5..fc412ebc2 100644 --- a/src/main/java/org/java_websocket/client/WebSocketClient.java +++ b/src/main/java/org/java_websocket/client/WebSocketClient.java @@ -266,6 +266,7 @@ public void connect() { if( writeThread != null ) throw new IllegalStateException( "WebSocketClient objects are not reuseable" ); writeThread = new Thread( this ); + writeThread.setName( "WebSocketWriteThread-" + writeThread.getId() ); writeThread.start(); } @@ -329,7 +330,7 @@ public void setAttachment(T attachment) { } @Override - protected Collection connections() { + protected Collection getConnections() { return Collections.singletonList((WebSocket ) engine ); } @@ -621,7 +622,7 @@ public void onFragment( Framedata frame ) { private class WebsocketWriteThread implements Runnable { @Override public void run() { - Thread.currentThread().setName( "WebsocketWriteThread" ); + Thread.currentThread().setName( "WebSocketWriteThread-" + Thread.currentThread().getId() ); try { try { while( !Thread.interrupted() ) { diff --git a/src/main/java/org/java_websocket/server/WebSocketServer.java b/src/main/java/org/java_websocket/server/WebSocketServer.java index 95f547996..6402e7a06 100644 --- a/src/main/java/org/java_websocket/server/WebSocketServer.java +++ b/src/main/java/org/java_websocket/server/WebSocketServer.java @@ -264,14 +264,29 @@ public void stop() throws IOException , InterruptedException { } /** + * PLEASE use the method getConnections() in the future! + * * Returns a WebSocket[] of currently connected clients. * Its iterators will be failfast and its not judicious * to modify it. * * @return The currently connected clients. + * */ + @Deprecated public Collection connections() { - return this.connections; + return getConnections(); + } + + /** + * Returns all currently connected clients. + * This collection does not allow any modification e.g. removing a client. + * + * @return A unmodifiable collection of all currently connected clients + * @since 1.3.8 + */ + public Collection getConnections() { + return Collections.unmodifiableCollection( new ArrayList(connections) ); } public InetSocketAddress getAddress() { @@ -305,7 +320,7 @@ public void run() { return; } } - selectorthread.setName( "WebsocketSelector" + selectorthread.getId() ); + selectorthread.setName( "WebSocketSelector-" + selectorthread.getId() ); try { server = ServerSocketChannel.open(); server.configureBlocking( false ); diff --git a/src/test/java/org/java_websocket/issues/AllIssueTests.java b/src/test/java/org/java_websocket/issues/AllIssueTests.java index 70951f1a8..1a80e284d 100644 --- a/src/test/java/org/java_websocket/issues/AllIssueTests.java +++ b/src/test/java/org/java_websocket/issues/AllIssueTests.java @@ -33,7 +33,9 @@ org.java_websocket.issues.Issue609Test.class, org.java_websocket.issues.Issue621Test.class, org.java_websocket.issues.Issue580Test.class, - org.java_websocket.issues.Issue256Test.class + org.java_websocket.issues.Issue256Test.class, + org.java_websocket.issues.Issue661Test.class, + org.java_websocket.issues.Issue666Test.class }) /** * Start all tests for issues diff --git a/src/test/java/org/java_websocket/issues/Issue666Test.java b/src/test/java/org/java_websocket/issues/Issue666Test.java new file mode 100644 index 000000000..fa1f9e179 --- /dev/null +++ b/src/test/java/org/java_websocket/issues/Issue666Test.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2010-2018 Nathan Rajlich + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.java_websocket.issues; + +import org.java_websocket.WebSocket; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.handshake.ServerHandshake; +import org.java_websocket.server.WebSocketServer; +import org.java_websocket.util.SocketUtil; +import org.java_websocket.util.ThreadCheck; +import org.junit.Assert; +import org.junit.Test; + +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +public class Issue666Test { + private CountDownLatch countServerDownLatch = new CountDownLatch( 1 ); + + @Test + public void testServer() throws Exception { + Map mapBefore = ThreadCheck.getThreadMap(); + int port = SocketUtil.getAvailablePort(); + WebSocketServer server = new WebSocketServer( new InetSocketAddress( port ) ) { + @Override + public void onOpen( WebSocket conn, ClientHandshake handshake ) { + } + + @Override + public void onClose( WebSocket conn, int code, String reason, boolean remote ) { + + } + + @Override + public void onMessage( WebSocket conn, String message ) { + + } + + @Override + public void onError( WebSocket conn, Exception ex ) { + + } + + @Override + public void onStart() { + countServerDownLatch.countDown(); + } + }; + server.start(); + countServerDownLatch.await(); + Map mapAfter = ThreadCheck.getThreadMap(); + for( long key : mapBefore.keySet() ) { + mapAfter.remove( key ); + } + for( Thread thread : mapAfter.values() ) { + String name = thread.getName(); + if( !name.startsWith( "WebSocketSelector-" ) && !name.startsWith( "WebSocketWorker-" ) && !name.equals( "WebSocketTimer" ) ) { + Assert.fail( "Thread not correctly named! Is: " + name ); + } + } + server.stop(); + } + + @Test + public void testClient() throws Exception { + int port = SocketUtil.getAvailablePort(); + WebSocketClient client = new WebSocketClient( new URI( "ws://localhost:" + port ) ) { + @Override + public void onOpen( ServerHandshake handshakedata ) { + + } + + @Override + public void onMessage( String message ) { + + } + + @Override + public void onClose( int code, String reason, boolean remote ) { + } + + @Override + public void onError( Exception ex ) { + + } + }; + WebSocketServer server = new WebSocketServer( new InetSocketAddress( port ) ) { + @Override + public void onOpen( WebSocket conn, ClientHandshake handshake ) { + } + + @Override + public void onClose( WebSocket conn, int code, String reason, boolean remote ) { + + } + + @Override + public void onMessage( WebSocket conn, String message ) { + + } + + @Override + public void onError( WebSocket conn, Exception ex ) { + + } + + @Override + public void onStart() { + countServerDownLatch.countDown(); + } + }; + server.start(); + countServerDownLatch.await(); + Map mapBefore = ThreadCheck.getThreadMap(); + client.connectBlocking(); + Map mapAfter = ThreadCheck.getThreadMap(); + for( long key : mapBefore.keySet() ) { + mapAfter.remove( key ); + } + for( Thread thread : mapAfter.values() ) { + String name = thread.getName(); + if( !name.equals( "WebSocketTimer" ) && !name.startsWith( "WebSocketWriteThread-" ) ) { + Assert.fail( "Thread not correctly named! Is: " + name ); + } + } + client.close(); + server.stop(); + } +} diff --git a/src/test/java/org/java_websocket/util/ThreadCheck.java b/src/test/java/org/java_websocket/util/ThreadCheck.java index 208d1c83a..fbebdde0c 100644 --- a/src/test/java/org/java_websocket/util/ThreadCheck.java +++ b/src/test/java/org/java_websocket/util/ThreadCheck.java @@ -71,7 +71,7 @@ private boolean checkZombies( boolean testOnly ) { return zombies > 0; } - private Map getThreadMap() { + public static Map getThreadMap() { Map map = new HashMap(); Thread[] threads = new Thread[ Thread.activeCount() * 2 ]; int actualNb = Thread.enumerate( threads );