Integrando VRaptor e Spring - Capítulo 15

Bom dia a todos!

Sou novo no desenvolvimento de Java para Web e estou estudando a apostila do Vraptor (FJ-28).
Estou utilizando a seguinte configuração para o desenvolvimento:

  • VRaptor3.1.3
  • Netbeans 6.9.1
  • GlassFish Server Open Source Edition 3.0.1

A maioria das dúvidas que tive ao longo do estudo encontrei soluções aqui no GUJ ou no próprio Google, porém dessa vez não estou conseguindo resolver esse problema…

Seguindo as instruções do Capítulo 15:

  1. Criei o arquivo applicationContext.xml dentro da pasta src (/goodbuy/src/applicationContext.xml):

[code]<?xml version="1.0" encoding="UTF-8"?>

<tx:annotation-driven />

<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="configLocation">
        <value>classpath:/hibernate.cfg.xml</value>
    </property>
</bean>

[/code]

  1. Removi a anotação @Component do CriadorDeSessionFactory (/goodbuy/src/br/com/caelum/goodbuy/infra/CriadorDeSessionFactory.java):

[code]package br.com.caelum.goodbuy.infra;

import br.com.caelum.vraptor.ioc.ApplicationScoped;
//import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.ioc.ComponentFactory;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

//@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory
{
private SessionFactory factory;

@PostConstruct
public void abre()
{
    AnnotationConfiguration configuration = new AnnotationConfiguration();
    configuration.configure();
    
    this.factory = configuration.buildSessionFactory();
}

public SessionFactory getInstance()
{
    return this.factory;
}

@PreDestroy
public void fecha()
{
    this.factory.close();
}

}
[/code]

  1. Modifiquei o CriadorDeSession para usar um Proxy Dinâmico (/goodbuy/src/br/com/caelum/goodbuy/infra/CriadorDeSession.java):

[code]package br.com.caelum.goodbuy.infra;

import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.ioc.ComponentFactory;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import net.vidageek.mirror.dsl.Mirror;

import org.springframework.orm.hibernate3.SessionFactoryUtils;

import br.com.caelum.vraptor.proxy.MethodInvocation;
import br.com.caelum.vraptor.proxy.Proxifier;
import br.com.caelum.vraptor.proxy.SuperMethod;

import java.lang.reflect.Method;

@Component
public class CriadorDeSession implements ComponentFactory
{
private final SessionFactory factory;
private Session session;
private final Proxifier proxifier;

public CriadorDeSession(SessionFactory factory, Proxifier proxifier)
{
    this.factory = factory;
    this.proxifier = proxifier;
}

@PostConstruct
public void abre()
{
    //this.session = factory.openSession();
    this.session = proxifier.proxify(Session.class, new MethodInvocation<Session>()
    {
        public Object intercept(Session proxy, Method method, Object[] args, SuperMethod superMethod)
        {
            Session sessionDoSpring = SessionFactoryUtils.doGetSession(factory, true);

            return new Mirror().on(sessionDoSpring).invoke().method(method).withArgs(args);
        }
    });

}

public Session getInstance()
{
    return this.session;
}

@PreDestroy
public void fecha()
{
    this.session.close();
}

}
[/code]

  1. Removi a abertura e o fechamento de transações dos métodos do DAO, e anotei o método com @Transactional (/goodbuy/src/br/com/caelum/goodbuy/dao/ProdutoDao.java):

[code]package br.com.caelum.goodbuy.dao;

import java.util.List;

import org.hibernate.Session;
//import org.hibernate.Transaction;

import br.com.caelum.goodbuy.modelo.Produto;
import br.com.caelum.vraptor.ioc.Component;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;

import org.springframework.transaction.annotation.Transactional;

@Component
public class ProdutoDao
{
private final Session session;

public ProdutoDao(Session session)
{
    this.session = session;
}

public Produto carrega(Long id)
{
    return (Produto) session.load(Produto.class, id);
}

public void recarrega(Produto produto)
{
    session.refresh(produto);
}

//@SuppressWarnings("unchecked")
public List<Produto> listaTudo()
{
    return session.createCriteria(Produto.class).list();
}

public List<Produto> busca(String nome)
{
    return session.createCriteria(Produto.class)
        .add(Restrictions.ilike("nome", nome, MatchMode.ANYWHERE))
            .list();
}

@Transactional
public void salva(Produto produto)
{
    //Transaction tx = session.beginTransaction();
    session.save(produto);
    //tx.commit();
}

@Transactional
public void atualiza(Produto produto)
{
    //Transaction tx = session.beginTransaction();
    session.update(produto);
    //tx.commit();
}

@Transactional
public void exclui(Produto produto)
{
    //Transaction tx = session.beginTransaction();
    session.delete(produto);
    //tx.commit();
}

}[/code]

Quando executo minha aplicação o GlassFish me retorna o seguinte erro:

[code]HTTP Status 500 -

type Exception report

message

descriptionThe server encountered an internal error () that prevented it from fulfilling this request.

exception

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/tx]
Offending resource: class path resource [applicationContext.xml]

note The full stack traces of the exception and its root causes are available in the GlassFish Server Open Source Edition 3.0.1 logs.[/code]

Alguém faz idéia de como solucionar o problema?

Obrigado,

Diogo

Pela mensagem de erro a DTD do seu applicationContext.xml está errado. Essas DTDs mudam conforme a versão do Spring que você está usando.

Dê uma olhada aqui: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/xsd-config.html

Olá Garcia, entendi!

Por acaso você saberia me dizer pra qual versão do Spring esse exemplo da apostila foi feito?

Não.

Mas a DTD deve sempre coincidir com a da versão do jar que você tem. Conforme a versão do Spring você deve atualizar as DTDs.

Ok, obrigado pela ajuda e pela rapidez das respostas!
Valeu mesmo!!!

Vou estudar esse DTD e atualizo o tópico com a (possível) solução encontrada.

acho que vc vai precisar de um jar a mais também do spring, aquele que controla as transações

Bom dia,

Substitui as bibliotecas do Spring pela última versão disponível no site.
Logo o GlassFish me retornou outro erro:

[code]HTTP Status 500 -

type Exception report

message

descriptionThe server encountered an internal error () that prevented it from fulfilling this request.

exception

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘produtoController’ defined in file [/media/DADOS/PROJETOS/java/goodbuy/build/web/WEB-INF/classes/br/com/caelum/goodbuy/controller/ProdutoController.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [br.com.caelum.goodbuy.dao.ProdutoDao]: : Error creating bean with name ‘produtoDao’ defined in file [/media/DADOS/PROJETOS/java/goodbuy/build/web/WEB-INF/classes/br/com/caelum/goodbuy/dao/ProdutoDao.class]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/aspectj/util/PartialOrder$PartialComparable; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘produtoDao’ defined in file [/media/DADOS/PROJETOS/java/goodbuy/build/web/WEB-INF/classes/br/com/caelum/goodbuy/dao/ProdutoDao.class]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/aspectj/util/PartialOrder$PartialComparable

note The full stack traces of the exception and its root causes are available in the GlassFish Server Open Source Edition 3.0.1 logs.[/code]

Procurei pelo erro no Google e achei um tópico nesse fórum:
http://www.guj.com.br/posts/list/15/149525.java#1119182

Não sei se o caso é o mesmo, mas não estou conseguindo corrigir o erro…

Você vai precisar das libs que estão no pacote mandatory do Vraptor e as que estão em lib/containers/spring. Esse erro é por causa da falta do aspectj.

Pois é Garcia, as libs do pacote mandatory do Vraptor eu já havia adicionado ao projeto em outra ocasião, mas por segurança sobreescrevi os mesmos.
Sobre os que você referenciou estando em lib/containers/spring eu não achei esse pacote não, nem no Vraptor nem no Spring.

Entretanto eu já havia adicionado também ao meu projeto todas as libs que vem no pacote do Spring (por segurança).

Você comentou sobre a falta do aspectj, não sei se é o mesmo, mas no pacote mandatory do Vraptor existe a lib “aspectjrt.jar” que também já faz parte do projeto.

Alguma outra sugestão?!

Valeu!

nas suas libs tem mais de um jar do aspectj?

Segurança? Cuidado para não acabar com libs demais no teu projeto.

Dê uma olhada se você possui a mesma lib com versões diferentes. Você adicionou alguma lib a nível de container? Isso pode causar alguns efeitos não muito bons como o classloader hell.

Hmm, agora que eu ví que você usa Vraptor 3.1x. Então você deve usar os jars que estão em lib/mandatory. Lá tem os jars aspectjrt e aopaliance que são necessários pelo Spring.

Lucas acredito que não…

Dá uma olhada na pasta de libs:


vc está usando a classe: org/aspectj/util/PartialOrder$PartialComparable diretamente no ProdutoDAO?

tá usando algo que envolve aspectos no Spring (AOP direto ou @Transactional ou Spring security, por exemplo)?

no zip do spring 3.0.5 tem algum jar do aspectj? se sim, substitua o aspectjrt.jar pelo que veio no spring.

e na dúvida também dê um clean no seu projeto e no servidor pra limpar os jars antigos

Então Lucas, o meu ProdutoDao está conforme descrito na apostila FJ-28… abaixo o código:

[code]package br.com.caelum.goodbuy.dao;

import java.util.List;

import org.hibernate.Session;
//import org.hibernate.Transaction;

import br.com.caelum.goodbuy.modelo.Produto;

import br.com.caelum.vraptor.ioc.Component;

import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;

import org.springframework.transaction.annotation.Transactional;

@Component
public class ProdutoDao
{
private final Session session;

public ProdutoDao(Session session)
{
    this.session = session;
}

public Produto carrega(Long id)
{
    return (Produto) session.load(Produto.class, id);
}

public void recarrega(Produto produto)
{
    session.refresh(produto);
}

@SuppressWarnings("unchecked")
public List<Produto> listaTudo()
{
    return session.createCriteria(Produto.class).list();
}

public List<Produto> busca(String nome)
{
    return session.createCriteria(Produto.class)
        .add(Restrictions.ilike("nome", nome, MatchMode.ANYWHERE))
            .list();
}

@Transactional
public void salva(Produto produto)
{
    //Transaction tx = session.beginTransaction();
    session.save(produto);
    //tx.commit();
}

@Transactional
public void atualiza(Produto produto)
{
    //Transaction tx = session.beginTransaction();
    session.update(produto);
    //tx.commit();
}

@Transactional
public void exclui(Produto produto)
{
    //Transaction tx = session.beginTransaction();
    session.delete(produto);
    //tx.commit();
}

}[/code]

Inclusive se você reparar o código irá ver que nos métodos que realizam transação de mudança no BD foram anotados com @Transactional, conforme descrito na apostila.

No pacote do Spring pelo que eu vi, não possui nenhum jar do aspectj não.

Vou continuar minhas buscas aqui no Google pra ver o que eu acho…

Valeu!

tenta adicionar esse jar:
http://mirrors.ibiblio.org/pub/mirrors/maven2/org/aspectj/aspectjtools/1.6.1/aspectjtools-1.6.1.jar

esse site é bem útil pra achar os jars corretos:
http://jarfinder.com

Exatamente o que eu achei aqui nas buscas:

  • aspectjtools-1.6.1.jar
  • aspectjweaver-1.6.9.jar

Baixei as duas libs e realizei os dois testes no projeto.

Ambos me retornaram erro semelhante:

[code]HTTP Status 500 -

type Exception report

message

descriptionThe server encountered an internal error () that prevented it from fulfilling this request.

exception

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘produtoController’ defined in file [/media/DADOS/PROJETOS/java/goodbuy/build/web/WEB-INF/classes/br/com/caelum/goodbuy/controller/ProdutoController.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [br.com.caelum.goodbuy.dao.ProdutoDao]: : Error creating bean with name ‘produtoDao’ defined in file [/media/DADOS/PROJETOS/java/goodbuy/build/web/WEB-INF/classes/br/com/caelum/goodbuy/dao/ProdutoDao.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class br.com.caelum.goodbuy.dao.ProdutoDao]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘produtoDao’ defined in file [/media/DADOS/PROJETOS/java/goodbuy/build/web/WEB-INF/classes/br/com/caelum/goodbuy/dao/ProdutoDao.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class br.com.caelum.goodbuy.dao.ProdutoDao]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given

note The full stack traces of the exception and its root causes are available in the GlassFish Server Open Source Edition 3.0.1 logs.
GlassFish Server Open Source Edition 3.0.1[/code]

Continuo aqui luta…

deixe só uma delas (o aspectjtools)

esse erro é o “certo”, não é problema dos jars… o aspectj só consegue instrumentar classes com construtor padrão ou interfaces

tem dois jeitos de fazer isso funcionar:

  • tirar o construtor do ProdutoDao e criar um setSession(Session session) anotado com @Autowired

  • extrair a interface ProdutoDao e chamar a classe atual de outra coisa (ProdutoDaoImpl) por exemplo.
    Jeito fácil de fazer isso no eclipse:

    • na classe ProdutoDao >> Refactor >> Rename para ProdutoDaoImpl
    • na classe ProdutoDaoImpl >> Refactor >> Extract Interface >> seleciona todos os métodos relevantes e deixe marcado o “Use extracted interface type where possible”

Pois é, achei num outro tópico você passando uma dessas soluções.
Tentei por enquanto a 1a que você mencionou, conforme código abaixo:

[code]import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.transaction.annotation.Transactional;

@Component
public class ProdutoDao
{
private Session session;
//private final Session session;

public ProdutoDao(/*Session session*/)
{
    //this.session = session;
}

@Autowired
public void setSession(Session session)
{
    this.session = session;
}

@Autowired
public Session getSession()
{
    return this.session;
}


[/code]

e retornou o erro:

[code]HTTP Status 500 -

type Exception report

message

descriptionThe server encountered an internal error () that prevented it from fulfilling this request.

exception

br.com.caelum.vraptor.InterceptionException: an exception was raised while executing resource method

note The full stack traces of the exception and its root causes are available in the GlassFish Server Open Source Edition 3.0.1 logs.[/code]

Vou tentar agora a segunda sugestâo…

o que tem mais pra baixo na exception? os caused-bys