/**
* 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.Dictionary;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
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.comm.xmpp.interfaces.ICommManager;
import org.societies.api.context.event.CtxChangeEvent;
import org.societies.api.context.event.CtxChangeEventListener;
import org.societies.api.context.model.CommunityCtxEntity;
import org.societies.api.context.model.CtxAssociation;
import org.societies.api.context.model.CtxAssociationIdentifier;
import org.societies.api.context.model.CtxAttribute;
import org.societies.api.context.model.CtxEntityIdentifier;
import org.societies.api.context.model.CtxIdentifier;
import org.societies.api.context.model.IndividualCtxEntity;
import org.societies.api.context.model.util.SerialisationHelper;
import org.societies.api.identity.IIdentity;
import org.societies.api.identity.Requestor;
import org.societies.api.internal.context.broker.ICtxBroker;
import org.societies.api.internal.context.model.CtxAssociationTypes;
import org.societies.api.internal.context.model.CtxAttributeTypes;
import org.societies.api.internal.privacytrust.trust.evidence.ITrustEvidenceCollector;
import org.societies.api.personalisation.model.IAction;
import org.societies.api.privacytrust.trust.TrustException;
import org.societies.api.privacytrust.trust.evidence.TrustEvidenceType;
import org.societies.api.privacytrust.trust.model.TrustedEntityId;
import org.societies.api.privacytrust.trust.model.TrustedEntityType;
import org.societies.api.privacytrust.trust.model.util.TrustedEntityIdFactory;
import org.societies.api.schema.servicelifecycle.model.ServiceResourceIdentifier;
import org.societies.privacytrust.trust.api.ITrustNodeMgr;
import org.societies.privacytrust.trust.api.evidence.repo.ITrustEvidenceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* This class is used to acquire trust evidence based on the CSS owner's direct
* interactions with other CSSs, the CISs they're member of, and their
* installed services. More specifically, it adds {@link DirectTrustEvidence}
* to the Trust Evidence Repository by monitoring the following context change
* events:
* <ol>
* <li>CSS owner uses a service</li>
* <li>CSS owner (un)friends another CSS.</li>
* <li>CSS owner joins/leaves a CIS.</li>
* <li>Membership changes in a CIS the CSS owner is member of.</li>
* </ol>
*
* The generated pieces of Direct Trust Evidence are then processed by the
* Direct Trust Engine in order to (re)evaluate the direct trust on 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 0.4.1
*/
@Service
public class CtxTrustEvidenceMonitor implements CtxChangeEventListener {
private static final Logger LOG = LoggerFactory.getLogger(CtxTrustEvidenceMonitor.class);
/** The time to wait between registration attempts for membership changes (in seconds) */
private static final long WAIT = 60l;
@Autowired(required=true)
private ITrustEvidenceCollector trustEvidenceCollector;
@Autowired(required=true)
private ITrustEvidenceRepository trustEvidenceRepository;
@Autowired(required=false)
private ICtxBroker ctxBroker;
private ICommManager commMgr;
private final IIdentity ownerId;
private final Set<String> friends = new CopyOnWriteArraySet<String>();
private final Set<String> communities = new CopyOnWriteArraySet<String>();
private final Set<String> unmonitoredCommunities = new CopyOnWriteArraySet<String>();
/** The executor service. */
private ExecutorService executorService = Executors.newSingleThreadExecutor();
/** The scheduled executor service. */
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
@Autowired
CtxTrustEvidenceMonitor(ITrustNodeMgr trustNodeMgr, ICommManager commMgr) throws Exception {
if (LOG.isInfoEnabled())
LOG.info(this.getClass() + " instantiated");
this.commMgr = commMgr;
final String ownerIdStr = commMgr.getIdManager().getThisNetworkNode().getBareJid();
this.ownerId = commMgr.getIdManager().fromJid(ownerIdStr);
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onCreation(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onCreation(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received CREATED event " + event);
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onUpdate(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onUpdate(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received UPDATED event " + event);
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onModification(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onModification(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received MODIFIED event " + event);
if (CtxAttributeTypes.LAST_ACTION.equals(event.getId().getType()))
this.executorService.execute(new UserLastActionHandler(event.getId()));
else if (CtxAssociationTypes.IS_FRIENDS_WITH.equals(event.getId().getType()))
this.executorService.execute(new UserIsFriendsWithHandler(event.getId()));
else if (CtxAssociationTypes.IS_MEMBER_OF.equals(event.getId().getType()))
this.executorService.execute(new UserIsMemberOfHandler(event.getId()));
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onRemoval(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onRemoval(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received REMOVED event " + event);
}
/**
* This method is called when the {@link ICtxBroker} service is bound.
*
* @param ctxBroker
* the {@link ICtxBroker} service that was bound
* @param props
* the set of properties that the {@link ICtxBroker} service
* was registered with
*/
void bindCtxBroker(ICtxBroker ctxBroker, Dictionary<Object,Object> props)
throws Exception {
LOG.info("Binding service reference " + ctxBroker);
this.executorService.submit(new Initialiser()).get();
}
/**
* This method is called when the {@link ICtxBroker} service is unbound.
*
* @param ctxBroker
* the {@link ICtxBroker} service that was unbound
* @param props
* the set of properties that the {@link ICtxBroker} service
* was registered with
*/
void unbindCtxBroker(ICtxBroker ctxBroker, Dictionary<Object,Object> props) {
LOG.info("Unbinding service reference " + ctxBroker);
}
private class UserLastActionHandler implements Runnable {
private final CtxIdentifier ctxId;
private UserLastActionHandler(CtxIdentifier ctxId) {
this.ctxId = ctxId;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
final CtxAttribute lastActionAttr = (CtxAttribute) ctxBroker.retrieve(ctxId).get();
if (lastActionAttr == null) {
LOG.error("Could not handle CSS last action: "
+ "Could not retrieve '" + this.ctxId + "'");
return;
}
if (lastActionAttr.getBinaryValue() == null) {
LOG.error("Could not handle CSS last action: "
+ "LAST_ACTION attribute value is null");
return;
}
final IAction lastAction = (IAction) SerialisationHelper.deserialise(
lastActionAttr.getBinaryValue(), this.getClass().getClassLoader());
LOG.debug("lastAction={}", lastAction);
final String userId = lastActionAttr.getId().getOwnerId();
final ServiceResourceIdentifier serviceId = lastAction.getServiceID();
final Date ts = lastActionAttr.getLastModified();
addServiceEvidence(userId, serviceId, ts);
} catch (Exception e) {
LOG.error("Could not handle CSS last action: "
+ e.getLocalizedMessage(), e);
}
}
}
private class UserIsFriendsWithHandler implements Runnable {
private final CtxIdentifier ctxId;
private UserIsFriendsWithHandler(CtxIdentifier ctxId) {
this.ctxId = ctxId;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
final CtxAssociation isFriendsWith = (CtxAssociation) ctxBroker.retrieve(ctxId).get();
if (isFriendsWith == null) {
LOG.error("Could not handle CSS friendship change: "
+ "Could not retrieve '" + this.ctxId + "'");
return;
}
final Set<String> currentFriends = new HashSet<String>();
for (final CtxEntityIdentifier cisCtxId : isFriendsWith.getChildEntities())
currentFriends.add(cisCtxId.getOwnerId());
// find friends the user is no long member of
final Set<String> oldFriends = new HashSet<String>(friends);
oldFriends.removeAll(currentFriends);
// find new friends the user is member of
final Set<String> newFriends = new HashSet<String>(currentFriends);
newFriends.removeAll(friends);
// update friends the user is member of
friends.clear();
friends.addAll(currentFriends);
final String userId = isFriendsWith.getParentEntity().getOwnerId();
final Date ts = isFriendsWith.getLastModified();
for (final String oldFriend : oldFriends)
addFriendshipEvidence(userId, oldFriend, TrustEvidenceType.UNFRIENDED_USER, ts);
for (final String newFriend : newFriends)
addFriendshipEvidence(userId, newFriend, TrustEvidenceType.FRIENDED_USER, ts);
} catch (Exception e) {
LOG.error("Could not handle CSS friendship change: "
+ e.getLocalizedMessage(), e);
}
}
}
private class UserIsMemberOfHandler implements Runnable {
private final CtxIdentifier ctxId;
private UserIsMemberOfHandler(CtxIdentifier ctxId) {
this.ctxId = ctxId;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
final CtxAssociation isMemberOf =
(CtxAssociation) ctxBroker.retrieve(ctxId).get();
if (isMemberOf == null) {
LOG.error("Could not handle CIS membership change: "
+ "Could not retrieve '" + this.ctxId + "'");
return;
}
final Set<String> currentCommunities = new HashSet<String>();
for (final CtxEntityIdentifier cisCtxId : isMemberOf.getChildEntities()) {
final String communityId = cisCtxId.getOwnerId();
currentCommunities.add(communityId);
}
// find communities the user is no long member of
final Set<String> oldCommunities = new HashSet<String>(communities);
oldCommunities.removeAll(currentCommunities);
if (LOG.isDebugEnabled())
LOG.debug("CSS ctx Id " + isMemberOf.getParentEntity() + ", oldCommunities=" + oldCommunities);
// find new communities the user is member of
final Set<String> newCommunities = new HashSet<String>(currentCommunities);
newCommunities.removeAll(communities);
if (LOG.isDebugEnabled())
LOG.debug("CSS ctx Id " + isMemberOf.getParentEntity() + ", newCommunities=" + newCommunities);
// update communities the user is member of
communities.clear();
communities.addAll(currentCommunities);
final String memberId = isMemberOf.getParentEntity().getOwnerId();
final Date ts = isMemberOf.getLastModified();
for (final String oldCommunity : oldCommunities)
addMembershipEvidence(memberId, oldCommunity,
TrustEvidenceType.LEFT_COMMUNITY, ts);
for (final String newCommunity : newCommunities) {
addMembershipEvidence(memberId, newCommunity,
TrustEvidenceType.JOINED_COMMUNITY, ts);
unmonitoredCommunities.add(newCommunity);
}
} catch (Exception e) {
LOG.error("Could not handle CIS membership change: "
+ e.getLocalizedMessage(), e);
}
}
}
private class CommunityHasMembersListener implements CtxChangeEventListener {
private final String communityId;
private final Set<String> members = new CopyOnWriteArraySet<String>();
private CommunityHasMembersListener(final String communityId, final Set<String> members) {
this.communityId = communityId;
this.members.addAll(members);
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onCreation(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onCreation(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received CREATED event " + event);
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onModification(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onModification(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received MODIFIED event " + event);
if (event.getId() == null) {
LOG.error("Could not handle MODIFIED event " + event
+ ": event.getId can't be null");
return;
}
executorService.execute(new CommunityHasMembersHandler(event.getId(), this));
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onRemoval(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onRemoval(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received REMOVED event " + event);
}
/*
* @see org.societies.api.context.event.CtxChangeEventListener#onUpdate(org.societies.api.context.event.CtxChangeEvent)
*/
@Override
public void onUpdate(CtxChangeEvent event) {
if (LOG.isDebugEnabled())
LOG.debug("Received UPDATED event " + event);
}
}
private class CommunityHasMembersHandler implements Runnable {
private final CtxIdentifier hasMembersAssocId;
private final CommunityHasMembersListener listener;
private CommunityHasMembersHandler(final CtxIdentifier hasMembersAssocId,
final CommunityHasMembersListener listener) {
this.hasMembersAssocId = hasMembersAssocId;
this.listener = listener;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (LOG.isDebugEnabled())
LOG.debug("Retrieving HAS_MEMBERS association '" + this.hasMembersAssocId
+ "' to handle membership change of CIS '" + this.listener.communityId + "'");
try {
final CtxAssociation hasMembersAssoc =
(CtxAssociation) ctxBroker.retrieve(this.hasMembersAssocId).get();
if (hasMembersAssoc == null) {
LOG.error("Could not handle membership change of CIS '"
+ this.listener.communityId + "': '"
+ this.hasMembersAssocId + "' is null");
return;
}
final Set<String> currentMembers = new HashSet<String>();
for (final CtxEntityIdentifier cisCtxId : hasMembersAssoc.getChildEntities())
currentMembers.add(cisCtxId.getOwnerId());
// find old members
final Set<String> oldMembers = new HashSet<String>(this.listener.members);
oldMembers.removeAll(currentMembers);
// find new members
final Set<String> newMembers = new HashSet<String>(currentMembers);
newMembers.removeAll(this.listener.members);
// update members
this.listener.members.clear();
this.listener.members.addAll(currentMembers);
final Date ts = hasMembersAssoc.getLastModified();
for (final String oldMember : oldMembers)
addMembershipEvidence(oldMember, this.listener.communityId,
TrustEvidenceType.LEFT_COMMUNITY, ts);
for (final String newMember : newMembers)
addMembershipEvidence(newMember, this.listener.communityId,
TrustEvidenceType.JOINED_COMMUNITY, ts);
} catch (Exception e) {
LOG.error("Could not handle membership change of CIS '"
+ this.listener.communityId + "': "
+ e.getLocalizedMessage(), e);
}
}
}
/**
* Runs periodically in the background to perform various maintenance
* tasks.
*/
private class MaintenanceDaemon implements Runnable {
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (LOG.isDebugEnabled())
LOG.debug("Communities to register for membership changes: " + unmonitoredCommunities);
for (final String communityId : new HashSet<String>(unmonitoredCommunities)) {
try {
registerForMembershipChanges(communityId);
unmonitoredCommunities.remove(communityId);
} catch (Exception e) {
LOG.warn("Failed to register for membership changes of CIS '" + communityId
+ "': " + e.getLocalizedMessage() + ". Will re-attempt to register in "
+ WAIT + " seconds...");
}
} // for each communityId ends
}
}
private class Initialiser implements Callable<Void> {
/*
* @see java.util.concurrent.Callable#call()
*/
@Override
public Void call() throws Exception {
final IndividualCtxEntity ownerEnt = ctxBroker.retrieveIndividualEntity(ownerId).get();
// init friends set
if (LOG.isInfoEnabled())
LOG.info("Acquiring friends of CSS '" + ownerId + "'");
if (ownerEnt.getAssociations(CtxAssociationTypes.IS_FRIENDS_WITH).iterator().hasNext()) {
final CtxAssociation isFriendsWith = (CtxAssociation)
ctxBroker.retrieve(ownerEnt.getAssociations(CtxAssociationTypes.IS_FRIENDS_WITH).iterator().next()).get();
for (final CtxEntityIdentifier friendEntId : isFriendsWith.getChildEntities())
friends.add(friendEntId.getOwnerId());
}
if (LOG.isDebugEnabled())
LOG.debug("friends=" + friends);
// init communitites set
if (LOG.isInfoEnabled())
LOG.info("Acquiring communities of CSS '" + ownerId + "'");
if (ownerEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().hasNext()) {
final CtxAssociation isMemberOf = (CtxAssociation)
ctxBroker.retrieve(ownerEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()).get();
for (final CtxEntityIdentifier communityEntId : isMemberOf.getChildEntities()) {
communities.add(communityEntId.getOwnerId());
unmonitoredCommunities.add(communityEntId.getOwnerId());
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("communities=" + communities);
LOG.debug("unmonitoredCommunities=" + unmonitoredCommunities);
}
scheduler.scheduleWithFixedDelay(new MaintenanceDaemon(),
WAIT, WAIT, TimeUnit.SECONDS);
if (LOG.isInfoEnabled())
LOG.info("Registering for context changes related to CSS '"
+ ownerId + "'");
ctxBroker.registerForChanges(CtxTrustEvidenceMonitor.this, ownerId);
return null;
}
}
private void addServiceEvidence(final String userId,
final ServiceResourceIdentifier serviceId, final Date ts)
throws TrustException {
final TrustedEntityId subjectId = new TrustedEntityId(
TrustedEntityType.CSS,
userId);
final TrustedEntityId objectId =
TrustedEntityIdFactory.fromServiceResourceIdentifier(serviceId);
final TrustEvidenceType type = TrustEvidenceType.USED_SERVICE;
if (LOG.isDebugEnabled())
LOG.debug("Adding direct trust evidence: subjectId="
+ subjectId + ", objectId=" + objectId
+ ", type=" + type + ", ts=" + ts);
trustEvidenceCollector.addDirectEvidence(
subjectId, objectId, type, ts, null);
}
private void addFriendshipEvidence(final String userId,
final String friendId, final TrustEvidenceType type,
final Date ts) throws TrustException {
if (!TrustEvidenceType.FRIENDED_USER.equals(type)
&& !TrustEvidenceType.UNFRIENDED_USER.equals(type))
throw new IllegalArgumentException("Illegal evidence type " + type);
final TrustedEntityId subjectId = new TrustedEntityId(
TrustedEntityType.CSS,
userId);
final TrustedEntityId objectId = new TrustedEntityId(
TrustedEntityType.CSS,
friendId);
if (LOG.isDebugEnabled())
LOG.debug("Adding direct trust evidence: subjectId="
+ subjectId + ", objectId=" + objectId
+ ", type=" + type + ", ts=" + ts);
trustEvidenceCollector.addDirectEvidence(
subjectId, objectId, type, ts, null);
}
private void addMembershipEvidence(final String memberId,
final String communityId, final TrustEvidenceType type,
final Date ts) throws TrustException {
if (!TrustEvidenceType.JOINED_COMMUNITY.equals(type)
&& !TrustEvidenceType.LEFT_COMMUNITY.equals(type))
throw new IllegalArgumentException("Illegal evidence type " + type);
final TrustedEntityId subjectId = new TrustedEntityId(
TrustedEntityType.CSS,
memberId);
final TrustedEntityId objectId = new TrustedEntityId(
TrustedEntityType.CIS,
communityId);
if (LOG.isDebugEnabled())
LOG.debug("Adding direct trust evidence: subjectId="
+ subjectId + ", objectId=" + objectId
+ ", type=" + type + ", ts=" + ts);
trustEvidenceCollector.addDirectEvidence(
subjectId, objectId, type, ts, null);
}
private void registerForMembershipChanges(final String communityId) throws Exception {
if (LOG.isDebugEnabled())
LOG.debug("Retrieving community context entity identifier of CIS '" + communityId + "'");
final CtxEntityIdentifier communityEntId = ctxBroker.retrieveCommunityEntityId(
new Requestor(ownerId), commMgr.getIdManager().fromJid(communityId)).get();
if (communityEntId == null) {
LOG.error("Failed to register for membership changes of CIS '" + communityId
+ "': Community context entity identifier is null");
return;
}
if (LOG.isDebugEnabled())
LOG.debug("Retrieving community context entity identified as " + communityEntId);
final CommunityCtxEntity communityEnt =
(CommunityCtxEntity) ctxBroker.retrieve(communityEntId).get();
if (communityEnt == null) {
LOG.error("Failed to register for membership changes of CIS '" + communityEntId.getOwnerId()
+ "': Community context entity is null");
return;
}
CtxAssociationIdentifier hasMembersId = null;
final Set<String> members = new HashSet<String>();
for (final CtxAssociationIdentifier foundHasMembersId : communityEnt.getAssociations(CtxAssociationTypes.HAS_MEMBERS)) {
final CtxAssociation foundHasMembers =
(CtxAssociation) ctxBroker.retrieve(foundHasMembersId).get();
if (foundHasMembers != null && communityEntId.equals(foundHasMembers.getParentEntity())) {
hasMembersId = foundHasMembersId;
for (final CtxEntityIdentifier memberEntId : foundHasMembers.getChildEntities()) {
final String memberId = memberEntId.getOwnerId();
if (LOG.isDebugEnabled())
LOG.debug("Checking existing evidence about '" + memberId + "' being member of community '" + communityId + "'");
if (trustEvidenceRepository.retrieveEvidence(
new TrustedEntityId(TrustedEntityType.CSS, memberId),
new TrustedEntityId(TrustedEntityType.CIS, communityId),
TrustEvidenceType.JOINED_COMMUNITY, null, null, null).isEmpty())
addMembershipEvidence(memberId, communityId, TrustEvidenceType.JOINED_COMMUNITY, new Date());
members.add(memberId);
}
break;
}
}
if (hasMembersId == null) {
LOG.error("Failed to register for membership changes of CIS '" + communityEntId.getOwnerId()
+ "': HAS_MEMBERS association not found");
return;
}
if (LOG.isInfoEnabled())
LOG.info("Registering for membership changes of CIS '" + communityEntId.getOwnerId() + "'");
if (LOG.isDebugEnabled())
LOG.debug("hasMembersId=" + hasMembersId + ", members=" + members);
ctxBroker.registerForChanges(new CommunityHasMembersListener(
communityEntId.getOwnerId(), members), hasMembersId);
}
}