NetBeans + JBoss + EJB3 - Erro ao acessar um bean

Olá pessoal, boa tarde à todos.

Estou desenvolvendo uma aplicação utilizando EJB3 no NetBeans e JBoss. Inicialmente estava tendo problemas com o bind do data source, mas já consegui resolver esse problema. Mas agora, não consigo acessar o bean de teste que criei. O deploy esta sendo feito corretamente, mas ao tentar acessar via um Servlet, uma exceção é disparada: bean not bound. O interessante é que se eu acessar o jmx-console do JBoss, o bean esta lá ativo.

Ao executar a aplicação, a linha final do Jboss é:

13:41:17,671 INFO [EJBContainer] STARTED EJB: br.com.teste.beans.FuncionarioBean ejbName: FuncionarioBean
13:41:17,671 INFO [EJB3Deployer] Deployed: file:/E:/web/jboss-4.2.2.GA/server/default/tmp/deploy/tmp16055teste.ear-contents/teste-ejb.jar
13:41:18,000 INFO [TomcatDeployer] deploy, ctxPath=/teste, warUrl=…/tmp/deploy/tmp16055teste.ear-contents/teste-war-exp.war/
13:41:18,156 INFO [EARDeployer] Started J2EE application: file:/E:/web/jboss-4.2.2.GA/server/default/deploy/teste.ear

Bean FuncionarioBean:

@Stateful()
public class FuncionarioBean implements FuncionarioRemoto {

@PersistenceContext(unitName="teste")
private EntityManager manager;

public FuncionarioBean() {}

public void createFuncionario(Pessoa funcionario) throws Exception {
    if (manager == null) throw new Exception("O EntityManager esta nulo!");
    manager.persist(funcionario);
}

}

No meu Servlet, fiz a seguinte chamada para acessar via jndi:

Context jndiCntx = new InitialContext();

//Object ref = jndiCntx.lookup(“java:comp/env/ejb/FuncionarioBean”);
Object ref = jndiCntx.lookup(“FuncionarioBean/remote”);
FuncionarioRemoto bean = (FuncionarioRemoto) PortableRemoteObject.narrow(ref, FuncionarioRemoto.class);

Quando tento criar um funcionário (meu bean de teste), a seguinte exeção é disparada: FuncionarioBean not bound. Tentei várias formas de obter a referência do beans mas não consegui.

Será que tem alguma configuração extra a ser feita?

Att,

Fred

Tenho duas perguntas:
Como vc configurou o jndi.properties?
Se vc chamar o endereço do seu jboss pela porta 1099 ele emite um arquivo binário?

Sim, se eu o chamar pela porta 1099 um arquivo binário é retornado.

Em relação ao jndi.properties, esse arquivo não existe no meu projeto. Eu o criei diretamente pelo NetBeans, e o tutorial que estava seguindo (livro Enterprise Java Beans 3.0), até onde eu li não demonstra esse arquivo.

Se puder me ajudar em relação a ele, ficarei grato.

Att,

Fred

Eu acabei de fazer o teste abaixo, e não retornou erro na localização do Bean:

        Object ref = jndiCntx.lookup("teste/FuncionarioBean");
        FuncionarioRemoto bean = (FuncionarioRemoto) PortableRemoteObject.narrow(ref, FuncionarioRemoto.class);

Porém, uma ClassCastException é retornada na linha que tento localizar a interface remota.

[quote=frederico.vieira]Olá pessoal, boa tarde à todos.

Estou desenvolvendo uma aplicação utilizando EJB3 no NetBeans e JBoss. Inicialmente estava tendo problemas com o bind do data source, mas já consegui resolver esse problema. Mas agora, não consigo acessar o bean de teste que criei. O deploy esta sendo feito corretamente, mas ao tentar acessar via um Servlet, uma exceção é disparada: bean not bound. O interessante é que se eu acessar o jmx-console do JBoss, o bean esta lá ativo.

Ao executar a aplicação, a linha final do Jboss é:

13:41:17,671 INFO [EJBContainer] STARTED EJB: br.com.teste.beans.FuncionarioBean ejbName: FuncionarioBean
13:41:17,671 INFO [EJB3Deployer] Deployed: file:/E:/web/jboss-4.2.2.GA/server/default/tmp/deploy/tmp16055teste.ear-contents/teste-ejb.jar
13:41:18,000 INFO [TomcatDeployer] deploy, ctxPath=/teste, warUrl=…/tmp/deploy/tmp16055teste.ear-contents/teste-war-exp.war/
13:41:18,156 INFO [EARDeployer] Started J2EE application: file:/E:/web/jboss-4.2.2.GA/server/default/deploy/teste.ear

Bean FuncionarioBean:

@Stateful()
public class FuncionarioBean implements FuncionarioRemoto {

@PersistenceContext(unitName="teste")
private EntityManager manager;

public FuncionarioBean() {}

public void createFuncionario(Pessoa funcionario) throws Exception {
    if (manager == null) throw new Exception("O EntityManager esta nulo!");
    manager.persist(funcionario);
}

}

No meu Servlet, fiz a seguinte chamada para acessar via jndi:

Context jndiCntx = new InitialContext();

//Object ref = jndiCntx.lookup(“java:comp/env/ejb/FuncionarioBean”);
Object ref = jndiCntx.lookup(“FuncionarioBean/remote”);
FuncionarioRemoto bean = (FuncionarioRemoto) PortableRemoteObject.narrow(ref, FuncionarioRemoto.class);

Quando tento criar um funcionário (meu bean de teste), a seguinte exeção é disparada: FuncionarioBean not bound. Tentei várias formas de obter a referência do beans mas não consegui.

Será que tem alguma configuração extra a ser feita?

Att,
Fred
[/quote]
algumas coisas:
vc configurou as propriedades do ambiente na hora de criar o contexto?
anote seu bean para forcar um nome jndi, tipo:

@RemoteBinding( jndiBinding="nome/queEuQuero/refenciar" )

no ejb3 não é mais necessaria essa linha:

FuncionarioRemoto bean = (FuncionarioRemoto) PortableRemoteObject.narrow(ref, FuncionarioRemoto.class);

vc pode fazer o lookup direto e ja fazer o casting para sua interface.

[]´s

procure aqui no forum essa dúvida ja foi sanada… Isso ocorre pq o JBoss tem um padrão diferenciado para nomenclatura de Ejbs no JNDI

[quote=frederico.vieira]Eu acabei de fazer o teste abaixo, e não retornou erro na localização do Bean:

        Object ref = jndiCntx.lookup("teste/FuncionarioBean");
        FuncionarioRemoto bean = (FuncionarioRemoto) PortableRemoteObject.narrow(ref, FuncionarioRemoto.class);

Porém, uma ClassCastException é retornada na linha que tento localizar a interface remota.[/quote]

vc não pode inserir o jar dos ejbs no pacote da aplicação web depoliada. use somente para poder compilar a app, mas na hora do deploy não empacote junto.

[]´s

Cara, meu arquivo ear esta assim:

teste.ear

  • jar
  • META-INF
  • teste-ejb.jar
  • teste-war.war

Do jeito que criei o projeto no NetBeans, eu deixei. Qual seria a melhor prática de executar esse projeto?

Em relação ao lookup direto, tentei fazê-lo conforme trecho do código abaixo, mas mesmo assim deu CastClassException:

        Context jndiCntx = new InitialContext();

        Object ref = jndiCntx.lookup("teste/FuncionarioBean");
        
        ((FuncionarioRemoto)ref).createFuncionario(criaPessoa(request));

[quote=frederico.vieira]Cara, meu arquivo ear esta assim:

teste.ear

  • jar
  • META-INF
  • teste-ejb.jar
  • teste-war.war

Do jeito que criei o projeto no NetBeans, eu deixei. Qual seria a melhor prática de executar esse projeto?

Em relação ao lookup direto, tentei fazê-lo conforme trecho do código abaixo, mas mesmo assim deu CastClassException:

        Context jndiCntx = new InitialContext();

        Object ref = jndiCntx.lookup("teste/FuncionarioBean");
        
        ((FuncionarioRemoto)ref).createFuncionario(criaPessoa(request));

[/quote]
de uma olhada dentro do war se na pasta lib o jar dos ejs ta la dentro. se tiver retire.

quanto ao lookup, vc pode fazer direto:

SuaInterfaceRemote beanRemote = (SuaInterfaceRemote) context.lookup("nome JNDI do bean");

[]´s

Não estão lá dentro… Então do jeito que fiz esta certo?

cara a principio ta ok.
faça um teste, crie uma classe de teste no mesmo projeto dos ejbs e execute ela de forma standalone p/ ver se o lookup funciona.
deve ser algum detalhe que não ta ok.

[]´s

Cara, muito tosco. Olha a exceção disparada:

Classe: class java.lang.ClassCastException
Mensagem: org.jnp.interfaces.NamingContext
servlets.ServletCadastroFuncionario.processRequest(ServletCadastroFuncionario.java:52)
servlets.ServletCadastroFuncionario.doPost(ServletCadastroFuncionario.java:168)
javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262)
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446)
java.lang.Thread.run(Thread.java:595)

A linha 52 do Servlet é justamente essa:

FuncionarioRemoto ref = (FuncionarioRemoto) jndiCntx.lookup(“vm/FuncionarioBean”);

Vou te dar uma ajudinha:
Usa essa classe para acessar o seu EJB.

public class ServiceLocator {

private InitialContext initialContext;
private static ServiceLocator locator;    

/** Creates a new instance of ServiceLocator */
public ServiceLocator() throws Exception {
    try {
        Properties prop = new Properties();
        prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        prop.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        prop.put(Context.PROVIDER_URL, "localhost");
        
        initialContext = new InitialContext(prop);
        
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public ServiceLocator( String url ) throws Exception {
    try {            
        Properties prop = new Properties();
        prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        prop.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        prop.put(Context.PROVIDER_URL, url);
        
        initialContext = new InitialContext(prop);
        
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static ServiceLocator getInstance() throws Exception{                
    if( locator == null ){ 
        locator = new ServiceLocator();
    }
    return locator;
}

protected Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException("Não é possível clonar o Service Locator");
}

public Object get( String jndiName ) throws Exception {
    try {
        Object result = null;
        result = initialContext.lookup(jndiName);
        
        if( result == null ){
            if( result == null ){
                throw new NamingException("Erro ao se comunicar com serviço EJB!");
            }
        }
        
        return result;
        
    } catch (NamingException ne) {
        ne.printStackTrace();
    }
    
    return null;
}

}

Vc usa assim:

Object ref = ServiceLocator.getInstance().get(“SuaClasseBean/remote”);
seuBeanRemote = (SuaInterfaceRemote) PortableRemoteObject.narrow(ref, SuaInterfaceRemote.class);

    seuBeanRemote.seuMetodo();

Lembrando que na url vc pode colocar nesse formato: jnp://o_ip_do_jboss:1099

Cabazzo, valeu pela dica. Irei testar ainda hoje…

Mas voltando ao assunto, eu editei meu aquivo jboss.xml que estava vazio e coloquei o seguinte código:

FuncionarioBean br.com.teste.FuncionarioRemoto br.com.teste.FuncionarioLocal br.com.teste.FuncionarioBean Stateful iiop ejb/FuncionarioBean

No Servlet, pequei um novo contexto e fiz a chamada:

FuncionarioRemoto ref = (FuncionarioRemoto) jndiCntx.lookup(“ejb/FuncionarioBean”);

Depois disso, funcionou o meu código.

Essas informações que coloquei no arquivo jboss.xml não podem ser substituidas por anotações diretamente no bean?

Att,

Fred

Podem sim, a forma como vc colocou força o seu bena para um nome. O que pode ser feito tb com annotations
como por exemplo @EJB. Mas usando o ServiceLocator que um padarão de J2EE, vc pode pegar pelo nome do bean sem precisar preencher o xml. Eu mesmo não o preencho.
vc tb pode combinar com outros padrões “Business Delegate” e “Session Façade” que fica melhor ainda.
Blz?

Blz cara. Muito obrigado a todos pela ajuda.

Foi um parto iniciar, agora que esta funcionando, não tem mais limites.

Valeu!!!

sim, na minha primeira resposta eu postei como.
basicamente vc vai usa a anotação @remoteBinding.
de uma olhada na documentação do Jboss.

[]'s

Foi mal cara, é que eu não havia prestado atenção nos parâmetros da anotação @RemoteBinding();

Fiz outro teste hoje a noite, comentando o arquivo jboss.xml e adicionando o parâmetro jndiBinding=“ejb/FuncionarioBean” e funcionou também.

Valeu pela dica.

Fred