on

HTTP/WebSocket abstraction layer for Java

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);
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.

Latest announcement
Asity 3.0.0 released on
Pre
WIP
Current
3.0.0

Write Once

At the code level, a web fragment is a set of Actions 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.

HTTP

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"));
  }
}

WebSocket

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());
  }
}

Run Anywhere

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.

Java API for WebSocket 1

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.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-jwa1.

Servlet 3

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.

Quirks

  • It works with Servlet 3.1 and 4 containers.
  • ServerHttpExchange's onclose isn't supported.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-servlet3.

Spring WebFlux 5

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.

Quirks

  • ServerHttpExchange's onclose isn't supported.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-spring-webflux5.

Spring MVC 4

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.

Quirks

  • ServerHttpExchange's onclose isn't supported.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-spring-webmvc4.

Vert.x 3

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.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-vertx3.

Netty 4

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.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-netty4.

Play Framework 2

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.

Quirks

  • It requires Play Framework 2.6 and above.
  • ServerHttpExchange's onclose isn't supported.
  • Play Framework doesn't support to read request body by chunk asynchronously.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-play2.

Grizzly 2

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.

Quirks

  • Grizzly 2.4 requires javax.servlet:javax.servlet-api:4.y.z.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-grizzly2.

Vert.x 2

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.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-vertx2.

Atmosphere 2

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.

Quirks

  • It requires Atmosphere 2.2 and above.
  • ServerHttpExchange's onclose isn't supported.
  • ServerWebSocket's headers doesn't support a header with multiple values.

Working Example

A full example is available at https://github.com/cettia/asity/tree/master/example-atmosphere2.

Showcase

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.

Cettia
A full-featured real-time web application framework that you can use to exchange events between server and client in real-time.
Jeasse Asity
One Server-Sent Events lib for Java to rule them all.

Get Involved

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.