on

Asity

Asity is a lightweight abstraction layer for various web frameworks on the Java Virtual Machine. It is designed to achieve and exploit "Write Once, Run Anywhere" by establishing an ecosystem of universally reusable web fragments* that run across different frameworks in the Java ecosystem. It provides abstractions for HTTP and WebSocket, and implementations per framework called bridges which are transparent to end-users and don't sacrifice the framework's performance, productivity and philosophy.

Web fragment authors can write a web fragment once and support almost all popular web frameworks in Java, and end-users can choose any technology stack they wish and use web fragments without being frustrated by compatibility issues.

* Web fragment represents a component that receives HTTP request-response or WebSocket connection like a controller in MVC but is able to be compatible with any web framework on the JVM.

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+ and Maven 3+ installed.

HTTP

Add a io.cettia.asity:asity-http:2.0.0 (Javadoc) as a dependency of your fragment.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-http</artifactId>
  <version>2.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.readAsBinary().onchunk((ByteBuffer binary) -> http.write(binary));
    // If request body is supposed to be text,
    // http.readAsText().onchunk((String chunk) -> http.write(chunk));

    // 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:2.0.0 (Javadoc) as a dependency of your fragment.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-websocket</artifactId>
  <version>2.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 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, 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:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-jwa1</artifactId>
  <version>2.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.

Add io.cettia.asity:asity-bridge-servlet3:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-servlet3</artifactId>
  <version>2.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.

Note

  • 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.

Add a io.cettia.asity:asity-bridge-spring-webflux5:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-spring-webflux5</artifactId>
  <version>2.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.

Note

  • ServerHttpExchange's onclose isn't supported.

Working Example

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:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-spring-webmvc4</artifactId>
  <version>2.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.

Note

  • ServerHttpExchange's onclose isn't supported.

Working Example

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

Add a io.cettia.asity:asity-bridge-vertx3:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-vertx3</artifactId>
  <version>2.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.

Add a io.cettia.asity:asity-bridge-netty4:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-netty4</artifactId>
  <version>2.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 asityServerCodec = new AsityServerCodec() {
              @Override
              protected boolean accept(HttpRequest req) {
                return URI.create(req.uri()).getPath().equals("/echo");
              }
            };
            asityServerCodec.onhttp(httpAction).onwebsocket(wsAction);

            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new HttpServerCodec()).addLast(asityServerCodec);
          }
        });
      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.

Add a io.cettia.asity:asity-bridge-grizzly2:2.0.0 (Javadoc)as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-grizzly2</artifactId>
  <version>2.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.

Note

  • 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.

Add a io.cettia.asity:asity-bridge-vertx2:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-vertx2</artifactId>
  <version>2.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.

Add a io.cettia.asity:asity-bridge-atmosphere2:2.0.0 (Javadoc) as a dependency of your application.

<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-atmosphere2</artifactId>
  <version>2.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.

Note

  • 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 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.

Get Involved

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.