From d20b49c0b690e8de0de5c4582dc4e9b4a0f4e415 Mon Sep 17 00:00:00 2001 From: Danilo Costa Viana Date: Thu, 26 Nov 2015 16:03:01 -0300 Subject: [PATCH] Refatorando sincronização de criação de views para o ViewContext. --- impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewBeanStore.java | 56 ++++++++++++++++++++++++++++++++++++++++++++++---------- impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewContextImpl.java | 41 ++++++++++++++++++++++------------------- 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewBeanStore.java b/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewBeanStore.java index 9adda0e..8cb213c 100644 --- a/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewBeanStore.java +++ b/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewBeanStore.java @@ -1,6 +1,8 @@ package br.gov.frameworkdemoiselle.internal.context; import java.io.Serializable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; @@ -18,23 +20,51 @@ import javax.enterprise.inject.Alternative; public class FacesViewBeanStore implements Serializable { private static final long serialVersionUID = -8265458933971929432L; - - private Long lastViewId = null; - - private BeanStore store; + + private static final int MAX_VIEWS_IN_SESSION = 15; + + private static final AtomicLong AGE_COUNTER = new AtomicLong(); + + private ConcurrentHashMap beanStoreCache = new ConcurrentHashMap(); synchronized BeanStore getStore(Long viewId, AbstractCustomContext context) { - if (lastViewId == null || !lastViewId.equals(viewId)) { - clear(context); - lastViewId = viewId; - store = AbstractCustomContext.createStore(); + BeanStoreAge beanStoreWithAge = beanStoreCache.get(viewId); + if (beanStoreWithAge == null) { + beanStoreWithAge = new BeanStoreAge(); + beanStoreWithAge.beanStore = AbstractCustomContext.createStore(); + beanStoreCache.put(viewId, beanStoreWithAge); + } + beanStoreWithAge.age = AGE_COUNTER.getAndIncrement(); + + if (beanStoreCache.size() > MAX_VIEWS_IN_SESSION) { + clearExpiredStores(context); } + + return beanStoreWithAge.beanStore; + } - return store; + private synchronized void clearExpiredStores(AbstractCustomContext context) { + BeanStoreAge oldestCache = null; + for (BeanStoreAge cache : beanStoreCache.values()) { + if (oldestCache == null || cache.age < oldestCache.age) { + oldestCache = cache; + } + } + + if (oldestCache != null) { + clear(context, oldestCache.beanStore); + } + } + + public void clear(AbstractCustomContext context) { + for (BeanStoreAge cache : beanStoreCache.values()) { + clear(context, cache.beanStore); + } + beanStoreCache.clear(); } @SuppressWarnings({ "rawtypes", "unchecked" }) - public void clear(AbstractCustomContext context) { + public void clear(AbstractCustomContext context, BeanStore store) { if (store != null) { for (String id : store) { Contextual contextual = context.getContextualStore().getContextual(id); @@ -48,4 +78,10 @@ public class FacesViewBeanStore implements Serializable { store.clear(); } } + + private class BeanStoreAge implements Serializable { + private static final long serialVersionUID = 1L; + BeanStore beanStore; + long age; + } } diff --git a/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewContextImpl.java b/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewContextImpl.java index 307a5e2..b11671c 100644 --- a/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewContextImpl.java +++ b/impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewContextImpl.java @@ -36,6 +36,7 @@ */ package br.gov.frameworkdemoiselle.internal.context; +import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import javax.enterprise.context.ApplicationScoped; @@ -90,37 +91,39 @@ public class FacesViewContextImpl extends AbstractCustomContext implements ViewC return null; } - /* - * Tenta obter o viewID de forma não thread-safe por questões de performance. - * Se o viewID não existe entra em um trecho thread-safe para incrementa-lo, evitando - * conflito entre duas requests tentando incrementar esse número. - */ + // Tenta obter a ViewStore associada a sessão atual. Se uma não existe, entra + // em um trecho sincronizado para criar a store de forma atômica. + FacesViewBeanStore currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); + if (currentStore==null){ + synchronized (this) { + currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); + if (currentStore==null){ + currentStore = new FacesViewBeanStore(); + session.setAttribute(VIEW_STORE_KEY, currentStore); + } + } + } + + // Obtém o View ID atualmente associado a essa view. Se um ID não existe (primeira vez + // que uma view é acessada) então um ID é criado de forma atômica, assegurando que + // cada ID é usado por apenas uma VIEW. Long viewId = (Long)Faces.getViewMap().get(FACES_KEY); if (viewId==null){ - synchronized (this) { + Map facesViewMap = Faces.getViewMap(); + + synchronized (currentStore) { //Tenta obte-lo novamente, caso entre a primeira tentativa e o bloqueio //da thread outra thread já tenha criado o número. viewId = (Long)Faces.getViewMap().get(FACES_KEY); if (viewId==null){ viewId = atomicLong.incrementAndGet(); - Faces.getViewMap().put(FACES_KEY, viewId); + facesViewMap.put(FACES_KEY, viewId); } } } - //A mesma técnica de bloqueio de thread acima é usada aqui para - //criar um SessionBeanStore caso o mesmo ainda não exista. - FacesViewBeanStore currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); - if (currentStore==null){ - synchronized (this) { - currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); - if (currentStore==null){ - currentStore = new FacesViewBeanStore(); - session.setAttribute(VIEW_STORE_KEY, currentStore); - } - } - } + return currentStore.getStore(viewId, this); } -- libgit2 0.21.2