/** * If this session is not connected, this method returns; otherwise this method sends a * LOGOUT_REQUEST to the server, and waits for this client to receive a LOGOUT_SUCCESS * acknowledgment or the timeout to expire, whichever comes first. If the LOGOUT_SUCCESS * acknowledgment is not received, then {@code AssertionFailedError} is thrown. */ public void logout() { synchronized (lock) { if (connected == false) { return; } logoutAck = false; } MessageBuffer buf = new MessageBuffer(1); buf.putByte(SimpleSgsProtocol.LOGOUT_REQUEST); try { connection.sendBytes(buf.getBuffer()); } catch (IOException e) { throw new RuntimeException(e); } synchronized (lock) { try { if (logoutAck == false) { lock.wait(WAIT_TIME); } if (logoutAck != true) { fail(toString() + " logout timed out"); } } catch (InterruptedException e) { fail(toString() + " logout timed out: " + e.toString()); } finally { if (!logoutAck) disconnect(); } } }
/** * Sends a login request and if {@code waitForLogin} is {@code true} waits for the request to be * acknowledged, returning {@code true} if login was successful, and {@code false} if login was * redirected, otherwise a {@code RuntimeException} is thrown because the login operation timed * out before being acknowledged. * * <p>If {@code waitForLogin} is false, this method returns {@code true} if the login is known to * be successful (the outcome may not yet be known because the login operation is asynchronous), * otherwise it returns false. Invoke {@code waitForLogin} to wait for an expected successful * login. */ public boolean login(boolean waitForLogin) { synchronized (lock) { if (connected == false) { throw new RuntimeException(toString() + " not connected"); } } String password = "******"; MessageBuffer buf = new MessageBuffer(2 + MessageBuffer.getSize(name) + MessageBuffer.getSize(password)); buf.putByte(SimpleSgsProtocol.LOGIN_REQUEST) .putByte(protocolVersion) .putString(name) .putString(password); loginAck = false; try { connection.sendBytes(buf.getBuffer()); } catch (IOException e) { throw new RuntimeException(e); } if (waitForLogin) { if (waitForLogin()) { return true; } else if (isLoginRedirected()) { int port = redirectPort; disconnect(); connect(port); return login(); } } synchronized (lock) { return loginSuccess; } }
/** Sends a SUSPEND_MESSAGES_COMPLETE ack to the server. */ public void sendSuspendMessagesComplete() { checkRelocateProtocolVersion(); synchronized (lock) { ByteBuffer msg = ByteBuffer.allocate(1); msg.put(SimpleSgsProtocol.SUSPEND_MESSAGES_COMPLETE).flip(); try { connection.sendBytes(msg.array()); } catch (IOException e) { throw new RuntimeException(e); } } }
/** * Writes the specified {@code bytes} directly to the underlying connection. If {@code * checkSuspended} is {@code true}, and messages sending is currently suspended, then throw an * {@code IllegalStateException}. */ protected void sendRaw(byte[] bytes, boolean checkSuspended) { synchronized (lock) { if (checkSuspended && suspendMessages) { throw new IllegalStateException("messages suspended"); } try { connection.sendBytes(bytes); } catch (IOException e) { throw new RuntimeException(e); } } }
/** * Relocates this client's connection, if the server has instructed it to do so via a * RELOCATE_NOTIFICATION. * * <p>If this client has not yet received a RELOCATE_NOTIFICATION, it first waits until one is * received or the timeout expires, which ever comes first. If a RELOCATE_NOTIFICATION is not * received or if the specified {@code expectedPort} is non-zero and does not match the relocation * port, then {@code AssertionFailedError} is thrown. * * <p>If a RELOCATE_NOTIFICATION is correctly received, then this method sends a RELOCATE_REQUEST * message to the local host on the relocation port received by a RELOCATE_NOTIFICATION. If {@code * useValidKey} is {@code true}, the current valid relocation key is used in the relocate request, * otherwise an invalid relocation key is used. * * <p>This method waits for an acknowledgment (either RELOCATE_SUCCESS or RELOCATE_FAILURE). If * {@code shouldSucceed} is {@code true} and a RELOCATE_FAILURE is received, this method throws * {@code AssertionFailedError}; similarly if {@code shouldSucceed} is {@code false} and a * RELOCATE_SUCCESS is received, {@code AssertionFailedError} will be thrown. */ public void relocate(int expectedPort, boolean useValidKey, boolean shouldSucceed) { checkRelocateProtocolVersion(); synchronized (lock) { if (!relocateSession) { waitForRelocationNotification(expectedPort); } else { if (expectedPort != 0) { assertEquals(expectedPort, relocatePort); } } } System.err.println(toString() + " relocating..."); disconnect(); relocateAck = false; relocateSuccess = false; suspendMessages = false; connect(relocatePort); byte[] key = useValidKey ? relocateKey : new byte[0]; ByteBuffer buf = ByteBuffer.allocate(2 + key.length); buf.put(SimpleSgsProtocol.RELOCATE_REQUEST).put(SimpleSgsProtocol.VERSION).put(key).flip(); try { connection.sendBytes(buf.array()); } catch (IOException e) { throw new RuntimeException(e); } synchronized (lock) { try { if (!relocateAck) { lock.wait(WAIT_TIME); } if (!relocateAck) { throw new RuntimeException(toString() + " relocate timed out"); } if (shouldSucceed) { if (!relocateSuccess) { fail("relocation failed"); } } else if (relocateSuccess) { fail("relocation succeeded"); } } catch (InterruptedException e) { throw new RuntimeException(toString() + " relocate timed out", e); } relocateSession = false; relocateAck = false; relocatePort = 0; relocateSuccess = false; } }
/** * Disconnects this client, and returns either when the connection is closed or the timeout * expires, which ever comes first. */ public void disconnect() { System.err.println(toString() + " disconnecting"); synchronized (lock) { if (!connected) { return; } try { connection.close(); lock.wait(WAIT_TIME); } catch (Exception e) { System.err.println(toString() + " disconnect exception:" + e); lock.notifyAll(); } finally { if (connected) { reset(); } } } }