{"id":19901555,"url":"https://github.com/cettia/cettia-java-server","last_synced_at":"2025-05-02T23:31:52.607Z","repository":{"id":28921864,"uuid":"32447005","full_name":"cettia/cettia-java-server","owner":"cettia","description":"A full-featured real-time web application framework for Java ","archived":false,"fork":false,"pushed_at":"2018-11-05T11:26:51.000Z","size":155,"stargazers_count":41,"open_issues_count":4,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-07T08:33:17.972Z","etag":null,"topics":["cettia","comet","framework-agnostic","java","long-polling","real-time","server","streaming","web-fragment","websocket"],"latest_commit_sha":null,"homepage":"https://cettia.io","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/cettia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-18T08:33:37.000Z","updated_at":"2024-05-22T08:42:36.000Z","dependencies_parsed_at":"2022-09-27T10:50:43.489Z","dependency_job_id":null,"html_url":"https://github.com/cettia/cettia-java-server","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cettia%2Fcettia-java-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cettia%2Fcettia-java-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cettia%2Fcettia-java-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cettia%2Fcettia-java-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cettia","download_url":"https://codeload.github.com/cettia/cettia-java-server/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252122275,"owners_count":21698305,"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":["cettia","comet","framework-agnostic","java","long-polling","real-time","server","streaming","web-fragment","websocket"],"created_at":"2024-11-12T20:15:30.777Z","updated_at":"2025-05-02T23:31:52.138Z","avatar_url":"https://github.com/cettia.png","language":"Java","funding_links":[],"categories":["开发框架"],"sub_categories":["Web框架"],"readme":"# Cettia\n\nCettia is a full-featured real-time web application framework for Java that you can use to exchange events between server and client in real-time. *It is meant for when you run into issues which are tricky to resolve with WebSocket, JSON, and switch statement per se:*\n\n- Avoiding repetitive boilerplate code\n- Supporting environments where WebSocket is not available\n- Handling both text and binary data together\n- Recovering missed events\n- Providing multi-device user experience\n- Scaling out an application, and so on.\n\nIt offers a reliable full duplex message channel and elegant patterns to achieve better user experience in the real-time web, and is compatible with any web frameworks on the Java Virtual Machine.\n\n---\n\nThe following is a summary of the Cettia starter kit to help you get started quickly. In the summary, comments starting with `##` refer to a title of a related chapter in the tutorial, [Building Real-Time Web Applications With Cettia](https://cettia.io/guides/cettia-tutorial/), where you can find a detailed explanation. You may want to highlight the `##`.\n\nMaven dependencies.\n\n```xml\n\u003c!-- ## Setting Up the Project --\u003e\n\u003c!-- To write a Cettia application --\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.cettia\u003c/groupId\u003e\n  \u003cartifactId\u003ecettia-server\u003c/artifactId\u003e\n  \u003cversion\u003e1.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n\u003c!-- To run a Cettia application on Servlet 3 and Java WebSocket API 1 --\u003e\n\u003c!-- Besides them, you can also use Spring WebFlux, Spring MVC, Grizzly, Vert.x, Netty, and so on --\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.cettia.asity\u003c/groupId\u003e\n  \u003cartifactId\u003easity-bridge-servlet3\u003c/artifactId\u003e\n  \u003cversion\u003e2.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.cettia.asity\u003c/groupId\u003e\n  \u003cartifactId\u003easity-bridge-jwa1\u003c/artifactId\u003e\n  \u003cversion\u003e2.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nA class to play with the Cettia server. Import statements, verbose try-catch blocks, empty methods, etc. are skipped for brevity.\n\n```Java\n@WebListener\npublic class CettiaConfigListener implements ServletContextListener {\n  public void contextInitialized(ServletContextEvent event) {\n    // Cettia part\n    // If you don't want to form a cluster,\n    // replace the following line with 'Server server = new DefaultServer();'\n    ClusteredServer server = new ClusteredServer();\n    HttpTransportServer httpAction = new HttpTransportServer().ontransport(server);\n    WebSocketTransportServer wsAction = new WebSocketTransportServer().ontransport(server);\n\n    // If a client opens a socket, the server creates and passes a socket to socket handlers\n    server.onsocket((ServerSocket socket) -\u003e {\n      // ## Socket Lifecycle\n      Action\u003cVoid\u003e logState = v -\u003e System.out.println(socket + \" \" + socket.state());\n      socket.onopen(logState).onclose(logState).ondelete(logState);\n\n      // ## Sending and Receiving Events\n      // An 'echo' event handler where any received echo event is sent back\n      socket.on(\"echo\", data -\u003e socket.send(\"echo\", data));\n\n      // ## Attributes and Tags\n      // Attributes and tags are contexts to store the socket state in the form of Map and Set\n      String username = findParam(socket.uri(), \"username\");\n      if (username == null) {\n        // Attaches a tag to the socket\n        socket.tag(\"nonmember\");\n      } else {\n        // Associates an attribute with the the socket\n        socket.set(\"username\", username);\n      }\n\n      // ## Working with Sockets\n      // A 'chat' event handler to send a given chat event to every socket in every server in the cluster\n      socket.on(\"chat\", data -\u003e server.find(ServerSocketPredicates.all()).send(\"chat\", data));\n\n      // ## Advanced Sockets Handling\n      if (username != null) {\n        // A group of sockets representing the same user, whose username is the given one\n        Sentence user = server.find(ServerSocketPredicates.attr(\"username\", username));\n        // A 'myself' event handler to send a given myself event to myself whose username is the same\n        socket.on(\"myself\", data -\u003e {\n          // You can directly handle each socket through the execute method of Sentence if needed\n          user.execute(s -\u003e s.send(\"myself\"));\n        });\n\n        // Limits only one socket per user\n        boolean onlyOneSocket = Boolean.parseBoolean(findParam(socket.uri(), \"onlyOneSocket\"));\n        if (onlyOneSocket) {\n          // Finds sockets opened by whose username is the same other than this socket and\n          // sends a 'signout' event to prevent reconnection and closes a connection\n          user.find(id(socket).negate()).send(\"signout\").close();\n        }\n      }\n\n      // ## Disconnection Handling\n      Queue\u003cObject[]\u003e queue = new ConcurrentLinkedQueue\u003c\u003e();\n      // Caches events that fail to send due to disconnection\n      socket.oncache(args -\u003e queue.offer(args));\n      // Sends cached events on the next connection\n      socket.onopen(v -\u003e {\n        while (socket.state() == ServerSocket.State.OPENED \u0026\u0026 !queue.isEmpty()) {\n          Object[] args = queue.poll();\n          socket.send((String) args[0], args[1], (Action\u003c?\u003e) args[2], (Action\u003c?\u003e) args[3]);\n        }\n      });\n      // If the client fails to connect within 1 minute after disconnection,\n      // You may want to consider notifying the user of finally missed events, like push notifications\n      socket.ondelete(v -\u003e queue.forEach(args -\u003e {\n        System.out.println(socket + \" missed event - name: \" + args[0] + \", data: \" + args[1]);\n      }));\n    });\n\n    // ## Working with Sockets\n    // To deal with sockets, inject the server wherever you want\n    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();\n    // Sends a 'welcome' event to sockets representing user not signed in every 5 seconds\n    executor.scheduleAtFixedRate(() -\u003e server.byTag(\"nonmember\").send(\"welcome\"), 0, 5, SECONDS);\n\n    // ## Plugging Into the Web Framework\n    // Cettia is designed to run on any web framework seamlessly on the JVM\n    // Note how 'httpAction' and 'wsAction' are plugged into Servlet and Java API for Websocket\n    ServletContext context = event.getServletContext();\n    AsityServlet asityServlet = new AsityServlet().onhttp(/* ㅇㅅㅇ */ httpAction);\n    ServletRegistration.Dynamic reg = context.addServlet(AsityServlet.class.getName(), asityServlet);\n    reg.setAsyncSupported(true);\n    reg.addMapping(\"/cettia\");\n\n    ServerContainer container = (ServerContainer) context.getAttribute(ServerContainer.class.getName());\n    ServerEndpointConfig.Configurator configurator = new ServerEndpointConfig.Configurator() {\n      public \u003cT\u003e T getEndpointInstance(Class\u003cT\u003e endpointClass) {\n        AsityServerEndpoint asityServerEndpoint = new AsityServerEndpoint();\n        asityServerEndpoint.onwebsocket(/* ㅇㅅㅇ */ wsAction);\n        return endpointClass.cast(asityServerEndpoint);\n      }\n    };\n    container.addEndpoint(ServerEndpointConfig.Builder.create(AsityServerEndpoint.class, \"/cettia\")\n      .configurator(configurator).build());\n\n    // ## Scaling a Cettia Application\n    // Any publish-subscribe messaging system can be used to scale a Cettia application horizontally,\n    // and it doesn’t require any modification in the existing application.\n    HazelcastInstance hazelcast = HazelcastInstanceFactory.newHazelcastInstance(new Config());\n    ITopic\u003cMap\u003cString, Object\u003e\u003e topic = hazelcast.getTopic(\"cettia\");\n    // It publishes messages given by the server\n    server.onpublish(message -\u003e topic.publish(message));\n    // It relays published messages to the server\n    topic.addMessageListener(message -\u003e server.messageAction().on(message.getMessageObject()));\n  }\n}\n```\n\nHere's an example with the Spring WebFlux 5 to show Cettia's framework-agnostic nature. Consult Asity’s [Run Anywhere](http://asity.cettia.io/#run-anywhere) section for how to plug a Cettia application into other various frameworks.\n\n```java\n@SpringBootApplication\n@EnableWebFlux\npublic class CettiaServer {\n  @Bean\n  public RouterFunction\u003cServerResponse\u003e httpMapping(HttpTransportServer httpAction) {\n    AsityHandlerFunction asityHandlerFunction = new AsityHandlerFunction();\n    asityHandlerFunction.onhttp(/* ㅇㅅㅇ */ httpAction);\n\n    RequestPredicate isNotWebSocket = headers(h -\u003e !\"websocket\".equalsIgnoreCase(h.asHttpHeaders().getUpgrade()));\n    return route(path(\"/cettia\").and(isNotWebSocket), asityHandlerFunction);\n  }\n\n  @Bean\n  public HandlerMapping wsMapping(WebSocketTransportServer wsAction) {\n    AsityWebSocketHandler asityWebSocketHandler = new AsityWebSocketHandler();\n    asityWebSocketHandler.onwebsocket(/* ㅇㅅㅇ */ wsAction);\n    Map\u003cString, WebSocketHandler\u003e map = new LinkedHashMap\u003c\u003e();\n    map.put(\"/cettia\", asityWebSocketHandler);\n\n    SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();\n    mapping.setUrlMap(map);\n\n    return mapping;\n  }\n}\n```\n\nYou need minimal HTML to load the `cettia` object. Also, if you have a `cettia-client` npm module installed, you should be able to load the `cettia` object with `require(\"cettia-client/cettia-bundler\");` and `require(\"cettia-client\");` in Webpack and Node, respectively.\n\n```html\n \u003c!DOCTYPE html\u003e\n \u003ctitle\u003eindex\u003c/title\u003e\n \u003cscript src=\"https://unpkg.com/cettia-client@1.0.1/cettia-browser.min.js\"\u003e\u003c/script\u003e\n```\n\nBelow is the JavaScript code to play with the `cettia` object. Open the above page and its developer console in several browsers, run the script, and watch results on the fly.\n\n```javascript\n// ## Opening a Socket\n// Manipulates the below params object to play with the server implementation\nvar params = {\n  username: \"flowersinthesand\",\n  onlyOneSocket: true\n};\n// Let's assume that each key and value are already encoding safe\nvar query = Object.keys(params).filter(k =\u003e params[k]).map(k =\u003e `${k}=${params[k]}`).join(\"\u0026\");\nvar socket = cettia.open(\"/cettia?\" + query);\n\n// ## Socket Lifecycle\nvar logState = () =\u003e console.log(socket.state());\nsocket.on(\"connecting\", logState).on(\"open\", logState).on(\"close\", logState);\nsocket.on(\"waiting\", (delay, attempts) =\u003e console.log(socket.state(), delay, attempts));\n\n// ## Sending and Receiving Events\n[\"echo\", \"chat\", \"myself\", \"welcome\"].forEach(event =\u003e {\n  socket.on(event, data =\u003e console.log(event, data));\n});\nsocket.on(\"signout\", () =\u003e {\n  console.log(\"signout\", \"You've been signed out since you signed in on another device\");\n  // It prevents reconnection\n  socket.close();\n});\n\n// This open event handler registered through `once` is called at maximum once\nsocket.once(\"open\", () =\u003e {\n  // Sends an echo event to be returned\n  socket.send(\"echo\", \"Hello world\");\n  // Sends a chat event to be broadcast to every sockets in the server\n  socket.send(\"chat\", {text: \"I'm a text\"});\n  // Sends an event to sockets whose username is the same\n  // with composite data consisting of text data and binary data\n  socket.send(\"myself\", {text: \"I'm a text\", binary: new TextEncoder().encode(\"I'm a binary\")});\n});\n```\n\nThe full source code for the starter kit is available at the repository, https://github.com/cettia/cettia-starter-kit. If you want to dig deeper, read an introductory tutorial to Cettia, [Building Real-Time Web Applications With Cettia](https://cettia.io/guides/cettia-tutorial/). It explains the reason behind key design decisions that the Cettia team have made in the Cettia, as well as various patterns and features required to build real-time oriented applications without compromise with Cettia.\n\n---\n\nIf you run into issues or have questions or are interested and would like to be more involved, feel free to join the [mailing list](http://groups.google.com/group/cettia) and share your feedback. Also, follow [@cettia_project](https://twitter.com/cettia_project) or [@flowersits](https://twitter.com/flowersits) on Twitter for the latest news and updates.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcettia%2Fcettia-java-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcettia%2Fcettia-java-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcettia%2Fcettia-java-server/lists"}