1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.cache;
35
36 import info.magnolia.cms.beans.config.ContentRepository;
37 import info.magnolia.cms.util.ObservationUtil;
38 import info.magnolia.module.ModuleRegistry;
39
40 import javax.jcr.observation.Event;
41 import javax.jcr.observation.EventIterator;
42 import javax.jcr.observation.EventListener;
43 import javax.jcr.RepositoryException;
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Map;
49
50 import org.apache.commons.lang.builder.ToStringBuilder;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55
56
57
58
59 public abstract class AbstractListeningFlushPolicy implements FlushPolicy {
60
61 private static final Logger log = LoggerFactory.getLogger(AbstractListeningFlushPolicy.class);
62
63 private List repositories = new ArrayList();
64 private final Map registeredListeners = new HashMap();
65
66
67
68
69
70 public List getRepositories() {
71 return repositories;
72 }
73
74 public void setRepositories(List repositories) {
75 this.repositories = repositories;
76 }
77
78 public void addRepository(String repository) {
79 repositories.add(repository);
80 }
81
82 @Override
83 public void start(Cache cache) {
84 for (Iterator iter = repositories.iterator(); iter.hasNext();) {
85 final String repository = (String) iter.next();
86 if (ContentRepository.getRepositoryMapping(repository) != null) {
87 final CacheCleaner cacheCleaner = new CacheCleaner(cache, repository);
88 final EventListener listener = ObservationUtil.instanciateDeferredEventListener(cacheCleaner, 5000, 30000);
89 try {
90 ObservationUtil.registerChangeListener(repository, "/", listener);
91 } catch (Exception e) {
92 log.warn("Failed to register cache flushing observation for repository {} due to {}", repository, e.getMessage());
93 log.warn("Publishing any content to {} will not result in update of the cache. Please flush the cache manually.");
94 }
95 registeredListeners.put(repository, listener);
96 }
97 }
98 }
99
100 @Override
101 public void stop(Cache cache) {
102 final Iterator i = registeredListeners.keySet().iterator();
103 while (i.hasNext()) {
104 final String repository = (String) i.next();
105 final EventListener listener = (EventListener) registeredListeners.get(repository);
106 if (listener == null) {
107
108 continue;
109 }
110 ObservationUtil.unregisterChangeListener(repository, listener);
111 }
112 }
113
114
115
116
117
118 protected abstract boolean preHandleEvents(Cache cache, String repository);
119
120
121
122
123
124 protected abstract void postHandleEvents(Cache cache, String repository);
125
126
127
128
129
130
131
132 protected abstract void handleSingleEvent(Cache cache, String repository, Event event);
133
134
135
136
137
138
139 protected void flushByUUID(String uuid, String repository, Cache cache) {
140 CacheModule cacheModule = ((CacheModule) ModuleRegistry.Factory.getInstance().getModuleInstance("cache"));
141
142 final CacheConfiguration config = cacheModule.getConfiguration(cache.getName());
143 final CachePolicy policy = config.getCachePolicy();
144 if (policy == null) {
145
146 return;
147 }
148
149 Object[] cacheEntryKeys = config.getCachePolicy().retrieveCacheKeys(uuid, repository);
150 log.debug("Flushing {} due to detected content {}:{} update.", new Object[] {ToStringBuilder.reflectionToString(cacheEntryKeys), repository, uuid});
151
152 if (cacheEntryKeys == null || cacheEntryKeys.length == 0) {
153
154 return;
155 }
156 for (Object key : cacheEntryKeys) {
157 cache.remove(key);
158 }
159
160 }
161
162
163
164
165 protected class CacheCleaner implements EventListener {
166 private final Cache cache;
167 private final String repository;
168
169 public CacheCleaner(Cache cache, String repository) {
170 this.cache = cache;
171 this.repository = repository;
172 }
173
174 @Override
175 public void onEvent(EventIterator events) {
176 List<Event> eventList = new ArrayList<Event>();
177
178 while (events.hasNext()) {
179 final Event event = events.nextEvent();
180 try {
181 if (!event.getPath().startsWith("/jcr:")) {
182 eventList.add(event);
183 }
184 } catch (RepositoryException e) {
185 log.warn("Failed to process an event {}, the observation based cache flushing might not have been fully completed.", event.toString());
186 }
187 }
188 if (eventList.isEmpty()) {
189 return;
190 }
191
192 boolean shouldContinue = preHandleEvents(cache, repository);
193 if (shouldContinue) {
194 for (Event event : eventList) {
195 handleSingleEvent(cache, repository, event);
196 }
197 postHandleEvents(cache, repository);
198 }
199 }
200 }
201 }