
net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicator Maven / Gradle / Ivy
/**
* Copyright 2003-2009 Luck Consulting Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.ehcache.distribution.jgroups;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import net.sf.ehcache.distribution.CacheManagerPeerProvider;
import net.sf.ehcache.distribution.CachePeer;
import net.sf.ehcache.distribution.CacheReplicator;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.UnmarshalException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Pierre Monestie (pmonestie[at]@gmail.com)
* @author Greg Luck
* @version $Id: JGroupsCacheReplicator.java 876 2009-01-20 05:05:32Z gregluck $
* This implements CacheReplicator using JGroups as underlying
* replication mechanism The peer provider should be of type
* JGroupsCacheManagerPeerProvider It is assumed that the cachepeer is
* a JGroupManager
*/
public class JGroupsCacheReplicator implements CacheReplicator {
/**
* Teh default interval for async cache replication
*/
public static final long DEFAULT_ASYNC_INTERVAL = 1000;
private static final Logger LOG = Logger.getLogger(JGroupsCacheReplicator.class.getName());
private long asynchronousReplicationInterval = DEFAULT_ASYNC_INTERVAL;
/**
* Whether or not to replicate puts
*/
private boolean replicatePuts;
/**
* Whether or not to replicate updates
*/
private boolean replicateUpdates;
/**
* Replicate update via copying, if false via deleting
*/
private boolean replicateUpdatesViaCopy;
/**
* Whether or not to replicate remove events
*/
private boolean replicateRemovals;
/**
* Weather or not to replicate asynchronously. If true a background thread
* is run and updates are fired at a set interval
*/
private boolean replicateAsync;
private ReplicationThread replicationThread;
private List replicationQueue = new LinkedList();
private Status status;
/**
* Constructor called by factory
*
* @param replicatePuts
* @param replicateUpdates
* @param replicateUpdatesViaCopy
* @param replicateRemovals
* @param replicateAsync
*/
public JGroupsCacheReplicator(boolean replicatePuts, boolean replicateUpdates, boolean replicateUpdatesViaCopy,
boolean replicateRemovals, boolean replicateAsync) {
super();
this.replicatePuts = replicatePuts;
this.replicateUpdates = replicateUpdates;
this.replicateUpdatesViaCopy = replicateUpdatesViaCopy;
this.replicateRemovals = replicateRemovals;
this.replicateAsync = replicateAsync;
if (replicateAsync) {
replicationThread = new ReplicationThread();
replicationThread.start();
}
status = Status.STATUS_ALIVE;
}
/**
* {@inheritDoc}
*/
public boolean alive() {
return true;
}
/**
* {@inheritDoc}
*/
public boolean isReplicateUpdatesViaCopy() {
return replicateUpdatesViaCopy;
}
/**
* {@inheritDoc}
*/
public boolean notAlive() {
return false;
}
/**
* {@inheritDoc}
*/
public void dispose() {
status = Status.STATUS_SHUTDOWN;
flushReplicationQueue();
}
/**
* {@inheritDoc}
*/
public void notifyElementExpired(Ehcache cache, Element element) {
// LOG.finest("Sending out exp el:"+element);
}
/**
* Used to send notification to the peer. If Async this method simply add
* the element to the replication queue. If not async, searches for the
* cachePeer and send the Message. That way the class handles both async and
* sync replication Sending is delegated to the peer (of type JGroupManager)
*
* @param cache
* @param e
*/
protected void sendNotification(Ehcache cache, JGroupEventMessage e) {
if (replicateAsync) {
addMessageToQueue(e);
return;
}
CacheManagerPeerProvider provider = cache.getCacheManager().getCacheManagerPeerProvider("JGroups");
List l = provider.listRemoteCachePeers(cache);
ArrayList a = new ArrayList();
a.add(e);
for (int i = 0; i < l.size(); i++) {
CachePeer peer = (CachePeer) l.get(i);
try {
peer.send(a);
} catch (RemoteException e1) {
// e1.printStackTrace();
}
// peer.
}
}
/**
* {@inheritDoc}
*/
public void notifyElementPut(Ehcache cache, Element element) throws CacheException {
if (notAlive()) {
return;
}
if (replicatePuts) {
// if (log.isTraceEnabled())
// LOG.finest("Sending out add/upd el:" + element);
replicatePutNotification(cache, element);
}
}
private void replicatePutNotification(Ehcache cache, Element element) {
if (!element.isKeySerializable()) {
LOG.warning("Key " + element.getObjectKey() + " is not Serializable and cannot be replicated.");
return;
}
if (!element.isSerializable()) {
LOG.warning("Object with key " + element.getObjectKey() + " is not Serializable and cannot be updated via copy");
return;
}
JGroupEventMessage e = new JGroupEventMessage(JGroupEventMessage.PUT, (Serializable) element.getObjectKey(), element,
cache, cache.getName());
sendNotification(cache, e);
}
private void replicateRemoveNotification(Ehcache cache, Element element) {
if (!element.isKeySerializable()) {
LOG.warning("Key " + element.getObjectKey() + " is not Serializable and cannot be replicated.");
return;
}
JGroupEventMessage e = new JGroupEventMessage(JGroupEventMessage.REMOVE, (Serializable) element.getObjectKey(), null,
cache, cache.getName());
sendNotification(cache, e);
}
/**
* {@inheritDoc}
*/
public void notifyElementRemoved(Ehcache cache, Element element) throws CacheException {
if (notAlive()) {
return;
}
if (replicateRemovals) {
replicateRemoveNotification(cache, element);
}
}
/**
* {@inheritDoc}
*/
public void notifyElementUpdated(Ehcache cache, Element element) throws CacheException {
if (notAlive()) {
return;
}
if (!replicateUpdates) {
return;
}
if (isReplicateUpdatesViaCopy()) {
replicatePutNotification(cache, element);
} else {
replicateRemoveNotification(cache, element);
}
}
/**
* {@inheritDoc}
*/
public void notifyElementEvicted(Ehcache cache, Element element) {
//nothing to do
}
/**
* {@inheritDoc}
*/
public void notifyRemoveAll(Ehcache cache) {
if (replicateRemovals) {
LOG.finest("Remove all elements called");
JGroupEventMessage e = new JGroupEventMessage(JGroupEventMessage.REMOVE_ALL, null, null, cache, cache.getName());
sendNotification(cache, e);
}
}
/**
* Package protected List of cache peers
*
* @param cache
* @return a list of {@link CachePeer} peers for the given cache, excluding
* the local peer.
*/
static List listRemoteCachePeers(Ehcache cache) {
CacheManagerPeerProvider provider = cache.getCacheManager().getCacheManagerPeerProvider("JGroups");
return provider.listRemoteCachePeers(cache);
}
/**
* {@inheritDoc}
*/
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* The replication thread
*
* @author pierrem
*
*/
private final class ReplicationThread extends Thread {
public ReplicationThread() {
super("Replication Thread");
setDaemon(true);
setPriority(Thread.NORM_PRIORITY);
}
/**
* RemoteDebugger thread method.
*/
public final void run() {
replicationThreadMain();
}
}
private void replicationThreadMain() {
while (true) {
// Wait for elements in the replicationQueue
while (alive() && replicationQueue != null && replicationQueue.size() == 0) {
try {
Thread.sleep(asynchronousReplicationInterval);
} catch (InterruptedException e) {
LOG.fine("Spool Thread interrupted.");
return;
}
}
if (notAlive()) {
return;
}
try {
if (replicationQueue.size() != 0) {
flushReplicationQueue();
}
} catch (Throwable e) {
LOG.log(Level.WARNING, "Exception on flushing of replication queue: " + e.getMessage() + ". Continuing...", e);
}
}
}
private void addMessageToQueue(JGroupEventMessage msg) {
synchronized (replicationQueue) {
replicationQueue.add(msg);
}
}
/**
* Gets called once per {@link #asynchronousReplicationInterval}.
* Sends accumulated messages in bulk to each peer. i.e. if ther are 100
* messages and 1 peer, 1 RMI invocation results, not 100. Also, if a peer
* is unavailable this is discovered in only 1 try. Makes a copy of the
* queue so as not to hold up the enqueue operations. Any exceptions
* are caught so that the replication thread does not die, and because
* errors are expected, due to peers becoming unavailable. This method
* issues warnings for problems that can be fixed with configuration
* changes.
*/
private void flushReplicationQueue() {
List resolvedEventMessages;
Ehcache cache;
synchronized (replicationQueue) {
if (replicationQueue.size() == 0) {
return;
}
resolvedEventMessages = extractAndResolveEventMessages(replicationQueue);
cache = ((JGroupEventMessage) replicationQueue.get(0)).getCache();
replicationQueue.clear();
}
List cachePeers = listRemoteCachePeers(cache);
for (int j = 0; j < cachePeers.size(); j++) {
CachePeer cachePeer = (CachePeer) cachePeers.get(j);
try {
cachePeer.send(resolvedEventMessages);
} catch (UnmarshalException e) {
String message = e.getMessage();
if (message.indexOf("Read time out") != 0) {
LOG.warning("Unable to send message to remote peer due to socket read timeout. Consider increasing"
+ " the socketTimeoutMillis setting in the cacheManagerPeerListenerFactory. " + "Message was: "
+ e.getMessage());
} else {
LOG.fine("Unable to send message to remote peer. Message was: " + e.getMessage());
}
} catch (Throwable t) {
LOG.log(Level.WARNING, "Unable to send message to remote peer. Message was: " + t.getMessage(), t);
}
}
}
/**
* Extracts CacheEventMessages and attempts to get a hard reference to the
* underlying EventMessage If an EventMessage has been invalidated due
* to SoftReference collection of the Element, it is not propagated. This
* only affects puts and updates via copy.
*
* @param replicationQueueCopy
* @return a list of EventMessages which were able to be resolved
*/
private static List extractAndResolveEventMessages(List replicationQueueCopy) {
List list = new ArrayList();
for (int i = 0; i < replicationQueueCopy.size(); i++) {
JGroupEventMessage eventMessage = (JGroupEventMessage) replicationQueueCopy.get(i);
if (eventMessage != null && eventMessage.isValid()) {
list.add(eventMessage);
} else {
LOG.severe("Collected soft ref");
}
}
return list;
}
/**
* Get the time interval is ms between asynchronous replication
*
* @return the interval
*/
public long getAsynchronousReplicationInterval() {
return asynchronousReplicationInterval;
}
/**
* Set the time inteval for asynchronous replication
*
* @param asynchronousReplicationInterval
* the interval between replication
*/
public void setAsynchronousReplicationInterval(long asynchronousReplicationInterval) {
this.asynchronousReplicationInterval = asynchronousReplicationInterval;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy