Commit d20b49c0b690e8de0de5c4582dc4e9b4a0f4e415

Authored by Danilo Costa Viana
1 parent bec80284

Refatorando sincronização de criação de views para o ViewContext.

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 }