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

swim.system.LaneModel Maven / Gradle / Ivy

The newest version!
// Copyright 2015-2024 Nstream, 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.system;

import java.util.Iterator;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import swim.api.Downlink;
import swim.api.Lane;
import swim.api.agent.AgentContext;
import swim.api.lane.DemandMapLane;
import swim.api.lane.function.OnCueKey;
import swim.api.lane.function.OnSyncKeys;
import swim.api.policy.Policy;
import swim.api.warp.WarpUplink;
import swim.collections.FingerTrieSeq;
import swim.concurrent.Cont;
import swim.concurrent.Schedule;
import swim.concurrent.Stage;
import swim.store.StoreBinding;
import swim.structure.Form;
import swim.structure.Value;
import swim.system.agent.AgentNode;
import swim.system.reflect.UplinkInfo;
import swim.uri.Uri;
import swim.warp.CommandMessage;

public abstract class LaneModel extends AbstractTierBinding implements LaneBinding {

  protected LaneContext laneContext;
  protected volatile Object views; // View | LaneView[]
  protected volatile FingerTrieSeq uplinks;
  AgentNode metaNode;
  DemandMapLane metaUplinks;

  public LaneModel() {
    this.laneContext = null;
    this.views = null;
    this.uplinks = FingerTrieSeq.empty();
    this.metaNode = null;
    this.metaUplinks = null;
  }

  @Override
  public final TierContext tierContext() {
    return this.laneContext;
  }

  @Override
  public final NodeBinding node() {
    return this.laneContext.node();
  }

  @Override
  public final LaneBinding laneWrapper() {
    return this;
  }

  @Override
  public final LaneContext laneContext() {
    return this.laneContext;
  }

  @Override
  public void setLaneContext(LaneContext laneContext) {
    this.laneContext = laneContext;
  }

  @SuppressWarnings("unchecked")
  @Override
  public  T unwrapLane(Class laneClass) {
    if (laneClass.isAssignableFrom(this.getClass())) {
      return (T) this;
    } else {
      return this.laneContext.unwrapLane(laneClass);
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public  T bottomLane(Class laneClass) {
    T lane = this.laneContext.bottomLane(laneClass);
    if (lane == null && laneClass.isAssignableFrom(this.getClass())) {
      lane = (T) this;
    }
    return lane;
  }

  protected abstract U createUplink(LinkBinding link);

  protected UplinkAddress createUplinkAddress(LinkBinding link) {
    return this.cellAddress().linkKey(LinkKeys.generateLinkKey());
  }

  @Override
  public LaneAddress cellAddress() {
    return this.laneContext.cellAddress();
  }

  @Override
  public String edgeName() {
    return this.laneContext.edgeName();
  }

  @Override
  public final Uri meshUri() {
    return this.laneContext.meshUri();
  }

  @Override
  public final Value partKey() {
    return this.laneContext.partKey();
  }

  @Override
  public final Uri hostUri() {
    return this.laneContext.hostUri();
  }

  @Override
  public final Uri nodeUri() {
    return this.laneContext.nodeUri();
  }

  @Override
  public final Uri laneUri() {
    return this.laneContext.laneUri();
  }

  @Override
  public Policy policy() {
    return this.laneContext.policy();
  }

  @Override
  public Schedule schedule() {
    return this.laneContext.schedule();
  }

  @Override
  public Stage stage() {
    return this.laneContext.stage();
  }

  @Override
  public StoreBinding store() {
    return this.laneContext.store();
  }

  @Override
  public Lane getLaneView(AgentContext agentContext) {
    final Object views = LaneModel.VIEWS.get(this);
    LaneView view;
    if (views instanceof LaneView) {
      view = (LaneView) views;
      if (agentContext == view.agentContext()) {
        return view;
      }
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        view = viewArray[i];
        if (agentContext == view.agentContext()) {
          return view;
        }
      }
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public void openLaneView(Lane view) {
    do {
      final Object oldLaneViews = LaneModel.VIEWS.get(this);
      final Object newLaneViews;
      if (oldLaneViews instanceof LaneView) {
        newLaneViews = new LaneView[] {(LaneView) oldLaneViews, (LaneView) view};
      } else if (oldLaneViews instanceof LaneView[]) {
        final LaneView[] oldLaneViewArray = (LaneView[]) oldLaneViews;
        final int n = oldLaneViewArray.length;
        final LaneView[] newLaneViewArray = new LaneView[n + 1];
        System.arraycopy(oldLaneViewArray, 0, newLaneViewArray, 0, n);
        newLaneViewArray[n] = (LaneView) view;
        newLaneViews = newLaneViewArray;
      } else {
        newLaneViews = (LaneView) view;
      }
      if (LaneModel.VIEWS.compareAndSet(this, oldLaneViews, newLaneViews)) {
        this.didOpenLaneView((View) view);
        this.activate((View) view);
        break;
      }
    } while (true);
  }

  @SuppressWarnings("unchecked")
  @Override
  public void closeLaneView(Lane view) {
    do {
      final Object oldLaneViews = LaneModel.VIEWS.get(this);
      final Object newLaneViews;
      if (oldLaneViews instanceof LaneView) {
        if (oldLaneViews == view) {
          newLaneViews = null;
        } else {
          break;
        }
      } else if (oldLaneViews instanceof LaneView[]) {
        final LaneView[] oldLaneViewArray = (LaneView[]) oldLaneViews;
        final int n = oldLaneViewArray.length;
        if (n == 2) {
          if (oldLaneViewArray[0] == view) {
            newLaneViews = oldLaneViewArray[1];
          } else if (oldLaneViewArray[1] == view) {
            newLaneViews = oldLaneViewArray[0];
          } else {
            break;
          }
        } else { // n > 2
          final LaneView[] newLaneViewArray = new LaneView[n - 1];
          int i = 0;
          while (i < n) {
            if (oldLaneViewArray[i] != view) {
              if (i < n - 1) {
                newLaneViewArray[i] = oldLaneViewArray[i];
              }
              i += 1;
            } else {
              break;
            }
          }
          if (i < n) {
            System.arraycopy(oldLaneViewArray, i + 1, newLaneViewArray, i, n - (i + 1));
            newLaneViews = newLaneViewArray;
          } else {
            break;
          }
        }
      } else {
        break;
      }
      if (LaneModel.VIEWS.compareAndSet(this, oldLaneViews, newLaneViews)) {
        ((View) view).didClose();
        this.didCloseLaneView((View) view);
        if (newLaneViews == null) {
          this.close();
        }
        break;
      }
    } while (true);
  }

  @Override
  public boolean isLinked() {
    return !LaneModel.UPLINKS.get(this).isEmpty();
  }

  @SuppressWarnings("unchecked")
  @Override
  public FingerTrieSeq uplinks() {
    return (FingerTrieSeq) (FingerTrieSeq) LaneModel.UPLINKS.get(this);
  }

  @SuppressWarnings("unchecked")
  @Override
  public LinkContext getUplink(Value linkKey) {
    final FingerTrieSeq uplinks = (FingerTrieSeq) (FingerTrieSeq) LaneModel.UPLINKS.get(this);
    for (int i = 0, n = uplinks.size(); i < n; i += 1) {
      final U uplink = uplinks.get(i);
      if (linkKey.equals(uplink.linkKey())) {
        return uplink;
      }
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public void openUplink(LinkBinding link) {
    FingerTrieSeq oldUplinks;
    FingerTrieSeq newUplinks;
    final U uplink = this.createUplink(link);
    if (uplink != null) {
      link.setLinkContext(uplink);
      do {
        oldUplinks = (FingerTrieSeq) (FingerTrieSeq) LaneModel.UPLINKS.get(this);
        newUplinks = oldUplinks.appended(uplink);
      } while (!LaneModel.UPLINKS.compareAndSet(this, oldUplinks, newUplinks));
      this.didOpenUplink(uplink);
      // TODO: onEnter
      final DemandMapLane metaUplinks = this.metaUplinks;
      if (metaUplinks != null) {
        metaUplinks.cue(link.linkKey());
      }
    } else {
      UplinkError.rejectUnsupported(link);
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public void closeUplink(Value linkKey) {
    outer: do {
      final FingerTrieSeq oldUplinks = (FingerTrieSeq) (FingerTrieSeq) LaneModel.UPLINKS.get(this);
      for (int i = 0, n = oldUplinks.size(); i < n; i += 1) {
        final U uplink = oldUplinks.get(i);
        if (linkKey.equals(uplink.linkKey())) {
          final FingerTrieSeq newUplinks = oldUplinks.removed(i);
          if (LaneModel.UPLINKS.compareAndSet(this, oldUplinks, newUplinks)) {
            uplink.linkBinding().didCloseUp();
            final DemandMapLane metaUplinks = this.metaUplinks;
            if (metaUplinks != null) {
              metaUplinks.remove(linkKey);
            }
            // TODO: onLeave
            this.didCloseUplink(uplink);
            break outer;
          } else {
            continue;
          }
        }
      }
      break;
    } while (true);
  }

  @Override
  public void openMetaLane(LaneBinding lane, NodeBinding metaLane) {
    if (metaLane instanceof AgentNode) {
      this.metaNode = (AgentNode) metaLane;
      this.openMetaLanes(lane, (AgentNode) metaLane);
    }
    this.laneContext.openMetaLane(lane, metaLane);
  }

  protected void openMetaLanes(LaneBinding lane, AgentNode metaLane) {
    this.openReflectLanes(lane, metaLane);
  }

  protected void openReflectLanes(LaneBinding lane, AgentNode metaLane) {
    this.metaUplinks = metaLane.demandMapLane()
                               .keyForm(Form.forValue())
                               .valueForm(UplinkInfo.uplinkForm())
                               .observe(new LaneModelUplinksController(lane));
    metaLane.openLane(LaneModel.UPLINKS_URI, this.metaUplinks);
  }

  @Override
  public void openMetaUplink(LinkBinding uplink, NodeBinding metaUplink) {
    this.laneContext.openMetaUplink(uplink, metaUplink);
  }

  @Override
  public void openMetaDownlink(LinkBinding downlink, NodeBinding metaDownlink) {
    this.laneContext.openMetaDownlink(downlink, metaDownlink);
  }

  @Override
  public abstract void pushUp(Push push);

  @Override
  public abstract void pushUpCommand(Push push);

  protected abstract void didOpenLaneView(View view);

  protected void didCloseLaneView(View view) {
    // hook
  }

  protected void didOpenUplink(U uplink) {
    // hook
  }

  protected void didCloseUplink(U uplink) {
    // hook
  }

  @Override
  public LinkBinding bindDownlink(Downlink downlink) {
    final LinkBinding link = this.laneContext.bindDownlink(downlink);
    link.setCellContext(this);
    return link;
  }

  @Override
  public void openDownlink(LinkBinding link) {
    this.laneContext.openDownlink(link);
    link.setCellContext(this);
  }

  @Override
  public void closeDownlink(LinkBinding link) {
    this.laneContext.closeDownlink(link);
  }

  @Override
  public void pushDown(Push push) {
    this.laneContext.pushDown(push);
  }

  @Override
  public void reportDown(Metric metric) {
    this.laneContext.reportDown(metric);
  }

  @Override
  public void trace(Object message) {
    this.laneContext.trace(message);
  }

  @Override
  public void debug(Object message) {
    this.laneContext.debug(message);
  }

  @Override
  public void info(Object message) {
    this.laneContext.info(message);
  }

  @Override
  public void warn(Object message) {
    this.laneContext.warn(message);
  }

  @Override
  public void error(Object message) {
    this.laneContext.error(message);
  }

  @Override
  public void fail(Object message) {
    this.laneContext.fail(message);
  }

  @Override
  protected void willOpen() {
    super.willOpen();
    final Object views = LaneModel.VIEWS.get(this);
    if (views instanceof LaneView) {
      ((LaneView) views).open();
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        viewArray[i].open();
      }
    }
  }

  @Override
  protected void willLoad() {
    super.willLoad();
    final Object views = LaneModel.VIEWS.get(this);
    if (views instanceof LaneView) {
      ((LaneView) views).load();
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        viewArray[i].load();
      }
    }
  }

  @Override
  protected void willStart() {
    super.willStart();
    final Object views = LaneModel.VIEWS.get(this);
    if (views instanceof LaneView) {
      ((LaneView) views).start();
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        viewArray[i].start();
      }
    }
  }

  @Override
  protected void willStop() {
    super.willStop();
    final Object views = LaneModel.VIEWS.get(this);
    if (views instanceof LaneView) {
      ((LaneView) views).stop();
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        viewArray[i].stop();
      }
    }
  }

  @Override
  protected void willUnload() {
    super.willUnload();
    final Object views = LaneModel.VIEWS.get(this);
    if (views instanceof LaneView) {
      ((LaneView) views).unload();
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        viewArray[i].unload();
      }
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  protected void willClose() {
    super.willClose();
    final FingerTrieSeq uplinks = (FingerTrieSeq) (FingerTrieSeq) LaneModel.UPLINKS.get(this);
    for (int i = 0, n = uplinks.size(); i < n; i += 1) {
      uplinks.get(i).close();
    }
    final Object views = LaneModel.VIEWS.get(this);
    if (views instanceof LaneView) {
      ((LaneView) views).close();
    } else if (views instanceof LaneView[]) {
      final LaneView[] viewArray = (LaneView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        viewArray[i].close();
      }
    }
    this.laneContext.close();
  }

  @Override
  public void didClose() {
    super.didClose();
    final AgentNode metaNode = this.metaNode;
    if (metaNode != null) {
      metaNode.close();
      this.metaNode = null;
      this.metaUplinks = null;
    }
  }

  @Override
  public void didFail(Throwable error) {
    if (Cont.isNonFatal(error)) {
      this.fail(error);
    } else {
      error.printStackTrace();
    }
  }

  public void accumulateExecTime(long execDelta) {
    // hook
  }

  @SuppressWarnings("unchecked")
  protected static final AtomicReferenceFieldUpdater, Object> VIEWS =
      AtomicReferenceFieldUpdater.newUpdater((Class>) (Class) LaneModel.class, Object.class, "views");
  @SuppressWarnings("unchecked")
  protected static final AtomicReferenceFieldUpdater, FingerTrieSeq> UPLINKS =
      AtomicReferenceFieldUpdater.newUpdater((Class>) (Class) LaneModel.class, (Class>) (Class) FingerTrieSeq.class, "uplinks");

  static final Uri UPLINKS_URI = Uri.parse("uplinks");

}

final class LaneModelUplinksController implements OnCueKey, OnSyncKeys {

  final LaneBinding lane;

  LaneModelUplinksController(LaneBinding lane) {
    this.lane = lane;
  }

  @Override
  public UplinkInfo onCue(Value linkKey, WarpUplink uplink) {
    final LinkContext linkContext = this.lane.getUplink(linkKey);
    if (linkContext == null) {
      return null;
    }
    return UplinkInfo.create(linkContext);
  }

  @Override
  public Iterator onSync(WarpUplink uplink) {
    return new LaneModelUplinksKeyIterator(this.lane.uplinks().iterator());
  }

}

final class LaneModelUplinksKeyIterator implements Iterator {

  final Iterator uplinks;

  LaneModelUplinksKeyIterator(Iterator uplinks) {
    this.uplinks = uplinks;
  }

  @Override
  public boolean hasNext() {
    return this.uplinks.hasNext();
  }

  @Override
  public Value next() {
    return this.uplinks.next().linkKey();
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException();
  }

}