/** * 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.privacytrust.trust.impl.engine; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.privacytrust.trust.TrustException; import org.societies.api.privacytrust.trust.evidence.TrustEvidenceType; import org.societies.api.privacytrust.trust.model.TrustValueType; import org.societies.api.privacytrust.trust.model.TrustedEntityId; import org.societies.api.privacytrust.trust.model.TrustedEntityType; import org.societies.privacytrust.trust.api.engine.IIndirectTrustEngine; import org.societies.privacytrust.trust.api.engine.TrustEngineException; import org.societies.privacytrust.trust.api.event.ITrustEventMgr; import org.societies.privacytrust.trust.api.event.ITrustEvidenceUpdateEventListener; import org.societies.privacytrust.trust.api.event.TrustEventTopic; import org.societies.privacytrust.trust.api.event.TrustEvidenceUpdateEvent; import org.societies.privacytrust.trust.api.evidence.model.ITrustEvidence; import org.societies.privacytrust.trust.api.model.ITrustedCss; import org.societies.privacytrust.trust.api.model.ITrustedEntity; import org.societies.privacytrust.trust.api.similarity.ITrustSimilarityEvaluator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author <a href="mailto:nicolas.liampotis@cn.ntua.gr">Nicolas Liampotis</a> (ICCS) * @since 0.0.8 */ @Service public class IndirectTrustEngine extends TrustEngine implements IIndirectTrustEngine { /** The logging facility. */ private static final Logger LOG = LoggerFactory.getLogger(IndirectTrustEngine.class); @Autowired(required=true) private ITrustSimilarityEvaluator trustSimilarityEvaluator; @Autowired(required=true) IndirectTrustEngine(ITrustEventMgr trustEventMgr) throws Exception { super(trustEventMgr); LOG.info("{} instantiated", this.getClass()); try { LOG.info("Registering for indirect trust evidence updates..."); super.trustEventMgr.registerEvidenceUpdateListener( new IndirectTrustEvidenceUpdateListener(), new String[] { TrustEventTopic.TRUST_EVIDENCE_UPDATED }); } catch (Exception e) { LOG.error(this.getClass() + " could not be initialised: " + e.getLocalizedMessage(), e); throw e; } } /* * @see org.societies.privacytrust.trust.api.engine.IIndirectTrustEngine#evaluate(org.societies.api.privacytrust.trust.model.TrustedEntityId, org.societies.privacytrust.trust.api.evidence.model.ITrustEvidence) */ @Override public Set<ITrustedEntity> evaluate(final TrustedEntityId trustorId, final ITrustEvidence evidence) throws TrustEngineException { if (trustorId == null) { throw new NullPointerException("trustorId can't be null"); } if (evidence == null) { throw new NullPointerException("evidence can't be null"); } LOG.debug("evaluate: trustorId={}, evidence={}", trustorId, evidence); final Set<ITrustedEntity> resultSet = new HashSet<ITrustedEntity>(); if (!this.areRelevant(trustorId, evidence)) { return resultSet; } try { // Does similarity between trustor and subject needs re-evaluation? boolean doSimilarityEval = false; // Create the trusted entity the evidence object refers to if not already available ITrustedEntity trustee = (ITrustedEntity) this.trustRepo.retrieveEntity( trustorId, evidence.getObjectId()); if (trustee == null) { trustee = super.trustRepo.createEntity(trustorId, evidence.getObjectId()); } else { doSimilarityEval = true; } LOG.debug("evaluate: doSimilarity={}", doSimilarityEval); resultSet.add(trustee); switch (evidence.getType()) { // Update value case DIRECTLY_TRUSTED: // Check if similarity between trustor and subject needs re-evaluation if (doSimilarityEval && TrustedEntityType.CSS == evidence.getSubjectId().getEntityType()) { final ITrustedCss subject = (ITrustedCss) super.createEntityIfAbsent(trustorId, evidence.getSubjectId()); final Double similarity = this.trustSimilarityEvaluator .evaluateCosineSimilarity(trustorId, evidence.getSubjectId()); LOG.debug("evaluate: similarity={}", similarity); if (similarity != null && !Double.isNaN(similarity)) { subject.setSimilarity(similarity); super.trustRepo.updateEntity(subject); } } // Fetch top N users final Map<TrustedEntityId, ITrustedCss> topNCssMap = this.retrieveTopNCss(trustorId); LOG.debug("evaluate: topNCssMap={}", topNCssMap); double weightedOpinionSum = 0d; double weightSum = 0d; // Retrieve all Indirect Trust Evidence related to the object // referenced in the specified TrustEvidence final Set<ITrustEvidence> evidenceSet = super.trustEvidenceRepo .retrieveLatestEvidence(null, evidence.getObjectId(), evidence.getType(), null); LOG.debug("evaluate: evidenceSet={}", evidenceSet); for (final ITrustEvidence relatedEvidence : evidenceSet) { if (!(relatedEvidence.getInfo() instanceof Double)) { LOG.warn("Related evidence " + relatedEvidence + " has no trust value!"); continue; } final ITrustedCss opinionSource = topNCssMap.get(relatedEvidence.getSubjectId()); if (opinionSource == null) { LOG.warn("Could not find CSS trust relationship with related evidence subject '" + relatedEvidence.getSubjectId() + "'"); continue; } final Double weight = evaluateWeight(opinionSource); Double weightedOpinion = null; if (weight != null) { final double meanOpinion = this.retrieveMeanTrustOpinion( relatedEvidence.getSubjectId()); weightedOpinion = weight * ((Double) relatedEvidence.getInfo() - meanOpinion); LOG.debug("evaluate: subjectId={}, meanOpinion={}, weightedOpinion={}", new Object[] { relatedEvidence.getSubjectId(), meanOpinion, weightedOpinion }); } if (weightedOpinion == null) { LOG.warn("Ignoring related evidence " + relatedEvidence + ": Weighted opinion is null"); continue; } weightedOpinionSum += weightedOpinion; weightSum += Math.abs(weight); } LOG.debug("evaluate: weightedOpinionSum={}, weightSum={}", weightedOpinionSum, weightSum); // t_x,i = avg(t_x) + weighted opinions double value = super.trustRepo.retrieveMeanTrustValue( trustorId, TrustValueType.DIRECT, null); final double confidence; if (weightSum > 0) { value += weightedOpinionSum / weightSum; confidence = 0.5d; // TODO constant or what? } else { confidence = 0.25d; // TODO constant or what? } LOG.debug("evaluate: value={}", value); if (value > 1) { value = 1.0d; // TODO use constant } else if (value < 0) { value = 0.0d; // TODO use constant } trustee.getIndirectTrust().setValue(value); trustee.getIndirectTrust().setConfidence(confidence); break; default: throw new TrustEngineException("Unsupported type: " + evidence.getType()); } // Add related evidence to trustee trustee.addEvidence(evidence); // Persist updated TrustedEntities in the Trust Repository for (final ITrustedEntity entity : resultSet) { super.trustRepo.updateEntity(entity); } } catch (Exception e) { throw new TrustEngineException("Could not evaluate indirect trust evidence " + evidence + ": " + e.getLocalizedMessage(), e); } return resultSet; } /* * @see org.societies.privacytrust.trust.api.engine.IIndirectTrustEngine#evaluate(org.societies.api.privacytrust.trust.model.TrustedEntityId, java.util.Set) */ @Override public Set<ITrustedEntity> evaluate(final TrustedEntityId trustorId, final Set<ITrustEvidence> evidenceSet) throws TrustEngineException { if (trustorId == null) { throw new NullPointerException("trustorId can't be null"); } if (evidenceSet == null) { throw new NullPointerException("evidenceSet can't be null"); } LOG.debug("evaluate: trustorId={}, evidenceSet={}", trustorId, evidenceSet); final Set<ITrustedEntity> resultSet = new HashSet<ITrustedEntity>(); // create sorted evidence set based on the evidence timestamps final SortedSet<ITrustEvidence> sortedEvidenceSet = new TreeSet<ITrustEvidence>(evidenceSet); LOG.debug("evaluate: sortedEvidenceSet={}", sortedEvidenceSet); for (final ITrustEvidence evidence : sortedEvidenceSet) { final Set<ITrustedEntity> newResultSet = this.evaluate(trustorId, evidence); resultSet.removeAll(newResultSet); resultSet.addAll(newResultSet); } return resultSet; } /** * Checks if the specified piece of evidence is relevant for the supplied * trustor. More specifically, a piece of evidence is relevant for indirect * trust evaluation if: * <ol> * <li>type == {@link TrustEvidenceType#DIRECTLY_TRUSTED DIRECTLY_TRUSTED}</li> * <li>trustorId != evidence.subjectId, i.e. ignore trust opinions * originating <i>from</i> the trustor</li> * <li>trustorId != evidence.objectId, i.e. ignore trust opinions * <i>about</i> the trustor</li> * <li>evidence.subjectId != evidence.objectId, i.e. ignore * self-referencing trust opinions</li> * </ol> * * @param trustorId * @param evidence * @return <code>true</code> if the specified piece of evidence is relevant * for the supplied trustor; <code>false</code> otherwise. */ private boolean areRelevant(final TrustedEntityId trustorId, final ITrustEvidence evidence) { boolean result = false; if (TrustEvidenceType.DIRECTLY_TRUSTED == evidence.getType() && !trustorId.equals(evidence.getSubjectId()) && !trustorId.equals(evidence.getObjectId()) && !evidence.getSubjectId().equals(evidence.getObjectId())) { result = true; } LOG.debug("areRelevant: trustorId={}, evidence={}, result={}", new Object[] { trustorId, evidence, result }); return result; } /* * Map<K,V> = Map<TRUSTEE_TEID,TRUSTEE> */ private Map<TrustedEntityId, ITrustedCss> retrieveTopNCss( final TrustedEntityId trustorId) throws TrustException { final Map<TrustedEntityId, ITrustedCss> result = new HashMap<TrustedEntityId, ITrustedCss>(); final Set<ITrustedCss> cssSet = super.trustRepo.retrieveCssBySimilarity(trustorId, null, null); for (final ITrustedCss css : cssSet) { result.put(css.getTrusteeId(), css); } return result; } /* * Returns the mean of the trust opinions of the specified trustee. */ private double retrieveMeanTrustOpinion( final TrustedEntityId trusteeId) throws TrustException { final Set<ITrustEvidence> evidenceSet = super.trustEvidenceRepo.retrieveLatestEvidence( trusteeId, null, TrustEvidenceType.DIRECTLY_TRUSTED, null); double sum = 0.0d; int count = 0; for (final ITrustEvidence evidence : evidenceSet) { if (evidence.getInfo() instanceof Double) { sum += (Double) evidence.getInfo(); count++; } } return (count > 0) ? sum/count : 0.0d; } private static Double evaluateWeight(final ITrustedCss opinionSource) { if (opinionSource.getDirectTrust().getValue() == null || opinionSource.getSimilarity() == null) { return null; // TODO or 0.0d??? } return (opinionSource.getDirectTrust().getValue() * opinionSource.getSimilarity()); } private class IndirectTrustEvidenceHandler implements Runnable { private final ITrustEvidence evidence; private IndirectTrustEvidenceHandler(final ITrustEvidence evidence) { this.evidence = evidence; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.debug("Handling evidence {}", this.evidence); try { for (final TrustedEntityId myId : IndirectTrustEngine.super.trustNodeMgr.getMyIds()) { evaluate(myId, this.evidence); } } catch (TrustException te) { LOG.error("Could not handle evidence " + evidence + ": " + te.getLocalizedMessage(), te); } } } private class IndirectTrustEvidenceUpdateListener implements ITrustEvidenceUpdateEventListener { /* * @see org.societies.privacytrust.trust.api.event.ITrustEvidenceUpdateEventListener#onNew(org.societies.privacytrust.trust.api.event.TrustEvidenceUpdateEvent) */ @Override public void onNew(TrustEvidenceUpdateEvent evt) { LOG.debug("Received TrustEvidenceUpdateEvent {}", evt); if (!(evt.getSource() instanceof ITrustEvidence)) { LOG.error("TrustEvidenceUpdateEvent source is not instance of ITrustEvidence"); return; } final ITrustEvidence evidence = (ITrustEvidence) evt.getSource(); executorService.execute(new IndirectTrustEvidenceHandler(evidence)); } } }