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