Problemas com serialização da sessão?

Ao compilar meu código está ocorrendo este erro:

Mai 09, 2012 10:10:53 PM org.apache.catalina.core.ApplicationContext log
Informações: ContextListener: contextInitialized()
Mai 09, 2012 10:10:53 PM org.apache.catalina.core.ApplicationContext log
Informações: SessionListener: contextInitialized()
Mai 09, 2012 10:20:05 PM org.apache.catalina.core.ApplicationContext log
Informações: SessionListener: contextDestroyed()
Mai 09, 2012 10:20:05 PM org.apache.catalina.core.ApplicationContext log
Informações: ContextListener: contextDestroyed()
Mai 09, 2012 10:20:05 PM org.apache.catalina.session.StandardSession writeObject
Advertência: Cannot serialize session attribute usuarioMB for session E7E0E904D66A67BCA853339A606A7480
java.io.NotSerializableException: com.modelo.dao.UsuarioDAO
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
	at org.apache.catalina.session.StandardSession.writeObject(StandardSession.java:1585)
	at org.apache.catalina.session.StandardSession.writeObjectData(StandardSession.java:1015)
	at org.apache.catalina.session.StandardManager.doUnload(StandardManager.java:528)
	at org.apache.catalina.session.StandardManager.unload(StandardManager.java:469)
	at org.apache.catalina.session.StandardManager.stop(StandardManager.java:678)
	at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4882)
	at org.apache.catalina.core.ContainerBase.removeChild(ContainerBase.java:936)
	at org.apache.catalina.startup.HostConfig.undeployApps(HostConfig.java:1359)
	at org.apache.catalina.startup.HostConfig.stop(HostConfig.java:1330)
	at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:326)
	at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142)
	at org.apache.catalina.core.ContainerBase.stop(ContainerBase.java:1098)
	at org.apache.catalina.core.ContainerBase.stop(ContainerBase.java:1110)
	at org.apache.catalina.core.StandardEngine.stop(StandardEngine.java:468)
	at org.apache.catalina.core.StandardService.stop(StandardService.java:604)
	at org.apache.catalina.core.StandardServer.stop(StandardServer.java:788)
	at org.apache.catalina.startup.Catalina.stop(Catalina.java:662)
	at org.apache.catalina.startup.Catalina.start(Catalina.java:629)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
	at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)

Minha classe UsuarioMB

package com.modelo.managedBean;

import com.modelo.bean.Administrador;
import com.modelo.bean.Promotor;
import com.modelo.dao.UsuarioDAO;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;

@ManagedBean
@SessionScoped
public class UsuarioMB implements Serializable {

	private UsuarioDAO usuarioDAO = new UsuarioDAO();
	private transient Administrador administrador;
	private transient Promotor promotor;

	public String logOut() {
		getRequest().getSession().invalidate();
		return "logout";
	}

	public boolean isUserAdmin() {
		return getRequest().isUserInRole("admin");
	}

	public boolean isUserPromotor() {
		return getRequest().isUserInRole("promotor");
	}

	private HttpServletRequest getRequest() {
		return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
	}

	// REGRAS DE NEGOCIO
	public Promotor getPromotor() {
		if (promotor == null) {
			ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
			String login = context.getUserPrincipal().getName();
			promotor = usuarioDAO.recuperarPromotor(login);
		}
		return promotor;
	}

	public Administrador getAdministrador() {
		if (administrador == null) {
			ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
			String login = context.getUserPrincipal().getName();
			administrador = usuarioDAO.recuperarAdministrador(login);
		}
		return administrador;
	}
}

Alguém tem idéia por que isso ocorre?
Estou usando JSF+Hibernate

A classe UsuarioDAO está implementando Serializable?

Pode ser ai o problema.

Acredito que não, preciso verificar. Mas o JSF não exige que somente os managed beans sejam serializáveis?

usar a palavra reservada ou a anotação dá na mesma?

Ex:[code] transient private Objeto objetoComPalavraReservada;

@Transient
private Objeto objetoAnotado;[/code]O efeito é o mesmo ?

O ManagedBean não precisa ser Serializado, remova a extensão para Serializable e compile novamente.

[quote=Big E]O ManagedBean não precisa ser Serializado, remova a extensão para Serializable e compile novamente.[/quote]Sendo session scoped precisa sim. Alguns servidores emitem alerta (dependendo do log), outros dão erro.
E quando acontece a serialização, tudo que esta no MB precisa tb ser serializado a não ser que seja transiente.

Eu tive problema com a anotação @Transiente uma vez e me esqueci de testar colocando a palavra transiente. Acabei por tornar tudo serializable.

E aí jakefrog, cara dei uma pesquisada a respeito e não vi nada falando sobre a obrigatóriedade ou distinção depente de escopo.
Os objetos são obrigatóriamente serializáveis, nesse caso, se suas informações forem trafegadas pela rede, o bean inteiro não teria necessidade de serialização, tornando-se opcional nesse contexto.

gRoOve,

Suas entidades, Promotor, Administrador, essas sim deverão ser anotadas como tal.
Agora respondendo a dúvida “Alguém tem idéia por que isso ocorre?”
O problema está sendo ocasionado justamente pelo fato do Bean estar descrito como Serializable e nem todos seus atributos não estarem, como é o caso do UsuarioDAO, se sua abordagem for permanecer com o ManagedBean inteiro como Serializable, você terá que gerenciar os atributos que não deseja serializar através da anotation @Transient ou a palavra reservada transient como o jakefrod disse no último post, porém ainda permaneço com a idéia de que não é obrigatório descrever o ManagedBean como Serializável, idenpendente de seu escopo.

[quote=Big E]E aí jakefrog, cara dei uma pesquisada a respeito e não vi nada falando sobre a obrigatóriedade ou distinção depente de escopo.
Os objetos são obrigatóriamente serializáveis, nesse caso, se suas informações forem trafegadas pela rede, o bean inteiro não teria necessidade de serialização, tornando-se opcional nesse contexto.[/quote]Eu quis dizer que precisa (para correto funcionamento), e não obrigatório! =P
Deixa eu me expressar melhor. Vou citar aqui partes do livro Core Java Server Faces (3rd edition):



O sessionBean pode ser a qualquer momento “passivado”, e por causa disso que é bom utilizar o serializable.

O JB7 alerta quando você está utilizado session scoped sem serializable.

[quote=Big E]E aí jakefrog, cara dei uma pesquisada a respeito e não vi nada falando sobre a obrigatóriedade ou distinção depente de escopo.
Os objetos são obrigatóriamente serializáveis, nesse caso, se suas informações forem trafegadas pela rede, o bean inteiro não teria necessidade de serialização, tornando-se opcional nesse contexto.

gRoOve,

Suas entidades, Promotor, Administrador, essas sim deverão ser anotadas como tal.
Agora respondendo a dúvida “Alguém tem idéia por que isso ocorre?”
O problema está sendo ocasionado justamente pelo fato do Bean estar descrito como Serializable e nem todos seus atributos não estarem, como é o caso do UsuarioDAO, se sua abordagem for permanecer com o ManagedBean inteiro como Serializable, você terá que gerenciar os atributos que não deseja serializar através da anotation @Transient ou a palavra reservada transient como o jakefrod disse no último post, porém ainda permaneço com a idéia de que não é obrigatório descrever o ManagedBean como Serializável, idenpendente de seu escopo.[/quote]

Esse post no StackOverFlow explica muito porque SessionScope MB’s devem ser serializáveis:

isso porque os MB são colocados na HttpSession. Por sua vez, a HttpSession pode ser “passivada” para o disco rígido ou enviada para outro servidor, para balancear carga. Sendo assim, a HttpSession e qualquer atributos colocado nela devem ser marcados como Serializable, incluindo SessionScoped MB’s.

Isso não quer dizer que marcar UsuarioDAO vai resolver seu problema. Geralmente DAO’s mantém conexões com o banco de dados como atributos de classe, que por sua vez não são Serializable. Daí você precisaria criar um mecanismo para liberar a conexão antes da serialização e obter uma nova após a desserialização. Esse processo pode ser um pouco complicado. Além do mais, mesmo que a sessão não sofra serialização e fique na memória, o seu DAO vai ser mantido durante todo o tempo da sessão. Consequentemente, você vai ter uma conexão ociosa aberta durante esse tempo todo, que pode inclusive, ferrar com o desempenho do se BD.

Sendo assim, acho que a melhor solução é criar uma instância nova do DAO a cada chamada de método de negócio.

Caras, obrigado pelo esclarecimento.

Resumindo, não preciso de quaiser objetos serializáveis, se eu tirar a implementação Serializable de todas as classes vai ficar gerando um alerta, mas não vai acarretar algum erro, certo?

Não, cara… rs. Não é isso.

resumidamente eu diria que o que a sugestão do rmendes08 é a maneira mais limpa e simples de se resolver seu problema.

Acho que entendi, vejam se minha interpretação está correta:
Como eu estou instanciando o usuarioDAO logo na instanciação do UsuarioMB, o meu objeto usuarioDAO fica na sessão do UsuarioMB com a conexão com o BD aberta e como alguns atributos de classe(o que são esses atributos? Não entendi -.-) não são serializáveis, ocorre o alerta. Então eu posso instanciar a usuarioDAO somente quando for carregar os dados de um objeto - neste caso - e depois setar como null pro coletor de lixo limpar ela.
Só não entendi esta parte:

A questão é não serializar o usuarioDAO, é isso? Devido aos atributos citados - que eu não entendi :slight_smile:

private UsuarioDAO usuarioDAO = new UsuarioDAO(); private transient Administrador administrador; private transient Promotor promotor; Esses aí acima que são seus atributos de classe.

Você pode também criar métodos que instanciam um DAO somente na hora de manipular o banco. como o rmendes08 comentou.

Não precisa ter ele como um atributo na sua classe.

Então, eu fiz exatamente isso. Instancio o DAO somente na hora que vou usar, e depois que eu uso configuro ele para null. Porém deixo ele como atributo da classe. O alerta parou de ocorrer agora.
Obrigado a todos pelas respostas :slight_smile: