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 | 1 | package br.gov.frameworkdemoiselle.internal.context; |
| 2 | 2 | |
| 3 | 3 | import java.io.Serializable; |
| 4 | +import java.util.concurrent.ConcurrentHashMap; | |
| 5 | +import java.util.concurrent.atomic.AtomicLong; | |
| 4 | 6 | |
| 5 | 7 | import javax.enterprise.context.spi.Contextual; |
| 6 | 8 | import javax.enterprise.context.spi.CreationalContext; |
| ... | ... | @@ -18,23 +20,51 @@ import javax.enterprise.inject.Alternative; |
| 18 | 20 | public class FacesViewBeanStore implements Serializable { |
| 19 | 21 | |
| 20 | 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 | 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 | 66 | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| 37 | - public void clear(AbstractCustomContext context) { | |
| 67 | + public void clear(AbstractCustomContext context, BeanStore store) { | |
| 38 | 68 | if (store != null) { |
| 39 | 69 | for (String id : store) { |
| 40 | 70 | Contextual contextual = context.getContextualStore().getContextual(id); |
| ... | ... | @@ -48,4 +78,10 @@ public class FacesViewBeanStore implements Serializable { |
| 48 | 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 | 36 | */ |
| 37 | 37 | package br.gov.frameworkdemoiselle.internal.context; |
| 38 | 38 | |
| 39 | +import java.util.Map; | |
| 39 | 40 | import java.util.concurrent.atomic.AtomicLong; |
| 40 | 41 | |
| 41 | 42 | import javax.enterprise.context.ApplicationScoped; |
| ... | ... | @@ -90,37 +91,39 @@ public class FacesViewContextImpl extends AbstractCustomContext implements ViewC |
| 90 | 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 | 110 | Long viewId = (Long)Faces.getViewMap().get(FACES_KEY); |
| 99 | 111 | if (viewId==null){ |
| 100 | - synchronized (this) { | |
| 112 | + Map<String, Object> facesViewMap = Faces.getViewMap(); | |
| 113 | + | |
| 114 | + synchronized (currentStore) { | |
| 101 | 115 | |
| 102 | 116 | //Tenta obte-lo novamente, caso entre a primeira tentativa e o bloqueio |
| 103 | 117 | //da thread outra thread já tenha criado o número. |
| 104 | 118 | viewId = (Long)Faces.getViewMap().get(FACES_KEY); |
| 105 | 119 | if (viewId==null){ |
| 106 | 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 | 128 | return currentStore.getStore(viewId, this); |
| 126 | 129 | } | ... | ... |