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 }