/**
* Copyright 2012 Ronen Hamias, Anton Kharenko
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package io.scalecube.socketio.session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicReference;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.scalecube.socketio.TransportType;
import io.scalecube.socketio.packets.IPacket;
import io.scalecube.socketio.packets.Packet;
import io.scalecube.socketio.packets.PacketType;
public abstract class AbstractSession implements ManagedSession {
private final Logger log = LoggerFactory.getLogger(getClass());
private final String sessionId;
private final String origin;
private final SocketAddress remoteAddress;
private final TransportType upgradedFromTransportType;
private final int localPort;
private final Packet connectPacket = new Packet(PacketType.CONNECT);
private final Packet disconnectPacket = new Packet(PacketType.DISCONNECT);
private final Packet heartbeatPacket = new Packet(PacketType.HEARTBEAT);
protected final SessionDisconnectHandler disconnectHandler;
protected final SocketIOHeartbeatScheduler heartbeatScheduler;
private final AtomicReference<State> stateHolder = new AtomicReference<>(State.CREATED);
private volatile boolean upgraded = false;
public AbstractSession(
final Channel channel,
final String sessionId,
final String origin,
final SessionDisconnectHandler disconnectHandler,
final TransportType upgradedFromTransportType,
final int localPort,
final SocketAddress remoteAddress) {
this.sessionId = sessionId;
this.remoteAddress = remoteAddress == null ? channel.remoteAddress() : remoteAddress;
this.origin = origin;
this.localPort = localPort;
this.disconnectHandler = disconnectHandler;
this.upgradedFromTransportType = upgradedFromTransportType;
heartbeatScheduler = new SocketIOHeartbeatScheduler(this);
setState(State.CONNECTING);
}
@Override
public final String getSessionId() {
return sessionId;
}
@Override
public final boolean isUpgradedSession() {
return upgradedFromTransportType != null;
}
@Override
public TransportType getUpgradedFromTransportType() {
return upgradedFromTransportType;
}
@Override
public final SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public final String getOrigin() {
return origin;
}
@Override
public int getLocalPort() {
return localPort;
}
protected boolean isUpgraded() {
return upgraded;
}
@Override
public State getState() {
return stateHolder.get();
}
@Override
public boolean connect(final Channel channel) {
heartbeatScheduler.reschedule();
State previousState = setState(State.CONNECTED);
boolean initialConnect = previousState == State.CONNECTING;
if (initialConnect) {
sendPacketToChannel(channel, connectPacket);
}
return initialConnect;
}
@Override
public void disconnect(final Channel channel) {
if (getState() == State.DISCONNECTED) {
return;
}
setState(State.DISCONNECTING);
heartbeatScheduler.disableHeartbeat();
if (!isUpgraded()) {
sendPacket(disconnectPacket);
disconnectHandler.onSessionDisconnect(this);
}
setState(State.DISCONNECTED);
}
@Override
public void sendHeartbeat() {
sendPacket(heartbeatPacket);
}
@Override
public void send(final ByteBuf message) {
Packet messagePacket = new Packet(PacketType.MESSAGE);
messagePacket.setData(message);
sendPacket(messagePacket);
}
protected void sendPacketToChannel(final Channel channel, IPacket packet) {
fillPacketHeaders(packet);
channel.writeAndFlush(packet);
}
@Override
public void acceptPacket(final Channel channel, final Packet packet) {
// Do nothing (for override)
}
public void markAsUpgraded() {
upgraded = true;
}
@Override
public void acceptHeartbeat() {
heartbeatScheduler.reschedule();
}
protected void fillPacketHeaders(IPacket packet) {
packet.setOrigin(getOrigin());
packet.setSessionId(getSessionId());
packet.setTransportType(getTransportType());
}
protected State setState(final State state) {
State previousState = stateHolder.getAndSet(state);
if (previousState != state && log.isDebugEnabled()) {
log.debug("Session {} state changed from {} to {}", getSessionId(), previousState, state);
}
return previousState;
}
@Override
public String toString() {
return "[id: " + getSessionId()
+ ", origin: " + getOrigin()
+ ", addr: " + getRemoteAddress()
+ ", bindPort: " + getLocalPort()
+ ", transport: " + getTransportType()
+ (isUpgradedSession() ? ", upgradedFrom: " + getUpgradedFromTransportType() : "")
+ ']';
}
}