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