1 /**
2 * Copyright 2003-2006 Greg Luck
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.distribution;
18
19 import net.sf.ehcache.Cache;
20 import net.sf.ehcache.CacheException;
21 import net.sf.ehcache.Element;
22 import net.sf.ehcache.Status;
23
24 import java.io.Serializable;
25 import java.util.List;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 /**
31 * Listens to {@link net.sf.ehcache.CacheManager} and {@link net.sf.ehcache.Cache} events and propagates those to
32 * {@link CachePeer} peers of the Cache.
33 *
34 * @author Greg Luck
35 * @version $Id: RMISynchronousCacheReplicator.java 52 2006-04-24 14:50:03Z gregluck $
36 */
37 public class RMISynchronousCacheReplicator implements CacheReplicator {
38
39 private static final Log LOG = LogFactory.getLog(RMISynchronousCacheReplicator.class.getName());
40
41
42 /**
43 * The status of the replicator. Only replicates when <code>STATUS_ALIVE</code>
44 */
45 protected Status status;
46
47 /**
48 * Whether to replicate puts.
49 */
50 protected final boolean replicatePuts;
51
52 /**
53 * Whether to replicate updates.
54 */
55 protected final boolean replicateUpdates;
56
57 /**
58 * Whether an update (a put) should be by copy or by invalidation, (a remove).
59 * <p/>
60 * By copy is best when the entry is expensive to produce. By invalidation is best when
61 * we are really trying to force other caches to sync back to a canonical source like a database.
62 * An example of a latter usage would be a read/write cache being used in Hibernate.
63 * <p/>
64 * This setting only has effect if <code>#replicateUpdates</code> is true.
65 */
66 protected final boolean replicateUpdatesViaCopy;
67
68 /**
69 * Whether to replicate removes
70 */
71 protected final boolean replicateRemovals;
72
73 /**
74 * Constructor for internal and subclass use
75 *
76 * @param replicatePuts
77 * @param replicateUpdates
78 * @param replicateUpdatesViaCopy
79 * @param replicateRemovals
80 */
81 protected RMISynchronousCacheReplicator(
82 boolean replicatePuts,
83 boolean replicateUpdates,
84 boolean replicateUpdatesViaCopy,
85 boolean replicateRemovals) {
86 this.replicatePuts = replicatePuts;
87 this.replicateUpdates = replicateUpdates;
88 this.replicateUpdatesViaCopy = replicateUpdatesViaCopy;
89 this.replicateRemovals = replicateRemovals;
90 status = Status.STATUS_ALIVE;
91 }
92
93 /**
94 * Called immediately after an element has been put into the cache. The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method
95 * will block until this method returns.
96 * <p/>
97 * Implementers may wish to have access to the Element's fields, including value, so the element is provided.
98 * Implementers should be careful not to modify the element. The effect of any modifications is undefined.
99 *
100 * @param cache the cache emitting the notification
101 * @param element the element which was just put into the cache.
102 */
103 public void notifyElementPut(final Cache cache, final Element element) throws CacheException {
104 if (notAlive()) {
105 return;
106 }
107
108 if (!replicatePuts) {
109 return;
110 }
111
112 if (!element.isSerializable()) {
113 if (LOG.isWarnEnabled()) {
114 LOG.warn("Object with key " + element.getObjectKey() + " is not Serializable and cannot be replicated");
115 }
116 return;
117 }
118
119
120 replicatePutNotification(cache, element);
121 }
122
123 /**
124 * Does the actual RMI remote call
125 *
126 * @param element
127 * @throws RemoteCacheException if anything goes wrong with the remote call
128 */
129 private static void replicatePutNotification(Cache cache, Element element) throws RemoteCacheException {
130 List cachePeers = listRemoteCachePeers(cache);
131 for (int i = 0; i < cachePeers.size(); i++) {
132 CachePeer cachePeer = (CachePeer) cachePeers.get(i);
133 try {
134 cachePeer.put(element);
135 } catch (Throwable t) {
136 throw new RemoteCacheException("Error doing put to remote peer. Message was: " + t.getMessage());
137 }
138 }
139 }
140
141
142 /**
143 * Called immediately after an element has been put into the cache and the element already
144 * existed in the cache. This is thus an update.
145 * <p/>
146 * The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method
147 * will block until this method returns.
148 * <p/>
149 * Implementers may wish to have access to the Element's fields, including value, so the element is provided.
150 * Implementers should be careful not to modify the element. The effect of any modifications is undefined.
151 *
152 * @param cache the cache emitting the notification
153 * @param element the element which was just put into the cache.
154 */
155 public void notifyElementUpdated(final Cache cache, final Element element) throws CacheException {
156 if (notAlive()) {
157 return;
158 }
159 if (!replicateUpdates) {
160 return;
161 }
162
163 if (replicateUpdatesViaCopy) {
164 if (!element.isSerializable()) {
165 if (LOG.isWarnEnabled()) {
166 LOG.warn("Object with key " + element.getObjectKey() + " is not Serializable and cannot be updated via copy");
167 }
168 return;
169 }
170
171 replicatePutNotification(cache, element);
172 } else {
173 if (!element.isKeySerializable()) {
174 if (LOG.isWarnEnabled()) {
175 LOG.warn("Key " + element.getObjectKey() + " is not Serializable and cannot be replicated.");
176 }
177 return;
178 }
179
180 replicateRemovalNotification(cache, (Serializable) element.getObjectKey());
181 }
182 }
183
184 /**
185 * Called immediately after an element has been removed. The remove method will block until
186 * this method returns.
187 * <p/>
188 * Ehcache does not check for
189 * <p/>
190 * As the {@link net.sf.ehcache.Element} has been removed, only what was the key of the element is known.
191 * <p/>
192 *
193 * @param cache the cache emitting the notification
194 * @param element just deleted
195 */
196 public void notifyElementRemoved(final Cache cache, final Element element) throws CacheException {
197 if (notAlive()) {
198 return;
199 }
200
201 if (!replicateRemovals) {
202 return;
203 }
204
205 if (!element.isKeySerializable()) {
206 if (LOG.isWarnEnabled()) {
207 LOG.warn("Key " + element.getObjectKey() + " is not Serializable and cannot be replicated.");
208 }
209 return;
210 }
211
212 replicateRemovalNotification(cache, (Serializable) element.getObjectKey());
213 }
214
215 /**
216 * Does the actual RMI remote call
217 *
218 * @param key
219 * @throws RemoteCacheException if anything goes wrong with the remote call
220 */
221 private static void replicateRemovalNotification(Cache cache, Serializable key) throws RemoteCacheException {
222 List cachePeers = listRemoteCachePeers(cache);
223 for (int i = 0; i < cachePeers.size(); i++) {
224 CachePeer cachePeer = (CachePeer) cachePeers.get(i);
225 try {
226 cachePeer.remove(key);
227 } catch (Throwable e) {
228 throw new RemoteCacheException("Error doing remove to remote peer. Message was: " + e.getMessage());
229 }
230 }
231 }
232
233 /**
234 * Package protected List of cache peers
235 * @param cache
236 */
237 static List listRemoteCachePeers(Cache cache) {
238 CacheManagerPeerProvider provider = cache.getCacheManager().getCachePeerProvider();
239 return provider.listRemoteCachePeers(cache);
240 }
241
242 /**
243 * {@inheritDoc}
244 * <p/>
245 * This implementation does not propagate expiries. It does not need to do anything because the element will
246 * expire in the remote cache at the same time. If the remote peer is not configured the same way they should
247 * not be in an cache cluster.
248 */
249 public final void notifyElementExpired(final Cache cache, final Element element) {
250
251
252
253 }
254
255 /**
256 * @return whether update is through copy or invalidate
257 */
258 public final boolean isReplicateUpdatesViaCopy() {
259 return replicateUpdatesViaCopy;
260 }
261
262 /**
263 * Asserts that the replicator is active.
264 *
265 * @return true if the status is not STATUS_ALIVE
266 */
267 public final boolean notAlive() {
268 return !status.equals(Status.STATUS_ALIVE);
269 }
270
271 /**
272 * Checks that the replicator is is <code>STATUS_ALIVE</code>.
273 */
274 public final boolean alive() {
275 return (status.equals(Status.STATUS_ALIVE));
276 }
277
278 /**
279 * Give the replicator a chance to cleanup and free resources when no longer needed
280 */
281 public void dispose() {
282 status = Status.STATUS_SHUTDOWN;
283 }
284 }