Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cettia/cettia-java-server
A full-featured real-time web application framework for Java
https://github.com/cettia/cettia-java-server
cettia comet framework-agnostic java long-polling real-time server streaming web-fragment websocket
Last synced: 2 months ago
JSON representation
A full-featured real-time web application framework for Java
- Host: GitHub
- URL: https://github.com/cettia/cettia-java-server
- Owner: cettia
- License: apache-2.0
- Created: 2015-03-18T08:33:37.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2018-11-05T11:26:51.000Z (about 6 years ago)
- Last Synced: 2024-04-16T06:15:57.491Z (9 months ago)
- Topics: cettia, comet, framework-agnostic, java, long-polling, real-time, server, streaming, web-fragment, websocket
- Language: Java
- Homepage: https://cettia.io
- Size: 151 KB
- Stars: 41
- Watchers: 5
- Forks: 3
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Cettia
Cettia 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:*
- Avoiding repetitive boilerplate code
- Supporting environments where WebSocket is not available
- Handling both text and binary data together
- Recovering missed events
- Providing multi-device user experience
- Scaling out an application, and so on.It 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.
---
The 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 `##`.
Maven dependencies.
```xml
io.cettia
cettia-server
1.2.0io.cettia.asity
asity-bridge-servlet3
2.0.0io.cettia.asity
asity-bridge-jwa1
2.0.0```
A class to play with the Cettia server. Import statements, verbose try-catch blocks, empty methods, etc. are skipped for brevity.
```Java
@WebListener
public class CettiaConfigListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Cettia part
// If you don't want to form a cluster,
// replace the following line with 'Server server = new DefaultServer();'
ClusteredServer server = new ClusteredServer();
HttpTransportServer httpAction = new HttpTransportServer().ontransport(server);
WebSocketTransportServer wsAction = new WebSocketTransportServer().ontransport(server);// If a client opens a socket, the server creates and passes a socket to socket handlers
server.onsocket((ServerSocket socket) -> {
// ## Socket Lifecycle
Action logState = v -> System.out.println(socket + " " + socket.state());
socket.onopen(logState).onclose(logState).ondelete(logState);// ## Sending and Receiving Events
// An 'echo' event handler where any received echo event is sent back
socket.on("echo", data -> socket.send("echo", data));// ## Attributes and Tags
// Attributes and tags are contexts to store the socket state in the form of Map and Set
String username = findParam(socket.uri(), "username");
if (username == null) {
// Attaches a tag to the socket
socket.tag("nonmember");
} else {
// Associates an attribute with the the socket
socket.set("username", username);
}// ## Working with Sockets
// A 'chat' event handler to send a given chat event to every socket in every server in the cluster
socket.on("chat", data -> server.find(ServerSocketPredicates.all()).send("chat", data));// ## Advanced Sockets Handling
if (username != null) {
// A group of sockets representing the same user, whose username is the given one
Sentence user = server.find(ServerSocketPredicates.attr("username", username));
// A 'myself' event handler to send a given myself event to myself whose username is the same
socket.on("myself", data -> {
// You can directly handle each socket through the execute method of Sentence if needed
user.execute(s -> s.send("myself"));
});// Limits only one socket per user
boolean onlyOneSocket = Boolean.parseBoolean(findParam(socket.uri(), "onlyOneSocket"));
if (onlyOneSocket) {
// Finds sockets opened by whose username is the same other than this socket and
// sends a 'signout' event to prevent reconnection and closes a connection
user.find(id(socket).negate()).send("signout").close();
}
}// ## Disconnection Handling
Queue queue = new ConcurrentLinkedQueue<>();
// Caches events that fail to send due to disconnection
socket.oncache(args -> queue.offer(args));
// Sends cached events on the next connection
socket.onopen(v -> {
while (socket.state() == ServerSocket.State.OPENED && !queue.isEmpty()) {
Object[] args = queue.poll();
socket.send((String) args[0], args[1], (Action>) args[2], (Action>) args[3]);
}
});
// If the client fails to connect within 1 minute after disconnection,
// You may want to consider notifying the user of finally missed events, like push notifications
socket.ondelete(v -> queue.forEach(args -> {
System.out.println(socket + " missed event - name: " + args[0] + ", data: " + args[1]);
}));
});// ## Working with Sockets
// To deal with sockets, inject the server wherever you want
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// Sends a 'welcome' event to sockets representing user not signed in every 5 seconds
executor.scheduleAtFixedRate(() -> server.byTag("nonmember").send("welcome"), 0, 5, SECONDS);// ## Plugging Into the Web Framework
// Cettia is designed to run on any web framework seamlessly on the JVM
// Note how 'httpAction' and 'wsAction' are plugged into Servlet and Java API for Websocket
ServletContext context = event.getServletContext();
AsityServlet asityServlet = new AsityServlet().onhttp(/* ㅇㅅㅇ */ httpAction);
ServletRegistration.Dynamic reg = context.addServlet(AsityServlet.class.getName(), asityServlet);
reg.setAsyncSupported(true);
reg.addMapping("/cettia");ServerContainer container = (ServerContainer) context.getAttribute(ServerContainer.class.getName());
ServerEndpointConfig.Configurator configurator = new ServerEndpointConfig.Configurator() {
public T getEndpointInstance(Class endpointClass) {
AsityServerEndpoint asityServerEndpoint = new AsityServerEndpoint();
asityServerEndpoint.onwebsocket(/* ㅇㅅㅇ */ wsAction);
return endpointClass.cast(asityServerEndpoint);
}
};
container.addEndpoint(ServerEndpointConfig.Builder.create(AsityServerEndpoint.class, "/cettia")
.configurator(configurator).build());// ## Scaling a Cettia Application
// Any publish-subscribe messaging system can be used to scale a Cettia application horizontally,
// and it doesn’t require any modification in the existing application.
HazelcastInstance hazelcast = HazelcastInstanceFactory.newHazelcastInstance(new Config());
ITopic> topic = hazelcast.getTopic("cettia");
// It publishes messages given by the server
server.onpublish(message -> topic.publish(message));
// It relays published messages to the server
topic.addMessageListener(message -> server.messageAction().on(message.getMessageObject()));
}
}
```Here'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.
```java
@SpringBootApplication
@EnableWebFlux
public class CettiaServer {
@Bean
public RouterFunction httpMapping(HttpTransportServer httpAction) {
AsityHandlerFunction asityHandlerFunction = new AsityHandlerFunction();
asityHandlerFunction.onhttp(/* ㅇㅅㅇ */ httpAction);RequestPredicate isNotWebSocket = headers(h -> !"websocket".equalsIgnoreCase(h.asHttpHeaders().getUpgrade()));
return route(path("/cettia").and(isNotWebSocket), asityHandlerFunction);
}@Bean
public HandlerMapping wsMapping(WebSocketTransportServer wsAction) {
AsityWebSocketHandler asityWebSocketHandler = new AsityWebSocketHandler();
asityWebSocketHandler.onwebsocket(/* ㅇㅅㅇ */ wsAction);
Map map = new LinkedHashMap<>();
map.put("/cettia", asityWebSocketHandler);SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(map);return mapping;
}
}
```You 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.
```html
index
```Below 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.
```javascript
// ## Opening a Socket
// Manipulates the below params object to play with the server implementation
var params = {
username: "flowersinthesand",
onlyOneSocket: true
};
// Let's assume that each key and value are already encoding safe
var query = Object.keys(params).filter(k => params[k]).map(k => `${k}=${params[k]}`).join("&");
var socket = cettia.open("/cettia?" + query);// ## Socket Lifecycle
var logState = () => console.log(socket.state());
socket.on("connecting", logState).on("open", logState).on("close", logState);
socket.on("waiting", (delay, attempts) => console.log(socket.state(), delay, attempts));// ## Sending and Receiving Events
["echo", "chat", "myself", "welcome"].forEach(event => {
socket.on(event, data => console.log(event, data));
});
socket.on("signout", () => {
console.log("signout", "You've been signed out since you signed in on another device");
// It prevents reconnection
socket.close();
});// This open event handler registered through `once` is called at maximum once
socket.once("open", () => {
// Sends an echo event to be returned
socket.send("echo", "Hello world");
// Sends a chat event to be broadcast to every sockets in the server
socket.send("chat", {text: "I'm a text"});
// Sends an event to sockets whose username is the same
// with composite data consisting of text data and binary data
socket.send("myself", {text: "I'm a text", binary: new TextEncoder().encode("I'm a binary")});
});
```The 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.
---
If 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.