Oi,
Estou usando JPA2 e EJB3.1 em um projeto, juntamente com VRaptor. Muitos componentes do JEE6 é Container-Managed, e o VRaptor não, por tanto não consigo fazer, por exemplo:
@PesistenceContext
EntityMangager
ou
@EJB
RegraDeNegocioEJB
sendo preciso fazer um lookup.
É desvantagem usar algumas coisas do JEE6 com VRaptor? Pois sei que se utilizar API nativa do Hibernate consigo injetar diretamente no controller.
Opiniõe?
sei que o garcia-jj já brincou com essas coisas, mas os componentes do VRaptor não são considerados beans gerenciados pelo container…
então eu não consigo simplesmente receber o @PersistenceContext, @EJB ou @Resource…
Eu tenho várias aplicações usando EJB X VRaptor. O VRaptor não consegue ter os EJBs injetados, pois ele não é “managed” pelo container. A solução bem simples é criar um service locator e invocar os EJBs que você precisar. Há outras formas, mas essa é a mais simples.
Conforme o exemplo abaixo, basta você invocar o service locator assim:
Internamente vai fazer o lookup na JNDI pelo nome UserServiceBean no EAR icob no EJB Module icob-ejb. Lembre-se de usar o codding conventions da Sun para o nome das interfaces remotas e beans de implementação. Caso você mudar isto, terá que alterar o service locator lá onde ele converse Class > Nome do EJB.
Você também pode altear o lookup para usar app, module, etc.
[code]import static icob.web.WebExceptions.EJB_INFRAESTRUCTURE_LOOKUP;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public final class ServiceLocator {
private static final InitialContext ctx;
static {
try {
ctx = new InitialContext();
} catch (NamingException e) {
throw new InfraestructureException(EJB_INFRAESTRUCTURE_LOOKUP);
}
}
public static <T> T lookup(Class<T> clazz) {
final String ejbName = buidEJBName(clazz);
try {
return clazz.cast(ctx.lookup(ejbName));
} catch (NamingException e) {
throw new InfraestructureException(e);
}
}
private static String buidEJBName(Class<?> clazz) {
final StringBuilder name = new StringBuilder(60);
name.append("java:global/icob/icob-ejb/");
name.append(clazz.getSimpleName().replaceAll("Local$|Remote$", "Bean"));
name.append("!");
name.append(clazz.getName());
return name.toString();
}
}[/code]
Lucas, acho que é bom adicionar um cookbook sobre isso, o que você acha? Dá para elaborar um texto legal mostrando como customizar tudo isso.
Como conversamos outro dia, será que conseguiriamos fazer isso usando CDI? O Lavieri esteve trabalhando nisso uma vez, mas não sei como ficou. Penso que se houver como buscar estaticamente ou via JDNI a Bean Factory do CDI, conseguiriamos facilmente buscar os EJBs, bastanto ter um beans.xml vazio nos módulos.
Abraços
[quote=garcia-jj]Eu tenho várias aplicações usando EJB X VRaptor. O VRaptor não consegue ter os EJBs injetados, pois ele não é “managed” pelo container. A solução bem simples é criar um service locator e invocar os EJBs que você precisar. Há outras formas, mas essa é a mais simples.
Conforme o exemplo abaixo, basta você invocar o service locator assim:
Internamente vai fazer o lookup na JNDI pelo nome UserServiceBean no EAR icob no EJB Module icob-ejb. Lembre-se de usar o codding conventions da Sun para o nome das interfaces remotas e beans de implementação. Caso você mudar isto, terá que alterar o service locator lá onde ele converse Class > Nome do EJB.
Você também pode altear o lookup para usar app, module, etc.
[code]import static icob.web.WebExceptions.EJB_INFRAESTRUCTURE_LOOKUP;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public final class ServiceLocator {
private static final InitialContext ctx;
static {
try {
ctx = new InitialContext();
} catch (NamingException e) {
throw new InfraestructureException(EJB_INFRAESTRUCTURE_LOOKUP);
}
}
public static <T> T lookup(Class<T> clazz) {
final String ejbName = buidEJBName(clazz);
try {
return clazz.cast(ctx.lookup(ejbName));
} catch (NamingException e) {
throw new InfraestructureException(e);
}
}
private static String buidEJBName(Class<?> clazz) {
final StringBuilder name = new StringBuilder(60);
name.append("java:global/icob/icob-ejb/");
name.append(clazz.getSimpleName().replaceAll("Local$|Remote$", "Bean"));
name.append("!");
name.append(clazz.getName());
return name.toString();
}
}[/code][/quote]
Olá garcia-jj bacana o código. E parabens pelas suas apps.
No caso de EntityManager com Cotainer-Managed Style, usando API JPA2, eu nao consigo usar por exemplo @PesistentContext. Nesse caso compensa usar API nativa de Hibernate para injetar no controller? Voce coloca um @PersistenceContext no EJB e deixa toda regra la?
Abraço!
é um dos jeitos… nunca mexer direto com o EntityManager, sempre usar EJBs pra fazer isso
Se você quer usar JPA no VRaptor use o componente opcional que o VRaptor disponibiliza. Não tente usar nada managed porque não compensa. Você terá aqueles problemas de lazy-load e afins. Acessar EJBs do container é interessante, mas um EntityManager gerenciado não.
Dê uma olhada na documentação do VRaptor, capitulo Componentes Opcionais, como trabalha o EntityManager que o VRaptor cria para você.
[quote=garcia-jj]Se você quer usar JPA no VRaptor use o componente opcional que o VRaptor disponibiliza. Não tente usar nada managed porque não compensa. Você terá aqueles problemas de lazy-load e afins. Acessar EJBs do container é interessante, mas um EntityManager gerenciado não.
Dê uma olhada na documentação do VRaptor, capitulo Componentes Opcionais, como trabalha o EntityManager que o VRaptor cria para você. [/quote]
Na verdade estou chegando a conclusão de que não compensa utilizar implementação do JPA2. A API dela é horrível. Vou usar API nativa do Hibernate. Obrigado pelas informações. Abraços.
na minha opinião, só a API de Criteria da JPA 2 é bem horrível, o resto da JPA 2 é tão ou mais legal que a do Hibernate.
ex:
User user = (User) session.load(User.class, 1); //precisa do cast
User user = manager.find(User.class, 1); // não precisa do cast
List<User> users = session.createQuery("from User").list(); //unchecked
List<User> users = manager.createQuery("from User", User.class).getResultList(); // não é unchecked!
e mais algumas coisinhas…
no pior dos casos vc faz manager.unwrap(Session.class) e usa a do hibernate 
A API é complexa, mas eu não diria horrível. Esse é o custo por você poder usar uma API fluente e fortemente tipada. Para mim usar criteria baseado em Strings não compensa, pois se você alterar os atributos da entidade ou remover alguma, tem que buscar as ocorrencias pelo código. Na API do JPA você recebe o erro de compilação na hora por causa do Meta Model.
Mas aí é só uma questão de gosto, creio eu.
Abraços
A API é complexa, mas eu não diria horrível. Esse é o custo por você poder usar uma API fluente e fortemente tipada. Para mim usar criteria baseado em Strings não compensa, pois se você alterar os atributos da entidade ou remover alguma, tem que buscar as ocorrencias pelo código. Na API do JPA você recebe o erro de compilação na hora por causa do Meta Model.
Mas aí é só uma questão de gosto, creio eu.
Abraços[/quote]
Ouvi por aí que Criteria API do JPA2 vai ser o padrão do Hibernate tb. Ele deixou de lado a elegancia por ser Type Safary, mas é como voce disse, é gosto.
Retomando o assunto de EJB, garcia-jj, voce nunca teve problemas com Performance fazendo lookup remoto (ou local), dentro da web app?
abraço!
Não entendi bem tua pergunta. Mas não, até agora não tive problemas de performance.
Porém não saia por aí distribuindo todos teus beans. Avalie a necessidade de usar beans remotos ou locais.
Ontem a noite resolvi a fazer umas pesquisas para ver se dá para buscar os EJBs direto de um contexto CDI. Fiz um teste bem simples, feio claro, mas dá para servir de inicio para uma coisa mais legal.
Com o service locator que te passei no outro post, você consegue pegar apenas EJBs locais e remotos. Já com esse outro código você pode pegar qualquer bean do CDI como aqueles anotados com @Named, etc. Dá inclusive para expandir melhor isso usando qualifiers e outras coisinhas mais.
[code]@Resource
public class CdiController {
private final Result result;
@Get("/cdi")
public void cdi() {
try {
BeanManager cdi = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
Bean<UserServiceLocal> bean = (Bean<UserServiceLocal>) cdi.getBeans(UserServiceLocal.class).iterator().next();
CreationalContext<UserServiceLocal> ctx = cdi.createCreationalContext(bean);
UserServiceLocal loginService = (UserServiceLocal) cdi.getReference(bean, UserServiceLocal.class, ctx);
System.out.println(loginService.findActive());
} catch (NamingException e) {
throw new RuntimeException(e);
}
result.nothing();
}
}[/code]
com isso dá pra criar um provider do VRaptor que usa o CDI, não?
Isso apenas faz o lookup dos beans gerenciados pelo CDI. Dá para expandir e buscar por qualifiers, etc. Creio que podemos logo ter aquela conversa sobre o CDI, que te falei.
Abraços
Se isso acontecer, vai deixar o VRaptor ainda mais poderoso.
Seria muito bom.
Eu já tenho umas idéias em mente, mas queria entender qual a idéia de integrar CDI ao VRaptor.
- Injetar os beans que estão no context do CDI nos componentes do VRaptor?
- usar o CDI como provider ao invés do trio Spring, Guice, Pico?
A primeira opção dá para fazer com uma classe que criei em um gist: https://gist.github.com/1189178 . O problema seria então injetar automagico os CDI Beans nos componentes do VRaptor. Dá para fazer criando factories para os beans, que por baixo usam essa classe que fiz. O problema disso é que teriamos que criar uma classe para cada bean, tornando trabalhoso.
Eu fiz algo assim em um projeto meu, porém criei um “generator” baseado no JPA Metamodel do Hibernate, que em tempo de compilação vê quais são meus EJBs remotos e cria as factories. Mas acho que isso não é produtivo. Talvez seja o momento de pensar em uma factory que possa criar beans genéricos.
Já na segunda opção, não tenho a mínima idéia :lol:, mas é possível.
Implementei um projeto que por enquanto está nas versões iniciais, que permite injetar beans do CDI no VRaptor: https://github.com/garcia-jj/vraptor-plugin-cdi
Aí a coisa ficou linda, baixando : ))