Home > JavaEE, JSF > Conversation Scope usando @CustomScoped do JSF 2.0

Conversation Scope usando @CustomScoped do JSF 2.0

Apesar do título, o JSF 2 não vai ter um escopo Conversation ou coisa parecida como tem no Seam ou Orchestra. O que vai ter é o suporte direto a escopos personalizados, o que torna simples a criação de um escopo como Conversation.

Nesse exemplo eu desenvolvi um escopo de conversação simples, mas não é difícil melhorá-lo para suportar diversas conversações simultâneas, assimilando a idéia a gente vê que não é nenhum bicho de sete cabeças. Claro que o principal objetivo é o aprendizado de como tudo funciona, e não ficar implementando o que já tem pronto em diversos frameworks.

Vamos então ao exemplo.

@ManagedBean(name="testeBean")
@CustomScoped("#{conversacao}")
public class TesteBean {
 
	private Integer contador;
 
	@PostConstruct
	public void init()
	{
		System.out.println("TesteBean.init()");
		contador = 0;
	}
 
	public void incrementarContador()
	{
		contador++;
	}
	public void iniciaConversacao()
	{
		Conversacao.instancia().iniciar();
	}
	public void finalizaConversacao()
	{
		Conversacao.instancia().finalizar();
	}
 
	public Integer getContador() {
		return contador;
	}
	public void setContador(Integer contador) {
		this.contador = contador;
	}
}

Acima está um managed bean que chamei de “beanTeste”. Coloquei um println no método init() e anotei esse método com @PostConstruct. Na prática isso quer dizer que toda vez que o managed bean for construído esse método será chamado, e consequentemente aparecerá no console. Isso é útil para vermos que o escopo de conversação realmente está funcionando. Já que estamos falando de escopo personalizado, quem faz isso é a anotação @CustomScoped, que recebe como valor uma EL que será resolvida por um EL Resolver que vamos criar.

Agora vamos ver a tela

Contador atualizado via ajax e mantido com a conversação: <h:outputText id="output3" value="#{testeBean.contador}"/>
 
<br/>
 
<h:commandButton action="#{testeBean.incrementarContador}" value="Incrementar Contador">
	<f:ajax render="output3"/>
</h:commandButton>
<h:commandButton action="#{testeBean.iniciarConversacao}" value="Iniciar Conversação">
	<f:ajax render="@none"/>
</h:commandButton>
<h:commandButton action="#{testeBean.finalizarConversacao}" value="Finalizar Conversação">
	<f:ajax render="@none"/>
</h:commandButton>

Tanto o exemplo da tela quanto do managed bean estão simplificados para o nosso exemplo, mas a versão disponível para download contém uns exemplos para testarmos o funcionamento do ajax também.

A idéia do nosso escopo “conversacao” é o mesmo do Seam, ele funciona como request até que seja explicitamente iniciada a conversação. Quando isso ocorre, o escopo passa a ser statefull até que a conversação seja finalizada, fazendo com que ela funcione como request novamente.

Seguindo essa idéia, o contador que aparece na tela não vai sair de “1″ até que a conversação seja iniciada, pois como o escopo é request, toda vez que executamos a ação “incrementaContador” o managed bean será criado novamente (contador = 0), depois a ação será executada (contador = 1) e então a página será renderizada (exibe contador = 1). Agora se a conversação for iniciada o managed bean será mantido entre as requisições, e o contador não será zerado até que a conversação termine.

Para nosso escopo funcionar, precisamos de um ELResolver, que será quem vai conseguir dizer para o faces de onde virá os objetos relacionados ao escopo “conversacao”. Registramos nosso ELResolver no faces-config.xml assim

<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
              version="2.0">
 
    <application>
        <el-resolver>br.eti.gilliard.exemplojsf2.conversacao.ConversacaoELResolver</el-resolver>
    </application>
 
</faces-config>

E a implementação fica assim

 
public class ConversacaoELResolver extends ELResolver {
 
	@Override
	public Object getValue(ELContext elContext, Object base, Object property) {
 
		if (property == null) {
			throw new PropertyNotFoundException("A Propriedade não pode ser nula!");
		}
		if (base == null) {
 
			if(Conversacao.NOME_ESCOPO.equals(property.toString()))
			{
				Conversacao conversacao = Conversacao.instancia();
				elContext.setPropertyResolved(true);
				return conversacao;
			}
 
			Conversacao conversacao = Conversacao.instancia();
			return getValue(conversacao, property.toString(), elContext);
 
		} else if (base instanceof Conversacao) {
			return getValue((Conversacao) base, property.toString(), elContext);
		}
		return null;
	}
 
	private Object getValue(Conversacao conversacao, String property, ELContext elContext) {
		Object objeto = conversacao.get(property);
		elContext.setPropertyResolved(objeto != null);
		return objeto;
	}
 
	// os outros métodos foram suprimidos nesse exemplo
 
}

Essa é uma implementação comum de EL Resolver, onde eu uso o objeto retornado por Conversacao.instancia() para localizar as propriedades solicitadas.

Para o jsf um escopo nada mais é do que um java.util.Map, e de fato a classe Conversacao estende ConcurrentHashMap, ou seja, é um Map como pede o jsf. Fora isso os métodos get e put foram sobrescritos para funcionarem de acordo com nossa especificação de conversação, ou seja, se ela não foi iniciada tudo deve funcionar como request, agora quando a conversação é iniciada, os valores passam a ser guardados dentro da sessão do usuário, fazendo assim ficar statefull. Depois que a conversação é finalizada os valores voltam a ser guardados no request.

Antes de ver a implementação da classe Conversacao, o mais importante é entender como o mecanismo de resolução de EL funciona. Como visto no faces-config.xml, não existe nenhuma ligação da nossa implementação de ELResolver com a El “#{conversacao}” que colocamos na anotação @CustomScoped do nosso managed bean. Toda vez que uma EL é encontrada ela é passada para os ELResolvers contidos na aplicação. Obviamente existem outras implementações padrões já disponíveis, e a nossa vai entrar nessa fila. Como nenhuma das outras implementações vai saber resolver essa EL, ela acaba vindo para a nossa implementação, e então quando encontramos o objeto procurado utilizamos o método “ElContext.setPropertyResolved(boolean b)” passando true para informar que não precisa continuar perguntando para os demais ELResolver‘s, pois o nosso já descobriu quem é o objeto.

Existem alguns detalhes que devemos seguir ao implementar um escopo personalizado, como o de avisar, utilizando o novo sistema de eventos do JSF 2, que estamos criando ou destruindo nosso escopo.

Além disso para fazer essa implementação suportar múltiplas conversações seria necessário apenas colocar um nível a mais de mapa na nossa implementação, onde teríamos uma identificação da conversação e então seu contexto. Em vez de um simples Map, ficaria um Map de Map :D . Então para saber qual Map interno devolver a gente buscaria a conversação atual de algum contexto da nossa escolha, e poderíamos deixar um combobox sempre visível na tela para o usuária escolher a conversação que ele quer usar. Novamente nada de novo, tudo igual o funcionamento do Seam, por exemplo.

Agora sim vamos à implementação da classe Conversacao.

public class Conversacao extends ConcurrentHashMap<string,Object>{
 
	private static final long serialVersionUID = 7556965369432050706L;
 
	public static final String NOME_ESCOPO = "conversacao";
 
	private static final String CONVERSACAO_ATUAL = "exemplojsf2.conversacao.ConversacaoAtual";
 
	private boolean conversacaoNaoIniciada = true;
 
 
	private Conversacao() {
	}
 
	public static Conversacao instancia()
	{
		Map<string, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
		Conversacao conversacao = (Conversacao) sessionMap.get(CONVERSACAO_ATUAL);
		if(conversacao == null)
		{
			conversacao = new Conversacao();
			sessionMap.put(CONVERSACAO_ATUAL, conversacao);
		}
		return conversacao;
	}
 
	public Object get(Object propriedade)
	{
		//se a conversacao nao for iniciada funciona como request
		if(conversacaoNaoIniciada)
		{
			return pegarDoRequest(propriedade);
		}
 
		return super.get(propriedade);
	}
 
	@SuppressWarnings("unchecked")
	private Object pegarDoRequest(Object propriedade)
	{
		Map<string, Object> requestConversation = (Map<string, Object>) FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get(CONVERSACAO_ATUAL);
		if(requestConversation != null)
		{
			return requestConversation.get(propriedade);
		}
		return null;
	}
 
	@Override
	public Object put(String key, Object value)
	{
		//se a conversacao nao for iniciada funciona como request
		if(conversacaoNaoIniciada)
		{
			return colocarNoRequest(key, value);
		}
 
		return super.put(key, value);
	}
 
	@SuppressWarnings("unchecked")
	private Object colocarNoRequest(String key, Object value)
	{
 
		Map<string, Object> requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
		Map<string, Object> requestConversation = (Map<string, Object>) requestMap.get(CONVERSACAO_ATUAL);
		if(requestConversation == null)
		{
			requestConversation = new ConcurrentHashMap<string, Object>();
			requestMap.put(CONVERSACAO_ATUAL, requestConversation);
			return requestConversation.put(key, value);
 
		}
 
		return requestConversation.put(key, value);
	}
 
	public void iniciar()
	{
		conversacaoNaoIniciada = false;
		promoverRequestParaConversacao();
		notificarCriacao();
	}
 
	@SuppressWarnings("unchecked")
	private void promoverRequestParaConversacao()
	{
		Map<string, Object> requestConversation = (Map<string, Object>) FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get(CONVERSACAO_ATUAL);
		super.putAll(requestConversation);
	}
 
	private void notificarCriacao()
	{
		ScopeContext context = new ScopeContext(NOME_ESCOPO, this);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.getApplication().publishEvent(facesContext, PostConstructCustomScopeEvent.class, context);
	}
 
	public void finalizar()
	{
		notificarFinalizacao();
		conversacaoNaoIniciada = true;
		rebaixarConversacaoParaRequest();
 
	}
 
	@SuppressWarnings("unchecked")
	private void rebaixarConversacaoParaRequest()
	{
		Map<string, Object> requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
		Map<string, Object> requestConversation = (Map<string, Object>) requestMap.get(CONVERSACAO_ATUAL);
		if(requestConversation == null)
		{
			requestConversation = new ConcurrentHashMap<string, Object>();
			requestMap.put(CONVERSACAO_ATUAL, requestConversation);
		}
		requestConversation.putAll(this);
		this.clear();
		FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(CONVERSACAO_ATUAL);
	}
 
	private void notificarFinalizacao()
	{
		ScopeContext context = new ScopeContext(NOME_ESCOPO, this);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		facesContext.getApplication().publishEvent(facesContext, PreDestroyCustomScopeEvent.class, context);
	}
}

O download do exemplo pode ser feito aqui.

Com certeza deve ter algum errinho nessa implementação, mas se tiver não se desespere, por essas e outras que você certamente deve estar usando um framework mais confiável na tua aplicação do que uma implementação de “fundo de quintal” ;) . De qualquer forma encontrando os errinhos me diga que eu vou corrigindo.

Categories: JavaEE, JSF Tags: , ,
  1. May 12th, 2009 at 16:32 | #1

    Ficou bem bacana a especficação do @CustomScoped. Agora você sabe dizer se já é algo definitivo ou ainda poderá mudar?

    Muito bom o post!
    Abraços.

  2. May 12th, 2009 at 16:57 | #2

    Pelo que andei lendo acho que vai ser assim mesmo. Pessoalmente eu preferiria que já tivesse um suporte a conversação mesmo que fosse simples, mas nunca fazem tudo né :/
    Apesar do @ViewScoped já melhorar muito, com um @ConversationScoped ia ficar completo, mas isso deve ficar apenas para o JCDI (WebBeans) mesmo.

  3. Thiago
    May 3rd, 2010 at 14:43 | #3

    Eu tava vendo a documentação o proprio jsf 2 especifica um @Conversationscoped, você sabe como usa-lo?

  4. May 3rd, 2010 at 15:03 | #4

    Thiago, você sabe me dizer em que parte da documentação você encontrou @ConversationScoped?
    Até onde sei essa anotação é da CDI, e não do JSF 2.

  5. Thiago
    May 6th, 2010 at 18:17 | #5

    @Gilliard Cordeiro
    Hum verdade, e pode-se usa-la em managed bean?

  6. May 25th, 2010 at 11:18 | #6

    @Thiago
    Pode sim, mas como é CDI, você precisará dele “instalado”. Depois disso fica sendo um escopo normal, igual o conversation do Seam.

  7. juniorsatanas
    July 14th, 2010 at 19:59 | #7

    Ficou otimo, Control C + Control V

  8. carlos
    August 19th, 2010 at 02:43 | #8

    nobre, será que há alguma solução semelhante para o jsf 1.2, sem o uso do seam?

  9. September 1st, 2010 at 12:00 | #9

    @carlos Sem o uso do Seam tem, mas aí você sai de um e cai em outro, como por exemplo o Spring.
    Sem usar nada o que você pode fazer é criar teu próprio ELResolver e seguir a mesma lógica que eu segui acima. A diferença é que você não ver explicitamente um escopo, e sim vai convencionar que teus beans estarão nesse escopo virtual.

  10. Leonardo Pinto
    September 20th, 2011 at 22:27 | #10

    Em primeiro lugar Parabéns por seu trabalho, venho aprendendo um tanto contigo.

    Sei da sua opinião em não desconsiderar o Seam, mas há de convir que a depender ele é “uma bala de canhão p matar mosquito”. Venho conseguindo minha própria implementação de Conversation com o CustomScoped. ESTÁ “TUDO” OK, E SÓ deparei com a situação utilizando f:ajax.

    A partir da sua implementação, cheguei no exemplo gringo do Sebastian Hennebrueder (http://www.laliluna.de). A implementação dele está mais adiantada e utiliza um ID (hidden input) para controlar os escopos. Porém essa implementação só funciona (claro) com http post.

    - Você teria alguma idéia de como eu possa submeter esse ID do escopo utilizando f:ajax???

  11. September 21st, 2011 at 00:25 | #11

    Valeu @Leonardo Pinto,
    que bom que o que escrevo tem sido útil. Se uma implementação simples funciona pra você então ótimo. Quando eu digo pra não desconsiderar eu falo porque tem gente que é purista demais e deixa de usar pq nao está na spec. Como esse nao foi tua motivação, e sim simplicidade, então concordo 100% com vc. O melhor é sempre usar o mais simples. Para alguns casos o mais simples é implementar você mesmo, para outros é usar algo pronto pois você pode precisar de varias coisas, e aí não ficaria simples implementá-las.

    Esse exemplo do meu blog eu coloquei para mostrar a possibilidade do custom scope, não cheguei a enfrentar esses problemas que você falou porque não tenho usado isso em produção. Estou usando CDI e Seam3 (seam faces) que são praticamente “out of the box” no jboss 7, por exemplo.

    Será que o problema que você está tendo não tem a ver com o fato de você não estar enviando esse input hidden que contém o id quando você faze os requests? As vezes você está colocando algo muito restritivo no “process”. Tente além também o input na lista. Agora se você não está especificando isso, a coisa complica um pouquinho. Mas infelizmente não vou saber te ajudar pois depois de ter implementado pra colocar bo blog não mexi mais com isso. É até legal saber que tem servido para uso real na maioria das situações.

    Se você fizer esses ajustes, poste um comentário no post que eu coloquei o código que eu faço um update e te dou os créditos, mas agora não sei se vou conseguir testar isso para resolver pois o tempo está apertado aqui rsrs

    Desculpe não poder ajudar nesse caso. Mas precisando entre em contato que quem sabe da próxima eu consigo te ajudar.
    Até mais.

  12. Leonardo Pinto
    September 22nd, 2011 at 13:55 | #12

    Desculpe por nao ter explanado mais detalhadamente. Achei q vc nem teria tempo p responder. Pois é isso, vc acertou em cheio. Optei por simplicidade mesmo, alem de poder adentrar nas entranhas do JSF e entender o conceito melhor.

    Então: meu post está submetendo numa boa essa ID de escopo. Tudo ocorre bem na implementação. Com ela posso ter duas abas na mesma sessão dum mesmo manager bean, em escopos diferentes. Genial, sem precisar usar CDI nem Seam. Um dia pego eles….

    Só que ao utilizar f:ajax para renderizar parte da tela, é que a implementação deixa a desejar. Tipo na action incrementaContador, o uso do ajax nao submete essa ID.

    A pergunta é: Tem como no f:ajax do commandButton, podermos enviar um hidden (ID) desse scopo???

    Desde já muito grato cara, abração!!!

  13. September 22nd, 2011 at 15:06 | #13

    Então @Leonardo Pinto, mas esse ID não vai na hora do submit? Ele é um campo normal não é? E nas actions, independente de ser ajax ou não, os campos da tela tem que ir pro server. Nessa action vc consegue acessar outros campos da tela? Uma ação de incrementar o contador é complicado porque vc nao usa nada da tela, entao mesmo um execute=”@none” vai funcionar. Agora se você usa outros valores da tela, precisa que os valores da tela sejam enviados ao servidor durante o request. Faça um teste colocando execute=”@all” só pra ver se esse valor chega no server. Se der certo, então diminua o escopo do execute, senão você vai mandar muita coisa pro servidor e a requisição vai ficar mais lenta (tanto em trafego quanto em processamento do lado servidor)

    Qualquer coisa “tamos aí”

  14. Leonardo Pinto
    September 22nd, 2011 at 16:47 | #14

    Você ainda não entendeu. Se eu submeto o formulário com post/submit o ID vai de boa. Agora num evento f:ajax específico, num canto da tela, é que não consigo enviar um ID junto com essa action. O que quero é enviar um parâmetro junto ao action. Algo como:
     

    <h:commandButton action="#{testeBean.incrementarContador}"value="Incrementar Contador">
          <f:ajax render="@none">
                <f:param value="#{cid}" name="#{testeBean.conversation.id}"/>
          </f:ajax>
    </h:commandButton>

     
    porém não dessa forma acima, pois dessa forma acima faz com que o evento f:ajax submeta e renderize toda a tela. Entendeu?

  15. September 22nd, 2011 at 18:16 | #15

    @Leonardo Pinto
    mas no código que vc mandou vc colocou o name do param como um id. acho que não era para ser isso né?
    além disso, a forma de enviar as informações não difere, sendo ajax ou não. a diferença é que no ajax vc controla.
    Onde está o input que guarda esse id? nesse botão vc está fazendo um post, entao os parametros geralmente são passados por campos (incluindo esse oculto que guarda o id).

    vc tentou colocar o execute=”@all” no f:ajax? fazendo isso fica exatamente igual um submite sem ajax, só diferenciando que a tela do usuário nao vai piscar.
    se sem ajax o id vai, assim também tem que ir. Aí o que você vai ter que fazer é diminuir o que vc vai mandar, pois @all é muita coisa.

    Se eu entendi o teu problema, é que você está fazendo como se essa requisição ajax fosse get, mas ela é post.

  16. Leonardo Pinto
    September 22nd, 2011 at 19:04 | #16

    Foi cara, você está correto, realmente na pressa acabei invertendo a syntaxe, e que provavelmente esse erro fez com que o formulário sofresse um request.

    Valeu!!! Agora começo a entender. Você desde o início veio me alertando do conceito jsf/execute. E foi isso mesmo, com “@all” funcionou!!! A implementação Conversation do Sebastian já concebe perfeitamente essa situação. Repare como o código fica limpo:

    @ManagedBean(name = "testeBean")
    @CustomScoped("#{ConversationScope}")
    public class TesteBean {
    	@ManagedProperty(value = "#{conversation}")
    	private Conversation conversation;
    (...)
    <h:form id="formTeste">
      <cp:conversationId />
      (...)
      <h:commandButton action="#{testeBean.incrementarContador}" value="Incrementar Contador">
        <f:ajax render="output2 output3" execute="@all" />
      </h:commandButton>
    </h:form>

    “cp:conversationId” É uma taglib que implementa um hidden input com o ID do scopo, sacou?!

    Resta uma pequena dúvida: O que se pode usar em “execute”?! f:view, rich:panel ? O id do form pode ser usado no execute?

  17. September 23rd, 2011 at 10:03 | #17

    @Leonardo Pinto,
    nesse post eu falo o que você pode usar. Na pratica é qualquer id de componente. E se esse componente tiver outros dentro (panelGroup, por exemplo) os componentes filhos vão também

  18. Leonardo Pinto
    September 26th, 2011 at 10:35 | #18

    Boa noite! Cara, infelizmente quando fui por em prática a implementação Conversation (CustomScoped) num JSF mais complexo, a coisa desandou total. Ela se comportou péssimo. E entrei numa mesmo de encarar algo mais complexo porém que funcione. Já implantei antes Spring, porém me rendo ao fato que especificações são mais coerentes. Como você evangeliza, e percebo também que CDI é o caminho.

    Então: tentei de primeira a OpenEjbBean, porém empacou num erro e como a documentação dela é escassa, fiquei na mão;

    Sem querer adotar o Seam 3 (pois tenho visto relatos de bugs, etc.) Decidí pelo Weld (1.1.2), só que depois de apanhar um pouco com as dependências (pois não uso nem manjo bem de maven), resolví os tropeços, fiz a aplicação subir, porém somente os ManagerBeans do JSF é que são resolvidos. Não consigo acessar os beans declarados pelo Weld. Sempre recebo:

    org.apache.myfaces.view.facelets.el.ContextAwarePropertyNotFoundException: javax.el.PropertyNotFoundException: Target Unreachable, identifier ‘conversacaoBean’ resolved to null

    @Named(“conversacaoBean”)
    @ConversationScoped
    public class ConversacaoBean implements Serializable {
    private static final long serialVersionUID = 3282304148343869107L;

    @Inject
    private Conversation conversation;
    (…)

    Repare que tudo parece subir bem, sem erros:

    24/09/2011 22:26:33 org.apache.catalina.startup.TaglibUriRule body
    INFO: TLD skipped. URI: http://java.sun.com/jstl/xml is already defined
    24/09/2011 22:26:33 org.jboss.weld.bootstrap.WeldBootstrap
    INFO: WELD-000900 1.1.2 (Final)
    24/09/2011 22:26:33 org.jboss.weld.bootstrap.WeldBootstrap startContainer
    INFO: WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
    24/09/2011 22:26:34 org.jboss.weld.environment.tomcat7.Tomcat7Container initialize
    INFO: Tomcat 7 detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported
    24/09/2011 22:26:34 org.apache.myfaces.shared.config.MyfacesConfig getBooleanInitParameter
    INFO: No context init parameter ‘org.apache.myfaces.PRETTY_HTML’ found, using default value true
    (…)
    24/09/2011 22:26:38 org.apache.myfaces.config.DefaultFacesConfigurationProvider getClassloaderFacesConfig
    INFO: Reading config : jar:file:/Users/developer/Servidores/apache-tomcat-7/lib/weld-servlet.jar!/META-INF/faces-config.xml
    (…)

    Você tem idéia do que pode estar ocorrendo?

    O que você indica para meu projeto? Desejo algo simples e eficaz, mas que possa implantar sem auxílio de maven. Prefiro na unha :)

    Desde já fico muito grato pelo auxílio, forte abraço!

  19. September 26th, 2011 at 10:40 | #19

    @Leonardo Pinto
    O OpenEjbBean que vc fala é o OpenWebBeans da apache né? Eu nem cogitaria usar isso… sinceramente, depois que mexi um pouco com myfaces e outras bibliotecas da apache eu corro das implementações dela. muito bugadas pro meu gosto.

    O Seam3 não é uma implementação de CDI, então nem teria como vc usar ele diretamente, sem OpenWebBeans ou Weld. De qualquer forma eu uso, e acho que vc também vai usar, pelo menos o seam-faces do Seam3.

    O Weld é o que eu uso. Não lembro dos passos da configuração, mas na documentação tem os passos necessários. Já fiz isso há um tempo, mas agora não vou lembrar de tudo que precisa não. Mas vai pela documentação que vc consegue.

    Ah… olhando o log vc está usando myfaces…. tomara que esteja melhorzinho do que a ultima vez que tentei usar….
    Se fosse eu, mudaria pro mojarra. Muito mais testado, pois todo mundo usa ele. Mas aí é opção de cada um né :)

    Um abraço.

  20. Leonardo Pinto
    September 26th, 2011 at 20:10 | #20

    Resolvido o problema: faltava apenas a presença do arquivo beans.xml :) Achei que era apenas necessário no OWB. Sim… misturei os “bois”. Quis me referir ao OpenWebBeans mesmo.

    Concordo contigo. Venho percebendo que as implementações da Apache são imaturas, MUITO PORÉM são mais enxutas. Velozes! Percebemos isso pelo Tomcat que não consigo abandoná-lo. Queria transformá-lo num quase JEE. Algo que eu tenha o controle do que está plugado. Como venho fazendo. Item-a-item, vou instalando, personalizando. Pra mim está sendo boa experiência. Afinal, quero mesmo tentar arrastar para produção esse ambiente. Algumas pessoas o faz, eu acredito que seja possível e viável o uso do Tomcat em produção. Ver pra crer!
    Quanto ao MyFaces, realmente eu mantenho os dois. As vezes testo no Mojarra. É que simpatizei com o output do console do MyFaces.

    Sei que o Seam é uma coletânea de frameworks. Mas é que vejo poucas pessoas encarar a implantação na unha do Weld. Então recorrem logo ao “balão mágico” do Seam.

    Bom, estou agora tentando agora simular um ambiente J2EE em JSE, com inject e intercept. Para controlar transação e o entity manager. Como ensina aqui:
    http://seamframework.org/Documentation/WeldAndJPARunningInTomcat

    - O que você acha dessa empreitada??? Não quero abarrotar o projeto com EJB, entende?!!

  21. September 27th, 2011 at 08:07 | #21

    @Leonardo Pinto

    É simples fazer a configuração do Weld e do Seam JPA em ambiente não EE (documentação). Mas tá vendo que já vai usar seam-faces e seam-persistence? Essa visão que você está do Seam é do Seam2. O Seam3 na maioria das vezes você coloca o jar e o resto praticamente acontece sozinho. Pelo menos em ambiente JEE6, pois nesse caso não é preciso configurar as coisas pois o servidor descobre o que precisa ser implantado com base nos teus jars. Um servlet container 3.0 também vai te ajudar. E não tem diferença entre colocar Weld+Seam ou só Weld. Pois sempre você vai ter que colocar o Weld antes. E como falei, se você usa CDI e JSF, você vai querer usar seam-faces. E se quer controle transacional em ambiente não JEE, você vai usar seam-persistence. Seam3 não é o canhão pra matar formiga que era o Seam2.

    Agora falando do tomcat, você vai ter que configurar uma coisinha ou outra, mas é perfeitamente possivel colocar em produção. Tem uma pancada de coisas em produção em Jetty e Tomcat. Servidor de aplicação não é escolhido por ser melhor para produção, e sim por ter vários serviços prontos. Serviços esses que você vai colocar no Tomcat. Será que no final você não vai ter praticamente um servidor de aplicação feito à mão? Se você vai usar tudo que tem num app server, será que não vale a pena dar uma olhada num jboss7 com seu esquema de modularização?

  22. Leonardo Pinto
    September 27th, 2011 at 20:59 | #22

    Sim! A configuração é simples. Sim, passei o olho no Seam-Persistence. Realmente é o que acabei por me render mesmo. :) Genial! E sáiba você que o que já estava utilizando é justamente essa classe SePersistenceContextExtension.java dessa implementação (). Só que empacotada por mim mesmo. Ela implementa transação e injeção de entity manager em ambiente SE/servelet. Está sendo fantástico, pois quando tiver (e se tiver) que escalonar a aplicação para um ambiente mais robusto (que certamente será JBoss) é só re-injetar os componentes. Sempre foi essa minha intenção.

    Humm, então é verdade que o jboss é todo plugável?! Ótimo saber, mas é inegável o desempenho ao subir um tomcat enxuto. Você sabe do que estou falando. Porém me sinto inclinado por um JEE. E pelo JBoss!!!

    - O que você diz como “servlet container 3.0″?!?!?

    Tenho que concordar contigo que estou reinventando a roda, tentando transformar o tomcat num 1/2 jee, mas existe diferença. O item-a-item inegavelmente faz com que fique menor. Mas você me convenceu. Me rendo a esse fato (jboss7). Logo, com mais tempo destrincho ele.

    Cara, valeu a força. Que o universo lhe devolva em dobro!!!

  23. September 28th, 2011 at 12:25 | #23

    @Leonardo Pinto

    Um servlet container 3.0 é um tomcat ou jetty com suporte a servlet 3.0. Essa versão de servlet permite menos configuração para adicionar coisas, pois em vez de declararmos coisas no web.xml o container lê as anotações dos jars. Então não precisamos mais colocar o servlet do nosso framework no web.xml. Aliado a isso o CDI também lê os jars, então se você usar o default também não vai precisar configurar (mas o CDI não tem a ver com o servlet container).

    Concordo que o tomcat é bem mais leve e sobe em menos de 1 segundo. Mas o jboss7 e o glassfish sobem em menos de 3, então a diferença principal vai ficar no tempo que leva para a tua aplicação subir. Se você vai usar tecnologias não JEE eu acredito que valha mais a pena subir num tomcat ou jetty. Mas se você vai usar a stack padrão, fica mais simples subir logo num servidor de aplicações como jboss ou glassfish.

    E valeu cara. Bom papo :)

  24. Leonardo Pinto
    September 28th, 2011 at 22:05 | #24

    Então, meio que peguei aqui o Seam :) No item-a-item, implantei a seam-persistence.jar e suas poucas dependências. Sobe de boa:

    INFO: Seam Solder 3.0.0.Final (build id: 3.0.0.Final)
    INFO: Preventing class org.jboss.seam.transaction.UTTransaction from being installed as bean due to @Veto annotation
    INFO: Preventing class org.jboss.seam.transaction.EntityTransaction from being installed as bean due to @Veto annotation
    28/09/2011 22:49:32 org.jboss.seam.persistence.ManagedPersistenceContextExtension processAnnotatedType
    INFO: Configuring Seam Managed Persistence Context from producer field pacote.library.PersistenceProducer.producerField with qualifiers [@javax.enterprise.inject.Any(), @javax.enterprise.inject.Default()]
    (…)

    Porém ao usar o entity manager:

    Caused by: javax.persistence.PersistenceException: No Persistence provider for EntityManager named at javax.persistence.Persistence.createEntityManagerFactory(Unknown Source)

    Segui o manual Seam Persistence Reference, bem escasso por sinal. E o que intrigou é o que ele manda no item 1.4.1, fala pra usar: “@PersistenceUnit”, mas onde nós especificamos o nome da unidade de persistência?! Como @PersistenceContext(unitName = “nomePU”)

  25. September 29th, 2011 at 10:11 | #25

    @Leonardo Pinto
    rapaz, faz tempo que usei isso. Atualmente ou tenho usado JEE6 (onde não precisa dessas configurações) ou uma stack inteira paralela (no vaso o Grails).
    Já usei tomcat com seam-persistence, e funcionou seguindo a documentação. Acho até que teve um ou outro detalhe, mas nada demais. E agora além de estar sem o projeto que usei aqui, provavelmente a versão estaria desatualizada.

  26. Giuliano Costa
    December 26th, 2011 at 08:14 | #26

    Olá Gilliard,

    estou tentando fazer uns testes com o CustomScoped e estou tendo um probleminha. Talvez tu ou alguém do blog já tenha passado por ele. Estou usando primefaces 2.2. O problema é o seguinte, quando eu entro na página que tem um managedBean com CustomScoped, todos recursos (css e js) de dependência do prime são carregados na página, porém se eu do um “reload” ná pagina ou se eu saio e entro nela, algumas recursos não são mais carregados. Isso ocasiona uma má renderização na tela pela falta das bibliotecas que os componetens fazem uso. Ex.: PrimeFaces.widget.CommandButton is not a constructor, PrimeFaces.widget.TabView is not a constructor, etc…

    Estou debugando aqui e não consigo encontrar o motivo para não carregar essa bibliotecas nas requisições posteriores ao inicio da Conversação. Vi que todos os Componentes do Prime estão anotados corretamente com as dependências do tipo:

    @ResourceDependencies({
    @ResourceDependency(library=”primefaces”, name=”forms/forms.css”),
    @ResourceDependency(library=”primefaces”, name=”jquery/jquery.js”),
    @ResourceDependency(library=”primefaces”, name=”core/core.js”),
    @ResourceDependency(library=”primefaces”, name=”forms/forms.js”)
    })

    Será que tu ou alguém do blog já passou por esse problema?

  1. May 12th, 2009 at 11:38 | #1