{"id":20929666,"url":"https://github.com/swiftech/swstate","last_synced_at":"2026-05-20T00:03:24.687Z","repository":{"id":57723461,"uuid":"387223739","full_name":"swiftech/SWState","owner":"swiftech","description":"A really easy to use but powerful State Machine implementation based on Java 8 with zero dependencies.","archived":false,"fork":false,"pushed_at":"2024-02-25T06:04:03.000Z","size":97,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-19T18:47:30.673Z","etag":null,"topics":["java","state","state-machine"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swiftech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-18T16:45:10.000Z","updated_at":"2023-12-09T00:03:45.000Z","dependencies_parsed_at":"2022-08-28T14:00:57.921Z","dependency_job_id":"3bbf8235-6bff-4860-b3cf-0e0230662540","html_url":"https://github.com/swiftech/SWState","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftech%2FSWState","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftech%2FSWState/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftech%2FSWState/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftech%2FSWState/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swiftech","download_url":"https://codeload.github.com/swiftech/SWState/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243324267,"owners_count":20273099,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","state","state-machine"],"created_at":"2024-11-18T21:22:41.706Z","updated_at":"2025-12-29T01:08:27.902Z","avatar_url":"https://github.com/swiftech.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SWState\n\nA really easy to use but useful State Machine implementation with zero dependencies.\n\n## Usage\n\nAssume that we have a turnstile with 2 states `Locked`, `Unlocked` and 2 actions `Coin`, `Push`, as the diagram shows:\n\n![](docs/state_machine1.png)\n\nThe main class that you use SWState is `StateMachine`, before using that, you should build it by class `StateBuilder`.\n\n### Build the `StateMachine`\n  \ndefined states：\n\n```java\nfinal String STATE_LOCKED = \"Locked\";\nfinal String STATE_UNLOCKED = \"Unlocked\";\n```  \n\nUse `StateBuilder` to define Actions and Processes:\n\n```java\nStateBuilder\u003cString, Serializable\u003e stateBuilder = new StateBuilder\u003c\u003e();\nstateBuilder\n        .state(STATE_LOCKED)\n        .in(order -\u003e {\n            // Handle before the turnstile is locked.\n            System.out.println(\"turnstile is locked\");\n        })\n        .state(STATE_UNLOCKED)\n        .in(order -\u003e {\n            // Handle before the turnstile is unlocked.\n            System.out.println(\"turnstile is unlocked\");\n        })\n        .initialize(STATE_LOCKED)\n        .action(\"coin_locked\", STATE_LOCKED, STATE_UNLOCKED)\n        .action(\"push_unlocked\", STATE_UNLOCKED, STATE_LOCKED)\n        .action(\"coin_unlocked\", STATE_UNLOCKED, STATE_UNLOCKED)\n        .action(\"push_unlocked\", STATE_LOCKED, STATE_LOCKED);\nStateMachine\u003cString, Serializable\u003e stateMachine = new StateMachine\u003c\u003e(stateBuilder);\n```\n\nAs you can see, we have set up two states and four actions to change states.\nthe method `in()` bind your actual processing code block\n\n\n### Use `StateMachine` to transit states as per previous definitions. \n\n```java\nString id = \"turnstile0-1\";\nstateMachine.start(id);\n...\nstateMachine.post(id, STATE_UNLOCKED);\n...\nstateMachine.post(id, STATE_LOCKED);\n```\n\n\u003e The parameter `id` of `start()` or `post()` identifies the object that using this state machine, which means different ids have their own state.\n\u003e If states change with payload, call `postWithPayload` methods with payload, like `postWithPayload(id, payload)`.\n\u003e As of v2.2, If states change on current state conditionally, use `postOnState` and `postWithPayloadOnState` methods.\n\nIf no ID is used to identify an object, just call methods without ID:\n\n```java\nstateMachine.start();\n...\nstateMachine.post(STATE_UNLOCKED);\n...\nstateMachine.post(STATE_LOCKED);\n```\n\n### Trigger\nAs of version 2.0, Trigger is introduced to provide state transition automatically. \nAssume that the state transitions are determined just by some input, with trigger, you don't need to write the logic code by your own, it can be done automatically.\n\nfor example, by defining specific input to trigger the state transition:\n\n* first, define the actions like this:\n```java\nstateBuilder\n        ...\n        .action(\"coin_locked\", STATE_LOCKED, STATE_UNLOCKED, stateBuilder.triggerBuilder().c('a', 'A').build())\n        .action(\"push_unlocked\", STATE_UNLOCKED, STATE_LOCKED, stateBuilder.triggerBuilder().i(1).build())\n        .action(\"coin_unlocked\", STATE_UNLOCKED, STATE_UNLOCKED, stateBuilder.triggerBuilder().f(1.0f).build())\n        .action(\"push_unlocked\", STATE_LOCKED, STATE_LOCKED, stateBuilder.triggerBuilder().s(\"STRING1\", \"STRING2\").build());\n```\n* to activate the state transition automatically, consume the input using `accept` method of `StateMachine` continuously:\n```java\n    stateMachine.accept('a'); // transit state from STATE_LOCKED to STATE_UNLOCKED\n    stateMachine.accept(1); // transit state from STATE_UNLOCKED to STATE_LOCKED\n    stateMachine.accept(\"unkown input\") // THIS WON'T TRIGGER ANY STATE TRANSITION. \n```\n\n* of course, custom trigger can be defined to handle specific situations, eg:\n```java\n.action(\"coin_locked\", STATE_LOCKED, STATE_UNLOCKED, stateBuilder.triggerBuilder().c('a', 'A')\n        .custom((data, payload) -\u003e {\n            return payload.content.equals(\"ALLOWED\");\n        )\n        .build())\n```\n\u003e this is equivalent to posting state by `post()` method.\n\n## Advanced\n\nThe `StateMachine` stores states in memory by default, if you want to store states into other storages like RDB or nosql,\nthere are two ways to get this done, implement a `StateProvide` or use `StateTransition` directly.\n\nExample:\nAssume that we have simplified online shopping order processing with some order states, as the diagram shows:\n\n![](docs/state_machine2.png)\n\ndefined states\n\n```java\nfinal String STATE_CREATED = \"Created\";\nfinal String STATE_PAYED = \"Payed\";\nfinal String STATE_CANCELED = \"Canceled\";\nfinal String STATE_RECEIVED = \"Received\";\n```\n\nSet up Actions and Process with `StateBuilder`:\n\n```java\nStateBuilder\u003cString, Order\u003e stateBuilder = new StateBuilder\u003c\u003e();\nstateBuilder\n    .state(STATE_CREATED)\n    .in(order -\u003e {\n    // Handle the order is created .\n    })\n    .state(STATE_PAYED)\n    .in(order -\u003e {\n    // Handle the order is payed.\n    })\n    .state(STATE_CANCELED)\n    .in(order -\u003e {\n    // Handle the order is canceled\n    })\n    .state(STATE_RECEIVED)\n    .in(order -\u003e {\n    // Handle the delivery\n    })\n    .initialize(\"create order\", STATE_CREATED)\n    .action(\"pay order\", STATE_CREATED, STATE_PAYED)\n    .action(\"cancel order\", STATE_CREATED, STATE_CANCELED)\n    .action(\"deliver goods\", STATE_PAYED, STATE_RECEIVED);\n```\n\n### Method 1: Customized State Provider\n\nTo store states, you need to implement a `StateProvider`, SWState provides a `DefaultStateProvider` which stores states\nin memory, but it is probably not suit your situation. Usually, the states you want to manage are in a column of\nDB tables, so let's implement a database version `StateProvider`.\n\n`MyDatabaseStateProvider.java`\n\n```java\nimport com.github.swiftech.swstate.StateProvider;\n\npublic class MyDatabaseStateProvider implements StateProvider\u003cString\u003e {\n  public MyDatabaseStateProvider() {\n      // do some necessary init\n  }\n  ... // implement all methods for storing or retrieving state from a database.\n}\n```\n\nReplace the default state provider of state machine with yours:\n\n```java\nstateMachine.setStateProvider(new MyDatabaseStateProvider());\n```\n\n### Method 2: Use `StateTransition`\n\nInstead of `StateMachine`, `StateTransition` is at lower level, it doesn't store current state but only process state transition.\n\nFirst, construct instance of `StateTransition` just like `StateMachine` does.\n\n```java\nStateTransition\u003cString, Order\u003e stateTransition = new StateTransition\u003c\u003e(stateBuilder);\n```\n\nSecond, use `stateTransition` to transit states which are loaded from other storage:\n\n```java\npublic void pay(String id){\n    String currentState = repository.getState(id); // repository is your own data access API\n    stateTransition.post(currentState, STATE_PAYED); // if the current state is not 'Created', it fails as per previous setting\n}\n```\n\n### Exception handling\n\nAs of v2.1, you can set whether to throw an exception when an internal exception occurs by calling `setSilent()`.\nBut you want to be notified when an exception occurs despite setting silence, call `setExceptionHandler()` to set an exception callback, e.g.:\n\n```java\nstateTransition.setSilent(true);\nstateTransition.setExceptionHandler(stateException -\u003e {\n    System.out.println(\"exception: \" + stateException.getMessage());\n});\n```\n\n### IN/OUT process on self-circulation\nBy default, a self-circulation state transition still causes the IN/OUT processes to be executed. \nHowever, if you want to disable this default behavior in some cases, As of v2.2, the SWState allows you to control the IN/OUT processes for self-circulation state transition. \nUse `setNoInProcessForSelfCirculation()` and `setNoOutProcessForSelfCirculation()` to set whether the IN/OUT processes will be executed for self-circulation state transition.\n\n## Maven\n\n* Stable version\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.swiftech\u003c/groupId\u003e\n    \u003cartifactId\u003eswstate\u003c/artifactId\u003e\n    \u003cversion\u003e2.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n* Unstable version\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.swiftech\u003c/groupId\u003e\n    \u003cartifactId\u003eswstate\u003c/artifactId\u003e\n    \u003cversion\u003e2.2.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\u003e The Minimum JDK version is 17. if you are still stuck on the JDK 8, please use v1.1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswiftech%2Fswstate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswiftech%2Fswstate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswiftech%2Fswstate/lists"}