My Items

I'm a title. ​Click here to edit me.

Introdução ao Apache Hive com Spark e Java

Introdução ao Apache Hive com Spark e Java

O Hive é um software de Data Warehouse que possibilita a leitura, escrita e o gerenciamento de dados distribuídos e permite a utilização de SQL em consultas estruturadas. Vamos utilizar o contexto do Spark para a configuração inicial, mas é possível fazer de outras formas sem a utilização do Spark. Maven <dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>2.4.5</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.12</artifactId>
<version>2.4.5</version>
</dependency> O primeiro passo é criar a configuração do contexto inicial: String dwDir = new File("warehouse-dir").getAbsolutePath();

SparkConf sparkConf = new SparkConf()
.set("spark.sql.warehouse.dir", wareHouseDir);

SparkSession sparkSession = SparkSession.builder()
.config(sparkConf)
.enableHiveSupport()
.master("local[1]")
.getOrCreate(); Entendendo as configurações acima: 1. Neste trecho a variável dwDir recebe o caminho da pasta warehouse-dir que será utilizada nas configurações do Spark. Até então essa pasta não foi criada. String dwDir = new File("warehouse-dir").getAbsolutePath(); 2. No próximo trecho é setado a parâmetro spark.sql.warehouse.dir no contexto do Spark com o caminho do diretório referenciado no primeiro trecho. Dessa forma o Spark usará este diretório como o repositório dos bancos que serão criados. SparkConf sparkConf = new SparkConf()
.set("spark.sql.warehouse.dir", wareHouseDir); 3. E por fim, a criação do SparkSession onde estão as configurações criadas anteriormente, o local onde o master será executado (localmente - local[1]) e a ativação do Hive. SparkSession sparkSession = SparkSession.builder()
.config(sparkConf)
.enableHiveSupport()
.master("local[1]")
.getOrCreate(); 4. Agora podemos executar algumas operações de DML e DDL 4.1. Criando um banco de dados sparkSession.sql("CREATE DATABASE IF NOT EXISTS hive_tutorial"); 4.2. Criando os Parquets Neste passo iremos criar uma tabela já apontando para o DataSource. Neste caso o DataSource será um Parquet. O Parquet é um arquivo de formato colunar que provê um melhor desempenho nas consultas. Para isso vamos criar um Parquet a partir de um JSON com seguinte o conteúdo: Arquivo data/pessoa.json {"id":1,"nome":"Joao","idade":12}
{"id":2,"nome":"Kelly","idade":21}
{"id":3,"nome":"Monica","idade":29}
{"id":4,"nome":"Laura","idade":32}
{"id":5,"nome":"Kiko","idade":23}
{"id":6,"nome":"Miguel","idade":55}
{"id":7,"nome":"Junior","idade":25}
{"id":8,"nome":"Luis","idade":36} Executando a leitura do arquivo para um DataFrame. Dataset<Row> df = sparkSession.read().json("data/pessoa.json"); Criando os arquivos Parquet com base no DataFrame dentro do diretório data/parquet do seu projeto df.write().parquet("data/parquet/") Veja que os arquivos foram criados Pronto, agora temos um Data Source criado. 4.3. Criando a Tabela Segue os passos: sparkSession.sql("USE hive_tutorial"); Após selecionar o banco HIVE_TUTORIAL. O comando CREATE TABLE possui alguns argumentos extras, segue: STORED AS PARQUET : É um argumento que o Hive utilizará para saber que tipo de arquivo será usado na conversão, neste caso o Parquet. LOCATION: Diretório do Data Source criado anteriormente. sparkSession.sql("CREATE TABLE IF NOT EXISTS pessoa " +
"(id BIGINT, nome STRING, idade BIGINT) " +
"STORED AS PARQUET " +
"LOCATION 'data/parquet/'"); É possível verificar a tabela criada executando o trecho abaixo: sparkSession.sql("SHOW TABLES").show(); Independente do fim da execução do programa, a tabela será mantida. Diferente de uma view criada com SparkSQL que é somente mantida em memória. 5. Exemplos de consultas Selecione o banco de dados sparkSession.sql("USE hive_tutorial"); Exemplo 1 sparkSession.sql("SELECT id, nome, idade " +
"FROM hive_tutorial.pessoa " +
"WHERE idade between 10 and 30 " +
"ORDER BY nome desc ").show(); Resultado Exemplo 2 sparkSession.sql("SELECT count(nome) " +
"FROM hive_tutorial.pessoa " +
"WHERE idade > 45 ").show(); Resultado 6. Exemplos de consultas mais complexas Agora vamos criar duas novas tabelas para explorar melhor os recursos do Hive. Crie o arquivo JSON data/produto.json {"id":1,"desc":"video game","preco":1800.0,"tipo":"eletronico"}
{"id":2,"desc":"geladeira","preco":1600.0,"tipo":"eletronico"}
{"id":3,"desc":"cama","preco":2000.0,"tipo":"quarto"}
{"id":4,"desc":"armário","preco":750.0,"tipo":"sala"}
{"id":5,"desc":"notebook","preco":4500.0,"tipo":"eletronico"}
{"id":6,"desc":"mesa","preco":2500.0,"tipo":"sala"}
{"id":7,"desc":"cadeira","preco":110.0,"tipo":"sala"}
{"id":8,"desc":"TV","preco":1500.0,"tipo":"eletronico"}
{"id":9,"desc":"fogão","preco":900.0,"tipo":"cozinha"} Crie os parquets para Produto Dataset<Row> dfP = sparkSession.read().json("data/produto.json");
dfProd.write().parquet("data/parquet/produto/"); Crie o arquivo JSON data/item.json {"id":1,"id_produto":2,"qtde":1}
{"id":2,"id_produto":1,"qtde":2}
{"id":3,"id_produto":3,"qtde":3}
{"id":4,"id_produto":4,"qtde":2}
{"id":5,"id_produto":5,"qtde":5} Crie os parquets para Item Dataset<Row> dfItem = sparkSession.read().json("data/item.json");
dfItem.write().parquet("data/parquet/item/"); Com base nos parquets criado, agora vamos criar a tabelas Produto e Item sparkSession.sql("USE hive_tutorial");

sparkSession.sql("CREATE TABLE IF NOT EXISTS produto " +
"(id BIGINT, desc STRING, " +
"preco BIGINT, " +
"tipo STRING) " +
"STORED AS PARQUET " +
"LOCATION 'data/parquet/produto'");

sparkSession.sql("CREATE TABLE IF NOT EXISTS item " +
"(id BIGINT, " +
"id_produto BIGINT, " +
"qtde BIGINT) " +
"STORED AS PARQUET " +
"LOCATION 'data/parquet/item/'"); Tabelas criadas sparkSession.sql("SHOW TABLES").show(); Consultas utilizando JOIN Exemplo 1 sparkSession.sql("SELECT prod.id, " +
"prod.desc, " +
"prod.preco, " +
"prod.tipo, " +
"item.qtde " +
"FROM produto prod inner join item item " +
"on (prod.id = item.id_produto) " +
"order by prod.id ").show(); Resultado Exemplo 2 sparkSession.sql(" SELECT " +
"prod.tipo, " +
"sum(item.qtde) " +
"FROM produto prod inner join item item " +
"on (prod.id = item.id_produto) " +
"group by prod.tipo").show(); Resultado Exemplo 3 sparkSession.sql(" SELECT " +
"prod.tipo, " +
"sum(item.qtde), " +
"sum(item.qtde * prod.preco) " +
"FROM produto prod inner join item item " +
"on (prod.id = item.id_produto) " +
"group by prod.tipo").show(); Resultado Pra finalizar, dê uma olhada na documentação oficial para mais detalhes: https://spark.apache.org/docs/latest/sql-data-sources-hive-tables.html https://hive.apache.org/ É isso, espero ter ajudado!

Lendo arquivo CSV com Apache Spark

Lendo arquivo CSV com Apache Spark

Apache Spark atua muito bem na leitura de diversos arquivos para extração de dados, nesse post vamos criar um exemplo de leitura de um arquivo CSV utilizando Spark, Java e Maven. Para quem não sabe o que é um CSV, é um arquivo texto que separa as colunas entre ponto e vírgula ( ; ). Maven <dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies> Conteúdo do CSV (Crie um arquivo chamado movies.csv com este conteúdo) title;year;rating
The Shawshank Redemption;1994;9.3
The Godfather;1972;9.2
The Dark Knight;2008;9.0
The Lord of the Rings: The Return of the King ;2003;8.9
Pulp Fiction;1994;8.9
Fight Club;1999;8.8
Star Wars: Episode V - The Empire Strikes Back;1980;8.7
Goodfellas;1990;8.7
Star Wars;1977;8.6 Criando SparkSession SparkConf sparkConf = new SparkConf();
sparkConf.setMaster("local[*]");
sparkConf.setAppName("app");

SparkSession sparkSession = SparkSession.builder()
.config(sparkConf)
.getOrCreate(); Executando a leitura Dataset<Row> ds = sparkSession.read()
.format("CSV")
.option("sep",";")
.option("inferSchema", "true")
.option("header", "true")
.load("movies.csv");

ds.select("title","year","rating").show(); Resultado Entendendo alguns parâmetros .option("sep", ";"): Define a utilização de um separador padrão para a leitura do arquivo, neste caso o separador é o ponto e vírgula (;) .option("inferSchema", "true"): O parâmetro inferSchema possibilita inferir o(s) arquivo(s) afim de entender (adivinhar) os tipos dos dados de cada campo .option("header", "true"): Habilitar o parâmetro header possibilita utilizar o nome de cada campo definido no cabeçalho do arquivo .load("movies.csv"): movies.csv é o nome do arquivo a ser lido Curtiu? Espero que sim, até mais!

Acessando e modificando o Terraform State

Acessando e modificando o Terraform State

Antes de começar a falar sobre o acesso aos estados, é necessário explicar o que são os estados ou State. O que são os States? O que é o Terraform State? O Terraform State é uma forma do Terraform gerenciar a infra, configurações e os recursos criados afim de manter um mapeamento do que já existe e controlar a atualização e criação de novos recursos. Um exemplo básico é quando criamos um Bucket S3, um EC2 ou uma SQS via Terraform. Todos estes recursos são mapeados no estado e passam a ser gerenciados pelo Terraform. Localização do State Local Por padrão o Terraform aloca o estado localmente no arquivo terraform.tfsate. Utilizar o State localmente pode funcionar bem para um estudo específico no qual não exista a necessidade em compartilhar o State entre times. Remote Ao contrário do Local, quando temos times compartilhando dos mesmos recursos, a utilização do State de forma remota se torna imprescindível. O Terraform provê suporte para que o State possa ser compartilhado de forma remota. Não entraremos em detalhes em como configurar, mas é possível manter o State no Amazon S3, Azure Blob Storage, Google Cloud Storage, Alibaba Cloud OSS e entre outras nuvens. O State é representado pelo arquivo terraform.tfsate, um arquivo no formato JSON, segue um exemplo de um Bucket S3 mapeando no State: {
"version": 4,
"terraform_version": "0.12.3",
"serial": 3,
"lineage": "853d8b-6ee1-c1e4-e27e-e10",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_s3_bucket",
"name": "s3_bucket_xpto",
"provider": "provider.aws",
"instances": [
{
"schema_version": 0,
"attributes": {
"acceleration_status": "",
"acl": "private",
"arn": "arn:aws:s3:::bucket.xpto",
"bucket": "bucket.xpto",
"bucket_domain_name": "bucket.xpto",
"bucket_prefix": null,
"bucket_regional_domain_name": "bucket.xpto",
"cors_rule": [],
"force_destroy": false,
"grant": [],
"hosted_zone_id": "Z3NHGSIKTF",
"id": "bucket.xpto",
"lifecycle_rule": [],
"logging": [],
"object_lock_configuration": [],
"policy": null,
"region": "us-east-1",
"replication_configuration": [],
"request_payer": "BucketOwner",
"server_side_encryption_configuration": [],
"tags": {
"Environment": "staging"
},
"versioning": [
{
"enabled": false,
"mfa_delete": false
}
],
"website": [],
"website_domain": null,
"website_endpoint": null
},
"private": "UJbhV=="
}
]
}
]
} Acessando e alterando o State Apesar do State estar alocado em um arquivo JSON, não é recomendado a alteração direta no arquivo. O Terraform provê a utilização do comando Terraform state executado via CLI para que pequenas modificações sejam efetuadas. Através do CLI, podemos executar comandos afim de manipular o State, segue o uso: terraform state <subcommand> [options] [args] Sub-comandos: list Lista os recursos no estado mv Move um item no estado pull Extrai o estado atual e lista o resultado no stdout push Atualiza um estado remoto de um arquivo de estado local rm Remove instância do estado show Mostra recursos do estado 1. Listando os recursos do State Comando: terraform state list O comando acima possibilita listar os recursos que estão sendo gerenciados pelo State Exemplo: $ terraform state list
aws_s3_bucket.s3_bucket
aws_sqs_queue.sqs-xpto No exemplo acima, temos um Bucket S3 e um SQS que foram criados via terraform e que estão sendo gerenciados pelo State. 2. Visualizando um recurso e seus atributos Comando: terraform state show [options] ADDRESS O comando acima possibilita mostrar em detalhes um recurso específico e seus atributos Exemplo: $ terraform state show aws_sqs_queue.sqs-xpto
# aws_sqs_queue.sqs-xpto:
resource "aws_sqs_queue" "sqs-xpto" {
arn = "arn:aws:sqs:sqs-xpto"
content_based_deduplication = false
delay_seconds = 90
fifo_queue = false
id = "https://sqs-xpto"
kms_data_key_reuse_period_seconds = 300
max_message_size = 262144
message_retention_seconds = 345600
name = "sqs-xpto"
receive_wait_time_seconds = 10
tags = {
"Environment" = "staging"
}
visibility_timeout_seconds = 30
} 3. Removendo recursos do State Comando: terraform state rm [options] ADDRESS... O comando acima remove um ou mais items do State. Diferente de um terraform destroy, que remove o recurso do State e os objetos remotos criados na nuvem. Exemplo: $ terraform state rm aws_sqs_queue.sqs-xpto E aí, Curtiu? Até mais!

Java: Streams API - findFirst()

Java: Streams API - findFirst()

Java 8 Streams introduziu diferentes métodos para manipular coleções. Um destes métodos é o findFirst(), que permite retornar o primeiro elemento de uma Stream através de uma instância Optional. Na prática List<String> names =
Arrays.asList("Monica Souza",
"Andre Silva",
"Elisa Santos",
"Adriano Silva");

Optional<String> opt =
names.stream()
.findFirst();

System.out.println("Item:" + opt.get()); Resultado Item: Monica Souza Utilizando filter Optional<String> opt =
names.stream()
.filter(f -> f.contains("Silva"))
.findFirst();

System.out.println("Item:" + opt.get()); Resultado Item: Andre Silva Perceba que retornou o primeiro nome com sobrenome Silva da coleção. A não utilização de Streams Se utilizarmos o modo tradicional, ou seja, sem a utilização de Streams. O código ficaria assim, filtrando pelo sobrenome "Silva" for(String item : list){
if(item.contains("Silva")){
break;
}
} Neste caso dependemos do break para encerrar a execução.

Primeiros passos com Delta Lake

Primeiros passos com Delta Lake

O que é o Delta Lake? O Delta Lake é um projeto open-source que visa gerenciar a camada de armazenamento (Storage Layer) que segundo o seu conceito: "traz confiabilidade para Datalakes". Na prática é uma abstração do Apache Spark reutilizando os mesmos mecanismos mas oferencendo recursos extras interessantes. Dentre eles, suporte para transações ACID. Todos sabem que manter a integridade dos dados em uma pipeline de dados é uma tarefa crítica diante do alta concorrência de leitura e escrita de dados. Neste caso, o ACID possibilita gerenciar ambientes como estes. Dentre outras vantagens, o Delta Lake provê histórico de auditoria, versionamento de dados, suporta operações DML do tipo delete e update e vários outras vantagens. Para este tutorial, vamos simular uma pipeline de dados simples de forma local, com o foco no que o Delta Lake pode nos oferecer de vantagens. Na etapa de ingestão, vamos carregar um Spark DataFrame a partir de um JSON, criaremos uma view temporária para nos auxiliar, criaremos uma tabela Delta via SQL e por fim, utilizaremos o Delta Lake para executar algumas operações de DML utilizando a view que será criada. Vamos utilizar Java e Maven para gerenciar as dependências, além de Spark e Hive no qual este, vai nos auxiliar em manter a tabelas em um catálogo de dados. Maven <dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>3.0.1</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>3.0.1</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.12</artifactId>
<version>3.0.1</version>
</dependency>

<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-core_2.12</artifactId>
<version>0.8.0</version>
</dependency>
</dependencies> O código será desenvolvido em pequenos trechos para um melhor entendimento. Configurando Spark com Delta e Hive String val_ext="io.delta.sql.DeltaSparkSessionExtension";
String val_ctl="org.apache.spark.sql.delta.catalog.DeltaCatalog";

SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("app");
sparkConf.setMaster("local[1]");
sparkConf.set("spark.sql.extensions",var_ext);
sparkConf.set("spark.sql.catalog.spark_catalog",val_ctl);

SparkSession sparkSession = SparkSession.builder()
.config(sparkConf)
.enableHiveSupport()
.getOrCreate(); Entendendo o trecho acima Definimos duas variáveis val_ext e val_ctl atribuindo os valores para as chaves (spark.sql.extensions e spark.sql.catalog.spark_catalog) . Estas, necessárias para a configuração do Delta junto com o Spark Demos o nome do contexto do Spark de app Como não estamos rodando o Spark em um cluster, o master está configurado para rodar local local[1] O Spark tem suporte para o Hive, nesse caso o habilitamos no trecho enableHiveSupport( ) Ingestão dos Dados Vamos trabalhar com Spark Dataframe como fonte dos dados. Carregaremos um Dataframe a partir de um JSON. Arquivo order.json {"id":1, "date_order": "2021-01-23", "customer": "Jerry", "product": "BigMac", "unit": 1, "price": 8.00}
{"id":2, "date_order": "2021-01-22", "customer": "Olivia", "product": "Cheese Burguer", "unit": 3, "price": 21.60}
{"id":3, "date_order": "2021-01-21", "customer": "Monica", "product": "Quarter", "unit": 2, "price": 12.40}
{"id":4, "date_order": "2021-01-23", "customer": "Monica", "product": "McDouble", "unit": 2, "price": 13.00}
{"id":5, "date_order": "2021-01-23", "customer": "Suzie", "product": "Double Cheese", "unit": 2, "price": 12.00}
{"id":6, "date_order": "2021-01-25", "customer": "Liv", "product": "Hamburger", "unit": 1, "price": 2.00}
{"id":7, "date_order": "2021-01-25", "customer": "Paul", "product": "McChicken", "unit": 1, "price": 2.40} Criando Dataframe Dataset<Row> df = sparkSession.read().json("datasource/");
df.createOrReplaceGlobalTempView("order_view"); Entendendo o trecho acima No trecho anterior, estamos criando um Dataframe a partir do arquivo JSON que está dentro do diretório datasource/ , crie este diretório para que a estrutura do seu código fique mais compreensiva e em seguida, crie o arquivo order.json com base no conteúdo mostrado anteriormente. Por último, criamos um view temporária que vai nos auxiliar mais a frente nos próximos passos. Criando a Tabela Delta (Delta Table) Vamos criar a Delta Table a partir de um script SQL. A princípio a criação é simples, mas perceba que usamos tipagens diferentes de uma tabela utilizada em um banco relacional. Como por exemplo, utilizamos STRING ao invés de VARCHAR e assim por diante. Estamos particionando a tabela pelo campo date_order. Este campo foi escolhido como partição pois acreditamos que haverá diferentes datas. Dessa forma, as consultas podem utilizar este campo como filtro, visando um melhor desempenho. E por fim, definimos a tabela como Delta Table a partir do trecho USING DELTA. String statement =
"CREATE OR REPLACE TABLE orders (" +
"id STRING, " +
"date_order STRING," +
"customer STRING," +
"product STRING," +
"unit INTEGER," +
"price DOUBLE) " +
"USING DELTA " +
"PARTITIONED BY (date_order) ";

sparkSession.sql(statement); Entendendo o trecho acima No trecho anterior estamos criando uma tabela Delta chamada orders e em seguida executamos a criação. Operações DML Delta suporta operações como Delete, Update e Upsert utilizando Merge Utilizando Merge junto com Insert e Update Neste passo, vamos executar um Merge que possibilita controlar o fluxo de inserção e atualização de dados através de uma tabela, Dataframe ou view. O Merge trabalha a partir de row matches que ficará mais compreensível no trecho seguinte. String mergeStatement = "Merge into orders " +
"using global_temp.order_view as orders_view " +
"ON orders.id = orders_view.id " +
"WHEN MATCHED THEN " +
"UPDATE SET orders.product = orders_view.product," +
"orders.price = orders_view.price " +
"WHEN NOT MATCHED THEN INSERT * ";

sparkSession.sql(mergeStatement); Entendendo o trecho acima No trecho acima estamos executando o Merge a partir da view order_view criada nos passos anteriores. No mesmo trecho temos uma condição orders.id = orders_view.id que vai auxiliar nos matches seguintes. Caso a condição anterior seja verdadeira, ou seja o MATCHED seja verdadeiro. Os dados serão atualizados. Caso a condição não seja verdadeira, NOT MATCHED. Os dados serão inseridos. No caso acima, os dados serão inseridos, pois até então não existia dados na tabela orders. Execute o comando abaixo para visualizar os dados inseridos. sparkSession.sql("select * from orders").show(); Atualize o arquivo datasource/order.json alterando o campo product, price e execute todos os trechos novamente. Você verá que todos os registros serão atualizados. Comando Update É possível executar Update sem a necessidade de usar o Merge, basta executar o comando abaixo: String updateStatement = "update orders " +
"set product = 'Milk-Shake' " +
"where id = 2";

sparkSession.sql(updateStatement); Comando Delete String deleteStatement = "delete from pedidos where id = 2";
sparkSession.sql(deleteStatement); Além de poder executar o comando Delete, é possível utilizar este comando junto ao Merge. Entendendo o Delta Lake Transaction Log (DeltaLog) Além de dar suporte a transações ACID, o delta gera alguns arquivos JSON que servem como uma forma de auditar e manter o histórico de cada transação, desde comandos DDL e DML Com este mecanismo é possível até voltar em um estado específico da tabela caso necessário. Para cada transação executada um arquivo JSON é criado dentro da pasta _delta_log. O arquivo inicial sempre será 000000000.json, contendo os commits da transação. Neste nosso cenário, este primeiro arquivo contém os commits da criação da tabela orders. Para visualizar melhor, acesse a pasta local que provavelmente foi criada na raiz do seu projeto chamada spark-warehouse. Esta pasta foi criada pelo Hive para alocar os recursos criados desde os arquivos JSON e os parquets. Dentro dela terá uma estrutura de pastas conforme imagem abaixo: Perceba que os arquivos são criados em ordem crescente a partir de cada transação executada. Acesse cada arquivo JSON e verá cada transação que foi executado através do campo operation, além de outras informações. 00000000000000000000.json "operation":"CREATE OR REPLACE TABLE" 00000000000000000001.json "operation":"MERGE" 00000000000000000002.json "operation":"UPDATE" 00000000000000000003.json "operation":"DELETE" Perceba também que os arquivos parquets foram gerados particionados em pastas pelo campo date_order. Utilizar partição visa consultas com melhores desempenho, mas não entraremos em detalhes neste post, futuramente falaremos mais sobre isso. Espero que neste post foi possível esclarecer algumas dúvidas sobre o que é o Delta Lake e seu funcionamento. Até a próxima!

Mensageria com Google Cloud Pub/Sub

Mensageria com Google Cloud Pub/Sub

A comunicação assíncrona via mensagens é um dos modelos mais utilizados atualmente entre microsserviços. Vamos fazer um exemplo bem simples usando a plataforma do Google. Vou começar mostrando o código e depois algumas configurações que precisaremos fazer. OK? Vamos lá!!! Para começar, vamos adicionar as seguintes dependências no projeto: (Caso tenha dúvidas em como iniciar um projeto spring boot, veja nesse post) <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-pubsub</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> Agora, vamos para o código de fato: Primeiramente vamos criar um VO que vamos usar para representar o nosso evento. public class EventVO {

private String name;
private String email;

[...] Getters/Setters and Constructor
} Agora , vamos disponibilizar um endpoint para facilitar a publicação da mensagem. import com.coffee.tipsgcp.vo.EventVO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gcp.pubsub.core.publisher.PubSubPublisherTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/producer-event")
public class ProducerEventController {

@Autowired
PubSubPublisherTemplate pubsubPublisher;

@Autowired
ObjectMapper objectMapper;

@PostMapping()
public void sendEvent(@RequestBody EventVO event) throws JsonProcessingException {
pubsubPublisher.publish("topico-test", objectMapper.writeValueAsString(event));
System.out.println("Mensagem enviada para o tópico !!!");
}
} No exemplo acima, nós injetamos a classe PubSubPublisherTemplate que é responsável por publicar a mensagem com o método publish(). Nele informamos o nome do tópico e transformamos o nosso objeto EventVO em json para ser enviado. E finalizando a parte do código, vamos criar um método para consumir as mensagens que publicamos. Lembrando que como estamos falando de microsserviços, o produtor e consumidor ficam em aplicações diferentes, aqui vamos colocar na mesma aplicação para facilitar o exemplo. import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gcp.pubsub.core.PubSubTemplate;
import org.springframework.stereotype.Component;

@Component
public class TopicSubscriber implements ApplicationRunner {

@Autowired
PubSubTemplate pubSubTemplate;

@Override
public void run(ApplicationArguments args) {
pubSubTemplate.subscribe("topico-test-assinatura", msg -> {
System.out.println("Consumindo mensagem \n" + msg.toString());
msg.ack();
});
}
} A classe acima implementa ApplicationRunner para que o código no método run() seja executado assim que a aplicação comece a rodar. Além disso, injetamos a classe PubSubTemplate para consumir as mensagens que chegam ao tópico especificado por meio do método subscribe(). Por fim, o metodo ack(), notifica o serviço do google que a mensagem foi recebida. Agora que finalizamos a parte do código, vamos a algumas configurações que teremos que fazer no Google Cloud Console. Isso pode ser feito por um client diretamento do terminal ou no próprio google console. Vou mostrar pelo console. Após criar um projeto no google console, precisaremos ativar uma chave para autenticação. Para isso, procure por Credentials na barra de busca: Após entrar nessa sessão, você encontrará uma listas com as contas que estão disponíveis para o seu usuário. Escolha a conta que deseja usar e clique em editar. Na próxima tela, será exibida a aba CHAVES. Nela terá a opção de adicionar uma nova chave. Clique na opção e será gerado um arquivo com a chave para autenticação no serviço. Faça dowload do arquivo. Após baixar o arquivo de autenticação, vamos criar uma variável de ambiente com o path do arquivo. Isso pode ser feito de várias formas, inclusive pode ser feito via código ao invés de variável de ambiente. Aqui, vou mostrar como foi feito na IDE Intellij. Clique em Edit Configurations e em, Environment Variables adicione: GOOGLE_APPLICATION_CREDENTIALS=[file_path]. Caso não seja especificado um arquivo de autenticação no google ou se a configuração estiver errada, a aplicação não vai nem conseguir iniciar. Além do arquivo de autenticação, vamos configurar agora o tópico e subscrição que vamos utilizar. Na barra de busca do Google Console, procure por Pub/Sub: Após abrir essa seção, clique no botão Criar Tópico e crie um tópico com o nome que você especificou no código. Depois de criado o tópico, clique em mais ações (3 pontinhos) no lado direito, e crie a assinatura com o mesmo nome especificado no código. Ótimo, agora sim nossa aplicação já está pronta!! Vamos subir a aplicação e fazer um POST para o nosso controller: Após fazer o POST, a saída do console será essa: Note que após a mensagem ser enviada, imediatamente ela é consumida pela outra ponta. É isso galera, espero ter contribuído. Até a próxima!

Spring Data REST: Um CRUD pronto em 5 minutos

Spring Data REST: Um CRUD pronto em 5 minutos

O módulo data rest do spring nos ajuda a disponibilizar operações CRUD com o mínimo de esforço possível. Imagine que você precise implementar um cadastro de produtos com regras complexas, mas que, antes disso, também tenha que ter um cadastro de “categoria de produtos”, que simplesmente insere uma nova categoria sem nenhuma regra complexa para se preocupar. Nesse contexto, o spring data rest pode facilitar bastante o trabalho, disponibilizando todas as operações no padrão REST para isso. Vamos lá!!! A primeira coisa que vamos precisar é incluir a dependência no projeto. <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId></dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> Feito isso, vamos criar a entidade PERSON, que será utilizada para o exemplo: @Entity
public class Person {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String document;

[...] //Getters and Setters
} E para finalizar(SIM, já estamos finalizando), criamos a classe Repository da entidade, onde vamos ter a anotação responsável por disponibilizar as operações rest. @RepositoryRestResource(collectionResourceRel = "persons", path = "persons")
public interface PersonRepository extends JpaRepository<Person, Long> {
} Pronto!!! Já temos várias operações REST funcionando. Vamos testar? Lembrando que foi usado o banco H2 para facilitar a implementação. Vamos fazer um POST para a url http://localhost:8080/persons, com o seguinte payload: {
"name": "Steve Rogers",
"document": "0000000001"
} Nesse caso temos como response um 201 Created e o body: {
"name": "Steve Rogers",
"document": "0000000001",
"_links": {
"self": {
"href": "http://localhost:8080/persons/1 "
},
"person": {
"href": "http://localhost:8080/persons/1 "
}
}
} Note que o retorno já faz referência a url GET do próprio registro. Para fazer update dos dados, podemos fazer um POST para a url http://localhost:8080/persons/1 com o seguinte payload: {
"name": "Tony Stark",
"document": "0000000001"
} E para deletar fazemos DELETE para a mesma url, e nesse caso receberemos como retorno o código 204 No Content. Após isso, se fizermos um GET para a url acima, passaremos a receber um 404 Not Found, já que o registro foi deletado. Então é isso pessoal, exemplo simples, mas que pode ajudar no ganho de produtividade no dia a dia. Até a próxima!!

Primeiros passos utilizando Terraform na AWS

Primeiros passos utilizando Terraform na AWS

O Terraform é uma ferramenta do tipo IaC (Infrastructure as code) que possibilita provisionar infra-estrutura nos serviços de nuvem, ou seja, ao invés de criar manualmente recursos na nuvem, o Terraform facilita a criação e o controle deste serviços através de gerenciamento de estado em poucas linhas código. O Terraform tem sua linguagem própria e pode ser utilizada de forma independente com outras linguagens de forma isolada. Para este tutorial, iremos criar um Bucket S3 e uma SQS utilizando Terraform na AWS. Instalação Terraform Para a instalação, faça o download do instalador neste link https://www.terraform.io/downloads.html e escolha o seu sistema operacional. Provider AWS Utilizaremos a AWS como provider, ou seja, quando selecionamos a AWS como provider, o Terraform fará o download dos pacotes que possibilitará a criação de recursos específicos para a AWS. Para seguir nos próximo passos, estamos levando em conta que você já possui: Credenciais da AWS O seu usuário já possui permissões necessárias para criar recursos na AWS Autenticação Como nós estamos utilizando a AWS como provider, precisamos configurar o Terraform para autenticar e em seguida criar os recursos. Existem algumas maneiras de autenticação. Pare este tutorial, escolhi utilizar um dos mecanismos da AWS que permite alocar as credencias em um arquivo na pasta $HOME/.aws e utilizar como fonte de autenticação única. Para criar esta pasta com as credenciais, precisamos instalar o AWS CLI, acesse este link e siga os passos de instalação. Este mecanismo evita a utilização das credenciais diretamente no código, dessa forma, caso precise executar algum comando ou SDK que conecte a AWS localmente, estas credencias serão carregadas a partir deste arquivo. Configuração das credenciais Após instalar o AWS CLI, abra o terminal e execute o comando a seguir: aws configure No próprio terminal, preencha os campos utilizando os dados das credencias do seu usuário: Após o preenchimento, 2 arquivos textos serão criados no diretório $HOME/.aws config: contendo o profile, neste caso o profile default foi criado credentials: contendo as credenciais Vamos alterar os arquivos para adequar a este tutorial, altere o arquivo config conforme abaixo: [profile staging] output = json region = us-east-1 [default] output = json region = us-east-1 No caso, temos 2 perfis configurados, o default e um perfil de staging. Altere o arquivo credentials conforme abaixo, substituindo pelas suas credenciais. [staging] aws_access_key_id = [Access key ID] aws_secret_access_key = [Secret access key] [default] aws_access_key_id = [Access key ID] aws_secret_access_key = [Secret access key] Criando os arquivos Terraform base Após todas estas configurações, iremos começar a trabalhar de fato com o Terraform. Para isso precisamos criar alguns arquivos base que vai nos auxiliar na criação dos recursos na AWS. 1º Passo: No diretório root do seu projeto, crie um pasta chamado /terraform 2º Passo: Dentro da pasta /terraform, crie os arquivos: main.tf vars.tf 3º Passo: Crie uma pasta chamada staging dentro de /terraform 4º Passo: Dentro da pasta terraform/staging/ crie o arquivo: vars.tfvars Pronto, agora temos a estrutura de pasta que vamos utilizar nos próximos passos. Configurando os arquivos Terraform Vamos começar pela declaração das variáveis utilizando o arquivo vars.tf. vars.tf Neste arquivo é onde vamos criar a variáveis em que vamos utilizar em nosso contexto, podemos criar variáveis com um valor default ou simplesmente vazias, onde estas serão preenchidas de acordo com o ambiente de execução, onde será explicado mais a frente. variable "region" {
default = "us-east-1"
type = "string"
}

variable "environment" {
} Criamos 2 variáveis: region: Variável do tipo string e seu valor default é a região da AWS em que vamos criar os recursos environment: Variável que vai representar o ambiente de execução staging/vars.tfvars Neste arquivo estamos definindo o valor da variável environment criada anteriormente sem valor default. environment = "staging" Essa separação é bem útil quando temos mais de um ambiente, caso tivéssemos um ambiente de produção, poderíamos ter criado outro arquivo vars.tfvars em uma pasta chamada production. Dessa forma, podemos escolher em qual ambiente vamos executar o Terraform. Vamos entender esta parte, quando executamos mais a frente. main.tf Este será o principal arquivo onde iremos declarar os recursos para que sejam criados na AWS. Nesta etapa vamos declarar os recursos para que seja criado um Bucket S3 e uma SQS. Vamos entendendo o arquivo em partes. Nesta primeira parte estamos declarando a AWS como provider e setando a região utilizando a variável que criamos anteriormente através de interpolação ${..}. Provider provider "aws" {
region = "${var.region}"
} Criando o Bucket S3 Para criar um recurso via Terraform, sempre começamos com a palavra chave resource e em seguida o nome do recurso e por fim um identificador. resource "nome do recurso" "identificador" {} Neste trecho estamos criando um Bucket chamado bucket.blog.data, lembre-se que nomes de Buckets devem ser únicos. O campo acl define as restrições do Bucket, neste caso, private. O campo tags é utilizado para passar informações extras ao recurso, neste caso será passando o valor da variável environment. Mais campos são descritos na documentação. resource "aws_s3_bucket" "s3_bucket" {
bucket = "bucket.blog.data"
acl = "private"

tags = {
Environment = "${var.environment}"
}
} Criando a SQS No próximo trecho, vamos criar uma SQS chamada sqs-posts. A criação do recurso segue as mesmas regras que descrevemos anteriormente. Para este cenário configuramos os campos delay_seconds que define o tempo de espera de uma mensagem ser entregue. Mais campos são descritos na documentação. resource "aws_sqs_queue" "sqs-blog" {
name = "sqs-posts"
delay_seconds = 90
tags = {
Environment = "${var.environment}"
}
} Executando o Terraform 1º Passo : Inicializar o Terraform Dentro do diretório /terraform execute o comando: terraform init Mensagens no console após a execução: 2º Passo: No Terraform existem workspaces. São ambientes de execução em que o Terraform provê para executar os recursos e separar os estados entre eles. Após inicializado, um workspace default é criado. terraform workspace list Para este tutorial vamos simular um ambiente de desenvolvimento. Lembra que criamos uma pasta chamada /staging ? Sim, esta pasta simula um ambiente de desenvolvimento. Para isso, vamos criar um workspace no Terraform chamado staging também. Se tivéssemos um ambiente de produção, um workspace de produção poderia ser criado. terraform workspace new "staging" Pronto, criamos um novo workspace e já estamos utilizando. 3º Passo: Neste passo, vamos listar todos os recursos existentes ou os que serão criados, neste caso, a última opção. terraform plan -var-file=staging/vars.tfvars O argumento plan possibilita visualizar os recursos que serão criados ou atualizados, é uma boa opção para entender o comportamento antes que o recurso seja criado definitivamente. O segundo argumento -var-file possibilita escolher um caminho específico contendo os valores das variáveis que serão utilizadas de acordo com o ambiente de execução. Neste caso o arquivo /staging/vars.tfvars contém valores referentes ao ambiente de staging. Caso existisse um workspace de produção, a execução seria a mesma, porém para uma pasta diferente. Mensagens no console após a execução: 4º Passo: Neste passo, vamos criar os recursos definitivamente. terraform apply -var-file=staging/vars.tfvars Basta substituir o plan por apply, em seguida uma mensagem de confirmação será mostrada no console: Digite yes para aplicar os recursos e aguarde o fim da execução. Pronto, o Bucket S3 e a SQS form criados! Agora você pode conferir direto no console da AWS. Escolha de workspace Caso necessite mudar de workspace, execute o comando selecionando o workspace em que deseja utilizar: terraform workspace select "[workspace]" Destruindo os recursos Esta parte do tutorial requer muita atenção. O próximo comando possibilita remover todos os recursos que foram criados sem a necessidade em remover um por um. terraform destroy -var-file=staging/vars.tfvars Digite yes, caso deseja que todos os recursos criados sejam destruídos. Não recomendo utilizar este comando em um ambiente profissional, mas para este tutorial é útil para que você não esqueça de apagar e a AWS te cobrar no futuro. Conclusão Terraform possibilita criar infra-estruturas de forma bem simples através de código e também oferece bastante segurança mantendo os recursos através do uso de estados. Para este tutorial utilizamos a AWS como provider, mas é possível utilizar Google Cloud, Azure e entre outros.

Salesforce: disponibilizando um endpoint para chamadas externas

Salesforce: disponibilizando um endpoint para chamadas externas

Salesforce é uma plataforma com vários módulos específicos para fazer a gestão do relacionamento do cliente em várias etapas da jornada, como venda e pós-venda, suporte, marketing, etc. O desenvolvimento com na plataforma Salesforce é uma das várias áreas do mundo dev que mais cresce nos últimos anos. É bem provável que se você está trabalhando com o CRM, também vai precisar fazer algumas integrações com outros sistemas ou base de dados da sua empresa, e para quem está iniciando nessa jornada, aqui vai um exemplo simples com a implementação de um endpoint no Salesforce e uma chamada externa a ele. Vamos lá!!! O primeiro passo é liberar o Salesforce para receber chamadas externas de URLs específicas. Por padrão, o Salesforce não aceita chamadas externas, então para liberar, você deve seguir os seguintes passos: Logar no Salesforce com acesso de administrador. Ir em Setup Na barra de busca procure por “Remote Site Settings” Crie um novo registro com a url da aplicação que fará a chamada REST ao Salesforce. Feito isso, agora podemos implementar um endpoint no salesforce como no modelo abaixo: @RestResource(urlMapping='/coffeeandtips/exemplo/*')
global with sharing class ExemploController {

@HttpPost
global static void recebeDadosESalva(List<RequestVO> req) {
String requestData = JSON.serialize(req);
[...]

}
} No próximo passo, vamos precisar fazer mais algumas configurações no salesforce. Logar no Salesforce com acesso de administrador. Ir em Setup Na barra de busca procure por “Connected Apps” Procure pelas informações “Consumer Key” e “Consumer Secret” Pronto! Agora já conseguimos fazer chamadas para o endpoint que acabamos de criar. Para isso, antes de fazer a chamada de fato, vamos precisar obter um token utilizando a key e secret do passo anterior. A chamada deve ser feita com os seguintes parâmetros: POST para https://minhaurl.salesforce.com/services/oauth2/token Content-type : application/x-www-form-urlencoded Parametros: grant_type : {esse parâmetro será sempre do tipo “password”}
client_id: {Consumer Key obtida no passo anterior}
client_secret : {Consumer Secret obtida no passo anterior}
username: {username de um usuário com permissões para fazer chamadas externas (pode ser o usuario que você usou para logar no salesforce)}
password: {senha de um usuário com permissões para fazer chamadas externas (pode ser o usuario que você usou para logar no salesforce)} Você receberá um token como resposta desse POST. E Finalmente podemos fazer uma chamada rest para o endpoint que criamos no Salesforce. POST para https://minhaurl.salesforce.com/services/apexrest/coffeeandtips/exemplo Content-type: application/json Parametros: Authorization : Bearer “Token Obtido no passo anterior” É isso pessoal. Espero ter contribuído. Até a próxima!

MySQL: Criando um schema e migrando dados

MySQL: Criando um schema e migrando dados

Não é raro que em meio a um projeto apareça a necessidade de fazer uma migração de dados. E quando pensamos na arquitetura de micro-serviços, começar separando os dados por schemas pode ser um primeiro passo para deixar o monolito para trás. Vamos lá!! O primeiro passo é criar um novo schema no MySQL, que pode ser feito com o seguinte comando: CREATE SCHEMA new_schema; Pronto! Agora já podemos iniciar a migração para o novo schema. A primeira coisa é copiar a estrutura de todas as tabelas que você deseja migrar. Da seguinte forma: CREATE TABLE new_schema.table_1 LIKE old_schema.table_1;
CREATE TABLE new_schema.table_2 LIKE old_schema.table_2;
CREATE TABLE new_schema.table_3 LIKE old_schema.table_3; Se fizermos um select agora em uma das tabelas do novo schema, vamos perceber que a tabela está criada exatamente com a mesma estrutura da antiga, porém estão todas vazias. Então, por fim, vamos fazer a migração dos dados. INSERT new_schema.table_1 SELECT * FROM old_schema.table_1;
INSERT new_schema.table_2 SELECT * FROM old_schema.table_2;
INSERT new_schema.table_3 SELECT * FROM old_schema.table_3; Um lembrete. Antes de fazer a cópia dos dados, é importante “desligar” qualquer aplicação que faça operações na base antiga para evitar perda de dados. Então é isso, seus dados já estão migrados e você já pode começar a usá-los. Até a próxima!!!

Java para iniciantes: Lendo e escrevendo arquivos de forma simples

Java para iniciantes: Lendo e escrevendo arquivos de forma simples

Existem muitas formas diferentes de manipular arquivos em Java. Algumas soluções podem chegar facilmente a 10 linhas de código (dependendo também da formatação do código) só para ler um arquivo e fazer um print no console. Felizmente, com as evoluções na linguagem através dos anos, essa tarefa se tornou bastante simples. Então, aqui vai um exemplo bem simples para vocês. Vamos lá!!! Para esse exemplo, vamos precisar importar as seguintes classes: import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; Para ler um arquivo de texto por exemplo, podemos usar a seguinte solução: private void read() throws IOException {
String filePath = "/tmp/filetoread.txt";
Path path = Paths.get(filePath);
Files.lines(path).forEach(line -> System.out.println(line));
} Essa solução vai funcionar em um ambiente linux, por causa do formato da string filePath. Para que funcione em um ambiente windows, podemos simplesmente alterar para: String filePath = "C:\\coffeeandtips\\tests\\filetoread.txt"; Bom, então após ler um arquivo com três linhas de código (que ainda pode ser reduzido para duas), vamos escrever um arquivo de forma bem resumida. private void write() throws IOException {
String filePath = "/tmp/filewrited.txt";
Files.write(Path.of(filePath), "Write a phrase.".getBytes());
} Da mesma forma que o exemplo anterior, esse formato de path só irá funcionar no ambiente linux, para executar em ambiente windows é só alterar para: String filePath = "C:\\coffeeandtips\\tests\\filewrited.txt"; Então é isso pessoal. Espero ter contribuído. Até a próxima.

Upload de arquivo no S3 com AWS SDK e Java

Upload de arquivo no S3 com AWS SDK e Java

A Amazon Web Services AWS é um dos serviços de Cloud Computing mais tradicionais e com mais recursos no mercado. Neste post vamos ensinar como fazer um upload de arquivo para um bucket S3. Neste post vamos criar um código capaz de fazer um upload de arquivo no S3 utilizando Java e a SDK da AWS. Dependências Maven <dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>1.11.327</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> Código de Upload import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import java.io.File;

public class UploadService {

public void upload() throws Exception {

try {

String BUCKET = "SEU-BUCKET";

AmazonS3 amazonS3Client = AmazonS3ClientBuilder
.standard().withRegion(Regions.US_EAST_1)
.build();

File file = new File("imagem.jpg");

PutObjectRequest putObjectRequest =
new PutObjectRequest(BUCKET,
"nome-do-arquivo",
file);

ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType("jpg");

putObjectRequest.setMetadata(objectMetadata);
amazonS3Client.putObject(putObjectRequest);

} catch (Exception e) {
throw new Exception(e);
}
}
} No código acima, temos um exemplo simples de upload de arquivos para o S3. Nele estamos enviando um arquivo do tipo .jpg. Passo a passo A variável BUCKET deve ser substituído pelo seu Bucket já criado O objeto amazonS3Client recebe as configurações necessárias para efetuar o upload. Nesta configuração é necessário configurar a região, neste caso estamos utilizando a região US-EAST-1 Em seguida carregamos o arquivo chamado imagem.jpg para o objeto do tipo File para efetuar o upload O objeto putObjectRequest é criado com base no Bucket, nome do arquivo e o objeto do tipo File criado anteriormente. Dessa forma o objeto putObjectRequest possui todas as informações para efetuar o upload. O objeto objectMetadata possibilita configurar detalhes do arquivo, como tipo do conteúdo, header, criptografia e etc. E em seguida ele é setado no objeto putObjectRequest. E por fim, a requisição final utilizando o método putObject que será responsável por fazer o upload do arquivo contendo todas as informações e configurações setadas nos passos anteriores. Agora, acesso o S3 e verifique se o arquivo esteja presente e pronto. É isso, curtiu? Até mais!