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 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 public void start(Cache cache) {
83 for (Iterator iter = repositories.iterator(); iter.hasNext();) {
84 final String repository = (String) iter.next();
85 if (ContentRepository.getRepositoryMapping(repository) != null) {
86 final CacheCleaner cacheCleaner = new CacheCleaner(cache, repository);
87 final EventListener listener = ObservationUtil.instanciateDeferredEventListener(cacheCleaner, 5000, 30000);
88 try {
89 ObservationUtil.registerChangeListener(repository, "/", listener);
90 } catch (Exception e) {
91 log.warn("Failed to register cache flushing observation for repository {} due to {}", repository, e.getMessage());
92 log.warn("Publishing any content to {} will not result in update of the cache. Please flush the cache manually.");
93 }
94 registeredListeners.put(repository, listener);
95 }
96 }
97 }
98
99 public void stop(Cache cache) {
100 final Iterator i = registeredListeners.keySet().iterator();
101 while (i.hasNext()) {
102 final String repository = (String) i.next();
103 final EventListener listener = (EventListener) registeredListeners.get(repository);
104 if (listener == null) {
105
106 continue;
107 }
108 ObservationUtil.unregisterChangeListener(repository, listener);
109 }
110 }
111
112
113
114
115
116 protected abstract boolean preHandleEvents(Cache cache, String repository);
117
118
119
120
121
122 protected abstract void postHandleEvents(Cache cache, String repository);
123
124
125
126
127
128
129
130 protected abstract void handleSingleEvent(Cache cache, String repository, Event event);
131
132
133
134
135
136
137 protected void flushByUUID(String uuid, String repository, Cache cache) {
138 CacheModule cacheModule = ((CacheModule) ModuleRegistry.Factory.getInstance().getModuleInstance("cache"));
139
140 final CacheConfiguration config = cacheModule.getConfiguration(cache.getName());
141 final CachePolicy policy = config.getCachePolicy();
142 if (policy == null) {
143
144 return;
145 }
146 Object[] cacheEntryKeys = config.getCachePolicy().removeCacheKeys(uuid, repository);
147 log.debug("Flushing {} due to detected content update.", ToStringBuilder.reflectionToString(cacheEntryKeys));
148
149 if (cacheEntryKeys == null || cacheEntryKeys.length == 0) {
150
151 return;
152 }
153 for (Object key : cacheEntryKeys) {
154 cache.remove(key);
155 }
156
157 }
158
159 protected class CacheCleaner implements EventListener {
160 private final Cache cache;
161 private final String repository;
162
163 public CacheCleaner(Cache cache, String repository) {
164 this.cache = cache;
165 this.repository = repository;
166 }
167
168 public void onEvent(EventIterator events) {
169 List<Event> eventList = new ArrayList<Event>();
170
171 while (events.hasNext()) {
172 final Event event = events.nextEvent();
173 try {
174 if (!event.getPath().startsWith("/jcr:")) {
175 eventList.add(event);
176 }
177 } catch (RepositoryException e) {
178 log.warn("Failed to process an event {}, the observation based cache flushing might not have been fully completed.", event.toString());
179 }
180 }
181 if (eventList.isEmpty()) {
182 return;
183 }
184
185 boolean shouldContinue = preHandleEvents(cache, repository);
186 if (shouldContinue) {
187 for (Event event : eventList) {
188 handleSingleEvent(cache, repository, event);
189 }
190 postHandleEvents(cache, repository);
191 }
192 }
193 }
194 }