org.ibatis.jgroups.ehcache.JGroupsCachePeer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbatis Show documentation
Show all versions of jbatis Show documentation
The jBATIS persistence framework will help you to significantly reduce the amount of Java code that you normally need to access a relational database. iBATIS simply maps JavaBeans to SQL statements using a very simple XML descriptor.
The newest version!
/**
* Copyright 2003-2010 Terracotta, Inc.
*
* 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 org.ibatis.jgroups.ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.distribution.CachePeer;
import org.jgroups.Address;
import org.jgroups.Channel;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
/**
* Handles {@link CachePeer}functions around a JGroups {@link Channel} and a
* {@link CacheManager}
*
* @author Eric Dalquist
* @version $Revision$
*/
public class JGroupsCachePeer implements CachePeer {
private static final Logger LOG = LoggerFactory.getLogger(JGroupsCachePeer.class.getName());
private static final int CHUNK_SIZE = 100;
private final Channel channel;
private final ConcurrentMap> asyncReplicationQueues =
new ConcurrentHashMap>();
private final Timer timer;
private volatile boolean alive;
/**
* Create a new {@link CachePeer}
*/
public JGroupsCachePeer(Channel channel, String clusterName) {
this.channel = channel;
this.alive = true;
this.timer = new Timer(clusterName + " Async Replication Thread", true);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public void send(List eventMessages) throws RemoteException {
this.send(null, eventMessages);
}
/* ********** Local Methods ********** */
/**
* @return Get the address of all members in the cluster
*/
public List getGroupMembership() {
final View view = this.channel.getView();
return view.getMembers();
}
/**
* @return Get the address of all members in the cluster other than this one
*/
public List getOtherGroupMembers() {
final Address localAddress = this.getLocalAddress();
final List members = this.getGroupMembership();
final List addresses = new ArrayList(members.size() - 1);
for (final Address member : members) {
if (!member.equals(localAddress)) {
addresses.add(member);
}
}
return addresses;
}
/**
* @return Get the address of this machine in the cluster
*/
public Address getLocalAddress() {
return this.channel.getAddress();
}
/**
* Shutdown the cache peer
*/
public void dispose() {
this.alive = false;
disposeTimer();
this.flushAllQueues();
this.asyncReplicationQueues.clear();
}
private void disposeTimer() {
// cancel the timer and all tasks
if (timer != null) {
timer.cancel();
timer.purge();
}
}
/**
* Sends a list of {@link JGroupEventMessage}s to the specified address, if no address is set the messages
* are sent to the entire group.
*/
public void send(Address dest, List eventMessages) {
if (!this.alive || eventMessages == null || eventMessages.isEmpty()) {
LOG.warn("Ignoring send request of {} messages. Replicator alive = {}",
eventMessages == null ? null : eventMessages.size() , this.alive);
return;
}
//Iterate over messages, queuing async messages then sending sync messages immediately
final LinkedList synchronousEventMessages = new LinkedList();
for (final JGroupEventMessage groupEventMessage : eventMessages) {
if (groupEventMessage.isAsync()) {
final long asyncTime = groupEventMessage.getAsyncTime();
final Queue queue = this.getMessageQueue(asyncTime);
queue.offer(groupEventMessage);
LOG.trace("Queued {} for asynchronous sending.", groupEventMessage);
} else {
synchronousEventMessages.add(groupEventMessage);
LOG.trace("Sending {} synchronously.", groupEventMessage);
}
}
//If there are no synchronous messages queued return
if (synchronousEventMessages.size() == 0) {
return;
}
LOG.debug("Sending {} JGroupEventMessages synchronously.", synchronousEventMessages.size());
this.sendData(dest, synchronousEventMessages);
}
private Queue getMessageQueue(long asyncTime) {
Queue queue = this.asyncReplicationQueues.get(asyncTime);
if (queue == null) {
final Queue newQueue = new ConcurrentLinkedQueue();
queue = this.asyncReplicationQueues.putIfAbsent(asyncTime, newQueue);
if (queue == null) {
LOG.debug("Created asynchronous message queue for {}ms period", asyncTime);
//New queue, setup a new timer to flush the queue
final AsyncTimerTask task = new AsyncTimerTask(newQueue);
timer.schedule(task, asyncTime, asyncTime);
return newQueue;
}
}
return queue;
}
/**
* Sends a serializable object to the specified address. If no address is specified it is sent to the
* entire group.
*/
private void sendData(Address dest, List extends Serializable> dataList) {
//Remove the list wrapper if only a single event is being sent
final Serializable toSend;
if (dataList.size() == 1) {
toSend = dataList.get(0);
} else {
toSend = (Serializable)dataList;
}
//Serialize the data into a byte[] for sending
final byte[] data;
try {
data = Util.objectToByteBuffer(toSend);
} catch (Exception e) {
LOG.error("Error serializing data, it will not be sent: " + toSend, e);
return;
}
//Send it off to the group
final Message msg = new Message(dest, null, data);
try {
this.channel.send(msg);
} catch (IllegalStateException e) {
LOG.error("Failed to send message(s) due to the channel being disconnected or closed: " + toSend, e);
} catch (Exception e) {
LOG.error("Failed to send message(s) : " + toSend, e);
}
}
private void flushAllQueues() {
for (final Queue queue : this.asyncReplicationQueues.values()) {
this.flushQueue(queue);
}
}
private void flushQueue(Queue queue) {
final ArrayList events = new ArrayList(CHUNK_SIZE);
while (!queue.isEmpty()) {
events.clear();
while (!queue.isEmpty() && events.size() < CHUNK_SIZE) {
final JGroupEventMessage event = queue.poll();
if (event == null) {
break;
}
if (event.isValid()) {
events.add(event);
} else {
LOG.warn("Collected soft reference during asynchronous queue flush, this event will not be replicated: " + event);
}
}
if (LOG.isTraceEnabled())
LOG.trace("Sending {} JGroupEventMessages from the asynchronous queue.", events.size());
this.sendData(null, events);
}
}
/**
* TimerTask that flushes the specified queue when called.
*/
private final class AsyncTimerTask extends TimerTask {
private final Queue queue;
private AsyncTimerTask(Queue newQueue) {
this.queue = newQueue;
}
@Override
public void run() {
if (!alive) {
this.cancel();
return;
}
flushQueue(this.queue);
if (!alive) {
this.cancel();
}
}
}
/* ********** CachePeer Unused ********** */
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public List> getElements(List keys) throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public String getGuid() throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public List> getKeys() throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public String getName() throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public Element getQuiet(Serializable key) throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public String getUrl() throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public String getUrlBase() throws RemoteException {
//Not Implemented
return null;
}
/**
* {@inheritDoc}
*/
public void put(Element element) throws IllegalArgumentException, IllegalStateException, RemoteException {
//Not Implemented
}
/**
* {@inheritDoc}
*/
public boolean remove(Serializable key) throws IllegalStateException, RemoteException {
//Not Implemented
return false;
}
/**
* {@inheritDoc}
*/
public void removeAll() throws RemoteException, IllegalStateException {
//Not Implemented
}
}