package org.epics.archiverappliance.config.persistence;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.config.ConfigPersistence;
import org.epics.archiverappliance.config.ConfigService;
import org.epics.archiverappliance.config.PVTypeInfo;
import org.epics.archiverappliance.config.UserSpecifiedSamplingParams;
import org.epics.archiverappliance.config.exception.ConfigException;
import org.epics.archiverappliance.utils.ui.JSONDecoder;
import org.epics.archiverappliance.utils.ui.JSONEncoder;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* Uses redis as a persistence layer.
* To set the path to the redis instance, use the environment variable ARCHAPPL_PERSISTENCE_LAYER_REDISURL.
* This defaults to <code>localhost</code>
* To use this persistence layer, use
* <pre>
* export ARCHAPPL_PERSISTENCE_LAYER="org.epics.archiverappliance.config.persistence.RedisPersistence"
* export ARCHAPPL_PERSISTENCE_LAYER_REDISURL="localhost"
* </pre>
* @author mshankar
*
*/
public class RedisPersistence implements ConfigPersistence {
private static Logger logger = Logger.getLogger(RedisPersistence.class.getName());
public static final String ARCHAPPL_PERSISTENCE_LAYER_REDISURL = ConfigService.ARCHAPPL_PERSISTENCE_LAYER + "_REDISURL";
private static String redisURL = "localhost";
private JedisPool jedisPool = null;
public RedisPersistence() throws ConfigException {
String pathFromEnv = System.getProperty(ARCHAPPL_PERSISTENCE_LAYER_REDISURL);
if(pathFromEnv == null) {
pathFromEnv = System.getenv(ARCHAPPL_PERSISTENCE_LAYER_REDISURL);
}
if(pathFromEnv != null) {
redisURL = pathFromEnv;
jedisPool = new JedisPool(redisURL);
}
logger.info("PV information is persisted in the redis instance at " + redisURL);
}
@Override
public List<String> getTypeInfoKeys() throws IOException {
return getKeys("TypeInfo");
}
@Override
public PVTypeInfo getTypeInfo(String pvName) throws IOException {
logger.debug("Getting typeinfo for pv " + pvName + " from db instead of cache");
return getValueForKey("TypeInfo", pvName, new PVTypeInfo(), PVTypeInfo.class);
}
@Override
public void putTypeInfo(String pvName, PVTypeInfo typeInfo) throws IOException {
putValueForKey("TypeInfo", pvName, typeInfo, PVTypeInfo.class);
}
@Override
public void deleteTypeInfo(String pvName) throws IOException {
logger.debug("Removing typeinfo for pv " + pvName + " from db and cache");
removeKey("TypeInfo", pvName);
}
@Override
public List<String> getArchivePVRequestsKeys() throws IOException {
return getKeys("ArchivePVRequests");
}
@Override
public UserSpecifiedSamplingParams getArchivePVRequest(String pvName) throws IOException {
return getValueForKey("ArchivePVRequests", pvName, new UserSpecifiedSamplingParams(), UserSpecifiedSamplingParams.class);
}
@Override
public void putArchivePVRequest(String pvName, UserSpecifiedSamplingParams userParams) throws IOException {
putValueForKey("ArchivePVRequests", pvName, userParams, UserSpecifiedSamplingParams.class);
}
@Override
public void removeArchivePVRequest(String pvName) throws IOException {
removeKey("ArchivePVRequests", pvName);
}
@Override
public List<String> getExternalDataServersKeys() throws IOException {
return getKeys("ExternalDataServers");
}
@Override
public String getExternalDataServer(String serverId) throws IOException {
return getStringValueForKey("ExternalDataServers", serverId);
}
@Override
public void putExternalDataServer(String serverId, String serverInfo) throws IOException {
putStringValueForKey("ExternalDataServers", serverId, serverInfo);
}
@Override
public void removeExternalDataServer(String serverId, String serverInfo) throws IOException {
removeKey("ExternalDataServers", serverId);
}
@Override
public List<String> getAliasNamesToRealNamesKeys() throws IOException {
return getKeys("AliasNamesToRealNames");
}
@Override
public String getAliasNamesToRealName(String pvName) throws IOException {
return getStringValueForKey("AliasNamesToRealNames", pvName);
}
@Override
public void putAliasNamesToRealName(String pvName, String realName) throws IOException {
putStringValueForKey("AliasNamesToRealNames", pvName, realName);
}
@Override
public void removeAliasName(String pvName, String realName) throws IOException {
removeKey("AliasNamesToRealNames", pvName);
}
private List<String> getKeys(String recordName) throws IOException {
try(Jedis jedis = jedisPool.getResource()) {
Set<String> keysFromRedis = jedis.keys(recordName + "/*");
LinkedList<String> retVal = new LinkedList<String>();
for(String keyFromRedis : keysFromRedis) {
retVal.add(keyFromRedis.split("/")[1]);
}
return retVal;
}
}
private <T> T getValueForKey(String recordName, String key, T obj, Class<T> clazz) throws IOException {
try(Jedis jedis = jedisPool.getResource()) {
String jsonStr = jedis.get(recordName + "/" + key);
if(jsonStr != null) {
JSONObject jsonObj = (JSONObject) JSONValue.parse(jsonStr);
JSONDecoder<T> decoder = JSONDecoder.getDecoder(clazz);
decoder.decode(jsonObj, obj);
return obj;
}
} catch(Exception ex) {
throw new IOException(ex);
}
logger.debug("Cannot find data for key " + recordName + "/" + key + " in redis");
return null;
}
private String getStringValueForKey(String recordName, String key) throws IOException {
try(Jedis jedis = jedisPool.getResource()) {
String jsonStr = jedis.get(recordName + "/" + key);
return jsonStr;
} catch(Exception ex) {
throw new IOException(ex);
}
}
private <T> void putValueForKey(String recordName, String key, T obj, Class<T> clazz) throws IOException {
if(key == null || key.equals("")) throw new IOException("key cannot be null when persisting " + recordName);
if(obj == null || obj.equals("")) throw new IOException("value cannot be null when persisting " + recordName);
try(Jedis jedis = jedisPool.getResource()) {
JSONEncoder<T> encoder = JSONEncoder.getEncoder(clazz);
JSONObject jsonObj = encoder.encode(obj);
String jsonStr = jsonObj.toJSONString();
jedis.set(recordName + "/" + key, jsonStr);
} catch(Exception ex) {
throw new IOException(ex);
}
}
private void putStringValueForKey(String recordName, String key, String value) throws IOException {
if(key == null || key.equals("")) throw new IOException("key cannot be null when persisting " + recordName);
if(value == null || value.equals("")) throw new IOException("value cannot be null when persisting " + recordName);
try(Jedis jedis = jedisPool.getResource()) {
jedis.set(recordName + "/" + key, value);
} catch(Exception ex) {
throw new IOException(ex);
}
}
private void removeKey(String recordName, String key) throws IOException {
try(Jedis jedis = jedisPool.getResource()) {
jedis.del(recordName + "/" + key);
} catch(Exception ex) {
throw new IOException(ex);
}
}
}