package com.rayo.server.storage.memory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.rayo.server.storage.ApplicationAlreadyExistsException;
import com.rayo.server.storage.ApplicationNotFoundException;
import com.rayo.server.storage.DatastoreException;
import com.rayo.server.storage.GatewayDatastore;
import com.rayo.server.storage.RayoNodeAlreadyExistsException;
import com.rayo.server.storage.RayoNodeNotFoundException;
import com.rayo.server.storage.model.Application;
import com.rayo.server.storage.model.GatewayCall;
import com.rayo.server.storage.model.GatewayClient;
import com.rayo.server.storage.model.GatewayMixer;
import com.rayo.server.storage.model.GatewayVerb;
import com.rayo.server.storage.model.RayoNode;
import com.rayo.server.util.JIDUtils;
/**
* <p>Fully in-memory Map based implementation of the {@link GatewayDatastore} interface.</p>
*
* <p>This datastore is not intended to be usable in clustered Gateways as it will only
* work on a single-box scenario. It is provided as a reference implementation and can be
* useful if you plan to use only a single Gateway.</p>
*
* @author martin
*
*/
public class InMemoryDatastore implements GatewayDatastore {
private ReadWriteLock applicationsLock = new ReentrantReadWriteLock();
private ReadWriteLock nodesLock = new ReentrantReadWriteLock();
private ReadWriteLock callsLock = new ReentrantReadWriteLock();
private ReadWriteLock mixersLock = new ReentrantReadWriteLock();
private ReadWriteLock filtersLock = new ReentrantReadWriteLock();
private Map<String, RayoNode> nodesMap = new ConcurrentHashMap<String, RayoNode>();
private Map<String, RayoNode> ipsMap = new ConcurrentHashMap<String, RayoNode>();
private Map<String, List<RayoNode>> platformsMap = new ConcurrentHashMap<String, List<RayoNode>>();
private Map<String, GatewayCall> callsMap = new ConcurrentHashMap<String, GatewayCall>();
private Map<String, List<GatewayCall>> jidsMap = new ConcurrentHashMap<String, List<GatewayCall>>();
private Map<String, GatewayClient> clientsMap = new ConcurrentHashMap<String, GatewayClient>();
private Map<String, List<String>> resourcesMap = new ConcurrentHashMap<String, List<String>>();
private Map<String, Application> applicationsMap = new ConcurrentHashMap<String, Application>();
private Map<String, Application> addressesMap = new ConcurrentHashMap<String, Application>();
private Map<String, List<String>> appToAddressesMap = new ConcurrentHashMap<String, List<String>>();
private Map<String, GatewayMixer> mixersMap = new ConcurrentHashMap<String, GatewayMixer>();
private Map<String, List<GatewayVerb>> verbsMap = new ConcurrentHashMap<String, List<GatewayVerb>>();
private Map<String, List<String>> filtersMap = new ConcurrentHashMap<String, List<String>>();
@Override
public RayoNode storeNode(RayoNode node) throws DatastoreException {
if (getNode(node.getHostname()) != null) {
throw new RayoNodeAlreadyExistsException();
}
return store(node);
}
@Override
public RayoNode updateNode(RayoNode node) throws DatastoreException {
if (getNode(node.getHostname()) == null) {
throw new RayoNodeNotFoundException();
}
return store(node);
}
private RayoNode store(RayoNode node) throws DatastoreException {
Lock nodeLock = nodesLock.writeLock();
nodeLock.lock();
try {
RayoNode original = nodesMap.get(node.getHostname());
nodesMap.put(node.getHostname(), node);
ipsMap.put(node.getIpAddress(), node);
for(String platform: node.getPlatforms()) {
List<RayoNode> nodes = platformsMap.get(platform);
if (nodes == null) {
nodes = new ArrayList<RayoNode>();
platformsMap.put(platform, nodes);
} else {
if (nodes.contains(original)) {
nodes.remove(original);
}
}
nodes.add(node);
}
} finally {
nodeLock.unlock();
}
return node;
}
@Override
public RayoNode removeNode(String id) throws DatastoreException {
Lock nodeLock = nodesLock.writeLock();
nodeLock.lock();
try {
RayoNode node = getNode(id);
if (node == null) {
throw new RayoNodeNotFoundException();
}
nodesMap.remove(node.getHostname());
ipsMap.remove(node.getIpAddress());
for(String platform: node.getPlatforms()) {
List<RayoNode> nodes = platformsMap.get(platform);
if (nodes != null) {
nodes.remove(node);
}
}
return node;
} finally {
nodeLock.unlock();
}
}
@Override
public String getNodeForCall(String callId) {
GatewayCall call = getCall(callId);
if (call != null) {
return call.getNodeJid();
}
return null;
}
@Override
public RayoNode getNode(String id) {
Lock nodeLock = nodesLock.readLock();
nodeLock.lock();
try {
return nodesMap.get(id);
} finally {
nodeLock.unlock();
}
}
@SuppressWarnings("unchecked")
public List<RayoNode> getRayoNodesForPlatform(String platformId) {
Lock nodeLock = nodesLock.readLock();
nodeLock.lock();
try {
List<RayoNode> nodes = platformsMap.get(platformId);
if (nodes == null) {
nodes = Collections.EMPTY_LIST;
}
return nodes;
} finally {
nodeLock.unlock();
}
}
@Override
public String getNodeForIpAddress(String ip) {
Lock nodeLock = nodesLock.readLock();
nodeLock.lock();
try {
RayoNode node = ipsMap.get(ip);
if (node != null) {
return node.getHostname();
}
} finally {
nodeLock.unlock();
}
return null;
}
public List<String> getPlatforms() {
Lock nodeLock = nodesLock.readLock();
nodeLock.lock();
try {
return new ArrayList<String>(platformsMap.keySet());
} finally {
nodeLock.unlock();
}
}
@Override
public GatewayCall storeCall(GatewayCall call) throws DatastoreException {
RayoNode node = getNode(call.getNodeJid());
if (node == null) {
throw new RayoNodeNotFoundException();
}
Lock callLock = callsLock.writeLock();
callLock.lock();
try {
callsMap.put(call.getCallId(), call);
addCallToJid(call, call.getClientJid());
addCallToJid(call, node.getHostname());
} finally {
callLock.unlock();
}
return call;
}
private void addCallToJid(GatewayCall call, String jid) {
List<GatewayCall> calls = jidsMap.get(jid);
if (calls == null) {
calls = new ArrayList<GatewayCall>();
jidsMap.put(jid, calls);
}
if (!calls.contains(call)) {
calls.add(call);
}
}
@Override
public GatewayCall getCall(String id) {
Lock callLock = callsLock.readLock();
callLock.lock();
try {
return callsMap.get(id);
} finally {
callLock.unlock();
}
}
@Override
public GatewayCall removeCall(String id) throws DatastoreException {
Lock callLock = callsLock.writeLock();
callLock.lock();
try {
GatewayCall call = getCall(id);
callsMap.remove(call.getCallId());
removeCallFromJid(call, call.getClientJid());
removeCallFromJid(call, call.getNodeJid());
return call;
} finally {
callLock.unlock();
}
}
private void removeCallFromJid(GatewayCall call, String jid) {
List<GatewayCall> calls = jidsMap.get(jid);
if (calls != null) {
calls.remove(call);
}
}
public Collection<String> getCalls(String jid) {
Lock callLock = callsLock.readLock();
callLock.lock();
try {
List<String> ids = new ArrayList<String>();
List<GatewayCall> calls = jidsMap.get(jid);
if (calls != null) {
for(GatewayCall call: calls) {
ids.add(call.getCallId());
}
}
return ids;
} finally {
callLock.unlock();
}
}
@Override
public Collection<String> getCalls() {
Lock callLock = callsLock.readLock();
callLock.lock();
try {
List<String> ids = new ArrayList<String>();
for(GatewayCall call: callsMap.values()) {
ids.add(call.getCallId());
}
return ids;
} finally {
callLock.unlock();
}
}
@Override
public Collection<String> getCallsForClient(String jid) {
return getCalls(jid);
}
@Override
public Collection<String> getCallsForNode(String jid) {
return getCalls(jid);
}
public GatewayClient storeClient(GatewayClient client) throws DatastoreException {
Lock applicationLock = applicationsLock.writeLock();
applicationLock.lock();
try {
clientsMap.put(client.getJid(), client);
List<String> resources = resourcesMap.get(client.getBareJid());
if (resources == null) {
resources = new ArrayList<String>();
resourcesMap.put(client.getBareJid(), resources);
}
if (!resources.contains(client.getResource())) {
resources.add(client.getResource());
}
} finally {
applicationLock.unlock();
}
return client;
}
@Override
public GatewayClient removeClient(String jid) throws DatastoreException {
Lock applicationLock = applicationsLock.writeLock();
applicationLock.lock();
try {
GatewayClient client = getClient(jid);
clientsMap.remove(jid);
List<String> resources = resourcesMap.get(client.getBareJid());
if (resources != null) {
resources.remove(client.getResource());
}
return client;
} finally {
applicationLock.unlock();
}
}
@Override
public GatewayClient getClient(String jid) {
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
return clientsMap.get(jid);
} finally {
applicationLock.unlock();
}
}
@SuppressWarnings("unchecked")
@Override
public List<String> getClientResources(String bareJid) {
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
List<String> resources = resourcesMap.get(bareJid);
if (resources != null) {
return Collections.unmodifiableList(resources);
} else {
return Collections.EMPTY_LIST;
}
} finally {
applicationLock.unlock();
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public List<String> getClients() {
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
Set<String> clients = new HashSet<String>();
for (String client : clientsMap.keySet()) {
clients.add(JIDUtils.getBareJid(client));
}
return new ArrayList(clients);
} finally {
applicationLock.unlock();
}
}
@Override
public Application storeApplication(Application application) throws DatastoreException {
if (getApplication(application.getBareJid()) != null) {
throw new ApplicationAlreadyExistsException();
}
return saveApplication(application);
}
@Override
public Application updateApplication(Application application) throws DatastoreException {
if (getApplication(application.getBareJid()) == null) {
throw new ApplicationNotFoundException();
}
return saveApplication(application);
}
private Application saveApplication(Application application) throws DatastoreException {
Lock applicationLock = applicationsLock.writeLock();
applicationLock.lock();
try {
applicationsMap.put(application.getBareJid(), application);
} finally {
applicationLock.unlock();
}
return application;
}
@Override
public Application getApplication(String jid) {
if (jid == null) return null;
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
return applicationsMap.get(jid);
} finally {
applicationLock.unlock();
}
}
@Override
public List<Application> getApplications() {
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
return new ArrayList<Application>(applicationsMap.values());
} finally {
applicationLock.unlock();
}
}
@Override
public Application removeApplication(String jid) throws DatastoreException {
Application application = getApplication(jid);
if (application == null) {
throw new ApplicationNotFoundException();
}
Lock applicationLock = applicationsLock.writeLock();
applicationLock.lock();
try {
applicationsMap.remove(jid);
List<String> addresses = appToAddressesMap.get(jid);
if (addresses != null) {
for(String address: addresses) {
addressesMap.remove(address);
}
}
appToAddressesMap.remove(jid);
} finally {
applicationLock.unlock();
}
return application;
}
@Override
public Application getApplicationForAddress(String address) {
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
return addressesMap.get(address);
} finally {
applicationLock.unlock();
}
}
@Override
public void storeAddress(String address, String appId) throws DatastoreException {
List<String> addresses = new ArrayList<String>();
addresses.add(address);
storeAddresses(addresses, appId);
}
@Override
public void storeAddresses(Collection<String> addresses, String jid) throws DatastoreException {
Application application = getApplication(jid);
if (application == null) {
throw new ApplicationNotFoundException();
}
Lock applicationLock = applicationsLock.writeLock();
applicationLock.lock();
try {
for (String address: addresses) {
addressesMap.put(address, application);
List<String> addr = appToAddressesMap.get(application.getBareJid());
if (addr == null) {
addr = new ArrayList<String>();
appToAddressesMap.put(application.getBareJid(), addr);
}
if (!addr.contains(address)) {
addr.add(address);
}
}
} finally {
applicationLock.unlock();
}
}
@Override
public void removeAddress(String address) throws DatastoreException {
Lock applicationLock = applicationsLock.writeLock();
applicationLock.lock();
try {
Application application = getApplicationForAddress(address);
if (application != null) {
addressesMap.remove(address);
List<String> addresses = appToAddressesMap.get(application.getBareJid());
if (addresses != null) {
addresses.remove(address);
appToAddressesMap.put(application.getBareJid(), addresses);
}
}
} finally {
applicationLock.unlock();
}
}
@SuppressWarnings("unchecked")
@Override
public List<String> getAddressesForApplication(String appId) {
Lock applicationLock = applicationsLock.readLock();
applicationLock.lock();
try {
List<String> addresses = appToAddressesMap.get(appId);
if (addresses != null) {
return new ArrayList<String>(addresses);
} else {
return Collections.EMPTY_LIST;
}
} finally {
applicationLock.unlock();
}
}
@Override
public GatewayMixer getMixer(String mixerName) {
Lock mixerLock = mixersLock.readLock();
mixerLock.lock();
try {
return mixersMap.get(mixerName);
} finally {
mixerLock.unlock();
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Collection<GatewayMixer> getMixers() {
Lock mixerLock = mixersLock.readLock();
mixerLock.lock();
try {
return new ArrayList(mixersMap.values());
} finally {
mixerLock.unlock();
}
}
@Override
public GatewayMixer removeMixer(String mixerName) throws DatastoreException {
Lock mixerLock = mixersLock.writeLock();
mixerLock.lock();
try {
return mixersMap.remove(mixerName);
} finally {
mixerLock.unlock();
}
}
@Override
public GatewayMixer storeMixer(GatewayMixer mixer) throws DatastoreException {
Lock mixerLock = mixersLock.writeLock();
mixerLock.lock();
try {
mixersMap.put(mixer.getName(), mixer);
return mixer;
} finally {
mixerLock.unlock();
}
}
@Override
public void addCallToMixer(String callId, String mixerName) throws DatastoreException {
Lock mixerLock = mixersLock.writeLock();
mixerLock.lock();
try {
GatewayMixer mixer = mixersMap.get(mixerName);
mixer.addCall(callId);
} finally {
mixerLock.unlock();
}
}
@Override
public void removeCallFromMixer(String callId, String mixerName) throws DatastoreException {
Lock mixerLock = mixersLock.writeLock();
mixerLock.lock();
try {
GatewayMixer mixer = mixersMap.get(mixerName);
mixer.removeCall(callId);
} finally {
mixerLock.unlock();
}
}
@Override
public void addVerbToMixer(GatewayVerb verb, String mixerName) {
Lock mixerLock = mixersLock.writeLock();
mixerLock.lock();
try {
List<GatewayVerb> verbs = verbsMap.get(mixerName);
if (verbs == null) {
verbs = new ArrayList<GatewayVerb>();
verbsMap.put(mixerName, verbs);
}
if (!verbs.contains(verb)) {
verbs.add(verb);
}
} finally {
mixerLock.unlock();
}
}
@Override
public void removeVerbFromMixer(String verbId, String mixerName)
throws DatastoreException {
Lock mixerLock = mixersLock.writeLock();
mixerLock.lock();
try {
List<GatewayVerb> verbs = verbsMap.get(mixerName);
if (verbs != null) {
Iterator<GatewayVerb> it = verbs.iterator();
while (it.hasNext()) {
if (it.next().getVerbId().equals(verbId)) {
it.remove();
}
}
}
} finally {
mixerLock.unlock();
}
}
@Override
public GatewayVerb getVerb(String mixerName, String verbId) {
Lock mixerLock = mixersLock.readLock();
mixerLock.lock();
try {
List<GatewayVerb> verbs = verbsMap.get(mixerName);
if (verbs != null) {
for(GatewayVerb verb: verbs) {
if (verb.getVerbId().equals(verbId)) {
return verb;
}
}
}
} finally {
mixerLock.unlock();
}
return null;
}
@SuppressWarnings("unchecked")
@Override
public List<GatewayVerb> getVerbs(String mixerName) {
Lock mixerLock = mixersLock.readLock();
mixerLock.lock();
try {
List<GatewayVerb> verbs = verbsMap.get(mixerName);
if (verbs == null) {
verbs = Collections.EMPTY_LIST;
}
return verbs;
} finally {
mixerLock.unlock();
}
}
@Override
public List<GatewayVerb> getVerbs() {
List<GatewayVerb> verbs = new ArrayList<GatewayVerb>();
Lock mixerLock = mixersLock.readLock();
mixerLock.lock();
try {
for (List<GatewayVerb> it: verbsMap.values()) {
if (!it.isEmpty()) {
verbs.addAll(it);
}
}
} finally {
mixerLock.unlock();
}
return verbs;
}
@Override
public void createFilter(String jid, String id) throws DatastoreException {
Lock filterLock = filtersLock.writeLock();
filterLock.lock();
try {
List<String> filters = filtersMap.get(id);
if (filters == null) {
filters = new ArrayList<String>();
filtersMap.put(id, filters);
}
if (!filters.contains(jid)) {
filters.add(jid);
}
} finally {
filterLock.unlock();
}
}
@Override
public void removeFilter(String jid, String id) throws DatastoreException {
Lock filterLock = filtersLock.writeLock();
filterLock.lock();
try {
List<String> filters = filtersMap.get(id);
if (filters != null) {
filters.remove(jid);
}
} finally {
filterLock.unlock();
}
}
@Override
public void removeFilters(String id) throws DatastoreException {
Lock filterLock = filtersLock.writeLock();
filterLock.lock();
try {
filtersMap.remove(id);
} finally {
filterLock.unlock();
}
}
@SuppressWarnings("unchecked")
@Override
public List<String> getFilteredApplications(String id) throws DatastoreException {
Lock filterLock = filtersLock.writeLock();
filterLock.lock();
try {
List<String> filters = filtersMap.get(id);
if (filters != null) {
return new ArrayList<String>(filters);
} else {
return Collections.EMPTY_LIST;
}
} finally {
filterLock.unlock();
}
}
}