FacesContextProxy executa indevidamente o construtor de FacesContext

Descrição

Para permitir a injeção de instâncias da classe javax.faces.context.FacesContext, o Demoiselle emprega uma classe FacesContextProxy que herda da primeira.

O objetivo é fornecer um proxy serializável (FacesContextProxy implementa Serializable, FacesContext não) que possui uma referência fraca à instância concreta de FacesContext, já que a mesma deve estar sempre associada à thread que a criou e não deve ser serializada ou associada a beans de escopo maior que o request que a criou.

Caso o proxy seja serializado e deserializado (como pode acontecer em ambientes distribuidos), o proxy se encarrega de gerar uma nova referência fraca ao objeto concreto que será anexada ao request atual, permitindo que FacesContext seja injetado em classes normalmente não permitidas por terem escopo maior que o request, por exemplo classes de escopo ViewScoped ou SessionScoped.

O problema surge no fato que o proxy herda de FacesContext e por isso executa seu construtor ao ser construído. O construtor de FacesContext possui código peculiar que detecta via reflexão qual thread, classe e método o invocaram, para então associar-se à essa thread.

Esse código é bloqueante. Isso normalmente não é um problema para instâncias de FacesContext fabricadas pelo container, mas no caso do Demoiselle o construtor executa toda vez que o usuário injeta uma instância de FacesContext e o framework cria um FacesContextProxy.

Essas execuções constantes de um construtor bloqueante geraram em alguns sistemas uma perceptível queda de performance em aplicações que recebem muitos requests em uma tela gerenciada por ManagedBeans que injetam FacesContext.

Para resolver o problema será necessário mudar a forma como o Demoiselle opera. Os seguintes passos serão implementados:

  • Não haverá mais um FacesContextProxy.

  • Haverá agora um FacesContextProducer de escopo Dependent.

  • Se FacesContext for injetado em uma classe de escopo maior que Request, pela especificação CDI o atributo onde ele é injetado deverá ser marcado como 'transient' (ex: @Inject private transient FacesContext context;).

  • FacesContextProducer consegue detectar casos em que a regra acima for violada e lançará um DemoiselleException (associado a um NotSerializableException) informando o problema e descrevendo a solução.

  • Infelizmente não é possível detectar casos em que a injeção se dá via Beans.getReference(). Isso não é problema se a variável que receber a referência tiver escopo de método, mas é recomendado não atribuir o retorno de Beans.getReference(FacesContext.class) a um atributo de classe, ou então se isso for necessário deve-se marcar o atributo como 'transient'.

Em testes detectamos que ao injetar uma classe serializável de escopo longo (ex: ViewScoped) que contém atributos transientes, o CDI gera um proxy no lugar. Serializar esse proxy e depois deserializa-lo mostrou que o CDI mantém os atributos transientes injetados, diferente do comportamento normal de classes serializadas onde atributos transientes retornam nulos, mostrando que o CDI está preparado para lidar com passivamento e serialização de beans de escopo longo.

Responsável

Danilo Costa Viana

Criador

Danilo Costa Viana

Etiquetas

None

Git Pull Request

None

Versões de correção

Prioridade

Trivial
Configure