Coder Social home page Coder Social logo

swimos / swim Goto Github PK

View Code? Open in Web Editor NEW
479.0 31.0 41.0 23.38 MB

Full stack application platform for building stateful microservices, streaming APIs, and real-time UIs

Home Page: https://www.swimos.org

License: Apache License 2.0

HTML 2.78% Java 58.78% TypeScript 37.68% JavaScript 0.76%
stateful distributed-systems asynchronous-programming non-blocking-io websockets streaming-data actor-model microservices-architecture streaming-api decentralized-applications serverless serverless-framework real-time web-agent

swim's Introduction

SwimOS version license

SwimOS is a full stack application platform for building stateful web services, streaming APIs, and real-time UIs.

  • Stateful backend: Build web service endpoints that continue to exist in-between operations. For every unique URI, run a lightweight, long-lived, general purpose compute process, called a Web Agent. Dynamically link Web Agents together to continuously synchronize their state. And use multiplexed streaming APIs to stream real-time changes to User Agents.
  • Real-time frontend: Create live web interfaces that continuously synchronize application views with real-timed shared Web Agent state. Dynamically stream only what's necessary to update visible views. And efficiently render massive amounts of rapidly changing data with a UI toolkit built like a game engine.
  • Vertically integrated: Built from first principles, the backend and frontend runtimes have zero transitive dependencies, and occupy only a couple megabytes on disk. The complete backend stack runs in a single OS process per server. And it's been proven at scale with hundreds of millions of concurrently running Web Agents handling millions of messages per second with millisecond latency.

Getting Started

Check out the SwimOS tutorials to get started building stateful web services with streaming APIs and real-time UIs.

Documentation

Learn more about streaming web services and real-time web applications on the SwimOS website.

Contributing

Read the Contributing Guide to learn how to contribute to the SwimOS project.

Code of Conduct

Help keep SwimOS open and inclusive to all by reading and following our Code of Conduct.

License

Licensed under the Apache 2.0 License.

swim's People

Contributors

afolson avatar ajay-gov avatar brohitbrose avatar c9r avatar dobromirm avatar horned-sphere avatar jcustenborder avatar johnson-brad avatar piggottdev avatar sircipher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swim's Issues

Join Value Lane didUpdate/didReceive firing multiple times

The JoinValueLaneSpec#testLinkToJoinValueLane test in swim-system-java is currently failing inconsistently due to an issue with the didUpdate/didReceive phase being dispatched multiple times. After an unpredictable number of test runs, the test fails due to the latches not counting down enough times.

Output snippet when the test is passing:

join link didUpdate key: "x"; newValue: "x0"; oldValue: ""
join link didReceive body: @update(key:x) x0
join link didUpdate key: "y"; newValue: "y0"; oldValue: ""
join link didReceive body: @update(key:y) y0

join link didUpdate key: "x"; newValue: "x0"; oldValue: ""
join link didReceive body: @update(key:x) x0
join link didUpdate key: "y"; newValue: "y0"; oldValue: ""
join link didReceive body: @update(key:y) y0

When it is failing:

join link didUpdate key: "y"; newValue: "y0"; oldValue: ""
join link didReceive body: @update(key:y) y0
join link didUpdate key: "x"; newValue: "x0"; oldValue: ""
join link didReceive body: @update(key:x) x0

This issue does not present itself every time the test is run. To reproduce it, run the tests back-to-back.

Join Map Lane creates duplicate didUpdate callbacks

If a Join Map Lane is created with a didUpdate callback and another one is added in the didStart method of the agent, the first one is duplicated.

Example:

BasicPlane.java

public class BasicPlane extends AbstractPlane {

  @SwimRoute("/unit/:id")
  AgentRoute<UnitAgent> unitAgentType;

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

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

    space.command("/unit/foo", "wakeup", Value.absent());
  }
}

UnitAgent.java

public class UnitAgent extends AbstractAgent {

  @SwimLane("shoppingCart")
  MapLane<String, Integer> shoppingCart = this.<String, Integer>mapLane();

  @SwimLane("addItem")
  CommandLane<String> publish = this.<String>commandLane()
      .onCommand(msg -> {
        final int n = this.shoppingCart.getOrDefault(msg, 0) + 1;
        this.shoppingCart.put(msg, n);
      });

    @SwimLane("join")
    JoinMapLane<String, String, Integer> stateStreetStats = this.<String, String, Integer>joinMapLane().didUpdate((key, newValue, oldValue) -> {
        System.out.println("join map lane: " + key + " count changed to " + newValue + " from " + oldValue);
    });

    @Override
    public void didStart() {
        stateStreetStats.downlink("foo").hostUri("warp://127.0.0.1:9001").nodeUri("/unit/foo").laneUri("shoppingCart").open()
        .didUpdate((key, newValue, oldValue) -> {
            System.out.println("join map lane agent didStart: " + key + " count changed to " + newValue + " from " + oldValue);
        });
    }
}

Input

@command(node:"/unit/foo", lane:"addItem")"foo"

Output

join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1

(The last line should not be present)

Downlinks should be able to provide the current value of a lane on sync

If a downlink to a lane is created, the value is available through the get() method, only after sync has been completed. There should be an overloaded didSync() method, that has the value of the lane as a parameter, similar to how didSet() provides both the new and the old value of a lane.

Example:

final ValueDownlink<Value> link = swimClient.downlinkMap()
                                            .hostUri(hostUri)
                                            .nodeUri(buildingUri)
                                            .laneUri("lights")
                                            .didSync((value) -> {
                                              System.out.println("Current value:" + value);
                                            })
                                            .open();

Receiving a close frame without a body raises an exception in the swim decoder.

Problem:
The swim server raises a decoder exception if it receives a close frame without a body.
According to the WebSocket protocol specification, the body is optional:

The Close frame MAY contain a body


Steps to reproduce:

  1. Start a swim server.
  2. Open a WS connection to the server.
  3. Close the connection by sending a close message without a body.

Exception:

swim.codec.DecoderException: incomplete
	at swim.ws.WsStatusDecoder.decode(WsStatusDecoder.java:66)
	at swim.ws.WsStatusDecoder.feed(WsStatusDecoder.java:39)
	at swim.ws.WsFrameDecoder.decode(WsFrameDecoder.java:132)
	at swim.ws.WsFrameDecoder.decode(WsFrameDecoder.java:179)
	at swim.ws.WsDecoder.decodeCloseFrame(WsDecoder.java:115)
	at swim.ws.WsDecoder.decodeFrame(WsDecoder.java:94)
	at swim.ws.WsOpcodeDecoder.decode(WsOpcodeDecoder.java:38)
	at swim.ws.WsOpcodeDecoder.feed(WsOpcodeDecoder.java:32)
	at swim.io.IpSocketModem.doRead(IpSocketModem.java:164)
	at swim.io.TcpSocket.doRead(TcpSocket.java:230)
	at swim.io.StationTransport.doRead(Station.java:579)
	at swim.io.StationReader.runTask(Station.java:796)
	at swim.concurrent.TheaterTask.run(Theater.java:431)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

HTTP Routing Directives

swim.web contains a simple HTTP routing interface in WebRoute.java. The swim.web.route package contains various WebRoute implementations, such as PathPrefixRoute and DirectoryRoute.

A WebRoute takes a WebRequest and returns a WebResponse. A WebResponse is a thin wrapper around a swim.io.http.HttpResponder. A WebRequest wraps a swim.http.HttpRequest, and provides convenience methods for constructing WebResponses. A WebRequest also contains a routePath, which is the UriPath against which path-selective WebRoutes should match. Path routes may consume and otherwise transform a WebRequest's routePath.

So a route is a function that transforms WebRequests into WebResponses. A routing directive, on the other hand, refers to any function that returns other WebRoutes. The only real routing directive implemented at the moment is the WebRoute.orElse method, which returns instances of AlternativeRoute.

Background

This approach to HTTP routing is inspired by Spray, which subsequently became Akka HTTP. See Akka's Routes and Directives documentation, along with their list of predefined directives. We'd like swim.web to have similar routing capabilities to Akka HTTP, but with a focus on cleanliness and simplicity. Spray and Akka HTTP make elegant use of Scala implicits and higher kinded types to implement a concise routing DSL. We have neither implicits nor HKTs at our disposal, so we'll have to make due with higher order methods on the WebRoute interface, which should be just fine given our focus.

Design

We will want to organize our routing directives into a set of *Directive interfaces, each containing default methods for a focussed set of routing tasks, which WebRoute will inherit. It's important that WebRoute remains a @FunctionalInterface, so routeRequest must remain WebRoute's sole abstract method. In general, each directive will consist of a WebRoute subclass in the swim.web.route package that implements the routing logic, along with a combinator function in a *Directive interface which returns new instances of the WebRoute subclass parameterized by the arguments to the higher order directive function.

Route chaining should be achieved via higher order directive methods on WebRoute that take combinator functions as arguments. Combinator functions should generally take no arguments, unless the combinator extracts an element of the request, in which case the combinator should be a function with the extracted value as its argument. Combinator functions should return a WebRoute, to which the directive will delegate after evaluating the closure.

Implementation

Path Directives

  • Add swim.web.route.PathDirectives interface, extended by WebRoute.
  • Add a pathPrefix combinator function that takes a literal UriPath, and a constant WebRoute as its arguments, and returns an instance of PathPrefixRoute.
  • Implement swim.web.route.PathPrefixDirective, analogous to PathPrefixRoute, but which takes a java.util.function.Supplier<WebRoute> as its then argument, instead of a constant WebRoute.
  • Add a pathPrefix overload that takes a literal UriPath, and a Supplier<WebRoute>, as its arguments, and returns an instance of PathPrefixDirective (statically return typed as a WebRoute).
  • Implement PathRoute and PathDirective, analogous to PathPrefixRoute and PathPrefixDirective, that match the entire routePath, rather than its prefix.
  • Add path combinator methods to PathDirectives.
  • Add a pathEnd combinator method to PathDirectives, which should be shorthand for path(UriPath.empty()).

Method Directives

  • Add swim.web.route.MethodDirectives interface, extended by WebRoute.
  • Implement MethodDirective, parameterized by a swim.http.HttpMethod and Supplier<WebRoute>, which evaluates and delegates to its then closure if the WebRequest's method equals the parameterized method.
  • Add a method(HttpMethod, Supplier<WebRoute>) combinator to MethodDirectives, along with options, get, head, post, put, patch, and delete convenience methods.

Header Directives

  • Add swim.web.route.HeaderDirectives interface, extended by WebRoute.
  • TBD: define header directives

Host Directives

  • Add swim.web.route.HostDirectives interface, extended by WebRoute.
  • TBD: define host directives

More advanced routing directives will be addressed in separate issues.

Fast updates on ValueDownLink via runtime client are not send?

I created a setup in which I have a Agent with a swim lane for a double and a Java client which updates it from 0 to 1000.

Client

value = client.downlinkValue()
        .valueForm(Form.forDouble())
        .keepSynced(true)
        .keepLinked(true)
        .hostUri("warp://localhost:9001").nodeUri("/Input/" + uuid).laneUri("value");
value.open();
for (double i=0; i<1000; i++) {
    value.set(i);
    //Thread.sleep(100)
}
value.close();

On the ValueLane I have added a didSet handler that outputs the value to the console.

public class Input extends AbstractAgent {

    @SwimLane("value")
    ValueLane<Double> value = this.<Double>valueLane()
            .didSet((newValue, oldValue) -> {
                System.out.println("observed info change to " + newValue + " from " + oldValue);
            });

}

When I run the client without delay between the setting of the double value, only the last value change is printed (observed info change to 999.0 from 0.0). When I put a thread sleep of 100 between the set all changes to the value are printed.

GoogleIdAuthenticator is not working

GoogleIdAuthenticator is not working. Issues to be fixed

Current value for map downlinks from a plane is always absent

If a map downlink is created from a PlaneContext the current value on didUpdate is always Value.absent().

Example:

BasicPlane.java

public class BasicPlane extends AbstractPlane {

  @SwimRoute("/unit/:id")
  AgentRoute<UnitAgent> unitAgentType;

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

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

    space.command("/unit/foo", "wakeup", Value.absent());
  }

  @Override
  public void didStart() {
    super.didStart();

    //Plane downlink
    context.downlinkMap()
            .keyForm(Form.forString()).valueForm(Form.forInteger())
            .nodeUri("/unit/foo").laneUri("shoppingCart")
            .didUpdate((key, newValue, oldValue) -> {
              System.out.println("plane: " + key + " count changed to " + newValue + " from " + oldValue);
            })
            .open();
  }
}

UnitAgent.java

public class UnitAgent extends AbstractAgent {

  @SwimLane("shoppingCart")
  MapLane<String, Integer> shoppingCart = this.<String, Integer>mapLane()
      .didUpdate((key, newValue, oldValue) -> {
          System.out.println("agent: " + key + " count changed to " + newValue + " from " + oldValue);
      });

  @SwimLane("addItem")
  CommandLane<String> publish = this.<String>commandLane()
      .onCommand(msg -> {
        final int n = this.shoppingCart.getOrDefault(msg, 0) + 1;
        this.shoppingCart.put(msg, n);
      });

}

Input:

@command(node:"/unit/foo", lane:"addItem")"foo"
@command(node:"/unit/foo", lane:"addItem")"foo"
@command(node:"/unit/foo", lane:"addItem")"foo"

Output:

agent: foo count changed to 1 from 0
plane: foo count changed to 1 from 0
agent: foo count changed to 2 from 1
plane: foo count changed to 2 from 0
agent: foo count changed to 3 from 2
plane: foo count changed to 3 from 0

(The plane downlink messages should match the agent downlink messages)

swim.concurrent.SyncSpec await timeout ends too soon

The test awaitTimeout is occasionally ending too soon.

swim.concurrent.SyncSpec > awaitTimeout FAILED
    java.lang.AssertionError: long too soon: dt: 402276200 expected [true] but found [false]
        at org.testng.Assert.fail(Assert.java:96)
        at org.testng.Assert.failNotEquals(Assert.java:776)
        at org.testng.Assert.assertTrue(Assert.java:44)
        at swim.concurrent.SyncSpec.awaitTimeout(SyncSpec.java:62)

This was moved over to using System.nanoTime() like the underlying await method is using. This did appear to reduce the frequency of this failure, however, it is still present.

Reference failure: https://dev.azure.com/SwimAi/swim-system-java/_build/results?buildId=58 jdk8_windows_s2012#720.

Https client requests to some sites fail

TLS stack needs review.
Tried the AbstractHttpClient with four different https URLS and different jdks and here are the results:

Jdk1.8
https://github.com/swimos - Works
https://www.swimos.org- javax.net.ssl.SSLException: Unsupported record version Unknown-0.0
https://www.googleapis.com/oauth2/v3/certs- Connection closed after TLS handshake completed
https://accounts.google.com/.well-known/openid-configuration- Works

Jdk9 and 9+
https://github.com/swimos - Works
https://www.swimos.org- javax.net.ssl.SSLException: Unrecognized record version (D)TLS-0.0
https://www.googleapis.com/oauth2/v3/certs- javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
https://accounts.google.com/.well-known/openid-configuration- javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

HTTP Downlinks

HTTP downlinks simplify the common pattern of polling HTTP resources and treating the sequence of responses as a stream of events. When used to update lanes, HTTP downlinks have the effect of turning stateless REST APIs into multiplexed streaming WARP APIs.

Background

This is important because the load induced by polling is proportional to the rate at which you poll, whereas the load on streaming APIs is proportional to how often the data actually changes. In the limit, this factor makes streaming APIs infinitely more efficient than polled APIs for real-time data, because you would have to poll infinitely fast in order to detect changes with arbitrarily low latency. Note that message brokers don't solve the general case of the problem either because they absorb backpressure signals, which inevitably leads to buffer bloat.

By transforming polled APIs into multiplexed streaming APIs, the cost of polling can be paid just once by the HTTP downlink. The data can then be cheaply and ubiquitously streamed using much more efficient WARP APIs. Of course, the latency of the WARP APIs will still be limited by the polling rate of the HTTP downlink. Nonetheless, HTTP downlinks provide a critical stepping stone for making more data available as multiplexed streaming APIs. The availability of these bridged APIs will hopefully help drive more data sources to expose native streaming APIs so that the cost and latency hit of polling can be entirely avoided.

Design

As the name implies, HTTP downlinks are intended to behave like WARP downlinks. Just as you can open a WARP downlink to a WARP lane and start receiving events, so too should you be able to open an HTTP downlink to an HTTP resource and start receiving responses. Unlike a WARP downlink, an HTTP downlink will have to be configured with the rate at which to poll. HTTP downlinks should poll automatically, and their polling rate should be modifiable at any time to support adaptive polling.

HTTP downlinks will build on the swim.http/swim.io.http libraries, which fully support HTTP pipelining and chunking. Chunked HTTP responses can be incrementally consumed, if desired, by passing a custom swim.codec.Decoder to the HTTP downlink. We could add a decodeChunks flag to decode each response chunk as a distinct event, which would facilitate compatibility with existing ad hoc HTTP response streams, like Twitter's streaming APIs. But chunked responses are more accurately modeled with custom HTTP entity decoders, since they are, in fact, HTTP entities; and swim.http already models HTTP entities as backpressure-regulated streams.

The HttpDownlink API will live in the swim.api.http package, alongside the HttpLane interface. Both HttpDownlink and HttpLane will share many of the same callback function interfaces.

The HTTP downlink runtime will reuse the HttpBinding and HttpContext runtime interfaces used by HTTP lanes. HttpBinding extends LinkBinding, and HttpContext extends LinkContext, enabling us to route HTTP links through the Swim Kernel just like WARP links.

Implementation

The biggest holdup for completing HTTP downlinks has been answering the question: "who should be responsible for doing the actual polling?" HTTP downlinks could just open their own HTTP connections, but this would be inefficient since it doesn't make use of any connection pooling. We could use per-agent connection pools, but HTTP downlinks should be useable outside of Web Agents.

We've solved this problem before for WARP links. We make a RemoteHost responsible for transporting WARP links over multiplexed network connections. Why not do the same for HTTP? It's a bit counter-intuitive at first to treat "remote" HTTP traffic specially, since unlike WARP, you'd expect HTTP traffic to always be remote. But doing so cleanly and flexibly solves a whole bunch of interrelated problems in one fell swoop.

Adding a RemoteHttpHost does a number of things. First, it gives us a distinct Swim Kernel execution cell to take responsibility for initiating HTTP requests. Second, it integrates HTTP connections into the Swim Kernel's host model, giving the Swim Kernel a more holistic picture of network connections. Third, it lets us generalize our host configuration and policy mechanisms—initially intended for WARP hosts—to HTTP hosts (and eventually other host types). And fourth, it inadvertently solves the seemingly unrelated problem of natively routing HTTP requests through fabrics.

Although we're going to treat HTTP hosts like WARP hosts, the actual RemoteHttpHostClient implementation will be rather different from RemoteWarpHost and RemoteWarpHostClient. For one, a WARP host manages a single connection, and doesn't particularly care whether that connection was opened by a client, or accepted by a server. Whereas HTTP client host needs to manage a connection pool to the remote endpoint. A hypothetical RemoteHttpHost, by contrast, would manage a single HTTP server connection. Combined with the fact that a RemoteHttpHostClient sends request and receives responses, while a RemoteHttpHost would receive requests and send responses, there's unlikely to be much commonality between RemoteHttpHostClient and a potential future RemoteHttpHost. We may eventually decide to implement a RemoteHttpHost to uniformly model server-side HTTP connections opened by swim.service.web. But we leave that task for another day.

Where possible, we will want to multicast HTTP downlink responses so that if multiple agents downlink to the same REST API, it will only be polled once. This can only happen if multiple HTTP downlinks wish to make compatible requests. If multiple compatible HTTP downlinks specify different polling intervals, the fastest interval should be chosen. The HTTP downlink implementation should follow the same model-view pattern used by WARP downlinks.

Tasks

Interfaces

  • Add HttpDownlink to swim.api.http.
  • Implement HttpBinding as an extension of LinkBinding in swim.runtime.
  • Implement HttpContext as an extension of LinkContext in swim.runtime.
  • Implement HttpProxy as a passthrough implementation of HttpBinding and HttpContext.

Remoting

  • Rename RemoteHost to RemoteWarpHost in swim.remote.
  • Rename RemoteHostClient to RemoteWarpHostClient.
  • Add new abstract RemoteHost base class; refactor HostBinding boilerplate out of RemoteWarpHost and into RemoteHost.
  • Add RemoteHttpHostClient, extending RemoteHost.
  • Implement RemoteHttpHostClient.openUplink, dispatching to an internal openHttpUplink method if the link is an instance of HttpBinding, and rejecting the link otherwise—analogous to RemoteWarpHostClient.openUplink.
  • Implement RemoteHttpHostClient.openHttpUplink. Enqueueing requests in the pipeline of an available connection using an HttpRequester. Start by maintaining and reusing a single HTTP connection, analogous to RemoteWarpHostClient.
  • Implement a RemoteHttpRequester subclass of AbstractHttpRequester to handle making requests on behalf of a RemoteHttpHostClient—vaguely analogous to RemoteWarpDownlink.
  • Implement RemoteHttpHostClient.openHttpUplink. Pick a best available connection from the host's connection pool, opening a new connection as needed and permitted, and enqueue the request with the connection's pipeline using an HttpRequester.
  • Modify openHttpUplink to dispatch requests to a connection pool. The connection pool itself should be implemented in swim.io.http. TODO: create a separate issue to implement swim.io.http.HttpClientPool.

Plumbing

  • Implement an abstract HttpDownlinkView in swim.runtime.http, extending DownlinkView, and implementing HttpDownlink, analogous to WarpDownlinkView.
  • Implement an abstract HttpDownlinkModem in swim.runtime.http, parameterized by a DownlinkView type, extending DownlinkModel, and implementing HttpBinding, analogous to—but much simpler than—WarpDownlinkModem; and similar to HttpUplinkModem.
  • Implement an abstract HttpDownlinkModel in swim.runtime.http, parameterized by an HttpDownlinkView, and extending HttpDownlinkModem. Implement DownlinkRelays to dispatch HTTP downlink callbacks to all views in their appropriate execution contexts, analogous to WarpDownlinkModel.
  • Implement concrete RestDownlinkModel and RestDownlinkView classes in swim.runtime.http. Although RestDownlink will be the de facto HTTP downlink implementation—similar to how RestLane is the de facto HTTP lane implementation—this design enables us to implement additional specialized HttpDownlink types, if needed, such as a possible HttpChunkedDownlink for HTTP APIs that stream results in HTTP chunks. Although an ordinary RestDownlink can handle such a APIs using custom decoders, we may want to explicitly support the pattern in the future.
  • Modify the downlinkHttp methods in AbstractSwimRef, HostScope, NodeScope, and LaneScope to return new instances of RestDownlinkView.
  • Modify the createHost functions in RemoteKernel to return new RemoteHttpHostClient instances for host URIs with http and https schemes.

swim.codec unit tests failing on Windows

A number of the unit tests in the swim.codec package are failing on Windows due to carriage returns being present in the Inputs. This causes equality checks to fail due to mismatched string lengths.

DiagnosticSpec tests failing on Windows

The unit tests in the swim.codec.DiagnosticSpec class are failing due to the Input class adding carriage returns in the String on Windows. This causes the equality check in the assertion to fail.

swim.io.warp.SecureWarpSocketSpec null pointer exceptions

A number of tests have resulted in NullPointerExceptions being thrown at swim.io.IpSocketModem.didWrite(IpSocketModem.java:243). The two causes have been:

swim.io.warp.SecureWarpSocketSpec#SecureWarpSocketSpec
swim.io.ws.SecureWebSocketSpec#testCloseHandshake

These exceptions cause the application and CI build agents to hang.

swim.structure polyglot language bindings

Flesh out polyglot language bindings for the swim-structure Java library. The bindings are located in the swim-dynamic-structure Java library. The JavaScript bindings should be compatible with the @swim/structure TypeScript library. Make sure to bind static methods, in addition to instance methods.

Structure bindings

  • Add Item bindings.
  • Add Field bindings.
  • Add Attr bindings.
  • Add Slot bindings.
  • Add Value bindings
  • Add Record bindings
  • Add Data bindings.
  • Add Text bindings.
  • Add Num bindings.
  • Add Bool bindings.
  • Add Extant bindings.
  • Add Absent bindings.

Expression bindings

  • Add Expression bindings.
  • Add Operator bindings.
  • Add Selector bindings.
  • Add Func bindings.
  • Add Interpreter bindings.

Form bindings

  • Add Form bindings.

Web Routing DSL

Add a Recon DSL for the basic swim.web.WebRoutes implemented as part of #5. These will not be simple swim.structure.Forms, as we will use Dynamic Recon to dynamically evaluate routes, with extracted parameters injected into scope. Details TBD.

Json content with trailing whitespace in http chunked response breaks the HttpChunkedDecoder

A server returning a chunked json response with trailing whitespace causes the HttpChunkedDecoder to parse it incorrectly. The issue is caused by the json ObjectParser which parses the json correctly but it terminates by returning the input with it's index pointed at the trailing whitespace character (it should be at the end of all trailing whitespace characters).

This is the responsible code
HttpChunkedDecoder
ObjectParser

An example http response that triggers this issue can be found from the google oauth server

Two options to fix:
Option A: Fix the HttpChunkDecoder to bump the index of the input to chunkRemaining which assumes that the Content parser didn't fully advance the index

Option B: Fix the ObjecParser to parse the trailing whitespace before terminating

Option A seems like a hack, Option B will be better I think..thoughts?

swim.deflate tests failing on Windows

The swim.deflate.DeflateSpec unit tests are failing on Windows. Possibly due to carriage returns in the files that are being read.

swim.deflate.DeflateSpec > deflateLorem FAILED
    org.testng.TestException: swim.codec.EncoderException: truncated
        at swim.deflate.DeflateSpec.assertDeflates(DeflateSpec.java:95)
        at swim.deflate.DeflateSpec.assertDeflates(DeflateSpec.java:117)
        at swim.deflate.DeflateSpec.deflateLorem(DeflateSpec.java:50)

        Caused by:
        swim.codec.EncoderException: truncated
            at swim.deflate.Deflate.pull(Deflate.java:576)
            at swim.deflate.DeflateSpec.assertDeflates(DeflateSpec.java:93)
            ... 2 more

swim.deflate.DeflateSpec > deflateLoremIncrementally FAILED
    org.testng.TestException: swim.codec.EncoderException: truncated
        at swim.deflate.DeflateSpec.assertDeflates(DeflateSpec.java:95)
        at swim.deflate.DeflateSpec.deflateLoremIncrementally(DeflateSpec.java:57)

        Caused by:
        swim.codec.EncoderException: truncated
            at swim.deflate.Deflate.pull(Deflate.java:576)
            at swim.deflate.DeflateSpec.assertDeflates(DeflateSpec.java:93)
            ... 1 more

The InflateSpec unit tests are also failing:

swim.deflate.InflateSpec > inflateLorem FAILED
    java.lang.AssertionError: expected [2585] but found [2576]
        at org.testng.Assert.fail(Assert.java:96)
        at org.testng.Assert.failNotEquals(Assert.java:776)
        at org.testng.Assert.assertEqualsImpl(Assert.java:137)
        at org.testng.Assert.assertEquals(Assert.java:118)
        at org.testng.Assert.assertEquals(Assert.java:652)
        at org.testng.Assert.assertEquals(Assert.java:662)
        at swim.deflate.InflateSpec.assertInflates(InflateSpec.java:99)
        at swim.deflate.InflateSpec.assertInflates(InflateSpec.java:118)
        at swim.deflate.InflateSpec.inflateLorem(InflateSpec.java:47)

swim.deflate.InflateSpec > inflateLoremIncrementally FAILED
    java.lang.AssertionError: expected [2585] but found [2576]
        at org.testng.Assert.fail(Assert.java:96)
        at org.testng.Assert.failNotEquals(Assert.java:776)
        at org.testng.Assert.assertEqualsImpl(Assert.java:137)
        at org.testng.Assert.assertEquals(Assert.java:118)
        at org.testng.Assert.assertEquals(Assert.java:652)
        at org.testng.Assert.assertEquals(Assert.java:662)
        at swim.deflate.InflateSpec.assertInflates(InflateSpec.java:99)
        at swim.deflate.InflateSpec.inflateLoremIncrementally(InflateSpec.java:55)

Join Map Lane repeatedly executes didUpdate

Using the same example from #67 the join map lane will keep executing the same didUpdate callback indefinitely every 90 seconds.

Input

@command(node:"/unit/foo", lane:"addItem")"foo"

Initial Output:

join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1

Output after 90 seconds:

join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1
join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1

Output after 180 seconds:

join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1
join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1
join map lane: foo count changed to 1 from 1
join map lane agent didStart: foo count changed to 1 from 0
join map lane: foo count changed to 1 from 1

etc...

Might be resolved when #67 is resolved.

node_modules libraries throwing build issues with skipLibCheck to false in angular 11 project

node_modules libraries throwing build issues with skipLibCheck to false in angular 11 project

Attached the sample project

Errors are as below when I try to run the application with npm start

Error: node_modules/@swim/client/lib/main/downlink/ListDownlinkRecord.d.ts:8:9 - error TS2611: 'downlink' is defined as a property in class 'DownlinkRecord', but is overridden here in 'ListDownlinkRecord' as an accessor.

8 get downlink(): ListDownlink<Value, AnyValue>;
~~~~~~~~

Error: node_modules/@swim/client/lib/main/downlink/MapDownlinkRecord.d.ts:8:9 - error TS2611: 'downlink' is defined as a property in class 'DownlinkRecord', but is overridden here in 'MapDownlinkRecord' as an accessor.

8 get downlink(): MapDownlink<Value, Value, AnyValue, AnyValue>;
~~~~~~~~

Error: node_modules/@swim/client/lib/main/downlink/ValueDownlinkRecord.d.ts:8:9 - error TS2611: 'downlink' is defined as a property in class 'DownlinkRecord', but is overridden here in 'ValueDownlinkRecord' as an accessor.

8 get downlink(): ValueDownlink<Value, AnyValue>;
~~~~~~~~

Error: node_modules/@swim/font/lib/main/Font.d.ts:56:481 - error TS2307: Cannot find module '../../length/main' or its corresponding type declarations.

56 static from(style: "normal" | "italic" | "oblique" | null | undefined, variant: "normal" | "small-caps" | null | undefined, weight: "normal" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900" | "bold" | "bolder" | "lighter" | null | undefined, stretch: "normal" | "ultra-condensed" | "extra-condensed" | "semi-condensed" | "condensed" | "expanded" | "semi-expanded" | "extra-expanded" | "ultra-expanded" | null | undefined, size: string | number | import("../../length/main").Length | null | undefined, height: string | number | import("../../length/main").Length | null | undefined, family: FontFamily | ReadonlyArray): Font;
~~~~~~~~~~~~~~~~~~~

Error: node_modules/@swim/font/lib/main/Font.d.ts:56:562 - error TS2307: Cannot find module '../../length/main' or its corresponding type declarations.

56 static from(style: "normal" | "italic" | "oblique" | null | undefined, variant: "normal" | "small-caps" | null | undefined, weight: "normal" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900" | "bold" | "bolder" | "lighter" | null | undefined, stretch: "normal" | "ultra-condensed" | "extra-condensed" | "semi-condensed" | "condensed" | "expanded" | "semi-expanded" | "extra-expanded" | "ultra-expanded" | null | undefined, size: string | number | import("../../length/main").Length | null | undefined, height: string | number | import("../../length/main").Length | null | undefined, family: FontFamily | ReadonlyArray): Font;
~~~~~~~~~~~~~~~~~~~

Error: node_modules/@swim/style/lib/main/StyleValue.d.ts:21:204 - error TS2307: Cannot find module '../../../../swim-core-js/@swim/time/main' or its corresponding type declarations.

21 form(unit?: string | number | boolean | BoxShadow | Length | Color | BoxShadowInit | import("./BoxShadow").BoxShadowArray | RgbColorInit | HslColorInit | DateTime | Angle | Font | Transform | import("../../../../swim-core-js/@swim/time/main").DateTimeInit | Date | import("../../font/main").FontInit | Interpolator<any, any> | Scale<any, any, any, any> | Transition | TransitionInit | TransitionInit | undefined): Form<StyleValue, AnyStyleValue>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Error: node_modules/@swim/style/lib/main/StyleValue.d.ts:21:277 - error TS2307: Cannot find module '../../font/main' or its corresponding type declarations.

21 form(unit?: string | number | boolean | BoxShadow | Length | Color | BoxShadowInit | import("./BoxShadow").BoxShadowArray | RgbColorInit | HslColorInit | DateTime | Angle | Font | Transform | import("../../../../swim-core-js/@swim/time/main").DateTimeInit | Date | import("../../font/main").FontInit | Interpolator<any, any> | Scale<any, any, any, any> | Transition | TransitionInit | TransitionInit | undefined): Form<StyleValue, AnyStyleValue>;
~~~~~~~~~~~~~~~~~

Error: ./node_modules/@swim/codec/lib/main/Format.js
Module not found: Error: Can't resolve 'os' in 'C:\Users\bshaik\Downloads\poc-swim-angular11\node_modules@swim\codec\lib\main'

poc-swim-angular11.zip

Stop method on client terminates commands

The Java ClientRuntime has a stop() method. If the method is executed directly after a .command() statement, the command never gets sent.

Example:

ClientRuntime swimClient = new ClientRuntime();
//This does not get sent
swimClient.command(hostUri, thirdRoomUri, "toggleLights", Value.absent());

swimClient.stop();

A workaround is to use Thread.sleep(2000); to add some delay.

Example:

ClientRuntime swimClient = new ClientRuntime();
//Now it works fine
swimClient.command(hostUri, thirdRoomUri, "toggleLights", Value.absent());

Thread.sleep(2000);
swimClient.stop();

I think that the stop() method should make sure that everything has been sent before terminating the connection, or at the very least, it should display a warning that a command has been terminated before being sent.

Integer ValueLane can be set with Text.

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).

Join Map Lane reporting the same value for both previous and current on didUpdate

If a didUpdate callback is added to a JoinMapLane it reports the previous and the current value as the same.

Example:

BasicPlane.java

public class BasicPlane extends AbstractPlane {

  @SwimRoute("/unit/:id")
  AgentRoute<UnitAgent> unitAgentType;

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

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

    space.command("/unit/foo", "wakeup", Value.absent());
  }
}

UnitAgent.java

public class UnitAgent extends AbstractAgent {

  @SwimLane("shoppingCart")
  MapLane<String, Integer> shoppingCart = this.<String, Integer>mapLane();

  @SwimLane("addItem")
  CommandLane<String> publish = this.<String>commandLane()
      .onCommand(msg -> {
        final int n = this.shoppingCart.getOrDefault(msg, 0) + 1;
        this.shoppingCart.put(msg, n);
      });

    @SwimLane("join")
    JoinMapLane<String, String, Integer> stateStreetStats = this.<String, String, Integer>joinMapLane().didUpdate((key, newValue, oldValue) -> {
        System.out.println("join map lane: " + key + " count changed to " + newValue + " from " + oldValue);
    });

    @Override
    public void didStart() {
        stateStreetStats.downlink("foo").hostUri("warp://127.0.0.1:9001").nodeUri("/unit/foo").laneUri("shoppingCart").open();
    }

}

Input:

@command(node:"/unit/foo", lane:"addItem")"foo"
@command(node:"/unit/foo", lane:"addItem")"foo"
@command(node:"/unit/foo", lane:"addItem")"foo"

Output:

join map lane: foo count changed to 1 from 1
join map lane: foo count changed to 2 from 2
join map lane: foo count changed to 3 from 3

(The previous value should be one lower than the current)

Seed nodes only if the host is primary or replica

Any node defined in the configuration as a Uri (not a UriPattern) gets automatically instantiated in all hosts. This should be restricted only to hosts that are primary or replicas.

The code here is should be guarded with an if condition.

HTTP Lane Directives

Extract the HTTP lane routing logic from swim.service.web.WebServer into a generalized swim.service.web.HttpLaneRoute class.

  • Implement HttpLaneRoute, taking an EdgeContext, a node URI, and a lane URI as parameters.
  • Add a LaneAddress POJO to swim.runtime, with hostUri, nodeUri, and laneUri fields (i.e. a 3-tuple of (Uri, Uri, Uri)).
  • Implement HttpLaneDirective, taking a java.util.Function<WebRequest, LaneAddress> as its parameter, which behaves like HttpLaneRoute, but dynamically computes the address of the HTTP lane to which it routes.
  • Modify swim.service.web.WebServer to use HttpLaneRoute, keeping it compatible with the current fixed-function configuration format. That format will be deprecated once the Web routing DSL (#6) is ready to replace it.

Due to the lack of extension methods in Java, we unfortunately can't add a WebRoute combinator function for producing HTTP Lane routes.

Tracking Issue for Polyglot Language Bindings

The swim.polyglot framework provides a meta API for bridging between swimOS APIs and guest programming languages executed within the JVM. The swim.dynamic library implements a set of typeclasses for precisely controlling the guest language interface to the host APIs, without modifying the underlying library. The swim.vm library late binds the swim.dynamic typeclasses to the GraalVM SDK.

We do not want Swim Polyglot Language Bindings to be straight passthroughs to the underlying Java APIs. Language Bindings should feel like first class citizens of each guest language. The swim.dynamic approach supports language-specific binding overrides, among other capabilities, to produce the exact API we wish to expose. For example the language bindings for the swim.structure Java library should be compatible with the native TypeScript @swim/structure library. As another example, the bindings for key Java interfaces, such as java.util.Map vary by guest language. All swim.dynamic types that extend the java.util.Map bindings inherit these language-specific base bindings.

Bindings for specific libraries will be tracked in separate issues.

  • java.lang bindings
  • swim.structure bindings (#12)
  • swim.api bindings

EventDownlinks not connecting or didConnect not firing

A reproducible example is PingPongSpec#testCommandPingPong. In some instances, the didConnect phase fires multiple times; sometimes up to four. Without a latch on the didConnect phase, sometimes the onEvent is missed due to the lanes not being ready.

Inconsistent didReceive for ListDownlink

The following test shows that the didReceive callback for ListDownlink is not always executed. The problem does not appear if only a single value is added to the list.

@Test(invocationCount = 10000)
  public void testInsert() throws InterruptedException {
    final Kernel kernel = ServerLoader.loadServerStack();
    final TestListPlane plane = kernel.openSpace(ActorSpaceDef.fromName("test"))
        .openPlane("test", TestListPlane.class);
  
    final CountDownLatch didSyncListLinkLatch = new CountDownLatch(1);
    final CountDownLatch linkDidReceive = new CountDownLatch(2);
    
    class ListLinkController implements DidReceive {
  
      @Override
      public void didReceive(Value body) {
        System.out.println("ListLinkController- link didReceive body ");
        linkDidReceive.countDown();
      }
    }
    
    try {
      kernel.openService(WebServiceDef.standard().port(53556).spaceName("test"));
      kernel.start();
      final ListDownlink<String> listLink = plane.downlinkList()
          .valueClass(String.class)
          .hostUri("warp://localhost:53556")
          .nodeUri("/list/todo")
          .laneUri("list")
          .observe(new ListLinkController())
          .didSync(didSyncListLinkLatch::countDown)
          .open();
      
      didSyncListLinkLatch.await();
      
      listLink.add(0, "a");
      listLink.add(1, "b");

      linkDidReceive.await(5, TimeUnit.SECONDS);
      
      assertEquals(linkDidReceive.getCount(), 0);
      assertEquals(listLink.size(), 2);
      assertEquals(listLink.get(0), "a");
      assertEquals(listLink.get(1), "b");
    } finally {
      kernel.stop();
    }

java.lang.OutOfMemoryError when starting a Station

While setting up the continuous integration and resolving some of the failing tests, myself and @DobromirM have noticed instances where our machines have come to a halt and tests have failed due to a java.lang.OutOfMemoryError. When profiling tests I noticed that a very large quantity of ClockEvent and ClockQueue instances were created.

Screen Shot 2019-11-29 at 08 25 42

This is due to the underlying Clock that is associated with a Theater's Schedule not being stopped when the Kernel is shutdown. This causes the clock timers to still be executed every tick until the JVM is shutdown. This issue is noticeable when running unit tests due to the amount of new Kernel instances that are created.

I have resolved this issue by shutting down the clock associated with the theater and changing the Clock#stop method to not attempt to stop if it hasn't been started; otherwise, the stop method will hang if it attempts to shutdown having never started.

Now that this has been resolved, the memory footprint is significantly lower and when running all of the unit tests either locally or on the CI build agent the application no longer hangs.

Screen Shot 2019-11-29 at 08 37 21

I will create a PR for this soon.

Can't resolve 'os' in @swim/codec/lib/main/Format.js with Angular 11

Hi

I am using Angular 11 and I get the below error with sim library

Error: ./node_modules/@swim/codec/lib/main/Format.js
Module not found: Error: Can't resolve 'os' in 'C:\Users\Basha\Downloads\poc-swim-angular11\node_modules@swim\codec\lib\main'

FYI I don't get the error with the angular version 9

Attaching the angular project
Unzip and do npm install and npm start
poc-swim-angular11.zip

Please advise

swim.io.Http test failures

Test classes HttpSocketSpec and SecureHttpSocketSpec both encounter failures after a number of runs locally and nearly every time on a CI build. I have tested assigning a random port for each run and while this does seem to reduce the frequency of the issue it is still encountered. The testRequestResponse() method appears to fail in connecting to the server and when the client.close() call is made, a NullPointerException is thrown by the underlying client and this causes a failure. I have tested asserting on the IpSocketRef.isConnected(), however, this seems to return false either way.

MapLane didReceive not being called

MapDownlinkSpec#testDrop is not calling the didReceive phase or the values are not being set. A latch has been used to await the downlink's synchronisation but the error still occurs after a number of runs.

expected [0] but found [2]
java.lang.AssertionError: expected [0] but found [2]
	at [email protected]/org.testng.Assert.fail(Assert.java:96)
	at [email protected]/org.testng.Assert.failNotEquals(Assert.java:776)
	at [email protected]/org.testng.Assert.assertEqualsImpl(Assert.java:137)
	at [email protected]/org.testng.Assert.assertEquals(Assert.java:118)
	at [email protected]/org.testng.Assert.assertEquals(Assert.java:547)
	at [email protected]/org.testng.Assert.assertEquals(Assert.java:557)
	at swim.server/swim.server.MapDownlinkSpec.testDrop(MapDownlinkSpec.java:437)
	at jdk.internal.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at [email protected]/org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
	at [email protected]/org.testng.internal.Invoker.invokeMethod(Invoker.java:583)
	at [email protected]/org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
	at [email protected]/org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989)
	at [email protected]/org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
	at [email protected]/org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
	at [email protected]/org.testng.TestRunner.privateRun(TestRunner.java:648)
	at [email protected]/org.testng.TestRunner.run(TestRunner.java:505)
	at [email protected]/org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
	at [email protected]/org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
	at [email protected]/org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
	at [email protected]/org.testng.SuiteRunner.run(SuiteRunner.java:364)
	at [email protected]/org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at [email protected]/org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
	at [email protected]/org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
	at [email protected]/org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
	at [email protected]/org.testng.TestNG.runSuites(TestNG.java:1049)
	at [email protected]/org.testng.TestNG.run(TestNG.java:1017)
	at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.runTests(TestNGTestClassProcessor.java:141)
	at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.stop(TestNGTestClassProcessor.java:90)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
	at com.sun.proxy.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:132)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:175)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:157)
	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.base/java.lang.Thread.run(Thread.java:835)

Error parsing BigIntegers using Recon, JSON and Codec

Some BigIntegers are parsed incorrectly while using Recon, JSON or the Codec library.
Eg for Recon (same is true for JSON and codec):
System.out.println(Recon.toString(Recon.parse("{a:259804429589205426119611}")));

will give the following output:
{a:1550012557271703495611}

The problem is with this
https://github.com/swimos/swim-system-java/blob/master/swim-core-java/swim.recon/src/main/java/swim/recon/NumberParser.java#L82-L83

newValue can be +ve in some cases even with the overflow since it overflows twice i.e into negative longs and then back to positive longs. So the (value >> 63 == newValue >> 63) will be true even if there is an overflow and it should be false.

swim.remote.RemoteHostSpec#testRemoteHostCommands didUpgrade not called the correct number of times

As per the title, the didUpgrade phase is not called every time during the test.

Gradle suite > Gradle test > swim.remote.RemoteHostSpec > testRemoteHostCommands FAILED
    java.lang.AssertionError: expected:<1> but was:<0>
        at [email protected]/org.testng.AssertJUnit.fail(AssertJUnit.java:59)
        at [email protected]/org.testng.AssertJUnit.failNotEquals(AssertJUnit.java:364)
        at [email protected]/org.testng.AssertJUnit.assertEquals(AssertJUnit.java:80)
        at [email protected]/org.testng.AssertJUnit.assertEquals(AssertJUnit.java:170)
        at [email protected]/org.testng.AssertJUnit.assertEquals(AssertJUnit.java:177)
        at swim.remote/swim.remote.RemoteHostSpec.testRemoteHostCommands(RemoteHostSpec.java:168)

I have tried adding a latch to the didConnect of the RemoteHostClient used and I noticed that in some cases the phase is called two times and others four. I also observed the following exception at times:

error: expected carriage return, but found end of input
 --> :2:16
  |
2 |   |                ^
	at swim.codec/swim.codec.Parser.error(Parser.java:231)
	at swim.http/swim.http.HttpRequestParser.parse(HttpRequestParser.java:173)
	at swim.http/swim.http.HttpRequestParser.feed(HttpRequestParser.java:52)
	at swim.codec/swim.codec.InputParser.parse(InputParser.java:50)
	at swim.codec/swim.codec.InputParser.feed(InputParser.java:31)
	at swim.codec/swim.codec.Parser.feed(Parser.java:144)
	at swim.codec/swim.codec.Parser.feed(Parser.java:101)
	at swim.io/swim.io.IpSocketModem.doRead(IpSocketModem.java:164)
	at swim.io/swim.io.TcpSocket.doRead(TcpSocket.java:230)
	at swim.io/swim.io.StationTransport.doRead(Station.java:579)
	at swim.io/swim.io.StationReader.runTask(Station.java:796)
	at swim.concurrent/swim.concurrent.TheaterTask.run(Theater.java:435)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

As well as:

swim.io.StationException: Can't restart stopped station
	at swim.io/swim.io.Station.start(Station.java:141)
	at swim.io/swim.io.Station.transport(Station.java:221)
	at swim.io/swim.io.IpStation.connectTcp(IpStation.java:83)
	at swim.remote/swim.remote.RemoteHostClient.connectHttp(RemoteHostClient.java:99)
	at swim.remote/swim.remote.RemoteHostClient.connect(RemoteHostClient.java:84)
	at swim.remote/swim.remote.RemoteHostClient.willOpen(RemoteHostClient.java:138)
	at swim.runtime/swim.runtime.AbstractTierBinding.start(AbstractTierBinding.java:168)
	at swim.remote/swim.remote.RemoteHost.didUpgrade(RemoteHost.java:532)
	at swim.remote/swim.remote.RemoteHostSpec$5.didUpgrade(RemoteHostSpec.java:119)
	at swim.io.warp/swim.io.warp.WarpWebSocket.didUpgrade(WarpWebSocket.java:127)
	at swim.io.ws/swim.io.ws.WsUpgradeRequester.didRespond(WsUpgradeRequester.java:82)
	at swim.io.http/swim.io.http.HttpClientRequester.didRespond(HttpClientRequester.java:150)
	at swim.io.http/swim.io.http.HttpClientModem.didRespond(HttpClientModem.java:345)
	at swim.io.http/swim.io.http.HttpClientModem.didRead(HttpClientModem.java:75)
	at swim.io.http/swim.io.http.HttpClientModem.doReadResponseEntity(HttpClientModem.java:323)
	at swim.io.http/swim.io.http.HttpClientRequester.willRespond(HttpClientRequester.java:145)
	at swim.io.http/swim.io.http.HttpClientModem.willRespond(HttpClientModem.java:336)
	at swim.io.http/swim.io.http.HttpClientModem.didRead(HttpClientModem.java:73)
	at swim.io.http/swim.io.http.HttpClientModem.didRead(HttpClientModem.java:34)
	at swim.io/swim.io.IpSocketModem.doRead(IpSocketModem.java:171)
	at swim.io/swim.io.TcpSocket.doRead(TcpSocket.java:230)
	at swim.io/swim.io.StationTransport.doRead(Station.java:579)
	at swim.io/swim.io.StationReader.runTask(Station.java:796)
	at swim.concurrent/swim.concurrent.TheaterTask.run(Theater.java:435)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

and:

java.util.NoSuchElementException
	at swim.collections/swim.collections.FingerTrieSeq.head(FingerTrieSeq.java:203)
	at swim.io.http/swim.io.http.HttpServerModem.willRespond(HttpServerModem.java:310)
	at swim.io.http/swim.io.http.HttpServerModem.doWriteResponse(HttpServerModem.java:304)
	at swim.io.http/swim.io.http.HttpServerResponder.doRespond(HttpServerResponder.java:175)
	at swim.io.http/swim.io.http.HttpServerModem.willRequest(HttpServerModem.java:289)
	at swim.io.http/swim.io.http.HttpServerModem.didRead(HttpServerModem.java:71)
	at swim.io.http/swim.io.http.HttpServerModem.didRead(HttpServerModem.java:34)
	at swim.io/swim.io.IpSocketModem.doRead(IpSocketModem.java:171)
	at swim.io/swim.io.TcpSocket.doRead(TcpSocket.java:230)
	at swim.io/swim.io.StationTransport.doRead(Station.java:579)
	at swim.io/swim.io.StationReader.runTask(Station.java:796)
	at swim.concurrent/swim.concurrent.TheaterTask.run(Theater.java:435)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
java.util.NoSuchElementException
	at swim.collections/swim.collections.FingerTrieSeq.head(FingerTrieSeq.java:203)
	at swim.io.http/swim.io.http.HttpServerModem.willRespond(HttpServerModem.java:310)
	at swim.io.http/swim.io.http.HttpServerModem.doWriteResponse(HttpServerModem.java:304)
	at swim.io.http/swim.io.http.HttpServerResponder.doRespond(HttpServerResponder.java:175)
	at swim.io.http/swim.io.http.HttpServerModem.willRequest(HttpServerModem.java:289)
	at swim.io.http/swim.io.http.HttpServerModem.didRead(HttpServerModem.java:71)
	at swim.io.http/swim.io.http.HttpServerModem.didRead(HttpServerModem.java:34)
	at swim.io/swim.io.IpSocketModem.doRead(IpSocketModem.java:171)
	at swim.io/swim.io.TcpSocket.doRead(TcpSocket.java:230)
	at swim.io/swim.io.StationTransport.doRead(Station.java:579)
	at swim.io/swim.io.StationReader.runTask(Station.java:796)
	at swim.concurrent/swim.concurrent.TheaterTask.run(Theater.java:435)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1425)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

RemoteHost has a null WebSocketContext

Scenario: Two Swim Planes where agents in one Plane send data to another Swim Plane over an unstable network connection. Effectively one SwimPlane (sender) acts as a Client that sends data to another SwimPlane (receiver) i.e. the data is sent in only one direction (not sure if that is relevant here though). Usually the RemoteHostClient reconnects and the data flow continues. Intermittently the RemoteHostClient reconnects but the WarpSocketContext associated with the RemoteHost is null. See stacktrace from the application running in Trafficware:

java.lang.NullPointerException at swim.remote/swim.remote.RemoteHost.pushUp(RemoteHost.java:542) at swim.actor/swim.actor.ActorCell.pushUp(ActorCell.java:122) at swim.runtime/swim.runtime.router.PartTable.pushUp(PartTable.java:622) at swim.actor/swim.actor.ActorCell.pushUp(ActorCell.java:122) at swim.runtime/swim.runtime.router.MeshTable.pushUp(MeshTable.java:670) at swim.actor/swim.actor.ActorCell.pushUp(ActorCell.java:122) at swim.runtime/swim.runtime.router.EdgeTable.pushUp(EdgeTable.java:579) at swim.runtime/swim.runtime.router.EdgeTable.pushDown(EdgeTable.java:568) at swim.runtime/swim.runtime.router.EdgeTableMesh.pushDown(EdgeTableMesh.java:245) at swim.actor/swim.actor.ActorCell.pushDown(ActorCell.java:107) at swim.runtime/swim.runtime.router.MeshTable.pushDown(MeshTable.java:659) at swim.runtime/swim.runtime.router.MeshTablePart.pushDown(MeshTablePart.java:244) at swim.actor/swim.actor.ActorCell.pushDown(ActorCell.java:107) at swim.runtime/swim.runtime.router.PartTable.pushDown(PartTable.java:605) at swim.runtime/swim.runtime.router.PartTableHost.pushDown(PartTableHost.java:222) at swim.actor/swim.actor.ActorCell.pushDown(ActorCell.java:107) at swim.runtime/swim.runtime.router.HostTable.pushDown(HostTable.java:644) at swim.runtime/swim.runtime.router.HostTableNode.pushDown(HostTableNode.java:212) at swim.actor/swim.actor.ActorCell.pushDown(ActorCell.java:107) at swim.runtime/swim.runtime.agent.AgentNode.pushDown(AgentNode.java:526) at swim.runtime/swim.runtime.agent.AgentView.pushDown(AgentView.java:413) at swim.runtime/swim.runtime.AbstractSwimRef.command(AbstractSwimRef.java:163) at swim.runtime/swim.runtime.AbstractSwimRef.command(AbstractSwimRef.java:226) at swim.api/swim.api.agent.AbstractAgent.command(AbstractAgent.java:701) at it.swim.traffic/it.swim.traffic.agent.IntersectionAgent.lambda$new$2(IntersectionAgent.java:169) at swim.runtime/swim.runtime.lane.CommandLaneView.dispatchOnCommand(CommandLaneView.java:173) at swim.runtime/swim.runtime.lane.CommandLaneRelayCommand.runPhase(CommandLaneModel.java:87) at swim.runtime/swim.runtime.lane.CommandLaneRelayCommand.runPhase(CommandLaneModel.java:52) at swim.runtime/swim.runtime.LaneRelay.pass(LaneRelay.java:100) at swim.runtime/swim.runtime.LaneRelay.run(LaneRelay.java:187) at swim.runtime/swim.runtime.agent.AgentNode.runTask(AgentNode.java:699) at swim.concurrent/swim.concurrent.TheaterTask.run(Theater.java:453) at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

swim.server.ListDownlinkSpec failing unit tests

testInsert failing:

It appears that in some instances values are not set on the lane and so the latches do not countdown enough.

A working run:

Test: 200
link willUpdate index: 0
ListLinkController- link didUpdate index: 0; newValue "a"; oldValue: null
link willUpdate index: 1
ListLinkController- link didUpdate index: 1; newValue "b"; oldValue: null
link willUpdate index: 2
ListLinkController- link didUpdate index: 2; newValue "c"; oldValue: null
ReadOnlyListLinkController- link didUpdate index: 0; newValue "a"; oldValue: ""
ListLinkController- link willReceive body @update(index:0,key:%YEdkLwEr) a
link willUpdate index: 0
ListLinkController- link didUpdate index: 0; newValue "a"; oldValue: ""
ListLinkController- link didReceive body @update(index:0,key:%YEdkLwEr) a
ListLinkController- link willReceive body @update(index:1,key:%wziHqnnF) b
link willUpdate index: 1
ReadOnlyListLinkController- link didUpdate index: 1; newValue "b"; oldValue: ""
ListLinkController- link didUpdate index: 1; newValue "b"; oldValue: ""
ListLinkController- link didReceive body @update(index:1,key:%wziHqnnF) b
ListLinkController- link willReceive body @update(index:2,key:%F2COEsmf) c
link willUpdate index: 2
ListLinkController- link didUpdate index: 2; newValue "c"; oldValue: ""
ReadOnlyListLinkController- link didUpdate index: 2; newValue "c"; oldValue: ""
ListLinkController- link didReceive body @update(index:2,key:%F2COEsmf) c

When an error occurs:

Test: 201
link willUpdate index: 0
ListLinkController- link didUpdate index: 0; newValue "a"; oldValue: null
link willUpdate index: 1
ListLinkController- link didUpdate index: 1; newValue "b"; oldValue: null
link willUpdate index: 2
ListLinkController- link didUpdate index: 2; newValue "c"; oldValue: null
ListLinkController- link willReceive body @update(index:0,key:%CH1SQHk/) a
link willUpdate index: 0
ListLinkController- link didUpdate index: 0; newValue "a"; oldValue: ""
ListLinkController- link didReceive body @update(index:0,key:%CH1SQHk/) a
ReadOnlyListLinkController- link didUpdate index: 0; newValue "a"; oldValue: ""
ListLinkController- link willReceive body @update(index:1,key:%61kn6Rag) b
link willUpdate index: 1
ListLinkController- link didUpdate index: 1; newValue "b"; oldValue: ""
ListLinkController- link didReceive body @update(index:1,key:%61kn6Rag) b
ListLinkController- link willReceive body @update(index:2,key:%B1KKssj0) c
link willUpdate index: 2
ListLinkController- link didUpdate index: 2; newValue "c"; oldValue: ""
ListLinkController- link didReceive body @update(index:2,key:%B1KKssj0) c
ReadOnlyListLinkController- link didUpdate index: 1; newValue "b"; oldValue: ""
ReadOnlyListLinkController- link didUpdate index: 2; newValue "c"; oldValue: ""

testClear failures:

swim.server.ListDownlinkSpec > testClear FAILED
    java.lang.AssertionError: expected [3] but found [0]
        at org.testng.Assert.fail(Assert.java:96)
        at org.testng.Assert.failNotEquals(Assert.java:776)
        at org.testng.Assert.assertEqualsImpl(Assert.java:137)
        at org.testng.Assert.assertEquals(Assert.java:118)
        at org.testng.Assert.assertEquals(Assert.java:652)
        at org.testng.Assert.assertEquals(Assert.java:662)
        at swim.server.ListDownlinkSpec.testClear(ListDownlinkSpec.java:681)

Inconsistent results of checkstyle

The checkstyleMain and checkstyleTest Gradle tasks display warnings only the first time that they are executed.

Steps for reproducing the behaviour:

  1. Open a file and change the order of import statements so that the they violate the style.
  2. Run either checkstyleMain or checkstyleTest depending on whether you changed a source file or a test file. At this step, Gradle will report a warning when the task is running.
  3. Without changing anything, run the task again. Gradle will not report any warnings.

Explanation:
This happens due to the fact that on step two, the style problems are reported as warnings and Gradle mark the tasks as successful (Gradle tasks fail only on errors and not on warnings). When the same task is executed again without any changes to the files, Gradle uses it's cache to display that it is successful instead of actually running the check.

Possible solutions:

  1. Force Gradle to never use the cache for checkstyleMain or checkstyleTest. This can be done by adding outputs.upToDateWhen { false } in the tasks but is generally considered a bad practice and it is discouraged by the Gradle developers. See: https://www.stefan-oehme.com/stop-rerunning-tests

  2. Set the severity level of style violations to error with <property name="severity" value="error"/> to the checkstyle.xml. This will make the Gradle tasks fail by reporting the style violations as errors.

  3. Add maxWarnings = 0 to the Gradle tasks to make them fail if a warning is reported. This is similar to the second solution because the tasks will again fail, but the style violations will still be marked as warnings instead of errors.

It should be decided if a style violation should result in a failed task or not. In my opinion, it would make sense for tasks that check the style of the code to fail, if there are any violations. However, it will also make it harder to do a build without fixing all style violations or disabling the failing, because the build task depends on the checkstyle tasks.

Errors with Swim apps installed in folder names with characters that are disallowed in the URI syntax

Consider an app with the following server.recon file:

tutorial: @fabric {
  @plane(class: "swim.tutorial.TutorialPlane")
}

@web(port: 9001) {
  space: "tutorial"
  documentRoot: "./ui/"
  @websocket {
    serverCompressionLevel: 0# -1 = default; 0 = off; 1-9 = deflate level
    clientCompressionLevel: 0# -1 = default; 0 = off; 1-9 = deflate level
  }
}

If this app is installed in a folder say: /home/user name/abc then it will fail with the following exception when it is run:

--> :1:28
  |
1 | /home/user name/abc
  |                            
        at swim.codec/swim.codec.Parser.error(Parser.java:142)
        at swim.uri/swim.uri.UriParser.parsePathString(UriParser.java:214)
        at swim.uri/swim.uri.UriPath.parse(UriPath.java:314)
        at swim.service.web/swim.service.web.WebServer.<init>(WebServer.java:62)
        at swim.service.web/swim.service.web.WebService.createServer(WebService.java:118)
        at swim.io.http/swim.io.http.HttpSocketService.createSocket(HttpSocketService.java:51)
        at swim.io/swim.io.TcpService.doAccept(TcpService.java:117)
        at swim.io/swim.io.StationTransport.doAccept(Station.java:453)
        at swim.io/swim.io.StationThread.doAccept(Station.java:1079)
        at swim.io/swim.io.StationThread.select(Station.java:1005)
        at swim.io/swim.io.StationThread.run(Station.java:882)

Note that this issue applies only for apps that have a DocumentRoot in the recon configuration file.
This occurs because the File's absolute path gets parsed into a Uri object which fails if the file path of the DocumentRoot has characters that aren't compliant with the URI syntax.

A possible solution would be to URI encode the absolute path of the File

Join Value Lane creates duplicate didSet callbacks

Similar to #67 but with a slightly different output, potentially due to #65.

Example:

BasicPlane.java

public class BasicPlane extends AbstractPlane {


  @SwimRoute("/swim")
  AgentRoute<UnitAgent> unitAgentType;

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

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

    space.command("/swim", "wakeup", Value.absent());
  }
}

UnitAgent.java

public class UnitAgent extends AbstractAgent {

  @SwimLane("people")
  ValueLane<Integer> people = this.valueLane();

  @SwimLane("addPeople")
  CommandLane<Integer> addPeople = this.<Integer>commandLane().onCommand(msg -> {
    this.people.set(this.people.get() + msg);
  });

  @SwimLane("join")
  JoinValueLane<Integer, Integer> join = this.<Integer, Integer>joinValueLane().didUpdate((Integer key, Integer newValue, Integer oldValue) -> {
    System.out.println("The people in room " + key + " are " + newValue + " from " + oldValue);
  });

  @Override
  public void didStart() {
    join.downlink(1).nodeUri("/swim").laneUri("people").open().didSet((newValue, oldValue) -> {
      System.out.println("people changed to " + newValue + " from " + oldValue);
    });
  }
}

Input

@command(node:"/swim", lane:"addPeople")5

Output

The people in room 1 are 0 from 0
people changed to 0 from 0
The people in room 1 are 0 from 0
The people in room 1 are 5 from 0
people changed to 5 from 5
The people in room 1 are 5 from 5

The first three messages are from the initial value and the next three are from the command.

Join Map Lane not responding after a high volume of usage

When using a JoinMapLane, if one is to put a large number of entries in to the map (>9000) in one go, either the observers stop being called or the entries are not written. I have tried waiting for ~30 or so seconds and then attempting to use the map again but still nothing happens. When adding a delay of 10ms between the entries this is not observed.

This is to be investigated and a reproducible example will be put here.

Downlinks not reconnecting after a Connection Timeout

Open a downlink using the ClientRuntime. If the connection to the host becomes stale i.e. no data sent over the link for 'x' seconds where 'x' is the inactivity timeout for the TransportSettings (90 seconds is the default), then the connection associated with the downlink is disconnected and then reconnected. However the downlink(s) are still stale and doesn't get data after the reconnect.

Tracking Issue for Web Server Enhancements

swimOS contains an explicitly flow-controlled HTTP and WebSocket implementation. The HTTP and WebSocket wire protocols are implemented as a family of incremental swim.codec Parsers and Writers in swim.http and swim.ws. These wire protocol implementations are then bound to flow-controller swim.io Modems by swim.io.http and swim.io.ws.

swimOS runs an internal Web server built on these HTTP and WebSocket implementations, contained in swim.service.web. The primary purpose of this web server has historically been to open WARP WebSockets. Over time, we added HTTP lanes, which get routed through the internal web server, as well as basic static resource serving. But HTTP lanes have been limited by fixed routing rules, and static resource servicing has been limited by serving up a single root document directory.

swim.service.web is itself built on top of swim.web, which is intended to implement generic web server capabilities, independent of any swimOS fabric integration. We'd like to enhance our web server with the following capabilities:

  • HTTP Routing Directives (#5)
  • Web Routing DSL (#6)
  • HTTP Lane Directives (#7)
  • WebSocket Lane Directives
  • HTTP Caching Directives
  • CORS Directives

Commands can get routed to an Agent before an Agent is properly initiliazed

This can be reproduced by sending two commands to an Agent in parallel. The first command triggers the Agent initialization process. The second command might get to the Agent before it has been initialized i.e. before gets to the start state. In this scenario if a lane's method is invoked on the callback function which receives the command then a NullPointerException will occur. This is because the lanes are not initialized in the Agent yet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.