001: /**
002: * Copyright 2003-2007 Luck Consulting Pty Ltd
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package net.sf.ehcache.distribution;
016:
017: import net.sf.ehcache.Ehcache;
018: import net.sf.ehcache.Element;
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: import java.io.Serializable;
023: import java.rmi.Remote;
024: import java.rmi.RemoteException;
025: import java.rmi.server.RMISocketFactory;
026: import java.rmi.server.UnicastRemoteObject;
027: import java.util.List;
028: import java.util.ArrayList;
029:
030: /**
031: * An RMI based implementation of <code>CachePeer</code>.
032: * <p/>
033: * This class features a customised RMIClientSocketFactory which enables socket timeouts to be configured.
034: *
035: * @author Greg Luck
036: * @version $Id: RMICachePeer.java 519 2007-07-27 07:11:45Z gregluck $
037: * @noinspection FieldCanBeLocal
038: */
039: public class RMICachePeer extends UnicastRemoteObject implements
040: CachePeer, Remote {
041:
042: private static final Log LOG = LogFactory.getLog(RMICachePeer.class
043: .getName());
044:
045: private final String hostname;
046: private final Integer port;
047: private final Ehcache cache;
048:
049: /**
050: * Construct a new remote peer.
051: *
052: * @param cache
053: * @param hostName
054: * @param port
055: * @param socketTimeoutMillis
056: * @throws RemoteException
057: */
058: public RMICachePeer(Ehcache cache, String hostName, Integer port,
059: Integer socketTimeoutMillis) throws RemoteException {
060: super (0, new ConfigurableRMIClientSocketFactory(
061: socketTimeoutMillis), RMISocketFactory
062: .getDefaultSocketFactory());
063:
064: this .hostname = hostName;
065: this .port = port;
066: this .cache = cache;
067: }
068:
069: /**
070: * {@inheritDoc}
071: * <p/>
072: * This implementation gives an URL which has meaning to the RMI remoting system.
073: *
074: * @return the URL, without the scheme, as a string e.g. //hostname:port/cacheName
075: */
076: public final String getUrl() {
077: return new StringBuffer().append("//").append(hostname).append(
078: ":").append(port).append("/").append(cache.getName())
079: .toString();
080: }
081:
082: /**
083: * {@inheritDoc}
084: * <p/>
085: * This implementation gives an URL which has meaning to the RMI remoting system.
086: *
087: * @return the URL, without the scheme, as a string e.g. //hostname:port
088: */
089: public final String getUrlBase() {
090: return new StringBuffer().append("//").append(hostname).append(
091: ":").append(port).toString();
092: }
093:
094: /**
095: * Returns a list of all elements in the cache, whether or not they are expired.
096: * <p/>
097: * The returned keys are unique and can be considered a set.
098: * <p/>
099: * The List returned is not live. It is a copy.
100: * <p/>
101: * The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
102: * for each 1000 entries.
103: *
104: * @return a list of {@link Object} keys
105: */
106: public List getKeys() throws RemoteException {
107: return cache.getKeys();
108: }
109:
110: /**
111: * Gets an element from the cache, without updating Element statistics. Cache statistics are
112: * still updated.
113: *
114: * @param key a serializable value
115: * @return the element, or null, if it does not exist.
116: */
117: public Element getQuiet(Serializable key) throws RemoteException {
118: return cache.getQuiet(key);
119: }
120:
121: /**
122: * Gets a list of elements from the cache, for a list of keys, without updating Element statistics. Time to
123: * idle lifetimes are therefore not affected.
124: * <p/>
125: * Cache statistics are still updated.
126: * <p/>
127: * Callers should ideally first call this method with a small list of keys to gauge the size of a typical Element.
128: * Then a calculation can be made of the right number to request each time so as to optimise network performance and
129: * not cause an OutOfMemory error on this Cache.
130: *
131: * @param keys a list of serializable values which represent keys
132: * @return a list of Elements. If an element was not found or null, it will not be in the list.
133: */
134: public List getElements(List keys) throws RemoteException {
135: if (keys == null) {
136: return new ArrayList();
137: }
138: List elements = new ArrayList();
139: for (int i = 0; i < keys.size(); i++) {
140: Serializable key = (Serializable) keys.get(i);
141: Element element = cache.getQuiet(key);
142: if (element != null) {
143: elements.add(element);
144: }
145: }
146: return elements;
147: }
148:
149: /**
150: * Puts an Element into the underlying cache without notifying listeners or updating statistics.
151: *
152: * @param element
153: * @throws java.rmi.RemoteException
154: * @throws IllegalArgumentException
155: * @throws IllegalStateException
156: */
157: public void put(Element element) throws RemoteException,
158: IllegalArgumentException, IllegalStateException {
159: cache.put(element, true);
160: if (LOG.isDebugEnabled()) {
161: LOG.debug("Remote put received. Element is: " + element);
162: }
163: }
164:
165: /**
166: * Removes an Element from the underlying cache without notifying listeners or updating statistics.
167: *
168: * @param key
169: * @return true if the element was removed, false if it was not found in the cache
170: * @throws RemoteException
171: * @throws IllegalStateException
172: */
173: public final boolean remove(Serializable key)
174: throws RemoteException, IllegalStateException {
175: if (LOG.isDebugEnabled()) {
176: LOG.debug("Remote remove received for key: " + key);
177: }
178: return cache.remove(key, true);
179: }
180:
181: /**
182: * Removes all cached items.
183: *
184: * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
185: */
186: public final void removeAll() throws RemoteException,
187: IllegalStateException {
188: if (LOG.isDebugEnabled()) {
189: LOG.debug("Remote removeAll received");
190: }
191: cache.removeAll(true);
192: }
193:
194: /**
195: * Send the cache peer with an ordered list of {@link EventMessage}s
196: * <p/>
197: * This enables multiple messages to be delivered in one network invocation.
198: */
199: public final void send(List eventMessages) throws RemoteException {
200: for (int i = 0; i < eventMessages.size(); i++) {
201: EventMessage eventMessage = (EventMessage) eventMessages
202: .get(i);
203: if (eventMessage.getEvent() == EventMessage.PUT) {
204: put(eventMessage.getElement());
205: } else if (eventMessage.getEvent() == EventMessage.REMOVE) {
206: remove(eventMessage.getSerializableKey());
207: } else if (eventMessage.getEvent() == EventMessage.REMOVE_ALL) {
208: removeAll();
209: } else {
210: LOG.error("Unknown event: " + eventMessage);
211: }
212: }
213: }
214:
215: /**
216: * Gets the cache name
217: */
218: public final String getName() throws RemoteException {
219: return cache.getName();
220: }
221:
222: /**
223: * {@inheritDoc}
224: */
225: public final String getGuid() throws RemoteException {
226: return cache.getGuid();
227: }
228:
229: /**
230: * Gets the cache instance that this listener is bound to
231: */
232: final Ehcache getBoundCacheInstance() {
233: return cache;
234: }
235:
236: /**
237: * Returns a String that represents the value of this object.
238: */
239: public String toString() {
240: return getUrl();
241: }
242:
243: }
|