Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Criei meu proprio sistema em Java para fazer Requests Http & File System

Eu já estou a ver conteúdos mais avançados sobre Java, mas gosto sempre de voltar um pouco e relembrar alguns conceitos do passado. Uma coisa que não gosto e que não gostei muito quando estava aprendendo sobre consumo de APIs Web em Java, é que ficávamos muito dependentes de várias classes espalhadas de forma confusa, como HttpClient, HttpRequest e HttpResponse. Isso incomodava um pouco, além de os métodos estarem soltos e termos que nos virar.

Foi daí que comecei a criar uma Lib Java para facilitar essas tarefas, centralizando as funções mais importantes. A ideia é eliminar o boilerplate e o código confuso, alem de aumentar a produtividade.

Essa lib possui diversas funções, dentre elas:

  • Http: Uma API fluente baseada no padrão Builder para fazer requisições HTTP (GET, POST, JSON, etc.) de forma simplificada. Ela cuida automaticamente de queries, form data, autenticação (Basic e Bearer), e ainda inclui retries automáticos em caso de falha de rede e logging detalhado. A resposta (ResponseWrapper) já tem métodos para verificar o status e deserializar o JSON diretamente para um objeto Java (.asJson(Class)).

  • Env: Simplifica o carregamento de variáveis de ambiente via arquivos .env (usando o padrão Dotenv). Isso garante que as credenciais e as configurações sensíveis permaneçam seguras e separadas do código.

  • Serializer: Centraliza a conversão de objetos Java para JSON e vice-versa, além de adicionar métodos fáceis para salvar e carregar objetos diretamente de arquivos (tanto JSON quanto binários/Base64), cuidando da persistência de forma muito mais limpa.

Exemplo

1. Env - Carregamento Simplificado de Variáveis de Ambiente

Carrega o arquivo .env automaticamente no diretório atual e obtém uma chave.

// 1. Carrega o .env
Env.builder().fastload();

// 2. Obtém a variável
String apiKey = Env.get("OMDB_API_KEY");

2. Http - Requisições Fluentes e Deserialização Direta

Faz a requisição GET e deserializa o JSON da resposta diretamente para um objeto Java.

TituloOmdb filme = Http.build()
        .GET(url)        // Envia GET para a URL
        .acceptJson() // Header: Accept: application/json
        .send()           // Executa a requisição
        .asJson(TituloOmdb.class); // Deserializa o corpo JSON

3. Serializer + File - Persistência de Objetos em JSON

Serializa uma lista de objetos Java para JSON e salva o resultado no caminho especificado em uma cadeia de chamadas.

File.at(MOVIES_FILE_PATH)
    .createDirectoriesIfNeeded() // Garante que a pasta exista
    .createIfNotExists()                  // Garante que o arquivo exista
    .write(Serializer.serialize(movies)); // Serializa e escreve o JSON

Matricule-se agora e aproveite até 50% OFF

O maior desconto do ano para você evoluir com a maior escola de tecnologia

QUERO APROVEITAR
6 respostas

Peguei o codigo de um outro aluno daqui e refatorei ele usando a minha Lib. O codigo default do aluno:

public class PrincipalComBusca {
    public static void main(String[] args) throws IOException, InterruptedException {
        Scanner leitura = new Scanner(System.in);
        String busca = "";

        List<Titulo> titulos = new ArrayList<>();

        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                .setPrettyPrinting()
                .create();

        while (!busca.equalsIgnoreCase("sair")) {

            System.out.println("Digite o filme para busca: ");
            busca = leitura.nextLine();

            if (busca.equalsIgnoreCase("sair")){
                break;
            }

            String chave = "Insira a chave OMDb aqui.";
            String endereco = "https://www.omdbapi.com/?t=" + busca.replace(" ", "+") + "&apikey=" + chave;
            System.out.println(endereco);

            try {

                HttpClient client = HttpClient.newHttpClient();
                HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create(endereco))
                        .build();

                HttpResponse<String> response = client
                        .send(request, HttpResponse.BodyHandlers.ofString());

                String json = response.body();
                System.out.println(json);

                TituloOmdb meuTituloOmdb = gson.fromJson(json, TituloOmdb.class);
                System.out.println(meuTituloOmdb);
                // try {
                Titulo meuTitulo = new Titulo(meuTituloOmdb);
                System.out.println("Título convertido: " + meuTitulo);


                titulos.add(meuTitulo);

            } catch (NumberFormatException e) {
                System.out.println("Aconteceu um erro: ");
                System.out.println(e.getMessage());
            } catch (IllegalArgumentException e) {
                System.out.println("Erro de argumento.");
            } catch (ErroDeConversaoDeAnoException e) {
                System.out.println(e.getMessage());

            }
        }
        System.out.println(titulos);

        FileWriter escrita = new FileWriter("filmes.json");
        escrita.write(gson.toJson(titulos));
        escrita.close();

        System.out.println("O programa foi executado corretamente!");

    }
}

E usando minha lib:

package test.alura;

import com.github.rickmvi.jtoolbox.console.util.Scan;
import com.github.rickmvi.jtoolbox.file.File;
import com.github.rickmvi.jtoolbox.util.Env;
import com.github.rickmvi.jtoolbox.util.http.Http;
import com.github.rickmvi.jtoolbox.util.serializable.Serializer;

import java.util.ArrayList;
import java.util.List;

import static com.github.rickmvi.jtoolbox.console.IO.println;

public class MovieFinder {
    private static final String EXIT_COMMAND = "sair";
    private static final String API_URL = "https://www.omdbapi.com/";
    private static final String MOVIES_FILE_PATH = "./src/main/resources/filmes.json";

    public static void main(String[] args) {
            Env.builder().fastload();
            String apiKey = Env.get("OMDB_API_KEY");

            List<Titulo> movieList = searchMovies(apiKey);
            saveMoviesToFile(movieList);
            println("O programa foi executado corretamente!");

    }

    private static List<Titulo> searchMovies(String apiKey) {
        List<Titulo> movies = new ArrayList<>();
        String searchTerm = "";

        while (!searchTerm.equalsIgnoreCase(EXIT_COMMAND)) {
            println();
            searchTerm = Scan.readPrompt("Digite o filme para busca:");

            if (searchTerm.equalsIgnoreCase(EXIT_COMMAND)) {
                break;
            }

            TituloOmdb movieData = fetchMovieData(searchTerm, apiKey);
            Titulo movie = new Titulo(movieData);
            println(movie);
            movies.add(movie);
        }

        return movies;
    }

    private static TituloOmdb fetchMovieData(String searchTerm, String apiKey) {
        String url = buildSearchUrl(searchTerm, apiKey);
        return Http.build()
                .GET(url)
                .acceptJson()
                .send()
                .asJson(TituloOmdb.class);
    }

    private static String buildSearchUrl(String searchTerm, String apiKey) {
        return API_URL + "?t=" + searchTerm.replace(" ", "+") + "&apikey=" + apiKey;
    }

    private static void saveMoviesToFile(List<Titulo> movies) {
        File.at(MOVIES_FILE_PATH)
                .createDirectoriesIfNeeded()
                .createIfNotExists()
                .write(Serializer.serialize(movies));
    }
}

imagem

Oi, Rick! Como vai?

Com base no que você explicou, a forma como você organizou Http, Env e Serializer chamou atenção pela clareza. A ideia de centralizar chamadas e remover boilerplate torna o fluxo mais limpo e previsível, algo que ajuda bastante no dia a dia com Java.

Uma dica interessante para o futuro é usar o metodo ofNullable do Optional para evitar verificacoes repetidas de null. Veja este exemplo:


Optional.ofNullable(apiKey)
        .ifPresent(valor -> System.out.println(valor));

Esse codigo evita if(apiKey != null) e deixa a intencao mais direta.

Alura Conte com o apoio da comunidade Alura na sua jornada. Abraços e bons estudos!

Olá Rick.
Tudo bem?
Cara você já está em outro nível.
Já deveria estar dando aulas se já não o faz.
Se tiver um canal no youtube eu gostaria muito de conhecer.
E obrigado por compartilhar suas soluções com a gente.
Bons estudos.

Eu tenho vontade de ter um canal para ensinar algumas coisas que tenho aprendido, tanto em Java quanto em Go, que geralmente não se ensinam em cursos ou formações. Talvez a timidez seja o meu elo mais fraco aqui kkkk. Dá até vontade de fazer um vídeo mostrando as praticidades desse meta-framework/lib que estou criando. Ela é tipo um Lombok: evita de você ter de escrever de mais. Você não precisa escrever muito para fazer alguma função, basta saber o que quer e chamar em um encadeamento de métodos.

Um exemplo de como ela aumenta a sua produtividade, supondo que voce quer criar e salvar um arquivo numa pasta, você poderia usar a API Nio 2 do java e fazer assim:

public class JavaStandardFileExample {
    public static void main(String[] args) {
        String dir = "logs_java_padrao";
        String file = "relatorio.txt";
        Path filePath = Paths.get(dir, file);
        String content = "Dados de log (Java Padrão)";

        try {
            Path parentDir = filePath.getParent();
            if (parentDir != null) {
                Files.createDirectories(parentDir);
            }

            Files.writeString(
                    filePath,
                    content,
                    StandardOpenOption.CREATE,
                    StandardOpenOption.TRUNCATE_EXISTING
            );

            System.out.println("Arquivo criado em: " + filePath.toAbsolutePath());

        } catch (IOException e) {
            System.err.println("Erro ao manipular o arquivo: " + e.getMessage());
        }
    }
}

O exemplo acima consome 31 linhas, alem de verboso e meio chato de entender, agora usando o sistema que criei para mexer com files:

public class JToolboxFileExample {
    public static void main(String[] args) {
        String dir = "logs_jtoolbox";
        String file = "relatorio.txt";
        String content = "Dados de log (JToolbox)";
        
        File.at(File.combine(dir, file).toString())
            .createDirectoriesIfNeeded() 
            .createIfNotExists()     
            .write(content); 

        IO.format("Arquivo criado em: {}", File.combine(dir, file).toAbsolutePath());
    }
}

Consome 18 linhas, nao que isso importe muito, o foco mesmo esta na agilidade/praticidade, com menos codigo, fiz exatamente a mesma coisa que o primeiro codigo mostrado.

Que é isso Rick!
Deixe essa timidez de lado!
De qualquer forma se tiver vergonha pode usar uma mascara ou nem aparecer pelo menos até se sentir confortavel.
Já até achei um titulo para o canal: O Programador Mascarado!
E com o conhecimento que tem vai fazer sucesso rapidinho.
Se quiser alguma dica ou precisar de ajuda comente ai.
Até...
Nem fez o canal e já tem um inscrito.
Continue compartilhando seu conhecimento.
Obrigado.

solução!

Fiz o que o Armano disse, so que ao inves de a pessoa se preocupar em passar optional direto no codigo dela, eu criei um metodo proprio no Env para retornar um Optional:

/**
     * Retrieves an {@link Optional} containing the value of the environment variable
     * specified by the provided key, or an empty {@link Optional} if the variable
     * is not found or the value is null.
     *
     * @param key the name of the environment variable to retrieve. Must not be null.
     * @return an {@link Optional} containing the variable value if it exists and is not null,
     *         otherwise an empty {@link Optional}.
     * @throws IllegalStateException if the environment configuration has not been loaded.
     * @throws NullPointerException if the provided key is null.
     */
    public static @NotNull Optional<String> optional(String key) {
        If.ThrowWhen(dotenv == null, () ->
                new IllegalStateException(FAILURE));
        return Optional.ofNullable(dotenv.get(Objects.requireNonNull(key)));
    }

Assim, ela tem uma maior seguranca contra NPEs. E so chamar Env.optional("VALUE").ifPresent(...); ou Env.optional("VALUE").OrElse(...);