package recommender.impl.database;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import recommender.core.Recommender;
import recommender.core.database.DBLogic;
import recommender.core.database.RecommenderDatabaseManager;
import recommender.core.database.params.EntityParam;
import recommender.core.database.params.FeedbackParam;
import recommender.core.database.params.LatencyParam;
import recommender.core.database.params.RecAdminOverview;
import recommender.core.database.params.RecQueryParam;
import recommender.core.database.params.RecQuerySettingParam;
import recommender.core.database.params.RecSettingParam;
import recommender.core.database.params.ResultParam;
import recommender.core.database.params.SelectorQueryMapParam;
import recommender.core.database.params.SelectorSettingParam;
import recommender.core.interfaces.RecommenderConnector;
import recommender.core.interfaces.model.RecommendationResult;
import recommender.core.model.Pair;
import recommender.core.model.RecommenderType;
import recommender.core.util.RecommendationResultComparator;
import recommender.core.util.RecommenderUtil;
import recommender.impl.webservice.WebserviceRecommender;
/**
* Provides implementation of the log and administration methods which are equal for recommendations.
*
* @author lukas
*
* @param <E>
* @param <R>
*/
public abstract class DBLogConfigAccess<E, R extends RecommendationResult> implements DBLogic<E, R> {
// avoid too long titles
private static final int MAXIMUM_TITLE_LENGTH = 255;
private static final Log log = LogFactory.getLog(DBLogConfigAccess.class);
protected RecommenderDatabaseManager manager;
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#addQuery(java.lang.String, java.util.Date, recommender.core.interfaces.model.RecommendationEntity, java.lang.String, int)
*/
@Override
public Long addQuery(String userName, Date date, E entity, int timeout) {
// construct parameter
final RecQueryParam<E> recQuery = new RecQueryParam<E>();
recQuery.setTimeStamp(new Timestamp(date.getTime()));
recQuery.setUserName(userName);
recQuery.setEntity(entity);
recQuery.setContentType(entity.getClass().getName());
recQuery.setQueryTimeout(timeout);
final String simpleName = entity.getClass().getSimpleName();
// insert recommender query
final Long queryId = (Long) this.manager.processInsertQuery("add" + simpleName + "RecommenderQuery", recQuery);
this.storeRecommendationEntity(userName, queryId, entity, true);
return queryId;
}
/**
* stores a complete {@link ItemRecommendationEntity} in the log database
*
* @param userName username of user who queries recommendation
* @param qid queryid
* @param entity the entity to log
* @param update
* @param session an open session to the log database
*/
protected void storeRecommendationEntity(final String userName, final Long qid, final E entity, final boolean update) {
final EntityParam<E> param = new EntityParam<E>();
param.setRequestId(qid.intValue());
param.setEntity(entity);
param.setUserName(userName);
param.setType(entity.getClass().getCanonicalName());
final String simpleName = entity.getClass().getSimpleName();
this.manager.processInsertQuery("insertRecommendation" + simpleName + "Entity", param);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#addRecommenderToQuery(java.lang.Long, java.lang.Long)
*/
@Override
public void addRecommenderToQuery(Long qid, Long sid) {
// connect query with setting
final RecQuerySettingParam queryMap = new RecQuerySettingParam();
queryMap.setQid(qid);
queryMap.setSid(sid);
this.manager.processInsertQuery("addRecommenderQuerySetting", queryMap);
}
@Override
public boolean isRecommenderRegistered(final Recommender<E, R> recommender) {
return this.getRecommenderId(recommender).longValue() != -1;
}
/* (non-Javadoc)
* @see recommender.core.database.DBLogic#isRecommenderActive(recommender.core.Recommender)
*/
@Override
public boolean isRecommenderActive(Recommender<E, R> recommender) {
final RecSettingParam param = createParamForRecommender(recommender);
final Long settingsId = this.manager.processQueryForObject(Long.class, "getRecommenderStatus", param);
return settingsId != null;
}
/* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommenderId(recommender.core.Recommender)
*/
@Override
public Long getRecommenderId(Recommender<E, R> recommender) {
final RecSettingParam param = createParamForRecommender(recommender);
final Long recommenderId = this.manager.processQueryForObject(Long.class, "lookupRecommenderSetting", param);
return recommenderId != null ? recommenderId : Long.valueOf(-1);
}
private static <E, R extends RecommendationResult> RecSettingParam createParamForRecommender(final Recommender<E, R> recommender) {
final RecSettingParam param = new RecSettingParam();
param.setRecId(RecommenderUtil.getRecommenderId(recommender));
return param;
}
/* (non-Javadoc)
* @see recommender.core.database.DBLogic#registerRecommender(recommender.core.Recommender)
*/
@Override
public void registerRecommender(Recommender<E, R> recommender) {
final RecSettingParam param = createParamForRecommender(recommender);
param.setRecDescr(recommender.getInfo());
if (recommender instanceof WebserviceRecommender<?, ?>) {
final WebserviceRecommender<?, ?> webserviceRecommender = (WebserviceRecommender<?, ?>) recommender;
param.setTrusted(webserviceRecommender.isTrusted());
param.setType(RecommenderType.REMOTE.getId()); // TODO: use typehandler
} else {
param.setType(RecommenderType.LOCAL.getId()); // TODO: use typehandler
}
final Long settingId = (Long) this.manager.processInsertQuery("addRecommenderSetting", param);
param.setSetting_id(settingId.longValue());
this.manager.processInsertQuery("createStatusForRecommender", param);
}
/* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRemoteRecommenders()
*/
@SuppressWarnings("unchecked")
@Override
public List<RecommenderConnector<E, R>> getRemoteRecommenders() {
return (List<RecommenderConnector<E, R>>) this.manager.processQueryForList("getRemoteRecommenders", Integer.valueOf(RecommenderType.REMOTE.getId()));
}
/* (non-Javadoc)
* @see recommender.core.database.DBLogic#removeRecommender(recommender.core.Recommender)
*/
@Override
public boolean removeRecommender(Recommender<E, R> recommender) {
final String id = RecommenderUtil.getRecommenderId(recommender);
this.manager.processDeleteQuery("deleteRecommenderFromRecommenderStatus", id);
this.manager.processDeleteQuery("deleteFromRecommenderSettings", id);
return true;
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#setResultSelectorToQuery(java.lang.Long, java.lang.Long)
*/
@Override
public void setResultSelectorToQuery(Long qid, Long rid) {
// connect query with setting
final SelectorQueryMapParam queryMap = new SelectorQueryMapParam();
queryMap.setQid(qid);
queryMap.setSid(rid);
log.info("Storing selection for " + qid + " (" + rid + ")");
this.manager.processInsertQuery("addSelectorQuerySetting", queryMap);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#insertSelectorSetting(java.lang.String, byte[])
*/
@Override
public Long insertSelectorSetting(String selectorInfo, byte[] selectorMeta) {
Long selectorID = null;
final SelectorSettingParam setting = new SelectorSettingParam();
setting.setInfo(selectorInfo);
setting.setMeta(selectorMeta);
// TODO: merge the two queries using dynamic statements
// determine which lookup sql statement we have to use.
String lookupFunction;
if (selectorMeta == null) {
lookupFunction = "lookupSelectorSetting2";
} else {
lookupFunction = "lookupSelectorSetting";
}
selectorID = this.manager.processQueryForObject(Long.class, lookupFunction, setting);
if (selectorID == null) {
log.debug("Given setting not found -> adding new");
selectorID = (Long) this.manager.processInsertQuery("addSelectorSetting", setting);
log.debug("Setting added @" + selectorID);
} else {
log.debug("Given setting found in DB at " + selectorID);
}
return selectorID;
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#addSelectedRecommender(java.lang.Long, java.lang.Long)
*/
@Override
public void addSelectedRecommender(Long qid, Long sid) {
final RecQuerySettingParam queryMap = new RecQuerySettingParam();
queryMap.setQid(qid);
queryMap.setSid(sid);
// insert recommender settings
this.manager.processInsertQuery("addQuerySelection", queryMap);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#addRecommendation(java.lang.Long, java.lang.Long, java.util.SortedSet, long)
*/
@Override
public int addRecommendation(Long queryId, Long recommenderId, SortedSet<R> results, long latency) {
if (results == null) {
return 0;
}
final List<Pair<String, Object>> executionMap = new ArrayList<Pair<String,Object>>();
// insert recommender response
for (final R result : results) {
final ResultParam response = new ResultParam();
response.setQueryId(queryId);
response.setSettingId(recommenderId);
response.setRecLatency(latency);
response.setResultTitle(cropToLength(result.getTitle(), MAXIMUM_TITLE_LENGTH));
response.setResultId(result.getRecommendationId());
response.setConfidence(result.getConfidence());
response.setScore(result.getScore());
executionMap.add(new Pair<String, Object>("addRecommenderResponse", response));
}
return this.manager.processBatchOfInsertQueries(executionMap).intValue();
}
/**
* @param title
* @param maximumTitleLength
* @return
*/
private String cropToLength(String title, int maximumTitleLength) {
// TODO Auto-generated method stub
return ""; // FIXME (refactor) entity.getUserName()
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommendations(java.lang.Long, java.lang.Long)
*/
@Override
public SortedSet<R> getRecommendations(Long qid, Long sid) {
final SortedSet<R> result = new TreeSet<R>(new RecommendationResultComparator<R>());
this.getRecommendations(qid, sid, result);
return result;
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommendations(java.lang.Long, java.lang.Long, java.util.Collection)
*/
@Override
public abstract void getRecommendations(Long qid, Long sid,
Collection<R> recommendations);
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommendations(java.lang.Long)
*/
@Override
public SortedSet<R> getRecommendations(Long qid) {
final SortedSet<R> result = new TreeSet<R>(
new RecommendationResultComparator<R>());
this.getRecommendations(qid, result);
return result;
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommendations(java.lang.Long, java.util.Collection)
*/
@Override
public abstract void getRecommendations(Long qid,
Collection<R> recommendations);
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getSelectedResults(java.lang.Long)
*/
@Override
public abstract List<R> getSelectedResults(Long qid);
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getActiveRecommenderIDs(java.lang.Long)
*/
@Override
public List<Long> getActiveRecommenderIDs(Long qid) {
return this.manager.processQueryForList(Long.class, "getActiveRecommenderIDsForQuery", qid);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getAllRecommenderIDs(java.lang.Long)
*/
@Override
public List<Long> getAllRecommenderIDs(Long qid) {
return this.manager.processQueryForList(Long.class, "getAllRecommenderIDsForQuery", qid);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommenderSelectionCount(java.lang.Long)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List<Pair<Long, Long>> getRecommenderSelectionCount(Long qid) {
final List<Pair> selectionCounts = this.manager.processQueryForList(Pair.class, "getRecommenderSelectionCount", qid);
final List<Pair<Long, Long>> resultList = new ArrayList<Pair<Long,Long>>();
for (Pair selection : selectionCounts) {
resultList.add(selection);
}
return resultList;
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getQuery(java.lang.Long)
*/
@Override
public RecQueryParam getQuery(Long qid) {
return this.manager.processQueryForObject(RecQueryParam.class, "getQueryByID", qid);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getRecommenderAdminOverview(java.lang.String)
*/
@Override
public RecAdminOverview getRecommenderAdminOverview(String id) {
return this.manager.processQueryForObject(RecAdminOverview.class, "recAdminOverview", id);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getAverageLatencyForRecommender(java.lang.Long, java.lang.Long)
*/
@Override
public Long getAverageLatencyForRecommender(Long sid, Long numberOfQueries) {
if (numberOfQueries.longValue() <= 0) {
return null;
}
final LatencyParam param = new LatencyParam(sid, numberOfQueries);
return this.manager.processQueryForObject(Long.class, "getAverageRecommenderLatencyForSettingID", param);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#updateRecommenderstatus(java.util.List, java.util.List)
*/
@Override
public void updateRecommenderstatus(List<Long> activeRecs, List<Long> disabledRecs) {
if (activeRecs != null) {
for (final Long p : activeRecs) {
this.manager.processUpdateQuery("activateRecommender", p);
}
}
if (disabledRecs != null) {
for (final Long p : disabledRecs) {
this.manager.processUpdateQuery("deactivateRecommender", p);
}
}
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#storeRecommendation(java.lang.Long, java.lang.Long, java.util.Collection)
*/
@Override
public int storeRecommendation(Long qid, Long rid, Collection<R> results) {
// set store applied selection strategie's id
this.setResultSelectorToQuery(qid, rid);
final List<Pair<String, Object>> executionList = new ArrayList<Pair<String,Object>>();
// insert recommender response
for (final R result : results) {
final ResultParam response = new ResultParam();
response.setQueryId(qid);
response.setSettingId(rid);
response.setResultTitle(cropToLength(result.getTitle(), MAXIMUM_TITLE_LENGTH));
response.setConfidence(result.getConfidence());
response.setScore(result.getScore());
response.setResultId(result.getRecommendationId());
executionList.add(new Pair<String, Object>("addSelectedResult", response));
}
return this.manager.processBatchOfInsertQueries(executionList);
}
/*
* (non-Javadoc)
* @see recommender.core.database.DBLogic#getQueryForEntity(java.lang.String, java.util.Date, java.lang.String)
*/
@Override
public Long getQueryForEntity(String user_name, Date date, String entityID) {
final EntityParam<Object> entityParam = new EntityParam<Object>(); // TODO: generics
entityParam.setDate(date);
entityParam.setUserName(user_name);
entityParam.setEntityId(entityID);
log.debug("Looking up queryID for " + user_name + ", " + date + ", " + entityID);
return this.manager.processQueryForObject(Long.class, "getQueryForEntity", entityParam);
}
/*
* (non-Javadoc)
* @see recommender.impl.database.DBLogConfigAccess#addFeedback(recommender.core.interfaces.model.RecommendationEntity, recommender.core.interfaces.model.RecommendationResult)
*/
@Override
public void addFeedback(String userName, E entity, R result) {
final FeedbackParam<E> feedback = new FeedbackParam<E>();
feedback.setUserName(userName);
feedback.setDate(new Date());
final String resultId = (result.getRecommendationId() == null ? "" : result.getRecommendationId());
feedback.setEntity(entity);
feedback.setResultId(resultId);
// insert data
this.manager.processInsertQuery("insertRecommenderFeedback", feedback);
}
/**
* @param manager the manager to set
*/
public void setManager(RecommenderDatabaseManager manager) {
this.manager = manager;
}
}