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 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 }
... ...