/******************************************************************************* * Copyright (c) 2016 Google, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Google, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.debug.tests.connectors; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.debug.core.ILaunch; import org.eclipse.jdt.debug.tests.AbstractDebugTest; import org.eclipse.jdt.internal.launching.SocketListenConnector; import org.eclipse.jdt.launching.SocketUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.sun.jdi.connect.Connector; /** * Test the SocketListenerConnector */ public class MultipleConnectionsTest extends AbstractDebugTest { public MultipleConnectionsTest(String name) { super(name); } private ILaunch launch = new MockLaunch(); private SocketListenConnector connector; private int port; @Override @Before protected void setUp() throws Exception { super.setUp(); port = SocketUtil.findFreePort(); } @Test public void testDefaultSettings() throws CoreException { connector = new SocketListenConnector(); Map<String, Connector.Argument> defaults = connector.getDefaultArguments(); assertTrue(defaults.containsKey("connectionLimit")); assertEquals(1, ((Connector.IntegerArgument) defaults.get("connectionLimit")).intValue()); } /** * Ensure out-of-the-box settings mimics previous behaviour of accepting a * single connection * * @throws IOException */ @Test public void testDefaultBehaviour() throws CoreException, InterruptedException { connector = new SocketListenConnector(); Map<String, String> arguments = new HashMap<>(); arguments.put("port", Integer.toString(port)); connector.connect(arguments, new NullProgressMonitor(), launch); Thread.sleep(200); assertTrue("first connect should succeed", connect()); assertFalse("second connect should fail", connect()); } /** * Ensure connector accepts a single connection * * @throws InterruptedException */ @Test public void testSingleConnectionBehaviour() throws CoreException, InterruptedException { connector = new SocketListenConnector(); Map<String, String> arguments = new HashMap<>(); arguments.put("port", Integer.toString(port)); arguments.put("connectionLimit", "1"); connector.connect(arguments, new NullProgressMonitor(), launch); Thread.sleep(200); assertTrue("first connect should succeed", connect()); assertFalse("second connect should fail", connect()); } /** * Ensure out-of-the-box settings mimics previous behaviour of accepting a * single connection * * @throws InterruptedException */ @Test public void testTwoConnectionsBehaviour() throws CoreException, InterruptedException { connector = new SocketListenConnector(); Map<String, String> arguments = new HashMap<>(); arguments.put("port", Integer.toString(port)); arguments.put("connectionLimit", "2"); connector.connect(arguments, new NullProgressMonitor(), launch); Thread.sleep(200); assertTrue("first connect should succeed", connect()); assertTrue("second connect should succeed", connect()); } /** * Ensure out-of-the-box settings mimics previous behaviour of accepting a * single connection * * @throws InterruptedException */ @Test public void testUnlimitedConnectionsBehaviour() throws CoreException, InterruptedException { connector = new SocketListenConnector(); Map<String, String> arguments = new HashMap<>(); arguments.put("port", Integer.toString(port)); arguments.put("connectionLimit", "0"); connector.connect(arguments, new NullProgressMonitor(), launch); Thread.sleep(200); for (int i = 0; i < 10; i++) { assertTrue("connection " + i + " should succeed", connect()); } } @Override @After protected void tearDown() throws Exception { launch.terminate(); super.tearDown(); } private boolean connect() { boolean result = true; // Two try blocks to distinguish between exceptions from socket close (ignorable) // and from dealing with the remote (errors) try (Socket s = new Socket()) { try { s.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)); byte[] buffer = new byte[14]; s.getInputStream().read(buffer); assertEquals("JDWP-Handshake", new String(buffer)); s.getOutputStream().write("JDWP-Handshake".getBytes()); s.getOutputStream().flush(); // Closing gracelessly like this produces // com.sun.jdi.VMDisconnectedExceptions on the log. Could // respond to JDWP to try to bring down the connections // gracefully, but it's a bit involved. } catch (IOException e) { result = false; } } catch(IOException e) { } try { // sleep to allow the remote side to setup the connection Thread.sleep(1000); } catch (InterruptedException ex) { // ignore } return result; } }