/** * 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; } }
/** * {@inheritDoc} * * <p>Adds the specified {@code sessionId} to the per-channel cache for the given channel's * local member sessions, and sends a CHANNEL_JOIN protocol message to the session with the * corresponding {@code sessionId}. */ public void join(String name, byte[] channelId, byte[] sessionId) { callStarted(); try { if (logger.isLoggable(Level.FINEST)) { logger.log( Level.FINEST, "join channelId:{0} sessionId:{1}", HexDumper.toHexString(channelId), HexDumper.toHexString(sessionId)); } // Update local channel membership cache. BigInteger channelRefId = new BigInteger(1, channelId); Set<BigInteger> localMembers = localChannelMembersMap.get(channelRefId); if (localMembers == null) { Set<BigInteger> newLocalMembers = Collections.synchronizedSet(new HashSet<BigInteger>()); localMembers = localChannelMembersMap.putIfAbsent(channelRefId, newLocalMembers); if (localMembers == null) { localMembers = newLocalMembers; } } BigInteger sessionRefId = new BigInteger(1, sessionId); localMembers.add(sessionRefId); // Update per-session channel set cache. Set<BigInteger> channelSet = localPerSessionChannelsMap.get(sessionRefId); if (channelSet == null) { Set<BigInteger> newChannelSet = Collections.synchronizedSet(new HashSet<BigInteger>()); channelSet = localPerSessionChannelsMap.putIfAbsent(sessionRefId, newChannelSet); if (channelSet == null) { channelSet = newChannelSet; } } channelSet.add(channelRefId); // Send CHANNEL_JOIN protocol message. MessageBuffer msg = new MessageBuffer(1 + MessageBuffer.getSize(name) + channelId.length); msg.putByte(SimpleSgsProtocol.CHANNEL_JOIN).putString(name).putBytes(channelId); sessionService.sendProtocolMessageNonTransactional( sessionRefId, ByteBuffer.wrap(msg.getBuffer()).asReadOnlyBuffer(), Delivery.RELIABLE); } finally { callFinished(); } }
/** * {@inheritDoc} * * <p>Reads the local membership list for the specified {@code channelId}, and updates the local * membership cache for that channel. If any join or leave notifications were missed, then send * the appropriate CHANNEL_JOIN or CHANNEL_LEAVE protocol message to the effected session(s). */ public void refresh(String name, byte[] channelId) { callStarted(); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "refreshing channelId:{0}", HexDumper.toHexString(channelId)); } try { BigInteger channelRefId = new BigInteger(1, channelId); GetLocalMembersTask getMembersTask = new GetLocalMembersTask(channelRefId); try { transactionScheduler.runTask(getMembersTask, taskOwner); } catch (Exception e) { // FIXME: what is the right thing to do here? logger.logThrow( Level.WARNING, e, "obtaining members of channel:{0} throws", HexDumper.toHexString(channelId)); } Set<BigInteger> newLocalMembers = Collections.synchronizedSet(getMembersTask.getLocalMembers()); if (logger.isLoggable(Level.FINEST)) { logger.log( Level.FINEST, "newLocalMembers for channel:{0}", HexDumper.toHexString(channelId)); for (BigInteger sessionRefId : newLocalMembers) { logger.log( Level.FINEST, "member:{0}", HexDumper.toHexString(sessionRefId.toByteArray())); } } /* * Determine which join and leave events were missed and * send protocol messages to clients accordingly. */ Set<BigInteger> oldLocalMembers = localChannelMembersMap.put(channelRefId, newLocalMembers); Set<BigInteger> joiners = null; Set<BigInteger> leavers = null; if (oldLocalMembers == null) { joiners = newLocalMembers; } else { for (BigInteger sessionRefId : newLocalMembers) { if (oldLocalMembers.contains(sessionRefId)) { oldLocalMembers.remove(sessionRefId); } else { if (joiners == null) { joiners = new HashSet<BigInteger>(); } joiners.add(sessionRefId); } } if (!oldLocalMembers.isEmpty()) { leavers = oldLocalMembers; } } if (joiners != null) { for (BigInteger sessionRefId : joiners) { MessageBuffer msg = new MessageBuffer(1 + MessageBuffer.getSize(name) + channelId.length); msg.putByte(SimpleSgsProtocol.CHANNEL_JOIN).putString(name).putBytes(channelId); sessionService.sendProtocolMessageNonTransactional( sessionRefId, ByteBuffer.wrap(msg.getBuffer()).asReadOnlyBuffer(), Delivery.RELIABLE); } } if (leavers != null) { for (BigInteger sessionRefId : leavers) { ByteBuffer msg = ByteBuffer.allocate(1 + channelId.length); msg.put(SimpleSgsProtocol.CHANNEL_LEAVE).put(channelId).flip(); sessionService.sendProtocolMessageNonTransactional( sessionRefId, msg.asReadOnlyBuffer(), Delivery.RELIABLE); } } } finally { callFinished(); } }