Construindo microsserviços com o Eclipse MicroProfile

Construindo microsserviços com o Eclipse MicroProfile

1. Visão geral

Neste artigo, vamos nos concentrar na construção de um microsserviço baseado no Eclipse MicroProfile.

Veremos como escrever um aplicativo da web RESTful usando APIs JAX-RS, CDI e JSON-P.

2. Uma arquitetura de microsserviço

Simplificando, os microsserviços são um estilo de arquitetura de software que forma um sistema completo como uma coleção de vários serviços independentes.

Cada um se concentra em um perímetro funcional e se comunica com os outros com um protocolo independente de idioma, como o REST.

3. Eclipse MicroProfile

O Eclipse MicroProfile é uma iniciativa que visa otimizar o Enterprise Java para a arquitetura de microsserviços. É baseado em um subconjunto de APIs Java EE WebProfile, para que possamos construir aplicativos MicroProfile como construímos Java EE.

O objetivo do MicroProfile é definir APIs padrão para a construção de microsserviços e fornecer aplicativos portáteis em vários tempos de execução do MicroProfile.

4. Dependências do Maven

Todas as dependências necessárias para construir um aplicativo Eclipse MicroProfile são fornecidas por esta dependência da BOM (Lista de materiais):


    org.eclipse.microprofile
    microprofile
    1.2
    pom
    provided

O escopo é definido comoprovided porque o tempo de execução do MicroProfile já inclui a API e a implementação.

5. Modelo de Representação

Vamos começar criando uma classe de recursos rápida:

public class Book {
    private String id;
    private String name;
    private String author;
    private Integer pages;
    // ...
}

Como podemos ver, não há nenhuma anotação nesta classeBook.

6. Usando CDI

Simplificando, o CDI é uma API que fornece injeção de dependência e gerenciamento do ciclo de vida. Simplifica o uso de beans corporativos em aplicativos da Web.

Vamos agora criar um bean gerenciado CDI como uma loja para a representação do livro:

@ApplicationScoped
public class BookManager {

    private ConcurrentMap inMemoryStore
      = new ConcurrentHashMap<>();

    public String add(Book book) {
        // ...
    }

    public Book get(String id) {
        // ...
    }

    public List getAll() {
        // ...
    }
}

Anotamos essa classe com@ApplicationScoped porque precisamos apenas de uma instância cujo estado é compartilhado por todos os clientes. Para isso, usamosConcurrentMap como um armazenamento de dados na memória seguro para tipos. Em seguida, adicionamos métodos para as operaçõesCRUD.

Agora nosso bean está pronto para CDI e pode ser injetado no beanBookEndpoint using a anotação@Inject.

7. API JAX-RS

Para criar um aplicativo REST com JAX-RS, precisamos criar uma classeApplication anotada com@ApplicationPathe um recurso anotado com@Path.

7.1. Aplicativo JAX RS

O Aplicativo JAX-RS identifica o URI base sob o qual expomos o recurso em um Aplicativo da Web.

Vamos criar o seguinte aplicativo JAX-RS:

@ApplicationPath("/library")
public class LibraryApplication extends Application {
}

Neste exemplo, todas as classes de recursos JAX-RS no aplicativo da Web estão associadas aoLibraryApplication tornando-as no mesmo caminholibrary, que é o valor deApplicationPath annotation.

Essa classe anotada informa ao tempo de execução do JAX RS que ele deve localizar recursos automaticamente e expô-los.

7.2. Terminal JAX RS

Uma classeEndpoint, também chamada de classeResource, deve definir um recurso, embora muitos dos mesmos tipos sejam tecnicamente possíveis.

Cada classe Java anotada com@Path ou tendo pelo menos um método anotado com@Path or @HttpMethod é um Endpoint.

Agora, vamos criar um Endpoint JAX-RS que expõe essa representação:

@Path("books")
@RequestScoped
public class BookEndpoint {

    @Inject
    private BookManager bookManager;

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getBook(@PathParam("id") String id) {
        return Response.ok(bookManager.get(id)).build();
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAllBooks() {
        return Response.ok(bookManager.getAll()).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response add(Book book) {
        String bookId = bookManager.add(book);
        return Response.created(
          UriBuilder.fromResource(this.getClass())
            .path(bookId).build())
            .build();
    }
}

Nesse ponto, podemos acessar o RecursoBookEndpoint no caminho/library/books no aplicativo da web.

7.3. Tipo de mídia JAX RS JSON

O JAX RS suporta muitos tipos de mídia para comunicação com clientes REST, masEclipse MicroProfile restricts the use of JSON, pois especifica o uso da API JSOP-P. Como tal, precisamos anotar nossos métodos com@Consumes(MediaType.APPLICATION_JSON)e @Produces(MediaType.APPLICATION_JSON).

A anotação@Consumes restringe os formatos aceitos - neste exemplo, apenas o formato de dados JSON é aceito. O cabeçalho da solicitação HTTPContent-Type deve serapplication/json.

A mesma ideia está por trás da anotação@Produces. O JAX RS Runtime deve empacotar a resposta para o formato JSON. O cabeçalho HTTP da solicitaçãoAccept deve serapplication/json.

8. JSON-P

O JAX RS Runtime oferece suporte a JSON-P pronto para uso, para que possamos usarJsonObject como um parâmetro de entrada do método ou tipo de retorno.

Mas no mundo real, muitas vezes trabalhamos com aulas do POJO. Portanto, precisamos de uma maneira de fazer o mapeamento entreJsonObjecte POJO. É aqui que o provedor de entidade JAX RS entra em ação.

Para empacotar o fluxo de entrada JSON para o POJOBook, que invoca um método de recurso com um parâmetro do tipoBook,, precisamos criar uma classeBookMessageBodyReader:

@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class BookMessageBodyReader implements MessageBodyReader {

    @Override
    public boolean isReadable(
      Class type, Type genericType,
      Annotation[] annotations,
      MediaType mediaType) {

        return type.equals(Book.class);
    }

    @Override
    public Book readFrom(
      Class type, Type genericType,
      Annotation[] annotations,
      MediaType mediaType,
      MultivaluedMap httpHeaders,
      InputStream entityStream) throws IOException, WebApplicationException {

        return BookMapper.map(entityStream);
    }
}

Fazemos o mesmo processo para desempacotar umBook para o fluxo de saída JSON, que invoca um método de recurso cujo tipo de retorno éBook, criando umBookMessageBodyWriter:

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class BookMessageBodyWriter
  implements MessageBodyWriter {

    @Override
    public boolean isWriteable(
      Class type, Type genericType,
      Annotation[] annotations,
      MediaType mediaType) {

        return type.equals(Book.class);
    }

    // ...

    @Override
    public void writeTo(
      Book book, Class type,
      Type genericType,
      Annotation[] annotations,
      MediaType mediaType,
      MultivaluedMap httpHeaders,
      OutputStream entityStream) throws IOException, WebApplicationException {

        JsonWriter jsonWriter = Json.createWriter(entityStream);
        JsonObject jsonObject = BookMapper.map(book);
        jsonWriter.writeObject(jsonObject);
        jsonWriter.close();
    }
}

ComoBookMessageBodyReadereBookMessageBodyWriter são anotados com@Provider, eles são registrados automaticamente pelo tempo de execução JAX RS.

9. Criando e executando o aplicativo

Um aplicativo MicroProfile é portátil e deve ser executado em qualquer tempo de execução compatível com o MicroProfile. Explicaremos como construir e executar nosso aplicativo emOpen Liberty, mas podemos usar qualquer Eclipse MicroProfile compatível.

Configuramos o tempo de execução do Open Liberty por meio de um arquivo de configuraçãoserver.xml:


    
        jaxrs-2.0
        cdi-1.2
        jsonp-1.0
    
    
    
    

Vamos adicionar o pluginliberty-maven-plugin ao nosso pom.xml:



    net.wasdev.wlp.maven.plugins
    liberty-maven-plugin
    2.1.2
    
        
            io.openliberty
            openliberty-runtime
            17.0.0.4
            zip
        
        ${basedir}/src/main/liberty/config/server.xml
        ${package.file}
        ${packaging.type}
        false
        project
        
            /
            ${project.artifactId}-${project.version}.war
            9080
            9443
        
    
    
        
            install-server
            prepare-package
            
                install-server
                create-server
                install-feature
            
        
        
            package-server-with-apps
            package
            
                install-apps
                package-server
            
        
    

Este plugin é configurável através de um conjunto de propriedades:


    
    library
    ${project.build.directory}/${app.name}-service.jar
    runnable

O objetivo do executivo acima produz um arquivo jar executável para que nosso aplicativo seja um microsserviço independente que possa ser implantado e executado isoladamente. Também podemos implantá-lo como imagem do Docker.

Para criar um jar executável, execute o seguinte comando:

mvn package

E para executar nosso microsserviço, usamos este comando:

java -jar target/library-service.jar

Isso iniciará o tempo de execução do Open Liberty e implementará nosso serviço. Podemos acessar nosso endpoint e obter todos os livros neste URL:

curl http://localhost:9080/library/books

O resultado é um JSON:

[
  {
    "id": "0001-201802",
    "isbn": "1",
    "name": "Building Microservice With Eclipse MicroProfile",
    "author": "example",
    "pages": 420
  }
]

Para obter um único livro, solicitamos este URL:

curl http://localhost:9080/library/books/0001-201802

E o resultado é JSON:

{
    "id": "0001-201802",
    "isbn": "1",
    "name": "Building Microservice With Eclipse MicroProfile",
    "author": "example",
    "pages": 420
}

Agora vamos adicionar um novo livro interagindo com a API:

curl
  -H "Content-Type: application/json"
  -X POST
  -d '{"isbn": "22", "name": "Gradle in Action","author": "example","pages": 420}'
  http://localhost:9080/library/books

Como podemos ver, o status da resposta é 201, indicando que o livro foi criado com sucesso, eLocation é o URI pelo qual podemos acessá-lo:

< HTTP/1.1 201 Created
< Location: http://localhost:9080/library/books/0009-201802

10. Conclusão

Este artigo demonstrou como construir um microsserviço simples baseado no Eclipse MicroProfile, discutindo JAX RS, JSON-P e CDI.

O código está disponívelover on Github; este é um projeto baseado em Maven, portanto, deve ser simples de importar e executar como está.