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

swim.runtime.agent.AgentModel 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.agent;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import swim.api.SwimContext;
import swim.api.agent.Agent;
import swim.api.agent.AgentFactory;
import swim.api.agent.AgentType;
import swim.concurrent.Conts;
import swim.runtime.LaneBinding;
import swim.runtime.NodeContext;
import swim.runtime.PushRequest;
import swim.structure.Text;
import swim.structure.Value;

public class AgentModel extends AgentNode {
  protected final AgentType agentType;

  protected final Value props;

  volatile Object views; // AgentView | AgentView[]

  public AgentModel(AgentType agentType, Value props) {
    this.agentType = agentType;
    this.props = props.commit();
  }

  @Override
  public void setNodeContext(NodeContext nodeContext) {
    super.setNodeContext(nodeContext);
    addAgentView(materializeAgent(this.agentType, this.props));
  }

  @SuppressWarnings("unchecked")
  @Override
  public  T unwrapNode(Class nodeClass) {
    if (nodeClass.isAssignableFrom(getClass())) {
      return (T) this;
    } else {
      final Object views = this.views;
      if (views instanceof AgentView) {
        return ((AgentView) views).unwrapNode(nodeClass);
      } else if (views instanceof AgentView[]) {
        final AgentView[] viewArray = (AgentView[]) views;
        for (int i = 0, n = viewArray.length; i < n; i += 1) {
          final T node = viewArray[i].unwrapNode(nodeClass);
          if (node != null) {
            return node;
          }
        }
      }
      return null;
    }
  }

  @Override
  public Value agentKey() {
    final String agentName = this.agentType.name();
    if (agentName != null) {
      return Text.from(agentName);
    } else {
      return Value.absent();
    }
  }

  public Value props() {
    return this.props;
  }

  public AgentType agentType() {
    return this.agentType;
  }

  public AgentView materializeAgent(AgentFactory agentFactory, Value props) {
    final AgentView view = new AgentView(this, props);
    SwimContext.setAgentContext(view);
    final Agent agent = agentFactory.createAgent(view);
    SwimContext.clear();
    view.setAgent(agent);
    return view;
  }

  public AgentView getAgentView(Value props) {
    final Object views = this.views;
    AgentView view;
    if (views instanceof AgentView) {
      view = (AgentView) views;
      if (props.equals(view.props)) {
        return view;
      }
    } else if (views instanceof AgentView[]) {
      final AgentView[] viewArray = (AgentView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        view = viewArray[i];
        if (props.equals(view.props)) {
          return view;
        }
      }
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  public  S getTrait(Class agentClass) {
    Agent agent;
    if (views instanceof AgentView) {
      agent = ((AgentView) views).agent;
      if (agentClass.isInstance(agent)) {
        return (S) agent;
      }
    } else if (views instanceof AgentView[]) {
      final AgentView[] viewArray = (AgentView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        agent = viewArray[i].agent;
        if (agentClass.isInstance(agent)) {
          return (S) agent;
        }
      }
    }
    return null;
  }

  public AgentView addAgentView(AgentView view) {
    final Value props = view.props();
    Object oldViews;
    Object newViews;
    AgentView oldView;
    do {
      oldViews = this.views;
      if (oldViews instanceof AgentView) {
        oldView = (AgentView) oldViews;
        if (props.equals(oldView.props())) {
          return oldView;
        } else {
          newViews = new AgentView[]{oldView, view};
        }
      } else if (oldViews instanceof AgentView[]) {
        final AgentView[] oldViewArray = (AgentView[]) oldViews;
        final int n = oldViewArray.length;
        final AgentView[] newViewArray = new AgentView[n + 1];
        for (int i = 0; i < n; i += 1) {
          oldView = oldViewArray[i];
          if (props.equals(oldView.props())) {
            return oldView;
          }
          newViewArray[i] = oldViewArray[i];
        }
        newViewArray[n] = view;
        newViews = newViewArray;
      } else {
        newViews = view;
      }
    } while (!VIEWS.compareAndSet(this, oldViews, newViews));
    activate(view);
    didAddAgentView(view);
    return view;
  }

  @SuppressWarnings("unchecked")
  public  S addTrait(Value props, AgentFactory agentFactory) {
    Object oldViews;
    Object newViews;
    AgentView oldView;
    AgentView view = null;
    do {
      oldViews = this.views;
      if (oldViews instanceof AgentView) {
        oldView = (AgentView) oldViews;
        if (props.equals(oldView.props())) {
          return (S) oldView.agent;
        } else {
          if (view == null) {
            view = materializeAgent(agentFactory, props);
          }
          newViews = new AgentView[]{oldView, view};
        }
      } else if (oldViews instanceof AgentView[]) {
        final AgentView[] oldViewArray = (AgentView[]) oldViews;
        final int n = oldViewArray.length;
        final AgentView[] newViewArray = new AgentView[n + 1];
        for (int i = 0; i < n; i += 1) {
          oldView = oldViewArray[i];
          if (props.equals(oldView.props())) {
            return (S) oldView.agent;
          }
          newViewArray[i] = oldViewArray[i];
        }
        if (view == null) {
          view = materializeAgent(agentFactory, props);
        }
        newViewArray[n] = view;
        newViews = newViewArray;
      } else {
        if (view == null) {
          view = materializeAgent(agentFactory, props);
        }
        newViews = view;
      }
    } while (!VIEWS.compareAndSet(this, oldViews, newViews));
    activate(view);
    didAddAgentView(view);
    return (S) view.agent;
  }

  public void removeAgentView(AgentView view) {
    Object oldViews;
    Object newViews;
    do {
      oldViews = this.views;
      if (oldViews instanceof AgentView) {
        if (oldViews == view) {
          newViews = null;
          continue;
        }
      } else if (oldViews instanceof AgentView[]) {
        final AgentView[] oldViewArray = (AgentView[]) 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 AgentView[] newViewArray = new AgentView[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) {
      didRemoveAgentView(view);
    }
  }

  @Override
  public void pushUp(PushRequest pushRequest) {
    execute(new AgentModelPushUp(this, pushRequest));
  }

  protected void didAddAgentView(AgentView view) {
    // stub
  }

  protected void didRemoveAgentView(AgentView view) {
    // stub
  }

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

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

  @Override
  protected void willLoad() {
    super.willLoad();
    execute(new AgentModelWillLoad(this));
  }

  @Override
  protected void didLoad() {
    super.didLoad();
    execute(new AgentModelDidLoad(this));
  }

  @Override
  protected void willStart() {
    super.willStart();
    execute(new AgentModelWillStart(this));
  }

  @Override
  protected void didStart() {
    super.didStart();
    execute(new AgentModelDidStart(this));
  }

  @Override
  protected void willStop() {
    super.willStop();
    execute(new AgentModelWillStop(this));
  }

  @Override
  protected void didStop() {
    super.didStop();
    execute(new AgentModelDidStop(this));
  }

  @Override
  protected void willUnload() {
    super.willUnload();
    execute(new AgentModelWillUnload(this));
  }

  @Override
  protected void didUnload() {
    super.didUnload();
    execute(new AgentModelDidUnload(this));
  }

  @Override
  public void willClose() {
    super.willClose();
    execute(new AgentModelWillClose(this));
  }

  @Override
  public void didClose() {
    super.didClose();
    execute(new AgentModelDidClose(this));
  }

  @Override
  public void didFail(Throwable error) {
    super.didFail(error);
    final Object views = this.views;
    if (views instanceof AgentView) {
      try {
        ((AgentView) views).didFail(error);
      } catch (Throwable cause) {
        if (Conts.isNonFatal(cause)) {
          cause.printStackTrace();
        } else {
          throw cause;
        }
      }
    } else if (views instanceof AgentView[]) {
      final AgentView[] viewArray = (AgentView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        try {
          viewArray[i].didFail(error);
        } catch (Throwable cause) {
          if (Conts.isNonFatal(cause)) {
            cause.printStackTrace();
          } else {
            throw cause;
          }
        }
      }
    }
  }

  static final AtomicReferenceFieldUpdater VIEWS =
      AtomicReferenceFieldUpdater.newUpdater(AgentModel.class, Object.class, "views");
}

final class AgentModelPushUp implements Runnable {
  final AgentNode node;
  final PushRequest pushRequest;

  AgentModelPushUp(AgentNode node, PushRequest pushRequest) {
    this.node = node;
    this.pushRequest = pushRequest;
  }

  @Override
  public void run() {
    try {
      final LaneBinding laneBinding = this.node.getLane(this.pushRequest.envelope().laneUri());
      if (laneBinding != null) {
        laneBinding.pushUp(this.pushRequest);
      } else {
        this.pushRequest.didDecline();
      }
    } catch (Throwable error) {
      if (Conts.isNonFatal(error)) {
        this.node.didFail(error);
      } else {
        throw error;
      }
    }
  }
}

abstract class AgentModelCallback implements Runnable {
  final AgentModel model;

  AgentModelCallback(AgentModel model) {
    this.model = model;
  }

  @Override
  public final void run() {
    final Object views = this.model.views;
    if (views instanceof AgentView) {
      try {
        call((AgentView) views);
      } catch (Throwable error) {
        if (Conts.isNonFatal(error)) {
          ((AgentView) views).didFail(error);
        } else {
          throw error;
        }
      }
    } else if (views instanceof AgentView[]) {
      final AgentView[] viewArray = (AgentView[]) views;
      for (int i = 0, n = viewArray.length; i < n; i += 1) {
        try {
          call(viewArray[i]);
        } catch (Throwable error) {
          if (Conts.isNonFatal(error)) {
            viewArray[i].didFail(error);
          } else {
            throw error;
          }
        }
      }
    }
  }

  abstract void call(AgentView view);
}

final class AgentModelWillLoad extends AgentModelCallback {
  AgentModelWillLoad(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.willLoad();
  }
}

final class AgentModelDidLoad extends AgentModelCallback {
  AgentModelDidLoad(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.didLoad();
  }
}

final class AgentModelWillStart extends AgentModelCallback {
  AgentModelWillStart(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.willStart();
  }
}

final class AgentModelDidStart extends AgentModelCallback {
  AgentModelDidStart(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.didStart();
  }
}

final class AgentModelWillStop extends AgentModelCallback {
  AgentModelWillStop(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.willStop();
  }
}

final class AgentModelDidStop extends AgentModelCallback {
  AgentModelDidStop(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.didStop();
  }
}

final class AgentModelWillUnload extends AgentModelCallback {
  AgentModelWillUnload(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.willUnload();
  }
}

final class AgentModelDidUnload extends AgentModelCallback {
  AgentModelDidUnload(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.didUnload();
  }
}

final class AgentModelWillClose extends AgentModelCallback {
  AgentModelWillClose(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.willClose();
  }
}

final class AgentModelDidClose extends AgentModelCallback {
  AgentModelDidClose(AgentModel model) {
    super(model);
  }

  @Override
  void call(AgentView view) {
    view.didClose();
  }
}