/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* 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.ontopia.persistence.proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import net.ontopia.utils.OntopiaRuntimeException;
import net.ontopia.utils.StreamUtils;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: Class that represents a cluster of OKS instances.
*/
public class JGroupsCluster extends ReceiverAdapter implements ClusterIF {
// Define a logging category.
static Logger log = LoggerFactory.getLogger(JGroupsCluster.class.getName());
final static Integer DATA = new Integer(1);
protected JChannel dchannel;
protected String clusterId;
protected String clusterProps;
protected StorageIF storage;
protected ConcurrentLinkedQueue<JGroupsEvent> queue;
// Sample cluster properties: UDP(mcast_addr=228.10.9.8;mcast_port=5678):PING:FD
JGroupsCluster(String clusterId, String clusterProps, StorageIF storage) {
this.clusterId = clusterId;
this.clusterProps = clusterProps;
this.storage = storage;
this.queue = new ConcurrentLinkedQueue<JGroupsEvent>();
}
public synchronized void join() {
try {
String joinMessage = "Joining JGroups cluster: '" + clusterId + "'";
try {
URL url = (clusterProps != null ? StreamUtils.getResource(clusterProps) : null);
if (url == null) {
if (clusterProps == null) {
log.info(joinMessage + ", using default cluster properties.");
this.dchannel = new JChannel();
} else {
log.info(joinMessage + ", using cluster properties as given: '" + clusterProps + "'");
this.dchannel = new JChannel(clusterProps);
}
} else {
log.info(joinMessage + ", using cluster properties in: '" + url + "'");
this.dchannel = new JChannel(url);
}
} catch (Exception e) {
throw new OntopiaRuntimeException("Problems occurred while loading " +
"JGroups properties from " + clusterProps + ", trying to join cluster '" +
clusterId + "'", e);
}
dchannel.setReceiver(this);
this.dchannel.connect(clusterId);
} catch (Exception e) {
throw new OntopiaRuntimeException("Could not connect to cluster '" + clusterId + "'.", e);
}
}
public synchronized void leave() {
log.info("Leaving cluster: '" + clusterId + "'");
flush();
if (dchannel != null) {
dchannel.close();
dchannel = null;
}
}
public void evictIdentity(IdentityIF identity) {
JGroupsEvent e = new JGroupsEvent();
e.eventType = ClusterIF.DATA_CACHE_IDENTITY_EVICT;
e.value = identity;
queue(e);
}
public void evictFields(IdentityIF identity) {
JGroupsEvent e = new JGroupsEvent();
e.eventType = ClusterIF.DATA_CACHE_FIELDS_EVICT;
e.value = identity;
queue(e);
}
public void evictField(IdentityIF identity, int field) {
JGroupsEvent e = new JGroupsEvent();
e.eventType = ClusterIF.DATA_CACHE_FIELD_EVICT;
e.value = identity;
e.field = field;
queue(e);
}
public void clearDatacache() {
JGroupsEvent e = new JGroupsEvent();
e.eventType = ClusterIF.DATA_CACHE_CLEAR;
queue(e);
}
public void evictCache(IdentityIF namespace, int cacheType, Object key) {
JGroupsEvent e = new JGroupsEvent();
e.eventType = cacheType; // event type is same as cache type
e.namespace = namespace;
e.value = key;
queue(e);
}
public void evictCache(IdentityIF namespace, int cacheType, Collection keys) {
JGroupsEvent e = new JGroupsEvent();
e.eventType = cacheType; // event type is same as cache type
e.namespace = namespace;
e.value = keys;
queue(e);
}
public void clearCache(IdentityIF namespace, int cacheType) {
JGroupsEvent e = new JGroupsEvent();
e.eventType = cacheType + 1; // event type is same as cache type + 1
e.namespace = namespace;
queue(e);
}
private void queue(JGroupsEvent e) {
queue.add(e);
}
// -----------------------------------------------------------------------------
// Event I/O
// -----------------------------------------------------------------------------
public synchronized void flush() {
// retrieve all pending events from event queue
JGroupsEvent o = queue.poll();
if (o != null) {
ArrayList<JGroupsEvent> data = new ArrayList<JGroupsEvent>();
do {
data.add(o);
o = queue.poll();
} while (o != null);
// send event list to cluster
log.debug("Sending " + data.size() + " events.");
sendEvent(data);
}
}
private void sendEvent(java.io.Serializable e) {
log.debug("Sending: " + e);
try {
Message msg = new Message(null, null, e);
dchannel.send(msg);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
protected void processEvent(JGroupsEvent e) {
//! log.debug("Processing event: " + e);
if (storage.getStorageCache() == null) {
log.warn("Cannot process cluster event without shared storage cache. Ignoring event: " + e);
return;
}
switch (e.eventType) {
case ClusterIF.DATA_CACHE_IDENTITY_EVICT:
log.debug(" IE: " + e.value);
storage.getStorageCache().evictIdentity((IdentityIF)e.value, false);
break;
case ClusterIF.DATA_CACHE_FIELDS_EVICT:
log.debug(" FE: " + e.value);
storage.getStorageCache().evictFields((IdentityIF)e.value, false);
break;
case ClusterIF.DATA_CACHE_FIELD_EVICT:
log.debug(" FI: " + e.field + " " + e.value);
storage.getStorageCache().evictField((IdentityIF)e.value, e.field, false);
break;
case ClusterIF.DATA_CACHE_CLEAR:
log.debug(" DC!");
storage.getStorageCache().clear(false);
break;
case ClusterIF.QUERY_CACHE_SRCLOC_EVICT:
case ClusterIF.QUERY_CACHE_SUBIND_EVICT:
case ClusterIF.QUERY_CACHE_SUBLOC_EVICT:
case ClusterIF.QUERY_CACHE_RT1_EVICT:
case ClusterIF.QUERY_CACHE_RT2_EVICT: {
log.debug(" QE " + e.eventType + ": " + e.value);
EvictableIF evictable = storage.getHelperObject(e.eventType, e.namespace);
if (e.value instanceof Collection)
evictable.removeAll((Collection)e.value, false);
else
evictable.remove(e.value, false);
break;
}
case ClusterIF.QUERY_CACHE_SRCLOC_CLEAR:
case ClusterIF.QUERY_CACHE_SUBIND_CLEAR:
case ClusterIF.QUERY_CACHE_SUBLOC_CLEAR:
case ClusterIF.QUERY_CACHE_RT1_CLEAR:
case ClusterIF.QUERY_CACHE_RT2_CLEAR: {
log.debug(" QC " + e.eventType + ": " + e.value);
EvictableIF evictable = storage.getHelperObject(e.eventType-1 , e.namespace);
evictable.clear(false);
break;
}
default:
log.debug("Ignored event: " + e);
}
}
// -----------------------------------------------------------------------------
// JGroups ReceiverAdapter implementation
// -----------------------------------------------------------------------------
@Override
public void receive(Message msg) {
try {
List data = (List)msg.getObject();
log.debug("Received " + data.size() + " events.");
for (int i=0; i < data.size(); i++) {
JGroupsEvent e = (JGroupsEvent)data.get(i);
processEvent(e);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}