2015-10-24 16:26:43 +02:00
/*
2017-01-04 14:37:29 +01:00
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
2015-10-24 16:26:43 +02:00
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
2015-11-20 20:54:27 +01:00
package io.moquette.spi ;
2015-10-24 16:26:43 +02:00
2016-02-03 23:20:15 +01:00
import io.moquette.parser.proto.messages.AbstractMessage ;
2017-01-09 21:53:26 +01:00
import io.moquette.spi.ISessionsStore.ClientTopicCouple ;
2015-11-20 20:54:27 +01:00
import io.moquette.spi.impl.subscriptions.Subscription ;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore ;
2015-10-31 11:14:40 +01:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2015-10-24 16:26:43 +02:00
2015-10-31 11:14:40 +01:00
import java.util.HashSet ;
import java.util.Set ;
2016-03-06 19:50:26 +01:00
import java.util.concurrent.BlockingQueue ;
2015-10-24 16:26:43 +02:00
/**
2015-11-15 11:16:18 +01:00
* Model a Session like describe on page 25 of MQTT 3.1.1 specification:
* The Session state in the Server consists of:
* <ul>
* <li>The existence of a Session, even if the rest of the Session state is empty.</li>
* <li>The Client’ s subscriptions.</li>
* <li>QoS 1 and QoS 2 messages which have been sent to the Client, but have not been
* completely acknowledged.</li>
* <li>QoS 1 and QoS 2 messages pending transmission to the Client.</li>
* <li>QoS 2 messages which have been received from the Client, but have not been
* completely acknowledged.</li>
* <li>Optionally, QoS 0 messages pending transmission to the Client.</li>
* </ul>
*
2015-10-24 16:26:43 +02:00
* @author andrea
*/
public class ClientSession {
2015-10-31 11:14:40 +01:00
private final static Logger LOG = LoggerFactory . getLogger ( ClientSession . class );
2015-10-24 16:26:43 +02:00
public final String clientID ;
private final IMessagesStore messagesStore ;
2015-10-31 11:14:40 +01:00
private final ISessionsStore m_sessionsStore ;
private Set < Subscription > subscriptions = new HashSet <> ();
2016-02-02 22:39:11 +01:00
private volatile boolean cleanSession ;
2015-11-08 18:25:55 +01:00
2017-01-09 21:53:26 +01:00
// private BlockingQueue<AbstractMessage> m_queueToPublish = new ArrayBlockingQueue<>(Constants.MAX_MESSAGE_QUEUE);
2016-03-06 19:50:26 +01:00
2015-11-08 18:25:55 +01:00
public ClientSession ( String clientID , IMessagesStore messagesStore , ISessionsStore sessionsStore ,
boolean cleanSession ) {
2015-10-24 16:26:43 +02:00
this . clientID = clientID ;
this . messagesStore = messagesStore ;
2015-10-31 11:14:40 +01:00
this . m_sessionsStore = sessionsStore ;
2015-11-08 18:25:55 +01:00
this . cleanSession = cleanSession ;
2015-10-24 16:26:43 +02:00
}
/**
2017-01-09 21:53:26 +01:00
* Return the list of persisted publishes for the given clientID.
* For QoS1 and QoS2 with clean session flag, this method return the list of
* missed publish events while the client was disconnected.
*
2015-10-24 16:26:43 +02:00
* @return the list of messages to be delivered for client related to the session.
* */
2017-01-09 21:53:26 +01:00
public BlockingQueue < IMessagesStore . StoredMessage > queue () {
2017-01-12 13:11:08 +01:00
LOG . info ( "Retrieving stored messages. MqttClientId = {}." , clientID );
2017-01-09 21:53:26 +01:00
return this . m_sessionsStore . queue ( clientID );
2015-10-24 16:26:43 +02:00
}
@Override
public String toString () {
2015-11-15 11:16:18 +01:00
return "ClientSession{clientID='" + clientID + '\'' + "}" ;
2015-10-24 16:26:43 +02:00
}
2015-10-31 11:14:40 +01:00
2016-07-17 14:24:19 +02:00
public boolean subscribe ( Subscription newSubscription ) {
2017-01-12 13:11:08 +01:00
LOG . info ( "Adding new subscription. MqttClientId = {}, topics = {}, qos = {}." , newSubscription . getClientId (),
newSubscription . getTopicFilter (), AbstractMessage . QOSType . formatQoS ( newSubscription . getRequestedQos ()));
2015-10-31 11:14:40 +01:00
boolean validTopic = SubscriptionsStore . validate ( newSubscription . getTopicFilter ());
if ( ! validTopic ) {
2017-01-12 13:11:08 +01:00
LOG . error ( "The topic filter is not valid. MqttClientId = {}, topics = {}." , newSubscription . getClientId (),
newSubscription . getTopicFilter ());
2015-10-31 11:14:40 +01:00
//send SUBACK with 0x80 for this topic filter
return false ;
}
2015-12-06 19:31:36 +01:00
ClientTopicCouple matchingCouple = new ClientTopicCouple ( this . clientID , newSubscription . getTopicFilter ());
2015-12-06 11:43:29 +01:00
Subscription existingSub = m_sessionsStore . getSubscription ( matchingCouple );
//update the selected subscriptions if not present or if has a greater qos
if ( existingSub == null || existingSub . getRequestedQos (). byteValue () < newSubscription . getRequestedQos (). byteValue ()) {
if ( existingSub != null ) {
2017-01-12 13:11:08 +01:00
LOG . info ( "The subscription already existed with a lower QoS value. It will be updated. MqttClientId = {}, topics = {}, existingQos = {}, newQos = {}." ,
newSubscription . getClientId (), newSubscription . getTopicFilter (), existingSub . getRequestedQos (),
newSubscription . getRequestedQos ());
2015-12-06 11:43:29 +01:00
subscriptions . remove ( newSubscription );
}
subscriptions . add ( newSubscription );
m_sessionsStore . addNewSubscription ( newSubscription );
}
2015-10-31 11:14:40 +01:00
return true ;
}
2015-10-31 11:34:15 +01:00
public void unsubscribeFrom ( String topicFilter ) {
2017-01-12 13:11:08 +01:00
LOG . info ( "Removing subscription. MqttClientID = {}, topics = {}." , clientID , topicFilter );
2015-10-31 11:34:15 +01:00
m_sessionsStore . removeSubscription ( topicFilter , clientID );
Set < Subscription > subscriptionsToRemove = new HashSet <> ();
for ( Subscription sub : this . subscriptions ) {
if ( sub . getTopicFilter (). equals ( topicFilter )) {
subscriptionsToRemove . add ( sub );
}
}
subscriptions . removeAll ( subscriptionsToRemove );
}
2015-11-08 18:25:55 +01:00
public void disconnect () {
if ( this . cleanSession ) {
2017-01-12 13:11:08 +01:00
LOG . info ( "The client has disconnected. Removing its subscriptions. MqttClientId = {}." , clientID );
2015-11-08 18:25:55 +01:00
//cleanup topic subscriptions
cleanSession ();
}
}
public void cleanSession () {
2017-01-12 13:11:08 +01:00
LOG . info ( "Wiping existing subscriptions. MqttClientId = {}." , this . clientID );
2015-11-08 18:25:55 +01:00
m_sessionsStore . wipeSubscriptions ( this . clientID );
//remove also the messages stored of type QoS1/2
2017-01-12 13:11:08 +01:00
LOG . info ( "Removing stored messages with QoS 1 and 2. MqttClientId = {}." , this . clientID );
2015-11-08 18:25:55 +01:00
messagesStore . dropMessagesInSession ( this . clientID );
}
2015-12-06 19:55:04 +01:00
public boolean isCleanSession () {
return this . cleanSession ;
}
2016-02-02 22:39:11 +01:00
public void cleanSession ( boolean cleanSession ) {
this . cleanSession = cleanSession ;
this . m_sessionsStore . updateCleanStatus ( this . clientID , cleanSession );
}
2015-11-15 11:16:18 +01:00
public int nextPacketId () {
2016-01-17 13:00:36 +01:00
return this . m_sessionsStore . nextPacketID ( this . clientID );
2015-11-15 11:16:18 +01:00
}
public void inFlightAcknowledged ( int messageID ) {
2017-01-12 13:11:08 +01:00
if ( LOG . isTraceEnabled ())
LOG . trace ( "Acknowledging inflight, clientID <{}> messageID {}" , this . clientID , messageID );
2015-11-15 11:16:18 +01:00
m_sessionsStore . inFlightAck ( this . clientID , messageID );
}
2016-09-23 11:51:25 +02:00
public void inFlightAckWaiting ( MessageGUID guid , int messageID ) {
2017-01-12 13:11:08 +01:00
if ( LOG . isTraceEnabled ())
LOG . trace ( "Adding to inflight {}, guid <{}>" , messageID , guid );
2015-11-15 11:16:18 +01:00
m_sessionsStore . inFlight ( this . clientID , messageID , guid );
}
2016-06-16 23:01:48 +02:00
public IMessagesStore . StoredMessage secondPhaseAcknowledged ( int messageID ) {
2016-09-23 11:51:25 +02:00
MessageGUID guid = m_sessionsStore . secondPhaseAcknowledged ( clientID , messageID );
2016-06-16 23:01:48 +02:00
return messagesStore . getMessageByGuid ( guid );
2015-11-15 11:16:18 +01:00
}
2017-01-09 21:53:26 +01:00
/**
* Enqueue a message to be sent to the client.
* @param message the message to enqueue.
* */
public void enqueue ( IMessagesStore . StoredMessage message ) {
this . m_sessionsStore . queue ( this . clientID ). add ( message );
2015-11-15 11:16:18 +01:00
}
2015-11-16 23:45:47 +01:00
2015-11-18 22:19:43 +01:00
public IMessagesStore . StoredMessage storedMessage ( int messageID ) {
2016-09-23 11:51:25 +02:00
final MessageGUID guid = m_sessionsStore . mapToGuid ( clientID , messageID );
2015-11-16 23:45:47 +01:00
return messagesStore . getMessageByGuid ( guid );
}
2016-03-06 19:50:26 +01:00
2016-06-16 23:01:48 +02:00
public void moveInFlightToSecondPhaseAckWaiting ( int messageID ) {
m_sessionsStore . moveInFlightToSecondPhaseAckWaiting ( this . clientID , messageID );
}
public IMessagesStore . StoredMessage getInflightMessage ( int messageID ) {
return m_sessionsStore . getInflightMessage ( clientID , messageID );
}
2015-10-24 16:26:43 +02:00
}