Java

Aplicando HATEOAS em uma API JAX-RS

HATEOAS (Hypermedia as the Engine of Application State) é um requisito da arquitetura REST, onde os recursos de uma API deve retornar nas suas respostas links (URI) que indicam como acessar outros recursos e/ou aplicar outras ações nos recursos acessados.

Desta forma, o client necessita conhecer apenas o endpoint principal da API para poder navegar nela. Ao implementar HATEOAS, este endpoint e todos os demais irão retornar links que permitirá ao client explorar todos os recursos fornecidos.

Existem algumas formas de implementar este recurso em API JAX-RS, que veremos a seguir.

Java - Fundamentos de JAX-WS e JAX-RS
Curso de Java - Fundamentos de JAX-WS e JAX-RS
CONHEÇA O CURSO

HATEOAS no JAX-RS com UriBuilder e Link

Para a implementação do HATEOAS, o JAX-RS fornece duas classes: UriBuilder e Link. Como o nome sugere, a UriBuilder facilita a criação de uma URI, enquanto Link é uma representação de um recurso relacionado que atende a especificação RFC 5988.

Para compreendê-los, vamos ver um exemplo prático.

Neste artigo, utilizarei de exemplo a API RESTful implementada em JAX-RS API já apresentada anteriormente. Como ela implementa autenticação JWT, o seu ponto de entrada será o endpoint de login, que no momento retorna apenas o token de acesso:

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_JSON)
public Response post(Usuario usuario)
{
    try{
        if(usuario.getUsuario().equals("teste@treinaweb.com.br") 
           && usuario.getSenha().equals("1234"))
        {
            String jwtToken = Jwts.builder()
                .setSubject(usuario.getUsuario())
                .setIssuer("localhost:8080")
                .setIssuedAt(new Date())
                .setExpiration(
                    Date.from(
                        LocalDateTime.now()
                        .plusMinutes(15L)
                        .atZone(ZoneId.systemDefault())
                        .toInstant()
                    )
                )
                .signWith(CHAVE, SignatureAlgorithm.HS512)
                .compact();

            return Response.status(Response.Status.OK).entity(jwtToken).build();
        }
        else
            return Response
                    .status(Response.Status.UNAUTHORIZED)
                    .entity("Usuário e/ou senha inválidos")
                    .build();
    }
    catch(Exception ex)
    {
        return Response
                .status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(ex.getMessage())
                .build();
    } 
}

A partir dele, ao ser autenticado, o client poderá acessar as pessoas cadastradas. Para indicar isso, podemos utilizar a classe UriBuilder para criar a URI deste endpoint:

UriBuilder.fromUri("http://localhost:8080/")
        .path("pessoa")
        .build();

Este endpoint não define parâmetros, mas caso houve isso poderia ser indicado:

UriBuilder.fromUri("http://localhost:8080/")
        .path("pessoa/{id}")
        .build(2);

Desta forma, a URI criada seria:

http://localhost:8080/pessoa/2

Da mesma forma, também poderia ser adicionadas querystrings:

UriBuilder.fromUri("http://localhost:8080/")
        .path("pessoa/{id}")
        .queryParam("q", "{nome}")
        .build(2, "Carlos");

Neste caso, a URI seria:

http://localhost:8080/pessoa/2?q=Carlos

Para definir a URI do nosso endpoint, além da classe UriBuilder, faremos uso da classe Link:

Link link = Link.fromUriBuilder(
                    UriBuilder.fromUri("http://localhost:8080/")
                    .path("pessoa")
                )
                .rel("lista_pessoas")
                .type("GET")
                .build();

Note que esta classe, além do UriBuilder , define a relação da URI com o endpoint atual (de login) e o tipo da solicitação.

Para que este dado seja retornado na resposta da solicitação, ele deve ser informado no método link da classe Response:

return Response.status(Response.Status.OK).entity(jwtToken).links(link).build();

Ao fazer isso, esta informação estará no header da resposta:

Enpoint Login com Hateoas retornado no header

Este é o formato que a especificação RFC 5899 define. Porém este não é o usual, o client normalmente espera esta informação no corpo da resposta. Veremos como fazer isso conhecendo o recursos para HATEOAS do Jersey.

Problemas do HATEOAS no corpo da resposta

Para que o link do HATEOAS também seja mostrado no corpo de uma resposta, é necessário que ele seja definido como um campo do recurso:

public class Pessoa {
    private int id;
    private String nome;
    private int idade;
    private Link link;

    public Link getLink() {
        return link;
    }

    public void setLink(Link link) {
        this.link = link;
    }

    //..Código omitido
}

E ao retornar este recurso, este campo deve ser preenchido:

@Authorize
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Pessoa getById(@PathParam("id") int id) {
    Pessoa pessoa = _repositorio.Get(id);
    pessoa.setLink(
        Link.fromUriBuilder(
            UriBuilder.fromUri("http://localhost:8080/")
            .path("pessoa/{id}")
        )
        .rel("self")
        .type("GET")
        .build(id)
    );
    return pessoa;
}

Com isso, no corpo da resposta desta solicitação, o link será informado:

Endpoint Pessoa com Hateoas com um link mostrando todas as propriedades da classe Link

Entretanto, um problema disso é que também são retornadas algumas informações indesejadas. Para que isso seja resolvido, é necessário alterar json provider para o Jackson:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
</dependency>

Registrá-lo:

public static HttpServer startServer() {
    final ResourceConfig rc = new ResourceConfig().packages("br.com.treinaweb");
    rc.register(JacksonFeature.class);
    return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}

E por fim, criar um Adapter:

public class LinkAdapter extends XmlAdapter<LinkJaxb, Link> {

    public LinkAdapter() {}

    public Link unmarshal(LinkJaxb p1) {
        Link.Builder builder = Link
                                .fromUri(p1.getUri())
                                .rel(p1.getRel())
                                .type(p1.getType());

        return builder.build();
    }

    public LinkJaxb marshal(Link p1) {
        return new LinkJaxb(
                        p1.getUri(), 
                        p1.getRel(), 
                        p1.getType()
                    );
    }
}

class LinkJaxb {

    private URI uri;
    private String rel;
    private String type;

    public LinkJaxb() {
        this(null, null, null);
    }

    public LinkJaxb(URI uri, 
                    String rel, 
                    String type) 
    {
        this.uri = uri;
        this.rel = rel;
        this.type = type;
    }

    @XmlAttribute(name = "href")
    public URI getUri() {
        return uri;
    }

    public void setUri(URI uri) {
        this.uri = uri;
    }

    @XmlAttribute(name = "rel")
    public String getRel(){
        return rel;
    }

    public void setRel(String rel) {
        this.rel = rel;
    }

    @XmlAttribute(name = "type")
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

Que deve ser informado no campo do recurso:

@XmlJavaTypeAdapter(LinkAdapter.class)
private Link link;

Agora, o link retornado será mais amigável:

Enpoint Pessoa com HATEOAS de um link amigável

Entretanto, até agora foi necessário definir “manualmente” o link do recurso. Imagine quando for uma lista, este procedimento será bem “chato”. Felizmente o Jersey tem a solução para este problema.

HATEOAS no Jersey com @InjectLink

Para resolver o trabalho de criar um link individualmente para cada recurso, o Jersey fornece módulo Declarative Linking, que define anotação @InjectLink, que pode ser aplicada diretamente no campo Link:

@InjectLink(
        resource = PessoaResource.class,
        style = Style.ABSOLUTE,
        rel = "self",
        bindings = @Binding(name = "id", value = "${instance.id}"),
        method = "GET"
)
@XmlJavaTypeAdapter(LinkAdapter.class)
private Link link;

Este módulo requer a dependência abaixo:

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-declarative-linking</artifactId>
</dependency>

Com isso não é necessário criá-lo manualmente:

@Authorize
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response post(Pessoa pessoa)
{
    try{
        _repositorio.Add(pessoa);
        return Response.status(Response.Status.CREATED).entity(pessoa).build();
    }
    catch(Exception ex)
    {
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
    } 
}

Pois será adicionado automaticamente pelo Jersey:

Enpoint Pessoa com HATEOAS de um link amigável

Caso o recurso possua vários links, eles podem ser agrupados em uma lista:

List<Link> links;

E esta lista pode receber a anotação @InjectLinks:

@InjectLinks({
    @InjectLink(
        resource = PessoaResource.class,
        style = Style.ABSOLUTE,
        rel = "self",
        bindings = @Binding(name = "id", value = "${instance.id}"),
        method = "GET"
    ),
    @InjectLink(
            resource = PessoaResource.class,
            style = Style.ABSOLUTE,
            rel = "update",
            bindings = @Binding(name = "id", value = "${instance.id}"),
            method = "PUT"
    ),
    @InjectLink(
            resource = PessoaResource.class,
            style = Style.ABSOLUTE,
            rel = "delete",
            bindings = @Binding(name = "id", value = "${instance.id}"),
            method = "DELETE"
    )
})
@XmlJavaTypeAdapter(LinkAdapter.class)
List<Link> links;

Ao acessar o recurso, ele retornará todos esses links:

Endpoint de Pessoa com HATEOAS com três links

Java - Criação de aplicações web com Spring MVC
Curso de Java - Criação de aplicações web com Spring MVC
CONHEÇA O CURSO

Conclusão

O HATEOAS possui pontos negativos e positivos (que não foram abordados aqui pois este não é o objetivo do artigo), entretanto é um novo padrão de projeto que facilita a compreensão de uma API RESTful. Como a sua implementação não é muito complexa, é algo que deve ser avaliado e sempre que possível implementado nas aplicações.

Por hoje é só, até a próxima 🙂

Implementando autenticação baseada em JWT em uma API RESTful JAX-RS

Segurança deve ser um ponto vital para qualquer aplicação web. Não importando o tamanho ela sempre conterá dados que necessitam de alguma proteção. APIs também se enquadram neste quesito, mas as formas tradicionais de autenticação, baseadas em telas de login e sessão, não podem ser aplicadas neste tipo de aplicação.

Por serem stateless por definição, APIs RESTful procuram implementar autenticações baseadas em alguma informação nas solicitações do usuário. Sendo que as opções mais comuns são: HTTP Basic Authentication, onde o usuário e senha codificado em base64 é enviado no header Authorization da solicitação; e Token Based Authentication, onde é enviado no header Authorization da solicitação um token assinado, garantindo que ele não foi adulterado.

Devido a facilidade de implementação e a maior segurança, dessas duas opções, a opção mais utilizada é a Token Based Authentication, que utiliza o padrão aberto JSON Web Token (JWT). Baseado em JSON este padrão nos permite fornecer várias informações sobre o usuário de forma compacta e auto-contida.

Neste artigo veremos como implementar este tipo de autenticação em uma API RESTful JAX-RS.

Java - Fundamentos de JAX-WS e JAX-RS
Curso de Java - Fundamentos de JAX-WS e JAX-RS
CONHEÇA O CURSO

Gerando o Token de acesso da API

Para este artigo não criarei uma aplicação do zero, utilizarei como base a API RESTful com a JAX-RS API que demostrei no meu artigo passado.

Para gerar o token, utilizaremos a biblioteca jjwt, assim, a primeira coisa a ser feita na aplicação é a adição das dependências dela:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.1</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.1</version>
    <scope>runtime</scope>
</dependency>

Com as dependências adicionadas, criaremos uma nova entidade chamada Usuario:

public class Usuario {
    private int idUsuario;
    private String usuario;
    private String senha;

    public int getIdUsuario() {
        return idUsuario;
    }

    public String getSenha() {
        return senha;
    }

    public void setSenha(String senha) {
        this.senha = senha;
    }

    public String getUsuario() {
        return usuario;
    }

    public void setUsuario(String usuario) {
        this.usuario = usuario;
    }

    public void setIdUsuario(int idUsuario) {
        this.idUsuario = idUsuario;
    }
}

E o recurso abaixo:

@Path("/login")
public class LoginResource {
    private final SecretKey CHAVE = Keys.hmacShaKeyFor(
        "7f-j&CKk=coNzZc0y7_4obMP?#TfcYq%fcD0mDpenW2nc!lfGoZ|d?f&RNbDHUX6"
        .getBytes(StandardCharsets.UTF_8));

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(Usuario usuario)
    {
        try{
            if(
                usuario.getUsuario().equals("teste@treinaweb.com.br") 
                    && 
                usuario.getSenha().equals("1234")
            )
            {
                String jwtToken = Jwts.builder()
                    .setSubject(usuario.getUsuario())
                    .setIssuer("localhost:8080")
                    .setIssuedAt(new Date())
                    .setExpiration(
                        Date.from(
                            LocalDateTime.now().plusMinutes(15L)
                                .atZone(ZoneId.systemDefault())
                            .toInstant()))
                    .signWith(CHAVE, SignatureAlgorithm.RS512)
                    .compact();

                return Response.status(Response.Status.OK).entity(jwtToken).build();
            }
            else
                return Response.status(Response.Status.UNAUTHORIZED)
                        .entity("Usuário e/ou senha inválidos").build();
        }
        catch(Exception ex)
        {
            return Response.status(
                        Response.Status.INTERNAL_SERVER_ERROR
                    ).entity(ex.getMessage())
                    .build();
        } 
    }
}

Note que nele há apenas um método, que realizará o login do usuário. Por esta ser uma aplicação simples, o usuário e senha válidos já estão definidos no código. Ao autenticar, será gerado um token:

String jwtToken = Jwts.builder()
    .setSubject(usuario.getUsuario())
    .setIssuer("localhost:8080")
    .setIssuedAt(new Date())
    .setExpiration(
        Date.from(
            LocalDateTime.now().plusMinutes(15L)
                .atZone(ZoneId.systemDefault())
            .toInstant()))
    .signWith(CHAVE, SignatureAlgorithm.RS512)
    .compact();

Onde, é informado:

  • Subject: o login do usuário;
  • Issuer: quem está gerando o token;
  • IssuedAt: data que o token foi gerado;
  • Expiration: tempo de vida do token;
  • sign: como o token será assinado;

Das informações, a mais importante é a assinatura do token. Neste exemplo ela utiliza uma chave textual visível:

private final SecretKey CHAVE = Keys.hmacShaKeyFor(
    "7f-j&CKk=coNzZc0y7_4obMP?#TfcYq%fcD0mDpenW2nc!lfGoZ|d?f&RNbDHUX6"
    .getBytes(StandardCharsets.UTF_8));

Entretanto em uma aplicação real, esta chave precisa ser bem protegida, pois é através dela que os tokens serão validados pela aplicação. Assim, se uma pessoa mal-intencionada obter esta informação, ela poderá gerar tokens válidos e terá acesso aos recursos protegidos da API.

Ao testar o endpoint, se for informado o usuário e senha corretos, um token será gerado:

Requisição de login correta, gerando um token

Caso contrário será retornado 401 Unauthorized:

Requisição com a senha inválida, gerando uma resposta 401 Unauthorized

Com a geração do token pronta, temos que proteger nossos endpoints, o que veremos a seguir.

Autenticando as solicitações

Como vimos acima, o token será enviado no header Authorization da solicitação. Assim, para autenticá-las é necessário analisar este header e verificar se o token informado é valido. Apenas nessas situações as solicitações devem ser permitidas.

Para fazer isso, podemos utilizar o @NameBinding, que é uma meta annotation que nos permite definir filtros e interceptadores no pipeline da solicitação. Estes filtros são implementados via anotação, assim, inicialmente uma anotação deve ser definida:

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Authorize { }

Onde utilizamos o @NameBinding e indicamos que a anotação será aplicada em métodos.

É na implementação desta anotação que verificamos a validade do token:

@Provider
@Authorize
@Priority(Priorities.AUTHENTICATION)
public class AuthorizeFilter implements ContainerRequestFilter {
    private final SecretKey CHAVE = 
            Keys.hmacShaKeyFor("7f-j&CKk=coNzZc0y7_4obMP?#TfcYq%fcD0mDpenW2nc!lfGoZ|d?f&RNbDHUX6"
                                .getBytes(StandardCharsets.UTF_8));

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        String authorizationHeader = requestContext
                                        .getHeaderString(HttpHeaders.AUTHORIZATION);
        try {
            String token = authorizationHeader.substring("Bearer".length()).trim();

            Jwts.parserBuilder()
                    .setSigningKey(CHAVE)
                    .build()
                    .parseClaimsJws(token);
        } catch (Exception e) {
            requestContext
                .abortWith(Response.status(Response.Status.UNAUTHORIZED)
                .build());
        }

    }

}

Inicialmente é indicado que a classe irá prover (@Provider) a implementação a nossa anotação (@Authorize) e que a prioridade dela no pipeline do JAX-RS é de autenticação (AUTHENTICATION). Ou seja, ela será executada antes dos endpoints.

Na classe, é obtido o token do header da solicitação:

String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
String token = authorizationHeader.substring("Bearer".length()).trim();

Em seguida é validado:

Jwts.parserBuilder()
    .setSigningKey(CHAVE)
    .build()
    .parseClaimsJws(token);

O método parseClaimsJws obtém os dados presentes no token como: Subject, Issuer, etc; caso haja algo errado, é gerada uma exceção. Assim, caso ocorra qualquer erro é retornado que o usuário não tem permissão de acesso:

requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());

Com a anotação definida, ela pode ser aplicada aos endpoints:

@Path("/pessoa")
public class PessoaResource {

    private PessoaRepository _repositorio = new PessoaRepository();

    @Authorize
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Pessoa> get() {
        return _repositorio.GetAll();
    }

    @Authorize
    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Pessoa getById(@PathParam("id") int id) {
        return _repositorio.Get(id);
    }

    @Authorize
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(Pessoa pessoa)
    {
        try{
            _repositorio.Add(pessoa);
            return Response.status(Response.Status.CREATED).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(
                        Response.Status.INTERNAL_SERVER_ERROR
                    ).entity(ex.getMessage())
                    .build();
        } 
    }

    @Authorize
    @PUT
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response put(@PathParam("id") int id, Pessoa pessoa)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            pessoa.setId(id);
            _repositorio.Edit(pessoa);
            return Response.status(Response.Status.OK).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(
                        Response.Status.INTERNAL_SERVER_ERROR
                    ).entity(ex.getMessage())
                    .build();
        } 
    }

    @Authorize
    @DELETE
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response delete(@PathParam("id") int id)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            _repositorio.Delete(id);
            return Response.status(Response.Status.OK).build();
        }
        catch(Exception ex)
        {
            return Response.status(
                        Response.Status.INTERNAL_SERVER_ERROR
                    ).entity(ex.getMessage())
                    .build();
        } 
    }
}

Para testá-la vamos utilizar o Postman.

Testando a autenticação

Inicialmente gere um token:

Requisição de login gerando o token

Com o token gerado, ele deve ser informado na aba Auth do Postman:

Aba Auth do Postman, informando o token

Não se esqueça de definir o tipo como Bearer Token. Com isso, ao enviar uma solicitação, ela será aceita:

Requisição POST para pessoa, com token válido

Se o token não for informado, ou caso seja inválido, será retornado 401 Unauthorized:

Requisição POST para pessoa, com token inválido

Java - Fundamentos de JAX-WS e JAX-RS
Curso de Java - Fundamentos de JAX-WS e JAX-RS
CONHEÇA O CURSO

Conclusão

Neste artigo vimos uma implementação simples de autenticação utilizando o padrão JWT. Um padrão bem difundido e muito utilizado, então caso necessite implementar este tipo de autenticação na sua aplicação não deixe de dar uma olhada nele.

Mas independente do padrão utilizado, procure não deixar suas aplicações expostas, sempre adote um sistema de autenticação e conexão segura, HTTPS.

Por fim, você pode ver a aplicação demonstrada aqui no meu Github.

Criando uma API RESTful com a JAX-RS API

No Java, quando pensamos na criação de uma API RESTful a primeira opção que vem a mente é o projeto Spring. Este projeto facilita a criação de APIs REST com o Spring Boot, que é uma das bibliotecas fornecidas por ele. Entretanto, o Java define uma especificação para este tipo de aplicação, a JAX-RS API.

Mesmo não possuindo tanto destaque quanto o Spring Boot, como veremos neste artigo, é bem simples criar uma API REST com a JAX-RS API.

Java - Fundamentos de JAX-WS e JAX-RS
Curso de Java - Fundamentos de JAX-WS e JAX-RS
CONHEÇA O CURSO

A especificação JAX-RS API

A JAX-RS API trata-se de uma especificação, ela define interfaces e anotações fornecidas pelo Java EE, que podem ser utilizadas na criação de uma API RESTful. Um desenvolvedor pode criar a sua aplicação baseada unicamente nesta especificação, entretanto o usual é fazer uso de uma biblioteca que a implemente.

Entre as bibliotecas existentes, as implementações da JAX-RS API mais conhecidas são RESTEasy e Jersey. Sendo que a mais utilizada é a Jersey, assim neste artigo faremos uso dela.

Então vamos colocar a mão na massa.

Criando a aplicação

Para este artigo irei criar um projeto Maven utilizando o arquétipo jersey-quickstart-grizzly2, pois este arquétipo já possui o Jersey configurado e fornece embutido o servidor Grizzly.

Pelo terminal, o projeto pode ser criado com o comando abaixo:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=br.com.treinaweb -DartifactId=jaxrsexample -Dpackage=br.com.treinaweb

No arquivo pom.xml haverão as dependências do Jersey:

<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.inject</groupId>
  <artifactId>jersey-hk2</artifactId>
</dependency>

Caso não queira utilizar o Jersey com o Grizzly, você pode substituir a primeira dependência acima pelas abaixo:

<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-server</artifactId>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-servlet</artifactId>
</dependency>

Com o projeto criado e o Jersey configurado, vamos começar a criar a nossa API REST com JAX-RS API.

Criando uma entidade e repositório

Este exemplo será um CRUD simples, mas para isso necessitamos de uma entidade, que será representada pela classe abaixo:

public class Pessoa {
    private int id;
    private String nome;
    private int idade;

    public int getId() {
        return id;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public void setId(int id) {
        this.id = id;
    }
}

Esta aplicação não lidará com um banco de dados, assim o repositório utilizará um hashmap na memória:

public class PessoaRepository {
    private final static HashMap<Integer, Pessoa> pessoas = new HashMap<>();

    public List<Pessoa> GetAll(){
        return new ArrayList<Pessoa>(pessoas.values());
    }

    public Pessoa Get(final int id) {
        return pessoas.get(id);
    }

    public void Add(final Pessoa pessoa) {
        if(pessoa.getId() == 0 )
            pessoa.setId(generateId(pessoas.size() + 1));
        pessoas.put(pessoa.getId(), pessoa);
    }

    public void Edit(final Pessoa pessoa) {
        pessoas.remove(pessoa.getId());
        pessoas.put(pessoa.getId(), pessoa);
    }

    public void Delete(final int id) {
        pessoas.remove(id);
    }

    private int generateId(final int possible)
    {
        if(pessoas.containsKey(possible))
            return generateId(possible + 1);
        return possible;
    }
}

Agora podemos criar o nosso recurso.

Recursos

No Jersey os endpoints são chamados de recursos. Um recurso nada mais é que uma classe que contém a anotação @Path:

import javax.ws.rs.Path;

@Path("/pessoa")
public class PessoaResource {

}

Como é possível notar no código acima, a anotação @Path é utilizada para indicar um caminho. Ao defini-lo na classe, significa que este será o nosso endpoint. Dentro dela, definimos métodos que representam os verbos HTTP, onde cada método deverá conter a anotação equivalente ao verbo que o invocará:

  • @DELETE = DELETE;
  • @GET = GET;
  • @HEAD = HEAD;
  • @OPTIONS = OPTIONS;
  • @POST = POST;
  • @PUT = PUT;
  • @PATCH = PATCH.

Neste nosso endpoint não utilizaremos todas essas anotações, apenas as equivalentes aos principais verbos: DELETE, GET, POST e PUT.

Ao realizar este procedimento, a classe ficará com o seguinte código:

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import br.com.treinaweb.model.Pessoa;
import br.com.treinaweb.repositories.PessoaRepository;

@Path("/pessoa")
public class PessoaResource {

    private PessoaRepository _repositorio = new PessoaRepository();

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Pessoa> get() {
        return _repositorio.GetAll();
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Pessoa getById(@PathParam("id") int id) {
        return _repositorio.Get(id);
    }

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(Pessoa pessoa)
    {
        try{
            _repositorio.Add(pessoa);
            return Response.status(Response.Status.CREATED).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }

    @PUT
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response put(@PathParam("id") int id, Pessoa pessoa)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            pessoa.setId(id);
            _repositorio.Edit(pessoa);
            return Response.status(Response.Status.OK).entity(pessoa).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }

    @DELETE
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response delete(@PathParam("id") int id)
    {
        Pessoa p = _repositorio.Get(id);
        if(p == null)
            return Response.status(Response.Status.NOT_FOUND).build();

        try{
            _repositorio.Delete(id);
            return Response.status(Response.Status.OK).build();
        }
        catch(Exception ex)
        {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
        } 
    }
}

Repare que os métodos também definem o tipo de conteúdo que irão produzir/retornar:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Pessoa> get() {
  return _repositorio.GetAll();
}

E o tipo que irão consumir:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response post(Pessoa pessoa)
{
  try{
    _repositorio.Add(pessoa);
    return Response.status(Response.Status.CREATED).entity(pessoa).build();
  }
  catch(Exception ex)
  {
    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
  } 
}

O Jersey se encarregará de converter o objeto retornado para JSON e o JSON recebido para o tipo do objeto indicado. Mas para que esta a conversão seja possível, é necessário definir a dependência abaixo (caso não esteja definida):

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-binding</artifactId>
</dependency>

Pronto, a nossa aplicação já pode ser utilizada.

Testando a aplicação

Caso esteja acompanhando este artigo e se o seu projeto foi criado com base o arquetipo jersey-quickstart-grizzly2, você pode compilar os códigos pelo Maven:

mvn clean compile

E executar o servidor embutido da aplicação:

mvn exec:java

Indiferente da forma que a aplicação é executada, poderemos interagir com o nosso endpoint:

POST:

Tela no Postman, mostrando o exemplo de uma requisição POST para o endpoint "http://localhost:8080/pessoa"

GET:

Tela no Postman, mostrando o exemplo de uma requisição GET para o endpoint "http://localhost:8080/pessoa"

PUT:

Tela no Postman, mostrando o exemplo de uma requisição PUT para o endpoint "http://localhost:8080/pessoa/1

GET ID:

Tela no Postman, mostrando o exemplo de uma requisição GET para o endpoint "http://localhost:8080/pessoa/1"

DELETE:

Tela no Postman, mostrando o exemplo de uma requisição DELETE para o endpoint "http://localhost:8080/pessoa/1"

Java - Estrutura de dados - Parte 1
Curso de Java - Estrutura de dados - Parte 1
CONHEÇA O CURSO

Conclusão

A JAX-RS API é uma poderosa especificação que pode ser aplicada em uma aplicação facilmente graças a implementação do Jersey, se tornando uma ótima alternativa para o Spring Boot.

Desta forma, caso necessite criar uma aplicação RESTfull API no Java, não deixe de dar uma olhada nos recursos fornecidos pela JAX-RS API e o Jersey.

Você pode baixar a aplicação deste artigo no meu Github.

Principais IDEs para desenvolvimento Java

IDE ou Integrated Development Environment (Ambiente de Desenvolvimento Integrado) é um software que auxilia no desenvolvimento de aplicações. Desta forma, combinam ferramentas comuns em uma única interface gráfica do usuário (GUI).

No artigo anterior, exploramos algumas características, vantagens e desvantagens em sua utilização, porém podemos dizer que, para o desenvolvedor, é uma forma de criar aplicações de maneira mais rápida, uma vez que estas IDEs auxiliam em todo o processo de desenvolvimento de uma aplicação.

Além disso, as IDEs proveêm diversos benefícios, como a análise de todo o código a ser escrito para identificar bugs causados por um erro de digitação, autocompletam trechos de códigos, e etc.

Java - Fundamentos
Curso de Java - Fundamentos
CONHEÇA O CURSO

Sendo assim, veremos neste artigo as principais IDEs para desenvolvimento em Java.

Principais IDEs para desenvolvimento em Java

Eclipse

Logo do Eclipse

Lançada em 2001, possuindo como autor a IBM, sobre a licença EPL (Eclipse Public Licence), o Eclipse é uma IDE para desenvolvimento em Java que também suporta diversas outras linguagens apenas com a instalação de plugins (C/C++, PHP, Python, Kotlin, entre outras).

Dentre suas principais características podemos citar:

  • Multiplataforma: Pode ser executado nos diferentes sistemas operacionais (Windows, Linux e macOS);
  • Tecnologia baseada em plugins: Através da instalação de plugins, o desenvolvedor poderá incrementar as funcionalidades do Eclipse;
  • Pacotes de desenvolvimento: Podemos utilizar diversos pacotes de desenvolvimento para criar diferentes tipos de aplicações com Java (Web e Desktop);
  • Uso de SWT (Standard Widget Toolkit): Widget toolkit para uso com a plataforma Java;
  • Criação de aplicações gráficas multiplataforma: Com o Eclipse podemos criar interfaces gráficas para aplicações Java.

O Eclipse é uma excelente IDE, muito utilizada no mercado. Desta forma, seu uso facilita a criação de aplicações Java tanto para Desktop ou Web.

O download do Eclipse poderá ser realizado em seu próprio site.

NetBeans

Logo do NetBeans

O NetBeans é uma IDE gratuita e de código fonte aberto para desenvolvimento Java, porém extensível para diversas outras linguagens, como PHP, Python, JavaScript, etc.

Lançada em dezembro de 2000, o NetBeans é uma das principais IDEs para o desenvolvimento Java.

Inicialmente desenvolvido como um software proprietário, em 2010, ao ser adquirido pela Oracle, o NetBeans se tornou parte do ecossistema Java, alavancando ainda mais sua utilização e popularidade.

Porém, em 2016, a Oracle propôs mover o projeto NetBeans para um projeto aberto dentro da Apache, o chamando de Apache NetBeans.

Dentre suas principais características, podemos citar:

  • Multiplataforma: Podemos utilizar o NetBeans nos principais sistemas operacionais do mercado (Windows, Linux e macOS);
  • Melhor suporte ao Java: Por fazer parte do ecossistema do Java, é a IDE oficial e recomendada pela própria Oracle;
  • Criação de interfaces: Possui suporte para criação de interfaces para aplicações web, desktop e mobile.

IDE oficial para o desenvolvimento Java, seja ela Desktop ou Web.

Em seu site é possível baixar o NetBeans, além de visualizar suas características.

IntelliJ

Logo do IntelliJ

O IntelliJ IDE é uma das principais IDEs do mercado.

Criada pela Jetbrains, uma empresa especializada no desenvolvimento de IDEs, o IntelliJ teve um crescimento impressionante nos últimos anos.

Apesar de ter sido lançada em 2001, foi a partir de 2010 que a IDE começou a ser reconhecida no mercado.

Em 2014, a Google anunciou que o Android Studio, uma IDE baseada no IntelliJ IDE para criação de aplicações Android, seria a IDE oficial para o desenvolvimento Android. Com este anúncio, as IDEs desenvolvidas pela Jetbrains ganharam mais visibilidade.

Dentre suas principais características, podemos citar:

  • Assistente de código: Possui um ótimo assistente de código, autocompletando trechos de sentenças para facilitar a criação de aplicações;
  • Uso de plugins: É possível desenvolver em diferentes tecnologias com o IntelliJ (Python, Dart, etc) com o uso de plugins;
  • Suporte nativo ao Kotlin: Podemos desenvolver aplicações utilizando o Kotlin, linguagem baseada no Java criada pela própria Jetbrains.

O IntelliJ cresceu muito nos últimos anos, se tornando uma das principais IDEs para o desenvolvimento de aplicações Java.

O IntelliJ possui duas versões, a “Ultimate” que possui diversos recursos, como ferramentas de bancos de dados, suporte nativo ao Spring e detecção de duplicidades.

Java - Orientação a objetos
Curso de Java - Orientação a objetos
CONHEÇA O CURSO

Já sua versão “Community”, um pouco mais limitada, porém completa o bastante para os principais desenvolvedores do mercado.

O download do IntelliJ pode ser feito em seu site oficial.

Podemos concluir que…

Como sabemos, uma IDE facilita (e muito) o desenvolvimento de aplicações, independente da linguagem e tecnologia que utilizamos.

No Java, o número de IDEs é bem alto, por isso, precisamos estudar bem a ferramenta que será utilizada em nossos projetos.

Neste artigo vimos algumas das principais opções do mercado, que possuem features essenciais e que facilita o desenvolvimento de nossos projetos.

O que é Scala?

Desenvolvida em 2001 pelo cientista da computação Martin Odersky, a Scalable Language ou simplesmente “Scala” é uma linguagem multiparadigma que possui uma tipagem estática e implícita.

Moderna e multi-plataforma, foi desenvolvida para expressar padrões de programação comuns, de forma concisa, elegante e com tipagem segura.

De código aberto, é uma linguagem de programação relativamente nova, mas que já vem sendo bastante utilizada por empresas que precisam operar grandes volumes de dados, como, por exemplo, o Twitter, GitHub e a LinkedIn.

O que é uma linguagem multiparadigma?

Linguagens multiparadigmas são linguagens que suportam vários paradigmas de programação e os utilizam para a solução de diversos problemas.

Ou seja, são as diversas características que determinada linguagem possui que a intitula como uma linguagem multiparadigma. Os paradigmas existentes são:

  • Funcional;
  • Lógico;
  • Declarativo;
  • Imperativo;
  • Orientado a objetos;
  • Orientado a eventos.

Dentre diversas linguagens multiparadigmas, podemos citar:

  • Python;
  • C++;
  • Swift;
  • Ruby;
  • Scala, entre outras.
Python - Fundamentos
Curso de Python - Fundamentos
CONHEÇA O CURSO

Aqui no blog possuimos um artigo sobre “Linguagens e paradigmas de programação” para que você possa entender melhor sobre o tema.

De volta à Scala…

Características

Similar ao Java, a Scala possui diversas características que fazem com que a linguagem tenha se tornado uma das principais e mais utilizadas nos últimos anos.

Dentre diversas características podemos citar:

  • Possui tipagem estática;
  • Permite a fácil adição de novas bibliotecas ao código;
  • Por ser executado na JVM do Java, permite que código Scala seja executado no Java ou código Java executado no Scala;
  • É orientada à objetos;
  • Possui uma vasta variedade de bibliotecas nativas para manusear dados em escala, entre outras;

Exemplo de código

Abaixo veremos um exemplo de um código Scala:

package main
object Main {
  def main(args:Array[String]) {
    print("Bem-vindo à TreinaWeb!")
  }
} 

Podemos notar a partir do código acima, a familiaridade do código Scala em relação ao Java. Basicamente, ao ser executado irá imprimir o texto “Bem-vindo à TreinaWeb” no console. Uma das grandes diferenças da Linguagem Scala com relação ao Java, é que o “ponto e vírgula” (;) não é necessário para finalizar uma instrução.

Concluindo

Vimos neste artigo algumas das principais características da Linguagem Scala, que nos últimos anos tem se tornado uma linguagem muito utilizada no mercado.

Segundo o Stack Overflow, em 2019 a Linguagem Scala tem se tornada a “queridinha” à frente de diversas outras linguagens mais consolidadas, como podemos ver na imagem abaixo:

Como dito acima, principalmente utilizada por grandes empresas e para trabalhar com grande fluxo de dados, a Linguagem Scala é uma ótima alternativa que pode ser tornar uma excelente opção para seu estudos.
Em seu site é possível acessar toda a sua documentação, realizar seu download, acessar sua comunidade, entre outros.

Criando um executável para uma aplicação Java

Recentemente, conversando com um dos nossos alunos, ele comentou as dificuldades dele em relação a criação de um instalador para uma aplicação Java. Como atualmente a maioria das aplicações são web, há pouco material em relação a criação de instaladores para aplicações desktop.

Para suprir um pouco desta necessidade, criarei uma série de dois artigos explicando como gerar um arquivo executável de uma aplicação Java e como criar um instalador para este executável.

Como o título do artigo indica, neste primeiro irei abordar a criação de um executável.

Java - Fundamentos
Curso de Java - Fundamentos
CONHEÇA O CURSO

Gerando o arquivo jar da aplicação

jar (Java Archive) é um formato de arquivo compactado que geralmente é utilizado para agrupar arquivos de classes Java e recursos associados (imagens, bibliotecas, etc.). Caso uma das classes agrupadas neste arquivo definir um método main, ele funcionará quase como um executável, com a diferença que necessitará do JRE (ou JDK) para ser executado. Caso nenhuma classe possua um método main o arquivo jar funcionará como uma biblioteca de classes que pode ser referenciada em outros projetos.

Para este artigo iremos gerar um arquivo jar de uma aplicação JavaFX, assim ele funcionará como um executável. Esta aplicação é abordada no nosso curso de JDBC e você pode obter o código dele no nosso repositório no Github.

Como esta aplicação define acesso ao banco de dados, antes de gerar o arquivo jar é necessário alterar os dados de conexão. Estes dados devem ser alterados de forma que reflitam os dados que serão utilizados quando a aplicação for instalada em outra máquina.

Por exemplo, caso a aplicação utilize um banco de dados online, deve ser informado os dados deste banco. Se no momento da instalação da aplicação, o banco de dados também for instalado, então os dados devem refletir as configurações deste banco.

Neste caso a opção mais simples seria utilizar um banco de dados online, mas para demostrar a segunda alternativa, optarei por instalar o banco em conjunto com a aplicação. Desta forma, estou utilizando os dados abaixo:

urlConexao=jdbc:mysql://localhost:3306/appjavafx?useSSL=false&serverTimezone=UTC
usuarioConexao=appjavafx
senhaConexao=app1java2fx3

Com isso configurado, vamos gerar o jar da aplicação no Eclipse.

Nesta IDE, selecione o projeto no Package Explorer, tw-agenda-fx neste exemplo:

Clique com o botão direito do mouse e selecione a opção “Export“:

Será apresentada uma tela. Nela procure por jar:

Selecione a opção “Runnable JAR file” e clique em “Next“:

Nesta tela é necessário configurar o arquivo jar que será criado. Em “Launch configuration” selecionamos a configuração de execução da aplicação (ao ser executada pelo menos uma vez, o Eclipse já terá criado esta configuração). Já em “Export destination” é informado o local onde o arquivo jar será salvo.

Por fim, em “Library handling“, caso o projeto referencie outras bibliotecas é importante marcar a opção “Extract required libraries into generated JAR” ou “Package required libraries into generated JAR“. Assim, as classes das bibliotecas utilizadas pela aplicação, ou o arquivo JAR delas, serão adicionadas no arquivo jar gerado.

Para esta aplicação em questão, optarei pela opção “Package required libraries into generated JAR“.

Também repare que há um aviso que esta aplicação define argumentos para a JVM, mas este é um detalhe que pode ser ignorado.

Clique em Finish para gerar o arquivo jar:

Para verificar se está tudo certo com o arquivo testado, no prompt de comando execute o comando abaixo:

java -jar AppJavaFx.jar

Se a aplicação for iniciada é porque está tudo certo com ela.

Java - Banco de dados e JDBC
Curso de Java - Banco de dados e JDBC
CONHEÇA O CURSO

Gerando o arquivo EXE

EXE é a extensão dos arquivos executáveis do Windows. Para transformar um arquivo jar em exe é necessário utilizar alguma aplicação de terceiro. Existem algumas disponíveis, mas a mais simples e mais utilizado, além de ser gratuita é a Launch4j. Com esta aplicação é possível transformar o arquivo jar em um executável de Windows, Linux ou Mac.

Neste artigo focaremos na versão de Windows, mas o processo é igual para os demais ambientes.

O Launch4j pode ser baixado no seu site: http://launch4j.sourceforge.net/

Após instalá-lo e executá-lo, será apresentada a tela abaixo:

Esta ferramenta possui muitas funcionalidades, mas aqui vamos focar apenas nos pontos necessários para gerar o executável. Na primeira tela informe o caminho do arquivo exe, do jar e um ícone:

E em JRE iremos informar o caminho do JRE:

O JRE informado acima é uma versão que gerei utilizando a ferramenta jlink e é importante marcar a opção “Only use private JDK runtimes“. Com isso o Launch4j configurará o executável a utilizar apenas este JRE e ele poderá ser incluído no instalador da aplicação. Por isso o caminho acima deve ser relativo a localização do executável.

Caso não queira gerar um JRE, você também pode informar um JDK.

Após a configuração, clique na seta para gerar o executável. Ou o salve em um arquivo xml e gere com a aplicação launch4jc:

launch4jc config.xml

Para verificar se tudo está correto, você pode executar o executável gerado. Se a aplicação for exibida é porque o arquivo foi criado corretamente.

Com este executável criado, já poderemos criar o instalador, que abordarei na segunda parte desta série. Então até lá!

É possível usar Kotlin no back-end?

Nos últimos anos, várias linguagens de programação, como Python e as linguagens funcionais em especial, começaram a ser mais e mais adotadas pelo mercado em geral. Uma destas linguagens que começou a se tornar mais popular, apesar de uma história peculiar, foi o Kotlin, a linguagem de programação da JetBrains.

Muitos conhecem o Kotlin como a linguagem padrão para desenvolvimento na plataforma Android, já que a Google trocou o Java pelo Kotlin em 2017. Porém, poucas pessoas sabem que o Kotlin pode também ser utilizado no desenvolvimento back-end.

Antes de tudo: o que vem a ser o Kotlin?

O Kotlin é uma linguagem de programação completamente open source, multiplataforma e multiparadigma com forte influência de C#, Scala, Groovy e JavaScript. Softwares escritos em Kotlin podem ser compilados para três diferentes plataformas: para a JVM, rodando sob a infraestrutura da plataforma Java; para JavaScript, sendo possível compilar código Kotlin e gerar código inteiramente JavaScript; e para código nativo, através do LVVM. O LVVM é basicamente uma biblioteca modular de compiladores e ferramentas escritos em C++ que permitem a criação de compiladores. Embora o Kotlin ofereça todas essas possibilidades, seu uso mais comum ocorre no processo de compilação e execução na JVM, utilizando toda a infraestrutura da plataforma Java.

Kotlin com Android - Primeiros Passos
Curso de Kotlin com Android - Primeiros Passos
CONHEÇA O CURSO

O Kotlin foi criado em 2010 por Andrey Breslav, engenheiro de software da JetBrains. A JetBrains é a empresa responsável por excelentes ferramentas de desenvolvimento, como o IntelliJ IDEA, PhpStorm, WebStorm e Resharper. Andrey Breslav deu esse nome à linguagem por se tratar do nome de uma pequena ilha russa. A ideia foi seguir a mesma filosofia do nome do Java, já que Java também é o nome de uma pequena ilha na Indonésia. Embora o desenvolvimento da linguagem tenha iniciado em 2010, a primeira versão estável oficial foi lançada somente em 2016.

Segundo a JetBrains, o Kotlin foi criado para ser uma linguagem completamente voltada ao mercado e completamente interoperável com o Java (já que ambos são compilados para bytecode, código intermediário este que é executado pela JVM). Porém, a ideia é que o Kotlin solucionasse alguns pontos de design que eram considerados problemáticos no Java. Por isso, o Kotlin adota recursos de linguagem muito interessantes, como uma sintaxe sensivelmente menos ruidosa e verborrágica, a incorporação natural de conceitos de linguagens funcionais (principalmente com relação à aspectos de imutabilidade) e a proteção natural ao “erro de um bilhão de dólares”: as referências nulas, ou o famoso NullPointerException. O Kotlin possui uma maneira peculiar que faz com que as chances de ponteiros nulos sejam drasticamente reduzidas.

A sintaxe do Kotlin é de fato muito mais concisa, direta e expressiva. Enquanto um tradicional “hello world” em Java ficaria da seguinte maneira…

package br.com.treinaweb;

class HelloWorld 
{ 
    public static void main(String args[]) 
    { 
        System.out.println("Hello, World"); 
    } 
}

O mesmo “hello world” em Kotlin poderia ser escrito da seguinte maneira:

fun main() {
    println("Hello, World")
}

Atualmente, o Kotlin é patrocinado pela Kotlin Foundation, uma organização sem fins lucrativos formada pela JetBrains e pela Google.

O Kotlin pode ser usado no backend?

Sim, o Kotlin pode ser utilizado para desenvolvimento backend, além do desenvolvimento para Android. A interoperabilidade com o Java permite que você utilize os tradicionais e poderosos frameworks Java, como Spring e Hibernate, em conjunto com o Kotlin de maneira praticamente transparente. Isso permite obter toda a extensibilidade e maturidade característica dos frameworks e bibliotecas Java através de uma linguagem muito menos verbosa, com uma API mais agradável e com um design mais moderno.

De maneira geral, utilizar bibliotecas Java em conjunto com o Kotlin é algo praticamente transparente, sendo idêntico à utilização do Java. Porém, algumas bibliotecas podem ir contra alguns princípios da filosofia do Kotlin. Um exemplo clássico desse “choque” é o Hibernate sendo utilizado em um projeto baseado no Kotlin. Um dos princípios do design do Kotlin é a imutabilidade. O grande problema é que o Hibernate preza justamente pelo contrário, ou seja, pela mutabilidade das entidades que são manipuladas. Não é impossível utilizar o Hibernate junto com o Kotlin, muito pelo contrário… Mas, nessa situação por exemplo, pode ser necessário abrir mão de algumas ideias legais de design que o Kotlin tem.

O Kotlin vem sendo adotado com muita força pelo mercado desde 2017. Hoje, grandes empresas como Pinterest, Uber, Amazon, Prezi, Slack, além da própria Google e da JetBrains, utilizam o Kotlin para desenvolvimento de aplicações internas e externas. Isso têm feito com que frameworks e bibliotecas específicas para o Kotlin também surjam com frequência. Hoje temos, por exemplo, o Ktor, um framework Kotlin para desenvolvimento web; e o Exposed, um framework SQL da JetBrains completamente baseado no Kotlin. Além disso, uma pesquisa do StackOverflow elegeu o Kotlin como a segunda linguagem de programação mais amada pela comunidade. Estes dados mostram como o Kotlin hoje é uma opção que deve ser considerada seriamente ao se desenvolver aplicações multi-propósito, além de aplicações para Android.

5 motivos para estudar Java

Quando começamos a estudar sobre desenvolvimento de software ou quando desejamos aprender uma nova plataforma de desenvolvimento, é normal que tenhamos dúvidas sobre a partir de qual linguagem começar. Fatalmente, uma das primeiras opções que acaba sendo considerada como uma possibilidade é o Java.

O Java é uma linguagem de programação majoritariamente orientada a objetos multiplataforma. Com o Java, é possível desenvolver toda uma gama de soluções que vão desde aplicações embarcadas até aplicações web e mobile. Sendo assim, será que ainda vale a pena estudar Java? Este artigo irá lhe apresentar cinco fortes motivos para que você estude a plataforma de desenvolvimento da Oracle.

Java - Fundamentos para Web
Curso de Java - Fundamentos para Web
CONHEÇA O CURSO

1. O Java é uma das linguagens mais populares em todo mundo

O Java está entre as três linguagens de programação mais utilizadas no mundo atualmente, alternando posições junto ao C e C++. A versatilidade e o aspecto multiplataforma do Java justificam a utilização da plataforma em uma gama enorme de soluções.

O TIOBE Index é um índice que classifica diferentes linguagens de programação mensalmente de acordo com o número de pesquisas pela linguagem nos sites de busca. No ranking de setembro de 2019, é possível notar que o Java está em primeiro lugar, posição esta que é ocupada pelo Java já há algum tempo.

Na imagem acima, está o gráfico com as cinco primeiras linguagens de setembro de 2019, o que inclui o Java. Observando o gráfico, é possível constatar a força do Java como plataforma de desenvolvimento frente a outras linguagens.

2. Existem várias oportunidades de emprego para desenvolvedores Java

A popularidade do Java como linguagem reflete diretamente em um ponto: a quantidade de vagas disponíveis para profissionais que trabalham com a plataforma. Existem várias vagas de emprego para desenvolvedores Java no Brasil e no exterior, vagas estas por vezes com salários e benefícios muito atrativos.

Uma rápida pesquisa por “Java” limitada ao Brasil no painel de vagas do LinkedIn trouxe mais de 6500 resultados. Entre as vagas retornadas pela pesquisa, apareceram oportunidades em empresas relevantes no meio de tecnologia da informação, como CI&T, Guiabolso, Sensedia, PagSeguro e Accenture. Também existiam vagas para todos os níveis profissionais, de estágios até mesmo para desenvolvedores nível sênior e especialistas.

3. A comunidade Java é muito grande e ativa

O Java apresenta uma das maiores comunidades open source. Somente a Apache Software Foundation, uma das maiores organizações voltadas a open source no mundo, ajuda a manter atualmente 235 projetos voltados ao Java em vertentes que vão desde ferramentas de build a servidores como o Tomcat.

Uma pesquisa por “java” no GitHub revela a existência de mais de 700 mil repositórios abertos fundamentados no Java, totalizando mais de 18 milhões de commits. Várias outras soluções de outras plataformas acabaram se baseando em soluções desenvolvidas inicialmente para o Java, como o NHibernate para linguagens baseadas em .NET, framework este fortemente inspirado no Hibernate do Java. E isso tudo sem contar ainda com o Android, o sistema operacional mobile utilizado em mais de 75% de dispositivos do mundo e que é fundamentado no Java. Estes dados deixam muito clara a força do Java como plataforma de desenvolvimento no mundo nos dias de hoje.

4. O Java está presente em uma infinidade de soluções diferentes

O aspecto multiplataforma do Java o fez ficar popular em uma série de ambientes e configurações distintas. É possível criar aplicações embarcadas com o Java através do Java Embedded, criar aplicações mobile para o Android através do Android SDK, aplicações desktop com o Java SE e aplicações web e corporativas de larga escala através do Java EE.

Desenvolvedores Java ainda podem contar com uma infinidade de alternativas da própria comunidade para o desenvolvimento dos mais variados tipos de aplicações, como o Spring, Quartz, Vaadin e JasperReports. Isso tudo faz com que o Java seja uma alternativa para o desenvolvimento de aplicações eficientes para os mais variados ambientes de execução, compartilhando uma grande base de código entre estes diferentes ambientes.

Desenvolvedor Java Júnior
Formação: Desenvolvedor Java Júnior
A formação Desenvolvedor Java nível Júnior da TreinaWeb tem como objetivo fornecer uma introdução ao desenvolvimento através do Java e todo o ecossistema para desenvolvimento da Oracle. Nesta formação, são abordados tópicos como o desenvolvimento da capacidade analítica, o paradigma orientado a objetos, a preparação do ambiente de desenvolvimento para o Java através do Eclipse e o controle de versão de código através do Git e do GitHub. Além disso, também são abordados aspectos mais essenciais da linguagem e estruturas importantíssimas dentro do ecossistema do Java, como a Stream API, que auxilia a lidar com coleções de dados; a implementação das estruturas de dados mais tradicionais como listas, filas e pilhas; e a API de coleções.
CONHEÇA A FORMAÇÃO

5. O Java está em evolução

Apesar de um período de estagnação pós a aquisição da Sun pela Oracle, hoje o Java é uma linguagem moderna e aderente aos novos paradigmas de desenvolvimento. Além da forte veia orientada a objetos da linguagem, é perfeitamente possível escrever código em outros paradigmas, como o paradigma funcional e o paradigma orientado a aspectos. Novas e poderosas APIs foram implementadas nas últimas versões do Java, como a API de Streams (que dá uma “cara” mais “funcional” para a linguagem), a API Date and Time e a API de I/O.

O Java hoje também suporta recursos naturais a qualquer linguagem moderna que está entre as mais utilizadas do mundo, como expressões lambda, inferência de tipagem, princípios de pattern matching e template strings. O Java hoje também possui um ciclo mais acelerado de lançamento de versões, sendo que uma nova versão é lançada a cada cerca de 6 meses. Isso mostra o quão acelerado é o ritmo de inovação e transformação da linguagem.

Documentando uma API Spring Boot com o Swagger

Documentar uma aplicação é um ponto essencial de qualquer projeto, muitas vezes negligenciado. Quando se trabalha em equipe, uma má documentação pode dificultar (e muito) o trabalho dos demais desenvolvedores.

Por já ter muita dificuldade com isso, procuro sempre documentar as aplicações que estou trabalhando. No ano passado, mostrei como documentar uma aplicação ASP.NET Core Web API com o Swagger. Por gostar muito dele, nesta semana quando precisei documentar API Spring Boot, não tive dúvidas que ele é era a escolha correta.

A partir da experiência que tive neste processo, aqui vou lhe mostrar como documentar uma API Spring Boot com o Swagger.

Relembrando características do Swagger

Caso não tenha visto o meu artigo de ASP.NET Core Web API com o Swagger, vamos relembrar alguns detalhes do Swagger:

  • O Swagger é uma aplicação open source que auxilia os desenvolvedores a definir, criar, documentar e consumir APIs REST;
  • É composto de um arquivo de configuração, que pode ser definido em YAML ou JSON;
  • Fornece ferramentas para: auxiliar na definição do arquivo de configuração (Swagger Editor), interagir com API através das definições do arquivo de configuração (Swagger UI) e gerar templates de código a partir do arquivo de configuração (Swagger Codegen).

Como é possível notar, o ponto mais importante é o arquivo de configuração do Swagger. É nele que a API é documentada.

Criar este arquivo na mão pode ser um trabalho hercúleo, felizmente existem algumas bibliotecas do Java que facilitam este processo. No caso de uma aplicação Spring, a melhor opção é a biblioteca SpringFox.

Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Adicionando a biblioteca SpringFox em uma aplicação API Spring Boot

Para este artigo, estou utilizando a aplicação mostrada no meu último artigo, onde ensinei a criar uma aplicação REST API no Spring Boot. Você também pode ver a aplicação que criei no meu Github.

Com a aplicação criada, temos que adicionar a dependência do SpringFox nela:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

Para interagir com a configuração, também é necessário adicionar a dependência que fornece o Swagger UI:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Agora para que o arquivo de especificação da API seja criado, é necessário habilitar o Swagger na aplicação. Para isso, adicione nela uma classe chamada SwaggerConfig, com o conteúdo abaixo:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
          .select()
          .apis(RequestHandlerSelectors.any())
          .paths(PathSelectors.any())
          .build();
    }
}

No Spring Boot o Swagger é ativado através da anotação @EnableSwagger2. O Docket que estamos definindo no nosso bean nos permite configurar aspectos dos endpoints expostos por ele.

Nos métodos apis() e paths() definimos que todas as apis e caminhos estarão disponíveis. Com isso através de reflection a biblioteca já consegue obter os endpoints definidos na aplicação.

Ao executá-la, o Swagger UI estará disponível em /swagger-ui.html:

Note que ele está pegando informações de todos os controllers definidos na aplicação até o de erro padrão do Spring Boot. Caso queria evitar isso, é possível configurar o Swagger.

Customizando o Swagger na aplicação Spring Boot

Todas as configurações do Swagger devem ser definidas na classe SwaggerConfig. No momento ela contém apenas as configurações padrão.

Indicando código e mensagem de retorno do Swagger

Nas configurações padrão, o Swagger irá indicar que os endpoints retornam os códigos 200, 201, 204, 401, 403 e 404. Caso a sua aplicação não retorne todos esses códigos, você pode especificar quais códigos ela retorna com o método globalResponseMessage.

O globalResponseMessage recebe por parâmetro o método HTTP e uma lista de ResponseMessage que indica quais códigos e mensagens o método retorna. Para que a aplicação fique modular, vamos definir os ResponseMessage em um método:

private List<ResponseMessage> responseMessageForGET()
{
    return new ArrayList<ResponseMessage>() {{
        add(new ResponseMessageBuilder()
            .code(500)
            .message("500 message")
            .responseModel(new ModelRef("Error"))
            .build());
        add(new ResponseMessageBuilder()
            .code(403)
            .message("Forbidden!")
            .build());
    }};
}

E ele será passado como parâmetro do globalResponseMessage:

public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.any())
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}

Agora, ficará indicado que todos os endpoints GET retornam o código 200 (que é sempre padrão) e os códigos 500 e 403:

Note na imagem acima, que em “Response content type” é indicado o valor /. Para alterar este ponto, basta indicar no seu endpoint o tipo do conteúdo que ele produz com o atributo produces:

@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
    return _pessoaRepository.findAll();
}

Com o atributo consumes é possível especificar o tipo de conteúdo que ele consome:

@RequestMapping(value = "/pessoa", method =  RequestMethod.POST, produces="application/json", consumes="application/json")
public Pessoa Post(@Valid @RequestBody Pessoa pessoa)
{
    return _pessoaRepository.save(pessoa);
}

Também é possível especificar os códigos e as mensagens de retorno diretamente no controller com as anotações @ApiResponses e @ApiResponse:

@ApiResponses(value = {
    @ApiResponse(code = 200, message = "Retorna a lista de pessoa"),
    @ApiResponse(code = 403, message = "Você não tem permissão para acessar este recurso"),
    @ApiResponse(code = 500, message = "Foi gerada uma exceção"),
})
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
    return _pessoaRepository.findAll();
}

Estas configurações serão válidas para os endpoints onde estiverem definidos. Onde não estiver, será utilizado o padrão.

Também é possível utilizar a anotação @ApiOperation para descrever o endpoint:

@ApiOperation(value = "Retorna uma lista de pessoas")
@ApiResponses(value = {
    @ApiResponse(code = 200, message = "Retorna a lista de pessoa"),
    @ApiResponse(code = 403, message = "Você não tem permissão para acessar este recurso"),
    @ApiResponse(code = 500, message = "Foi gerada uma exceção"),
})
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
    return _pessoaRepository.findAll();
}

E caso queira descrever o model, pode ser utilizado a anotação @ApiModelProperty:

@Entity
public class Pessoa
{
    @ApiModelProperty(value = "Código da pessoa")
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ApiModelProperty(value = "Nome da pessoa")
    @Column(nullable = false)
    private String nome;

    public long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public void setId(long id) {
        this.id = id;
    }
}
Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Filtrando os endpoints da API Spring Boot no Swagger

No momento o Swagger está listando todos os endpoints definidos na aplicação, inclusive os padrões do Spring Boot, como o de erro. Mas na configuração dele, no método apis podemos utilizar a classe RequestHandlerSelectors para filtrar quais serão considerados com base no pacote ou anotação. Por exemplo, para que seja listada apenas os endpoints definidos pela nossa aplicação, utilizamos o método basePackage() desta classe:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
       .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}

Com a classe PathSelectors, também é possível filtrar os caminhos aceitos para os endpoints, como:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
       .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.ant("/api/*"))
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}

Acima estamos utilizando o método ant() para adicionar o filtro, mas também seria possível definir uma expressão regular com o método regex().

Especificando a autenticação da aplicação no Swagger

Uma das principais vantagens da Swagger UI é a possibilidade de testar os endpoints diretamente pela interface. Quando a aplicação define alguma autenticação, é necessário configurar isso, para que o SpringFox também especifique isso, e mesmo endpoints protegidos sejam testáveis.

Esta especificação é realizada com os métodos securitySchemes e securityContexts. No primeiro é definido o tipo de autenticação (no momento os suportados são: ApiKey, BasicAuth e OAuth). Já no segundo são especificadas particularidades desta autenticação, como os escopos e endpoints que necessitam de autenticação.

No caso deste exemplo, será definida uma autenticação ApiKey:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET())
        .securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name())))
        .securityContexts(Arrays.asList(securityContext()));
}

Já o securityContext() conterá o código abaixo:

private SecurityContext securityContext() {
    return SecurityContext.builder()
        .securityReferences(defaultAuth())
        .forPaths(PathSelectors.ant("/pessoa/**"))
        .build();
}

List<SecurityReference> defaultAuth() {
    AuthorizationScope authorizationScope
        = new AuthorizationScope("ADMIN", "accessEverything");
    AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
    authorizationScopes[0] = authorizationScope;
    return Arrays.asList(
        new SecurityReference("Token Access", authorizationScopes));
}

Onde é definido que os endpoint de /pessoa necessitam de autenticação.

Agora ao executar a aplicação e acessar o Swagger, haverá o botão “Authorize”:

Ao clicar nele, será exibido a tela onde a forma de acesso deve ser informada:

Na aplicação deste exemplo, a autenticação é TOKEN, então ao informá-lo e clicar em Authorize, será possível realizar solicitações nos endpoints que necessitam de autenticação.

Descrevendo a API no Swagger

Por fim, é possível adicionar algumas informações da api no método apiInfo:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
        .paths(PathSelectors.any())
        .build()
        .useDefaultResponseMessages(false)
        .globalResponseMessage(RequestMethod.GET, responseMessageForGET())
        .securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name())))
        .securityContexts(Arrays.asList(securityContext()))
        .apiInfo(apiInfo());
}

private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
            .title("Simple Spring Boot REST API")
            .description(""Um exemplo de aplicação Spring Boot REST API"")
            .version("1.0.0")
            .license("Apache License Version 2.0")
            .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0"")
            .contact(new Contact("Wladimilson", "https://treinaweb.com.br", "contato@treinaweb.com.br"))
            .build();
}

O resultado será:

Você pode ver a aplicação deste artigo no meu Github.

Devo usar o Swagger em uma aplicação API Spring Boot?

Neste artigo vemos que está claro que o Swagger é uma ótima forma de documentar APIs REST e com a biblioteca SpringFox, caso esteja desenvolvendo uma API Spring Boot, a geração da especificação do Swagger é facilitada. Assim, sempre que possível documente as suas aplicações utilizando esta ferramenta. Tenho certeza que isso irá facilitar o trabalho de todos que consomem a sua API, incluindo você.

Criando uma API REST com o Spring Boot

Até pouco tempo o Java possuia um ciclo atualizações grande, entre o lançamento da versão 5 (07/2004) até a versão 9 (07/2017) foram treze anos. A partir desta nona versão o ciclo foi alterado e agora há lançamento de novas versões duas vezes ao ano, nos meses de março e setembro, por isso que entre a versão 9 e a 12 (a mais atual), o intervalo é de 1 ano e meio.

Este grande intervalo entre as antigas versões era necessário devido ao seu complexo processo de atualização. Visando estabilidade e segurança, cada versão passava por vários testes e interações até serem liberados para os usuários. É preciso dizer que infelizmente nem sempre a versão lançada fornecia estabilidade e segurança. Qualquer usuário que já tenha instalado o Java no seu computador sabe que mensalmente havia alguma atualização de segurança.

Tudo isso dificultava a adição de novos recursos na linguagem, o que permitiu o destaque de alguns frameworks. Dentre vários, o que mais ganhou os holofotes foi o Spring. Criado para aplicações Java EE, hoje o Spring é um projeto que contém várias bibliotecas, de segurança (Spring Security) à big data (Spring XD).

Dentre todos os projetos abaixo da asa do Spring, o que mais se destaca é o Spring Boot.

Java - Criação de aplicações web com Spring Boot
Curso de Java - Criação de aplicações web com Spring Boot
CONHEÇA O CURSO

Spring Boot

Todo desenvolvedor Java sabe que as vezes configurar uma aplicação pode ser um trabalho hercúleo. Às vezes são horas de configurações e apenas alguns minutos de codificação.

O Spring Boot veio para resolver esta situação. Utilizando como base o Core do Spring, o Spring Boot trabalha seguindo convenções e configurações padrão para abstrair o máximo possível das configurações necessárias de uma aplicação Spring.

Basta você definir qual tipo de aplicação deseja criar, escolher o starter apropriado, que o Spring se encarregará as configurações básicas necessárias da aplicação que escolheu.

O starter escolhido contém todas as dependências que sua aplicação necessita para funcionar. Então não é necessário nem se preocupar com as dependências do projeto.

Mesmo funcionando através de convenção, caso queira, é possível customizar todas as configurações da aplicação, de forma simples ou complexa, dependendo do que pretende fazer.

O maior benefício deste framework é que permite o desenvolvedor se preocupar com o ponto mais importante da aplicação, as regras de negócio.

Para demonstrar esta facilidade, neste artigo veremos como definir uma API REST utilizando este framework.

Criando a aplicação

Existem algumas formas de criar uma aplicação Spring Boot. Ela pode ser criada no Eclipse, utilizando a IDE Spring Tool Suíte, que também é fornecida como plug-in para o Eclipse, Visual Studio Conde e Atom. Ou mesmo no site Spring Initializr.

No momento estou utilizando uma máquina Mac OS X com o Visual Studio Code instalando, assim vou criar a aplicação inicial utilizando a extensão deste editor fornecida pela Microsoft:

Ao criar, a aplicação terá a estrutura abaixo:

Neste projeto os pontos mais importantes são o arquivo pom.xml, que contém o starter web e das dependências que definimos:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>br.com.treinaweb</groupId>
    <artifactId>springbootapi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

E o arquivo DemoApplication.java que contém o nosso método main:

package br.com.treinaweb.springbootapiexemplo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Caso não esteja habituado com o Spring Boot, pode estranhar a criação de um método main em uma aplicação web. Isso ocorre porque este método será responsável por carregar todas dependências embutidas na aplicação, isso inclui o servidor web (o Tomcat).

Desta forma, para esta aplicação web não é necessário definir um arquivo war e adicioná-lo no servidor.

Caso execute a aplicação agora, notará que o servidor embutido será iniciado e a aplicação já poderá ser utilizada. Só que no momento ela não possui nada.

Criando a entidade e repositório

Uma das conversões que o Spring Boot adota é que ele reconhece como componentes da aplicação, todas as classes definidas no mesmo pacote da classe que contém o método main ou em um package “abaixo” do package.

Ou seja, como a nossa classe está definida no pacote br.com.treinaweb.springbootapiexemplo, qualquer classe definida nele ou em um “subpackage” dele, será reconhecida pelo Spring Boot. Desta forma, vamos definir a nossa entidade no pacote br.com.treinaweb.springbootapiexemplo.entity:

package br.com.treinaweb.springbootapi.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Pessoa
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(nullable = false)
    private String nome;

    public long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public void setId(long id) {
        this.id = id;
    }
}

Já o repositório ficará no pacote br.com.treinaweb.springbootapi.repository:

package br.com.treinaweb.springbootapi.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import br.com.treinaweb.springbootapi.entity.Pessoa;

@Repository
public interface PessoaRepository extends JpaRepository<Pessoa, Long> { }

Note que para o repositório foi necessário apenas estender da interface JpaRepository do Spring Data. Esta interface possui métodos para as operações padrão de um CRUD.

Aproveitando que definimos o repositório, vamos configurar o banco de dados. Esta configuração deve ser definida no arquivo application.properties:

## Database Properties
spring.datasource.url = jdbc:mysql://localhost:3306/treinaweb?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update

Alguns comportamentos do Spring Boot podem ser alterados com configurações neste arquivo. Vamos deixar apenas as configurações do banco.

Controller

Por fim, vamos definir o controller da nossa aplicação. Assim como antes, ele será definido em um sub-package do package padrão da aplicação:

package br.com.treinaweb.springbootapi.controller;

import java.util.List;
import java.util.Optional;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import br.com.treinaweb.springbootapi.entity.Pessoa;
import br.com.treinaweb.springbootapi.repository.PessoaRepository;

@RestController
public class PessoaController {
    @Autowired
    private PessoaRepository _pessoaRepository;

    @RequestMapping(value = "/pessoa", method = RequestMethod.GET)
    public List<Pessoa> Get() {
        return _pessoaRepository.findAll();
    }

    @RequestMapping(value = "/pessoa/{id}", method = RequestMethod.GET)
    public ResponseEntity<Pessoa> GetById(@PathVariable(value = "id") long id)
    {
        Optional<Pessoa> pessoa = _pessoaRepository.findById(id);
        if(pessoa.isPresent())
            return new ResponseEntity<Pessoa>(pessoa.get(), HttpStatus.OK);
        else
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    @RequestMapping(value = "/pessoa", method =  RequestMethod.POST)
    public Pessoa Post(@Valid @RequestBody Pessoa pessoa)
    {
        return _pessoaRepository.save(pessoa);
    }

    @RequestMapping(value = "/pessoa/{id}", method =  RequestMethod.PUT)
    public ResponseEntity<Pessoa> Put(@PathVariable(value = "id") long id, @Valid @RequestBody Pessoa newPessoa)
    {
        Optional<Pessoa> oldPessoa = _pessoaRepository.findById(id);
        if(oldPessoa.isPresent()){
            Pessoa pessoa = oldPessoa.get();
            pessoa.setNome(newPessoa.getNome());
            _pessoaRepository.save(pessoa);
            return new ResponseEntity<Pessoa>(pessoa, HttpStatus.OK);
        }
        else
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    @RequestMapping(value = "/pessoa/{id}", method = RequestMethod.DELETE)
    public ResponseEntity<Object> Delete(@PathVariable(value = "id") long id)
    {
        Optional<Pessoa> pessoa = _pessoaRepository.findById(id);
        if(pessoa.isPresent()){
            _pessoaRepository.delete(pessoa.get());
            return new ResponseEntity<>(HttpStatus.OK);
        }
        else
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

Na classe acima, é importante destacar alguns detalhes:

  • A anotação @RestController permite definir um controller com características REST;
  • A anotação @Autowired delega ao Spring Boot a inicialização do objeto;
  • A anotação @RequestMapping permite definir uma rota. Caso não seja informado o método HTTP da rota, ela será definida para todos os métodos.
  • A anotação @PathVariable indica que o valor da variável virá de uma informação da rota;
  • A anotação @RequestBody indica que o valor do objeto virá do corpo da requisição;
  • E a anotação @Valid indica que os dados recebidos devem ser validados.

Pronto, a nossa aplicação pode ser utilizada.

Para testar os endpoints, vamos utilizar o Postman:

Post

Get

Get id

Put

Conclusão

O Spring Boot facilita muito a criação de aplicações web em Java, caso seja um desenvolvedor desta linguagem, é quase uma obrigação conhecê-lo.

Você pode baixar a aplicação deste artigo no meu Github.

© 2004 - 2019 TreinaWeb Tecnologia LTDA - CNPJ: 06.156.637/0001-58 Av. Paulista, 1765, Conj 71 e 72 - Bela Vista - São Paulo - SP - 01311-200