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