quinta-feira, 12 de agosto de 2021

Consumindo API Rest com Spring Boot usando a classe WebClient

           Nesse artigo vou mostrar como fazer um cliente para uma API Rest com Spring 5 usando a classe WebClient que é parte do novo WebFlux Framework. Esse cliente vai consumir uma API de Livros. Aproveitando o post, já vou criar esse cliente em uma classe de teste usando JUnit para mostrar o teste de APIs com ele. Mas a forma de uso aqui não se limita a testes.

         

Como o foco é a construção de um cliente para consumir uma API, vou omitir o código da API, trazendo só a construção do cliente. Então vamos ao código:

 

import java.util.List;

import org.junit.jupiter.api.Assertions

import org.junit.jupiter.api.BeforeAll;

import org.junit.jupiter.api.Test;

import org.springframework.core.ParameterizedTypeReference;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpMethod;

import org.springframework.http.MediaType;

import org.springframework.web.reactive.function.client.WebClient;

import org.springframework.web.reactive.function.client.WebClient.Builder;

import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;

 

import br.com.socialbooks.model.Livro;

import reactor.core.publisher.Mono;

 

class LivroControllerTest {

 

       @BeforeAll

       static void inserirLivro() {

             // configura uma requisição

             Builder builder = WebClient.builder();

             builder.baseUrl("http://localhost:8080");

 

             Livro livro = new Livro("Aprendendo Spring boot");

 

             // prepara o cliente

             WebClient client = builder.build();

             ResponseSpec response = client.method(HttpMethod.POST).uri("/livros").bodyValue(livro).retrieve();

 

             // recebendo o recurso salvo

             Mono<Livro> monoLivro = response.bodyToMono(Livro.class);

             livro = monoLivro.block();

       }

 

       @Test

       void testGetListaLivros() {

             // configura uma requisição

             Builder builder = WebClient.builder();

             builder.baseUrl("http://localhost:8080");

             builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

 

             // prepara o client

             WebClient client = builder.build();

             ResponseSpec response = client.method(HttpMethod.GET).uri("/livros").retrieve();

 

             /*

              * recebe o Mono que executa de forma assíncrona. OBS: Para carregar uma lista

              * de objetos, é preciso usar o ParameterizedTypeReference.

              */

             Mono<List<Livro>> monoLivro = response.bodyToMono(new                  ParameterizedTypeReference<List<Livro>>() {

             });

 

             // como funciona de forma assincrona, usamos o block para forçar ele esperar a resposta.

             List<Livro> livros = monoLivro.block();

 

              Assert.assertTrue(livros.get(0).getTituloLivro().equals("Aprendendo Spring boot"));

 

       }

 

       @Test

       void testGetLivro() {

             // configura uma requisição

             Builder builder = WebClient.builder();

             builder.baseUrl("http://localhost:8080");

             builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

 

             // prepara o client

             WebClient client = builder.build();

             ResponseSpec response =             client.method(HttpMethod.GET).uri("/livros/1").retrieve();


             // recebe o Mono que executa de forma assíncrona

             Mono<Livro> monoLivro = response.bodyToMono(Livro.class);

 

             // como funciona de forma assincrona, usamos o block para forçar ele esperar 

             // a resposta.

             Livro livro = monoLivro.block();

             Assert.assertTrue(livro.getTituloLivro().equals("Aprendendo Spring boot"));

       }

}

 

Pra esse artigo é só.


Esse artigo foi baseado estudos feitos em:

quinta-feira, 10 de junho de 2021

Estrutura de diretórios Linux

      Nesse post trago apenas uma imagem que a Vannessa Moura postou no linkedin e mostra a estrutura de diretórios no Linux. Deixei aqui pra garantir ter sempre a mão quando precisar.




terça-feira, 1 de junho de 2021

Substituido Entidades em um projeto Java

Este post é um pouco diferente dos demais já que vou tratar mais de um passo-a-passo do que fazer ao invés de trazer código.

 

Não sei se você já passou pela necessidade de precisar trocar uma entidade por outra em todo o sistema, mas quando isso acontece o trabalho é enorme. 

 

Entidades são as classes javas que são mapeadas para o banco de dados. Normalmente, aplicando as boas práticas e padrões de projeto em um determinado sistema, temos uma entidade para uma tabela, claro que podemos ter VOs (ou DTOs como tbm são conhecidos), mas falo de uma classe que vai ser a responsável pelo objeto inserido no banco, atualizado, apagado, enfim, o bean persistente. Em um projeto que trabalhei, não sei porque fizeram isso (falta de um arquiteto ou diria mesmo de uma pessoa com boa noção de O.O.), essa regra básica não foi seguida, e tinhamos 3, 4 ou até 6 classes diferentes apontando para a mesma tabela, com basicamente as mesmas propriedades, mudava apenas uma ou outra, e que ainda tinham sua própria classe de negócio e seu próprio controller. Ou seja, duplicação (ou melhor, multiplacação) de código enorme, regras espalhadas, bagunça total.

Nesse cenário caótico, eu precisei juntar as várias cópias de duas entidades principais, uma que tinha 6 classes e outra que tinha 4, e deixar apenas uma de cada. Não foi feito a junção do controller nem das classes de negócio devido ao tamanho, complexidade e tempo, mas ter apenas um objeto trafegando entre elas facilitou bastante as coisas. E pra fazer isso nesse sistema que é grande, eu usei o seguinte passo a passo:

1.     Juntar todas as propriedades em uma unica entidade removendo as repetidas mas não apagar nesse momento as classes duplicadas.

 

2.     Para cada uma das classes que serão eliminadas faça o seguinte: use o Search do eclipse na opção File Search seguindo os padrões abaixo, ao retornar resultados, clique com botão direito na aba que abriu de mesmo nome da funcionalidade (Search) e escolha a opção substituir todos, e vá substituindo pela nova classe. 

 

1.     Marque a opção Case Sensitive pra todas as buscas

 

2.     Procure os imports e substitua pelo import da classe que vai ficar

 

3.     Procure o nome da classe com um parêntese no início – com ou sem espaço dependendo do padrão de codificação usado no projeto entre o nome e os parênteses. Ex: (Usuario  

 

4.     Procure o nome da classe com o parentese no final – com ou sem espaço dependendo do padrão de codificação usado no projeto entre o nome e os parênteses. Ex: Usuario( 

 

5.     Procure o nome da classe com entre parênteses – com ou sem espaço dependendo do padrão do projeto entre o nome e os parênteses. Ex: (Usuario)

 

6.    Procure o nome da classe entre os sinais de menor que ‘<’  e maior que ‘>’. Ex: <Usuario>

 

7.    Procure o nome da classe entre espaços. Ex: Usuario

 

8.    Procure o nome da classe seguido do .class. Ex: Usuario.class

 

Nesse ponto, podem haver alguns erros apontados na aba Problems do Eclipse, geralmente são alguns imports que são necessários corrigir na mão Pronto, agora apague a(s) classe(s) que não deseja mais.

Exemplo de UPDATE com SELECT em uma outra tabela

       Nesse post rápido eu trago o exemplo de um UPDATE com dados de um SELECT em outra tabela. Note que o where faz ligação entre a tabela do update e a tabela do FROM. Isso é importante para evitar que todas as linhas fiquem com dados repetidos.


update t_agendamento set id_paciente = paciente.id_paciente from t_agendamento agc inner join t_paciente paciente on agc.num_prontuario = paciente.num_prontuario where agc.id_agendamento = t_agendamento.id_agendamento

quarta-feira, 19 de maio de 2021

Threads - Exemplo de uso para tarefas pesadas: synchronized, wait, notify e mais...

      Olá, nesse post vou mostrar um estudo de caso de como separar uma tarefa pesada – como um processamento de dados que envolva várias entidades e regras de negócio – e dar algumas dicas de como trabalhar essa questão com threads.

 

      Primeiro vamos ao caso:

 

     Temos uma tarefa que consiste em processar um grande volume de dados, (poderia ser uma folha de pagamento, contas de serviços de clientes, etc). Essa atividade hoje é feita da seguinte forma:

 

1.    Em um laço for uma consulta paginada retorna de 1000 em 1000 registros usados no início do processo (poderia ser o id dos colaboradores no caso da folha de pagamento, dos clientes no caso das contas de clientes, etc).

2.    Para cada página, temos um outro laço for que percorre cada um dos 1000 registros e inicia o processo de consultar os demais dados necessários, realizar processamento (todos os descontos, acréscimos, multas, juros, etc...), e gravar o resultado.

3.    Vai para próxima página, busca mais 1000 registros e continua.

 

Veja o código (esse código não compila, é apenas um exemplo didático. Mais à frente o código que uso threads está OK, compila e roda perfeitamente):

 

/*representa a classe que da início ao nosso processo*/

public class SincronizacaoThreads {

 

    public static void main( String[] args ) {

 

        ClasseDeNegocio classeDeNegocio = new ClasseDeNegocio();

        classeDeNegocio.executaTarefaPesada();

    }

 

}

 

public class ClasseDeNegocio {

 

    public void executaTarefaPesada() {

 

        /*

         * Digamos que eu tivesse um grande processamento de dados muito pesado para fazer,

         * Nesse ponto eu prepararia uma consulta paginada para buscar os dados, 1k por pagina.

         * Esse primeiro for representa essa paginação, aqui simulo 3 páginas.

         */

        for ( int j = 0; j < 3; j++ ) {

 

            // aqui buscaríamos os 1000 registros no banco e preencheríamos nossa lista

            System.out.println( "Tarefa pesada rodada número " + j );

 

            // Aqui percorreríamos todos os 1000 registros

            for ( int i = 0; i < lista.lenght; i++ ) {

 

                /*

                 * executando todo processo pesado, item a item...

                 * chamando outras classes de negócio necessárias etc...

                 */

            }

        }

    }

}

 

Vamos otimizar esse processo usando threads e ver algumas questões importantes na construção de soluções usando essa abordagem. A imagem abaixo mostra um rascunho de como era e como ficou o código:

 


Agora vamos ao código. São suas classes apenas, você pode copiar e colocar o código para rodar na sua máquina para facilitar o entendimento (elas estão funcionando rsrsrs):

 

/*representa a classe que da início ao nosso processo*/

public class SincronizacaoThreads {

 

    public static void main( String[] args ) {

 

        ClasseDeNegocio classeDeNegocio = new ClasseDeNegocio();

        classeDeNegocio.executaTarefaPesada();

    }

 

}

 

public class ClasseDeNegocio {

 

    // esse contador serve para validar se posso mudar de página

    private int contadorThreadsFilhasAtivas = 0;

 

    public void executaTarefaPesada() {

 

        /*

         * Digamos que eu tivesse um grande processamento de dados muito pesado para fazer,

         * Nesse ponto eu prepararia uma consulta paginada para buscar os dados, 1k por página.

         * Esse primeiro for representa essa paginação, aqui simulo 3 páginas.

         */

        for ( int j = 0; j < 3; j++ ) {

 

            // mostro o múmero da pagina (0 a 2)

            System.out.println( "Tarefa pesada rodada número " + j );

 

            // Começando o trabalho - Dividindo a tarefa em 10 threads.

            for ( int i = 0; i < 10; i++ ) {

 

                // aqui eu criaria a sublista com 100 elementos

 

                // incremento o contador de threads filhas

                contadorThreadsFilhasAtivas++;

 

                new Thread( new Runnable() {

 

                    @Override

                    public void run() {

 

                        // aqui passaria a sublista com 100 elementos (1k / 10) para o processaDados

                        processaDados();

                    }

                }, "thread " + i ).start();

            }

 

            /*

             * só posso continuar a paginação e buscar mais 1k de dados após todas as 10 threads

             * auxiliares acaberem seu serviço, para evitar sobrecarga no sistema e no banco, já

             * estamos falando de uma tarefa bastante pesada de processamento de dados.

             */

            synchronized ( this ) {

                try {

                    System.out.println( "Esperando a execução das threads filhas para continuar" );

                    while ( contadorThreadsFilhasAtivas != 0 ) {

                        this.wait();

                    }

                } catch ( InterruptedException e ) {

                    e.printStackTrace();

                }

            }

 

            System.out.println( "Fim das threads filhas da página " + j + " e agora posso continuar" );

            System.out.println();

        }

    }

 

    public void processaDados() {

 

        String nome = Thread.currentThread().getName();

        System.out.println( nome + " entrando no processaDados" );

 

        try {

            /*

             * simula o tempo de execução do método.

             * o tempo dos sleep simula o processamento dos 100 dados que o método receberia

             * executando todo processo pesado, item a item...

             * chamando outras classes de negócio necessárias etc...

             */

            Thread.sleep( 9000 );

        } catch ( InterruptedException e ) {

            e.printStackTrace();

        }

 

        System.out.println( nome + " terminando execução do processaDados" );

        System.out.println( nome + " saindo do processaDados" );

 

        synchronized ( this ) {

            // decremento o contador de threads filhas antes de sair

            contadorThreadsFilhasAtivas--;

            this.notify();

        }

    }

}

 

Vamos analisar o código e algumas dicas.

 

A classe ClasseDeNegocio tem apenas 2 métodos: executaTarefaPesada() e processaDados(). Se fosse um código real, o método processaDados() receberia um lista com 100 objetos para ele processar (já que minha paginação pega 1.000 registros e divido em 10 threads no laço).

 

Antes de usarmos threads, o método executaTarefaPesada fazia tudo sozinho. Agora, ele apenas busca os dados e divide para as threads fazerem o trabalho. Você pode se perguntar: por que cada thread não busca seus próprios dados? Porque facilmente as threads poderiam buscar os mesmos dados e isso poderia ocasionar sérios problemas de duplicação de dados entre outras coisas.

 

Fizemos uso de wait() após disparamos as threads que vão fazer o trabalho pesado e deixamos ela em espera até todas as threads terminarem, e então seguimos para a próxima página. Esse controle é feito através da variável contadorThreadsFilhasAtivas que criamos na ClasseDeNegocio e começa com valor 0, porém a cada thread que lançamos esse valor é acrescido de 1, e no final do método processaDados() ela é decrescida em 1.

 

Logo que decrescemos o valor, usamos o notify() método processaDados() que serve para dizer a thread que deixamos parada com o wait() que ela pode continuar seu trabalho. Como o código que chama o wait() está dentro de um laço, ele vai executar o laço mais uma vez e checar se o valor da variável contadorThreadsFilhasAtivas já chegou a 0, caso contrário, ele executa outra chamada a wait() e entra em espera novamente. Isso ocorre até que todas as threads tenha terminado seu trabalho, e o código segue seu fluxo.

 

Quando usamos o wait(), notify() ou notifyAll(), nem sempre ele vai ser chamado a partir do “this”, mas pode ser de um parâmetro passado no método, etc. O ponto de aplicação do wait(), notify() ou notifyAll() é o objeto ou classe de negócio, enfim, aquilo que é de fato acessado de forma paralela. Ao dar um wait() em um objeto, faz-se necessário um notify() ou notifyAll() para o mesmo objeto, afim de que ele volte a execução, caso contrário ficará fadado a espera eterna. Wait(), notify() ou notifyAll() só podem ser usados dentro de um bloco synchronized.

 

Usamos também o synchronized. Ele pode ser usado tanto em blocos específicos de código, como foi o nosso caso, quanto na declaração do método. Qual a diferença? Bem, quando declaramos algo como sincronizado, isso significa que será permitido apenas um acesso por vez aquele trecho de código. Isso é muito ruim, pois cria gargalos... pra quê usar várias threads se eu deixar os métodos com 1 acesso por vez? Não faz sentido! Mas, as vezes é necessário. E isso vai depender de cada regra de negócio. No nosso caso, apenas o trecho que controla a variável contadorThreadsFilhasAtivas  teve essa necessidade.

 

Então, onde usar synchronized? Onde houver chance de duplicação de dados ou erro de calculo em variáveis compartilhadas entre threads como era o caso do nosso contadorThreadsFilhasAtivas ou coisas do tipo. Sincronizar o método ou um trecho? Sempre o menor possível, mantendo integridade e consistência para não perder muita performance.

 

O controle da execução de blocos sincronizados é válido para cada instância da classe. Ou seja, mesmo que anote um método com synchronized, se eu chama-lo a partir de duas instancias distintas, ele será executado ao mesmo tempo.

 

Acredito que para esse post era isso. Espero que tenha ajudado.