/** * 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.evidence.monitor; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.internal.privacytrust.trust.ITrustBroker; import org.societies.api.internal.privacytrust.trust.evidence.ITrustEvidenceCollector; import org.societies.api.privacytrust.trust.TrustException; import org.societies.api.privacytrust.trust.TrustQuery; import org.societies.api.privacytrust.trust.event.ITrustUpdateEventListener; import org.societies.api.privacytrust.trust.event.TrustUpdateEvent; import org.societies.api.privacytrust.trust.evidence.TrustEvidenceType; import org.societies.api.privacytrust.trust.model.TrustRelationship; 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.ITrustNodeMgr; import org.societies.privacytrust.trust.api.evidence.model.ITrustEvidence; import org.societies.privacytrust.trust.api.evidence.repo.ITrustEvidenceRepository; import org.societies.privacytrust.trust.api.evidence.repo.TrustEvidenceRepositoryException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * This class is used to acquire trust evidence based on the trust opinions of * the CSSs this CSS is directly connected to. More specifically, it adds * {@link IndirectTrustEvidence} to the Trust Evidence Repository by monitoring * {@link TrustValueType#DIRECT direct} trust value updates of other CSSs. * <p> * The generated pieces of Indirect Trust Evidence are then processed by the * Direct Trust Engine in order to (re)evaluate the indirect trust in the * referenced entities, i.e. CSS, CISs or services, on behalf of the CSS owner. * * @author <a href="mailto:nicolas.liampotis@cn.ntua.gr">Nicolas Liampotis</a> (ICCS) * @since 1.0 */ @Service public class IndirectTrustEvidenceMonitor implements ITrustUpdateEventListener { private static final Logger LOG = LoggerFactory.getLogger(IndirectTrustEvidenceMonitor.class); /** The time to wait between registration attempts for DIRECT trust updates (in seconds) */ private static final long WAIT = 60l; @Autowired(required=true) private ITrustEvidenceCollector trustEvidenceCollector; @Autowired(required=true) private ITrustEvidenceRepository trustEvidenceRepository; private ITrustBroker trustBroker; private ITrustNodeMgr trustNodeMgr; /** The connections registered for DIRECT trust updates. */ private final Set<TrustedEntityId> monitoredConnections = new CopyOnWriteArraySet<TrustedEntityId>(); /** The connections to register for DIRECT trust updates. */ private final Set<TrustedEntityId> unmonitoredConnections = new CopyOnWriteArraySet<TrustedEntityId>(); /** The executor service. */ private ExecutorService executorService = Executors.newSingleThreadExecutor(); /** The scheduled executor service. */ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); @Autowired IndirectTrustEvidenceMonitor(ITrustNodeMgr trustNodeMgr, ITrustBroker trustBroker) throws Exception { LOG.info("{} instantiated", this.getClass()); this.trustNodeMgr = trustNodeMgr; this.trustBroker = trustBroker; try { for (final TrustedEntityId myTeid : trustNodeMgr.getMyIds()) { this.retrieveConnections(myTeid); trustBroker.registerTrustUpdateListener(this, new TrustQuery(myTeid).setTrustValueType(TrustValueType.DIRECT)); } this.scheduler.scheduleWithFixedDelay(new MaintenanceDaemon(), WAIT, WAIT, TimeUnit.SECONDS); } catch (Exception e) { LOG.error("Failed to initialise " + this.getClass() + ": " + e.getLocalizedMessage(), e); throw e; } } /* * @see org.societies.api.privacytrust.trust.event.ITrustUpdateEventListener#onUpdate(org.societies.api.privacytrust.trust.event.TrustUpdateEvent) */ @Override public void onUpdate(TrustUpdateEvent event) { LOG.debug("Received event {}", event); if (event.getTrustRelationship() == null) { LOG.error("Could not handle DIRECT trust update event: " + " TrustRelationship can't be null"); return; } if (TrustValueType.DIRECT != event.getTrustRelationship().getTrustValueType()) { LOG.error("Could not handle trust update event: " + " Unexpected TrustRelationship trust value type: " + event.getTrustRelationship().getTrustValueType()); return; } this.executorService.submit(new DirectTrustUpdateHandler(event.getTrustRelationship())); } private class DirectTrustUpdateHandler implements Runnable { private final TrustRelationship updatedRelationship; private DirectTrustUpdateHandler(final TrustRelationship updatedRelationship) { this.updatedRelationship = updatedRelationship; } /* * @see java.lang.Runnable#run() */ @Override public void run() { final TrustedEntityId trustorId = this.updatedRelationship.getTrustorId(); final TrustedEntityId trusteeId = this.updatedRelationship.getTrusteeId(); // IF the trustor is mine THEN // If the trustee is: // 1. of type CSS, AND // 2. not me, AND // 3. not already monitored connection THEN // Add trustee to connections to be monitored // ELSE // Add indirect trust evidence if (IndirectTrustEvidenceMonitor.this.trustNodeMgr.getMyIds().contains(trustorId)) { if (TrustedEntityType.CSS != trusteeId.getEntityType() || trustorId.equals(trusteeId)) { LOG.debug("Nothing to do - '{}' cannot be monitored", trusteeId); return; } if (IndirectTrustEvidenceMonitor.this.monitoredConnections.contains(trusteeId)) { LOG.debug("Nothing to do - '{}' already monitored", trusteeId); return; } else { LOG.debug("Adding '{}' to connections to be monitored", trusteeId); IndirectTrustEvidenceMonitor.this.unmonitoredConnections.add(trusteeId); } } else { try { IndirectTrustEvidenceMonitor.this.addIndirectEvidence( this.updatedRelationship, trustorId); } catch (TrustException te) { LOG.error("Could not add indirect trust evidence from relationship " + this.updatedRelationship + ": " + te.getLocalizedMessage(), te); } } } } /** * Runs periodically in the background to perform various maintenance * tasks. */ private class MaintenanceDaemon implements Runnable { /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.debug("Connections to monitor for direct trust updates: {}", IndirectTrustEvidenceMonitor.this.unmonitoredConnections); for (final TrustedEntityId connectionId : new HashSet<TrustedEntityId>( IndirectTrustEvidenceMonitor.this.unmonitoredConnections)) { try { IndirectTrustEvidenceMonitor.this.retrieveOpinions(connectionId); IndirectTrustEvidenceMonitor.this.trustBroker.registerTrustUpdateListener( IndirectTrustEvidenceMonitor.this, new TrustQuery(connectionId).setTrustValueType(TrustValueType.DIRECT)); if (IndirectTrustEvidenceMonitor.this.unmonitoredConnections.remove(connectionId)) { LOG.info("Started monitoring connection '{}' for direct trust updates", connectionId); IndirectTrustEvidenceMonitor.this.monitoredConnections.add(connectionId); } } catch (Exception e) { LOG.warn("Failed to register for trust updates of CSS '" + connectionId + "': " + e.getLocalizedMessage() + ". Will re-attempt to register in " + WAIT + " seconds..."); } } // for each connectionId ends } } private void addIndirectEvidence(final TrustRelationship trustRelationship, final TrustedEntityId sourceId) throws TrustException { final TrustedEntityId subjectId = trustRelationship.getTrustorId(); final TrustedEntityId objectId = trustRelationship.getTrusteeId(); final TrustEvidenceType type = TrustEvidenceType.DIRECTLY_TRUSTED; final Double trustValue = trustRelationship.getTrustValue(); final Date ts = trustRelationship.getTimestamp(); // Ignore evidence where subjectId == objectId if (!subjectId.equals(objectId)) { LOG.debug("Adding indirect trust evidence: subjectId={}, objectId={}" + ", type={}, ts={}, trustValue={}, sourceId={}", new Object[] { subjectId, objectId, type, ts, trustValue, sourceId }); this.trustEvidenceCollector.addIndirectEvidence(subjectId, objectId, type, ts, trustValue, sourceId); } else { LOG.debug("Ignoring indirect trust evidence: subjectId={}, objectId={}" + ", type={}, ts={}, trustValue={}, sourceId={}", new Object[] { subjectId, objectId, type, ts, trustValue, sourceId }); } } private void retrieveOpinions(final TrustedEntityId connectionId) throws TrustException { LOG.debug("retrieveOpinions: connectionId={}", connectionId); final Set<TrustRelationship> retrievedRelationships; final Set<ITrustEvidence> existingEvidenceSet; try { existingEvidenceSet = this.trustEvidenceRepository.retrieveLatestEvidence( connectionId, null, TrustEvidenceType.DIRECTLY_TRUSTED, connectionId); // Get the last evidence timestamp Date lastEvidenceTimestamp = null; final Iterator<ITrustEvidence> evidenceIter = existingEvidenceSet.iterator(); while (evidenceIter.hasNext()) { lastEvidenceTimestamp = evidenceIter.next().getTimestamp(); } // Fetch trust relationships after lastEvidenceTimestamp // TODO Add fromDate param to TrustQuery retrievedRelationships = this.trustBroker.retrieveTrustRelationships( new TrustQuery(connectionId).setTrustValueType(TrustValueType.DIRECT)).get(); for (final TrustRelationship retrievedRelationship : retrievedRelationships) { if (lastEvidenceTimestamp == null || retrievedRelationship.getTimestamp().compareTo(lastEvidenceTimestamp) > 0) { this.addIndirectEvidence(retrievedRelationship, connectionId); } } } catch (TrustEvidenceRepositoryException tere) { throw new TrustEvidenceMonitorException( "Could not retrieve latest indirect trust evidence originating from '" + connectionId + "': " + tere.getLocalizedMessage(), tere); } catch (Exception e) { throw new TrustEvidenceMonitorException( "Could not retrieve direct trust relationships of trustor '" + connectionId + "': " + e.getLocalizedMessage(), e); } } /** * Retrieves the connections to be monitored on behalf of the specified * trustor. More specifically, the method finds all CSSs directly trusted * by the specified trustor; any CSSs not already monitored are added to * the {@link #unmonitoredConnections} set. * * @param myTeid * @throws TrustException */ private void retrieveConnections(final TrustedEntityId myTeid) throws TrustException { LOG.debug("retrieveConnections: myTeid={}", myTeid); try { final Set<TrustRelationship> trs = this.trustBroker.retrieveTrustRelationships( new TrustQuery(myTeid).setTrusteeType(TrustedEntityType.CSS) .setTrustValueType(TrustValueType.DIRECT)).get(); for (final TrustRelationship tr : trs) { if (!myTeid.equals(tr.getTrusteeId()) && !this.monitoredConnections.contains(tr.getTrusteeId())) { this.unmonitoredConnections.add(tr.getTrusteeId()); } } } catch (Exception e) { throw new TrustEvidenceMonitorException( "Interrupted while retrieving DIRECT trust relationships of trustor '" + myTeid + "'"); } } }