Rodrigo Kumpera Weblog

Meus achados sobre tecnologia

EJB3 contra Generics

April 29th, 2007 · 7 Comments

Esta semana eu descobri que misturar EJB3 e Generics não acontece sem alguns problemas. EJB3 é um bom exemplo de porque erasure nunca foi uma boa idéia e te proibe usar alguns idiomas comuns. Bom, felizmente não é o fim do mundo e tem como contornar sem muita dor.

Para entender o problema, vamos lembrar um pouco como Generics funciona com herança, temos as interfaces Foo<T> e Bar extends Foo<String>, os métodos de Foo que dependem de T vão, na verdade, usar Object, o mesmo acontece com Bar, que possui nenhum método a mais que Foo. Diferente do que acontece com classes, não são criados métodos sintéticos com a assinatura especializada, por exemplo, em Bla implements Comparable<Bla>, vamos ter um método sintético int compareTo(Bla b).

Qual a relação de erasure e métodos sintéticos com EJB3? Simples, em um Stateless Session Bean definimos as interfaces remota e local, mas não precisamos implementá-las. Isso cria uma espécie de ilusão de ótica, pois de um lado achamos que os métodos implementados possuem a assinatura correta e de outro, por conta do erasure, a assinatura é toda reduzida para Object. O resultado é um só, o container não localiza o método com a assinatura solicitada. O seguinte exemplo demonstra este problema:


public interface Foo {
T concat(T t1, T t2);
}

public interface Bla extends Foo {

}

@Stateless
@Remote(Bla.class)
@Local(Bla.class)
public class BlaBean {
public String concat(String str1, String str2) {
return str1 + str2;
}
}

Podem testar este código, vão notar que o container reclama não consegue chamar Object concat(Object, Object). Nós queremos implementar o código utilizando o tipo especializado, só que para resolver isso vamos ter que jogar pelas regras do compilador e fazer BlaBean implementar Bla. Dessa forma, implementando a interface, o compilador vai aplicar erasure no bean também e a chamada do método vai funcionar corretamente.

Esta situação me ocorreu quando refatorei a interface de vários SLSB para usarem uma inteface base genérica. Quebrei a cabeça por um bom tempo até me ocorrer que o erasure do generics criou este problema, gostaria de ter encontrado uma solução melhor, mas a que apresentei aqui é um comprometimento razoavel. Meu gosto seria por uma solução da plataforma, como generics implementado direito, ou então uma atualização no RMI para levar reification em conta.

Tags: Programming · java

7 responses so far ↓

  • 1 seufagner // Apr 30, 2007 at 5:17 pm

    Com uma solução da plataforma, como eu faria para estender meu modelo feito em EJB2.x com EJB3? Visto que o EJB3 da suporte para isso (e eles se preocuparam bastante). Muitos legados não são projetos como o de simples portal, vide Bradesco.

  • 2 kumpera // Apr 30, 2007 at 8:39 pm

    Fagner, sinceramente, se você está usando EJB 2.x e não pode migrar, o melhor a fazer é esconder toda dor de cabeça com Spring e xdocket. Não existe muita opção se você está nessa plataforma legado. Mesmo com a spec se preocupando muito com compatibilidade, é um fardo enorme.

  • 3 Paulo Silveira // May 6, 2007 at 2:05 am

    O container que voce esta usando é quem nao esta sendo esperto o suficiente. Ele podia, por reflection, perceber que o método da interface mae é parametrizado e inferir T=String da interface Bla, não é mesmo?

    Apesar da spec não dizer nada sobre os métodos bridge, acho que o container deveria ter esse cuidado.

  • 4 kumpera // May 6, 2007 at 12:00 pm

    Paulo, o problema não é o container, ele não tem como resolver isso. Como um Session Bean não precisa implementar suas interfaces local/remota e não existem os métodos bridge, já que as interfaces que são genéricas. A resolução segundo a spec dita que a mesma assinuatura usada na interface deve estar presente no tipo concreto.
    .
    Porém o container poderia considerar o fato da interface ser genérica e aplicar o tipo paramétrico na hora de resolver a assinatura, isso resolveria o problema, mas exigiria alteração no protocolo do RMI .

  • 5 Paulo Silveira // May 6, 2007 at 11:18 pm

    Kumpera, entendi a quebra do RMI!

    Mas e quanto a requisicao ejb? Porque apesar da requisicao ser via RMI, cada container passar as informacoes do metodo a ser invocado da maneira que ele quise, isso nao é padrao. O cara que faz o unmarshal da Invocation do jboss no lado do servidor poderia fazer esse lookup de metodos de acordo com o real tipo parametrizado… nao?

  • 6 kumpera // May 7, 2007 at 9:40 am

    Paulo,

    O container pode fazer como quiser, mas precisa seguir uma especificação se quiser ser chamado de EJB3. Pelo que lembro, o mecanismo de binding tem que ser funcionalmente igual ao do RMI, ou seja, a assinatura do método do Bean deve ser idêntica àquela utilizada pelo cliente. RMI não possui qualquer provisão de suporte a generics ou erasure.

  • 7 Paulo Silveira // Jun 16, 2010 at 3:47 pm

    So pra lembrar que passei por essa dificuldade, 3 anos depois do Kumpera ter escrito aqui :).

Leave a Comment