Package ch.ethz.inf.vs.californium.network.stack

Source Code of ch.ethz.inf.vs.californium.network.stack.ObserveLayer$ReregistrationTask

package ch.ethz.inf.vs.californium.network.stack;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import ch.ethz.inf.vs.californium.coap.CoAP.ResponseCode;
import ch.ethz.inf.vs.californium.coap.CoAP.Type;
import ch.ethz.inf.vs.californium.coap.EmptyMessage;
import ch.ethz.inf.vs.californium.coap.MessageObserverAdapter;
import ch.ethz.inf.vs.californium.coap.Request;
import ch.ethz.inf.vs.californium.coap.Response;
import ch.ethz.inf.vs.californium.network.Exchange;
import ch.ethz.inf.vs.californium.network.Exchange.Origin;
import ch.ethz.inf.vs.californium.network.config.NetworkConfig;
import ch.ethz.inf.vs.californium.network.config.NetworkConfigDefaults;
import ch.ethz.inf.vs.californium.observe.ObserveRelation;

public class ObserveLayer extends AbstractLayer {

  private long backoff = 0; // additional time to wait until re-registration
 
  public ObserveLayer(NetworkConfig config) {
    this.backoff = config.getInt(NetworkConfigDefaults.NOTIFICATION_REREGISTRATION_BACKOFF);
  }
 
  @Override
  public void sendRequest(Exchange exchange, Request request) {
    super.sendRequest(exchange, request);
  }
 
  @Override
  public void sendResponse(final Exchange exchange, final Response response) {
    final ObserveRelation relation = exchange.getRelation();
    if (relation != null && relation.isEstablished()) {
     
      if (exchange.getRequest().isAcknowledged() || exchange.getRequest().getType()==Type.NON) {
        // Transmit errors as CON
        if (!ResponseCode.isSuccess(response.getCode())) {
          LOGGER.fine("Response has error code "+response.getCode()+" and must be sent as CON");
          response.setType(Type.CON);
          relation.cancel();
        } else {
          // Make sure that every now and than a CON is mixed within
          if (relation.check()) {
            LOGGER.fine("The observe relation requires the notification to be sent as CON");
            response.setType(Type.CON);
          // By default use NON, but do not override resource decision
          } else if (response.getType()==null) {
            response.setType(Type.NON);
          }
        }
      }
     
      // This is a notification
      response.setLast(false);
     
      /*
       * Only one Confirmable message is allowed to be in transit. A CON
       * is in transit as long as it has not been acknowledged, rejected,
       * or timed out. All further notifications are postponed here. If a
       * former CON is acknowledged or timeouts, it starts the youngest
       * notification (In case of a timeout, it keeps the retransmission
       * counter). When a fresh/younger notification arrives but must be
       * postponed we forget any former notification.
       */
      if (response.getType() == Type.CON) {
        prepareSelfReplacement(exchange, response);
      }
     
      // The decision whether to postpone this notification or not and the
      // decision which notification is the youngest to send next must be
      // synchronized
      synchronized (exchange) {
        Response current = relation.getCurrentControlNotification();
        if (current != null && isInTransit(current)) {
          LOGGER.fine("A former notification is still in transit. Postpone " + response);
          relation.setNextControlNotification(response);
          return;
        } else {
          relation.setCurrentControlNotification(response);
          relation.setNextControlNotification(null);
        }
      }

    } // else no observe was requested or the resource does not allow it
    super.sendResponse(exchange, response);
  }
 
  /**
   * Returns true if the specified response is still in transit. A response is
   * in transit if it has not yet been acknowledged, rejected or its current
   * transmission has not yet timed out.
   */
  private boolean isInTransit(Response response) {
    Type type = response.getType();
    boolean acked = response.isAcknowledged();
    boolean timeout = response.isTimedOut();
    boolean result = type == Type.CON && !acked && !timeout;
    return result;
  }

  @Override
  public void receiveResponse(Exchange exchange, Response response) {
    if (response.getOptions().hasObserve()) {
      if (exchange.getRequest().isCanceled()) {
        // The request was canceled and we no longer want notifications
        LOGGER.finer("ObserveLayer rejecting notification for canceled Exchange");
        EmptyMessage rst = EmptyMessage.newRST(response);
        sendEmptyMessage(exchange, rst);
      } else {
        prepareReregistration(exchange, response, new ReregistrationTask(exchange));
        super.receiveResponse(exchange, response);
      }
    } else {
      // No observe option in response => always deliver
      super.receiveResponse(exchange, response);
    }
  }
 
  @Override
  public void receiveEmptyMessage(Exchange exchange, EmptyMessage message) {
    // NOTE: We could also move this into the MessageObserverAdapter from
    // sendResponse into the method rejected().
    if (message.getType() == Type.RST && exchange.getOrigin() == Origin.REMOTE) {
      // The response has been rejected
      ObserveRelation relation = exchange.getRelation();
      if (relation != null) {
        relation.cancel();
      } // else there was no observe relation ship and this layer ignores the rst
    }
    super.receiveEmptyMessage(exchange, message);
  }
 
  private void prepareSelfReplacement(Exchange exchange, Response response) {
    response.addMessageObserver(new NotificationController(exchange, response));
  }
 
  private void prepareReregistration(Exchange exchange, Response response, ReregistrationTask task) {
    long timeout = response.getOptions().getMaxAge()*1000 + this.backoff;
    LOGGER.finest("Scheduling re-registration in " + timeout + "ms for " + exchange.getRequest());
    ScheduledFuture<?> f = executor.schedule(task , timeout, TimeUnit.MILLISECONDS);
    exchange.setReregistrationHandle(f);
  }
 
  /**
   * Sends the next CON as soon as the former CON is no longer in transit.
   */
  private class NotificationController extends MessageObserverAdapter {
   
    private Exchange exchange;
    private Response response;
   
    public NotificationController(Exchange exchange, Response response) {
      this.exchange = exchange;
      this.response = response;
    }
   
    @Override
    public void onAcknowledgement() {
      synchronized (exchange) {
        ObserveRelation relation = exchange.getRelation();
        Response next = relation.getNextControlNotification();
        relation.setCurrentControlNotification(next); // next may be null
        relation.setNextControlNotification(null);
        if (next != null) {
          LOGGER.fine("Notification has been acknowledged, send the next one");
          ObserveLayer.super.sendResponse(exchange, next); // TODO: make this as new task?
        }
      }
    }
   
    @Override
    public void onRetransmission() {
      synchronized (exchange) {
        final ObserveRelation relation = exchange.getRelation();
        final Response next = relation.getNextControlNotification();
        if (next != null) {
          LOGGER.fine("The notification has timed out and there is a younger notification. Send the younger one");
          relation.setNextControlNotification(null);
          // Send the next notification
          response.cancel();
          Type nt = next.getType();
          if (nt != Type.CON); {
            LOGGER.finer("The next notification's type was "+nt+". Since it replaces a CON control notification, it becomes a CON as well");
            prepareSelfReplacement(exchange, next);
            next.setType(Type.CON); // Force the next to be a Confirmable as well
          }
          relation.setCurrentControlNotification(next);
          // Create a new task for sending next response so that we can leave the sync-block
          executor.execute(new Runnable() {
            public void run() {
              ObserveLayer.super.sendResponse(exchange, next);
            }
          });
        }
      }
    }
   
    @Override
    public void onTimeout() {
      ObserveRelation relation = exchange.getRelation();
      LOGGER.info("Notification timed out. Cancel all relations with source "+relation.getSource());
      relation.cancelAll();
    }
   
    // Cancellation on RST is done in receiveEmptyMessage()
  }
 

  /*
   * The main reason to create this class was to enable the methods
   * sendRequest and sendResponse to use the same code for sending messages
   * but where the retransmission method calls sendRequest and sendResponse
   * respectively.
   */
  private class ReregistrationTask implements Runnable {
   
    private Exchange exchange;
   
    public ReregistrationTask(Exchange exchange) {
      this.exchange = exchange;
    }
   
    @Override
    public void run() {
      if (!exchange.getRequest().isCanceled()) {
        Request refresh = Request.newGet();
        refresh.setOptions(exchange.getRequest().getOptions());
        // make sure Observe is set and zero
        refresh.setObserve();
        // use same Token
        refresh.setToken(exchange.getRequest().getToken());
        refresh.setDestination(exchange.getRequest().getDestination());
        refresh.setDestinationPort(exchange.getRequest().getDestinationPort());
        LOGGER.info("Re-registering for " + exchange.getRequest());
        sendRequest(exchange, refresh);
      } else {
        LOGGER.finer("Dropping re-registration for canceled " + exchange.getRequest());
      }
    }
  }
}
TOP

Related Classes of ch.ethz.inf.vs.californium.network.stack.ObserveLayer$ReregistrationTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.