Asity is an HTTP/WebSocket abstraction layer for various web frameworks on the JVM. Asity provides a tool-kit for writing a web fragment, a function that handles HTTP request-response exchange and WebSocket, and allows to create web applications by combining web fragments.
For example, with Asity you can write a web fragment that sends back incoming WebSocket messages as follows
Action<ServerWebSocket> action = ws -> ws.ontext(ws::send).onbinary(ws::send);
And plug it into Java API for WebSocket, Spring WebFlux, Spring MVC, Vert.x, Netty, Play framework, Grizzly, Atmosphere, and so on.
At the code level, a web fragment is a set of Action
s to handle ServerHttpExchange
or ServerWebSocket
, which represents HTTP request-response exchange and WebSocket connection, respectively. These APIs are asynchronous and designed to conform the relevant RFC specifications, i.e. RFC2616 and RFC6455, from a pragmatic perspective.
Let's take a look at the APIs by building an echo fragment which simply respond to the client with whatever data the client sent. The data to exchange between server and client is a chunk of the Chunked transfer encoding in case of HTTP, and a text frame and a binary frame in case of WebSocket. Before getting started, be sure that you have Java 8+ installed.
Add a io.cettia.asity:asity-http:3.0.0
(Javadoc) as a dependency of your web fragment.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-http</artifactId>
<version>3.0.0</version>
</dependency>
Then write an Action
consuming ServerHttpExchange
.
package io.cettia.asity.example.echo;
import io.cettia.asity.action.Action;
import io.cettia.asity.http.HttpStatus;
import io.cettia.asity.http.ServerHttpExchange;
import java.nio.ByteBuffer;
public class HttpEchoServer implements Action<ServerHttpExchange> {
@Override
public void on(ServerHttpExchange http) {
// Reads request URI, method and headers
System.out.println(http.method() + " " + http.uri());
http.headerNames().stream().forEach(name -> System.out.println(name + ": " + String.join(", ", http.headers(name))));
// Writes response status code and headers
http.setStatus(HttpStatus.OK).setHeader("content-type", http.header("content-type"));
// Reads a chunk from request body and writes it to response body
http.onchunk((ByteBuffer binary) -> http.write(binary)).readAsBinary();
// If request body is supposed to be text,
// http.onchunk((String chunk) -> http.write(chunk)).readAsText();
// Ends response if request ends
http.onend((Void v) -> http.end());
// Exception handling
http.onerror((Throwable t) -> t.printStackTrace()).onclose((Void v) -> System.out.println("disconnected"));
}
}
Add a io.cettia.asity:asity-websocket:3.0.0
(Javadoc) as a dependency of your web fragment.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-websocket</artifactId>
<version>3.0.0</version>
</dependency>
Then write an Action
consuming ServerWebSocket
.
package io.cettia.asity.example.echo;
import io.cettia.asity.action.Action;
import io.cettia.asity.websocket.ServerWebSocket;
import java.nio.ByteBuffer;
public class WebSocketEchoServer implements Action<ServerWebSocket> {
@Override
public void on(ServerWebSocket ws) {
// Reads handshake request URI and headers
System.out.println(ws.uri());
ws.headerNames().stream().forEach(name -> System.out.println(name + ": " + String.join(", ", ws.headers(name))));
// Sends the received text frame and binary frame back
ws.ontext((String text) -> ws.send(text)).onbinary((ByteBuffer binary) -> ws.send(binary));
// Exception handling
ws.onerror((Throwable t) -> t.printStackTrace());
}
}
To run a web fragment on your framework, you need to plug the web fragment in to the framework through a dedicated bridge for the framework. A bridge is a minimal implementation; just enough to convert a framework's HTTP request-response exchange and WebSocket connection to the Asity's ServerHttpExchange
and ServerWebSocket
, and feed web fragments with them. Note that even if your favorite framework is not supported now, with about 200 lines of code, you can write a bridge to your framework.
Let's map the echo fragment above to the path /echo
.
Add a io.cettia.asity:asity-bridge-jwa1:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-jwa1</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityServerEndpoint
as a WebSocket server endpoint in a server container. When registering the server endpoint, you should put a handshake request instance into a map returned by ServerEndpointConfig#getUserProperties
with the javax.websocket.server.HandshakeRequest
key by overriding a Configurator#modifyHandshake
method.
package io.cettia.asity.example.jwa1;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.jwa1.AsityServerEndpoint;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.websocket.ServerWebSocket;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.websocket.DeploymentException;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
@WebListener
public class EchoServerInitializer implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// Web fragments
Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
ServletContext context = event.getServletContext();
ServerContainer container = (ServerContainer) context.getAttribute(ServerContainer.class.getName());
ServerEndpointConfig.Configurator configurator = new ServerEndpointConfig.Configurator() {
@Override
public <T> T getEndpointInstance(Class<T> endpointClass) {
AsityServerEndpoint asityServerEndpoint = new AsityServerEndpoint().onwebsocket(wsAction);
return endpointClass.cast(asityServerEndpoint);
}
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
config.getUserProperties().put(HandshakeRequest.class.getName(), request);
}
};
try {
container.addEndpoint(ServerEndpointConfig.Builder.create(AsityServerEndpoint.class, "/echo").configurator(configurator).build());
} catch (DeploymentException e) {
throw new RuntimeException(e);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {}
}
Now you can handle WebSocket connections from /echo
asynchronously through a AsityServerEndpoint
's onwebsocket
method.
A full example is available at https://github.com/cettia/asity/tree/master/example-jwa1.
Add io.cettia.asity:asity-bridge-servlet3:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-servlet3</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityServlet
as a servlet in a servlet context. When registering the servlet, you should set asyncSupported
to true
.
package io.cettia.asity.example.servlet3;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.servlet3.AsityServlet;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;
@WebListener
public class EchoServerInitializer implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// Web fragments
Action<ServerHttpExchange> httpAction = new HttpEchoServer();
ServletContext context = event.getServletContext();
Servlet servlet = new AsityServlet().onhttp(httpAction);
ServletRegistration.Dynamic reg = context.addServlet(AsityServlet.class.getName(), servlet);
reg.setAsyncSupported(true);
reg.addMapping("/echo");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {}
}
Now you can handle HTTP requests from /echo
asynchronously through a AsityServlet
's onhttp
method.
ServerHttpExchange
's onclose
isn't supported.A full example is available at https://github.com/cettia/asity/tree/master/example-servlet3.
Add a io.cettia.asity:asity-bridge-spring-webflux5:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-spring-webflux5</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityHandlerFunction
as a handler function and a AsityWebSocketHandler
as a WebSocket handler.
package io.cettia.asity.example.spring.webflux5;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.spring.webflux5.AsityHandlerFunction;
import io.cettia.asity.bridge.spring.webflux5.AsityWebSocketHandler;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.springframework.web.reactive.function.server.RequestPredicates.headers;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
@SpringBootApplication
@EnableWebFlux
public class EchoServer {
@Bean
public Action<ServerHttpExchange> httpAction() {
return new HttpEchoServer();
}
@Bean
public Action<ServerWebSocket> wsAction() {
return new WebSocketEchoServer();
}
@Bean
public RouterFunction<ServerResponse> httpMapping() {
AsityHandlerFunction asityHandlerFunction = new AsityHandlerFunction().onhttp(httpAction());
return RouterFunctions.route(
path("/echo")
// Excludes WebSocket handshake requests
.and(headers(headers -> !"websocket".equalsIgnoreCase(headers.asHttpHeaders().getUpgrade()))), asityHandlerFunction);
}
@Bean
public HandlerMapping wsMapping() {
AsityWebSocketHandler asityWebSocketHandler = new AsityWebSocketHandler().onwebsocket(wsAction());
Map<String, WebSocketHandler> map = new LinkedHashMap<>();
map.put("/echo", asityWebSocketHandler);
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(map);
return mapping;
}
@Bean
public WebSocketHandlerAdapter webSocketHandlerAdapter() {
return new WebSocketHandlerAdapter();
}
public static void main(String[] args) {
SpringApplication.run(EchoServer.class, args);
}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityHandlerFunction
's onhttp
method and a AsityWebSocketHandler
's onwebsocket
method.
ServerHttpExchange
's onclose
isn't supported.A full example is available at https://github.com/cettia/asity/tree/master/example-spring-webflux5.
Add a io.cettia.asity:asity-bridge-spring-webmvc4:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-spring-webmvc4</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityController
as a controller bean and a AsityWebSocketHandler
as a WebSocket handler bean.
package io.cettia.asity.example.spring.webmvc4;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.spring.webmvc4.AsityController;
import io.cettia.asity.bridge.spring.webmvc4.AsityWebSocketHandler;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.servlet.http.HttpServletRequest;
@SpringBootApplication
@EnableWebMvc
@EnableWebSocket
public class EchoServer implements WebSocketConfigurer {
@Bean
public Action<ServerHttpExchange> httpAction() {
return new HttpEchoServer();
}
@Bean
public Action<ServerWebSocket> wsAction() {
return new WebSocketEchoServer();
}
@Bean
public HandlerMapping httpMapping() {
AsityController asityController = new AsityController().onhttp(httpAction());
AbstractHandlerMapping mapping = new AbstractHandlerMapping() {
@Override
protected Object getHandlerInternal(HttpServletRequest request) {
// Check whether a path equals '/echo'
return "/echo".equals(request.getRequestURI()) &&
// Delegates WebSocket handshake requests to a webSocketHandler bean
!"websocket".equalsIgnoreCase(request.getHeader("upgrade")) ? asityController : null;
}
};
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
return mapping;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
AsityWebSocketHandler asityWebSocketHandler = new AsityWebSocketHandler().onwebsocket(wsAction());
registry.addHandler(asityWebSocketHandler, "/echo");
}
public static void main(String[] args) {
SpringApplication.run(EchoServer.class);
}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityController
's onhttp
method and a AsityWebSocketHandler
's onwebsocket
method.
ServerHttpExchange
's onclose
isn't supported.A full example is available at https://github.com/cettia/asity/tree/master/example-spring-webmvc4.
Add a io.cettia.asity:asity-bridge-vertx3:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-vertx3</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityRequestHandler
as a HTTP request handler and a AsityWebSocketHandler
as a WebSocket connection handler in a http server.
package io.cettia.asity.example.vertx3;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.vertx3.AsityRequestHandler;
import io.cettia.asity.bridge.vertx3.AsityWebSocketHandler;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpServer;
public class EchoServerVerticle extends AbstractVerticle {
@Override
public void start() {
// Web fragments
Action<ServerHttpExchange> httpAction = new HttpEchoServer();
Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
HttpServer httpServer = vertx.createHttpServer();
AsityRequestHandler asityRequestHandler = new AsityRequestHandler().onhttp(httpAction);
httpServer.requestHandler(request -> {
if (request.path().equals("/echo")) {
asityRequestHandler.handle(request);
}
});
AsityWebSocketHandler asityWebsocketHandler = new AsityWebSocketHandler().onwebsocket(wsAction);
httpServer.websocketHandler(socket -> {
if (socket.path().equals("/echo")) {
asityWebsocketHandler.handle(socket);
}
});
httpServer.listen(8080);
}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityRequestHandler
's onhttp
method and a AsityWebSocketHandler
's onwebsocket
method.
A full example is available at https://github.com/cettia/asity/tree/master/example-vertx3.
Add a io.cettia.asity:asity-bridge-netty4:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-netty4</artifactId>
<version>3.0.0</version>
</dependency>
Then add a AsityServerCodec
as a channel handler to a channel pipeline. When configuring the handler, you should add HttpServerCodec
in front of the handler.
package io.cettia.asity.example.netty4;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.netty4.AsityServerCodec;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpServerCodec;
import java.net.URI;
public class EchoServer {
public static void main(String[] args) throws Exception {
// Web fragments
Action<ServerHttpExchange> httpAction = new HttpEchoServer();
Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
AsityServerCodec codec = new AsityServerCodec() {
@Override
protected boolean accept(HttpRequest req) {
return URI.create(req.uri()).getPath().equals("/echo");
}
};
codec.onhttp(httpAction).onwebsocket(wsAction);
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec()).addLast(codec);
}
});
Channel channel = bootstrap.bind(8080).sync().channel();
channel.closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityServerCodec
's onhttp
and onwebsocket
methods.
A full example is available at https://github.com/cettia/asity/tree/master/example-netty4.
Add a io.cettia.asity:asity-bridge-play2:3.0.0
(Javadoc) as a dependency of your application.
libraryDependencies += "io.cettia.asity" % "asity-bridge-play2" % "3.0.0"
Then write an action that returns AsityHttpAction
's CompletionStage<Result>
as a HTTP request handler and an action that returns AsityWebSocket
as a WebSocket handler, and add them to the routes file.
package io.cettia.asity.example.play2;
import akka.actor.ActorSystem;
import akka.stream.Materializer;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.play2.AsityHttpAction;
import io.cettia.asity.bridge.play2.AsityWebSocket;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import play.mvc.BodyParser;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
import play.mvc.WebSocket;
import javax.inject.Inject;
import java.util.concurrent.CompletionStage;
public class EchoController extends Controller {
// Web fragments
private final Action<ServerHttpExchange> httpAction = new HttpEchoServer();
private final Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
private final ActorSystem actorSystem;
private final Materializer materializer;
@Inject
public EchoController(ActorSystem actorSystem, Materializer materializer) {
this.actorSystem = actorSystem;
this.materializer = materializer;
}
@BodyParser.Of(BodyParser.Raw.class)
public CompletionStage<Result> http(Http.Request request) {
AsityHttpAction action = new AsityHttpAction();
action.onhttp(httpAction);
return action.apply(request);
}
public WebSocket websocket() {
AsityWebSocket webSocket = new AsityWebSocket(actorSystem, materializer);
webSocket.onwebsocket(wsAction);
return webSocket;
}
}
GET /echo io.cettia.asity.example.play2.EchoController.websocket
POST /echo io.cettia.asity.example.play2.EchoController.http(request: Request)
Now you can handle POST HTTP requests and WebSocket connections from /echo
asynchronously through a AsityHttpAction
's onhttp
and a AsityWebSocket
's onwebsocket
methods. Add more routes to handle requests using other HTTP methods with AsityHttpAction
.
ServerHttpExchange
's onclose isn't supported.A full example is available at https://github.com/cettia/asity/tree/master/example-play2.
Add a io.cettia.asity:asity-bridge-grizzly2:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-grizzly2</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityHttpHandler
as a HTTP request handler in a server configuration and a AsityWebSocketApplication
as a WebSocket application in a WebSocket engine.
package io.cettia.asity.example.grizzly2;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.grizzly2.AsityHttpHandler;
import io.cettia.asity.bridge.grizzly2.AsityWebSocketApplication;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.http.server.ServerConfiguration;
import org.glassfish.grizzly.websockets.WebSocketAddOn;
import org.glassfish.grizzly.websockets.WebSocketEngine;
public class EchoServer {
public static void main(String[] args) throws Exception {
// Web fragments
Action<ServerHttpExchange> httpAction = new HttpEchoServer();
Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
HttpServer httpServer = HttpServer.createSimpleServer();
ServerConfiguration config = httpServer.getServerConfiguration();
config.addHttpHandler(new AsityHttpHandler().onhttp(httpAction), "/echo");
NetworkListener listener = httpServer.getListener("grizzly");
listener.registerAddOn(new WebSocketAddOn());
WebSocketEngine.getEngine().register("", "/echo", new AsityWebSocketApplication().onwebsocket(wsAction));
httpServer.start();
System.in.read();
}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityHttpHandler
's onhttp
method and a AsityWebSocketApplication
's onwebsocket
method.
javax.servlet:javax.servlet-api:4.y.z
.A full example is available at https://github.com/cettia/asity/tree/master/example-grizzly2.
Add a io.cettia.asity:asity-bridge-vertx2:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-vertx2</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityRequestHandler
as a HTTP request handler and a AsityWebSocketHandler
as a WebSocket connection handler in a http server.
package io.cettia.asity.example.vertx2;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.vertx2.AsityRequestHandler;
import io.cettia.asity.bridge.vertx2.AsityWebSocketHandler;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import org.vertx.java.core.http.HttpServer;
import org.vertx.java.core.http.RouteMatcher;
import org.vertx.java.platform.Verticle;
public class EchoServerVerticle extends Verticle {
@Override
public void start() {
// Web fragments
Action<ServerHttpExchange> httpAction = new HttpEchoServer();
Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
HttpServer httpServer = vertx.createHttpServer();
RouteMatcher httpMatcher = new RouteMatcher();
httpMatcher.all("/echo", new AsityRequestHandler().onhttp(httpAction));
httpServer.requestHandler(httpMatcher);
AsityWebSocketHandler websocketHandler = new AsityWebSocketHandler().onwebsocket(wsAction);
httpServer.websocketHandler(socket -> {
if (socket.path().equals("/echo")) {
websocketHandler.handle(socket);
}
});
httpServer.listen(8080);
}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityRequestHandler
's onhttp
method and a AsityWebSocketHandler
's onwebsocket
method.
A full example is available at https://github.com/cettia/asity/tree/master/example-vertx2.
Add a io.cettia.asity:asity-bridge-atmosphere2:3.0.0
(Javadoc) as a dependency of your application.
<dependency>
<groupId>io.cettia.asity</groupId>
<artifactId>asity-bridge-atmosphere2</artifactId>
<version>3.0.0</version>
</dependency>
Then register a AsityAtmosphereServlet
as a servlet in a servlet context. When registering the servlet, you should set asyncSupported
to true
and an init parameter org.atmosphere.cpr.AtmosphereInterceptor.disableDefaults
to true
.
package io.cettia.asity.example.atmosphere2;
import io.cettia.asity.action.Action;
import io.cettia.asity.bridge.atmosphere2.AsityAtmosphereServlet;
import io.cettia.asity.example.echo.HttpEchoServer;
import io.cettia.asity.example.echo.WebSocketEchoServer;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import org.atmosphere.cpr.ApplicationConfig;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;
@WebListener
public class EchoServerInitializer implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// Web fragments
Action<ServerHttpExchange> httpAction = new HttpEchoServer();
Action<ServerWebSocket> wsAction = new WebSocketEchoServer();
ServletContext context = event.getServletContext();
Servlet servlet = new AsityAtmosphereServlet().onhttp(httpAction).onwebsocket(wsAction);
ServletRegistration.Dynamic reg = context.addServlet(AsityAtmosphereServlet.class.getName(), servlet);
reg.setAsyncSupported(true);
reg.setInitParameter(ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTOR, Boolean.TRUE.toString());
reg.addMapping("/echo");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {}
}
Now you can handle HTTP requests and WebSocket connections from /echo
asynchronously through a AsityAtmosphereServlet
's onhttp
and onwebsocket
methods.
ServerHttpExchange
's onclose
isn't supported.ServerWebSocket
's headers
doesn't support a header with multiple values.A full example is available at https://github.com/cettia/asity/tree/master/example-atmosphere2.
A web fragment is not just limited to echo. Check out an interview with the creator of Asity to learn more about use cases for Asity. You can build a framework built on top of HTTP and WebSocket, a pure server-side Java implementation, a middleware layer for Java, etc. The followings are some of the projects to demonstrate the real world use case of Asity.
What if a myriad of web fragments compose Asity’s ecosystem, and accordingly the future of Java web development? Asity is an open source project licensed under Apache License 2.0 and driven by the community, for the community. If you are interested and would like to be more involved, feel free to join the community and share your feedback.