Como "desencapsular" exception do EJB

Bom dia,

como vocês trabalham com exceções que vem do EJB pois elas vem encapsuladas em uma exceção de EJB e com uma mensagem “genérica” mas eu gostaria de obter a exceção que eu estou gerando lá no EJB.

grato.

Voce pode lançar um application exception (usando a anotacao @ApplicationException), com isso você consegue receber a real exception lançada pelo application server. Não esqueca de verificar se voce precisa que a exception faca rollback ou não da transacao (isso também é especificado na anotacao).

Obrigado por responder. Ainda ficaram várias dúvidas, na verdade surgiram dúvidas com sua explicação, mas creio que antes de fazê-las tenho que dar uma procurada sobre @ApplicationException e caso as dúvidas persistam aí sim eu coloco aqui.

t+

[quote=townray]Bom dia,

como vocês trabalham com exceções que vem do EJB pois elas vem encapsuladas em uma exceção de EJB e com uma mensagem “genérica” mas eu gostaria de obter a exceção que eu estou gerando lá no EJB.

grato.[/quote]

Seguindo as boas práticas de tratamento de exceção elas são encapsuladas em exceções do EJB. Mas vc pode desencapsulá-las fácilmente analizando o stacktrace presente em todas as exceções. Mas lembre-se, desencapsulá-las não lhe adianta nada se não as souber resolver. Portanto, tecnicamente é relativamente simples desencapsular,mas precisa saber o que está fazendo com isso.

Legal, entendi qual é. Mas um exemplo: digamos que meu EJB SB faça alguma persistência utilizando JPA conforme o código abaixo. Código este gerado pelo “wizard” do Netbeans 6.8:

@Stateless
public class LivroFacade {
    @PersistenceContext(unitName = "IenWebAppPU")
    private EntityManager em;

    public void create(Livro livro) {
        em.persist(livro);
    }
(...)

Nesse caso eu gostaria que o método create() lançasse uma exception mais “verbosa” do que “Transaction Aborted”. Por exemplo, eu propositalmente inclui um registro com chave primária duplicada (vamos esquecer como contornar esse problema aqui ok?) ao invés do erro legalzinho que aparece no meu console aparecer lá na aplicação client, aparece como eu disse “Transaction Aborted”. O erro que aparece no meu console é:


Caused by: java.sql.SQLIntegrityConstraintViolationException: A instrução foi interrompida, porque iria gerar um valor duplicado da chave em uma restrição de chave primária ou de unicidade identificada por ‘SQL091210100323020’ definida em ‘LIVROS’.
at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.executeUpdate(Unknown Source)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:791)
… 83 more
Caused by: org.apache.derby.client.am.SqlException: A instrução foi interrompida, porque iria gerar um valor duplicado da chave em uma restrição de chave primária ou de unicidade identificada por ‘SQL091210100323020’ definida em ‘LIVROS’.
at org.apache.derby.client.am.Statement.completeExecute(Unknown Source)
at org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(Unknown Source)
at org.apache.derby.client.net.NetStatementReply.readExecute(Unknown Source)
at org.apache.derby.client.net.StatementReply.readExecute(Unknown Source)
at org.apache.derby.client.net.NetPreparedStatement.readExecute_(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.readExecute(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.flowExecute(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.executeUpdateX(Unknown Source)
… 85 more

Andei dando uma olhada no método persist() do JPA e ele lança algumas exceções mais detalhadas como por exemplo: EntityExistsException, java.lang.IllegalArgumentException, TransactionRequiredException mas como tudo no final é exception eu fiz a modificação abaixo sem sucesso:

LivroFacade.java

@Stateless
public class LivroFacade {
    @PersistenceContext(unitName = "IenWebAppPU")
    private EntityManager em;

    public void create(Livro livro) throws MinhaExceptionDePersistencia {
        try {
            em.persist(livro);
        } catch (Exception ex) {
            throw new MinhaExceptionDePersistencia(ex.getLocalizedMessage());
        }
    }
(...)

MinhaExceptionDePersistencia.java

@ApplicationException(rollback=true)
public class MinhaExceptionDePersistencia extends Exception {

O que está errado aqui?

[quote=sergiotaborda]
Seguindo as boas práticas de tratamento de exceção elas são encapsuladas em exceções do EJB. Mas vc pode desencapsulá-las fácilmente analizando o stacktrace presente em todas as exceções. Mas lembre-se, desencapsulá-las não lhe adianta nada se não as souber resolver. Portanto, tecnicamente é relativamente simples desencapsular,mas precisa saber o que está fazendo com isso.[/quote]

A questão técnica ou o “saber o que está fazendo com isso” nem são as questões que me preocupam. O que eu gostaria de saber é se realmente é dessa forma que TODOS que trabalham com Java fazem, pois cá entre nós ficar buscando exceções em stackTrace é algo incábivel… pelo menos IMHO.

Agora como o rlazoti disse, posso lançar mão do @ApplicationException que para mim seria perfeito se eu conseguisse fazer funcionar aqui, conforme o meu outro post.

Para obter a real exceção que aconteceu no lado servidor, usam-se métodos como getCausedByException e getCause.
O problema delas é que elas podem estar empacotadas em mais de uma exceção, testa isso aqui em um servlet pra ver se funciona:

[code] try {
String message = bean.getMessage(); // aqui você troca pelo seu método
PrintWriter out = response.getWriter();
out.println(message);
out.flush();
out.close();
} catch(EJBException e) {
Throwable ee = e.getCausedByException();
Throwable th = e;

		while((ee != null) && ((ee = ee.getCause()) != null)) {
			th = ee;
		}
		
		System.out.println("Exceção: " + th);
	}[/code]

towray, da uma olhada nesse link para ver quando usar um tipo ou outro de exception:

http://java.sun.com/javaee/5/docs/tutorial/doc/bnbpj.html

Entendi, era isso realmente que eu tinha pensado em fazer antes de postar a dúvida aqui, mas eu não fiz pois não posso me conformar que essa seja a maneira “mais correta” de fazer isso. Eu poderia jurar que deeveria existir uma forma mais inteligente e amigável de resolver essa questão. Creio que o @ApplicationException deve resolver meu problema, só não estou conseguindo fazê-lo funcionar.

[quote=rlazoti]towray, da uma olhada nesse link para ver quando usar um tipo ou outro de exception:
http://java.sun.com/javaee/5/docs/tutorial/doc/bnbpj.html[/quote]

rodrigo,

eu conheço as diferenças entre os tipos de exceções. Entendo que uma exceção de aplicação deveria ser lançada quando detectado algum problema na minha regra de negócio, mas eu queria mesmo era interceptar o erro do JPA e traduzir numa exceção de aplicação pois não faz muito sentido eu exibir a mensagem “Transaction Aborted” quando o erro na verdade é de “chave duplicada”. Mas agradeço a atenção e vou continuar tentando aqui. t+

É realmente, ApplicationExceptions são uma maneira melhor de se lidar com isso, mas se for só pra testes, o código acima funciona.

[quote=townray]
Andei dando uma olhada no método persist() do JPA e ele lança algumas exceções mais detalhadas como por exemplo: EntityExistsException, java.lang.IllegalArgumentException, TransactionRequiredException mas como tudo no final é exception eu fiz a modificação abaixo sem sucesso:

LivroFacade.java

@Stateless
public class LivroFacade {
    @PersistenceContext(unitName = "IenWebAppPU")
    private EntityManager em;

    public void create(Livro livro) throws MinhaExceptionDePersistencia {
        try {
            em.persist(livro);
        } catch (Exception ex) {
            throw new MinhaExceptionDePersistencia(ex.getLocalizedMessage());
        }
    }
(...)

O que está errado aqui?[/quote]

O que está errado é que vc não está seguindo as boas práticas de tratamento de exceção. O que vc quer é matar o stacktrace e isso simplesmente NÂO SE FAZ.

O seu codigo seria correto assim:

@Stateless
public class LivroFacade {
    @PersistenceContext(unitName = "IenWebAppPU")
    private EntityManager em;

    public void create(Livro livro) throws MinhaExceptionDePersistencia {
        try {
            em.persist(livro);
        } catch (Exception ex) {
            throw new MinhaExceptionDePersistencia(ex);
        }
    }
(...)

Isto é correto.
Só que é exactamente o contrário do titulo do tópico. Vc quer é encapsular a exceção. Não “desencapsula-la”

Um exemplo do que vc estava pedindo no inicio seria :

@Stateless
public class LivroFacade {
    @PersistenceContext(unitName = "IenWebAppPU")
    private EntityManager em;

    public void create(Livro livro) throws MinhaExceptionDePersistencia {
        try {
            em.persist(livro);
        } catch (Exception ex) {
            if ( ExceptionUtils.unwrapp(ex, DuplicatedInsertionException.class) != null ){
                // tentativa de registro duplicado; ignora 
                return;
            } else {
                throw new MinhaExceptionDePersistencia(ex);
            }
        }
    }
(...)

Em que o método unwrapp iria procurar no stackstrace de ex se existe uma exceção do tipo DuplicatedInsertionException.
E se sim, retorná-la.

analizar o stackstrace não é incabivel e faz parte do correto tratamento de exceções. Ele é feito sempre que soubermos tratar um certo tipo de exceção ou, quando na ultima camada, temos que escolher que mensagem dar ao usuário .Por exemplo, se o banco está fora do ar ou estamos sem rede uma exceção de IO será encapsulada numa SQLException que por sua vez será encapulada pelo DAO, etc… até chegar na camada de apresentação. Mas ai o que eu quero mostrar para o usuário é “Sem conexão ao banco de dados” e para isso tenho que fazer um unwrapp da exceção que chegar a mim.

[quote=sergiotaborda]
analizar o stackstrace não é incabivel e faz parte do correto tratamento de exceções. Ele é feito sempre que soubermos tratar um certo tipo de exceção ou, quando na ultima camada, temos que escolher que mensagem dar ao usuário.[/quote]

O que você está querendo dizer é que a responsabilidade de tratar (fazer unwrap) uma exceção é daquele que irá ou exibir na tela ou tomar uma decisão que não seja de relançamento daquela exceção? Não sei se fui claro… no meu caso seria então a aplicação cliente ou camada de apresentação que deveria fazer o unwrap dos Exceptions?

Legal… onde eu posso encontrar referências dessa afirmação?

[quote=townray][quote=sergiotaborda]
analizar o stackstrace não é incabivel e faz parte do correto tratamento de exceções. Ele é feito sempre que soubermos tratar um certo tipo de exceção ou, quando na ultima camada, temos que escolher que mensagem dar ao usuário.[/quote]

O que você está querendo dizer é que a responsabilidade de tratar (fazer unwrap) uma exceção é daquele que irá ou exibir na tela ou tomar uma decisão que não seja de relançamento daquela exceção? Não sei se fui claro… no meu caso seria então a aplicação cliente ou camada de apresentação que deveria fazer o unwrap dos Exceptions?
[/quote]

Acho que vc está usando os termos ao contrário. A sua aplicação não está fazendo nenhum unwrapping de exceções.
Quando vc coloca aesceção dentro da sua isso se chama wrapping ( ou encapsular)

unwrapping é o contrário. É procurar uma exceção dentro da exceção que foi lançada.

Tratar não é unwrapping. Tratar é resolver o problema. Para resolver o problema, às vezes precisamos de recorrer a operações de unwrapping. Apenas capturar e encapsular a exceção não caracteriza tratamento e sim manipulação.
Existe uma diferença entre tratar (resolver) e manipular (capturar e trabalhar, mas não resolver).

Nem todas as formas de manipulação são boas práticas.

[quote]

Legal… onde eu posso encontrar referências dessa afirmação?[/quote]

Aqui Procure por Destructive Wrapping (que é o que vc estava fazendo)

Aqui para mais detalhes e em português.

[quote=sergiotaborda]
Acho que vc está usando os termos ao contrário. A sua aplicação não está fazendo nenhum unwrapping de exceções.
Quando vc coloca aesceção dentro da sua isso se chama wrapping ( ou encapsular)[/quote]

Não Sérgio… eu sei sim o que é wrap e unwrap e que no nosso contexto, estamos usando o termo unwrap para definir localização de uma exceção dentro do stacktrace. Então pra evitar essas divergencias que estão acontecendo, gostaria de saber como você resolveria essa minha questão. Tenho um CRUD onde um EJB Session Bean Stateless é quem faz o persist(). Porém a chave a ser gravada é aberta para a digitação de qualquer valor e nesse caso estamos sujeitos a um EntityExistsException. Eu tinha pensado em tratar esse erro dentro do próprio EJB pois se eu tratasse na aplicação cliente eu teria que fazer o “unwrap” em busca dessa exceção, e quando eu postei pela primeira vez eu achei essa solução bem incoerente. Na verdade eu ainda acho, porém se é assim que as “boas normas da programação” sugere ou se é assim que todos usam ou se é assim que é, sem problemas eu uso assim. Porém estou na dúvida se eu tenho que passar para a aplicação cliente uma exceção “final” do tipo MinhaPersistenciaException e no getMessage() dessa exceção eu obtenho o erro que deverá ser exibido ou se o EJB passa a exceção toda encapsulada e a aplicação cliente que será responsável por fazer o unwrap e exibir a mensagem. Como você, ou as boas maneiras, ou a maioria dos desenvolvedores faria neste cenário?

[quote=townray][quote=sergiotaborda]
Acho que vc está usando os termos ao contrário. A sua aplicação não está fazendo nenhum unwrapping de exceções.
Quando vc coloca aesceção dentro da sua isso se chama wrapping ( ou encapsular)[/quote]

Não Sérgio… eu sei sim o que é wrap e unwrap e que no nosso contexto, estamos usando o termo unwrap para definir localização de uma exceção dentro do stacktrace. Então pra evitar essas divergencias que estão acontecendo, gostaria de saber como você resolveria essa minha questão. Tenho um CRUD onde um EJB Session Bean Stateless é quem faz o persist(). Porém a chave a ser gravada é aberta para a digitação de qualquer valor e nesse caso estamos sujeitos a um EntityExistsException. Eu tinha pensado em tratar esse erro dentro do próprio EJB pois se eu tratasse na aplicação cliente eu teria que fazer o “unwrap” em busca dessa exceção, e quando eu postei pela primeira vez eu achei essa solução bem incoerente. Na verdade eu ainda acho, porém se é assim que as “boas normas da programação” sugere ou se é assim que todos usam ou se é assim que é, sem problemas eu uso assim. Porém estou na dúvida se eu tenho que passar para a aplicação cliente uma exceção “final” do tipo MinhaPersistenciaException e no getMessage() dessa exceção eu obtenho o erro que deverá ser exibido ou se o EJB passa a exceção toda encapsulada e a aplicação cliente que será responsável por fazer o unwrap e exibir a mensagem. Como você, ou as boas maneiras, ou a maioria dos desenvolvedores faria neste cenário?[/quote]

O ponto é o seguinte: se acontecer uma EntityExistsException o que o seu EJB vai fazer ?

 public void create(Livro livro) throws MinhaExceptionDePersistencia {  
        try {  
            em.persist(livro);  
        } catch (EntityExistsException  e){
          // o que vc faria aqui ?
        } catch (Exception ex) {  
            throw new MinhaExceptionDePersistencia(ex);  
        } 

[quote=townray]Bom dia,

como vocês trabalham com exceções que vem do EJB pois elas vem encapsuladas em uma exceção de EJB e com uma mensagem “genérica” mas eu gostaria de obter a exceção que eu estou gerando lá no EJB.

grato.[/quote]

Townray,

Pelo que percebi você não irá encontrar a causa da exceção gerada pelo banco por chaves duplicadas, ou qualquer erro de integridade de dados. Isto porque não está na pilha e sendo assim não conseguirá tratá-la.

Tenho esta mesma dificuldade que está descrito neste post, e acredito que seja o mesmo problema que o seu e do Frederico também.

Se você já encontrou a solução pra isso por gentileza nos avise.

@braços