View Javadoc

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         /*do not propagate expiries. The element should expire in the remote cache at the same time, thus
251           preseerving coherency.
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 }