segunda-feira, 2 de novembro de 2020

Usando o Maven para logar no Docker Hub e publicar a imagem da sua aplicação

 

Nesse artigo vou mostrar como configurar uma aplicação Maven para logar no Docker Hub através do arquivo settings.xml da pasta .m2 e então publicar a imagem do seu projeto.

 

Assumimos que você já tem seu projeto configurado para usar o Docker, ou seja, já tem o Dockerfile configurado. Vamos configurar então o pom.xml pois é nele que vamos dizer o repositório do projeto e que ele deve se logar pelas configurações do Maven:

 

<build>

<plugins>

       <plugin>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-maven-plugin</artifactId>

       </plugin>

       <plugin>

           <groupId>com.spotify</groupId>

           <artifactId>dockerfile-maven-plugin</artifactId>

           <version>1.4.6</version>

           <executions>

                 <execution>

                      <id>default</id>

                      <goals>

                          <goal>build</goal>

                          <goal>push</goal>

                      </goals>

                  </execution>

            </executions>

            <configuration>

                 <useMavenSettingsForAuth>true</useMavenSettingsForAuth>

                 <repository>seuDockerID/seuProjeto</repository>

                 <tag>${project.version}</tag>

                 <buildArgs>

                    <JAR_FILE>${project.build.finalName}.jar</JAR_FILE>

                 </buildArgs>

            </configuration>

       </plugin>

</plugins>

</build>

 

 

          Agora vamos configurar o arquivo settings.xml que fica dentro da pasta .m2 e contém configurações globais do maven para sua máquina. É possível que esse arquivo não exista na sua máquina, pois ele não é obrigatório para o Maven funcionar.

 

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<settings

  xmlns="http://maven.apache.org/SETTINGS/1.0.0"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0

  http://maven.apache.org/xsd/settings-1.0.0.xsd">

 

  <servers>

             <server>

                     <id>docker.io</id>

                     <username>seu_usuario</username>

                     <password>sua_senha</password>

                     <configuration>

                       <email>seu_email</email>

                     </configuration>

             </server>

       </servers>

      

       <configuration>

          <repository>seuDockerID/seuProjeto</repository> //o mesmo usado no pom.xml

          <tag>${project.version}</tag>

          <useMavenSettingsForAuth>true</useMavenSettingsForAuth>

    </configuration>

</settings>

 

          Agora basta você gerar o seu projeto com o Maven seja pelo comando mvn package ou mvn clean install que o Maven vai gerar sua imagem Docker! Basta você executar o comando Docker images para ver sua nova imagem criada 😊!

 

Para enviar para o Docker Hub basta executar o comando mvn dockerfile:push.

 

          Pronto, assim o ciclo de geração e publicação de imagens Docker ficou controlado pelo Maven.


Fonte: https://medium.com/@fernandoevangelista_28291/criando-e-enviando-imagem-docker-com-java-e-maven-4fa3c70dba0f

 

 

segunda-feira, 5 de outubro de 2020

Formas de acessar um ManagedBean JSF instanciado pelo container

     Nesse post vou trazer duas formas de acessar um ManagedBEans JSF já instanciados pelo container e chamar seus métodos. Útil principalmente para beans de escopo de sessão ou aplicação, não criando instâncias extras.

 

     A primeira:


public static <T> T findBean(String beanName) {
        FacesContext context = FacesContext.getCurrentInstance();
        return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);

}


    A segunda:

 

public static Object pegaManagedBean(String nomeNoXML) {
        FacesContext context = FacesContext.getCurrentInstance();
        ELResolver resolver = context.getApplication().getELResolver();
        return resolver.getValue(context.getELContext(), null, nomeNoXML);
}

terça-feira, 25 de agosto de 2020

JPA - Carregando multiplas listas em uma mesma entidade evitando o erro MultipleBagFetchException

    É comum termos um entidade que precise carregar mais de uma lista, e quando essas listas precisam ser carregadas juntas no mesmo select, podemos nos deparar com o seguinte erro:
 
Caused by: org.hibernate.loader.MultipleBagFetchException: 
cannot simultaneously fetch multiple bags

    Bag significa algo como "bolsa" ou "sacola" (tradução livre rsrsrs), e isso quer dizer estamos tentando carregar mais de uma "bolsa" de dados e o JPA não sabe como organizar esses dados para nos entregar.

    Isso ocorre porque geralmente o banco nos devolve uma lista ordenada de dados (List), mas como o JPA está tentando montar uma lista que contém listas dentro delas, ele pode acabar desordenando essas listas. Então ele quer que você diga pra ele como tratar esses dados.

    Existem basicamente 3 formas de contornarmos esse problema:

  1. Nunca carregar as listas na mesma consulta.
  2. Usar Set ao invés de List ao criar as listas na entidade.
  3. Usando a anotação @OrderColumn.
   Vamos abordar cada uma delas a seguir:

    1 - Nunca carregar as listas na mesma consulta.

    Ao invés de você usar o FetchType.EAGER ou no select fazer um join fetch caso o carregamento seja FetchType.LAZY nas "n" listas da sua entidade, você carrega apenas uma delas no primeiro momento, e a(s) outra(s) você carrega separadamente, por exemplo chamando o método get da(s) outra(s) lista(s) com o objeto ainda gerenciado pelo JPA, de forma que ele vá ao banco e preencha essas listas.
     
    2 - Usar Set ao invés de List ao criar as listas na entidade.

    Outra solução é usar o Set ao invés do List. Como o Set não é ordenado como o List o JPA não vai se perder tentando ordenar essa lista. Lembrando que o Set não trabalha com dados duplicados.

    3 - Usando a anotação @OrderColumn.

    Você também pode criar um campo para o hibernate trabalhar a ordenação e não se perder. A desvantagem dessa solução é unicamente a de criar um campo extra para o JPA poder ordenar os dados. 

    Se o mapeamento for bidirecional, será preciso criar a propriedade e mapea-la na classe que detêm o mapeamento além de usá-la no @orderColumn.
    
@Entity
public class Paciente {
 
    //se tiver o mappedBy (bidirecional) precisa criar a propriedade na outra classe 
    @OneToMany(mappedBy="paciente")
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteSintoma> lstPacienteSintoma;
 
    //se tiver o mappedBy (bidirecional) precisa criar a propriedade na outra classe 
    @OneToMany(mappedBy="paciente")
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteDiagnostico> lstPacienteDiagnostico; 
 
    ...
}

@Entity
public class PacienteSintoma {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false)
    private Paciente  paciente;
 
    ...
}
 
@Entity
public class PacienteDiagnostico {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false)
    private Paciente  paciente;
 
    ...
} 
 
    Se o mapeamento for unidirecional, será preciso criar apenas o campo no banco e passá-lo no @orderColumn.

@Entity
public class Paciente {
 
    //unidirecional 
    @OneToMany
    @JoinColumn(name="paciente_id", nullable=false) 
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteSintoma> lstPacienteSintoma;
 
    //se tiver o mappedBy (bidirecional) precisa criar a propriedade na outra classe 
    @OneToMany(mappedBy="paciente")
    @JoinColumn(name="paciente_id", nullable=false)  
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteDiagnostico> lstPacienteDiagnostico; 
 
    ...
}

@Entity
public class PacienteSintoma {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false, insertable=false, updatable=false)
    private Paciente  paciente;
 
    ...
}
 
@Entity
public class PacienteDiagnostico {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false, insertable=false, updatable=false)
    private Paciente  paciente;
 
    ...
} 
Fonte: https://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#entity-hibspec-collection-extratype-indexbidir


 

sexta-feira, 17 de julho de 2020

Resolvendo problema CORS no Spring

Olá, hoje vou falar sobre o CORS – Cross-Origin Resource Sharing que significa compartilhamento de recursos de origem cruzada.

Antes era muito comum o uso de iFrames ou similares para fazer com que um site acessasse diretamente conteúdo de outros o que também era válido para aplicações web mais complexas, o problema é que isso começou a ser explorado por pessoas mal intencionadas para se passarem por um site ou serviço que não são e roubar informações ou outras atividades ilícitas. Então os navegadores começaram a “bloquear” esse tipo de coisa, validando antes se a origem tem acesso ao backend vindo de um IP diferente ou não configurado em uma digamos “White list”.

         Tratar o CORS é obrigação do backend, e vamos ver como fazer isso no Spring. No caso, podemos fazer de duas formas: liberando geral ou especificando as URLs que terão o acesso garantido.

    Para liberar qualquer endereço, podemos desabilitar o CORS na classe de configuração de segurança:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/v2/api-docs",
                "/configuration/ui",
                "/swagger-resources/**",
                "/configuration/security",
                "/swagger-ui.html",
                "/webjars/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().antMatchers("/xxx/**").authenticated();
        http.authorizeRequests().anyRequest().permitAll();
        http.cors().disable();

    }

}


        Ou podemos criar um bean na classe principal da aplicação e passar o padrão de URL como "/**":
       
@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("http://localhost:9000");
        }
    };
}


    Para liberar endereços específicos, o que é mais recomendado, basta passar o endereço no lugar do "/**":

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
        }
    };
}


     Outra forma de fazer é ao invés de criar o bean na classe principal, é criar uma classe específica com a configuração, deixando-a mais explícita:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class CorsConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

}


        Exemplo de configuração do CORS em versões mais novas do Spring Boot:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configure(http)) // aplica o CorsConfigurationSource registrado
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").permitAll()
.anyRequest().authenticated()
);

return http.build();
}

}

 



 FOnte: https://spring.io/guides/gs/rest-service-cors/