days
-7
0
hours
0
-5
minutes
-4
-8
seconds
-5
-3
search
Gives you all the tools to exploit the "Write Once, Run Anywhere" principle

Asity is here chirping *universally reusable web fragments on the JVM*

jvm
© Shutterstock /Artush  

Since summer is well upon us, how about taking a quick trip to exotic Madagascar to meet some sweet asity birds? Or, we can just build universally reusable web fragments on the JVM with Asity, a lightweight abstraction layer for various web frameworks on the Java Virtual Machine that makes the long-lived “Write Once, Run Anywhere” concept a fascinating reality!

Have you met Asity? It is probably the most Java-intelligent exotic bird you’ll ever meet!

By establishing an ecosystem of universally reusable web fragments that run across different frameworks in the Java ecosystem, Asity gives all the tools (and reasons) you need to achieve and exploit the “Write Once, Run Anywhere” principle that has been central to the Java ecosystem for ages!

According to the official web page, Asity provides abstractions for HTTP and WebSocket, and implementations per framework, called bridges, which are transparent to end-users and don’t affect the framework’s performance, productivity and philosophy.

SEE ALSO: Meet Jib: Containerizing a Java application has never been easier

Feeling intrigued? Wait until you hear the news!

Just hours ago, Asity 2.0.0 was officially released and I cannot wait to give you a quick tour of how working with reusable web fragments on the JVM looks like!

More “Write Once, Use Anywhere” things coming your way

In order to demonstrate how to build an echo fragment which simply responds to the client with whatever data the client sent, here is a comprehensive example of Asity 2.

Add the following dependencies:

<!-- To write a web fragment -->
<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-http</artifactId>
  <version>2.0.0</version>
</dependency>
<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-websocket</artifactId>
  <version>2.0.0</version>
</dependency>
<!-- To run a web fragment on Spring WebFlux 5 -->
<dependency>
  <groupId>io.cettia.asity</groupId>
  <artifactId>asity-bridge-spring-webflux5</artifactId>
  <version>2.0.0</version>
</dependency>

And the following class:

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.http.HttpStatus;
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.nio.ByteBuffer;
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);
  }

  public static 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.readAsText().onchunk((String chunk) -> http.write(chunk));
      // If request body is binary,
      // http.readAsBinary().onchunk((ByteBuffer binary) -> http.write(binary));

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

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

As you would expect, HttpEchoServer and WebSocketEchoServer are web fragments and can be reused in other frameworks through other bridges like asity-bridge-spring-webmvc4. You can also see that a bridge implementation is completely transparent to end-users who still have full control over web fragments on frameworks they selected.

Asity 2.0.0 now supports Java API for WebSocket 1, Servlet 3, Spring WebFlux 5, Spring MVC 4, Vert.x 3, Netty 4, Grizzly 2, Vert.x 2 and Atmosphere 2. You can find a list of working examples per supported frameworks here and the full documentation here.

agewuz

Author
Eirini-Eleni Papadopoulou
Eirini-Eleni Papadopoulou is an assistant editor for JAXenter.com. Just finished her masters in Modern East Asian Studies and plans to continue with her old hobby that is computer science.