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

swim.runtime.router.HostTable 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.router;

import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import swim.api.data.DataFactory;
import swim.api.downlink.Downlink;
import swim.api.policy.Policy;
import swim.collections.HashTrieMap;
import swim.concurrent.Schedule;
import swim.concurrent.Stage;
import swim.math.Z2Form;
import swim.runtime.AbstractTierBinding;
import swim.runtime.HostBinding;
import swim.runtime.HostContext;
import swim.runtime.HttpBinding;
import swim.runtime.LinkBinding;
import swim.runtime.NodeBinding;
import swim.runtime.NodeContext;
import swim.runtime.PushRequest;
import swim.runtime.TierContext;
import swim.runtime.uplink.ErrorUplinkModem;
import swim.runtime.uplink.HttpErrorUplinkModem;
import swim.store.DataBinding;
import swim.store.ListDataBinding;
import swim.store.MapDataBinding;
import swim.store.SpatialDataBinding;
import swim.store.ValueDataBinding;
import swim.structure.Record;
import swim.structure.Value;
import swim.uri.Uri;

public class HostTable extends AbstractTierBinding implements HostBinding {
  protected HostContext hostContext;

  volatile HashTrieMap nodes;

  volatile int flags;

  public HostTable() {
    this.nodes = HashTrieMap.empty();
  }

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

  @Override
  public final HostContext hostContext() {
    return this.hostContext;
  }

  @Override
  public void setHostContext(HostContext hostContext) {
    this.hostContext = hostContext;
  }

  @SuppressWarnings("unchecked")
  @Override
  public  T unwrapHost(Class hostClass) {
    if (hostClass.isAssignableFrom(getClass())) {
      return (T) this;
    } else {
      return null;
    }
  }

  protected NodeContext createNodeContext(NodeBinding node, Uri nodeUri) {
    return new HostTableNode(this, node, nodeUri);
  }

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

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

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

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

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

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

  @Override
  public DataFactory data() {
    return this.hostContext.data();
  }

  @Override
  public boolean isConnected() {
    return true;
  }

  @Override
  public boolean isRemote() {
    return false;
  }

  @Override
  public boolean isSecure() {
    return true;
  }

  @Override
  public boolean isPrimary() {
    return (this.flags & PRIMARY) != 0;
  }

  @Override
  public void setPrimary(boolean isPrimary) {
    int oldFlags;
    int newFlags;
    do {
      oldFlags = this.flags;
      newFlags = oldFlags | PRIMARY;
    } while (oldFlags != newFlags && !FLAGS.compareAndSet(this, oldFlags, newFlags));
  }

  @Override
  public boolean isReplica() {
    return (this.flags & REPLICA) != 0;
  }

  @Override
  public void setReplica(boolean isReplica) {
    int oldFlags;
    int newFlags;
    do {
      oldFlags = this.flags;
      newFlags = oldFlags | REPLICA;
    } while (oldFlags != newFlags && !FLAGS.compareAndSet(this, oldFlags, newFlags));
  }

  @Override
  public boolean isMaster() {
    return (this.flags & MASTER) != 0;
  }

  @Override
  public boolean isSlave() {
    return (this.flags & SLAVE) != 0;
  }

  @Override
  public void didBecomeMaster() {
    int oldFlags;
    int newFlags;
    do {
      oldFlags = this.flags;
      newFlags = oldFlags & ~SLAVE | MASTER;
    } while (oldFlags != newFlags && !FLAGS.compareAndSet(this, oldFlags, newFlags));
  }

  @Override
  public void didBecomeSlave() {
    int oldFlags;
    int newFlags;
    do {
      oldFlags = this.flags;
      newFlags = oldFlags & ~MASTER | SLAVE;
    } while (oldFlags != newFlags && !FLAGS.compareAndSet(this, oldFlags, newFlags));
    if (oldFlags != newFlags) {
      closeNodes();
    }
  }

  @Override
  public HashTrieMap getNodes() {
    return this.nodes;
  }

  @Override
  public NodeBinding getNode(Uri nodeUri) {
    return this.nodes.get(nodeUri);
  }

  @Override
  public NodeBinding openNode(Uri nodeUri) {
    HashTrieMap oldNodes;
    HashTrieMap newNodes;
    NodeBinding nodeBinding = null;
    do {
      oldNodes = this.nodes;
      final NodeBinding node = oldNodes.get(nodeUri);
      if (node != null) {
        if (nodeBinding != null) {
          // Lost creation race.
          nodeBinding.close();
        }
        nodeBinding = node;
        newNodes = oldNodes;
        break;
      } else if (nodeBinding == null) {
        nodeBinding = this.hostContext.createNode(nodeUri);
        if (nodeBinding != null) {
          nodeBinding = this.hostContext.injectNode(nodeUri, nodeBinding);
          final NodeContext nodeContext = createNodeContext(nodeBinding, nodeUri);
          nodeBinding.setNodeContext(nodeContext);
          newNodes = oldNodes.updated(nodeUri, nodeBinding);
        } else {
          newNodes = oldNodes;
          break;
        }
      } else {
        newNodes = oldNodes.updated(nodeUri, nodeBinding);
      }
    } while (oldNodes != newNodes && !NODES.compareAndSet(this, oldNodes, newNodes));
    if (oldNodes != newNodes) {
      activate(nodeBinding);
    }
    return nodeBinding;
  }

  @Override
  public NodeBinding openNode(Uri nodeUri, NodeBinding node) {
    HashTrieMap oldNodes;
    HashTrieMap newNodes;
    NodeBinding nodeBinding = null;
    do {
      oldNodes = this.nodes;
      if (oldNodes.containsKey(nodeUri)) {
        nodeBinding = null;
        newNodes = oldNodes;
        break;
      } else {
        if (nodeBinding == null) {
          nodeBinding = this.hostContext.injectNode(nodeUri, node);
          final NodeContext nodeContext = createNodeContext(nodeBinding, nodeUri);
          nodeBinding.setNodeContext(nodeContext);
        }
        newNodes = oldNodes.updated(nodeUri, nodeBinding);
      }
    } while (oldNodes != newNodes && !NODES.compareAndSet(this, oldNodes, newNodes));
    if (nodeBinding != null) {
      activate(nodeBinding);
    }
    return nodeBinding;
  }

  public void closeNode(Uri nodeUri) {
    HashTrieMap oldNodes;
    HashTrieMap newNodes;
    NodeBinding nodeBinding = null;
    do {
      oldNodes = this.nodes;
      final NodeBinding node = oldNodes.get(nodeUri);
      if (node != null) {
        nodeBinding = node;
        newNodes = oldNodes.removed(nodeUri);
      } else {
        nodeBinding = null;
        newNodes = oldNodes;
        break;
      }
    } while (oldNodes != newNodes && !NODES.compareAndSet(this, oldNodes, newNodes));
    if (nodeBinding != null) {
      nodeBinding.didClose();
    }
  }

  public void closeNodes() {
    HashTrieMap oldNodes;
    final HashTrieMap newNodes = HashTrieMap.empty();
    do {
      oldNodes = this.nodes;
    } while (oldNodes != newNodes && !NODES.compareAndSet(this, oldNodes, newNodes));
    for (NodeBinding nodeBinding : oldNodes.values()) {
      nodeBinding.close();
      nodeBinding.didClose();
    }
  }

  @Override
  public Iterator dataBindings() {
    return Collections.emptyIterator();
  }

  @Override
  public void closeData(Value name) {
    // nop
  }

  @Override
  public ListDataBinding openListData(Value name) {
    return this.hostContext.openListData(name);
  }

  @Override
  public ListDataBinding injectListData(ListDataBinding dataBinding) {
    return this.hostContext.injectListData(dataBinding);
  }

  @Override
  public MapDataBinding openMapData(Value name) {
    return this.hostContext.openMapData(name);
  }

  @Override
  public MapDataBinding injectMapData(MapDataBinding dataBinding) {
    return this.hostContext.injectMapData(dataBinding);
  }

  @Override
  public  SpatialDataBinding openSpatialData(Value name, Z2Form shapeForm) {
    return this.hostContext.openSpatialData(name, shapeForm);
  }

  @Override
  public  SpatialDataBinding injectSpatialData(SpatialDataBinding dataBinding) {
    return this.hostContext.injectSpatialData(dataBinding);
  }

  @Override
  public ValueDataBinding openValueData(Value name) {
    return this.hostContext.openValueData(name);
  }

  @Override
  public ValueDataBinding injectValueData(ValueDataBinding dataBinding) {
    return this.hostContext.injectValueData(dataBinding);
  }

  @Override
  public LinkBinding bindDownlink(Downlink downlink) {
    return this.hostContext.bindDownlink(downlink);
  }

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

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

  @Override
  public void httpDownlink(HttpBinding http) {
    this.hostContext.httpDownlink(http);
  }

  @Override
  public void pushDown(PushRequest pushRequest) {
    this.hostContext.pushDown(pushRequest);
  }

  @Override
  public void openUplink(LinkBinding link) {
    final NodeBinding nodeBinding = openNode(link.nodeUri());
    if (nodeBinding != null) {
      nodeBinding.openUplink(link);
    } else {
      final ErrorUplinkModem linkContext = new ErrorUplinkModem(link, Record.of().attr("nodeNotFound"));
      link.setLinkContext(linkContext);
      linkContext.cueDown();
    }
  }

  @Override
  public void httpUplink(HttpBinding http) {
    final NodeBinding nodeBinding = openNode(http.nodeUri());
    if (nodeBinding != null) {
      nodeBinding.httpUplink(http);
    } else {
      final HttpErrorUplinkModem httpContext = new HttpErrorUplinkModem(http);
      http.setHttpContext(httpContext);
    }
  }

  @Override
  public void pushUp(PushRequest pushRequest) {
    final NodeBinding nodeBinding = openNode(pushRequest.envelope().nodeUri());
    if (nodeBinding != null) {
      nodeBinding.pushUp(pushRequest);
    } else {
      pushRequest.didDecline();
    }
  }

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

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

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

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

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

  @Override
  protected void willOpen() {
    super.willOpen();
    final Iterator nodesIterator = this.nodes.valueIterator();
    while (nodesIterator.hasNext()) {
      nodesIterator.next().open();
    }
  }

  @Override
  protected void willLoad() {
    super.willLoad();
    final Iterator nodesIterator = this.nodes.valueIterator();
    while (nodesIterator.hasNext()) {
      nodesIterator.next().load();
    }
  }

  @Override
  protected void willStart() {
    super.willStart();
    final Iterator nodesIterator = this.nodes.valueIterator();
    while (nodesIterator.hasNext()) {
      nodesIterator.next().start();
    }
  }

  @Override
  protected void willStop() {
    super.willStop();
    final Iterator nodesIterator = this.nodes.valueIterator();
    while (nodesIterator.hasNext()) {
      nodesIterator.next().stop();
    }
  }

  @Override
  protected void willUnload() {
    super.willUnload();
    final Iterator nodesIterator = this.nodes.valueIterator();
    while (nodesIterator.hasNext()) {
      nodesIterator.next().unload();
    }
  }

  @Override
  protected void willClose() {
    super.willClose();
    final Iterator nodesIterator = this.nodes.valueIterator();
    while (nodesIterator.hasNext()) {
      nodesIterator.next().close();
    }
  }

  @Override
  public void didClose() {
    // nop
  }

  @Override
  public void didFail(Throwable error) {
    error.printStackTrace();
  }

  static final int PRIMARY = 1 << 0;
  static final int REPLICA = 1 << 1;
  static final int MASTER = 1 << 2;
  static final int SLAVE = 1 << 3;

  @SuppressWarnings("unchecked")
  static final AtomicReferenceFieldUpdater> NODES =
      AtomicReferenceFieldUpdater.newUpdater(HostTable.class, (Class>) (Class) HashTrieMap.class, "nodes");

  static final AtomicIntegerFieldUpdater FLAGS =
      AtomicIntegerFieldUpdater.newUpdater(HostTable.class, "flags");
}