All Downloads are FREE. Search and download functionalities are using the official Maven repository.

swim.runtime.downlink.DownlinkModel Maven / Gradle / Ivy

There is a newer version: 3.10.0
Show newest version
// Copyright 2015-2019 SWIM.AI inc.
//
// 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 swim.runtime.downlink;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import swim.structure.Value;
import swim.uri.Uri;
import swim.warp.CommandMessage;
import swim.warp.EventMessage;
import swim.warp.LinkRequest;
import swim.warp.LinkedResponse;
import swim.warp.SyncRequest;
import swim.warp.SyncedResponse;
import swim.warp.UnlinkRequest;
import swim.warp.UnlinkedResponse;

public abstract class DownlinkModel extends DownlinkModem {
  protected volatile Object views; // View | DownlinkView[]

  public DownlinkModel(Uri meshUri, Uri hostUri, Uri nodeUri, Uri laneUri,
                       float prio, float rate, Value body) {
    super(meshUri, hostUri, nodeUri, laneUri, prio, rate, body);
  }

  @Override
  public final boolean keepLinked() {
    final Object views = this.views;
    if (views instanceof DownlinkView) {
      return ((DownlinkView) views).keepLinked();
    } else if (views instanceof DownlinkView[]) {
      final DownlinkView[] viewArray = (DownlinkView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        if (viewArray[i].keepLinked()) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public final boolean keepSynced() {
    final Object views = this.views;
    if (views instanceof DownlinkView) {
      return ((DownlinkView) views).keepSynced();
    } else if (views instanceof DownlinkView[]) {
      final DownlinkView[] viewArray = (DownlinkView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        if (viewArray[i].keepSynced()) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  protected void pushDownEvent(EventMessage message) {
    onEvent(message);
    new DownlinkRelayOnEvent(this, message).run();
  }

  @Override
  protected void pushDownLinked(LinkedResponse response) {
    didLink(response);
    new DownlinkRelayDidLink(this, response).run();
  }

  @Override
  protected void pushDownSynced(SyncedResponse response) {
    didSync(response);
    new DownlinkRelayDidSync(this, response).run();
  }

  @Override
  protected void pushDownUnlinked(UnlinkedResponse response) {
    didUnlink(response);
    new DownlinkRelayDidUnlink(this, response).run();
  }

  @Override
  protected void pullUpCommand(CommandMessage message) {
    onCommand(message);
    new DownlinkRelayWillCommand(this, message).run();
  }

  @Override
  protected void pullUpLink(LinkRequest request) {
    willLink(request);
    new DownlinkRelayWillLink(this, request).run();
  }

  @Override
  protected void pullUpSync(SyncRequest request) {
    willSync(request);
    new DownlinkRelayWillSync(this, request).run();
  }

  @Override
  protected void pullUpUnlink(UnlinkRequest request) {
    willUnlink(request);
    new DownlinkRelayWillUnlink(this, request).run();
  }

  public void addDownlink(View view) {
    Object oldViews;
    Object newViews;
    do {
      oldViews = this.views;
      if (oldViews instanceof DownlinkView) {
        newViews = new DownlinkView[]{(DownlinkView) oldViews, view};
      } else if (oldViews instanceof DownlinkView[]) {
        final DownlinkView[] oldViewArray = (DownlinkView[]) oldViews;
        final int n = oldViewArray.length;
        final DownlinkView[] newViewArray = new DownlinkView[n + 1];
        System.arraycopy(oldViewArray, 0, newViewArray, 0, n);
        newViewArray[n] = view;
        newViews = newViewArray;
      } else {
        newViews = view;
      }
    } while (!VIEWS.compareAndSet(this, oldViews, newViews));
    didAddDownlink(view);
    if (oldViews == null) {
      openDown();
    }
  }

  public void removeDownlink(View view) {
    Object oldViews;
    Object newViews;
    do {
      oldViews = this.views;
      if (oldViews instanceof DownlinkView) {
        if (oldViews == view) {
          newViews = null;
          continue;
        }
      } else if (oldViews instanceof DownlinkView[]) {
        final DownlinkView[] oldViewArray = (DownlinkView[]) oldViews;
        final int n = oldViewArray.length;
        if (n == 2) {
          if (oldViewArray[0] == view) {
            newViews = oldViewArray[1];
            continue;
          } else if (oldViewArray[1] == view) {
            newViews = oldViewArray[0];
            continue;
          }
        } else { // n > 2
          final DownlinkView[] newViewArray = new DownlinkView[n - 1];
          int i = 0;
          while (i < n) {
            if (oldViewArray[i] != view) {
              if (i < n - 1) {
                newViewArray[i] = oldViewArray[i];
              }
              i += 1;
            } else {
              break;
            }
          }
          if (i < n) {
            System.arraycopy(oldViewArray, i + 1, newViewArray, i, n - (i + 1));
            newViews = newViewArray;
            continue;
          }
        }
      }
      newViews = oldViews;
      break;
    } while (!VIEWS.compareAndSet(this, oldViews, newViews));
    if (oldViews != newViews) {
      didRemoveDownlink(view);
    }
    if (newViews == null) {
      closeDown();
    }
  }

  protected void didAddDownlink(View view) {
    // stub
  }

  protected void didRemoveDownlink(View view) {
    // stub
  }

  @SuppressWarnings("unchecked")
  @Override
  public void reopen() {
    final Object views = VIEWS.getAndSet(this, null);
    View view;
    if (views instanceof DownlinkView) {
      view = (View) views;
      view.close();
      didRemoveDownlink(view);
      closeDown();
      view.open();
    } else if (views instanceof DownlinkView[]) {
      final DownlinkView[] viewArray = (DownlinkView[]) views;
      final int n = viewArray.length;
      for (int i = 0; i < n; i += 1) {
        view = (View) viewArray[i];
        view.close();
        didRemoveDownlink(view);
      }
      closeDown();
      for (int i = 0; i < n; i += 1) {
        view = (View) viewArray[i];
        view.open();
      }
    }
  }

  @Override
  public void didConnect() {
    super.didConnect();
    new DownlinkRelayDidConnect(this).run();
  }

  @Override
  public void didDisconnect() {
    super.didDisconnect();
    new DownlinkRelayDidDisconnect(this).run();
  }

  @Override
  public void didCloseUp() {
    super.didCloseUp();
    new DownlinkRelayDidClose(this).run();
  }

  @Override
  public void didFail(Throwable error) {
    super.didFail(error);
    new DownlinkRelayDidFail(this, error).run();
  }

  @SuppressWarnings("unchecked")
  static final AtomicReferenceFieldUpdater, Object> VIEWS =
      AtomicReferenceFieldUpdater.newUpdater((Class>) (Class) DownlinkModel.class, Object.class, "views");
}

final class DownlinkRelayOnEvent extends DownlinkRelay, View> {
  final EventMessage message;

  DownlinkRelayOnEvent(DownlinkModel model, EventMessage message) {
    super(model, 2);
    this.message = message;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkWillReceive(this.message);
      }
      return view.dispatchWillReceive(this.message.body(), preemptive);
    } else if (phase == 1) {
      if (preemptive) {
        view.downlinkDidReceive(this.message);
      }
      return view.dispatchDidReceive(this.message.body(), preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }

  @Override
  void done() {
    this.model.cueDown();
  }
}

final class DownlinkRelayWillCommand extends DownlinkRelay, View> {
  final CommandMessage message;

  DownlinkRelayWillCommand(DownlinkModel model, CommandMessage message) {
    super(model);
    this.message = message;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkWillCommand(this.message);
      }
      return view.dispatchWillCommand(this.message.body(), preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayWillLink extends DownlinkRelay, View> {
  final LinkRequest request;

  DownlinkRelayWillLink(DownlinkModel model, LinkRequest request) {
    super(model);
    this.request = request;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkWillLink(this.request);
      }
      return view.dispatchWillLink(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayDidLink extends DownlinkRelay, View> {
  final LinkedResponse response;

  DownlinkRelayDidLink(DownlinkModel model, LinkedResponse response) {
    super(model);
    this.response = response;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidLink(this.response);
      }
      return view.dispatchDidLink(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }

  @Override
  void done() {
    this.model.cueDown();
  }
}

final class DownlinkRelayWillSync extends DownlinkRelay, View> {
  final SyncRequest request;

  DownlinkRelayWillSync(DownlinkModel model, SyncRequest request) {
    super(model);
    this.request = request;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkWillSync(this.request);
      }
      return view.dispatchWillSync(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayDidSync extends DownlinkRelay, View> {
  final SyncedResponse response;

  DownlinkRelayDidSync(DownlinkModel model, SyncedResponse response) {
    super(model);
    this.response = response;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidSync(this.response);
      }
      return view.dispatchDidSync(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }

  @Override
  void done() {
    this.model.cueDown();
  }
}

final class DownlinkRelayWillUnlink extends DownlinkRelay, View> {
  final UnlinkRequest request;

  DownlinkRelayWillUnlink(DownlinkModel model, UnlinkRequest request) {
    super(model);
    this.request = request;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkWillUnlink(this.request);
      }
      return view.dispatchWillUnlink(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayDidUnlink extends DownlinkRelay, View> {
  final UnlinkedResponse response;

  DownlinkRelayDidUnlink(DownlinkModel model, UnlinkedResponse response) {
    super(model);
    this.response = response;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidUnlink(this.response);
      }
      return view.dispatchDidUnlink(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }

  @Override
  void done() {
    // Don't cueDown model after unlinked.
  }
}

final class DownlinkRelayDidConnect extends DownlinkRelay, View> {
  DownlinkRelayDidConnect(DownlinkModel model) {
    super(model);
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidConnect();
      }
      return view.dispatchDidConnect(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayDidDisconnect extends DownlinkRelay, View> {
  DownlinkRelayDidDisconnect(DownlinkModel model) {
    super(model);
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidDisconnect();
      }
      return view.dispatchDidDisconnect(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayDidClose extends DownlinkRelay, View> {
  DownlinkRelayDidClose(DownlinkModel model) {
    super(model);
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidClose();
      }
      return view.dispatchDidClose(preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}

final class DownlinkRelayDidFail extends DownlinkRelay, View> {
  final Throwable error;

  DownlinkRelayDidFail(DownlinkModel model, Throwable error) {
    super(model);
    this.error = error;
  }

  @Override
  boolean runPhase(View view, int phase, boolean preemptive) {
    if (phase == 0) {
      if (preemptive) {
        view.downlinkDidFail(this.error);
      }
      return view.dispatchDidFail(this.error, preemptive);
    } else {
      throw new AssertionError(); // unreachable
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy