Fila de Mensagens Mortas

O que acontece quando há uma exceção em um recebedor registrado em uma fila?

Por exemplo:

@MessageDriven(activationConfig = {
  @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/FILA"),
  @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class RecebedorMDB implements MessageListener {
  public void onMessage(Message msg) {
    throw new RuntimeException("Erro!");
  }
}

A classe acima é um Message Driven Bean (MDB) registrado em FILA que lança uma exceção não checada ao receber uma mensagem.

Se verificarmos a fila, a mensagem foi consumida. Só que aconteceu um erro!

Mais

Pacote java.time com JAX-RS no Wildfly 8.2

Temos a seguinte API de lista de tarefas feita com JAX-RS:

@Path("/tarefas")
public class TarefasResource {
  @Inject
  private TarefasRepository repo;

  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public Response nova(Tarefa t) throws URISyntaxException {
    repo.cria(t);
    return Response.created(new URI("/tarefas/" + t.getId()))
      .entity(t)
      .build();
  }

  @GET
  @Path("/{id}")
  @Produces(MediaType.APPLICATION_JSON)
  public Tarefa lista(@PathParam("id") Integer id) {
    return repo.busca(id);
  }
}

Uma tarefa tem as propriedades id, do tipo Integer descricao, do tipo String e data, do tipo LocalDate do pacote java.time:

public class Tarefa {
  private Integer id;
  private LocalDate data;
  private String descricao;
  //getters e setters...
}

A API de estilo REST define o recurso /tarefas. Para criar uma nova tarefa deve ser enviado um POST para a URI /tarefas com uma representação da tarefa em JSON. Para obter um JSON com a tarefa de id 1, deve ser enviado um GET para a URI /tarefas/1.

O código acima foi implantado em um Wildfly 8.2.0.Final.

Problemas ao serializar objetos do java.time de/para JSON

Mas há algo de estranho.

Ao enviarmos um GET para /tarefas/1, obtemos o seguinte JSON:

{
  "id": 1,
  "descricao": "Configurar JAX-RS",
  "data": {"year":2016,"month":"FEBRUARY","chronology":{"calendarType":"iso8601","id":"ISO"},"era":"CE","dayOfYear":56,"dayOfWeek":"THURSDAY","leapYear":true,"dayOfMonth":25,"monthValue":2}
}

A representação do LocalDate em JSON ficou gigantesca! Vários detalhes internos foram exibidos…

E se tentarmos enviar um POST para /tarefas com o JSON abaixo?

{
  "data": "2016-02-26",
  "descricao": "Configurar Wildfly"
}

O resultado será um erro 400 (Bad Request) com a seguinte mensagem:

com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDate] from String value ('2016-02-26'); no single-String constructor/factory method
 at [Source: io.undertow.servlet.spec.ServletInputStreamImpl@1c6dc29c; line: 1, column: 2] (through reference chain: br.com.alexandreaquiles.modelo.Tarefa["data"])

A mensagem de erro acima informa que o Jackson, a biblioteca de serialização JSON usada pelo Wildfly, não conseguiu transformar a String em um LocalDate.

Como fazer para ensinar o Jackson a trabalhar com um objeto do tipo LocalDate ou de outras classes do pacote java.time como se fossem Strings?

Melhorando a (de)serialização

O Wildfly 8.2.0.Final usa a versão 2.4.1 do Jackson, que tem a extensão jackson-datatype-jsr310, responsável por serializações mais interessantes de/para classes do pacote java.time.

Se estivermos usando o Maven, basta adicionar mais uma dependência:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>2.4.1</version>
</dependency>

Outras ferramentas de gerenciamento de dependência teriam configurações análogas. Caso não esteja usando nenhuma ferramenta do tipo, baixe o jar.

Ao testarmos novamente, os mesmos erros acontecem… Que chato!

É que precisamos configurar um ContextResolver, anotando-o com @Provider e registrando o módulo JSR310Module do Jackson:

@Provider
public class JacksonJavaTimeConfiguration implements ContextResolver<ObjectMapper> {
  private final ObjectMapper mapper;

  public JacksonJavaTimeConfiguration() {
    mapper = new ObjectMapper();
    mapper.registerModule(new JSR310Module());
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  }

  @Override
  public ObjectMapper getContext(Class<?> type) {
    return mapper;
  }
}

Note a configuração WRITE_DATES_AS_TIMESTAMPS setada para false. Se não fizermos isso, as datas são representadas como um array no JSON, ao invés de um texto.

Agora, se enviarmos um GET para /tarefas/1, obtemos:

{
  "id": 1,
  "descricao": "Configurar JAX-RS",
  "data": "2016-02-25";
}

O LocalDate passa a ser representado no JSON como uma String no formato ISO-8601.

Ao enviarmos o POST para /tarefas novamente, tudo dá certo! Recebemos um status 201 (Created).

Funciona em outro servidor de aplicação?

Infelizmente, não!

A grande questão é que nossa configuração foi feita para a biblioteca usada pelo Wildfly, o Jackson, inclusive em uma versão específica.

O Glassfish, por exemplo, usa a biblioteca MOXy para serialização de/para JSON. As configurações seriam diferentes…

Dependências do Java EE 7 no Maven

Um projeto que usa o Maven ganha um monte de facilidades, da compilação a geração dos entregáveis em apenas um comando.

Vamos supor que temos o seguinte pom.xml:

<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>

  <groupId>br.com.alexandreaquiles</groupId>
  <artifactId>exemplo</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>

  <dependencies>
    <!-- aqui ficam as dependências -->
  </dependencies>
</project>

Considere que nosso projeto tem a seguinte classe:

@Stateless
@Path("/produtos")
public class ProdutosResource {
  @Inject
  private EntityManager em;
  @GET
  public List<Produto> lista(){
    return em.createQuery("select p from Produto p", Produto.class).getResultList();
  }
}

Usamos algumas funcionalidades do Java EE 7 como EJBs (@Stateless), JPA (EntityManager) e JAX-RS (@Path e @GET).

A questão é: como devemos declarar as dependências dessas API do Java EE 7? Será que precisamos declarar cada API utilizada, uma a uma?

Dependência do Java EE 7

Não! Há uma dependência publicada no repositório central do Maven que disponibiliza as APIs do Java EE 7:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

Perceba o escopo provided acima. Estamos indicando que essas APIs devem ser utilizadas apenas para compilação e não precisam ser incluídas no entregável (WAR, no nosso caso).

É importante notar que passamos a depender apenas das APIs e não de nenhuma implementação. Isso é muito interessante se desejarmos mudar de servidor de aplicação. Podemos gerar o WAR e implantá-lo no Wildfly, no Glassfish e em qualquer servidor que implemente o Java EE 7.

Dependência do Java EE 7 Web Profile

Na verdade, a dependência acima é da versão Full do Java EE, fazendo com que possamos usar JMS, JCA, entre outros. No nosso caso, é suficiente depender do Web Profile, que possui menos funcionalidades mas tem o que precisamos. Então, poderíamos usar:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-web-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

Dependências mantidas pela JBoss

A empresa JBoss, que mantém os servidores de aplicação Wildfly e JBoss EAP e vários outros projetos, disponibiliza dependências alternativas para as APIs do Java EE 7.

Para ter como dependência as APIs do Java EE 7 Full:

<dependency>
  <groupId>org.jboss.spec</groupId>
  <artifactId>jboss-javaee-7.0</artifactId>
  <version>1.0.0.Final</version>
  <type>pom</type>
  <scope>provided</scope>
</dependency>

Já para as do Java EE 7 Web Profile:

<dependency>
  <groupId>org.jboss.spec</groupId>
  <artifactId>jboss-javaee-web-7.0</artifactId>
  <version>1.0.0.Final</version>
  <type>pom</type>
  <scope>provided</scope>
</dependency>

As dependências acima não são para a implementação do Wildfly ou do JBoss EAP. São dependências para as APIs do Java EE alternativas às vistas anteriormente.

Métodos default, colisão de interfaces e precedência de superclasses no Java 8

Métodos públicos e abstratos em Interfaces

Imagine que temos a interface Aquatico, que define um método move:

interface Aquatico {
	public abstract void move();
}

Se tivermos uma classe Navio que implementa essa interface mas não fornece uma implementação para o método move, teremos um erro de compilação:

class Navio implements Aquatico {
^
error: Navio is not abstract and does not override abstract method move() in Aquatico
}

Uma interface nos obriga a respeitar um contrato, ou seja, a assinatura (nome, parâmetros e retorno) de um ou mais métodos.

No Java, desde os primórdios, os métodos das interfaces são implicitamente public e abstract. Essa última palavra-chave indica que são métodos abstratos, ou seja, sem corpo definido e sem código, meros moldes a serem preenchidos.

Para o código da classe concreta Navio ser compilado com sucesso, temos que fornecer uma implementação para o método move. Por exemplo:

class Navio implements Aquatico {
	public void move() {
		System.out.println("Navega na água!");
	}
}

O código acima compila normalmente e podemos invocá-lo de maneira polimórfica, como a seguir:

public class Programa {
	public static void main(String[] args){
		Aquatico titanic = new Navio();
		titanic.move(); //imprime "Navega na água!"
	}
}

Métodos com mesma assinatura em Interfaces diferentes

Considere que temos uma interface Terrestre, conforme abaixo:

interface Terrestre {
	public abstract void move();
}

Poderíamos criar um classe Caminhao que implementa a interface Terrestre, de maneira semelhante ao que fizemos acima:

class Caminhao implements Terrestre {
	public void move() {
		System.out.println("Move na longa estrada da vida!");
	}
}

Para utilizar a classe Caminhao, faríamos de maneira parecida com a anterior:

public class Programa {
	public static void main(String[] args){
		Caminhao scania = new Caminhao();
		scania.move(); //imprime "Move na longa estrada da vida!"
	}
}

Mas o que acontece se tivermos uma classe que representa um Hovercraft, um veículo anfíbio, que pode se deslocar tanto na água como na terra?

Será que podemos implementar tanto a interface Aquatico como a interface Terrestre simultaneamente?

Claro que sim, já que a interface define apenas a assinatura (ou seja, nome, parâmetros e retorno) do método move e não uma implementação.

class Hovercraft implements Aquatico, Terrestre {
	public void move(){
		System.out.println("Move na água e na terra!");
	}
}

Não há problema algum, porque a implementação do método move na classe Hovercraft é a mesma tanto para Aquatico como para Terrestre.

Podemos usar a classe Hovercraft conforme a seguir:

public class Programa {
	public static void main(String[] args){
		Hovercraft srn4 = new Hovercraft();
		srn4.move(); //imprime "Move na terra e na água!"
	}
}

Métodos default em Interfaces no Java 8

Conforme vimos em um post anterior, no Java 8 agora é possível definir, além de contratos, implementações de métodos em interfaces.

Esse novo mecanismo é chamado de métodos default e, com ele, é possível evoluir interfaces antigas como a Iterable, que agora possui um método forEach, por exemplo.

No exemplo acima, poderíamos definir o comportamento de navegar na água diretamente na interface Aquatico, utilizando a palavra reservada default:

interface Aquatico {
	public default void move() {
		System.out.println("Navega na água!");
	}
}

Métodos default só podem ser definidos em interfaces e são implicitamente públicos e, é claro, não-abstratos porque possuem um corpo definido, com código.

A classe Navio já não precisará fornercer uma implementação para o método move:

class Navio implements Aquatico {
}	

A interface Terrestre também pode ter uma implementação do método move com um método default:

interface Terrestre {
	public default void move() {
		System.out.println("Move na longa estrada da vida!");
	}
}

A classe Caminhao também seria simplificada:

class Caminhao implements Terrestre {
}

O programa que utiliza as classes Navio e Caminhao, bem como suas respectivas interfaces, não precisa ser alterado:

public class Programa {
	public static void main(String[] args){
		Aquatico titanic = new Navio();
		titanic.move(); //imprime "Navega na água!"

		Caminhao scania = new Caminhao();
		scania.move(); //imprime "Move na longa estrada da vida!"
	}
}

Colisão de métodos default

Mas o que será que acontece com a nossa classe Hovercraft, que implementa tanto Aquatico como Terrestre?

class Hovercraft implements Aquatico, Terrestre {
^
error: class Hovercraft inherits unrelated defaults for move() from types Aquatico and Terrestre

Observe que, para a classe Hovercraft, o código não pode ser compilado. Como com um método default não apenas define uma assinatura mas também uma implementação, há ambiguidade na definição do método move. O compilador não pode definir qual das duas implementações deve ser escolhida e, por isso, nos avisa dessa colisão com um erro de compilação.

Para fazermos a classe Hovercraft compilar com sucesso, temos que definir uma implementação para o método move, evitando qualquer ambiguidade:

class Hovercraft implements Aquatico, Terrestre {
	public void move() {
		System.out.println("Move na terra e na água!");
	}
}

Uma alternativa é invocar um método default de uma interface a partir de uma classe que a implementa, utilizando o nome da interface seguido de super e do nome do método default. Por exemplo:

class Hovercraft implements Aquatico, Terrestre {
	public void move() {
		Aquatico.super.move();
		Terrestre.super.move();
		System.out.println("Move na terra e na água!");
	}
}

Se utilizarmos a classe Hovercraft acima em um programa, teremos algo como:

public class Programa {
	public static void main(String[] args){
		Hovercraft srn4 = new Hovercraft();
		srn4.move(); 
		// imprime 
		//	"Navega na água!"
		//	"Move na longa estrada da vida!"
		//	"Move na terra e na água!"
	}
}

Precedência de métodos da superclasse

Suponha que tenhamos uma interface Aereo, com um método default chamado move:

interface Aereo {
	public default void move() {
		System.out.println("Move no ar!");
	}
}

Também temos uma classe Aviao que implementa a interface Aereo, porém sobreescrevendo o método move:

class Aviao implements Aereo {
	public void move(){
		System.out.println("Voa...");
	}
}

Se criarmos um objeto da classe Aviao, qual método será chamado: o método sobreescrito na classe ou o método default da interface?

Será escolhido o método sobreescrito na classe Aviao, funcionando da mesma maneira que sobreescrita de métodos para classes:

public class Programa {
	public static void main(String[] args){
		Aviao boeing747 = new Aviao();
		boeing747.move(); //imprime "Voa..."
	}
}

Agora, se quisermos representar um Hidroavião, um tipo de avião que pode decolar e pousar na água, teremos uma classe semelhante a seguinte:

class HidroAviao extends Aviao implements Aquatico {
}

A classe HidroAviao é um Aviao que implementa a interface Aquatico.

Mas o que será que acontece ao invocarmos o método move de uma instância de HidroAviao? Será que é invocado a implementação da superclasse Aviao ou da interface Aquatico?

A implementação escolhida será a da superclasse:

public class Programa {
	public static void main(String[] args){
		HidroAviao curtissNC = new HidroAviao();
		curtissNC.move(); //imprime "Voa..."
	}
}

Uma implementação definida em uma superclasse sempre tem precedência sobre uma implementação definida em um método default de uma interface.

É importante lembrar que, em Java, uma classe só pode ter uma superclasse.

Resumindo

As regras do uso de métodos default são bem claras:

  1. Colisão de interfaces – se uma classe implementa duas interfaces que possuem métodos default com a mesma assinatura, o método deve ser sobreescrito
  2. Precedência de superclasses – se uma superclasse provê uma implementação de um método que tem a mesma assinatura de um método default de uma interface, a implementação da superclasse tem precedência