top of page

Search

108 itens encontrados para ""

  • Java: Default Methods

    Default methods: O que é? E quando usar? Default Methods foi uma das novidades adicionadas na versão 8 do Java e possibilita flexibilizar bastante o uso de Interfaces. Para entender melhor a utilidade, é necessário que nós lembremos algumas "dores" em utilizar Interfaces. A Interface é um ótimo recurso quando precisamos definir um contrato e que este seja cumprido por quem o implementa. Até então é bem útil e faz com que a aplicação siga boas práticas de implementação. Mas caso precisamos criar um novo método em uma interface que já é implementada por diversas outras classes? Antes do Java 8, teríamos que implementar este novo método em todas estas classes para que o código não quebre e mesmo assim nem todas estas classes utilizaria este novo método. Lembrando que métodos de uma Interface são assinaturas de um contrato. Para entender melhor, vamos criar alguns exemplos simulando uma destas situações. Veja a Interface chamada CrudInterface public interface CrudInterface { void create(); List read(); void update(); void delete(); } O objetivo desta Interface é criar um contrato para classes que necessitem executar operações de CRUD (Create, Read, Update e Delete) sigam o mesmo padrão de implementação. A seguir vamos criar duas classes que farão a implementação desta Interface. A classe UserDAO public class UserDAO implements CrudInterface { @Override public void create() { System.out.println("Create an User"); } @Override public List read() { return Arrays.asList("user 1","user 2"); } @Override public void update() { System.out.println("Update an User"); } @Override public void delete() { System.out.println("Delete an User"); } } A classe ProductDAO public class ProductDAO implements CrudInterface { @Override public void create() { System.out.println("Create a Product"); } @Override public List read() { return Arrays.asList("Product 1", "Product 2"); } @Override public void update() { System.out.println("Update a Product"); } @Override public void delete() { System.out.println("Delete a Product"); } } As classes ProductDAO e UserDAO são obrigadas a implementar todos os métodos ou assinaturas declaradas na Interface CrudInterface. Apesar desta obrigatoriedade ter um lado positivo, que é manter um padrão de implementação, também possui um lado negativo, que é a não flexibilidade e uma possível quebra de código caso a Interface CrudInterface sofra alguma mudança futuramente. Imagine que além das classes ProductDAO e UserDAO, outras 20 classes também implementam a Interface CrudInterface. Cada vez mais o código está criando uma dependência. Agora, imagine que você precise adicionar um novo método na Interface CrudInterface que nem todas as classes que já implementam esta Interface vão utilizar. No cenário atual, teríamos que implementar este novo método em cada uma delas. Mas é aí que o recurso Default Methods entra pra nos ajudar. Para entender melhor, vamos supor que a classe UserDAO precise de um método que vai remover todos os usuário da base e que o time preferiu criar este novo método na interface CrudInterface pois poderia ser útil para as demais classes. Mas o time precisa garantir que esta alteração não quebre o código, ou seja, que o compilador não obrigue as classes implementarem este novo método. Vamos alterar a Interface CrudInterface utilizando Default Methods public interface CrudInterface { void create(); List read(); void update(); void delete(); default void deleteAll(){ System.out.println("Delete all objects"); } } Adicionamos o método deleteAll( ) que supostamente será responsável por deletar todos os registros de uma entidade em específico. Perceba que é um método concreto dentro de uma Interface, que antes era utilizada apenas para declarar assinaturas abstratas. O ponto principal é a palavra chave default que assegura a utilização de Default Methods em uma Interface. Neste caso, não somos mais obrigados a implementar o método deleteAll( ) para todas as classes que implementam a Interface CrudInterface, ou seja, sem riscos de quebra o código. E por fim, possibilita a implementação de uma assinatura dentro da própria Interface. É claro que este é só uma forma de exemplificar a utilização de Default Methods, mas sugiro uma análise caso você precise criar um método que atenderá poucos cenários. Nem sempre a utilização de Interfaces seja a mais vantajosa. E aí, Curtiu? Até mais!

  • 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 com.amazonaws aws-java-sdk-s3 com.amazonaws aws-java-sdk-bom 1.11.327 pom import 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!

  • Introdução ao Lombok

    O projeto Lombok é uma biblioteca Java que permite criar um código Java utilizando anotações que permitem substituir a criação de getters e setters, métodos equals, builders e entre outros. Para os exemplos a seguir, iremos utilizar como build tool o Maven. Maven org.projectlombok lombok 1.18.16 provided Estamos utilizando o escopo do tipo provided que significa que o Lombok será configurado em tempo de compilação. Vamos criar uma classe chamada Customer e nela iremos criar alguns campos, construtores, métodos getters e setters e um builder. Tudo isso de forma manual. E em seguida vamos recriar esta classe utilizando os recurso do Lombok. Classe Customer import java.util.Date; public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; public Customer(){ } public Customer(Long id, String firstName, String lastName, Date birthDate){ this.id = id; this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } } Entendendo a classe Customer Na classe Customer criamos: 4 campos 2 Construtores, sendo 1 default métodos getters e setters para cada campo Perceba que uma quantidade razoável de código foi criado. Recriando a classe Customer utilizando Lombok import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; @AllArgsConstructor @NoArgsConstructor @Data public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; } Agora perceba a diferença entre o primeiro e este último. Muito mais limpo e elegante. Entendendo as anotações utilizadas @AllArgsConstructor Esta anotação permite substituir a criação de um construtor como por exemplo: public Customer(Long id, String firstName, String lastName, Date birthDate){ this.id = id; this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } Assim, caso seja necessário adicionar um novo campo, não é necessário declará-lo no construtor. O Lombok faz este trabalho. @NoArgsConstructor Já está anotação, substitui o construtor default, ou seja, não te obriga a passar campos como parâmetros no construtor ao criar um novo objeto. Por exemplo: public Customer(){} @Data Esta é uma das anotações mais interessantes. Ela permite a não necessidade da criação do métodos getters e setters, como foi criado no primeiro exemplo de código da classe Customer. Criando a classe Customer utilizando Builder de forma tradicional O Builder é um padrão de projeto (padrão de criação) utilizado bastante em código Java para a criação de objetos. No próximo código vamos implementar um builder de forma manual. import java.util.Date; public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; private Customer(){} public static class Builder { private Long id; private String firstName; private String lastName; private Date birthDate; public Builder id(Long id){ this.id = id; return this; } public Builder firstName(String firstName){ this.firstName = firstName; return this; } public Builder lastName(String lastName){ this.lastName = lastName; return this; } public Builder birthDate(Date birthDate){ this.birthDate = birthDate; return this; } public Customer build(){ Customer customer = new Customer(); customer.id = this.id; customer.firstName = this.firstName; customer.lastName = this.lastName; customer.birthDate = this.birthDate; return customer; } } @Override public String toString() { return "id = " + this.id + " FirstName = " + this.firstName + " LastName = " + this.lastName + " BirthDate = " + this.birthDate; } } Aplicando o Builder na prática import java.util.Date; public class Main { public static void main(String[] args) { Customer customer = new Customer .Builder() .id(1L) .firstName("Jones") .lastName("Paul") .birthDate(new Date()) .build(); System.out.println(customer); } } Resultado id = 1 FirstName = Jones LastName = Paul BirthDate = Sun Dez 28 11:48:55 BRT 2020 Perceba mais uma vez, um código verboso que poderia ser facilmente substituído pelo Lombok. Agora vamos recriar o código acima utilizando o Lombok. Recriando a classe Customer utilizando Builder do Lombok import lombok.Builder; import java.util.Date; @Builder public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; @Override public String toString() { return "id = " + this.id + " FirstName = " + this.firstName + " LastName = " + this.lastName + " BirthDate = " + this.birthDate; } } Perceba agora a diferença em termos de quantidade de código escrito. Apenas criamos os mesmos campos, reutilizamos o método toString() e adicionamos a anotação @Builder responsável pela criação e substituição de todo aquele código criado anteriormente no primeiro exemplo. Aplicando o Builder utilizando Lombok na prática import java.util.Date; public class Main { public static void main(String[] args) { Customer customer = Customer .builder() .id(1L) .firstName("Jones") .lastName("Paul") .birthDate(new Date()) .build(); System.out.println(customer); } } Perceba que houve uma pequena mudança para executar o código acima. Neste caso não foi preciso criar um objeto utilizando a palavra chave new, apenas invocamos o método builder() e os campos da classe para atribuição. Resultado id = 1 FirstName = Jones LastName = Paul BirthDate = Sun Dez 28 11:48:55 BRT 2020 Prático e útil, certo? É isso, curtiu? Até mais!

  • Schema Merging com Spark e Java

    Schema Merging é uma forma de evoluir schemas adicionando novas colunas através da junção de DataFrames. Imagine que você possui diferentes arquivos parquets com diferentes schemas, e que exista a necessidade em criar um novo schema a partir de todas as colunas destes variados parquets. Podemos resolver este problema em uma simples linha de código, conforme será mostrado a seguir. A seguir vamos criar arquivos parquets com diferentes schemas através de arquivos JSON e em seguida, faremos o merge destes arquivos transformando em um único schema consolidado. Arquivos JSON que usaremos como DataSource: Arquivo user.json {"id":1, "login": "Jonh", "age": 24} {"id":2, "login": "Lucas", "age": 24} {"id":3, "login": "Miesha", "age": 25} {"id":4, "login": "Suzie", "age": 22} Arquivo address.json {"id":1, "city": "Los Angeles", "country": "USA"} {"id":2, "city": "New York", "country": "USA"} {"id":3, "city": "San Louis Obispo", "country": "USA"} Criando o SparkSession SparkConf sparkConf = new SparkConf(); sparkConf.setMaster("local[1]"); sparkConf.setAppName("app"); SparkSession sparkSession = new SparkSession.Builder() .config(sparkConf) .getOrCreate(); Criando os DataFrames Dataset dfUser = sparkSession.read().json("user.json"); Dataset dfAddress = sparkSession.read().json("address.json"); Gerando os Parquets para cada DataFrame No código a seguir, estamos criando os parquets no diretório data/table/ para a partição chamada partition de valor 1 dfUser.write().parquet("data/table/partition=1"); No código a seguir, estamos criando os parquets no diretório data/table/ para a partição chamada partition de valor 2 dfAddress.write().parquet("data/table/partition=2"); Agora, criaremos um novo DataFrame com base nos parquets criados anteriormente executando o Schema Merging Dataset dfMerge = sparkSession .read().option("mergeSchema", true) .parquet("data/table"); O Schema Merging é executado no seguinte trecho option("mergeSchema", true) Por fim, podemos ver o novo schema mergeado através do mergeSchema dfMerge.printSchema(); Resultado root |-- age: long (nullable = true) |-- id: long (nullable = true) |-- login: string (nullable = true) |-- city: string (nullable = true) |-- country: string (nullable = true) |-- partition: integer (nullable = true) Veja que a partir dos DataFrames dfUser e dfAddress foi criado um novo schema consolidando as colunas entre eles. Código completo SparkConf sparkConf = new SparkConf(); sparkConf.setMaster("local[1]"); sparkConf.setAppName("app"); SparkSession sparkSession = new SparkSession.Builder() .config(sparkConf) .getOrCreate(); Dataset dfUser = sparkSession.read().json("user.json"); dfUser.write().parquet("data/table/partition=1"); Dataset dfAddress = sparkSession.read().json("address.json"); dfAddress.write().parquet("data/table/partition=2"); Dataset dfMerge = sparkSession .read().option("mergeSchema", true) .parquet("data/table"); dfMerge.printSchema(); Curtiu? Espero que sim, até mais!

  • Tipos primitivos em Java

    Falar de tipos primitivos pode causar algumas confusões, mas é bem simples de entender. Os tipos primitivos é a forma mais simples de um dado ou variável e não é considerado uma instância de uma classe. 1. Para tipos numéricos long int byte short float double 2. Para tipo texto char 3. Para tipo booleano boolean

  • Converter Date para LocalDateTime

    Conversão simples e rápida de um objeto do tipo Date para um objeto do tipo LocalDateTime LocalDateTime localDateTime = new Date() .toInstant() .atZone(ZoneOffset.UTC) .toLocalDateTime();

  • Consultas com Spark SQL

    Spark SQL faz parte do core do Apache Spark e permite consultas estruturadas dentro do contexto Spark utilizando SQL. Com Spark SQL é possível conectar em diversos datasources como Avro, ORC, JSON, Parquet e dentre outros. Neste tutorial vamos utilizar como datasource um arquivo JSON para mostrar os poderosos recursos do Spark SQL. Maven org.apache.spark spark-core_2.12 3.1.0 org.apache.spark spark-sql_2.12 3.1.0 Configurar o contexto Spark No passo seguinte, criaremos o SparkSession. Pense que neste ponto você pode reutiliza-lo em sua aplicação, então pense em um classe Singleton para alocar este objeto na inicialização. No exemplo a seguir não farei isso para que fique mais prático e simples. public class SparkSql { public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); } } O objeto SparkConf é responsável pela configuração da Session, perceba que é um objeto simples com os atributos appName e master. O atributo master é uma configuração especifica caso a aplicação execute em um cluster, como nesse exemplo é local, então o valor [1] é o suficiente para a execução e por fim o appName é o nome da aplicação. Listagem 1 - Select simples Conteúdo do arquivo produto.json {"id":1, "nome":"arroz", "preco":12.0, "qtde": 50} {"id":2, "nome":"feijao", "preco":7.50, "qtde": 30} {"id":3, "nome":"coca-cola", "preco":5.50, "qtde": 150} {"id":4, "nome":"suco", "preco":3.80, "qtde": 250} {"id":5, "nome":"milho", "preco":1.50, "qtde": 33} {"id":6, "nome":"yogurte", "preco":6.0, "qtde": 15} {"id":7, "nome":"leite", "preco":3.70, "qtde": 250} {"id":8, "nome":"oleo", "preco":5.60, "qtde": 100} public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select * from produto"); sqlFrame.show(); } Neste exemplo acima é uma forma simples de listar todo o conteúdo do arquivo através de um DataFrame. Um DataFrame é basicamente uma coleção de dados distribuídos que se assemelha bastante com uma tabela relacional. Neste trecho é criado uma view temporária com base no Dataframe que foi carregado pela sessão. dataFrame.createOrReplaceTempView("produto"); No próximo trecho é executado uma consulta SQL simples com base na view criada anteriormente. Dataset sqlFrame = session.sql("select * from produto"); Por fim é executado o método .show() que é uma ação do Spark. Este método lista todos os registros da coleção. É possível passar como argumento neste método a quantidade de registros para listagem, o valor padrão é de 20 registros. sqlFrame.show(); Resultado da execução Listagem 2 - Cláusula Where public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select nome, preco " + "from produto " + "where preco >= 5.0"); sqlFrame.show(); } Resultado da execução Listagem 3 - Between public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select " + "nome, preco, qtde " + "from produto " + "where qtde between 10 and 50 "); sqlFrame.show(); } Resultado da execução Listagem 4 - Sum public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select " + "sum(preco * qtde) as total " + "from produto " + "where qtde > 100 "); sqlFrame.show(); } Resultado da execução Conteúdo do arquivo produto.json alterado {"id":1,"nome":"arroz","preco":12.0,"qtde":50,"tipo":"sólido"} {"id":2,"nome":"feijao","preco":7.50,"qtde":30,"tipo":"sólido"} {"id":3,"nome":"coca","preco":5.50,"qtde":150,"tipo":"líquido"} {"id":4,"nome":"suco","preco":3.80,"qtde":250,"tipo":"líquido"} {"id":5,"nome":"milho","preco":1.50,"qtde":33,"tipo":"sólido"} {"id":6,"nome":"yogurte","preco":6.0,"qtde":15,"tipo":"líquido"} {"id":7,"nome":"leite","preco":3.70,"qtde":250,"tipo":"líquido"} {"id":8,"nome":"oleo","preco":5.60,"qtde":100,"tipo":"líquido"} Listagem 5 - Count + group by + having public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select " + "tipo, count(tipo) as qtde" + "from produto " + "group by tipo " + "having (tipo = 'sólido') "); sqlFrame.show(); } Resultado da execução Tentei mostrar alguns exemplos simples de como utilizar o Spark SQL e o que ele é capaz, espero ter ajudado no entendimento e até mais. Documentação: https://spark.apache.org/sql/ Github: https://github.com/apache/spark

  • Configurar TTL no DynamoDB

    O TTL (Time to Live) é um mecanismo do DynamoDB que permite controlar a expiração dos items. Dessa forma você não precisa manter grande volume de dados que não serão usados e nem se dar o trabalho de exclui-los manualmente. Como configurar É bem simples a configuração deste mecanismo, segue o passo a passo: 1. O atributo do item deve ser do tipo Number, conforme a documentação da AWS. Observe o campo expiredAt Observe o campo expiredAt, o formato do valor dever ser do tipo Epoch. O formato Epoch pode ser extraído de uma data, neste caso você pode preencher este campo utilizando LocalDateTime, segue o exemplo: LocalDateTime localDateTime = LocalDateTime.of(2020, 02, 02, 12,02,00); long expiredAt = localDateTime.plus(2, ChronoUnit.HOURS).toEpochSecond(ZoneOffset.UTC); System.out.println("Valor final(Epoch): " + expiredAt) No exemplo acima é uma soma de 2 horas á data de 02-02-2020 ás 12:02:00 que resultará em 02-02-2020 ás 14:02:00. Logo em formato Epoch o valor final será: Valor final(Epoch): 1580652120 2. Levando em conta que a sua aplicação já está salvando os items no formato esperado, basta efetuar a última configuração. Na aba Visão geral, na seção Detalhes da tabela selecione a opção Atributo tempo de vida - Gerenciar TTL Ao clicar na opção uma pop-up será aberta e no campo Atributo TTL preencha o nome do atributo que fará o controle. Pronto, basta clicar em continuar que a AWS vai provisionar a alteração para que o mecanismo comece a funcionar em breve. Neste cenário do exemplo, o item mostrado será apagado na data de 02-02-2020 ás 14:02:00. Ou seja, caso precise utilizar algo assim, pense em utilizar a data atual + o tempo de vida do item que você deseja de acordo com o seu negócio.

  • Criando um projeto Spring Boot em 2 minutos

    Uma das características do Spring Boot é diminuir o tempo de configuração para o projeto. Comparado com os projetos Springs tradicionais que necessita criar diversos arquivos de configuração tornando o processo tedioso e demorado. Hoje com Spring Boot é mais fácil. Neste poste mostrarei como gerar um projeto Spring Boot em 2 minutos. Siga os passos: 1. O primeiro passo é acessar o link https://start.spring.io/ A ideia principal deste portal é a possibilidade de gerar um arquivo POM para projetos Maven ou um arquivo Gradle para projetos Gradle com todas as dependências necessárias. Perceba que é possível selecionar a versão do Spring e do Java. 2. Escolha o tipo de projeto, versão e linguagem e preencha os campos na seção Project Metadata 3. Clique no botão ADD DEPENDENCIES para selecionar as dependências para o seu projeto 4. Após selecionar as dependências necessárias, basta clicar no botão GENERATE na tela principal 5. Será gerado um arquivo zipado para download. Agora basta descompactar o arquivo e importar o projeto!

  • SQS: Duplicidade de mensagens na fila

    Evitar mensagens duplicadas na fila pode ser um requisito básico para o modelo do seu negócio. A SQS possui um recurso de "desduplicação" de mensagens bem interessante. Este recurso está presente apenas em filas do tipo FIFO Existe duas formas de configurar: 1. Na configuração da fila, habilite a opção Desduplicação baseada em conteúdo 2. Uma outra forma de configurar seria no ato do envio da mensagem. Neste caso a fila não precisa ser configurada conforme o item 1. Segue um trecho de código Java utilizando a SDK da AWS que exemplifica este cenário: AmazonSQS amazonSQS = AmazonSQSClientBuilder.standard() .withRegion(Regions.US_EAST_1) .build(); SendMessageRequest messageRequest = new SendMessageRequest(); messageRequest.setMessageBody("mensagem"); messageRequest.setMessageGroupId("id-grupo"); messageRequest.setMessageDeduplicationId("id-desduplicação"); messageRequest.setQueueUrl("url-da-fila"); amazonSQS.sendMessage(messageRequest); Neste caso é necessário atribuir um ID que identifica a mensagem como duplicada, conforme o trecho: messageRequest.setMessageDeduplicationId("id-desduplicação"); O código descrito no item 2 pode ser utilizado para o item 1, mas nesse caso, o trecho anterior é opcional. Ponto de atenção Este recurso só funciona quando mensagens de mesmo conteúdo são enviadas em um intervalo de até 5 minutos. Caso o intervalo entre elas seja maior que 5 minutos, a fila receberá a mensagem duplicada.

  • Git: Desfazendo commits

    Quando precisamos desfazer algum commit feito de forma incorreta ou por outro motivo. A seguir alguns comandos Git que vão te ajudar a resolver este problema. Utilizando o modo --soft como argumento git reset --soft HEAD~ Neste caso o último commit será desfeito na atual branch (HEAD). O arquivo não será alterado de acordo com as alterações anteriores ao commit. O que vai acontecer é que o arquivo ficará disponível para um novo commit. Se você executar o comando git status Será possível ver o(s) arquivo(s) na seção "changes to be committed", ou seja, o commit foi desfeito e está disponível para ser comitado. Podemos melhorar este comando passando o número de commits em que você deseja desfazer. Por ex: git reset --soft HEAD~3 O número 3 fará com que os últimos 3 commits sejam desfeitos. É bem simples. Utilizando o modo --hard como argumento git reset --hard HEAD~ O modo --hard possibilita que além de desfazer o commit, o arquivo seja alterado com base nas alterações anteriores ao commit. Caso precise desfazer um ou mais commits conforme mostrado no primeiro exemplo, a regra é a mesma. Existem outros modos de reset, veja a documentação oficial do Git

  • Java: Streams API - Map

    Desde o Java 8 lançado em 2014 foram adicionados dezenas de novas funcionalidades dentre melhorias na JVM e funções para facilitar a vida do desenvolvedor, pois ele merece. Dentre estas features, estão as Expression Lambda (EL) que foi o ponta pé inicial para a entrada do Java no mundo da programação funcional, melhoria na API de Data e a não necessidade de criar implementações de Interfaces já existentes com a utilização de Default methods. E a outra novidade é a API de Streams, o foco desse post. A Stream API é uma nova abordagem para se trabalhar com Collections deixando o código menos verboso e mais inteligente. A Stream API trabalha com o processamento de dados sob demanda e fornece dezenas de funcionalidades para manipular Collections diminuindo o código e simplificando o desenvolvimento em uma espécie de pipeline que será explicado mais a frente. Vamos criar um Classe representando a entidade Cidade no qual terá como atributos: nome, estado e população. E por fim um método chamado listaCidades que carrega uma lista de objetos do tipo Cidade. public class Cidade { String nome; String estado; long populacao; public Cidade(){} public Cidade(String nome, String estado, long populacao){ this.nome = nome; this.estado = estado; this.populacao = populacao; } public List listaCidades(){ List cidades = new ArrayList(); cidades.add(new Cidade("Hollywood", "CA", 30L)); cidades.add(new Cidade("Venice", "CA", 10L)); cidades.add(new Cidade("Houston", "TX", 14L)); cidades.add(new Cidade("New York", "NY", 21L)); cidades.add(new Cidade("Albany", "NY", 11L)); cidades.add(new Cidade("Rio de Janeiro", "RJ", 14L)); cidades.add(new Cidade("São Paulo", "SP", 90L)); return cidades; } @Override public String toString() { return "Cidade: " + nome + " /Estado: " + estado + " /População: " + populacao; } } Map O Map possibilita converter o objeto no tipo do campo que for passado como argumento List nomes = cidade.listaCidades() .stream() .map(c -> c.nome) .collect(Collectors.toList()); nomes.forEach( c -> System.out.println(c) ); No exemplo acima, no método map foi passado o campo nome do objeto Cidade, neste caso, ao invés de retornar a lista baseado nos objetos do tipo Cidade, será retornado uma lista do tipo String. Saída: Hollywood Venice Houston New York Albany Rio de Janeiro São Paulo Outro exemplo com o campo Cidade.populacao List listaPopulacao = cidade.listaCidades() .stream() .map(c -> c.populacao) .collect(Collectors.toList()); listaPopulacao.forEach( c -> System.out.println(c) ); Saída: 30 10 14 21 11 14 90 Resumindo, o map possibilita um cast para o campo do objeto em que você deseja que seja retornado. Como funciona a pipeline? Seguindo o exemplo anterior, a pipeline é um processo sequencial que se diferencia entre operações intermediárias e finais. No exemplo, a Stream é invocada a partir de uma fonte de dados (lista de objetos do tipo Cidade) que trabalha sob demanda, o método map é uma operação intermediária, ou seja, ela processa os dados até que o método collect é invocado, originando uma operação final. E aí, curtiu? Até mais!

bottom of page