package com.rayo.server.storage;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.rayo.server.storage.ApplicationNotFoundException;
import com.rayo.server.storage.DatastoreException;
import com.rayo.server.storage.GatewayDatastore;
import com.rayo.server.storage.GatewayException;
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.voxeo.logging.Loggerf;
import com.voxeo.moho.util.ParticipantIDParser;
import com.voxeo.servlet.xmpp.JID;
/**
* <p>Default {@link GatewayStorageService} implementation. This implementation uses
* a {@link GatewayDatastore} to delegate all the persistence operations while adds
* the required business logic to guarantee the semantics needed by the Gateway.
* There is three different types of entities this storage service deals with:</p>
* <ul>
* <li>Rayo Nodes: Rayo nodes are the Rayo servers that register their interest
* in this particular Gateway.</li>
* <li>Client Applications: Client applications are developer applications which
* are linked with a JID and that will connect to a gateway to start cals, send
* call commands and receive call events.<li>
* <li>Calls: Finally, a Gateway Storage will also track all the different calls
* that are executed within a gateway.<li>
* </ul>
*
* <p>This Gateway Datastore implementation manages all these types of entities.
* All these classes are abstractions totally independent from the actual storage
* mechanism. By default we are providing two different implementations of the
* {@link GatewayDatastore} that can be switched from the spring configuration: A
* Cassandra based data store which can be used on distributed environments and a
* simple HashMap based data store which is only recommended for single gateway
* deployments.</p>
*
* @see GatewayDatastore
*
* @author martin
*
*/
public class DefaultGatewayStorageService implements GatewayStorageService {
protected static final Loggerf log = Loggerf.getLogger(DefaultGatewayStorageService.class);
private GatewayDatastore store;
private ReentrantLock nodeLock = new ReentrantLock();
@Override
public String getPlatformForClient(JID clientJid) {
GatewayClient client = store.getClient(clientJid.toString());
String platformId = null;
if (client != null) {
return client.getPlatform();
}
log.debug("Platform lookup for %s found %s", clientJid, platformId);
return platformId;
}
@Override
public GatewayClient registerClient(JID clientJid) throws GatewayException {
Application application = store.getApplication(clientJid.getBareJID().toString());
if (application == null) {
throw new ApplicationNotFoundException();
}
GatewayClient client = new GatewayClient(clientJid.toString(), application.getPlatform());
return store.storeClient(client);
}
@Override
public GatewayClient unregisterClient(JID clientJid) throws GatewayException {
return store.removeClient(clientJid.toString());
}
@Override
public GatewayClient getClient(JID bareJid) {
return store.getClient(bareJid.getBareJID().toString());
}
@Override
public List<RayoNode> getRayoNodes(String platformId) {
return store.getRayoNodesForPlatform(platformId);
}
@Override
public RayoNode registerRayoNode(String rayoNode, Collection<String> platformIds) throws GatewayException {
return registerRayoNode(new RayoNode(rayoNode, null, new HashSet<String>(platformIds)));
}
@Override
public RayoNode registerRayoNode(RayoNode rayoNode) throws GatewayException {
RayoNode node = store.getNode(rayoNode.getHostname());
try {
if (node != null) {
nodeLock.lock();
try {
// trick, consecutive errors and blacklisted are managed are gateway-only variables
// do not consider them when comparing
//rayoNode.setConsecutiveErrors(node.getConsecutiveErrors());
//rayoNode.setBlackListed(node.isBlackListed());
if (rayoNode.getIpAddress() == null) {
node.setIpAddress(null);
}
if (node.toString().equals(rayoNode.toString())) {
log.debug("Rayo Node [%s] already exists. Ignoring status update.", rayoNode);
return node;
} else {
log.debug("Rayo Node [%s] has been updated. Updating storage service.", rayoNode);
if (rayoNode.getIpAddress() == null) {
rayoNode.setIpAddress(InetAddress.getByName(rayoNode.getHostname()).getHostAddress());
}
return store.updateNode(rayoNode);
}
} finally {
nodeLock.unlock();
}
}
if (rayoNode.getIpAddress() == null) {
rayoNode.setIpAddress(InetAddress.getByName(rayoNode.getHostname()).getHostAddress());
}
return store.storeNode(rayoNode);
} catch (UnknownHostException uhe) {
throw new GatewayException("Unknown host", uhe);
}
}
@Override
public RayoNode updateRayoNode(RayoNode rayoNode) throws GatewayException {
nodeLock.lock();
try {
return store.updateNode(rayoNode);
} finally {
nodeLock.unlock();
}
}
@Override
public Collection<String> getRegisteredPlatforms() {
return store.getPlatforms();
}
@Override
public void unregisterRayoNode(String rayoNode) throws GatewayException {
nodeLock.lock();
try {
store.removeNode(rayoNode);
} finally {
nodeLock.unlock();
}
}
@SuppressWarnings("unchecked")
@Override
public Collection<String> getCallsForClient(String clientJid) {
Collection<String> calls = store.getCallsForClient(clientJid);
if (calls == null) {
calls = Collections.EMPTY_SET;
}
return calls;
}
@SuppressWarnings("unchecked")
@Override
public Collection<String> getCallsForNode(String nodeJid) {
Collection<String> calls = store.getCallsForNode(nodeJid);
if (calls == null) {
calls = Collections.EMPTY_SET;
}
return calls;
}
@SuppressWarnings("unchecked")
@Override
public Collection<String> getCalls() {
Collection<String> calls = store.getCalls();
if (calls == null) {
calls = Collections.EMPTY_SET;
}
return calls;
}
@Override
public void registerCall(String callId, String clientJid) throws GatewayException {
String ipAddress = ParticipantIDParser.getIpAddress(callId);
String nodeJid = store.getNodeForIpAddress(ipAddress);
if (nodeJid == null) {
throw new RayoNodeNotFoundException(String.format("Node not found for callId %s", callId));
}
if (log.isDebugEnabled()) {
log.debug("Call %s mapped to client %s", callId, clientJid);
log.debug("Call %s mapped to Rayo node %s", callId, nodeJid);
}
store.storeCall(new GatewayCall(callId, nodeJid, clientJid));
}
@Override
public void unregistercall(String callId) throws GatewayException {
store.removeCall(callId);
}
@Override
public String getclientJID(String callId) {
GatewayCall call = store.getCall(callId);
if (call != null) {
return call.getClientJid();
}
return null;
}
@SuppressWarnings("unchecked")
@Override
public List<String> getResourcesForClient(String jid) {
List<String> resources = store.getClientResources(jid);
if (resources == null) {
resources = Collections.EMPTY_LIST;
}
return resources;
}
@Override
public List<String> getClients() {
return store.getClients();
}
@Override
public String getRayoNode(String callId) {
GatewayCall call = store.getCall(callId);
if (call != null) {
return call.getNodeJid();
}
return null;
}
@Override
public Application registerApplication(Application application) throws DatastoreException {
return store.storeApplication(application);
}
@Override
public Application updateApplication(Application application) throws DatastoreException {
return store.updateApplication(application);
}
@Override
public Application unregisterApplication(String jid) throws DatastoreException {
return store.removeApplication(jid);
}
@Override
public Application getApplication(String jid) {
return store.getApplication(jid);
}
@Override
public List<Application> getApplications() {
return store.getApplications();
}
@Override
public Application getApplicationForAddress(String address) {
return store.getApplicationForAddress(address);
}
@Override
public List<String> getAddressesForApplication(String jid) {
return store.getAddressesForApplication(jid);
}
@Override
public void removeAddress(String address) throws DatastoreException {
store.removeAddress(address);
}
@Override
public void storeAddress(String address, String jid) throws DatastoreException {
store.storeAddress(address, jid);
}
@Override
public void storeAddresses(Collection<String> addresses, String jid) throws DatastoreException {
store.storeAddresses(addresses, jid);
}
@Override
public GatewayMixer getMixer(String id) {
return store.getMixer(id);
}
@Override
public void registerMixer(String mixerName, String hostname)
throws DatastoreException {
GatewayMixer mixer = new GatewayMixer(mixerName, hostname);
store.storeMixer(mixer);
}
@Override
public void unregisterMixer(String mixerName) throws DatastoreException {
store.removeMixer(mixerName);
}
@Override
public void addCallToMixer(String callId, String mixerName) throws DatastoreException {
store.addCallToMixer(callId, mixerName);
}
@Override
public void removeCallFromMixer(String callId, String mixerName) throws DatastoreException {
store.removeCallFromMixer(callId, mixerName);
}
@Override
public void addVerbToMixer(String verbId, String appJid, String mixerName)
throws DatastoreException {
GatewayVerb verb = new GatewayVerb(mixerName, verbId, appJid);
store.addVerbToMixer(verb, mixerName);
}
@Override
public void removeVerbFromMixer(String verbId, String mixerName)
throws DatastoreException {
store.removeVerbFromMixer(verbId, mixerName);
}
@Override
public List<GatewayVerb> getVerbs(String mixerName) {
return store.getVerbs(mixerName);
}
@Override
public List<GatewayVerb> getVerbs() {
return store.getVerbs();
}
@Override
public GatewayVerb getVerb(String mixerName, String verbId) {
return store.getVerb(mixerName, verbId);
}
@Override
public List<GatewayMixer> getMixers() {
return new ArrayList<GatewayMixer>(store.getMixers());
}
@Override
public void createFilter(String jid, String id) throws DatastoreException {
store.createFilter(jid, id);
}
@Override
public List<String> getFilteredApplications(String id) throws DatastoreException {
return store.getFilteredApplications(id);
}
@Override
public void removeFilter(String jid, String id) throws DatastoreException {
store.removeFilter(jid, id);
}
@Override
public void removeFilters(String id) throws DatastoreException {
store.removeFilters(id);
}
public void setStore(GatewayDatastore store) {
this.store = store;
}
}