<commit_before>package org.realityforge.replicant.server.transport;

import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.websocket.CloseReason;
import javax.websocket.Session;
import org.realityforge.replicant.server.ChangeSet;
import org.realityforge.replicant.server.ChannelAddress;
import org.realityforge.replicant.server.json.JsonEncoder;

public final class ReplicantSession
  implements Serializable, Closeable
{
  @Nonnull
  private final Session _webSocketSession;
  @Nonnull
  private final Map<ChannelAddress, String> _eTags = new HashMap<>();
  @Nonnull
  private final Map<ChannelAddress, SubscriptionEntry> _subscriptions = new HashMap<>();
  @Nullable
  private String _authToken;

  public ReplicantSession( @Nonnull final Session webSocketSession )
  {
    _webSocketSession = Objects.requireNonNull( webSocketSession );
  }

  public void close( @Nonnull final CloseReason closeReason )
  {
    if ( _webSocketSession.isOpen() )
    {
      try
      {
        _webSocketSession.close( closeReason );
      }
      catch ( final IOException ignored )
      {
        // Assume it is already closing
      }
    }
  }

  @Override
  public void close()
  {
    if ( _webSocketSession.isOpen() )
    {
      try
      {
        _webSocketSession.close();
      }
      catch ( final IOException ignored )
      {
        // Assume it is already closing
      }
    }
  }

  /**
   * Send a ping at the network level to ensure the connection is kept alive.
   *
   * This is required to keep connection alove when passing through some load balancers
   * that proxy non-ssl websockets and close the socket after an idle period.
   */
  public void pingTransport()
  {
    if ( _webSocketSession.isOpen() )
    {
      try
      {
        _webSocketSession.getBasicRemote().sendPing( null );
      }
      catch ( final IOException ignored )
      {
        // All scenarios we can envision imply the session is shutting down, and thus can be ignored
      }
    }
  }

  @Nonnull
  public Session getWebSocketSession()
  {
    return _webSocketSession;
  }

  public void setAuthToken( @Nullable final String authToken )
  {
    _authToken = authToken;
  }

  /**
   * @return a token used for authentication, if any.
   */
  @SuppressWarnings( "unused" )
  @Nullable
  public String getAuthToken()
  {
    return _authToken;
  }

  /**
   * @return an opaque ID representing session.
   */
  @Nonnull
  public String getId()
  {
    return getWebSocketSession().getId();
  }

  /**
   * Add packet to queue and potentially send packet to client if client has acked last message.
   *
   * @param requestId the opaque identifier indicating the request that caused the changes if the owning session initiated the changes.
   * @param etag      the opaque identifier identifying the version. May be null if packet is not cache-able
   * @param changeSet the changeSet to create packet from.
   */
  public void sendPacket( @Nullable final Integer requestId,
                          @Nullable final String etag,
                          @Nonnull final ChangeSet changeSet )
  {
    WebSocketUtil.sendText( getWebSocketSession(), JsonEncoder.encodeChangeSet( requestId, etag, changeSet ) );
  }

  @SuppressWarnings( "WeakerAccess" )
  @Nonnull
  public Map<ChannelAddress, String> getETags()
  {
    return Collections.unmodifiableMap( _eTags );
  }

  @SuppressWarnings( "WeakerAccess" )
  @Nullable
  public String getETag( @Nonnull final ChannelAddress address )
  {
    return _eTags.get( address );
  }

  @SuppressWarnings( "WeakerAccess" )<commit_msg>Javadocs cleanup<commit_after>package org.realityforge.replicant.server.transport;

import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.websocket.CloseReason;
import javax.websocket.Session;
import org.realityforge.replicant.server.ChangeSet;
import org.realityforge.replicant.server.ChannelAddress;
import org.realityforge.replicant.server.json.JsonEncoder;

public final class ReplicantSession
  implements Serializable, Closeable
{
  @Nonnull
  private final Session _webSocketSession;
  @Nonnull
  private final Map<ChannelAddress, String> _eTags = new HashMap<>();
  @Nonnull
  private final Map<ChannelAddress, SubscriptionEntry> _subscriptions = new HashMap<>();
  @Nullable
  private String _authToken;

  public ReplicantSession( @Nonnull final Session webSocketSession )
  {
    _webSocketSession = Objects.requireNonNull( webSocketSession );
  }

  public void close( @Nonnull final CloseReason closeReason )
  {
    if ( _webSocketSession.isOpen() )
    {
      try
      {
        _webSocketSession.close( closeReason );
      }
      catch ( final IOException ignored )
      {
        // Assume it is already closing
      }
    }
  }

  @Override
  public void close()
  {
    if ( _webSocketSession.isOpen() )
    {
      try
      {
        _webSocketSession.close();
      }
      catch ( final IOException ignored )
      {
        // Assume it is already closing
      }
    }
  }

  /**
   * Send a ping at the network level to ensure the connection is kept alive.
   *
   * This is required to keep connection alove when passing through some load balancers
   * that proxy non-ssl websockets and close the socket after an idle period.
   */
  public void pingTransport()
  {
    if ( _webSocketSession.isOpen() )
    {
      try
      {
        _webSocketSession.getBasicRemote().sendPing( null );
      }
      catch ( final IOException ignored )
      {
        // All scenarios we can envision imply the session is shutting down, and thus can be ignored
      }
    }
  }

  @Nonnull
  public Session getWebSocketSession()
  {
    return _webSocketSession;
  }

  public void setAuthToken( @Nullable final String authToken )
  {
    _authToken = authToken;
  }

  /**
   * @return a token used for authentication, if any.
   */
  @SuppressWarnings( "unused" )
  @Nullable
  public String getAuthToken()
  {
    return _authToken;
  }

  /**
   * @return an opaque ID representing session.
   */
  @Nonnull
  public String getId()
  {
    return getWebSocketSession().getId();
  }

  /**
   * Send a packet to the client.
   *
   * @param requestId the request id that caused these changes if this session requested the changes.
   * @param etag      the opaque identifier identifying the version. May be null if packet is not cache-able
   * @param changeSet the changeSet to create packet from.
   */
  public void sendPacket( @Nullable final Integer requestId,
                          @Nullable final String etag,
                          @Nonnull final ChangeSet changeSet )
  {
    WebSocketUtil.sendText( getWebSocketSession(), JsonEncoder.encodeChangeSet( requestId, etag, changeSet ) );
  }

  @SuppressWarnings( "WeakerAccess" )
  @Nonnull
  public Map<ChannelAddress, String> getETags()
  {
    return Collections.unmodifiableMap( _eTags );
  }

  @SuppressWarnings( "WeakerAccess" )
  @Nullable
  public String getETag( @Nonnull final ChannelAddress address )
  {
    return _eTags.get( address );
  }

  @SuppressWarnings( "WeakerAccess" )
{'fraction_non_alphanumeric': 0.08233092755947607, 'fraction_numerical': 0.0, 'mean_word_length': 2.9803191489361702, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2374975', 'n_tokens_mistral': 2156, 'n_tokens_neox': 2128, 'n_words': 635}