Commit d20b49c0b690e8de0de5c4582dc4e9b4a0f4e415
1 parent
bec80284
Refatorando sincronização de criação de views para o ViewContext.
Showing
2 changed files
with
68 additions
and
29 deletions
Show diff stats
impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewBeanStore.java
| 1 | package br.gov.frameworkdemoiselle.internal.context; | 1 | package br.gov.frameworkdemoiselle.internal.context; |
| 2 | 2 | ||
| 3 | import java.io.Serializable; | 3 | import java.io.Serializable; |
| 4 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 5 | +import java.util.concurrent.atomic.AtomicLong; | ||
| 4 | 6 | ||
| 5 | import javax.enterprise.context.spi.Contextual; | 7 | import javax.enterprise.context.spi.Contextual; |
| 6 | import javax.enterprise.context.spi.CreationalContext; | 8 | import javax.enterprise.context.spi.CreationalContext; |
| @@ -18,23 +20,51 @@ import javax.enterprise.inject.Alternative; | @@ -18,23 +20,51 @@ import javax.enterprise.inject.Alternative; | ||
| 18 | public class FacesViewBeanStore implements Serializable { | 20 | public class FacesViewBeanStore implements Serializable { |
| 19 | 21 | ||
| 20 | private static final long serialVersionUID = -8265458933971929432L; | 22 | private static final long serialVersionUID = -8265458933971929432L; |
| 21 | - | ||
| 22 | - private Long lastViewId = null; | ||
| 23 | - | ||
| 24 | - private BeanStore store; | 23 | + |
| 24 | + private static final int MAX_VIEWS_IN_SESSION = 15; | ||
| 25 | + | ||
| 26 | + private static final AtomicLong AGE_COUNTER = new AtomicLong(); | ||
| 27 | + | ||
| 28 | + private ConcurrentHashMap<Long, BeanStoreAge> beanStoreCache = new ConcurrentHashMap<Long, FacesViewBeanStore.BeanStoreAge>(); | ||
| 25 | 29 | ||
| 26 | synchronized BeanStore getStore(Long viewId, AbstractCustomContext context) { | 30 | synchronized BeanStore getStore(Long viewId, AbstractCustomContext context) { |
| 27 | - if (lastViewId == null || !lastViewId.equals(viewId)) { | ||
| 28 | - clear(context); | ||
| 29 | - lastViewId = viewId; | ||
| 30 | - store = AbstractCustomContext.createStore(); | 31 | + BeanStoreAge beanStoreWithAge = beanStoreCache.get(viewId); |
| 32 | + if (beanStoreWithAge == null) { | ||
| 33 | + beanStoreWithAge = new BeanStoreAge(); | ||
| 34 | + beanStoreWithAge.beanStore = AbstractCustomContext.createStore(); | ||
| 35 | + beanStoreCache.put(viewId, beanStoreWithAge); | ||
| 36 | + } | ||
| 37 | + beanStoreWithAge.age = AGE_COUNTER.getAndIncrement(); | ||
| 38 | + | ||
| 39 | + if (beanStoreCache.size() > MAX_VIEWS_IN_SESSION) { | ||
| 40 | + clearExpiredStores(context); | ||
| 31 | } | 41 | } |
| 42 | + | ||
| 43 | + return beanStoreWithAge.beanStore; | ||
| 44 | + } | ||
| 32 | 45 | ||
| 33 | - return store; | 46 | + private synchronized void clearExpiredStores(AbstractCustomContext context) { |
| 47 | + BeanStoreAge oldestCache = null; | ||
| 48 | + for (BeanStoreAge cache : beanStoreCache.values()) { | ||
| 49 | + if (oldestCache == null || cache.age < oldestCache.age) { | ||
| 50 | + oldestCache = cache; | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + if (oldestCache != null) { | ||
| 55 | + clear(context, oldestCache.beanStore); | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public void clear(AbstractCustomContext context) { | ||
| 60 | + for (BeanStoreAge cache : beanStoreCache.values()) { | ||
| 61 | + clear(context, cache.beanStore); | ||
| 62 | + } | ||
| 63 | + beanStoreCache.clear(); | ||
| 34 | } | 64 | } |
| 35 | 65 | ||
| 36 | @SuppressWarnings({ "rawtypes", "unchecked" }) | 66 | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| 37 | - public void clear(AbstractCustomContext context) { | 67 | + public void clear(AbstractCustomContext context, BeanStore store) { |
| 38 | if (store != null) { | 68 | if (store != null) { |
| 39 | for (String id : store) { | 69 | for (String id : store) { |
| 40 | Contextual contextual = context.getContextualStore().getContextual(id); | 70 | Contextual contextual = context.getContextualStore().getContextual(id); |
| @@ -48,4 +78,10 @@ public class FacesViewBeanStore implements Serializable { | @@ -48,4 +78,10 @@ public class FacesViewBeanStore implements Serializable { | ||
| 48 | store.clear(); | 78 | store.clear(); |
| 49 | } | 79 | } |
| 50 | } | 80 | } |
| 81 | + | ||
| 82 | + private class BeanStoreAge implements Serializable { | ||
| 83 | + private static final long serialVersionUID = 1L; | ||
| 84 | + BeanStore beanStore; | ||
| 85 | + long age; | ||
| 86 | + } | ||
| 51 | } | 87 | } |
impl/extension/jsf/src/main/java/br/gov/frameworkdemoiselle/internal/context/FacesViewContextImpl.java
| @@ -36,6 +36,7 @@ | @@ -36,6 +36,7 @@ | ||
| 36 | */ | 36 | */ |
| 37 | package br.gov.frameworkdemoiselle.internal.context; | 37 | package br.gov.frameworkdemoiselle.internal.context; |
| 38 | 38 | ||
| 39 | +import java.util.Map; | ||
| 39 | import java.util.concurrent.atomic.AtomicLong; | 40 | import java.util.concurrent.atomic.AtomicLong; |
| 40 | 41 | ||
| 41 | import javax.enterprise.context.ApplicationScoped; | 42 | import javax.enterprise.context.ApplicationScoped; |
| @@ -90,37 +91,39 @@ public class FacesViewContextImpl extends AbstractCustomContext implements ViewC | @@ -90,37 +91,39 @@ public class FacesViewContextImpl extends AbstractCustomContext implements ViewC | ||
| 90 | return null; | 91 | return null; |
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | - /* | ||
| 94 | - * Tenta obter o viewID de forma não thread-safe por questões de performance. | ||
| 95 | - * Se o viewID não existe entra em um trecho thread-safe para incrementa-lo, evitando | ||
| 96 | - * conflito entre duas requests tentando incrementar esse número. | ||
| 97 | - */ | 94 | + // Tenta obter a ViewStore associada a sessão atual. Se uma não existe, entra |
| 95 | + // em um trecho sincronizado para criar a store de forma atômica. | ||
| 96 | + FacesViewBeanStore currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); | ||
| 97 | + if (currentStore==null){ | ||
| 98 | + synchronized (this) { | ||
| 99 | + currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); | ||
| 100 | + if (currentStore==null){ | ||
| 101 | + currentStore = new FacesViewBeanStore(); | ||
| 102 | + session.setAttribute(VIEW_STORE_KEY, currentStore); | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + // Obtém o View ID atualmente associado a essa view. Se um ID não existe (primeira vez | ||
| 108 | + // que uma view é acessada) então um ID é criado de forma atômica, assegurando que | ||
| 109 | + // cada ID é usado por apenas uma VIEW. | ||
| 98 | Long viewId = (Long)Faces.getViewMap().get(FACES_KEY); | 110 | Long viewId = (Long)Faces.getViewMap().get(FACES_KEY); |
| 99 | if (viewId==null){ | 111 | if (viewId==null){ |
| 100 | - synchronized (this) { | 112 | + Map<String, Object> facesViewMap = Faces.getViewMap(); |
| 113 | + | ||
| 114 | + synchronized (currentStore) { | ||
| 101 | 115 | ||
| 102 | //Tenta obte-lo novamente, caso entre a primeira tentativa e o bloqueio | 116 | //Tenta obte-lo novamente, caso entre a primeira tentativa e o bloqueio |
| 103 | //da thread outra thread já tenha criado o número. | 117 | //da thread outra thread já tenha criado o número. |
| 104 | viewId = (Long)Faces.getViewMap().get(FACES_KEY); | 118 | viewId = (Long)Faces.getViewMap().get(FACES_KEY); |
| 105 | if (viewId==null){ | 119 | if (viewId==null){ |
| 106 | viewId = atomicLong.incrementAndGet(); | 120 | viewId = atomicLong.incrementAndGet(); |
| 107 | - Faces.getViewMap().put(FACES_KEY, viewId); | 121 | + facesViewMap.put(FACES_KEY, viewId); |
| 108 | } | 122 | } |
| 109 | } | 123 | } |
| 110 | } | 124 | } |
| 111 | 125 | ||
| 112 | - //A mesma técnica de bloqueio de thread acima é usada aqui para | ||
| 113 | - //criar um SessionBeanStore caso o mesmo ainda não exista. | ||
| 114 | - FacesViewBeanStore currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); | ||
| 115 | - if (currentStore==null){ | ||
| 116 | - synchronized (this) { | ||
| 117 | - currentStore = (FacesViewBeanStore) session.getAttribute(VIEW_STORE_KEY); | ||
| 118 | - if (currentStore==null){ | ||
| 119 | - currentStore = new FacesViewBeanStore(); | ||
| 120 | - session.setAttribute(VIEW_STORE_KEY, currentStore); | ||
| 121 | - } | ||
| 122 | - } | ||
| 123 | - } | 126 | + |
| 124 | 127 | ||
| 125 | return currentStore.getStore(viewId, this); | 128 | return currentStore.getStore(viewId, this); |
| 126 | } | 129 | } |