├── .gitignore ├── LICENSE ├── README.md ├── README.zh-cn.md ├── agent_auth ├── build.gradle └── src │ └── main │ ├── java │ ├── swim_auth │ │ ├── AuthPolicy.java │ │ ├── MainPlane.java │ │ └── UnitAgent.java │ └── utils │ │ └── PublicKeyConverter.java │ └── resources │ └── server.recon ├── aggregations ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── aggregations │ │ ├── BasicPlane.java │ │ ├── StateAgent.java │ │ ├── StateLoggingAgent.java │ │ ├── StationaryVehicleAgent.java │ │ ├── VehicleAgent.java │ │ └── VehicleSimulatorAgent.java │ └── resources │ └── server.recon ├── auth_policy ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── BasicPolicy.java │ │ ├── ControlAgent.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── command_lanes ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── config └── checkstyle │ └── checkstyle.xml ├── demand_map_lanes ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── demand_value_lanes ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── downlinks ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ ├── ListenerAgent.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── egress_bridges ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── grade │ │ ├── EgressAgent.java │ │ ├── GradePlane.java │ │ ├── Sim.java │ │ ├── StudentAgent.java │ │ └── db │ │ ├── BlockingStudentsDriver.java │ │ └── Database.java │ └── resources │ └── server.recon ├── forms ├── README.md ├── README.zh-ch.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BarType.java │ │ ├── BasicPlane.java │ │ ├── BazType.java │ │ ├── CustomClient.java │ │ ├── FooType.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── http_ingestion ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── vehicle │ │ ├── AgencyAgent.java │ │ ├── Assets.java │ │ ├── Main.java │ │ ├── NextBusApi.java │ │ └── VehicleAgent.java │ └── resources │ └── server.recon ├── http_lanes ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── ingress_bridges ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── UnitAgent.java │ │ ├── mqtt │ │ ├── DataSourcePopulator.java │ │ └── IngressBridge.java │ │ └── warp │ │ ├── SourceAgent.java │ │ └── SourcePlane.java │ └── resources │ ├── server.recon │ └── source.recon ├── introspection ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── introspection │ │ ├── BasicPlane.java │ │ ├── BuildingAgent.java │ │ ├── RoomAgent.java │ │ └── RoomSimulatorAgent.java │ └── resources │ └── server.recon ├── jms_ingestion ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── vehicle │ │ ├── Assets.java │ │ ├── JmsAgent.java │ │ ├── Main.java │ │ └── VehicleAgent.java │ └── resources │ └── server.recon ├── join_map_lanes ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── AggregatedStatisticsAgent.java │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── StreetStatisticsAgent.java │ └── resources │ └── server.recon ├── join_value_lanes ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── BuildingAgent.java │ │ ├── CustomClient.java │ │ └── RoomAgent.java │ └── resources │ └── server.recon ├── kafka_ingestion ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── vehicle │ │ ├── Assets.java │ │ ├── KafkaConsumingAgent.java │ │ ├── Main.java │ │ └── VehicleAgent.java │ └── resources │ └── server.recon ├── map_lanes ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── mongodb_ingestion ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── vehicle │ │ ├── Assets.java │ │ ├── Main.java │ │ ├── MongoDbPollingAgent.java │ │ └── VehicleAgent.java │ └── resources │ └── server.recon ├── planes ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── project.gradle ├── pulsar_ingestion ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── vehicle │ │ ├── Assets.java │ │ ├── Main.java │ │ ├── PulsarConsumingAgent.java │ │ └── VehicleAgent.java │ └── resources │ └── server.recon ├── server_downlinks ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── CustomerAgent.java │ │ ├── SupplierAgent.java │ │ ├── SupplierPlane.java │ │ ├── WarehouseAgent.java │ │ └── WarehousePlane.java │ └── resources │ ├── supplier.recon │ └── warehouse.recon ├── settings.gradle ├── summary_statistics ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── tower │ │ ├── AbstractTowerAgent.java │ │ ├── BucketedTowerAgent.java │ │ ├── Main.java │ │ ├── Simulation.java │ │ ├── TowerAgent.java │ │ ├── TowerSummaryState.java │ │ └── WindowedTowerAgent.java │ └── resources │ └── server.recon ├── time_series ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ └── swim │ │ └── timeseries │ │ ├── BasicPlane.java │ │ ├── CountWindowAgent.java │ │ ├── HistoryLoggingAgent.java │ │ ├── RecencyWindowAgent.java │ │ └── TimeWindowAgent.java │ └── resources │ └── server.recon ├── timers ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon ├── traits ├── README.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── liquid │ │ ├── LiquidPlane.java │ │ └── agent │ │ ├── JuiceAgent.java │ │ ├── LiquidAgent.java │ │ └── WaterAgent.java │ └── resources │ └── server.recon ├── value_lanes ├── README.md ├── README.zh-cn.md ├── build.gradle └── src │ └── main │ ├── java │ ├── module-info.java │ └── swim │ │ └── basic │ │ ├── BasicPlane.java │ │ ├── CustomClient.java │ │ └── UnitAgent.java │ └── resources │ └── server.recon └── web_agents ├── README.md ├── README.zh-cn.md ├── build.gradle └── src └── main ├── java ├── module-info.java └── swim │ └── basic │ ├── BasicPlane.java │ └── UnitAgent.java └── resources └── server.recon /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.DS_Store 3 | .idea 4 | .gradle 5 | .vscode 6 | *.iml 7 | build 8 | bin 9 | out 10 | Listener-tcpioteclipseorg1883/ 11 | Writer-tcpioteclipseorg1883/ 12 | -------------------------------------------------------------------------------- /README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Cookbook(烹饪书) 2 | 3 | 所有的[cookbook](https://swimos.org/tutorials/) 代码片段,组合成了可运行的Gradle项目。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行Java类 8 | 9 | 运行任何cookbook要求运行至少一个Java类 10 | 11 | ### 方法1:使用IDE创建且运行 12 | 13 | 只需要浏览到需要运行的类(集),然后通过IDE运行它(们) 14 | 15 | ### 方法2:使用Gradle创建且运行 16 | 17 | 每一个cookbook代表了一个Swim 伺服器(Server)。为一个已知的cookbook运行伺服器,可简单地用命令行来发送 `gradle $COOKBOOK-NAME:run` (或者是它的其他变异之一,这在接下来的部分中会概括)来执行。例如:`gradle web-agents:run` 运行cookbook网络代理 (Web Agent)。 18 | 19 | Cookbook的展示中会需要运行多个Java类,用户将会发现每个Java类都会有相对应的不同Gradle run task。在所有的cookbook的子目录中都可以找到专门的README,里面描述了如何运行子目录Task。例如:要使得完全运行Command Lanes cookbook,用户必须在该目录中发送 `gradle command-lanes:run` 和 `gradle command-lanes:runClient`。 20 | 21 | #### `gradle` 的变异 22 | 23 | 用户只有在安装了Gradle的机器上才能够使用 `gradle` 命令。补充:cookbook只支持Gradle 5.2 版本及以上 24 | 25 | 若用户并不希望安装或者更新当地Gradle分配,用户可以使用以下的命令来代替 `gradle`: 26 | 27 | - `./gradlew` 适用于大部分非Windows环境 28 | 29 | - `.\gradlew.bat` 适用于Windows 30 | 31 | ### 方法3:使用Gradle创建,但用Java运行 32 | 33 | 创建和包装一个cookbook,只需要运行 `gradle $COOKBOOK-NAME:build`(或者是之前叙述过的 `gradle` 变异命令之一) 34 | 35 | 该命令将会在 `$COOKBOOK_NAME/build/distributions/` 目录下创建 `.tar` 和 `.zip` 文件。解压其中任意一个文件使脚本文件显示在`bin`目录下 36 | 37 | 在默认情况下,引发的脚本将会只运行指定的cookbook的主要Swim伺服器。对于那些需要运行多个Java类的cookbook,最简单地方法是在新文件中精确地复制黏贴脚本,然后将默认主函类换成目标主类。 38 | 39 | 举例来说,[出口网桥cookbook](/egress_bridges) 需要按顺序运行 `swim.grade.db.Database`, `swim.grade.GradePlane`, 和 `swim.grade.Sim` 类。在接下来的步骤中将会介绍如何配置环境使得机器可运行以上类(该教程假设基于*nix环境,但相对应的Windows指令不言自明): 40 | 41 | 1. `gradle egress-bridges:build`(或者其他之前叙述过的 `gradle` 变异) 42 | 43 | 2. `cd egress_bridges/build/distributions/` 44 | 45 | 3. `unzip egress-bridges-3.10.0.zip` 46 | 47 | 4. `cd egress-bridges-3.10.0/bin`(若cookbook只需要运行一个Java类,则到此已完成运行) 48 | 49 | 5. `cp egress-bridges database` (在Windows环境中使用 `.bat` 文件) 50 | 51 | 6. 编辑 `database(.bat)` 文件,将 `swim.grade.GradePlane` 替换成 `swim.grade.db.Database` 52 | 53 | 7. `cp egress-bridges sim`(在Windows环境中使用 `.bat` 文件) 54 | 55 | 8. 编辑 `sim(.bat)`, 将 `swim.grade.GradePlane` 替换成 `swim.grade.Sim` 56 | 57 | 此时运行cookbook只需要在之前提到的 `bin/` 目录下按照顺序运行 `./database`,`./egress-bridges`,以及 `./sim` 即可。 58 | -------------------------------------------------------------------------------- /agent_auth/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Authentication and Authorisation cookbook' 4 | ext.moduleName = 'swim_auth' 5 | mainClassName = 'swim_auth.MainPlane' 6 | -------------------------------------------------------------------------------- /agent_auth/src/main/java/swim_auth/AuthPolicy.java: -------------------------------------------------------------------------------- 1 | package swim_auth; 2 | 3 | import swim.api.auth.Identity; 4 | import swim.api.policy.AbstractPolicy; 5 | import swim.api.policy.PolicyDirective; 6 | import swim.warp.Envelope; 7 | 8 | public class AuthPolicy extends AbstractPolicy { 9 | 10 | protected PolicyDirective authorize(Envelope envelope, Identity identity) { 11 | // Add your custom authorization logic here. 12 | if (identity.isAuthenticated()) { 13 | return allow(); 14 | } else { 15 | return forbid(); 16 | } 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /agent_auth/src/main/java/swim_auth/MainPlane.java: -------------------------------------------------------------------------------- 1 | package swim_auth; 2 | 3 | import swim.api.plane.AbstractPlane; 4 | import swim.kernel.Kernel; 5 | import swim.server.ServerLoader; 6 | 7 | public class MainPlane extends AbstractPlane { 8 | 9 | public MainPlane() { 10 | context.setPolicy(new AuthPolicy()); 11 | } 12 | 13 | public static void main(String[] args) { 14 | final Kernel kernel = ServerLoader.loadServer(); 15 | System.out.println("Starting server..."); 16 | kernel.start(); 17 | System.out.println("Running server..."); 18 | kernel.run(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /agent_auth/src/main/java/swim_auth/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2022 SWIM.AI inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim_auth; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | 22 | public class UnitAgent extends AbstractAgent { 23 | 24 | @SwimLane("info") 25 | ValueLane info = this.valueLane() 26 | .didSet((newValue, oldValue) -> { 27 | logMessage("`info` set to {" + newValue + "} from {" + oldValue + "}"); 28 | }); 29 | 30 | @SwimLane("publishInfo") 31 | CommandLane publishInfo = this.commandLane() 32 | .onCommand(msg -> { 33 | this.info.set("from publishInfo: " + msg); 34 | }); 35 | 36 | private void logMessage(Object msg) { 37 | System.out.println(nodeUri() + ": " + msg); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /agent_auth/src/main/java/utils/PublicKeyConverter.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Base64; 5 | 6 | public class PublicKeyConverter { 7 | 8 | public static void main(String[] args) { 9 | String n = "qsY3Ky__MCM5c3p4ZRdZPn-QnvI84-P1GuY5q9zWbEGKXY2I4bJ_ZvAG092RHxUeAKE7SyfP6TPiOmpc5KVwd3WPLHcGXeNvCunxDsscVdBFQhq3_NUqgJDijOVK1ze5eKQiSbE2VyQ-bRoXN3j7rRPHGKf-OdBvCmeYU6zk6td8wgzNKxtBSnsuZBqeO5JFMxDGtDZc3hooH4CIWPqPj_h9lnikLXCtp2JwXkQW0jRKvaNpv_tPDN6O6Cs9XCm4n5nxjj8xM3W-x8USuPc3sCx3s3BzY8xLFi2xvX_GrGdiAU63xGEV8s03AN_f6xVCX0TAIh1ek1ppVBDvblK9y9eMASi5AMR62gJ5lOq3x_G93oWop-6zeupmOXYxPqtCGvqY4BN2AZUg8q8UmFSGrWImrTjbpocC4LoYB5NT-9f5jKxa6C-7LQp-6gF1KYAKtGi4SMPN5lJNRxXNpSz4Uqq8xcKjNSXeuy9tdh-94ktdikzWILJKkBjIIdDGYqQ5"; 10 | String e = "AQAB"; 11 | 12 | BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(n)); 13 | BigInteger publicExponent = new BigInteger(1, Base64.getUrlDecoder().decode(e)); 14 | System.out.println("modulus: " + modulus); 15 | System.out.println("publicExponent: " + publicExponent); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /agent_auth/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | swim_auth: @fabric { 2 | @plane(class: "swim_auth.MainPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim_auth.UnitAgent") 6 | } 7 | auth: @auth { 8 | # Provide the name of the token that is going to be used in the RECON generated by the initial `auth` message sent by the client. 9 | @token("access_token") 10 | 11 | ########################################################## 12 | # Everything bellow this line is used to verify the JWTs # 13 | ########################################################## 14 | # Specify the issuer of the token 15 | @issuer("Nstream") 16 | # Specify the audience of the token 17 | @audience("nstream-app") 18 | 19 | # Provide a public key to check the signature of the JWT 20 | # This can be done using a URL to the key 21 | # publicKeyUri: "https://myapp.auth0.com/.well-known/jwks.json" 22 | # Or by directly providing the key 23 | @RSAPublicKey { 24 | modulus: 123 25 | publicExponent: 456 26 | } 27 | 28 | # Provide any additional claims that you need to verify 29 | @claim(user_type: "admin") 30 | } 31 | } 32 | @web(port: 9001) { 33 | space: "swim_auth" 34 | } 35 | 36 | -------------------------------------------------------------------------------- /aggregations/README.md: -------------------------------------------------------------------------------- 1 | # Aggregations 2 | 3 | Code corresponding to the [Aggregations Guide](https://www.swimos.org/guides/aggregations.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.aggregations.BasicPlane` class. 10 | 11 | Thus, `gradle aggregations:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `gradle aggregations:build`, unpackage either artifact in `aggregations/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /aggregations/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Aggregations guide' 4 | ext.moduleName = 'swim.aggregations' 5 | mainClassName = 'swim.aggregations.BasicPlane' 6 | -------------------------------------------------------------------------------- /aggregations/src/main/java/swim/aggregations/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.aggregations; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) throws InterruptedException { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("aggregations"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | // Start 10 vehicles 37 | for (int i = 0; i < 10; i++) { 38 | space.command("/vehicle/" + i, "startVehicle", Value.absent()); 39 | Thread.sleep(1000); 40 | } 41 | 42 | Thread.sleep(60000); 43 | 44 | kernel.stop(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /aggregations/src/main/java/swim/aggregations/StateLoggingAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.aggregations; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | import swim.recon.Recon; 22 | import swim.structure.Value; 23 | import swim.uri.Uri; 24 | 25 | public class StateLoggingAgent extends AbstractAgent { 26 | 27 | public StateLoggingAgent() { 28 | } 29 | 30 | @SwimLane("status") 31 | private ValueLane status = this.valueLane() 32 | .didSet((nv, ov) -> info(nodeUri() + ": new status: " + Recon.toString(nv))); 33 | 34 | @SwimLane("addVehicle") 35 | private CommandLane addVehicle = this.commandLane() 36 | .onCommand(v -> info(nodeUri() + ": vehicle entering state: " + v)); 37 | 38 | @SwimLane("removeVehicle") 39 | private CommandLane removeVehicle = this.commandLane() 40 | .onCommand(v -> info(nodeUri() + ": vehicle leaving state: " + v)); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /aggregations/src/main/java/swim/aggregations/StationaryVehicleAgent.java: -------------------------------------------------------------------------------- 1 | package swim.aggregations; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.ValueLane; 7 | import swim.structure.Value; 8 | import swim.uri.Uri; 9 | 10 | /** 11 | * This agent is not used in the demo. 12 | * It is included to show the use of the {@link AbstractAgent#didStart()} method. 13 | */ 14 | public class StationaryVehicleAgent extends AbstractAgent { 15 | 16 | public StationaryVehicleAgent() { 17 | } 18 | 19 | @SwimLane("addEvent") 20 | private CommandLane addEvent = this.commandLane() 21 | .onCommand(v -> this.status.set(v)); 22 | 23 | @SwimLane("status") 24 | private ValueLane status = this.valueLane(); 25 | 26 | private void joinState(final String state) { 27 | command( 28 | "/state/" + state, 29 | "addVehicle", 30 | Uri.form().mold(nodeUri()).toValue() 31 | ); 32 | } 33 | 34 | @Override 35 | public void didStart() { 36 | joinState("California"); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /aggregations/src/main/java/swim/aggregations/VehicleAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.aggregations; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | import swim.structure.Value; 22 | import swim.uri.Uri; 23 | 24 | public class VehicleAgent extends AbstractAgent { 25 | 26 | public VehicleAgent() { 27 | } 28 | 29 | private String currentState; 30 | 31 | @SwimLane("addEvent") 32 | private CommandLane addEvent = this.commandLane() 33 | .onCommand(v -> this.status.set(v)); 34 | 35 | @SwimLane("status") 36 | private ValueLane status = this.valueLane() 37 | .didSet((nv, ov) -> joinState(nv.get("state").stringValue(null))); 38 | 39 | private void joinState(final String state) { 40 | if (isSameAsCurrentState(state)) { 41 | // If the new state is the same as the current state, then do nothing 42 | return; 43 | } 44 | 45 | if (this.currentState != null) { 46 | command("/state/" + this.currentState, 47 | "removeVehicle", 48 | Uri.form().mold(nodeUri()).toValue() 49 | ); 50 | } 51 | 52 | if (state != null) { 53 | command( 54 | "/state/" + state, 55 | "addVehicle", 56 | Uri.form().mold(nodeUri()).toValue() 57 | ); 58 | } 59 | 60 | this.currentState = state; 61 | } 62 | 63 | private boolean isSameAsCurrentState(final String state) { 64 | return (this.currentState == null && state == null) 65 | || (this.currentState != null && this.currentState.equals(state)); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /aggregations/src/main/java/swim/aggregations/VehicleSimulatorAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.aggregations; 16 | 17 | import java.util.Random; 18 | import swim.api.SwimLane; 19 | import swim.api.agent.AbstractAgent; 20 | import swim.api.lane.ValueLane; 21 | import swim.structure.Record; 22 | import swim.structure.Value; 23 | 24 | public class VehicleSimulatorAgent extends AbstractAgent { 25 | 26 | public VehicleSimulatorAgent() { 27 | } 28 | 29 | private static final String[] STATES = {"California", "Oregon"}; 30 | private static final int MAX_SPEED = 100; 31 | private static final long UPDATE_INTERVAL = 10000L; 32 | 33 | @SwimLane("status") 34 | private ValueLane status = this.valueLane(); 35 | 36 | private Value randomVehicleStatus() { 37 | 38 | final boolean isMoving = new Random().nextBoolean(); 39 | 40 | return Record.create(3) 41 | .slot("state", randomState()) 42 | .slot("isMoving", isMoving) 43 | .slot("speed", isMoving ? randomSpeed() : 0); 44 | } 45 | 46 | private String randomState() { 47 | return STATES[ new Random().nextInt(STATES.length) ]; 48 | } 49 | 50 | private int randomSpeed() { 51 | return new Random().nextInt(MAX_SPEED) + 1; 52 | } 53 | 54 | private void setRandomStatusAndSetTimer() { 55 | this.status.set(randomVehicleStatus()); 56 | setTimer(UPDATE_INTERVAL, this::setRandomStatusAndSetTimer); 57 | } 58 | 59 | @Override 60 | public void didStart() { 61 | setRandomStatusAndSetTimer(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /aggregations/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | aggregations: @fabric { 2 | @plane(class: "swim.aggregations.BasicPlane") 3 | 4 | @node { 5 | pattern: "/state/:name" 6 | @agent(class: "swim.aggregations.StateAgent") 7 | @agent(class: "swim.aggregations.StateLoggingAgent") 8 | } 9 | 10 | @node { 11 | pattern: "/vehicle/:id" 12 | @agent(class: "swim.aggregations.VehicleAgent") 13 | @agent(class: "swim.aggregations.VehicleSimulatorAgent") 14 | } 15 | 16 | } 17 | @web(port: 9001) { 18 | space: "aggregations" 19 | @websocket { 20 | serverCompressionLevel: 0 21 | # -1 = default; 0 = off; 1-9 = deflate level 22 | clientCompressionLevel: 0 23 | # -1 = default; 0 = off; 1-9 = deflate level 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /auth_policy/README.md: -------------------------------------------------------------------------------- 1 | # Auth Policy 2 | 3 | Code corresponding to the [Auth Policy cookbook](https://swimos.org/tutorials/auth-policy/). 4 | 5 | ## Running This Demo 6 | 7 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 8 | -------------------------------------------------------------------------------- /auth_policy/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Auth Policy cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /auth_policy/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /auth_policy/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.plane.AbstractPlane; 4 | import swim.kernel.Kernel; 5 | import swim.server.ServerLoader; 6 | import swim.structure.Value; 7 | 8 | /** 9 | * The complimentary code as part of the Auth Policy cookbook. 10 | *

11 | * In this cookbook, a plane is created with an auth policy. Requests to the server will only be accepted if the token 12 | * provided has sufficient permissions. 13 | *

14 | * See {@link CustomClient} 15 | */ 16 | public class BasicPlane extends AbstractPlane { 17 | 18 | public BasicPlane() { 19 | context.setPolicy(new BasicPolicy()); 20 | } 21 | 22 | public static void main(String[] args) { 23 | final Kernel kernel = ServerLoader.loadServer(); 24 | 25 | System.out.println("Starting server..."); 26 | kernel.start(); 27 | kernel.run(); 28 | } 29 | 30 | @Override 31 | public void didStart() { 32 | super.didStart(); 33 | command("/unit", "WAKEUP", Value.absent()); 34 | command("/control", "WAKEUP", Value.absent()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /auth_policy/src/main/java/swim/basic/BasicPolicy.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.auth.Identity; 4 | import swim.api.policy.AbstractPolicy; 5 | import swim.api.policy.PolicyDirective; 6 | import swim.warp.Envelope; 7 | 8 | public class BasicPolicy extends AbstractPolicy { 9 | 10 | public BasicPolicy() { 11 | } 12 | 13 | private static final String ADMIN_TOKEN = "abc123"; 14 | private static final String USER_TOKEN = "abc"; 15 | 16 | @Override 17 | protected PolicyDirective authorize(Envelope envelope, Identity identity) { 18 | if (identity != null) { 19 | final String token = identity.requestUri().query().get("token"); 20 | 21 | //Always authorize admins 22 | if (ADMIN_TOKEN.equals(token)) { 23 | logRequest(true, envelope.nodeUri().toString(), envelope.laneUri().toString(), token); 24 | return allow(); 25 | } 26 | 27 | //Admin tokens must be used for 'adminInfo' lanes or '/control' agents 28 | if ("adminInfo".equals(envelope.laneUri().toString()) 29 | || envelope.nodeUri().toString().startsWith("/control")) { 30 | logRequest(false, envelope.nodeUri().toString(), envelope.laneUri().toString(), token); 31 | return forbid(); 32 | } 33 | 34 | //Users can access any remaining lanes 35 | if (USER_TOKEN.equals(token)) { 36 | logRequest(true, envelope.nodeUri().toString(), envelope.laneUri().toString(), token); 37 | return allow(); 38 | } 39 | logRequest(false, envelope.nodeUri().toString(), envelope.laneUri().toString(), token); 40 | } 41 | return forbid(); 42 | } 43 | 44 | private static void logRequest(final boolean accepted, final String nodeUri, final String laneUri, final String token) { 45 | System.out.println("policy: " + (accepted ? "Accepted " : "Rejected ") + "request to " + nodeUri + "/" + laneUri + " with token " + token); 46 | } 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /auth_policy/src/main/java/swim/basic/ControlAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.structure.Value; 7 | 8 | public class ControlAgent extends AbstractAgent { 9 | 10 | public ControlAgent() { 11 | } 12 | 13 | @SwimLane("command") 14 | CommandLane command = this.commandLane() 15 | .onCommand(value -> logMessage("command: " + value.toString())); 16 | 17 | @Override 18 | public void didStart() { 19 | super.didStart(); 20 | System.out.println(nodeUri() + " didStart"); 21 | } 22 | 23 | private void logMessage(final String message) { 24 | System.out.println(nodeUri() + ": " + message); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /auth_policy/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.ValueLane; 7 | import swim.structure.Value; 8 | 9 | public class UnitAgent extends AbstractAgent { 10 | 11 | public UnitAgent() { 12 | } 13 | 14 | @SwimLane("info") 15 | ValueLane info = this.valueLane() 16 | .didSet((newValue, oldValue) -> logMessage("info changed from " + oldValue + " to " + newValue)); 17 | 18 | @SwimLane("setInfo") 19 | CommandLane setInfo = this.commandLane() 20 | .onCommand(body -> this.info.set(body)); 21 | 22 | @SwimLane("adminInfo") 23 | ValueLane adminInfo = this.valueLane() 24 | .didSet((newValue, oldValue) -> logMessage("adminInfo changed from " + oldValue + " to " + newValue)); 25 | 26 | @Override 27 | public void didStart() { 28 | super.didStart(); 29 | System.out.println(nodeUri() + " didStart"); 30 | } 31 | 32 | private void logMessage(final String message) { 33 | System.out.println(nodeUri() + ": " + message); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /auth_policy/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | uri: "/unit" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | @node { 8 | uri: "/control" 9 | @agent(class: "swim.basic.UnitAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "basic" 14 | documentRoot: "./ui/" 15 | @websocket { 16 | serverCompressionLevel: 0 17 | # -1 = default; 0 = off; 1-9 = deflate level 18 | clientCompressionLevel: 0 19 | # -1 = default; 0 = off; 1-9 = deflate level 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /command_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Command Lanes 2 | 3 | Code corresponding to the [Command Lanes cookbook](https://swimos.org/tutorials/command-lanes/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 10 | -------------------------------------------------------------------------------- /command_lanes/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Command Lanes 2 | 3 | 代码可在 [Command Lanes cookbook](https://swimos.org/tutorials/command-lanes/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Command Lanes 演示 8 | 9 | 该演示需要按照顺序依次运行 `swim.basic.BasicPlane` 类和 `swim.basic.CustomClient 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.basic.CustomClient` 的名称为 `runClient`。 10 | -------------------------------------------------------------------------------- /command_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Command Lanes cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /command_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /command_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | space.command("/unit/foo", "wakeup", Value.absent()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /command_lanes/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.downlink.EventDownlink; 18 | import swim.client.ClientRuntime; 19 | import swim.structure.Num; 20 | import swim.structure.Value; 21 | 22 | final class CustomClient { 23 | 24 | private CustomClient() { 25 | } 26 | 27 | public static void main(String[] args) throws InterruptedException { 28 | final ClientRuntime swimClient = new ClientRuntime(); 29 | swimClient.start(); 30 | 31 | final String hostUri = "warp://localhost:9001"; 32 | final String nodeUri = "/unit/foo"; 33 | // Reduce probability of startup race; no need to conflate this example 34 | // with proper synchronization just yet 35 | swimClient.command(hostUri, nodeUri, "WAKEUP", Value.absent()); 36 | 37 | final EventDownlink link = swimClient.downlink() 38 | .hostUri(hostUri).nodeUri(nodeUri).laneUri("publishValue") 39 | .onEvent((Value event) -> { 40 | System.out.println("link received event: " + event); 41 | }) 42 | .open(); 43 | final Value msg = Num.from(9035768); 44 | // command() `msg` TO 45 | // the "publish" lane OF 46 | // the agent addressable by `/unit/foo` RUNNING ON 47 | // the plane with hostUri "warp://localhost:9001" 48 | swimClient.command(hostUri, nodeUri, "publish", msg); 49 | Thread.sleep(2000); 50 | swimClient.stop(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /command_lanes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.recon.Recon; 21 | import swim.structure.Record; 22 | import swim.structure.Value; 23 | import swim.uri.Uri; 24 | 25 | public class UnitAgent extends AbstractAgent { 26 | 27 | public UnitAgent() { 28 | } 29 | 30 | @SwimLane("publish") 31 | CommandLane publish = this.commandLane() 32 | .onCommand((Integer msg) -> { 33 | logMessage("`publish` commanded with " + msg); 34 | final Value updatedMsg = Record.create(1).slot("fromServer", msg); 35 | // command() `updatedMsg` TO 36 | // the "publishValue" lane OF 37 | // the agent addressable by `nodeUri()` RUNNING ON 38 | // this plane (indicated by no hostUri argument) 39 | command(nodeUri(), Uri.parse("publishValue"), updatedMsg); 40 | }); 41 | 42 | @SwimLane("publishValue") 43 | CommandLane publishV = this.commandLane() 44 | .onCommand((Value msg) -> { 45 | logMessage("`publishValue` commanded with " + Recon.toString(msg)); 46 | }); 47 | 48 | private void logMessage(Object msg) { 49 | System.out.println(nodeUri() + ": " + msg); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /command_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demand_map_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Demand Map Lanes 2 | 3 | Code corresponding to the [Demand Map Lanes cookbook](https://swimos.org/tutorials/demand-map-lanes/). 4 | 5 | ## Running This Demo 6 | 7 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 8 | -------------------------------------------------------------------------------- /demand_map_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Demand Map Lane cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /demand_map_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /demand_map_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import java.util.Base64; 4 | import swim.actor.ActorSpace; 5 | import swim.api.downlink.MapDownlink; 6 | import swim.api.plane.AbstractPlane; 7 | import swim.kernel.Kernel; 8 | import swim.server.ServerLoader; 9 | import swim.structure.Form; 10 | import swim.structure.Value; 11 | 12 | /** 13 | * The complimentary code as part of the Demand Map Lanes cookbook. 14 | *

15 | * In this cookbook, an agent is created with a Demand Map Lane. This Demand Lane will only transform data when there is a downlink 16 | * that is subscribed to it, for given key. 17 | *

18 | * See {@link CustomClient} 19 | */ 20 | public class BasicPlane extends AbstractPlane { 21 | 22 | public BasicPlane() { 23 | } 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | final Kernel kernel = ServerLoader.loadServer(); 27 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 28 | 29 | System.out.println("Starting server..."); 30 | kernel.start(); 31 | kernel.run(); 32 | 33 | final MapDownlink rawDownlink = space.downlinkMap() 34 | .keyForm(Form.forString()).valueForm(Form.forString()) 35 | .nodeUri("/unit").laneUri("raw") 36 | .didUpdate((key, newValue, oldValue) -> { 37 | if (!oldValue.equals(newValue)) { 38 | System.out.println("raw updated entry " + key + " : '" + newValue + "'"); 39 | } 40 | }) 41 | .open(); 42 | 43 | //Incrementally update raw lane so downlink can observe changes 44 | int messageNumber = 0; 45 | while (true) { 46 | Thread.sleep(2000); 47 | rawDownlink.put("foo", encode("foo_message_" + messageNumber)); 48 | rawDownlink.put("bar", encode("bar_message_" + messageNumber++)); 49 | } 50 | } 51 | 52 | private static String encode(final String rawMessage) { 53 | return Base64.getEncoder().encodeToString(rawMessage.getBytes()); 54 | } 55 | 56 | @Override 57 | public void didStart() { 58 | super.didStart(); 59 | //Immediately wake up the Unit Agent on plane load 60 | command("/unit", "wakeup", Value.absent()); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /demand_map_lanes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import java.util.Base64; 4 | import java.util.Collections; 5 | import swim.api.SwimLane; 6 | import swim.api.agent.AbstractAgent; 7 | import swim.api.lane.DemandMapLane; 8 | import swim.api.lane.MapLane; 9 | 10 | public class UnitAgent extends AbstractAgent { 11 | 12 | public UnitAgent() { 13 | } 14 | 15 | @SwimLane("raw") 16 | protected MapLane raw = this.mapLane() 17 | .didUpdate((key, newValue, oldValue) -> this.data.cue(key)); 18 | 19 | @SwimLane("data") 20 | protected DemandMapLane data = this.demandMapLane() 21 | .onCue((key, uplink) -> { 22 | final String name = uplink.laneUri().query().get("name"); 23 | //Decode if no parameter was passed by the uplink or if the key matches the parameter 24 | return (name == null || name.equals(key)) ? decodeRaw(key) : null; 25 | }) 26 | .onSync(uplink -> { 27 | final String name = uplink.laneUri().query().get("name"); 28 | return (this.raw.containsKey(name)) ? Collections.singletonList(name).iterator() : this.raw.keyIterator(); 29 | }); 30 | 31 | private String decodeRaw(String key) { 32 | final String encoded = this.raw.get(key); 33 | if (encoded == null) { 34 | return ""; 35 | } 36 | final String decoded = new String(Base64.getDecoder().decode(encoded.getBytes())); 37 | System.out.println(nodeUri() + ": Decoded raw data to: " + decoded); 38 | return decoded; 39 | } 40 | 41 | @Override 42 | public void didStart() { 43 | System.out.println(nodeUri() + " did start"); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /demand_map_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | uri: "/unit" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demand_value_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Demand Value Lanes 2 | 3 | Code corresponding to the [Demand Value Lanes cookbook](https://swimos.org/tutorials/demand-value-lanes/). 4 | 5 | ## Running This Demo 6 | 7 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 8 | -------------------------------------------------------------------------------- /demand_value_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Demand Value Lane cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /demand_value_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /demand_value_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import java.util.Base64; 4 | import swim.actor.ActorSpace; 5 | import swim.api.downlink.ValueDownlink; 6 | import swim.api.plane.AbstractPlane; 7 | import swim.kernel.Kernel; 8 | import swim.server.ServerLoader; 9 | import swim.structure.Form; 10 | import swim.structure.Value; 11 | 12 | /** 13 | * The complimentary code as part of the Demand Value Lanes cookbook. 14 | *

15 | * In this cookbook, an agent is created with a Demand Lane. This Demand Lane will only transform data when there is a downlink 16 | * that is subscribed to it. 17 | *

18 | * See {@link CustomClient} 19 | */ 20 | public class BasicPlane extends AbstractPlane { 21 | 22 | public BasicPlane() { 23 | } 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | final Kernel kernel = ServerLoader.loadServer(); 27 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 28 | 29 | System.out.println("Starting server..."); 30 | kernel.start(); 31 | kernel.run(); 32 | 33 | final ValueDownlink rawDownlink = space.downlinkValue() 34 | .valueForm(Form.forString()) 35 | .nodeUri("/unit").laneUri("raw") 36 | .didSet((n, o) -> { 37 | if (!n.equals(o)) { 38 | System.out.println("raw updated from '" + o + "' to '" + n + "'"); 39 | } 40 | }) 41 | .open(); 42 | 43 | //Incrementally change the raw lane so the client can observe changes 44 | int messageNumber = 0; 45 | while (true) { 46 | Thread.sleep(1000); 47 | rawDownlink.set(encode("MESSAGE_" + messageNumber++)); 48 | } 49 | } 50 | 51 | private static String encode(final String rawMessage) { 52 | return Base64.getEncoder().encodeToString(rawMessage.getBytes()); 53 | } 54 | 55 | @Override 56 | public void didStart() { 57 | super.didStart(); 58 | //Immediately wake up the Unit Agent on plane load 59 | command("/unit", "wakeup", Value.absent()); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /demand_value_lanes/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.downlink.ValueDownlink; 4 | import swim.client.ClientRuntime; 5 | import swim.structure.Form; 6 | 7 | /** 8 | * The complimentary code as part of the Demand Value Lanes cookbook. 9 | *

10 | * In this cookbook, an agent is created with a Demand Lane. This Demand Lane will only transform data when there is a downlink 11 | * that is subscribed to it. 12 | *

13 | * See {@link BasicPlane} 14 | */ 15 | public final class CustomClient { 16 | 17 | private CustomClient() { 18 | } 19 | 20 | public static void main(String[] args) throws InterruptedException { 21 | final ClientRuntime swimClient = new ClientRuntime(); 22 | swimClient.start(); 23 | 24 | final String hostUri = "warp://localhost:9001"; 25 | 26 | System.out.println("Opening downlink to data, raw will start being decoded"); 27 | final ValueDownlink dataDownlnink = swimClient.downlinkValue() 28 | .valueForm(Form.forString()) 29 | .hostUri(hostUri) 30 | .nodeUri("/unit").laneUri("data") 31 | .didSet((n, o) -> System.out.println("data updated from '" + o + "' to '" + n + "'")) 32 | .open(); 33 | 34 | Thread.sleep(10000); 35 | System.out.println("Closing downlink to data, raw will stop being decoded"); 36 | dataDownlnink.close(); 37 | swimClient.stop(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /demand_value_lanes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import java.util.Base64; 4 | import swim.api.SwimLane; 5 | import swim.api.agent.AbstractAgent; 6 | import swim.api.lane.DemandLane; 7 | import swim.api.lane.ValueLane; 8 | import swim.api.warp.WarpUplink; 9 | 10 | public class UnitAgent extends AbstractAgent { 11 | 12 | public UnitAgent() { 13 | } 14 | 15 | @SwimLane("raw") 16 | ValueLane raw = this.valueLane().didSet((n, o) -> this.data.cue()); 17 | 18 | @SwimLane("data") 19 | DemandLane data = this.demandLane() 20 | .onCue(this::decodeRaw); 21 | 22 | // Transform raw data to the desired format 23 | private String decodeRaw(WarpUplink uplink) { 24 | final String encoded = this.raw.get(); 25 | if (encoded == null) { 26 | return ""; 27 | } 28 | final String decoded = new String(Base64.getDecoder().decode(encoded.getBytes())); 29 | System.out.println(nodeUri() + ": Decoded raw data to: " + decoded); 30 | return decoded; 31 | } 32 | 33 | @Override 34 | public void didStart() { 35 | System.out.println(nodeUri() + " did start"); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /demand_value_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | uri: "/unit" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /downlinks/README.md: -------------------------------------------------------------------------------- 1 | # Downlinks 2 | 3 | Code corresponding to the [Downlinks cookbook](https://swimos.org/tutorials/downlinks/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 10 | -------------------------------------------------------------------------------- /downlinks/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Downlinks 2 | 3 | 代码可在 [Downlinks cookbook](https://swimos.org/tutorials/downlinks/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Downlinks 展示 8 | 9 | 该演示需要按照顺序依次运行 `swim.basic.BasicPlane` 类和 `swim.basic.CustomClient` 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.basic.CustomClient` 的名称为 `runClient`。 10 | -------------------------------------------------------------------------------- /downlinks/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Downlinks cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /downlinks/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /downlinks/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import java.io.IOException; 18 | import swim.actor.ActorSpace; 19 | import swim.api.plane.AbstractPlane; 20 | import swim.kernel.Kernel; 21 | import swim.recon.Recon; 22 | import swim.server.ServerLoader; 23 | import swim.structure.Value; 24 | 25 | public class BasicPlane extends AbstractPlane { 26 | 27 | public BasicPlane() { 28 | } 29 | 30 | public static void main(String[] args) throws IOException, InterruptedException { 31 | final Kernel kernel = ServerLoader.loadServer(); 32 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 33 | kernel.start(); 34 | System.out.println("Running Basic server..."); 35 | kernel.run(); 36 | 37 | // Event downlink issued against plane context 38 | space.downlink() 39 | .nodeUri("/unit/0") 40 | .laneUri("addItem") 41 | .onEvent(v -> { 42 | System.out.println("event downlink saw " + Recon.toString(v)); 43 | }) 44 | .open(); 45 | } 46 | 47 | @Override 48 | public void didStart() { 49 | super.didStart(); 50 | // Immediately wake up ListenerAgent upon plane load 51 | context.command("/listener", "wakeup", Value.absent()); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /downlinks/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.downlink.MapDownlink; 18 | import swim.client.ClientRuntime; 19 | import swim.structure.Form; 20 | import swim.structure.Text; 21 | 22 | final class CustomClient { 23 | 24 | private CustomClient() { 25 | } 26 | 27 | public static void main(String[] args) throws InterruptedException { 28 | 29 | final ClientRuntime swimClient = new ClientRuntime(); 30 | swimClient.start(); 31 | 32 | final String hostUri = "warp://localhost:9001"; 33 | final String nodeUriPrefix = "/unit/"; 34 | 35 | // Write-only downlink; note keepLinked is false 36 | final MapDownlink link = swimClient.downlinkMap() 37 | .keyForm(Form.forString()).valueForm(Form.forInteger()) 38 | .hostUri(hostUri).nodeUri(nodeUriPrefix + "0").laneUri("shoppingCart") 39 | .keepLinked(false) 40 | .open(); 41 | link.put("FromClientLink", 25); 42 | 43 | Thread.sleep(1000); 44 | // Hereafter we will strictly use commands, so link is safe to close 45 | link.close(); 46 | 47 | final String[] items = {"bat", "cat", "rat"}; 48 | for (int i = 0; i < 50; i++) { 49 | // randomly add an item from `items` to the shopping carts of /unit/0, /unit/1, /unit/2 via commands 50 | swimClient.command(hostUri, nodeUriPrefix + (i % 3), "addItem", Text.from(items[(int) (Math.random() * 3)])); 51 | } 52 | 53 | System.out.println("Will shut down client in 2 seconds"); 54 | Thread.sleep(2000); 55 | swimClient.stop(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /downlinks/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.MapLane; 21 | import swim.structure.Text; 22 | 23 | public class UnitAgent extends AbstractAgent { 24 | 25 | public UnitAgent() { 26 | } 27 | 28 | @SwimLane("shoppingCart") 29 | MapLane shoppingCart = this.mapLane() 30 | .didUpdate((key, newValue, oldValue) -> { 31 | logMessage(key + " count changed to " + newValue + " from " + oldValue); 32 | }); 33 | 34 | @SwimLane("addItem") 35 | CommandLane publish = this.commandLane() 36 | .onCommand(msg -> { 37 | final int n = this.shoppingCart.getOrDefault(msg, 0) + 1; 38 | this.shoppingCart.put(msg, n); 39 | }); 40 | 41 | private void logMessage(Object msg) { 42 | System.out.println(nodeUri() + ": " + msg); 43 | } 44 | 45 | @Override 46 | public void didStart() { 47 | command("/listener", "triggerListen", Text.from(nodeUri().toString())); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /downlinks/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | uri: "/listener" 5 | @agent(class: "swim.basic.ListenerAgent") 6 | } 7 | @node { 8 | pattern: "/unit/:id" 9 | @agent(class: "swim.basic.UnitAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "basic" 14 | documentRoot: "./ui/" 15 | @websocket { 16 | serverCompressionLevel: 0 17 | # -1 = default; 0 = off; 1-9 = deflate level 18 | clientCompressionLevel: 0 19 | # -1 = default; 0 = off; 1-9 = deflate level 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /egress_bridges/README.md: -------------------------------------------------------------------------------- 1 | # Egress Bridges 2 | 3 | Code corresponding to the [egress bridges cookbook](https://swimos.org/tutorials/egress-bridges/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Demo Motivation 8 | 9 | Recall the two designs of push-type egress bridges to non-Swim sinks: 10 | 11 | 1. A *singleton* Web Agent writes to the sink through a *non-blocking* driver 12 | 13 | 2. *Any* Web Agent writes to the sink through a *thread-safe, non-blocking* driver 14 | 15 | Our sink in this example is a relational H2 database. Oracle has an [Asynchronous Database Acesss](https://blogs.oracle.com/java/jdbc-next:-a-new-asynchronous-api-for-connecting-to-a-database) (ADBA) API, of which [ADBA over JDBC](https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ) implements a subset. Everything in the `com.oracle.adbaoverjdbc` package in this repository has been pulled straight from there; the only modifications were to make the directory structures match what Gradle expects. 16 | 17 | We scrape together the working pieces to create our non-blocking `CustomDriver`. In order to keep the implementation simple, `CustomDriver` is not thread-safe. We therefore only demonstrate Option 2 in this example. 18 | 19 | ## Running This Demo 20 | 21 | This demo requires running the `swim.grade.db.Database`, `swim.grade.GradePlane`, and `swim.grade.Sim` classes in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.grade.db.Database` is called `runDb`, and the one corresponding to `swim.grade.Sim` is called `runSim`. 22 | 23 | ## Stopping This Demo 24 | 25 | Close aforementioned three processes in the opposite order to which they were run. Immediately before `GradePlane` exits, the process issues a read of each student in the database; you should confirm that these match the final log for each `StudentAgent`. 26 | -------------------------------------------------------------------------------- /egress_bridges/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Egress Bridges(出口网桥) 2 | 3 | 代码可在 [egress bridges cookbook](https://swimos.org/tutorials/egress-bridges/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 展示动机 8 | 9 | 回忆两种 push-type egress bridge(出口网桥)到非Swim sink的设计: 10 | 11 | 1. *独立*网络代理通过 *non-blocking* 驱动写入sink 12 | 13 | 2. *任意*网络代理通过 *thread-safe, non-blocking* 驱动写入sink 14 | 15 | 在此举例中sink指代的是 relational H2 database。Oracle 提供了 [Asynchronous Database Acesss](https://blogs.oracle.com/java/jdbc-next:-a-new-asynchronous-api-for-connecting-to-a-database) (ADBA) API,完成了 [ADBA over JDBC](https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ)的一部分。在此repository中的 `com.oracle.adbaoverjdbc` 包都是直接从API中读取的;只在目录构建中做了改动以匹配Gradle需求。 16 | 17 | 我们拼凑了需要的部分制作了我们自己的 non-blocking `CustomDriver`。为了保证实现的简单化,`CustomDriver` 不是 thread-safe。因此在此举例中我们只演示了方法2。 18 | 19 | ## 运行 egress bridge 展示 20 | 21 | 该展示要求按照顺序依次运行 `swim.grade.db.Database`,`swim.grade.GradePlane`,和 `swim.grade.Sim` 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.grade.db.Database` 的名称为 `runDb`,`swim.grade.Sim` 对应的名称是 `runSim`。 22 | 23 | ## 停止运行 egress bridge 展示 24 | 25 | 按照倒序关闭之前提到的三个运行程序。在 `GradePlane` 即将退出之前,程序会从数据库中读取每个学生的信息;用户可以确认到读取的信息匹对每个 `StudentAgent` 的最终log。 26 | -------------------------------------------------------------------------------- /egress_bridges/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Egress Bridges cookbook' 4 | ext.moduleName = 'swim.grade' 5 | mainClassName = 'swim.grade.GradePlane' 6 | 7 | repositories { 8 | maven { url "https://repo.eclipse.org/content/repositories/paho-snapshots/" } 9 | maven { url "https://dl.bintray.com/pull-vert/adba/" } 10 | } 11 | 12 | dependencies { 13 | api group: 'com.h2database', name: 'h2', version: '1.4.199' 14 | } 15 | 16 | task runSim(type: JavaExec) { 17 | group = "application" 18 | classpath sourceSets.main.runtimeClasspath 19 | mainClass = "swim.grade.Sim" 20 | } 21 | 22 | task runDb(type: JavaExec) { 23 | group = "application" 24 | classpath sourceSets.main.runtimeClasspath 25 | mainClass = "swim.grade.db.Database" 26 | } 27 | -------------------------------------------------------------------------------- /egress_bridges/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | @SuppressWarnings("requires-automatic") 16 | open module swim.grade { 17 | requires transitive swim.api; 18 | requires swim.server; 19 | requires swim.client; 20 | requires com.h2database; 21 | requires java.sql; 22 | } 23 | -------------------------------------------------------------------------------- /egress_bridges/src/main/java/swim/grade/EgressAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.grade; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.grade.db.BlockingStudentsDriver; 21 | import swim.structure.Value; 22 | 23 | public class EgressAgent extends AbstractAgent { 24 | 25 | public EgressAgent() { 26 | } 27 | 28 | @SwimLane("write") 29 | CommandLane write = this.commandLane() 30 | .onCommand(v -> { 31 | BlockingStudentsDriver.updateGrade( 32 | v.get("id").intValue(), 33 | v.get("earned").intValue(), 34 | v.get("possible").intValue()); 35 | }); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /egress_bridges/src/main/java/swim/grade/GradePlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.grade; 16 | 17 | import java.sql.SQLException; 18 | import java.util.concurrent.ForkJoinPool; 19 | import java.util.concurrent.TimeUnit; 20 | import swim.actor.ActorSpace; 21 | import swim.api.plane.AbstractPlane; 22 | import swim.grade.db.BlockingStudentsDriver; 23 | import swim.kernel.Kernel; 24 | import swim.server.ServerLoader; 25 | import swim.structure.Value; 26 | 27 | public class GradePlane extends AbstractPlane { 28 | 29 | public GradePlane() { 30 | } 31 | 32 | public static void main(String[] args) 33 | throws ClassNotFoundException, SQLException { 34 | 35 | // Attempt to start driver 36 | BlockingStudentsDriver.start("tcp://localhost:9002", "~/test", "sa", ""); 37 | 38 | final Kernel kernel = ServerLoader.loadServer(); 39 | final ActorSpace space = (ActorSpace) kernel.getSpace("grade"); 40 | kernel.start(); 41 | System.out.println("Running Grade server..."); 42 | kernel.run(); 43 | 44 | // Immediately wake up EgressAgent 45 | space.command("/egress", "wakeup", Value.absent()); 46 | 47 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 48 | System.out.println("Database sees:"); 49 | BlockingStudentsDriver.logGrade(1); 50 | BlockingStudentsDriver.logGrade(2); 51 | BlockingStudentsDriver.logGrade(3); 52 | BlockingStudentsDriver.logGrade(4); 53 | BlockingStudentsDriver.logGrade(5); 54 | ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.MINUTES); 55 | })); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /egress_bridges/src/main/java/swim/grade/Sim.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.grade; 16 | 17 | import swim.client.ClientRuntime; 18 | import swim.structure.Record; 19 | 20 | final class Sim { 21 | 22 | private Sim() { 23 | } 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | final ClientRuntime client = new ClientRuntime(); 27 | client.start(); 28 | while (true) { 29 | client.command("warp://localhost:9001", 30 | String.format("/student/%d", ((int) (Math.random() * 5)) + 1), 31 | "addAssignment", 32 | Record.create(3) 33 | .attr("assignment") 34 | .slot("earned", ((int) (Math.random() * 5)) + 15) 35 | .slot("possible", 20)); 36 | Thread.sleep(500); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /egress_bridges/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | @kernel(class: "swim.store.db.DbStoreKernel", optional: true) 2 | grade: @fabric { 3 | @plane(class: "swim.grade.GradePlane") 4 | @node { 5 | pattern: "/student/:id" 6 | @agent(class: "swim.grade.StudentAgent") 7 | } 8 | @node { 9 | uri: "/egress" 10 | @agent(class: "swim.grade.EgressAgent") 11 | } 12 | } 13 | @web(port: 9001) { 14 | space: "grade" 15 | documentRoot: "./ui/" 16 | @websocket { 17 | serverCompressionLevel: 0 18 | # -1 = default; 0 = off; 1-9 = deflate level 19 | clientCompressionLevel: 0 20 | # -1 = default; 0 = off; 1-9 = deflate level 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /forms/README.md: -------------------------------------------------------------------------------- 1 | # Forms 2 | 3 | Code corresponding to the [Forms cookbook](https://swimos.org/tutorials/forms/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Forms Motivation 8 | A Form defines a conversion between a structural type, and some POJO. The mold method converts a Java object to an Item. And the cast method converts an Item to a Java object, if possible. 9 | 10 | ## Running This Demo 11 | 12 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 13 | -------------------------------------------------------------------------------- /forms/README.zh-ch.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swimos/cookbook/8a42f9a4164cd2b479826ef5cc2dfe0395767773/forms/README.zh-ch.md -------------------------------------------------------------------------------- /forms/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Forms cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /forms/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /forms/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.plane.AbstractPlane; 4 | import swim.api.space.Space; 5 | import swim.kernel.Kernel; 6 | import swim.server.ServerLoader; 7 | import swim.structure.Value; 8 | 9 | public class BasicPlane extends AbstractPlane { 10 | 11 | public BasicPlane() { 12 | } 13 | 14 | public static void pojoTransformations() { 15 | // FooType instance fooType 16 | final FooType fooType = new FooType(7, "cat"); 17 | 18 | // BarType instance barType 19 | final BarType barType = new BarType(4, "dog", 2); 20 | 21 | final Value fooVal = (Value) FooType.form().mold(fooType); 22 | final Value barVal = (Value) BarType.form().mold(barType); 23 | 24 | System.out.println(FooType.form().cast(fooVal)); 25 | System.out.println(BarType.form().cast(barVal)); 26 | 27 | // The following two statements return null. Why do you think that is? 28 | System.out.println(FooType.form().cast(barVal)); 29 | System.out.println(BarType.form().cast(fooVal)); 30 | 31 | // Consider why the following would fail to compile: 32 | // FooType.form().mold(barType); 33 | 34 | // The following logic exercises introduce the somewhat advanced use for Forms that is mentioned in the cookbook: composability 35 | final BazType bazType = new BazType(fooType, barType); 36 | final Value bazVal = (Value) BazType.form().mold(bazType); 37 | BazType.form().cast(bazVal); 38 | } 39 | 40 | public static void runServer() { 41 | final Kernel kernel = ServerLoader.loadServer(); 42 | final Space space = kernel.getSpace("basic"); 43 | 44 | kernel.start(); 45 | System.out.println("Running Basic server..."); 46 | space.command("/unit/foo", "wakeup", Value.absent()); 47 | kernel.run(); 48 | } 49 | 50 | public static void main(String[] args) { 51 | // Switch the flag to tue to run the server only once you understand what happens in pojoTransformations() 52 | final boolean willRunServer = false; 53 | 54 | pojoTransformations(); 55 | 56 | if (willRunServer) { 57 | runServer(); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /forms/src/main/java/swim/basic/BazType.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.recon.Recon; 4 | import swim.structure.Form; 5 | import swim.structure.Kind; 6 | import swim.structure.Member; 7 | import swim.structure.Tag; 8 | 9 | @Tag("bazType") 10 | public class BazType { 11 | 12 | @Member("fooType") 13 | private FooType f = new FooType(); 14 | @Member("barType") 15 | private BarType b = new BarType(); 16 | 17 | public BazType() { 18 | } 19 | 20 | public BazType(FooType f, BarType b) { 21 | this.f = f; 22 | this.b = b; 23 | } 24 | 25 | public FooType getFooType() { 26 | return this.f; 27 | } 28 | 29 | public BarType getBarType() { 30 | return this.b; 31 | } 32 | 33 | //@Kind-annotated static field and static accessor method 34 | @Kind 35 | private static Form form; 36 | 37 | public static Form form() { 38 | if (form == null) { 39 | form = Form.forClass(BazType.class); 40 | } 41 | return form; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return Recon.toString(form().mold(this)); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /forms/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.client.ClientRuntime; 4 | import swim.structure.Record; 5 | import swim.structure.Value; 6 | 7 | final class CustomClient { 8 | 9 | private CustomClient() { 10 | } 11 | 12 | public static void main(String[] args) throws InterruptedException { 13 | final ClientRuntime swimClient = new ClientRuntime(); 14 | swimClient.start(); 15 | final String hostUri = "warp://localhost:9001"; 16 | final String fooNodeUri = "/unit/foo"; 17 | final String barNodeUri = "/unit/bar"; 18 | 19 | final Value fooVal = Record.create(3).attr("fooType").slot("i", 5).slot("s", "potato"); 20 | 21 | final BarType barObj = new BarType(1, "two", 3); 22 | final Value barVal = (Value) BarType.form().mold(barObj); 23 | 24 | for (int i = 0; i < 10; i++) { 25 | // Commands the addFoo lane of UnitAgent with some Record that is compatible with FooType. 26 | swimClient.command(hostUri, fooNodeUri, "addFoo", fooVal); 27 | 28 | // Commands the addBar lane of UnitAgent with BarType molded into type Value 29 | swimClient.command(hostUri, barNodeUri, "addBar", barVal); 30 | 31 | // Commands the addValue lane of UnitAgent with both of the aforementioned messages. 32 | swimClient.command(hostUri, fooNodeUri, "addValue", fooVal); 33 | swimClient.command(hostUri, barNodeUri, "addValue", barVal); 34 | 35 | Thread.sleep(5000 * i); 36 | } 37 | System.out.println("Will shut down client"); 38 | swimClient.stop(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /forms/src/main/java/swim/basic/FooType.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.recon.Recon; 4 | import swim.structure.Form; 5 | import swim.structure.Kind; 6 | import swim.structure.Member; 7 | import swim.structure.Tag; 8 | 9 | // Fundamentally a simple Java class, with a small number of fields of any simple type 10 | // with the necessary pieces added to automatically generate a form 11 | 12 | @Tag("fooType") 13 | public class FooType { 14 | 15 | @Member("i") 16 | private int i = 0; 17 | @Member("s") 18 | private String s = ""; 19 | 20 | public FooType() { 21 | } 22 | 23 | public FooType(int i, String s) { 24 | this.i = i; 25 | this.s = s; 26 | } 27 | 28 | public int getNumber() { 29 | return this.i; 30 | } 31 | 32 | public String getString() { 33 | return this.s; 34 | } 35 | 36 | //@Kind-annotated static field and static accessor method 37 | @Kind 38 | private static Form form; 39 | 40 | public static Form form() { 41 | if (form == null) { 42 | form = Form.forClass(FooType.class); 43 | } 44 | return form; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return Recon.toString(form().mold(this)); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /forms/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.ValueLane; 7 | import swim.structure.Value; 8 | 9 | public class UnitAgent extends AbstractAgent { 10 | 11 | public UnitAgent() { 12 | } 13 | 14 | // ValueLane foo, whose didSet(newValue, oldValue) callback prints newValue 15 | @SwimLane("foo") 16 | ValueLane foo = this.valueLane() 17 | .didSet((newValue, oldValue) -> { 18 | System.out.println("foo: " + newValue); 19 | }); 20 | 21 | // CommandLane addFoo, whose onCommand(value) callback invokes foo.set(value) 22 | @SwimLane("addFoo") 23 | CommandLane addFoo = this.commandLane() 24 | .onCommand((FooType value) -> { 25 | this.foo.set(value); 26 | }); 27 | 28 | // ValueLane bar, whose didSet(newValue, oldValue) callback prints newValue 29 | @SwimLane("bar") 30 | ValueLane bar = this.valueLane() 31 | .didSet((newValue, oldValue) -> { 32 | System.out.println("bar: " + newValue); 33 | }); 34 | 35 | // CommandLane addBar, whose onCommand(value) callback invokes bar.set(value) 36 | @SwimLane("addBar") 37 | CommandLane addBar = this.commandLane() 38 | .onCommand((BarType value) -> { 39 | this.bar.set(value); 40 | }); 41 | 42 | // CommandLane addValue, whose onCommand(value) callback prints value 43 | // Just included to exercise that the internal lane type is always fundamentally a Value, so this type is compatible with anything. 44 | @SwimLane("addValue") 45 | CommandLane addValue = this.commandLane() 46 | .onCommand((Value value) -> { 47 | System.out.println("some value: " + value); 48 | }); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /forms/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | swim.version=4.1.0.12 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swimos/cookbook/8a42f9a4164cd2b479826ef5cc2dfe0395767773/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /http_ingestion/README.md: -------------------------------------------------------------------------------- 1 | # HTTP Ingestion 2 | 3 | Code corresponding to the [HTTP Ingestion Guide](https://www.swimos.org/guides/http-ingestion.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.vehicle.Main` class. 10 | 11 | Thus, `./gradlew http-ingestion:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `./gradlew http-ingestion:build`, unpackage either artifact in `http-ingestion/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /http_ingestion/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the HTTP Ingestion guide' 4 | ext.moduleName = 'swim.vehicle' 5 | mainClassName = 'swim.vehicle.Main' 6 | 7 | dependencies { 8 | implementation group: 'org.swimos', name: 'swim-xml', version: version 9 | } 10 | -------------------------------------------------------------------------------- /http_ingestion/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.vehicle { 16 | requires transitive java.net.http; 17 | requires transitive swim.api; 18 | requires swim.server; 19 | requires swim.client; 20 | requires swim.xml; 21 | 22 | exports swim.vehicle; 23 | } 24 | -------------------------------------------------------------------------------- /http_ingestion/src/main/java/swim/vehicle/Assets.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import java.net.http.HttpClient; 4 | 5 | public final class Assets { 6 | 7 | private Assets() { 8 | } 9 | 10 | private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient(); 11 | 12 | public static HttpClient httpClient() { 13 | return Assets.HTTP_CLIENT; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /http_ingestion/src/main/java/swim/vehicle/Main.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.kernel.Kernel; 4 | import swim.server.ServerLoader; 5 | 6 | public final class Main { 7 | 8 | private Main() { 9 | } 10 | 11 | public static void main(String[] args) { 12 | startServer(); 13 | } 14 | 15 | private static void startServer() { 16 | final Kernel kernel = ServerLoader.loadServer(); 17 | kernel.start(); 18 | System.out.println("Running Http-ingesting server..."); 19 | kernel.run(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /http_ingestion/src/main/java/swim/vehicle/NextBusApi.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import java.io.InputStream; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | import java.util.zip.GZIPInputStream; 9 | import swim.codec.Utf8; 10 | import swim.structure.Value; 11 | import swim.xml.Xml; 12 | 13 | public final class NextBusApi { 14 | 15 | private NextBusApi() { 16 | } 17 | 18 | private static final String ENDPOINT_FMT = "https://retro.umoiq.com/service/publicXMLFeed?command=vehicleLocations&a=%s&t=%d"; 19 | 20 | private static String endpointForAgency(String agency, long since) { 21 | return String.format(ENDPOINT_FMT, agency, since); 22 | } 23 | 24 | private static HttpRequest requestForEndpoint(String endpoint) { 25 | return HttpRequest.newBuilder(URI.create(endpoint)) 26 | .GET() 27 | .headers("Accept-Encoding", "gzip") 28 | .build(); 29 | } 30 | 31 | public static Value getVehiclesForAgency(HttpClient executor, String agency, long since) { 32 | final HttpRequest request = requestForEndpoint(endpointForAgency(agency, since)); 33 | try { 34 | final HttpResponse response = executor.send(request, HttpResponse.BodyHandlers.ofInputStream()); 35 | return Utf8.read(new GZIPInputStream(response.body()), Xml.structureParser().documentParser()); 36 | // Alternatively: convert GZIPInputStream to String, then invoke the more familiar Xml.parse() 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | return Value.absent(); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /http_ingestion/src/main/java/swim/vehicle/VehicleAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.MapLane; 7 | import swim.structure.Value; 8 | 9 | public class VehicleAgent extends AbstractAgent { 10 | 11 | @SwimLane("addMessage") 12 | CommandLane addMessage = this.commandLane() 13 | .onCommand(v -> { 14 | this.history.put(v.get("timestamp").longValue(), v); 15 | }); 16 | 17 | @SwimLane("history") 18 | MapLane history = this.mapLane() 19 | .didUpdate((k, n, o) -> { 20 | System.out.println(nodeUri() + ": received " + n); 21 | }); 22 | 23 | public VehicleAgent() { 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /http_ingestion/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | vehicle: @fabric { 2 | @plane(class: "swim.api.plane.AbstractPlane") 3 | @node { 4 | uri: "/agency/portland-sc" 5 | @agent(class: "swim.vehicle.AgencyAgent") 6 | } 7 | @node { 8 | uri: "/agency/reno" 9 | @agent(class: "swim.vehicle.AgencyAgent") 10 | } 11 | @node { 12 | pattern: "/vehicle/:aid/:vid" 13 | @agent(class: "swim.vehicle.VehicleAgent") 14 | } 15 | } 16 | @web(port: 9001) { 17 | space: "vehicle" 18 | @websocket { 19 | serverCompressionLevel: 0 20 | clientCompressionLevel: 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /http_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Http Lanes 2 | 3 | Code corresponding to the [Http Lanes cookbook](https://swimos.org/tutorials/http-lanes/). 4 | 5 | ## Running This Demo 6 | 7 | This demo requires running the `swim.basic.BasicPlane` class followed by the below curl commands. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. 8 | 9 | ### Curl Commands 10 | 11 | Get state from the server: 12 | `curl --location --request GET 'localhost:9001/unit?lane=http'` 13 | 14 | Get state from the server, in Json: 15 | `curl --location --request GET 'localhost:9001/unit?lane=httpJson'` 16 | 17 | Update the state of the server: 18 | `curl --location --request POST 'localhost:9001/unit?lane=http' \ 19 | --header 'Content-Type: application/json' \ 20 | --data-raw '{"foo": 5}'` 21 | -------------------------------------------------------------------------------- /http_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Http Lane cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | -------------------------------------------------------------------------------- /http_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /http_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.plane.AbstractPlane; 4 | import swim.kernel.Kernel; 5 | import swim.server.ServerLoader; 6 | import swim.structure.Value; 7 | 8 | public class BasicPlane extends AbstractPlane { 9 | 10 | public BasicPlane() { 11 | } 12 | 13 | public static void main(String[] args) { 14 | final Kernel kernel = ServerLoader.loadServer(); 15 | 16 | System.out.println("Starting server..."); 17 | kernel.start(); 18 | kernel.run(); 19 | } 20 | 21 | @Override 22 | public void didStart() { 23 | super.didStart(); 24 | //Immediately wake up the Unit Agent on plane load 25 | command("/unit", "wakeup", Value.absent()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /http_lanes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.http.HttpLane; 6 | import swim.api.lane.ValueLane; 7 | import swim.http.HttpMethod; 8 | import swim.http.HttpResponse; 9 | import swim.http.HttpStatus; 10 | import swim.http.MediaType; 11 | import swim.json.Json; 12 | import swim.recon.Recon; 13 | import swim.structure.Record; 14 | import swim.structure.Value; 15 | 16 | public class UnitAgent extends AbstractAgent { 17 | 18 | public UnitAgent() { 19 | } 20 | 21 | @SwimLane("state") 22 | ValueLane state = this.valueLane() 23 | .didSet((newValue, oldValue) -> { 24 | logMessage("State changed from " + Recon.toString(oldValue) + " to " + Recon.toString(newValue)); 25 | }); 26 | 27 | @SwimLane("http") 28 | HttpLane http = this.httpLane() 29 | .doRespond(request -> { 30 | if (HttpMethod.POST.equals(request.method())) { 31 | this.state.set(request.payload().get()); 32 | } 33 | return HttpResponse.create(HttpStatus.OK) 34 | .body(Recon.toString(this.state.get()), MediaType.applicationXRecon()); 35 | }); 36 | 37 | @SwimLane("httpJson") 38 | HttpLane httpJson = this.httpLane() 39 | .doRespond(request -> 40 | HttpResponse.create(HttpStatus.OK) 41 | .body(Json.toString(this.state.get()), MediaType.applicationJson())); 42 | 43 | @Override 44 | public void didStart() { 45 | logMessage("did start"); 46 | //Insert some dummy values into the state of the web agent 47 | this.state.set(Record.create(2) 48 | .slot("foo", 1) 49 | .slot("bar", 2)); 50 | } 51 | 52 | private void logMessage(final String message) { 53 | System.out.println(nodeUri() + ": " + message); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /http_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | @web(port: 9001) { 2 | space: "basic" 3 | documentRoot: "./ui/" 4 | @websocket { 5 | serverCompressionLevel: 0 6 | # -1 = default; 0 = off; 1-9 = deflate level 7 | clientCompressionLevel: 0 8 | # -1 = default; 0 = off; 1-9 = deflate level 9 | } 10 | } 11 | basic: @fabric { 12 | @plane(class: "swim.basic.BasicPlane") 13 | @node { 14 | uri: "/unit" 15 | @agent(class: "swim.basic.UnitAgent") 16 | } 17 | } -------------------------------------------------------------------------------- /ingress_bridges/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Ingress Bridges cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | repositories { 8 | maven { url "https://repo.eclipse.org/content/repositories/paho-snapshots/" } 9 | } 10 | 11 | dependencies { 12 | api group: 'org.eclipse.paho', name: 'org.eclipse.paho.client.mqttv3', version: '1.0.2' 13 | } 14 | 15 | task runMqtt(type: JavaExec) { 16 | group = "application" 17 | classpath sourceSets.main.runtimeClasspath 18 | mainClass = "swim.basic.mqtt.DataSourcePopulator" 19 | } 20 | 21 | task runBridge(type: JavaExec) { 22 | group = "application" 23 | classpath sourceSets.main.runtimeClasspath 24 | mainClass = "swim.basic.mqtt.IngressBridge" 25 | } 26 | 27 | task runWarp(type: JavaExec) { 28 | group = "application" 29 | classpath sourceSets.main.runtimeClasspath 30 | mainClass = "swim.basic.warp.SourcePlane" 31 | } 32 | -------------------------------------------------------------------------------- /ingress_bridges/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | @SuppressWarnings("requires-automatic") 16 | open module swim.basic { 17 | requires transitive swim.api; 18 | requires swim.server; 19 | requires swim.client; 20 | requires org.eclipse.paho.client.mqttv3; 21 | 22 | exports swim.basic; 23 | } 24 | -------------------------------------------------------------------------------- /ingress_bridges/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import java.io.IOException; 18 | import swim.actor.ActorSpace; 19 | import swim.api.plane.AbstractPlane; 20 | import swim.kernel.Kernel; 21 | import swim.server.ServerLoader; 22 | import swim.structure.Value; 23 | 24 | public class BasicPlane extends AbstractPlane { 25 | 26 | public BasicPlane() { 27 | } 28 | 29 | public static void main(String[] args) throws IOException { 30 | final Kernel kernel = ServerLoader.loadServer(); 31 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | // A Web Agent won't run unless its URI is invoked for the first time. 37 | // In the MQTT demo, an external process does this, so the upcoming command 38 | // is unnecessary. In the WARP demo, the data flow from `SourcePlane` to 39 | // `BasicPlane` is strictly pull-based, and it happens in a Web Agent; we 40 | // solve this chicken-and-egg problem by jump-starting one `UnitAgent`. 41 | space.command("/unit/0", "wakeup", Value.absent()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ingress_bridges/src/main/java/swim/basic/warp/SourceAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic.warp; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.ValueLane; 20 | 21 | public class SourceAgent extends AbstractAgent { 22 | 23 | @SwimLane("val") 24 | ValueLane val = this.valueLane(); 25 | 26 | @Override 27 | public void didStart() { 28 | System.out.println(nodeUri() + " didStart region"); 29 | close(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ingress_bridges/src/main/java/swim/basic/warp/SourcePlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic.warp; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Text; 22 | 23 | public class SourcePlane extends AbstractPlane { 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | System.setProperty("swim.config", "source.recon"); 27 | 28 | final Kernel kernel = ServerLoader.loadServer(); 29 | final ActorSpace space = (ActorSpace) kernel.getSpace("source"); 30 | kernel.start(); 31 | System.out.println("Running Source server..."); 32 | kernel.run(); 33 | 34 | int count = 0; 35 | while (true) { 36 | for (int i = 0; i < 10; i++) { 37 | space.command("/source/" + i, "val", 38 | Text.from("FromOtherSwimServer" + (count++))); 39 | Thread.sleep(100); 40 | } 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ingress_bridges/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ingress_bridges/src/main/resources/source.recon: -------------------------------------------------------------------------------- 1 | source: @fabric { 2 | @plane(class: "swim.basic.warp.SourcePlane") 3 | @node { 4 | pattern: "/source/:id" 5 | @agent(class: "swim.basic.warp.SourceAgent") 6 | } 7 | } 8 | @web(port: 9002) { 9 | space: "source" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /introspection/README.md: -------------------------------------------------------------------------------- 1 | # Introspection 2 | 3 | Code corresponding to the [Introspection Guide](https://www.swimos.org/guides/introspection.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.introspection.BasicPlane` class. 10 | 11 | Thus, `gradle introspection:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `gradle introspection:build`, unpackage either artifact in `introspection/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /introspection/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Introspection guide' 4 | ext.moduleName = 'swim.introspection' 5 | mainClassName = 'swim.introspection.BasicPlane' 6 | 7 | dependencies { 8 | api group: 'org.swimos', name: 'swim-meta', version: version 9 | } -------------------------------------------------------------------------------- /introspection/src/main/java/swim/introspection/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.introspection; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | final Kernel kernel = ServerLoader.loadServer(); 27 | final ActorSpace space = (ActorSpace) kernel.getSpace("introspection"); 28 | 29 | kernel.start(); 30 | System.out.println("Running Basic server..."); 31 | kernel.run(); 32 | 33 | // Start 2 buildings with 5 rooms 34 | for (int bi = 1; bi < 3; bi++) { 35 | for (int ri = 1; ri < 6; ri++) { 36 | space.command("/building/" + bi + "/room/" + ri, "start", Value.absent()); 37 | } 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /introspection/src/main/java/swim/introspection/BuildingAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.introspection; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.JoinValueLane; 21 | import swim.structure.Value; 22 | import swim.uri.Uri; 23 | 24 | public class BuildingAgent extends AbstractAgent { 25 | 26 | public BuildingAgent() { 27 | } 28 | 29 | @SwimLane("rooms") 30 | JoinValueLane rooms = this.joinValueLane(); 31 | 32 | @SwimLane("addRoom") 33 | CommandLane addRoom = this.commandLane() 34 | .onCommand(uri -> 35 | this.rooms.downlink(uri.pathName()) 36 | .nodeUri(uri) 37 | .laneUri("info") 38 | .open()); 39 | 40 | @Override 41 | public void didStart() { 42 | info(nodeUri() + " didStart"); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /introspection/src/main/java/swim/introspection/RoomAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.introspection; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.ValueLane; 20 | import swim.structure.Value; 21 | import swim.uri.Uri; 22 | 23 | public class RoomAgent extends AbstractAgent { 24 | 25 | public RoomAgent() { 26 | } 27 | 28 | @SwimLane("info") 29 | ValueLane info = this.valueLane(); 30 | 31 | @SwimLane("lights") 32 | ValueLane lights = this.valueLane(); 33 | 34 | @SwimLane("occupied") 35 | ValueLane occupied = this.valueLane(); 36 | 37 | @SwimLane("temperature") 38 | ValueLane temperature = this.valueLane(); 39 | 40 | private void registerWithBuilding() { 41 | command( 42 | "/building/" + this.getProp("buildingId").intValue(), 43 | "addRoom", 44 | Uri.form().mold(this.nodeUri()).toValue() 45 | ); 46 | } 47 | 48 | @Override 49 | public void didStart() { 50 | info(nodeUri() + " didStart"); 51 | registerWithBuilding(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /introspection/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | @kernel(class: "swim.meta.MetaKernel") 2 | 3 | introspection: @fabric { 4 | @plane(class: "swim.introspection.BasicPlane") 5 | 6 | @node { 7 | pattern: "/building/:buildingId" 8 | @agent(class: "swim.introspection.BuildingAgent") 9 | } 10 | 11 | @node { 12 | pattern: "/building/:buildingId/room/:roomId" 13 | @agent(class: "swim.introspection.RoomAgent") 14 | @agent(class: "swim.introspection.RoomSimulatorAgent") 15 | } 16 | 17 | } 18 | @web(port: 9001) { 19 | space: "introspection" 20 | @websocket { 21 | serverCompressionLevel: 0 22 | # -1 = default; 0 = off; 1-9 = deflate level 23 | clientCompressionLevel: 0 24 | # -1 = default; 0 = off; 1-9 = deflate level 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jms_ingestion/README.md: -------------------------------------------------------------------------------- 1 | # JMS Ingestion 2 | 3 | Code corresponding to the [JMS Ingestion Guide](https://www.swimos.org/guides/jms-ingestion.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.vehicle.Main` class. 10 | 11 | Thus, `./gradlew jms-ingestion:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `./gradlew jms-ingestion:build`, un-package either artifact in `jms-ingestion/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /jms_ingestion/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the JMS Ingestion guide' 4 | ext.moduleName = 'swim.vehicle' 5 | mainClassName = 'swim.vehicle.Main' 6 | 7 | dependencies { 8 | implementation group: 'org.apache.activemq', name: 'activemq-all', version: '5.17.4' 9 | implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.1' 10 | } 11 | -------------------------------------------------------------------------------- /jms_ingestion/src/main/java/swim/vehicle/Assets.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import javax.jms.Connection; 4 | import javax.jms.ConnectionFactory; 5 | import javax.jms.JMSException; 6 | import org.apache.activemq.ActiveMQConnectionFactory; 7 | 8 | public final class Assets { 9 | 10 | private Assets() { 11 | } 12 | 13 | private static ConnectionFactory connectionFactory; 14 | private static Connection connection; 15 | 16 | public static ConnectionFactory connectionFactory() { 17 | return Assets.connectionFactory; 18 | } 19 | 20 | private static ConnectionFactory loadConnectionFactory() { 21 | // Here we can configure the ConnectionFactory with additional settings - perhaps loaded from a properties file 22 | return new ActiveMQConnectionFactory("tcp://activemq:61616"); 23 | } 24 | 25 | public static void init() { 26 | Assets.connectionFactory = loadConnectionFactory(); 27 | } 28 | 29 | public static Connection getOrCreateConnection() throws JMSException { 30 | if (Assets.connection == null) { 31 | Assets.connection = Assets.connectionFactory.createConnection(); 32 | } 33 | return Assets.connection; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /jms_ingestion/src/main/java/swim/vehicle/JmsAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import javax.jms.Connection; 4 | import javax.jms.JMSException; 5 | import javax.jms.Message; 6 | import javax.jms.MessageConsumer; 7 | import javax.jms.Session; 8 | import javax.jms.TextMessage; 9 | import swim.api.agent.AbstractAgent; 10 | import swim.json.Json; 11 | import swim.structure.Value; 12 | 13 | public class JmsAgent extends AbstractAgent { 14 | 15 | private void subscribe() { 16 | try { 17 | // Create a connection 18 | final Connection connection = Assets.getOrCreateConnection(); 19 | connection.start(); 20 | 21 | // Create a session 22 | final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); 23 | 24 | // Create a message consumer and process messages 25 | final MessageConsumer consumer = session.createConsumer(session.createTopic("myTopic")); 26 | consumer.setMessageListener(this::processMessage); 27 | } catch (JMSException jmsException) { 28 | jmsException.printStackTrace(); 29 | } 30 | } 31 | 32 | private void processMessage(final Message message) { 33 | try { 34 | final Value body = Json.parse(((TextMessage) message).getText()); 35 | final String nodeUri = "/vehicle/" + body.get("id").longValue(); 36 | command(nodeUri, "addMessage", body); 37 | } catch (JMSException jmsException) { 38 | jmsException.printStackTrace(); 39 | } 40 | } 41 | 42 | @Override 43 | public void didStart() { 44 | asyncStage().task(this::subscribe).cue(); 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /jms_ingestion/src/main/java/swim/vehicle/Main.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.kernel.Kernel; 4 | import swim.server.ServerLoader; 5 | 6 | public final class Main { 7 | 8 | private Main() { 9 | } 10 | 11 | public static void main(String[] args) { 12 | Assets.init(); 13 | startServer(); 14 | } 15 | 16 | private static void startServer() { 17 | final Kernel kernel = ServerLoader.loadServer(); 18 | kernel.start(); 19 | System.out.println("Running JMS-ingesting server..."); 20 | kernel.run(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /jms_ingestion/src/main/java/swim/vehicle/VehicleAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.MapLane; 7 | import swim.structure.Value; 8 | 9 | public class VehicleAgent extends AbstractAgent { 10 | 11 | @SwimLane("addMessage") 12 | CommandLane addMessage = this.commandLane() 13 | .onCommand(v -> this.history.put(v.get("timestamp").longValue(), v)); 14 | 15 | @SwimLane("history") 16 | MapLane history = this.mapLane() 17 | .didUpdate((k, n, o) -> System.out.println(nodeUri() + ": received " + n)); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /jms_ingestion/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | vehicle: @fabric { 2 | @plane(class: "swim.api.plane.AbstractPlane") 3 | @node { 4 | pattern: "/vehicle/:id" 5 | @agent(class: "swim.vehicle.VehicleAgent") 6 | } 7 | @node { 8 | uri: "/activemq" 9 | @agent(class: "swim.vehicle.JmsAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "vehicle" 14 | @websocket { 15 | serverCompressionLevel: 0 16 | clientCompressionLevel: 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /join_map_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Join Map Lanes 2 | 3 | Code corresponding to the [Join Map Lanes cookbook](https://swimos.org/tutorials/join-map-lanes/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class. 10 | -------------------------------------------------------------------------------- /join_map_lanes/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Join Map Lanes 2 | 3 | 代码可在 [Join Map Lanes cookbook](https://swimos.org/tutorials/join-map-lanes/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Join Map Lanes 展示 8 | 9 | 该展示需要运行 `swim.basic.BasicPlane` 类。 -------------------------------------------------------------------------------- /join_map_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Join Map Lanes cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /join_map_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /join_map_lanes/src/main/java/swim/basic/AggregatedStatisticsAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.JoinMapLane; 6 | 7 | class AggregatedStatisticsAgent extends AbstractAgent { 8 | 9 | AggregatedStatisticsAgent() { 10 | } 11 | 12 | // Aggregated statistics of US states 13 | @SwimLane("join") 14 | JoinMapLane stateStreetStats = this.joinMapLane(); 15 | 16 | @Override 17 | public void didStart() { 18 | this.stateStreetStats.downlink("california") 19 | .hostUri(BasicPlane.HOST_URI) 20 | .nodeUri("/state/california").laneUri("state") 21 | .open(); 22 | this.stateStreetStats.downlink("texas") 23 | .hostUri(BasicPlane.HOST_URI) 24 | .nodeUri("/state/texas").laneUri("state") 25 | .open(); 26 | this.stateStreetStats.downlink("florida") 27 | .hostUri(BasicPlane.HOST_URI) 28 | .nodeUri("/state/florida").laneUri("state") 29 | .open(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /join_map_lanes/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.client.Client; 4 | import swim.api.downlink.MapDownlink; 5 | import swim.client.ClientRuntime; 6 | import swim.structure.Form; 7 | 8 | /** 9 | * The complimentary code as part of the Join Map Lane cookbook. 10 | *

11 | * In this cookbook, three map lanes are created to hold information pertaining to state statistics and is aggregated 12 | * into a Join Map Lane. This join map lane checks entries that are added to see if they exceed a threshold. If it is, 13 | * then the data is logged. 14 | *

15 | * See {@link BasicPlane} 16 | */ 17 | public final class CustomClient { 18 | 19 | private CustomClient() { 20 | } 21 | 22 | private static final int THRESHOLD = 1000; 23 | 24 | public static void main(String[] args) throws InterruptedException { 25 | System.out.println("Starting client..."); 26 | 27 | final Client clientRuntime = new ClientRuntime(); 28 | clientRuntime.start(); 29 | 30 | final MapDownlink mapDownlink = clientRuntime.downlinkMap() 31 | .keyForm(Form.forString()) 32 | .valueForm(Form.forInteger()) 33 | .hostUri(BasicPlane.HOST_URI) 34 | .nodeUri("/join/state/all") 35 | .laneUri("join") 36 | .didUpdate((key, newValue, oldValue) -> { 37 | if (newValue > THRESHOLD) { 38 | logStreet(key, newValue); 39 | } 40 | }) 41 | .open(); 42 | 43 | Thread.sleep(1000); 44 | 45 | System.out.println("Shutting down client..."); 46 | clientRuntime.stop(); 47 | } 48 | 49 | private static void logStreet(String streetName, Integer population) { 50 | System.out.println(streetName + " has " + population + " residents"); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /join_map_lanes/src/main/java/swim/basic/StreetStatisticsAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.MapLane; 6 | 7 | class StreetStatisticsAgent extends AbstractAgent { 8 | 9 | StreetStatisticsAgent() { 10 | } 11 | 12 | /* 13 | - Key: Street name 14 | - Value: Street population 15 | */ 16 | @SwimLane("state") 17 | MapLane streetStatistics = this.mapLane(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /join_map_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | @web(port: 9001) { 2 | space: "basic" 3 | @websocket { 4 | serverCompressionLevel: 0 # -1 = default; 0 = off; 1-9 = deflate level 5 | clientCompressionLevel: 0 # -1 = default; 0 = off; 1-9 = deflate level 6 | } 7 | } 8 | basic: @fabric { 9 | @plane(class: "swim.basic.BasicPlane") 10 | @node { 11 | pattern: "/state/:name" 12 | @agent(class: "swim.basic.StreetStatisticsAgent") 13 | } 14 | @node { 15 | pattern: "/join/state/:name" 16 | @agent(class: "swim.basic.AggregatedStatisticsAgent") 17 | } 18 | } -------------------------------------------------------------------------------- /join_value_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Join Lanes 2 | 3 | Code corresponding to the [Join Value Lanes cookbook](https://swimos.org/tutorials/join-value-lanes/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 10 | -------------------------------------------------------------------------------- /join_value_lanes/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Join Lanes 2 | 3 | 代码可在 [Join Value Lanes cookbook](https://swimos.org/tutorials/join-value-lanes/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Join Lanes 展示 8 | 9 | 该演示需要按照顺序依次运行 `swim.basic.BasicPlane` 类和 `swim.basic.CustomClient` 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.basic.CustomClient` 的名称为 `runClient`。 10 | -------------------------------------------------------------------------------- /join_value_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Join Value Lanes cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /join_value_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /join_value_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | space.command("/building/swim", "wakeup", Value.absent()); 37 | 38 | space.command("/swim/1", "wakeup", Value.absent()); 39 | space.command("/swim/2", "wakeup", Value.absent()); 40 | space.command("/swim/3", "wakeup", Value.absent()); 41 | } 42 | 43 | @Override 44 | public void didStart() { 45 | super.didStart(); 46 | // Immediately wake up BuildingAgent upon plane load 47 | context.command("/building/swim", "wakeup", Value.absent()); 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /join_value_lanes/src/main/java/swim/basic/BuildingAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.JoinValueLane; 21 | import swim.structure.Value; 22 | 23 | public class BuildingAgent extends AbstractAgent { 24 | 25 | public BuildingAgent() { 26 | } 27 | 28 | @SwimLane("lights") 29 | JoinValueLane lights = this.joinValueLane() 30 | .didUpdate((Integer key, Boolean newValue, Boolean oldValue) -> { 31 | System.out.println("The lights in room " + key + " are " + (newValue ? "on." : "off.")); 32 | }); 33 | 34 | @SwimLane("registerRoom") 35 | CommandLane registerRoom = this.commandLane() 36 | .onCommand(room -> { 37 | final String roomUri = "/" + this.getProp("name").stringValue() + "/" + room.stringValue(); 38 | this.lights.downlink(room.intValue()).nodeUri(roomUri).laneUri("lights").open(); 39 | }); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /join_value_lanes/src/main/java/swim/basic/RoomAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | import swim.structure.Value; 22 | 23 | public class RoomAgent extends AbstractAgent { 24 | 25 | public RoomAgent() { 26 | } 27 | 28 | @SwimLane("lights") 29 | ValueLane lights = this.valueLane(); 30 | 31 | @SwimLane("toggleLights") 32 | CommandLane toggleLights = this.commandLane().onCommand(msg -> { 33 | this.lights.set(!this.lights.get()); 34 | }); 35 | 36 | @Override 37 | public void didStart() { 38 | this.lights.set(false); 39 | register(); 40 | } 41 | 42 | private void register() { 43 | final String buildingUri = "/building/" + this.getProp("building").stringValue(); 44 | final Value roomId = getProp("room"); 45 | command(buildingUri, "registerRoom", roomId); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /join_value_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/building/:name" 5 | @agent(class: "swim.basic.BuildingAgent") 6 | } 7 | @node { 8 | pattern: "/:building/:room" 9 | @agent(class: "swim.basic.RoomAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "basic" 14 | documentRoot: "./ui/" 15 | @websocket { 16 | serverCompressionLevel: 0 17 | # -1 = default; 0 = off; 1-9 = deflate level 18 | clientCompressionLevel: 0 19 | # -1 = default; 0 = off; 1-9 = deflate level 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /kafka_ingestion/README.md: -------------------------------------------------------------------------------- 1 | # Kafka Ingestion 2 | 3 | Code corresponding to the [Kafka Ingestion Guide](https://www.swimos.org/guides/kafka-ingestion.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.vehicle.Main` class. 10 | 11 | Thus, `./gradlew kafka-ingestion:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `./gradlew kafka-ingestion:build`, unpackage either artifact in `kafka-ingestion/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /kafka_ingestion/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Kafka Ingestion guide' 4 | ext.moduleName = 'swim.vehicle' 5 | mainClassName = 'swim.vehicle.Main' 6 | 7 | repositories { 8 | maven { url 'https://packages.confluent.io/maven/' } 9 | } 10 | 11 | dependencies { 12 | api group: 'org.apache.kafka', name: 'kafka-clients', version: '3.2.0' 13 | } 14 | -------------------------------------------------------------------------------- /kafka_ingestion/src/main/java/swim/vehicle/Assets.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import java.util.Properties; 4 | import org.apache.kafka.clients.consumer.KafkaConsumer; 5 | 6 | public final class Assets { 7 | 8 | private Assets() { 9 | } 10 | 11 | private static KafkaConsumer kafkaConsumer; 12 | 13 | public static KafkaConsumer kafkaConsumer() { 14 | return Assets.kafkaConsumer; 15 | } 16 | 17 | private static KafkaConsumer loadKafkaConsumer() { 18 | final Properties props = new Properties(); 19 | props.setProperty("bootstrap.servers", "your-bootstrap-host:9092"); 20 | props.setProperty("group.id", "your-group"); 21 | props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); 22 | props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); 23 | // Alternatively, load above from a .properties file 24 | return new KafkaConsumer<>(props); 25 | } 26 | 27 | public static void init() { 28 | Assets.kafkaConsumer = loadKafkaConsumer(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /kafka_ingestion/src/main/java/swim/vehicle/KafkaConsumingAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import java.time.Duration; 4 | import org.apache.kafka.clients.consumer.ConsumerRecord; 5 | import org.apache.kafka.clients.consumer.ConsumerRecords; 6 | import swim.api.agent.AbstractAgent; 7 | import swim.concurrent.AbstractTask; 8 | import swim.concurrent.TaskRef; 9 | import swim.json.Json; 10 | import swim.structure.Value; 11 | 12 | public class KafkaConsumingAgent extends AbstractAgent { 13 | 14 | private final TaskRef endlessConsumingTask = asyncStage().task(new AbstractTask() { 15 | 16 | @Override 17 | public void runTask() { 18 | while (true) { 19 | final ConsumerRecords records = Assets.kafkaConsumer() 20 | .poll(Duration.ofMillis(100)); 21 | for (ConsumerRecord record : records) { 22 | final String nodeUri = "/vehicle/" + record.key(); 23 | final Value payload = Json.parse(record.value()); 24 | command(nodeUri, "addMessage", payload); 25 | } 26 | } 27 | } 28 | 29 | @Override 30 | public boolean taskWillBlock() { 31 | return true; 32 | } 33 | 34 | }); 35 | 36 | public KafkaConsumingAgent() { 37 | } 38 | 39 | @Override 40 | public void didStart() { 41 | this.endlessConsumingTask.cue(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /kafka_ingestion/src/main/java/swim/vehicle/Main.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.kernel.Kernel; 4 | import swim.server.ServerLoader; 5 | 6 | public final class Main { 7 | 8 | private Main() { 9 | } 10 | 11 | public static void main(String[] args) { 12 | Assets.init(); 13 | startServer(); 14 | } 15 | 16 | private static void startServer() { 17 | final Kernel kernel = ServerLoader.loadServer(); 18 | kernel.start(); 19 | System.out.println("Running Kafka-ingesting server..."); 20 | kernel.run(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /kafka_ingestion/src/main/java/swim/vehicle/VehicleAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.MapLane; 7 | import swim.structure.Value; 8 | 9 | public class VehicleAgent extends AbstractAgent { 10 | 11 | @SwimLane("addMessage") 12 | CommandLane addMessage = this.commandLane() 13 | .onCommand(v -> { 14 | this.history.put(v.get("timestamp").longValue(), v); 15 | }); 16 | 17 | @SwimLane("history") 18 | MapLane history = this.mapLane() 19 | .didUpdate((k, n, o) -> { 20 | System.out.println(nodeUri() + ": received " + n); 21 | }); 22 | 23 | public VehicleAgent() { 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /kafka_ingestion/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | vehicle: @fabric { 2 | @plane(class: "swim.api.plane.AbstractPlane") 3 | @node { 4 | pattern: "/vehicle/:id" 5 | @agent(class: "swim.vehicle.VehicleAgent") 6 | } 7 | @node { 8 | uri: "/kafka" 9 | @agent(class: "swim.vehicle.KafkaConsumingAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "vehicle" 14 | @websocket { 15 | serverCompressionLevel: 0 16 | clientCompressionLevel: 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /map_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Map Lanes 2 | 3 | Code corresponding to the [Map Lanes cookbook](https://swimos.org/tutorials/map-lanes/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 10 | -------------------------------------------------------------------------------- /map_lanes/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Map Lanes 2 | 3 | 代码可在 [Map Lanes cookbook](https://swimos.org/tutorials/map-lanes/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Map Lanes 展示 8 | 9 | 该演示需要按照顺序依次运行 `swim.basic.BasicPlane` 类和 `swim.basic.CustomClient` 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.basic.CustomClient` 的名称为 `runClient`。 10 | -------------------------------------------------------------------------------- /map_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Map Lanes cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /map_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /map_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | space.command("/unit/foo", "wakeup", Value.absent()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /map_lanes/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.downlink.MapDownlink; 18 | import swim.client.ClientRuntime; 19 | import swim.structure.Form; 20 | import swim.structure.Text; 21 | 22 | final class CustomClient { 23 | 24 | private CustomClient() { 25 | } 26 | 27 | public static void main(String[] args) throws InterruptedException { 28 | final ClientRuntime swimClient = new ClientRuntime(); 29 | swimClient.start(); 30 | final String hostUri = "warp://localhost:9001"; 31 | final String nodeUri = "/unit/foo"; 32 | // Reduce probability of startup race; no need to conflate this example 33 | // with proper synchronization just yet 34 | final MapDownlink link = swimClient.downlinkMap() 35 | .keyForm(Form.forString()).valueForm(Form.forInteger()) 36 | .hostUri(hostUri).nodeUri(nodeUri).laneUri("shoppingCart") 37 | .didUpdate((key, newValue, oldValue) -> { 38 | System.out.println("link watched " + key + " change to " + newValue + " from " + oldValue); 39 | }) 40 | .open(); 41 | 42 | // Send using either the proxy command lane... 43 | swimClient.command(hostUri, nodeUri, "addItem", Text.from("FromClientCommand")); 44 | // ...or a downlink put() 45 | link.put("FromClientLink", 25); 46 | Thread.sleep(2000); 47 | link.remove("FromClientLink"); 48 | 49 | System.out.println("Will shut down client in 2 seconds"); 50 | Thread.sleep(2000); 51 | swimClient.stop(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /map_lanes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.MapLane; 21 | 22 | public class UnitAgent extends AbstractAgent { 23 | 24 | public UnitAgent() { 25 | } 26 | 27 | @SwimLane("shoppingCart") 28 | MapLane shoppingCart = this.mapLane() 29 | .didUpdate((key, newValue, oldValue) -> { 30 | logMessage(key + " count changed to " + newValue + " from " + oldValue); 31 | }) 32 | .didRemove((key, oldValue) -> { 33 | logMessage("removed <" + key + "," + oldValue + ">"); 34 | }); 35 | 36 | @SwimLane("addItem") 37 | CommandLane publish = this.commandLane() 38 | .onCommand(msg -> { 39 | final int n = this.shoppingCart.getOrDefault(msg, 0) + 1; 40 | this.shoppingCart.put(msg, n); 41 | }); 42 | 43 | @Override 44 | public void didStart() { 45 | System.out.println(nodeUri() + " didStart region"); 46 | close(); 47 | } 48 | 49 | private void logMessage(Object msg) { 50 | System.out.println(nodeUri() + ": " + msg); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /map_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mongodb_ingestion/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Ingestion 2 | 3 | Code corresponding to the [MongoDB Ingestion Guide](https://www.swimos.org/guides/mongodb-ingestion.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.vehicle.Main` class. 10 | 11 | Thus, `./gradlew mongodb-ingestion:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `./gradlew mongodb-ingestion:build`, un-package either artifact in `mongodb-ingestion/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /mongodb_ingestion/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the MongoDB Ingestion guide' 4 | ext.moduleName = 'swim.vehicle' 5 | mainClassName = 'swim.vehicle.Main' 6 | 7 | dependencies { 8 | api group: 'org.mongodb', name: 'mongodb-driver-sync', version: '4.11.0' 9 | } 10 | -------------------------------------------------------------------------------- /mongodb_ingestion/src/main/java/swim/vehicle/Assets.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import com.mongodb.client.MongoClient; 4 | import com.mongodb.client.MongoClients; 5 | 6 | public final class Assets { 7 | 8 | private Assets() { 9 | } 10 | 11 | private static MongoClient mongoClient; 12 | 13 | public static MongoClient mongoClient() { 14 | return Assets.mongoClient; 15 | } 16 | 17 | private static MongoClient loadMongoClient() { 18 | // Here we can configure the MongoClient with additional settings - perhaps loaded from a properties file 19 | return MongoClients.create("mongodb://myConnectionString"); 20 | } 21 | 22 | public static void init() { 23 | Assets.mongoClient = loadMongoClient(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /mongodb_ingestion/src/main/java/swim/vehicle/Main.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.kernel.Kernel; 4 | import swim.server.ServerLoader; 5 | 6 | public final class Main { 7 | 8 | private Main() { 9 | } 10 | 11 | public static void main(String[] args) { 12 | Assets.init(); 13 | startServer(); 14 | } 15 | 16 | private static void startServer() { 17 | final Kernel kernel = ServerLoader.loadServer(); 18 | kernel.start(); 19 | System.out.println("Running MongoDB-ingesting server..."); 20 | kernel.run(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /mongodb_ingestion/src/main/java/swim/vehicle/MongoDbPollingAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import com.mongodb.client.FindIterable; 4 | import com.mongodb.client.MongoCursor; 5 | import org.bson.Document; 6 | import swim.api.agent.AbstractAgent; 7 | import swim.json.Json; 8 | import swim.structure.Value; 9 | 10 | public class MongoDbPollingAgent extends AbstractAgent { 11 | 12 | private FindIterable find() { 13 | return Assets.mongoClient().getDatabase("myDatabase") 14 | .getCollection("myCollection") 15 | .find(); 16 | } 17 | 18 | private void poll() { 19 | try (MongoCursor cursor = find().cursor()) { 20 | while (cursor.hasNext()) { 21 | processDocument(cursor.next()); 22 | } 23 | } 24 | } 25 | 26 | private void processDocument(final Document document) { 27 | final Value body = Json.parse(document.toJson()); 28 | final String nodeUri = "/vehicle/" + body.get("id").longValue(); 29 | command(nodeUri, "addMessage", body); 30 | } 31 | 32 | @Override 33 | public void didStart() { 34 | asyncStage().task(this::poll).cue(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /mongodb_ingestion/src/main/java/swim/vehicle/VehicleAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.ValueLane; 7 | import swim.structure.Value; 8 | 9 | public class VehicleAgent extends AbstractAgent { 10 | 11 | @SwimLane("addMessage") 12 | CommandLane addMessage = this.commandLane() 13 | .onCommand(v -> this.metadata.set(v)); 14 | 15 | @SwimLane("metadata") 16 | ValueLane metadata = this.valueLane() 17 | .didSet((n, o) -> System.out.println(nodeUri() + ": received " + n)); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mongodb_ingestion/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | vehicle: @fabric { 2 | @plane(class: "swim.api.plane.AbstractPlane") 3 | @node { 4 | pattern: "/vehicle/:id" 5 | @agent(class: "swim.vehicle.VehicleAgent") 6 | } 7 | @node { 8 | uri: "/mongo" 9 | @agent(class: "swim.vehicle.MongoDbPollingAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "vehicle" 14 | @websocket { 15 | serverCompressionLevel: 0 16 | clientCompressionLevel: 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /planes/README.md: -------------------------------------------------------------------------------- 1 | # Planes 2 | 3 | Code corresponding to the [Planes cookbook](https://swimos.org/tutorials/planes/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo only requires running the `swim.basic.BasicPlane` class. 10 | 11 | Thus, `gradle planes:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `gradle planes:build`, unpackage either artifact in `planes/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /planes/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Planes 2 | 3 | 代码可在 [Planes cookbook](https://swimos.org/tutorials/planes/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Planes 展示 8 | 9 | 该展示只需要运行 `swim.basic.BasicPlane` 类。 10 | 11 | 因此,对于在[`父目录中的README.zh-cn.md`](../README.zh-cn.md)中选了选项2的用户来说,命令行 `gradle planes:run` 就足够了。 12 | 13 | 对于选择了选项3的用户,只需简单地执行 `gradle planes:build`,解压任意在目录 `planes/build/distributions` 下生成的文件,然后运行在 `bin/` 之下的与环境相匹配的脚本文件;并不需要复制或者改动文件。 14 | -------------------------------------------------------------------------------- /planes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Planes cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | -------------------------------------------------------------------------------- /planes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | 19 | exports swim.basic; 20 | } 21 | -------------------------------------------------------------------------------- /planes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | 22 | public class UnitAgent extends AbstractAgent { 23 | 24 | public UnitAgent() { 25 | } 26 | 27 | @SwimLane("info") 28 | ValueLane info = this.valueLane(); 29 | 30 | @SwimLane("publishInfo") 31 | CommandLane publishInfo = this.commandLane() 32 | .onCommand(msg -> { 33 | this.info.set("from publishInfo: " + msg); 34 | }); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /planes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /project.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'application' 3 | apply plugin: 'checkstyle' 4 | 5 | group = 'org.swimos' 6 | version = project.property('swim.version') 7 | 8 | repositories { 9 | mavenCentral() 10 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } 11 | } 12 | 13 | dependencies { 14 | api group: 'org.swimos', name: 'swim-api', version: version 15 | api group: 'org.swimos', name: 'swim-server', version: version 16 | api group: 'org.swimos', name: 'swim-client', version: version 17 | } 18 | 19 | checkstyle { 20 | // Enforce checkstyle and fail the build if there are any violations 21 | if (project.hasProperty('enforceCheckstyle')) { 22 | ignoreFailures = false 23 | maxWarnings = 0 24 | } 25 | 26 | // configFile = new File(gradle.swimRuntimeDir, 'checkstyle.xml') 27 | toolVersion "8.41.1" 28 | 29 | checkstyleMain { 30 | source = sourceSets.main.allJava 31 | exclude '*module-info*' 32 | } 33 | 34 | checkstyleTest { 35 | source = sourceSets.test.allJava 36 | exclude '*module-info*' 37 | } 38 | } 39 | 40 | afterEvaluate { 41 | sourceSets { 42 | main.output.resourcesDir = main.java.classesDirectory 43 | } 44 | 45 | compileJava { 46 | options.compilerArgs = ['--module-path', classpath.asPath, 47 | '-Xlint:all', 48 | '--patch-module', "$moduleName=" + files(sourceSets.main.resources.srcDirs).asPath] 49 | options.encoding = 'UTF-8' 50 | source = sourceSets.main.allJava 51 | sourceCompatibility = JavaVersion.VERSION_11 52 | targetCompatibility = JavaVersion.VERSION_11 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pulsar_ingestion/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Pulsar Ingestion guide' 4 | ext.moduleName = 'swim.vehicle' 5 | mainClassName = 'swim.vehicle.Main' 6 | 7 | repositories { 8 | maven { url 'https://packages.confluent.io/maven/' } 9 | } 10 | 11 | dependencies { 12 | api group: 'org.apache.pulsar', name: 'pulsar-client', version: '3.1.1' 13 | } 14 | -------------------------------------------------------------------------------- /pulsar_ingestion/src/main/java/swim/vehicle/Assets.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import java.util.Map; 4 | import org.apache.pulsar.client.api.PulsarClient; 5 | 6 | public final class Assets { 7 | 8 | private Assets() { 9 | } 10 | 11 | private static PulsarClient client; 12 | 13 | public static PulsarClient pulsarClient() { 14 | return Assets.client; 15 | } 16 | 17 | private static PulsarClient loadPulsarClient() { 18 | final Map config = Map.ofEntries( 19 | Map.entry("serviceUrl", "pulsar://localhost:6650"), 20 | Map.entry("numListenerThreads", 1) 21 | ); 22 | try { 23 | return PulsarClient.builder() 24 | .loadConf(config) 25 | .build(); 26 | } catch (Exception e) { 27 | throw new RuntimeException("Failed to load Pulsar client", e); 28 | } 29 | } 30 | 31 | public static void init() { 32 | Assets.client = loadPulsarClient(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /pulsar_ingestion/src/main/java/swim/vehicle/Main.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.kernel.Kernel; 4 | import swim.server.ServerLoader; 5 | 6 | public final class Main { 7 | 8 | private Main() { 9 | } 10 | 11 | public static void main(String[] args) { 12 | Assets.init(); 13 | startServer(); 14 | } 15 | 16 | private static void startServer() { 17 | final Kernel kernel = ServerLoader.loadServer(); 18 | kernel.start(); 19 | System.out.println("Running Kafka-ingesting server..."); 20 | kernel.run(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /pulsar_ingestion/src/main/java/swim/vehicle/PulsarConsumingAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import java.util.Map; 4 | import org.apache.pulsar.client.api.Consumer; 5 | import org.apache.pulsar.client.api.PulsarClient; 6 | import org.apache.pulsar.client.api.Schema; 7 | import swim.api.agent.AbstractAgent; 8 | 9 | public class PulsarConsumingAgent extends AbstractAgent { 10 | 11 | private Consumer pulsarConsumer; // or other type parameter 12 | 13 | private Consumer loadPulsarConsumer(PulsarClient client) { 14 | final Map config = Map.ofEntries( 15 | Map.entry("topicNames", "myTopic"), 16 | Map.entry("subscriptionName", "mySubscription") 17 | ); 18 | try { 19 | return client.newConsumer(Schema.STRING) 20 | .loadConf(config) 21 | .messageListener((c, m) -> { 22 | asyncStage().execute(() -> { 23 | // TODO: take an action on m 24 | }); 25 | }) 26 | .subscribe(); 27 | } catch (Exception e) { 28 | throw new RuntimeException("Failed to load Pulsar consumer", e); 29 | } 30 | } 31 | 32 | @Override 33 | public void didStart() { 34 | this.pulsarConsumer = loadPulsarConsumer(Assets.pulsarClient()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /pulsar_ingestion/src/main/java/swim/vehicle/VehicleAgent.java: -------------------------------------------------------------------------------- 1 | package swim.vehicle; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.MapLane; 7 | import swim.structure.Value; 8 | 9 | public class VehicleAgent extends AbstractAgent { 10 | 11 | @SwimLane("addMessage") 12 | CommandLane addMessage = this.commandLane() 13 | .onCommand(v -> { 14 | this.history.put(v.get("timestamp").longValue(), v); 15 | }); 16 | 17 | @SwimLane("history") 18 | MapLane history = this.mapLane() 19 | .didUpdate((k, n, o) -> { 20 | System.out.println(nodeUri() + ": received " + n); 21 | }); 22 | 23 | public VehicleAgent() { 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /pulsar_ingestion/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | vehicle: @fabric { 2 | @plane(class: "swim.api.plane.AbstractPlane") 3 | @node { 4 | pattern: "/vehicle/:id" 5 | @agent(class: "swim.vehicle.VehicleAgent") 6 | } 7 | @node { 8 | uri: "/pulsar" 9 | @agent(class: "swim.vehicle.PulsarConsumingAgent") 10 | } 11 | } 12 | @web(port: 9001) { 13 | space: "vehicle" 14 | @websocket { 15 | serverCompressionLevel: 0 16 | clientCompressionLevel: 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server_downlinks/README.md: -------------------------------------------------------------------------------- 1 | # Server Downlinks 2 | 3 | Code corresponding to the [Server Downlinks cookbook](https://swimos.org/tutorials/server-downlinks/). 4 | 5 | ## Running This Demo 6 | 7 | This demo requires running the `swim.basic.WarehousePlane` class and the `swim.basic.SupplierPlane` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.SupplierPlane` is called `runServer`. 8 | -------------------------------------------------------------------------------- /server_downlinks/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Server Downlinks cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.WarehousePlane' 6 | 7 | task runServer(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.SupplierPlane" 11 | } 12 | -------------------------------------------------------------------------------- /server_downlinks/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | 19 | exports swim.basic; 20 | } 21 | -------------------------------------------------------------------------------- /server_downlinks/src/main/java/swim/basic/CustomerAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import java.util.Objects; 4 | import swim.api.SwimLane; 5 | import swim.api.agent.AbstractAgent; 6 | import swim.api.lane.CommandLane; 7 | import swim.concurrent.TimerRef; 8 | import swim.structure.Form; 9 | import swim.structure.Text; 10 | 11 | public class CustomerAgent extends AbstractAgent { 12 | 13 | public CustomerAgent() { 14 | } 15 | 16 | private TimerRef timer; 17 | 18 | @SwimLane("register") 19 | CommandLane register = this.commandLane() 20 | .onCommand(location -> { 21 | addStockNotificationDownlink(location); 22 | createTakeItemTimer(location); 23 | }); 24 | 25 | private void addStockNotificationDownlink(final String location) { 26 | final String warehouseNodeUri = "/warehouse/" + location; 27 | 28 | //Create a value downlink to a different Swim server from an agent 29 | this.downlinkValue().valueForm(Form.forInteger()) 30 | .hostUri(SupplierPlane.WAREHOUSE_HOST_URI) 31 | .nodeUri(warehouseNodeUri).laneUri("lastResupplyId") 32 | .didSet((newValue, oldValue) -> { 33 | if (!Objects.equals(newValue, oldValue)) { 34 | logMessage("customer received new stock notification, resupply: " + newValue); 35 | } 36 | }) 37 | .open(); 38 | } 39 | 40 | private void createTakeItemTimer(final String location) { 41 | cancelTimer(); 42 | this.timer = setTimer(2000, () -> { 43 | command(SupplierPlane.WAREHOUSE_HOST_URI, "/warehouse/" + location, "takeItem", Text.from("foo")); 44 | this.timer.reschedule(2000); 45 | }); 46 | } 47 | 48 | public void cancelTimer() { 49 | if (this.timer != null) { 50 | this.timer.cancel(); 51 | } 52 | this.timer = null; 53 | } 54 | 55 | @Override 56 | public void willStop() { 57 | cancelTimer(); 58 | } 59 | 60 | private void logMessage(final Object msg) { 61 | System.out.println(nodeUri() + ": " + msg); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /server_downlinks/src/main/java/swim/basic/SupplierAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.structure.Form; 7 | import swim.structure.Text; 8 | 9 | public class SupplierAgent extends AbstractAgent { 10 | 11 | public SupplierAgent() { 12 | } 13 | 14 | private static final int RESUPPLY_THRESHOLD = 3; 15 | 16 | @SwimLane("register") 17 | CommandLane register = this.commandLane() 18 | .onCommand(this::addResupplyDownlink); 19 | 20 | /** 21 | * Creates a downlink to a given location that will resupply stock when it falls below the threshold 22 | */ 23 | private void addResupplyDownlink(final String location) { 24 | final String warehouseNodeUri = "/warehouse/" + location; 25 | 26 | //Create a map downlink to a different Swim server from an agent 27 | this.downlinkMap() 28 | .keyForm(Form.forString()).valueForm(Form.forInteger()) 29 | .hostUri(SupplierPlane.WAREHOUSE_HOST_URI) 30 | .nodeUri(warehouseNodeUri).laneUri("stock") 31 | .didUpdate((item, newValue, oldValue) -> { 32 | if (newValue < RESUPPLY_THRESHOLD) { //If the stock is too low then resupply it 33 | logResupply(item, newValue, location); 34 | this.command(SupplierPlane.WAREHOUSE_HOST_URI, warehouseNodeUri, "resupply", Text.from(item)); 35 | } 36 | }) 37 | .open(); 38 | } 39 | 40 | @Override 41 | public void didStart() { 42 | logEvent("opened"); 43 | } 44 | 45 | @Override 46 | public void willStop() { 47 | logEvent("stopped"); 48 | } 49 | 50 | private void logResupply(final String item, final Integer stock, final String location) { 51 | logMessage("current stock of (" + item + ":" + stock + ") too low at " + location + " warehouse, resupplying"); 52 | } 53 | 54 | private void logEvent(Object msg) { 55 | logMessage("supplier " + msg); 56 | } 57 | 58 | private void logMessage(final Object msg) { 59 | System.out.println(nodeUri() + ": " + msg); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /server_downlinks/src/main/java/swim/basic/WarehouseAgent.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.MapLane; 7 | import swim.api.lane.ValueLane; 8 | 9 | public class WarehouseAgent extends AbstractAgent { 10 | 11 | public WarehouseAgent() { 12 | } 13 | 14 | @SwimLane("stock") 15 | MapLane stock = this.mapLane(); 16 | 17 | @SwimLane("takeItem") 18 | CommandLane takeItem = this.commandLane() 19 | .onCommand(item -> { 20 | int newValue = this.stock.getOrDefault(item, 1) - 1; 21 | if (newValue < 0) { 22 | newValue = 0; 23 | } 24 | this.stock.put(item, newValue); 25 | }); 26 | 27 | @SwimLane("resupply") 28 | CommandLane resupply = this.commandLane() 29 | .onCommand(item -> { 30 | final int newValue = this.stock.getOrDefault(item, 0) + 5; 31 | this.stock.put(item, newValue); 32 | this.lastResupplyId.set(this.lastResupplyId.get() + 1); 33 | logResupply(item, newValue); 34 | }); 35 | 36 | @SwimLane("lastResupplyId") 37 | ValueLane lastResupplyId = this.valueLane(); 38 | 39 | @Override 40 | public void didStart() { 41 | this.lastResupplyId.set(0); 42 | logEvent("opened"); 43 | } 44 | 45 | @Override 46 | public void willStop() { 47 | logEvent("stopped"); 48 | } 49 | 50 | private void logResupply(final String item, final Integer stock) { 51 | logMessage("resupplied " + item + " in " + getProp("location").stringValue() + " warehouse to " + stock + " units"); 52 | } 53 | 54 | private void logEvent(final Object msg) { 55 | logMessage(getProp("location").stringValue() + " warehouse " + msg); 56 | } 57 | 58 | private void logMessage(final Object msg) { 59 | System.out.println(nodeUri() + ": " + msg); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /server_downlinks/src/main/java/swim/basic/WarehousePlane.java: -------------------------------------------------------------------------------- 1 | package swim.basic; 2 | 3 | import swim.api.plane.AbstractPlane; 4 | import swim.kernel.Kernel; 5 | import swim.server.ServerLoader; 6 | import swim.structure.Value; 7 | 8 | /** 9 | * The complimentary code as part of the Server Downlinks cookbook. 10 | *

11 | * In this cookbook, two Swim servers create two different agents simulating a warehouse and a supplier. A downlink is created 12 | * between them so that the supplier can resupply any stock that falls below a given threshold. 13 | *

14 | * See {@link SupplierPlane} 15 | */ 16 | public class WarehousePlane extends AbstractPlane { 17 | 18 | public WarehousePlane() { 19 | } 20 | 21 | public static void main(String[] args) { 22 | System.setProperty("swim.config", "warehouse.recon"); 23 | 24 | final Kernel kernel = ServerLoader.loadServer(); 25 | System.out.println("Starting Warehouse server..."); 26 | kernel.start(); 27 | kernel.run(); 28 | } 29 | 30 | @Override 31 | public void didStart() { 32 | super.didStart(); 33 | // Immediately wake up Warehouse Agent upon plane load 34 | context.command("/warehouse/cambridge", "wakeup", Value.absent()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server_downlinks/src/main/resources/supplier.recon: -------------------------------------------------------------------------------- 1 | supplier: @fabric { 2 | @plane(class: "swim.basic.SupplierPlane") 3 | @node { 4 | uri: "/supplier" 5 | @agent(class: "swim.basic.SupplierAgent") 6 | } 7 | @node { 8 | pattern: "/customer/:id" 9 | @agent(class: "swim.basic.CustomerAgent") 10 | } 11 | } 12 | @web(port: 9002) { 13 | space: "supplier" 14 | documentRoot: "./ui/" 15 | @websocket { 16 | serverCompressionLevel: 0 17 | # -1 = default; 0 = off; 1-9 = deflate level 18 | clientCompressionLevel: 0 19 | # -1 = default; 0 = off; 1-9 = deflate level 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server_downlinks/src/main/resources/warehouse.recon: -------------------------------------------------------------------------------- 1 | warehouse: @fabric { 2 | @plane(class: "swim.basic.WarehousePlane") 3 | @node { 4 | pattern: "/warehouse/:location" 5 | @agent(class: "swim.basic.WarehouseAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "warehouse" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /summary_statistics/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Summary Statistics guide' 4 | ext.moduleName = 'swim.tower' 5 | mainClassName = 'swim.tower.Main' 6 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/AbstractTowerAgent.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.structure.Value; 7 | 8 | public abstract class AbstractTowerAgent extends AbstractAgent { 9 | 10 | @SwimLane("addMessage") 11 | CommandLane addMessage = this.commandLane() 12 | .onCommand(v -> updateSummary(messageTimestamp(v), v)); 13 | 14 | protected long messageTimestamp(Value v) { 15 | return v.get("timestamp").longValue(); 16 | } 17 | 18 | protected abstract void updateSummary(long timestamp, Value newValue); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/BucketedTowerAgent.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import swim.api.SwimLane; 6 | import swim.api.lane.MapLane; 7 | import swim.recon.Recon; 8 | import swim.structure.Value; 9 | 10 | public class BucketedTowerAgent extends AbstractTowerAgent { 11 | 12 | private static final long SAMPLE_PERIOD_MS = 60000L; 13 | 14 | private Map summaryStates; 15 | 16 | @SwimLane("summaries") 17 | MapLane summaries = this.mapLane() 18 | .didUpdate((k, n, o) -> 19 | System.out.println(nodeUri() + ": updated summary under " + k + " to " + Recon.toString(n))); 20 | 21 | @Override 22 | protected void updateSummary(long timestamp, Value v) { 23 | final long key = bucket(timestamp); 24 | final TowerSummaryState state = this.summaryStates.getOrDefault(key, new TowerSummaryState()); 25 | state.addValue(v.get("s_n_ratio").doubleValue(), 26 | v.get("disconnects").intValue()); 27 | this.summaries.put(key, state.getSummary()); 28 | this.summaryStates.put(key, state); 29 | } 30 | 31 | private static long bucket(long timestamp) { 32 | return timestamp / SAMPLE_PERIOD_MS * SAMPLE_PERIOD_MS; 33 | } 34 | 35 | @Override 36 | public void didStart() { 37 | if (this.summaryStates != null) { 38 | this.summaryStates.clear(); 39 | } 40 | this.summaryStates = new HashMap<>(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/Main.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import swim.api.space.Space; 4 | import swim.kernel.Kernel; 5 | import swim.server.ServerLoader; 6 | 7 | public final class Main { 8 | 9 | private Main() { 10 | } 11 | 12 | public static void main(String[] args) { 13 | final Space space = startServer(); 14 | // Occupies main thread until process termination 15 | Simulation.run(space); 16 | } 17 | 18 | private static Space startServer() { 19 | final Kernel kernel = ServerLoader.loadServer(); 20 | final Space space = kernel.getSpace("tower"); 21 | kernel.start(); 22 | System.out.println("Running summary statistics guide..."); 23 | kernel.run(); 24 | return space; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/Simulation.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import swim.api.space.Space; 4 | import swim.structure.Record; 5 | import swim.structure.Value; 6 | 7 | public final class Simulation { 8 | 9 | private Simulation() { 10 | } 11 | 12 | private static void simulateOnce(Space space, String id, double center, int spread, 13 | long now) { 14 | final double val = center + (Math.random() * spread) - (spread / 2.0); 15 | // 9% chance for 2, 21% for 1, 70% for 0 16 | final int failures = Math.random() < 0.3 ? Math.random() < 0.3 ? 2 : 1 : 0; 17 | final String[] nodeUris = {"/tower/" + id, "/towerB/" + id, "/towerW/" + id}; 18 | final Value payload = Record.create(3).slot("s_n_ratio", val) 19 | .slot("disconnects", failures) 20 | .slot("timestamp", now); 21 | for (String nodeUri : nodeUris) { 22 | space.command(nodeUri, "addMessage", payload); 23 | } 24 | } 25 | 26 | public static void run(Space space) { 27 | // infinite loop 28 | while (true) { 29 | final long now = System.currentTimeMillis(); 30 | simulateOnce(space, "2350", 18.0, 15, now); 31 | simulateOnce(space, "2171", 27, 6, now); 32 | try { 33 | Thread.sleep(2000); 34 | } catch (InterruptedException e) { 35 | // impossible 36 | } 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/TowerAgent.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.lane.ValueLane; 5 | import swim.recon.Recon; 6 | import swim.structure.Value; 7 | 8 | public class TowerAgent extends AbstractTowerAgent { 9 | 10 | private TowerSummaryState state; 11 | 12 | @SwimLane("summary") 13 | ValueLane summary = this.valueLane() 14 | .didSet((n, o) -> 15 | System.out.println(nodeUri() + ": updated summary to " + Recon.toString(n))); 16 | 17 | @Override 18 | protected void updateSummary(long timestamp, Value v) { 19 | this.state.addValue(v.get("s_n_ratio").doubleValue(), 20 | v.get("disconnects").intValue()); 21 | this.summary.set(this.state.getSummary()); 22 | } 23 | 24 | @Override 25 | public void didStart() { 26 | this.state = new TowerSummaryState(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/TowerSummaryState.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import swim.structure.Record; 4 | import swim.structure.Value; 5 | 6 | class TowerSummaryState { 7 | 8 | private double min = Double.MAX_VALUE; 9 | private double max = -Double.MIN_VALUE; 10 | private int count = 0; 11 | private double mean = 0.0; 12 | private double agg = 0.0; 13 | private int failures = 0; 14 | 15 | public void addValue(double d, int f) { 16 | this.min = Math.min(this.min, d); 17 | this.max = Math.max(this.max, d); 18 | this.count += 1; 19 | final double delta = d - this.mean; 20 | this.mean += delta / this.count; 21 | this.agg += delta * (d - this.mean); 22 | this.failures += f; 23 | } 24 | 25 | public Value getSummary() { 26 | if (this.count == 0) { 27 | return Value.extant(); 28 | } 29 | return Record.create(6) 30 | .slot("count", this.count) 31 | .slot("min", this.min) 32 | .slot("max", this.max) 33 | .slot("avg", this.mean) 34 | .slot("variance", this.agg / this.count) 35 | .slot("failures", this.failures); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /summary_statistics/src/main/java/swim/tower/WindowedTowerAgent.java: -------------------------------------------------------------------------------- 1 | package swim.tower; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.lane.MapLane; 5 | import swim.recon.Recon; 6 | import swim.structure.Value; 7 | 8 | public class WindowedTowerAgent extends AbstractTowerAgent { 9 | 10 | private static final long SAMPLE_PERIOD_MS = 60000L; 11 | 12 | private TowerSummaryState currentState; 13 | private long currentBucket; 14 | 15 | @SwimLane("summaries") 16 | MapLane summaries = this.mapLane() 17 | .didUpdate((k, n, o) -> 18 | System.out.println(nodeUri() + ": updated summary under " + k + " to " + Recon.toString(n))); 19 | 20 | @Override 21 | protected long messageTimestamp(Value v) { 22 | return System.currentTimeMillis(); 23 | } 24 | 25 | @Override 26 | protected void updateSummary(long timestamp, Value v) { 27 | final long key = bucket(timestamp); 28 | if (key != this.currentBucket) { 29 | resetState(timestamp); 30 | } 31 | this.currentState.addValue(v.get("s_n_ratio").doubleValue(), 32 | v.get("disconnects").intValue()); 33 | this.summaries.put(key, this.currentState.getSummary()); 34 | } 35 | 36 | private void resetState(long now) { 37 | this.currentState = new TowerSummaryState(); 38 | this.currentBucket = bucket(now); 39 | } 40 | 41 | private static long bucket(long timestamp) { 42 | return timestamp / SAMPLE_PERIOD_MS * SAMPLE_PERIOD_MS; 43 | } 44 | 45 | @Override 46 | public void didStart() { 47 | resetState(System.currentTimeMillis()); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /summary_statistics/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | tower: @fabric { 2 | @plane(class: "swim.api.plane.AbstractPlane") 3 | @node { 4 | uri: "/tower/:id" 5 | @agent(class: "swim.tower.TowerAgent") 6 | } 7 | @node { 8 | pattern: "/towerB/:id" 9 | @agent(class: "swim.tower.BucketedTowerAgent") 10 | } 11 | @node { 12 | pattern: "/towerW/:id" 13 | @agent(class: "swim.tower.WindowedTowerAgent") 14 | } 15 | } 16 | @web(port: 9001) { 17 | space: "tower" 18 | @websocket { 19 | serverCompressionLevel: 0 20 | clientCompressionLevel: 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /time_series/README.md: -------------------------------------------------------------------------------- 1 | # Time Series 2 | 3 | Code corresponding to the [Time Series Guide](https://www.swimos.org/guides/time-series.html). 4 | 5 | ## Running This Demo 6 | 7 | _All commands should be executed from the parent directory._ 8 | 9 | This demo only requires running the `swim.timeseries.BasicPlane` class. 10 | 11 | Thus, `gradle time-series:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `gradle time-series:build`, unpackage either artifact in `time-series/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /time_series/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Time Series guide' 4 | ext.moduleName = 'swim.timeseries' 5 | mainClassName = 'swim.timeseries.BasicPlane' 6 | -------------------------------------------------------------------------------- /time_series/src/main/java/swim/timeseries/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.timeseries; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Record; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | final Kernel kernel = ServerLoader.loadServer(); 27 | final ActorSpace space = (ActorSpace) kernel.getSpace("timeseries"); 28 | 29 | kernel.start(); 30 | System.out.println("Running Basic server..."); 31 | System.out.println("Waiting for time series agents to start dropping records..."); 32 | kernel.run(); 33 | 34 | for (int i = 0; i < 20; i++) { 35 | space.command("/timeseries/window/by-time", "addEvent", Record.create().slot("id", i)); 36 | space.command("/timeseries/window/by-count", "addEvent", Record.create().slot("id", i)); 37 | space.command("/timeseries/window/by-recency", "addEvent", Record.create().slot("id", i)); 38 | Thread.sleep(5000); 39 | } 40 | 41 | kernel.stop(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /time_series/src/main/java/swim/timeseries/CountWindowAgent.java: -------------------------------------------------------------------------------- 1 | package swim.timeseries; 2 | 3 | import swim.api.SwimLane; 4 | import swim.api.agent.AbstractAgent; 5 | import swim.api.lane.CommandLane; 6 | import swim.api.lane.MapLane; 7 | import swim.structure.Value; 8 | 9 | public class CountWindowAgent extends AbstractAgent { 10 | 11 | public CountWindowAgent() { 12 | } 13 | 14 | private static final int MAX_HISTORY_SIZE = 10; 15 | 16 | @SwimLane("addEvent") 17 | private CommandLane addEvent = this.commandLane() 18 | .onCommand(v -> this.history.put(System.currentTimeMillis(), v)); 19 | 20 | @SwimLane("history") 21 | private MapLane history = this.mapLane() 22 | .didUpdate((k, n, o) -> trimHistory()); 23 | 24 | private void trimHistory() { 25 | final int dropCount = this.history.size() - MAX_HISTORY_SIZE; 26 | if (dropCount > 0) { 27 | this.history.drop(dropCount); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /time_series/src/main/java/swim/timeseries/HistoryLoggingAgent.java: -------------------------------------------------------------------------------- 1 | package swim.timeseries; 2 | 3 | import java.sql.Timestamp; 4 | import swim.api.SwimLane; 5 | import swim.api.agent.AbstractAgent; 6 | import swim.api.lane.MapLane; 7 | import swim.recon.Recon; 8 | import swim.structure.Value; 9 | 10 | public class HistoryLoggingAgent extends AbstractAgent { 11 | 12 | public HistoryLoggingAgent() { 13 | } 14 | 15 | @SwimLane("history") 16 | private MapLane history = this.mapLane() 17 | .didRemove(this::logRemoval) 18 | .didDrop(this::logDrop); 19 | 20 | private void logRemoval(final long key, final Value oldValue) { 21 | info( 22 | new Timestamp(System.currentTimeMillis()) 23 | + " " 24 | + nodeUri().toString() 25 | + ": " 26 | + "Removed record: " 27 | + "{ " 28 | + new Timestamp(key) 29 | + ": " 30 | + Recon.toString(oldValue) 31 | + " }" 32 | ); 33 | } 34 | 35 | private void logDrop(final int lower) { 36 | info( 37 | new Timestamp(System.currentTimeMillis()) 38 | + " " 39 | + nodeUri().toString() 40 | + ": " 41 | + "Dropped records: " 42 | + lower 43 | + " , new record count: " 44 | + this.history.size() 45 | ); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /time_series/src/main/java/swim/timeseries/RecencyWindowAgent.java: -------------------------------------------------------------------------------- 1 | package swim.timeseries; 2 | 3 | import java.util.Iterator; 4 | import swim.api.SwimLane; 5 | import swim.api.agent.AbstractAgent; 6 | import swim.api.lane.CommandLane; 7 | import swim.api.lane.MapLane; 8 | import swim.concurrent.TimerRef; 9 | import swim.structure.Value; 10 | 11 | public class RecencyWindowAgent extends AbstractAgent { 12 | 13 | public RecencyWindowAgent() { 14 | } 15 | 16 | private static final long MAX_HISTORY_TIME_MS = 30000L; 17 | 18 | private TimerRef timer; 19 | 20 | @SwimLane("addEvent") 21 | private CommandLane addEvent = this.commandLane() 22 | .onCommand(v -> this.history.put(System.currentTimeMillis(), v)); 23 | 24 | @SwimLane("history") 25 | private MapLane history = this.mapLane() 26 | .didUpdate((k, n, o) -> rescheduleNextTrim()); 27 | 28 | private void trimHistory() { 29 | final long oldestAllowedTimestamp = System.currentTimeMillis() - MAX_HISTORY_TIME_MS; 30 | final Iterator iterator = this.history.keyIterator(); 31 | 32 | while (iterator.hasNext()) { 33 | final long key = iterator.next(); 34 | if (key < oldestAllowedTimestamp) { 35 | // If the key is too old then remove it 36 | this.history.remove(key); 37 | } else { 38 | // Keys are ordered so stop when first key within allowed time is found 39 | rescheduleNextTrim(); 40 | break; 41 | } 42 | } 43 | } 44 | 45 | private void rescheduleNextTrim() { 46 | if (this.timer != null && this.timer.isScheduled()) { 47 | // The timer is already being handled 48 | return; 49 | } 50 | 51 | final long timeUntilNextTrim = (this.history.firstKey() + MAX_HISTORY_TIME_MS) - System.currentTimeMillis(); 52 | if (timeUntilNextTrim > 0) { 53 | // Set a timer for when the next record needs to be dropped 54 | this.timer = setTimer(timeUntilNextTrim, this::trimHistory); 55 | } else if (!this.history.isEmpty()) { 56 | // A record needs to be dropped now 57 | trimHistory(); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /time_series/src/main/java/swim/timeseries/TimeWindowAgent.java: -------------------------------------------------------------------------------- 1 | package swim.timeseries; 2 | 3 | import java.util.Iterator; 4 | import swim.api.SwimLane; 5 | import swim.api.agent.AbstractAgent; 6 | import swim.api.lane.CommandLane; 7 | import swim.api.lane.MapLane; 8 | import swim.structure.Value; 9 | 10 | public class TimeWindowAgent extends AbstractAgent { 11 | 12 | public TimeWindowAgent() { 13 | } 14 | 15 | private static final long TIME_INTERVAL_MS = 30000L; 16 | 17 | @SwimLane("addEvent") 18 | private CommandLane addEvent = this.commandLane() 19 | .onCommand(v -> this.history.put(System.currentTimeMillis(), v)); 20 | 21 | @SwimLane("history") 22 | private MapLane history = this.mapLane() 23 | .didUpdate((k, n, o) -> trimHistory()); 24 | 25 | private void trimHistory() { 26 | final long oldestAllowedTimestamp = this.history.lastKey() - TIME_INTERVAL_MS; 27 | final Iterator iterator = this.history.keyIterator(); 28 | 29 | while (iterator.hasNext()) { 30 | final long key = iterator.next(); 31 | if (key < oldestAllowedTimestamp) { 32 | // If the key is too old then remove it 33 | this.history.remove(key); 34 | } else { 35 | // Keys are ordered so stop when first key within allowed time is found 36 | break; 37 | } 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /time_series/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | timeseries: @fabric { 2 | @plane(class: "swim.timeseries.BasicPlane") 3 | 4 | @node { 5 | pattern: "/timeseries/window/by-count" 6 | @agent(class: "swim.timeseries.CountWindowAgent") 7 | @agent(class: "swim.timeseries.HistoryLoggingAgent") 8 | } 9 | 10 | @node { 11 | pattern: "/timeseries/window/by-time" 12 | @agent(class: "swim.timeseries.TimeWindowAgent") 13 | @agent(class: "swim.timeseries.HistoryLoggingAgent") 14 | } 15 | 16 | @node { 17 | pattern: "/timeseries/window/by-recency" 18 | @agent(class: "swim.timeseries.RecencyWindowAgent") 19 | @agent(class: "swim.timeseries.HistoryLoggingAgent") 20 | } 21 | 22 | } 23 | @web(port: 9001) { 24 | space: "timeseries" 25 | @websocket { 26 | serverCompressionLevel: 0 27 | # -1 = default; 0 = off; 1-9 = deflate level 28 | clientCompressionLevel: 0 29 | # -1 = default; 0 = off; 1-9 = deflate level 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /timers/README.md: -------------------------------------------------------------------------------- 1 | # Timers 2 | 3 | Code corresponding to the [Timers cookbook](https://swimos.org/tutorials/timers/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 10 | -------------------------------------------------------------------------------- /timers/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Timers 2 | 3 | 代码可在 [Timers cookbook](https://swimos.org/tutorials/timers/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Timers 展示 8 | 9 | 该演示需要按照顺序依次运行 `swim.basic.BasicPlane` 类和 `swim.basic.CustomClient` 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.basic.CustomClient` 的名称为 `runClient`。 10 | -------------------------------------------------------------------------------- /timers/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Timers cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /timers/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /timers/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | space.command("/unit/foo", "wakeup", Value.absent()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /timers/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.client.ClientRuntime; 18 | import swim.structure.Value; 19 | 20 | final class CustomClient { 21 | 22 | private CustomClient() { 23 | } 24 | 25 | public static void main(String[] args) throws InterruptedException { 26 | final ClientRuntime swimClient = new ClientRuntime(); 27 | swimClient.start(); 28 | final String hostUri = "warp://localhost:9001"; 29 | final String nodeUri = "/unit/foo"; 30 | for (int i = 0; i < 10; i++) { 31 | swimClient.command(hostUri, nodeUri, "publish", Value.absent()); 32 | Thread.sleep(5000 * i); 33 | } 34 | System.out.println("Will shut down client"); 35 | swimClient.stop(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /timers/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | import swim.concurrent.TimerRef; 22 | import swim.structure.Value; 23 | 24 | public class UnitAgent extends AbstractAgent { 25 | 26 | public UnitAgent() { 27 | } 28 | 29 | @SwimLane("minutesSincePublish") 30 | ValueLane minutes = this.valueLane() 31 | .didSet((n, o) -> { 32 | System.out.println((n * 1) + " seconds since last event"); 33 | }); 34 | private TimerRef timer; 35 | @SwimLane("publish") 36 | CommandLane publish = this.commandLane() 37 | .onCommand(v -> { 38 | this.minutes.set(0); 39 | resetTimer(); 40 | }); 41 | 42 | @Override 43 | public void didStart() { 44 | resetTimer(); 45 | } 46 | 47 | @Override 48 | public void willStop() { 49 | cancelTimer(); 50 | } 51 | 52 | private void resetTimer() { 53 | cancelTimer(); 54 | this.timer = setTimer(1000, () -> { 55 | this.minutes.set(this.minutes.get() + 1); 56 | this.timer.reschedule(1000); 57 | }); 58 | } 59 | 60 | private void cancelTimer() { 61 | if (this.timer != null) { 62 | this.timer.cancel(); 63 | } 64 | this.timer = null; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /timers/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /traits/README.md: -------------------------------------------------------------------------------- 1 | # Traits 2 | 3 | Code corresponding to the [Traits cookbook](https://swimos.org/tutorials/traits/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo only requires running the `swim.liquid.LiquidPlane` class. 10 | 11 | Thus, `gradle traits:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `gradle traits:build`, unpackage either artifact in `traits/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /traits/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Traits cookbook' 4 | ext.moduleName = 'swim.liquid' 5 | mainClassName = 'swim.liquid.LiquidPlane' 6 | -------------------------------------------------------------------------------- /traits/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.liquid { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | 19 | exports swim.liquid; 20 | } 21 | -------------------------------------------------------------------------------- /traits/src/main/java/swim/liquid/LiquidPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.liquid; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class LiquidPlane extends AbstractPlane { 24 | 25 | public LiquidPlane() { 26 | } 27 | 28 | public static void main(String[] args) throws InterruptedException { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("liquid"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | // Dynamic Agent(s) 37 | int n = 0; 38 | String nodeString = ""; 39 | final String[] listOfLiquid = new String[]{"black", "pineapple", "tap", "mango"}; 40 | while (n < 4) { 41 | if (n % 2 == 0) { 42 | nodeString = "/liquid/dynamic/water/" + listOfLiquid[n]; 43 | } else { 44 | nodeString = "/liquid/dynamic/juice/" + listOfLiquid[n]; 45 | } 46 | space.command(nodeString, "unusedForNow", Value.absent()); 47 | n++; 48 | Thread.sleep(2000); 49 | } 50 | 51 | System.out.println("Server will shut down in 3 seconds"); 52 | Thread.sleep(3000); 53 | System.out.println("Sent shutdown signal to server"); 54 | kernel.stop(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /traits/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | @kernel(class: "swim.store.db.DbStoreKernel", optional: true) 2 | @web(port: 9001) { 3 | space: "liquid" 4 | documentRoot: "./ui/" 5 | @websocket { 6 | serverCompressionLevel: 0 7 | # -1 = default; 0 = off; 1-9 = deflate level 8 | clientCompressionLevel: 0 9 | # -1 = default; 0 = off; 1-9 = deflate level 10 | } 11 | } 12 | liquid: @fabric { 13 | @plane(class: "swim.liquid.LiquidPlane") 14 | @node { 15 | uri: "/liquid/static/water/sparkling" 16 | @agent(class: "swim.liquid.agent.WaterAgent") { 17 | waterType: "Sparkling Water" 18 | } 19 | @agent(class: "swim.liquid.agent.LiquidAgent") { 20 | liquidType: "Water" 21 | } 22 | } 23 | @node { 24 | uri: "/liquid/static/juice/orange" 25 | @agent(class: "swim.liquid.agent.JuiceAgent") { 26 | juiceType: "Orange Juice" 27 | } 28 | @agent(class: "swim.liquid.agent.LiquidAgent") { 29 | liquidType: "Juice" 30 | } 31 | } 32 | @node { 33 | pattern: "/liquid/:trait/:id1/:id2" 34 | @agent(class: "swim.liquid.agent.WaterAgent") { 35 | } 36 | @agent(class: "swim.liquid.agent.JuiceAgent") { 37 | } 38 | @agent(class: "swim.liquid.agent.LiquidAgent") { 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /value_lanes/README.md: -------------------------------------------------------------------------------- 1 | # Value Lanes 2 | 3 | Code corresponding to the [Value Lanes cookbook](https://swimos.org/tutorials/value-lanes/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo requires running the `swim.basic.BasicPlane` class and the `swim.basic.CustomClient` class in that order. Review [`README.md` from the parent directory](../README.md) for instructions on how to do this. For those who pick Option 2, the `gradle` task corresponding to `swim.basic.CustomClient` is called `runClient`. 10 | -------------------------------------------------------------------------------- /value_lanes/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Value Lanes 2 | 3 | 代码可在 [Value Lanes cookbook](https://swimos.org/tutorials/value-lanes/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Value Lanes 展示 8 | 9 | 该演示需要按照顺序依次运行 `swim.basic.BasicPlane` 类和 `swim.basic.CustomClient` 类。回顾[`父目录中的README.zh-cn.md`](../README.zh-cn.md)教程可知如何进行运行。对于选择了选项2的用户,`gradle` task中对应 `swim.basic.CustomClient` 的名称为 `runClient`。 10 | -------------------------------------------------------------------------------- /value_lanes/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Value Lanes cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | 7 | task runClient(type: JavaExec) { 8 | group = "application" 9 | classpath sourceSets.main.runtimeClasspath 10 | mainClass = "swim.basic.CustomClient" 11 | } 12 | -------------------------------------------------------------------------------- /value_lanes/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | requires swim.client; 19 | 20 | exports swim.basic; 21 | } 22 | -------------------------------------------------------------------------------- /value_lanes/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | space.command("/unit/foo", "wakeup", Value.absent()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /value_lanes/src/main/java/swim/basic/CustomClient.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.downlink.ValueDownlink; 18 | import swim.client.ClientRuntime; 19 | import swim.structure.Text; 20 | import swim.structure.Value; 21 | 22 | final class CustomClient { 23 | 24 | private CustomClient() { 25 | } 26 | 27 | public static void main(String[] args) throws InterruptedException { 28 | final ClientRuntime swimClient = new ClientRuntime(); 29 | swimClient.start(); 30 | final String hostUri = "warp://localhost:9001"; 31 | final String nodeUri = "/unit/foo"; 32 | // Reduce probability of startup race; no need to conflate this example 33 | // with proper synchronization just yet 34 | swimClient.command(hostUri, nodeUri, "WAKEUP", Value.absent()); 35 | // Link with a didSet() override 36 | final ValueDownlink link = swimClient.downlinkValue() 37 | .hostUri(hostUri).nodeUri(nodeUri).laneUri("info") 38 | .didSet((newValue, oldValue) -> { 39 | System.out.println("link watched info change TO " + newValue + " FROM " + oldValue); 40 | }) 41 | .open(); 42 | // Send using either the proxy command lane... 43 | swimClient.command(hostUri, nodeUri, "publishInfo", Text.from("Hello from command, world!")); 44 | // ...or a downlink set() 45 | link.set(Text.from("Hello from link, world!")); 46 | System.out.println("synchronous link get: " + link.get()); 47 | 48 | System.out.println("Will shut down client in 2 seconds"); 49 | Thread.sleep(2000); 50 | swimClient.stop(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /value_lanes/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.SwimLane; 18 | import swim.api.agent.AbstractAgent; 19 | import swim.api.lane.CommandLane; 20 | import swim.api.lane.ValueLane; 21 | 22 | public class UnitAgent extends AbstractAgent { 23 | 24 | public UnitAgent() { 25 | } 26 | 27 | @SwimLane("info") 28 | ValueLane info = this.valueLane() 29 | .didSet((newValue, oldValue) -> { 30 | logMessage("`info` set to {" + newValue + "} from {" + oldValue + "}"); 31 | }); 32 | 33 | @SwimLane("publishInfo") 34 | CommandLane publishInfo = this.commandLane() 35 | .onCommand(msg -> { 36 | this.info.set("from publishInfo: " + msg); 37 | }); 38 | 39 | private void logMessage(Object msg) { 40 | System.out.println(nodeUri() + ": " + msg); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /value_lanes/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /web_agents/README.md: -------------------------------------------------------------------------------- 1 | # Web Agents 2 | 3 | Code corresponding to the [Web Agents cookbook](https://swimos.org/tutorials/web-agents/). 4 | 5 | *Read this in other languages: [简体中文](README.zh-cn.md)* 6 | 7 | ## Running This Demo 8 | 9 | This demo only requires running the `swim.basic.BasicPlane` class. 10 | 11 | Thus, `gradle web-agents:run` is sufficient for those who pick Option 2 in [`README.md` from the parent directory](../README.md). 12 | 13 | For those who pick Option 3, simply execute `gradle web-agents:build`, unpackage either artifact in `web-agents/build/distributions`, and run the appropriate script in `bin/`; no file copying/editing is required. 14 | -------------------------------------------------------------------------------- /web_agents/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Web Agents 2 | 3 | 代码可在 [Web Agents cookbook](https://swimos.org/tutorials/web-agents/) 中找到。 4 | 5 | *其他语言版本:[English](README.md), [简体中文](README.zh-cn.md)* 6 | 7 | ## 运行 Web Agents 展示 8 | 9 | 该展示只需要运行 `swim.basic.BasicPlane` 类。 10 | 11 | 因此,对于在[`父目录中的README.zh-cn.md`](../README.zh-cn.md)中选了选项2的用户来说,命令行`gradle web-agents:run`就足够了。 12 | 13 | 对于选择了选项3的用户,只需简单地执行 `gradle web-agents:build`,解压任意在目录 `web-agents/build/distributions` 下生成的文件,然后运行在 `bin/` 之下的与环境相匹配的脚本文件;并不需要复制或者改动文件。 14 | -------------------------------------------------------------------------------- /web_agents/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../project.gradle' 2 | 3 | description = 'Code corresponding to the Web Agents cookbook' 4 | ext.moduleName = 'swim.basic' 5 | mainClassName = 'swim.basic.BasicPlane' 6 | -------------------------------------------------------------------------------- /web_agents/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | open module swim.basic { 16 | requires transitive swim.api; 17 | requires swim.server; 18 | 19 | exports swim.basic; 20 | } 21 | -------------------------------------------------------------------------------- /web_agents/src/main/java/swim/basic/BasicPlane.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.actor.ActorSpace; 18 | import swim.api.plane.AbstractPlane; 19 | import swim.kernel.Kernel; 20 | import swim.server.ServerLoader; 21 | import swim.structure.Value; 22 | 23 | public class BasicPlane extends AbstractPlane { 24 | 25 | public BasicPlane() { 26 | } 27 | 28 | public static void main(String[] args) throws InterruptedException { 29 | final Kernel kernel = ServerLoader.loadServer(); 30 | final ActorSpace space = (ActorSpace) kernel.getSpace("basic"); 31 | 32 | kernel.start(); 33 | System.out.println("Running Basic server..."); 34 | kernel.run(); 35 | 36 | space.command("/unit/1", "unusedForNow", Value.absent()); 37 | space.command("/unit/foo", "unusedForNow", Value.absent()); 38 | space.command("/unit/foo_1", "unusedForNow", Value.absent()); 39 | 40 | Thread.sleep(1000); 41 | 42 | System.out.println("Server will shut down in 3 seconds"); 43 | Thread.sleep(3000); 44 | System.out.println("Sent shutdown signal to server"); 45 | kernel.stop(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /web_agents/src/main/java/swim/basic/UnitAgent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2023 Nstream, inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package swim.basic; 16 | 17 | import swim.api.agent.AbstractAgent; 18 | 19 | public class UnitAgent extends AbstractAgent { 20 | 21 | public UnitAgent() { 22 | } 23 | 24 | @Override 25 | public void didStart() { 26 | logMessage("didStart"); 27 | close(); 28 | } 29 | 30 | @Override 31 | public void willStop() { 32 | logMessage("willStop"); 33 | } 34 | 35 | private void logMessage(Object msg) { 36 | System.out.println(nodeUri() + ": " + msg); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /web_agents/src/main/resources/server.recon: -------------------------------------------------------------------------------- 1 | basic: @fabric { 2 | @plane(class: "swim.basic.BasicPlane") 3 | @node { 4 | pattern: "/unit/:id" 5 | @agent(class: "swim.basic.UnitAgent") 6 | } 7 | } 8 | @web(port: 9001) { 9 | space: "basic" 10 | documentRoot: "./ui/" 11 | @websocket { 12 | serverCompressionLevel: 0 13 | # -1 = default; 0 = off; 1-9 = deflate level 14 | clientCompressionLevel: 0 15 | # -1 = default; 0 = off; 1-9 = deflate level 16 | } 17 | } 18 | --------------------------------------------------------------------------------