/**
* Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET
* (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije
* informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE
* COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp.,
* INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM
* ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC))
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.societies.personalisation.UserPreferenceManagement.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.societies.api.context.model.CtxAttribute;
import org.societies.api.context.model.CtxIdentifier;
import org.societies.api.identity.IIdentity;
import org.societies.api.internal.context.broker.ICtxBroker;
import org.societies.api.internal.personalisation.model.IOutcome;
import org.societies.api.internal.personalisation.model.PreferenceDetails;
import org.societies.api.internal.servicelifecycle.ServiceModelUtils;
import org.societies.api.personalisation.model.IActionConsumer;
import org.societies.api.personalisation.model.PersonalisablePreferenceIdentifier;
import org.societies.api.schema.servicelifecycle.model.ServiceResourceIdentifier;
import org.societies.personalisation.UserPreferenceManagement.impl.evaluation.PreferenceConditionExtractor;
import org.societies.personalisation.UserPreferenceManagement.impl.evaluation.PreferenceEvaluator;
import org.societies.personalisation.UserPreferenceManagement.impl.evaluation.PrivateContextCache;
import org.societies.personalisation.UserPreferenceManagement.impl.management.PrivatePreferenceCache;
import org.societies.personalisation.preference.api.IUserPreferenceManagement;
import org.societies.personalisation.preference.api.model.*;
import org.springframework.scheduling.annotation.AsyncResult;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.*;
import java.util.concurrent.Future;
public class UserPreferenceManagement implements IUserPreferenceManagement {
private Logger logging = LoggerFactory.getLogger(this.getClass());
private PrivateContextCache contextCache;
private PrivatePreferenceCache preferenceCache;
private Hashtable<IPreferenceOutcome, List<CtxIdentifier>> outcomeConditionListTable;
private ICtxBroker ctxBroker;
//private IIdentity userId;
// Personalisable preferences from registered services
private final Map<IActionConsumer, Set<PersonalisablePreferenceIdentifier>> actionConsumerPreferenceMap = new HashMap<IActionConsumer, Set<PersonalisablePreferenceIdentifier>>();
private final Set<PersonalisablePreferenceIdentifier> aggregateActionConsumerPreferences = new HashSet<PersonalisablePreferenceIdentifier>();
public UserPreferenceManagement(ICtxBroker broker) {
this.ctxBroker = broker;
this.contextCache = new PrivateContextCache(this.ctxBroker);
this.preferenceCache = new PrivatePreferenceCache(this.ctxBroker);
outcomeConditionListTable = new Hashtable<IPreferenceOutcome, List<CtxIdentifier>>();
}
/*
* Get the instance of the context cache held under the preference manager
*
*/
public PrivateContextCache getPrivateContextCache() {
return this.contextCache;
}
/*
* get the instance of the preference cache held under the preference manager
*
*/
public PrivatePreferenceCache getPrivatePreferenceCache() {
return this.preferenceCache;
}
public void removePreference(IIdentity ownerID, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
this.preferenceCache.deletePreference(ownerID, serviceType, serviceID, preferenceName);
}
public IPreferenceOutcome getPreference(IIdentity ownerID, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
if (serviceID == null) {
logging.debug("Request for preference with null serviceID, returning empty Action");
return null;
}
if (preferenceName == null) {
logging.debug("Request for preference with null preferenceName, returning empty Action");
return null;
}
IPreferenceTreeModel model = this.preferenceCache.getPreference(serviceType, serviceID, preferenceName);
if (null != model) {
IPreference p = model.getRootPreference();
PreferenceEvaluator pEvaluator = new PreferenceEvaluator(this.contextCache);
Hashtable<IPreferenceOutcome, List<CtxIdentifier>> evaluationResult = pEvaluator.evaluatePreference(p);
Enumeration<IPreferenceOutcome> e = evaluationResult.keys();
IPreferenceOutcome action = null;
if (e.hasMoreElements()) {
action = e.nextElement();
action.setServiceID(serviceID);
action.setServiceType(serviceType);
logging.debug("evaluated preference " + preferenceName + " of " + serviceType + ":" + ServiceModelUtils.serviceResourceIdentifierToString(serviceID) + "\nand returning value: " + action.getvalue());
return action;
} else {
logging.debug("evaluated preference " + preferenceName + " of " + serviceType + ":" + ServiceModelUtils.serviceResourceIdentifierToString(serviceID) + "\n did not yield any actions, returning empty action");
}
}
logging.debug("No preference available for: " + preferenceName + " of " + serviceType + ":" + ServiceModelUtils.serviceResourceIdentifierToString(serviceID));
return null;
}
public List<IPreferenceConditionIOutcomeName> getPreferenceConditions(IIdentity ownerID, String serviceType, ServiceResourceIdentifier serviceID) {
logging.debug("extracting conditions for all preferences of : " + serviceType + ":" + ServiceModelUtils.serviceResourceIdentifierToString(serviceID));
List<IPreferenceConditionIOutcomeName> list = new ArrayList<IPreferenceConditionIOutcomeName>();
List<String> prefnames = this.preferenceCache.getPreferenceNamesofService(serviceType, serviceID);
PreferenceConditionExtractor pce = new PreferenceConditionExtractor();
for (int i = 0; i < prefnames.size(); i++) {
logging.debug("extracting conditions for: " + prefnames.get(i));
IPreferenceTreeModel model = this.preferenceCache.getPreference(serviceType, serviceID, prefnames.get(i));
if (null != model) {
logging.debug("got preference " + prefnames.get(i) + " from cache");
List<IPreferenceConditionIOutcomeName> tempList = pce.extractConditions(model);
if (null != tempList) {
logging.debug("found conditions: ");
for (int k = 0; k < tempList.size(); k++) {
logging.debug("condition: " + tempList.get(k).getICtxIdentifier().getType());
}
list.addAll(tempList);
} else {
logging.debug("not found any conditions, preference must be context-independent");
}
} else {
logging.debug("not found any preference " + prefnames.get(i));
}
}
logging.debug("found " + list.size() + " entries");
return list;
}
public IPreferenceOutcome reEvaluatePreferences(IIdentity ownerID, CtxAttribute attribute, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
logging.debug("New context event received, requested re-evaluation of preference: ");
logging.debug(preferenceName + "" + serviceType + ":" + ServiceModelUtils.serviceResourceIdentifierToString(serviceID));
this.contextCache.updateCache(attribute);
IPreferenceTreeModel model = this.preferenceCache.getPreference(serviceType, serviceID, preferenceName);
if (model != null) {
PreferenceEvaluator pEvaluator = new PreferenceEvaluator(this.contextCache);
Hashtable<IPreferenceOutcome, List<CtxIdentifier>> evaluationResult = pEvaluator.evaluatePreference(model.getRootPreference());
Enumeration<IPreferenceOutcome> e = evaluationResult.keys();
IPreferenceOutcome o = null;
if (e.hasMoreElements()) {
o = e.nextElement();
o.setServiceID(serviceID);
o.setServiceType(serviceType);
this.outcomeConditionListTable.put(o, evaluationResult.get(o));
logging.debug("returning new Outcome to PCM: " + o.getparameterName() + " -> " + o.getvalue());
} else {
logging.debug("no new outcome for PCM, returning empty Action");
}
return o;
} else {
logging.debug("Preference not found in cache");
}
return null;
}
/*
public void updatePreference(IIdentity ownerID, IPreference preference, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
log("Request to update preference: "+preferenceName+" of "+serviceType+":"+serviceID.toString());
PreferenceRetriever retr = new PreferenceRetriever(this.myContext);
IPreferenceTreeModel iptm = retr.retrievePreference(serviceID, serviceType, preferenceName);
if (null==iptm){
log("No existing preference, try to store it directly to context mgmt");
this.storePreference(ownerID, preference, serviceType, serviceID, preferenceName);
}else{
log("existing preference exists, merging will start now");
PreferenceMerger merger = new PreferenceMerger();
IPreference mergedPreference = merger.mergeTrees(iptm.getRootPreference(), preference, "");
if (null!=mergedPreference){
PreferenceStorer storer = new PreferenceStorer(this.myContext, this.registryManager);
IPreferenceTreeModel newModel = new PreferenceTreeModel(mergedPreference);
newModel.setPreferenceName(preferenceName);
newModel.setServiceID(serviceID);
newModel.setServiceType(serviceType);
log("storing merged preference in context");
storer.storeExisting(newModel);
}
}
}*/
private void calculateSizeOfObject(IPreference p) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos;
try {
oos = new ObjectOutputStream(bos);
oos.writeObject(p);
oos.flush();
oos.close();
bos.close();
this.logging.debug("Trying to store preference of size: " + bos.size());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private byte[] getBytes(Object obj) throws java.io.IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
oos.close();
bos.close();
byte[] data = bos.toByteArray();
return data;
}
public boolean storePreference(IIdentity ownerID, PreferenceDetails details, IPreference preference) {
logging.debug("request to store preference: for " + details.toString() + "\nPreference:\n" + preference.toTreeString());
IPreferenceTreeModel model = new PreferenceTreeModel(details, preference);
return this.preferenceCache.storePreference(ownerID, details, model);
//this.calculateSizeOfObject(preference);
}
public IPreferenceOutcome getPreference(IIdentity requestor, IIdentity owner, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
// TODO: Need to use the AccessControl to request permission to personalise service
return this.getPreference(requestor, serviceType, serviceID, preferenceName);
/*IPreferenceTreeModel model = this.preferenceCache.getPreference(serviceType, serviceID, preferenceName);
PrefEvaluator pEvaluator = new PrefEvaluator(this.contextCache);
if (model!=null){
Hashtable<IPreferenceOutcome, List<ICtxIdentifier>> evaluationResult = pEvaluator.evaluatePreference(model.getRootPreference());
Enumeration<IPreferenceOutcome> e = evaluationResult.keys();
IPreferenceOutcome o = null;
while (e.hasMoreElements()){
o = (IPreferenceOutcome) e.nextElement();
o.setServiceID(serviceID);
o.setServiceType(serviceType);
}
return o;
}else{
logging.debug("Preference "+preferenceName+" not found in cache");
return null;
}*/
}
public List<IPreferenceOutcome> reEvaluatePreferences(IIdentity dpi, CtxAttribute attr, List<PreferenceDetails> preferenceIdentifiers) {
logging.debug("New context event received, requested re-evaluation of preference ");
List<IPreferenceOutcome> list = new ArrayList<IPreferenceOutcome>();
this.contextCache.updateCache(attr);
logging.debug("updated my context cache");
for (int i = 0; i < preferenceIdentifiers.size(); i++) {
PreferenceDetails details = preferenceIdentifiers.get(i);
logging.debug("getting preference: " + details.getServiceType() + ":" + details.getServiceID().toString() + ":" + details.getPreferenceName() + " from my preference cache");
IPreferenceTreeModel model = this.preferenceCache.getPreference(details);
if (model != null) {
logging.debug("got valid preference from my preference cache. attempting to evaluate it");
}
IPreference preference = model.getRootPreference();
if (null == preference) {
logging.debug("Preference object inside PreferenceTreeModel is null");
} else {
PreferenceEvaluator pEvaluator = new PreferenceEvaluator(this.contextCache);
Hashtable<IPreferenceOutcome, List<CtxIdentifier>> evaluationResult = pEvaluator.evaluatePreference(preference);
Enumeration<IPreferenceOutcome> e = evaluationResult.keys();
IPreferenceOutcome o = null;
while (e.hasMoreElements()) {
o = e.nextElement();
o.setServiceID(model.getPreferenceDetails().getServiceID());
o.setServiceType(model.getPreferenceDetails().getServiceType());
this.outcomeConditionListTable.put(o, evaluationResult.get(o));
}
if (null != o) {
logging.debug("Evaluation result: " + o.getparameterName() + " -> " + o.getvalue());
list.add(o);
} else {
logging.debug("Evaluation result for " + details.getServiceType() + ":" + details.getServiceID().toString() + ":" + details.getPreferenceName() + " is NULL");
}
}
}
return list;
}
/* (non-Javadoc)
* @see org.personalsmartspace.pm.prefmgr.api.platform.IPreferenceHandler#getConditions(org.personalsmartspace.sre.api.pss3p.IIdentity, org.personalsmartspace.pm.prefmodel.api.platform.IPreferenceOutcome)
*/
public List<CtxIdentifier> getConditions(IIdentity dpi,
IPreferenceOutcome outcome) {
return this.outcomeConditionListTable.get(outcome);
}
//TODO: use PrivatePreferenceCache. not PreferenceRetriever
public IPreferenceTreeModel getModel(IIdentity ownerID, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
return this.preferenceCache.getPreference(serviceType, serviceID, preferenceName);
}
public List<CtxIdentifier> getPreferenceConditions(IIdentity ownerID, String serviceType, ServiceResourceIdentifier serviceID, String preferenceName) {
PreferenceConditionExtractor pce = new PreferenceConditionExtractor();
IPreferenceTreeModel model = this.preferenceCache.getPreference(serviceType, serviceID, preferenceName);
if (model == null) {
this.logging.debug("Preference for " + new Tools(this.ctxBroker).convertToKey(serviceType, ServiceModelUtils.serviceResourceIdentifierToString(serviceID), preferenceName) + " doesn't exist");
return new ArrayList<CtxIdentifier>();
}
List<IPreferenceConditionIOutcomeName> list = pce.extractConditions(model);
List<CtxIdentifier> ctxIDs = new ArrayList<CtxIdentifier>();
for (IPreferenceConditionIOutcomeName obj : list) {
ctxIDs.add(obj.getICtxIdentifier());
}
return ctxIDs;
}
@Override
public boolean deletePreference(IIdentity ownerID, PreferenceDetails details) {
return this.preferenceCache.deletePreference(ownerID, details);
}
/*
* (non-Javadoc)
* @see org.societies.personalisation.preference.api.IUserPreferenceManagement#getPreferenceDetailsForAllPreferences()
*/
@Override
public List<PreferenceDetails> getPreferenceDetailsForAllPreferences() {
return this.preferenceCache.getPreferenceDetailsForAllPreferences();
}
/*
* (non-Javadoc)
* @see org.societies.personalisation.preference.api.IUserPreferenceManagement#getModel(org.societies.api.identity.IIdentity, org.societies.api.internal.personalisation.model.PreferenceDetails)
*/
@Override
public IPreferenceTreeModel getModel(IIdentity ownerDPI,
PreferenceDetails details) {
return this.preferenceCache.getPreference(details);
}
public void emptyContextCache() {
this.contextCache = new PrivateContextCache(ctxBroker);
}
public void updateContext(CtxAttribute attribute) {
this.contextCache.updateCache(attribute);
}
/*
* (non-Javadoc)
* @see org.societies.api.internal.personalisation.preference.IUserPreferenceManagement#getOutcome(org.societies.api.identity.IIdentity, org.societies.api.schema.servicelifecycle.model.ServiceResourceIdentifier, java.lang.String)
*/
@Override
public Future<IOutcome> getOutcome(IIdentity ownerId,
ServiceResourceIdentifier serviceId, String preferenceName) {
return new AsyncResult<IOutcome>(this.getPreference(ownerId, "", serviceId, preferenceName));
}
@Override
public void registerPersonalisableService(IActionConsumer actionConsumer, PersonalisablePreferenceIdentifier preference) {
if (actionConsumer == null)
throw new IllegalArgumentException("actionConsumer cannot be null");
if (preference == null)
throw new IllegalArgumentException("preference cannot be null");
// add to actionConsumerPreferenceMap and aggregateActionConsumerPreferences
Set<PersonalisablePreferenceIdentifier> consumerSet;
if (actionConsumerPreferenceMap.containsKey(actionConsumer))
consumerSet = actionConsumerPreferenceMap.get(actionConsumer);
else {
consumerSet = new HashSet<PersonalisablePreferenceIdentifier>();
actionConsumerPreferenceMap.put(actionConsumer, consumerSet);
}
aggregateActionConsumerPreferences.add(preference);
consumerSet.add(preference);
}
@Override
public List<PersonalisablePreferenceIdentifier> getKnownPersonalisablePreferences() {
// clone the list so it can't be messed with
return new ArrayList<PersonalisablePreferenceIdentifier>(aggregateActionConsumerPreferences);
}
}