package org.fluxtream.core.services.impl;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.fluxtream.core.Configuration;
import org.fluxtream.core.aspects.FlxLogger;
import org.fluxtream.core.connectors.Connector;
import org.fluxtream.core.connectors.annotations.Updater;
import org.fluxtream.core.connectors.updaters.AbstractUpdater;
import org.fluxtream.core.connectors.updaters.SettingsAwareUpdater;
import org.fluxtream.core.connectors.updaters.UpdateFailedException;
import org.fluxtream.core.domain.*;
import org.fluxtream.core.domain.GuestSettings.DistanceMeasureUnit;
import org.fluxtream.core.domain.GuestSettings.LengthMeasureUnit;
import org.fluxtream.core.domain.GuestSettings.TemperatureUnit;
import org.fluxtream.core.domain.GuestSettings.WeightMeasureUnit;
import org.fluxtream.core.services.GuestService;
import org.fluxtream.core.services.SettingsService;
import org.fluxtream.core.utils.JPAUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
@Service
@Transactional(readOnly=true)
public class SettingsServiceImpl implements SettingsService {
static FlxLogger logger = FlxLogger.getLogger(SettingsServiceImpl.class);
@Autowired
Configuration env;
@Autowired
GuestService guestService;
@PersistenceContext
EntityManager em;
@Autowired
BeanFactory beanFactory;
Gson gson = new Gson();
@Override
@Transactional(readOnly = false)
public GuestSettings getSettings(long guestId) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
if (settings != null) {
settings.config = env;
settings.topics = getTopics(guestId);
return settings;
}
else {
settings = new GuestSettings();
settings.guestId = guestId;
settings.createMessageDisplayCounters();
em.persist(settings);
settings.config = env;
settings.topics = getTopics(guestId);
return settings;
}
}
private HashMap<String, String> getTopics(long guestId) {
Query nativeQuery = em.createNativeQuery("SELECT fluxtreamId, name FROM Facet_FluxtreamCaptureTopic topic WHERE topic.guestId=?");
nativeQuery.setParameter(1, guestId);
List<Object[]> resultList = nativeQuery.getResultList();
HashMap<String,String> topics = new HashMap<String,String>();
for (Object[] objects : resultList) {
String rawTopicId = (String) objects[0];
String topicId = rawTopicId;
// int dotIndex = rawTopicId.indexOf(".");
// if (dotIndex!=-1)
// topicId = rawTopicId.substring(0, dotIndex);
topics.put("topic_" + topicId, objects[1].toString());
}
return topics;
}
@Override
@Transactional(readOnly=false)
public int incrementDisplayCounter(final long guestId, final String messageName) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
final Integer count = settings.getMessageDisplayCounter(messageName);
int incremented = count==null? 1 : count+1;
if (count==null) settings.setMessageDisplayCounter(messageName, incremented);
else settings.setMessageDisplayCounter(messageName, incremented);
em.persist(settings);
return incremented;
}
@Override
@Transactional(readOnly = false)
public void setFirstname(long guestId, String firstname) {
Guest guest = guestService.getGuestById(guestId);
guest.firstname = firstname;
}
@Override
@Transactional(readOnly = false)
public void setLastname(long guestId, String lastname) {
Guest guest = guestService.getGuestById(guestId);
guest.lastname = lastname;
}
@Override
@Transactional(readOnly = false)
public void setWeightMeasureUnit(long guestId, WeightMeasureUnit unit) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
settings.weightMeasureUnit = unit;
}
@Override
@Transactional(readOnly = false)
public void setTemperatureUnit(long guestId, TemperatureUnit unit) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
settings.temperatureUnit = unit;
}
@Override
@Transactional(readOnly = false)
public void setConnectorFilterState(final long guestId, final String stateJSON) {
ConnectorFilterState filterState = JPAUtils.findUnique(em, ConnectorFilterState.class,
"connectorFilterState",
guestId);
if (filterState==null) {
filterState = new ConnectorFilterState();
filterState.guestId = guestId;
filterState.stateJSON = stateJSON;
em.persist(filterState);
} else {
filterState.stateJSON = stateJSON;
em.merge(filterState);
}
}
@Override
public String getConnectorFilterState(final long guestId) {
ConnectorFilterState filterState = JPAUtils.findUnique(em, ConnectorFilterState.class, "connectorFilterState", guestId);
return filterState == null ? "{}" : filterState.stateJSON;
}
/**
* Returns the serialized settings for a connector instance or new settings of the right
* type if none have been already persisted and a non-default Settings class is specified in
* the connector's Updater (annotation)
* @param apiKeyId
* @return
* @throws UpdateFailedException
*/
@Override
@Transactional(readOnly = false)
public Object getConnectorSettings(final long apiKeyId) {
ApiKey apiKey = guestService.getApiKey(apiKeyId);
Object settings = apiKey.getSettings();
final Class<? extends AbstractUpdater> updaterClass = apiKey.getConnector().getUpdaterClass();
final Class<?> settingsClass = updaterClass.getAnnotation(Updater.class).settings();
if (settings==null&& settingsClass != Updater.EmptySettings.class){
try {
settings = settingsClass.newInstance();
apiKey.setSettings(settings);
em.persist(apiKey);
return settings;
}
catch (Exception e) {
throw new RuntimeException("Could not instantiate default settings for connector " + apiKey.getConnector().getName());
}
}
return settings;
}
@Override
@Transactional(readOnly=false)
public void persistConnectorSettings(final long apiKeyId, final Object settings, final Object defaultSettings) {
ApiKey apiKey = guestService.getApiKey(apiKeyId);
apiKey.setSettings(settings);
apiKey.setDefaultSettings(defaultSettings);
em.persist(apiKey);
}
@Override
@Transactional(readOnly=false)
public void saveConnectorSettings(final long apiKeyId, final String json) {
ApiKey apiKey = guestService.getApiKey(apiKeyId);
final Updater updaterAnnotation = apiKey.getConnector().getUpdaterClass().getAnnotation(Updater.class);
StringBuilder sb = new StringBuilder("module=connectors component=settingsServiceImpl action=saveConnectorSettings")
.append(" apiKeyId=").append(apiKeyId);
if (updaterAnnotation.settings()==Updater.EmptySettings.class){
logger.warn(sb.append(" message=\"no settings class has been specified for \"" + apiKey.getConnector().getName()));
return;
}
try {
final Object settings = gson.fromJson(json, updaterAnnotation.settings());
guestService.setApiKeySettings(apiKeyId, settings);
final AbstractUpdater updater = beanFactory.getBean(apiKey.getConnector().getUpdaterClass());
((SettingsAwareUpdater)updater).connectorSettingsChanged(apiKeyId, settings);
}
catch (Exception e) {
logger.warn(sb.append(" message=\"unexpected exception when saving connector settings for connector \"" + apiKey.getConnector().getName()));
return;
}
}
/**
* Reset connector settings to the defaults if they have been specified, or a new instance of
* the settings class for this connector, if one exists
* @param apiKeyId
*/
@Override
@Transactional(readOnly=false)
public void saveConnectorSettings(final long apiKeyId, final Serializable settings){
saveConnectorSettings(apiKeyId, gson.toJson(settings));
}
@Override
@Transactional(readOnly=false)
public void resetConnectorSettings(final long apiKeyId) {
ApiKey apiKey = guestService.getApiKey(apiKeyId);
final Class<? extends AbstractUpdater> updaterClass = apiKey.getConnector().getUpdaterClass();
Object defaultSettings = apiKey.getDefaultSettings();
if (defaultSettings!=null)
apiKey.setSettings(defaultSettings);
else {
final Class<?> settingsClass = updaterClass.getAnnotation(Updater.class).settings();
if (settingsClass != Updater.EmptySettings.class){
try {
defaultSettings = settingsClass.newInstance();
apiKey.setSettings(defaultSettings);
}
catch (Exception e) {
throw new RuntimeException("Could not instantiate default settings for connector " + apiKey.getConnector().getName());
}
}
}
if (SettingsAwareUpdater.class.isAssignableFrom(updaterClass)) {
final AbstractUpdater updater = beanFactory.getBean(apiKey.getConnector().getUpdaterClass());
((SettingsAwareUpdater)updater).connectorSettingsChanged(apiKeyId, defaultSettings);
}
em.persist(apiKey);
}
@Override
@Transactional(readOnly=false)
public void setPreferences(long guestId, String preferences) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
settings.preferences = preferences;
}
@Override
public String getPreferences(long guestId) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
return settings.preferences;
}
@Override
@Transactional(readOnly = false)
public void setLengthMeasureUnit(long guestId, LengthMeasureUnit unit) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
settings.lengthMeasureUnit = unit;
}
@Override
@Transactional(readOnly = false)
public void setDistanceMeasureUnit(long guestId, DistanceMeasureUnit unit) {
GuestSettings settings = JPAUtils.findUnique(em, GuestSettings.class,
"settings.byGuestId", guestId);
settings.distanceMeasureUnit = unit;
}
@Override
@Transactional(readOnly=false)
public GuestAddress addAddress(long guestId, String type, String add, double latitude,
double longitude, long since, long until, double radius, String jsonString) {
GuestAddress address = new GuestAddress();
address.guestId = guestId;
address.address = add;
address.latitude = latitude;
address.longitude = longitude;
address.since = since;
address.until = until;
address.type = type;
address.radius = radius;
address.jsonStorage = jsonString;
if (!isAddressValid(address)) {
throw new RuntimeException("invalid address");
}
em.persist(address);
return address;
}
@Override
@Transactional(readOnly=false)
public GuestAddress addAddress(long guestId, String type, String add, double latitude,
double longitude, long since, double radius, String jsonString) {
GuestAddress address = new GuestAddress();
address.guestId = guestId;
address.address = add;
address.latitude = latitude;
address.longitude = longitude;
address.since = since;
address.type = type;
address.radius = radius;
address.jsonStorage = jsonString;
em.persist(address);
return address;
}
@Override
public String[] getChannelsForConnector(final long guestId, final Connector connector) {
ConnectorChannelSet channelSet = JPAUtils.findUnique(em, ConnectorChannelSet.class, "connectorChannelSet.byApi", guestId, connector.value());
String[] channels;
if (channelSet == null){
channels = connector.getDefaultChannels();
}
else{
channels = channelSet.channels.split(",");
}
return channels;
}
@Override
@Transactional(readOnly = false)
public void setChannelsForConnector(final long guestId, final Connector connector, String[] channels) {
ConnectorChannelSet channelSet = JPAUtils.findUnique(em, ConnectorChannelSet.class, "connectorChannelSet.byApi", guestId, connector.value());
if (channelSet==null) {
channelSet = new ConnectorChannelSet();
channelSet.guestId = guestId;
channelSet.api = connector.value();
channelSet.channels = StringUtils.join(channels,",");
em.persist(channelSet);
} else {
channelSet.channels = StringUtils.join(channels,",");
em.merge(channelSet);
}
}
@Override
public List<GuestAddress> getAllAddressesForDate(long guestId, long date) {
return JPAUtils.find(em, GuestAddress.class, "address.when", guestId, date, date);
}
@Override
public List<GuestAddress> getAllAddresses(long guestId){
return JPAUtils.find(em,GuestAddress.class,"addresses.byGuestId",guestId);
}
@Override
public List<GuestAddress> getAllAddressesOfType(long guestId, String type){
return JPAUtils.find(em,GuestAddress.class,"addresses.byType", guestId, type);
}
@Override
public List<GuestAddress> getAllAddressesOfTypeForDate(long guestId, String type, long date){
return JPAUtils.find(em,GuestAddress.class,"addresses.byType.when", guestId, type, date, date);
}
//delete functions are currently unimplemented
@Override
@Transactional(readOnly=false)
public void deleteAddressById(long guestId, long id){
GuestAddress address = em.find(GuestAddress.class,id);
if (address.guestId != guestId) {
throw new RuntimeException("Cannot delete address you don't have ownership of.");
}
deleteAddress(address);
}
@Override
@Transactional(readOnly=false)
public void deleteAllAddresses(long guestId){
deleteAddresses(getAllAddresses(guestId));
}
@Override
@Transactional(readOnly=false)
public void deleteAllAddressesAtDate(long guestId, long date){
deleteAddresses(getAllAddressesForDate(guestId, date));
}
@Override
@Transactional(readOnly=false)
public void deleteAllAddressesOfType(long guestId, String type){
deleteAddresses(getAllAddressesOfType(guestId, type));
}
@Override
@Transactional(readOnly=false)
public void deleteAllAddressesOfTypeForDate(long guestId, String type, long date){
deleteAddresses(getAllAddressesOfTypeForDate(guestId,type,date));
}
@Transactional(readOnly=false)
private void deleteAddresses(List<GuestAddress> addresses){
for (GuestAddress address : addresses) {
em.remove(address);
}
}
@Transactional(readOnly=false)
private void deleteAddress(GuestAddress address){
em.remove(address);
}
@Override
public GuestAddress getAddressById(long guestId, long id){
GuestAddress address = em.find(GuestAddress.class,id);
if (address.guestId == guestId) {
return address;
}
return null;
}
@Override
@Transactional(readOnly=false)
public GuestAddress updateAddress(long guestId, long addressId, String type, String address, Double latitude,
Double longitude, Long since, Long until, Double radius, String jsonString){
GuestAddress add = getAddressById(guestId,addressId);
if (address != null) {
add.address = address;
}
if (type != null) {
add.type = type;
}
if (latitude != null) {
add.latitude = latitude;
}
if (longitude != null) {
add.longitude = longitude;
}
if (since != null) {
add.since = since;
}
if (until != null) {
add.until = until;
}
if (jsonString != null) {
add.jsonStorage = jsonString;
}
if (radius != null) {
add.radius = radius;
}
if (!isAddressValid(add)){
em.refresh(add);
throw new RuntimeException("invalid address");
}
return add;
}
private boolean isAddressValid(GuestAddress address){
if (address.until < address.since) {
return false;
}
return true;
}
/*@Override //saving for reference
public void removeAddress(long guestId, long addressId) {
GuestAddress add = em.find(GuestAddress.class, addressId);
if (add.guestId==guestId)
em.remove(add);
}*/
}