swimos / swim

Full stack application platform for building stateful microservices, streaming APIs, and real-time UIs
https://www.swimos.org
Apache License 2.0
489 stars 39 forks source link

Integer ValueLane can be set with Text. #40

Open DobromirM opened 4 years ago

DobromirM commented 4 years ago

It is possible to set an Integer Value Lane ValueLane<Integer> with a Text object. The lane will work fine in terms of setting the value and sending it to all downlinks, however the new and old value for .didSet() method will always be zero (the default).

DobromirM commented 4 years ago

Example:

Plane.java

public class Plane extends AbstractPlane {

  @SwimRoute ("/agent/:id")
  AgentRoute<Agent> agent;

  public static void main(String[] args) {
    final Kernel kernel = ServerLoader.loadServer();
    final Fabric fabric = (Fabric) kernel.getSpace("basic");

    kernel.start();
    System.out.println("Running Basic server...");
    kernel.run();

    fabric.command("/agent/1", "wakeup", Value.absent());
  }
}

Agent.java

public class Agent extends AbstractAgent {

  @SwimLane ("age")
  ValueLane<Integer> age = this.<Integer>valueLane().didSet((newValue, oldValue) -> {
    System.out.println("`age` set to {" + newValue + "} from {" + oldValue + "}");
  });
}

Client.java

class Client {

    public static void main(String[] args) throws InterruptedException {
        ClientRuntime swimClient = new ClientRuntime();
        swimClient.start();
        final String hostUri = "warp://localhost:9001";
        final String nodeUri = "/agent/1";
        final ValueDownlink<Value> link = swimClient.downlinkValue().hostUri(hostUri).nodeUri(nodeUri).laneUri("age").didSet((newValue, oldValue) -> {
            System.out.println("link watched info change TO " + newValue + " FROM " + oldValue);
        }).open();

        Thread.sleep(2000);
        link.set(Text.from("twenty"));
        Thread.sleep(2000);
        System.out.println(link.get());
        Thread.sleep(2000);
        System.out.println("Will shut down client in 2 seconds");
        swimClient.stop();
    }
}

Client Output

link watched info change TO Value.absent() FROM Value.absent()
link watched info change TO Text.from("twenty") FROM Value.absent()
link watched info change TO Text.from("twenty") FROM Text.from("twenty")
Text.from("twenty")

Server Output

'age' set to {0} from {0}
brohitbrose commented 4 years ago

Good find. As I mentioned in Slack, the server output is especially disturbing.

Regarding client-side, see here for something similar. Lane versioning is something that has come up recently for different reasons, so this may be solved soon after all. Will update after further discussion.

ajay-gov commented 4 years ago

This is the code block responsible for this behavior:

The valueForm.cast(this.newValue) will return null since the valueForm in this case is the IntegerForm and it tries to cast a Text value to an Integer. Since it returns a null it defaults to valueForm.unit() which is 0 for the IntegerForm.

In general in these kinds of scenarios the return value is the form's unit value.

In terms of fixing it, we can use the unit value of the form when the cast fails. However, should we ignore this error silently or should we send an error back to the client with a failure? The latter will involve adding another callback function to the downlink lifecycle.