View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
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.Ehcache;
20  import net.sf.ehcache.Element;
21  
22  import java.io.Serializable;
23  import java.rmi.Remote;
24  import java.rmi.RemoteException;
25  import java.rmi.server.UnicastRemoteObject;
26  import java.util.ArrayList;
27  import java.util.List;
28  
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /***
33   * An RMI based implementation of <code>CachePeer</code>.
34   * <p/>
35   * This class features a customised RMIClientSocketFactory which enables socket timeouts to be configured.
36   *
37   * @author Greg Luck
38   * @version $Id: RMICachePeer.html 13146 2011-08-01 17:12:39Z oletizi $
39   */
40  public class RMICachePeer extends UnicastRemoteObject implements CachePeer, Remote {
41  
42      private static final Logger LOG = LoggerFactory.getLogger(RMICachePeer.class.getName());
43  
44      private final String hostname;
45      private final Integer rmiRegistryPort;
46      private Integer remoteObjectPort;
47      private final Ehcache cache;
48  
49      /***
50       * Construct a new remote peer.
51       *
52       * @param cache               The cache attached to the peer
53       * @param hostName            The host name the peer is running on.
54       * @param rmiRegistryPort     The port number on which the RMI Registry listens. Should be an unused port in
55       *                            the range 1025 - 65536
56       * @param remoteObjectPort    the port number on which the remote objects bound in the registry receive calls.
57       *                            This defaults to a free port if not specified.
58       *                            Should be an unused port in the range 1025 - 65536
59       * @param socketTimeoutMillis
60       * @throws RemoteException
61       */
62      public RMICachePeer(Ehcache cache, String hostName, Integer rmiRegistryPort, Integer remoteObjectPort,
63                          Integer socketTimeoutMillis)
64              throws RemoteException {
65          super(remoteObjectPort.intValue(), new ConfigurableRMIClientSocketFactory(socketTimeoutMillis),
66                  ConfigurableRMIClientSocketFactory.getConfiguredRMISocketFactory());
67  
68          this.remoteObjectPort = remoteObjectPort;
69          this.hostname = hostName;
70          this.rmiRegistryPort = rmiRegistryPort;
71          this.cache = cache;
72      }
73  
74      /***
75       * {@inheritDoc}
76       * <p/>
77       * This implementation gives an URL which has meaning to the RMI remoting system.
78       *
79       * @return the URL, without the scheme, as a string e.g. //hostname:port/cacheName
80       */
81      public final String getUrl() {
82          return new StringBuilder()
83                  .append("//")
84                  .append(hostname)
85                  .append(":")
86                  .append(rmiRegistryPort)
87                  .append("/")
88                  .append(cache.getName())
89                  .toString();
90      }
91  
92      /***
93       * {@inheritDoc}
94       * <p/>
95       * This implementation gives an URL which has meaning to the RMI remoting system.
96       *
97       * @return the URL, without the scheme, as a string e.g. //hostname:port
98       */
99      public final String getUrlBase() {
100         return new StringBuilder()
101                 .append("//")
102                 .append(hostname)
103                 .append(":")
104                 .append(rmiRegistryPort)
105                 .toString();
106     }
107 
108     /***
109      * Returns a list of all elements in the cache, whether or not they are expired.
110      * <p/>
111      * The returned keys are unique and can be considered a set.
112      * <p/>
113      * The List returned is not live. It is a copy.
114      * <p/>
115      * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
116      * for each 1000 entries.
117      *
118      * @return a list of {@link Object} keys
119      */
120     public List getKeys() throws RemoteException {
121         List keys = cache.getKeys();
122         if (keys instanceof Serializable) {
123             return keys;
124         }
125         return new ArrayList(keys);
126     }
127 
128     /***
129      * Gets an element from the cache, without updating Element statistics. Cache statistics are
130      * still updated.
131      *
132      * @param key a serializable value
133      * @return the element, or null, if it does not exist.
134      */
135     public Element getQuiet(Serializable key) throws RemoteException {
136         return cache.getQuiet(key);
137     }
138 
139     /***
140      * Gets a list of elements from the cache, for a list of keys, without updating Element statistics. Time to
141      * idle lifetimes are therefore not affected.
142      * <p/>
143      * Cache statistics are still updated.
144      * <p/>
145      * Callers should ideally first call this method with a small list of keys to gauge the size of a typical Element.
146      * Then a calculation can be made of the right number to request each time so as to optimise network performance and
147      * not cause an OutOfMemory error on this Cache.
148      *
149      * @param keys a list of serializable values which represent keys
150      * @return a list of Elements. If an element was not found or null, it will not be in the list.
151      */
152     public List getElements(List keys) throws RemoteException {
153         if (keys == null) {
154             return new ArrayList();
155         }
156         List elements = new ArrayList();
157         for (int i = 0; i < keys.size(); i++) {
158             Serializable key = (Serializable) keys.get(i);
159             Element element = cache.getQuiet(key);
160             if (element != null) {
161                 elements.add(element);
162             }
163         }
164         return elements;
165     }
166 
167 
168     /***
169      * Puts an Element into the underlying cache without notifying listeners or updating statistics.
170      *
171      * @param element
172      * @throws java.rmi.RemoteException
173      * @throws IllegalArgumentException
174      * @throws IllegalStateException
175      */
176     public void put(Element element) throws RemoteException, IllegalArgumentException, IllegalStateException {
177         cache.put(element, true);
178         if (LOG.isDebugEnabled()) {
179             LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote put received. Element is: " + element);
180         }
181     }
182 
183 
184     /***
185      * Removes an Element from the underlying cache without notifying listeners or updating statistics.
186      *
187      * @param key
188      * @return true if the element was removed, false if it was not found in the cache
189      * @throws RemoteException
190      * @throws IllegalStateException
191      */
192     public boolean remove(Serializable key) throws RemoteException, IllegalStateException {
193         if (LOG.isDebugEnabled()) {
194             LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote remove received for key: " + key);
195         }
196         return cache.remove(key, true);
197     }
198 
199     /***
200      * Removes all cached items.
201      *
202      * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
203      */
204     public void removeAll() throws RemoteException, IllegalStateException {
205         if (LOG.isDebugEnabled()) {
206             LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote removeAll received");
207         }
208         cache.removeAll(true);
209     }
210 
211     /***
212      * Send the cache peer with an ordered list of {@link EventMessage}s
213      * <p/>
214      * This enables multiple messages to be delivered in one network invocation.
215      */
216     public void send(List eventMessages) throws RemoteException {
217         for (int i = 0; i < eventMessages.size(); i++) {
218             EventMessage eventMessage = (EventMessage) eventMessages.get(i);
219             if (eventMessage.getEvent() == EventMessage.PUT) {
220                 put(eventMessage.getElement());
221             } else if (eventMessage.getEvent() == EventMessage.REMOVE) {
222                 remove(eventMessage.getSerializableKey());
223             } else if (eventMessage.getEvent() == EventMessage.REMOVE_ALL) {
224                 removeAll();
225             } else {
226                 LOG.error("Unknown event: " + eventMessage);
227             }
228         }
229     }
230 
231     /***
232      * Gets the cache name
233      */
234     public final String getName() throws RemoteException {
235         return cache.getName();
236     }
237 
238 
239     /***
240      * {@inheritDoc}
241      */
242     public final String getGuid() throws RemoteException {
243         return cache.getGuid();
244     }
245 
246     /***
247      * Gets the cache instance that this listener is bound to
248      */
249     final Ehcache getBoundCacheInstance() {
250         return cache;
251     }
252 
253     /***
254      * Returns a String that represents the value of this object.
255      */
256     public String toString() {
257         StringBuilder buffer = new StringBuilder("URL: ");
258         buffer.append(getUrl());
259         buffer.append(" Remote Object Port: ");
260         buffer.append(remoteObjectPort);
261         return buffer.toString();
262     }
263 
264 }