/** * 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.context.source.csscis.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.comm.xmpp.interfaces.ICommManager; import org.societies.api.comm.xmpp.pubsub.PubsubClient; import org.societies.api.comm.xmpp.pubsub.Subscriber; import org.societies.api.context.CtxException; 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.CtxModelType; 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.InvalidFormatException; import org.societies.api.identity.Requestor; import org.societies.api.identity.RequestorCis; 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.css.CSSManagerEnums; import org.societies.api.osgi.event.CSSEvent; import org.societies.api.osgi.event.EventListener; import org.societies.api.osgi.event.EventTypes; import org.societies.api.osgi.event.IEventMgr; import org.societies.api.osgi.event.InternalEvent; import org.societies.api.privacytrust.privacy.util.privacypolicy.RequestPolicyUtils; import org.societies.api.schema.activity.MarshaledActivity; import org.societies.api.schema.cis.community.Community; import org.societies.api.schema.css.directory.CssFriendEvent; import org.societies.api.schema.identity.DataIdentifierScheme; import org.societies.api.schema.privacytrust.privacy.model.privacypolicy.RequestPolicy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; /** * This class is used to update user/community context based on CSS/CIS related * events (e.g. new CSS connections, CIS membership changes, etc). * * @author <a href="mailto:nicolas.liampotis@cn.ntua.gr">Nicolas Liampotis</a> (ICCS) * @since 1.0 */ @Service @Lazy(false) public class CssCisCtxMonitor extends EventListener implements Subscriber { /** The logging facility. */ private static final Logger LOG = LoggerFactory.getLogger(CssCisCtxMonitor.class); private static final String[] INTERNAL_EVENT_TYPES = { EventTypes.CSS_FRIENDED_EVENT, EventTypes.CIS_SUBS, EventTypes.CIS_UNSUBS, EventTypes.CIS_CREATION, EventTypes.CIS_DELETION, EventTypes.CIS_RESTORE }; private static final String[] EXTERNAL_EVENT_TYPES = { CSSManagerEnums.CSS_FRIEND_REQUEST_ACCEPTED_EVENT }; private static final String VERB_CSS_JOINED = "joined"; private static final String VERB_CSS_LEFT = "left"; /** The internal Context Broker service. */ @Autowired(required=true) private ICtxBroker ctxBroker; /** The Event Mgr service. */ private IEventMgr eventMgr; /** The PubsubClient service reference. */ private PubsubClient pubsubClient; /** The Comm Mgr service. */ private ICommManager commMgr; /** The executor service. */ private ExecutorService executorService = Executors.newSingleThreadExecutor(); @Autowired(required=true) CssCisCtxMonitor(IEventMgr eventMgr, PubsubClient pubsubClient, ICommManager commMgr) throws Exception { LOG.info("{} instantiated", this.getClass()); this.eventMgr = eventMgr; this.pubsubClient = pubsubClient; this.commMgr = commMgr; LOG.info("Registering for internal events '{}'", Arrays.asList(INTERNAL_EVENT_TYPES)); this.eventMgr.subscribeInternalEvent(this, INTERNAL_EVENT_TYPES, null); new Thread(new PubsubEventSubscriber()).start(); } /* * @see org.societies.api.osgi.event.EventListener#handleExternalEvent(org.societies.api.osgi.event.CSSEvent) */ @Override public void handleExternalEvent(CSSEvent event) { LOG.warn("Received unexpected external '" + event.geteventType() + "' event: " + event); } /* * @see org.societies.api.osgi.event.EventListener#handleInternalEvent(org.societies.api.osgi.event.InternalEvent) */ @Override public void handleInternalEvent(InternalEvent event) { LOG.debug("Received internal event: {}", eventToString(event)); if (EventTypes.CSS_FRIENDED_EVENT.equals(event.geteventType())) { if (event.geteventSource() == null || event.geteventSource().length() == 0) { LOG.error("Could not handle internal " + event.geteventType() + " event: " + "Expected non-null or non-empty event source of type IIdentity JID String" + " but was " + event.geteventSource()); return; } if (!(event.geteventInfo() instanceof String) || ((String) event.geteventInfo()).length() == 0) { LOG.error("Could not handle internal " + event.geteventType() + " event: " + "Expected non-null or non-empty event info of type IIdentity JID String" + " but was " + event.geteventInfo()); return; } final String myCssIdStr = event.geteventSource(); final String newFriendIdStr = (String) event.geteventInfo(); this.executorService.execute(new CssFriendedHandler(myCssIdStr, newFriendIdStr)); } else if (EventTypes.CIS_SUBS.equals(event.geteventType()) || EventTypes.CIS_UNSUBS.equals(event.geteventType())) { if (event.geteventSource() == null || event.geteventSource().length() == 0) { LOG.error("Could not handle internal " + event.geteventType() + " event: " + "Expected non-null or non-empty event source of type IIdentity JID String" + " but was " + event.geteventSource()); return; } if (!(event.geteventInfo() instanceof Community)) { LOG.error("Could not handle internal " + event.geteventType() + " event: " + "Expected event info of type " + Community.class.getName() + " but was " + event.geteventInfo().getClass()); return; } final Community cisRecord = (Community) event.geteventInfo(); if (EventTypes.CIS_SUBS.equals(event.geteventType())) { this.executorService.execute(new MyCssJoinedCisHandler( event.geteventSource(), cisRecord.getCommunityJid())); } else { //if (EventTypes.CIS_UNSUBS.equals(event.geteventType())) this.executorService.execute(new MyCssLeftCisHandler( event.geteventSource(), cisRecord.getCommunityJid())); } } else if (EventTypes.CIS_CREATION.equals(event.geteventType()) || EventTypes.CIS_DELETION.equals(event.geteventType()) || EventTypes.CIS_RESTORE.equals(event.geteventType())) { if (event.geteventSource() == null || event.geteventSource().length() == 0) { LOG.error("Could not handle internal " + event.geteventType() + " event: " + "Expected non-null or non-empty event source of type IIdentity JID String" + " but was " + event.geteventSource()); return; } if (!(event.geteventInfo() instanceof Community)) { LOG.error("Could not handle internal " + event.geteventType() + " event: " + "Expected event info of type " + Community.class.getName() + " but was " + event.geteventInfo().getClass()); return; } if (EventTypes.CIS_CREATION.equals(event.geteventType())) { this.executorService.execute( new CisCreatedHandler((Community) event.geteventInfo())); } else if (EventTypes.CIS_DELETION.equals(event.geteventType())) { this.executorService.execute( new CisRemovedHandler((Community) event.geteventInfo())); } else { // if (EventTypes.CIS_RESTORE.equals(event.geteventType())) this.executorService.execute( new CisRestoredHandler((Community) event.geteventInfo())); } } else { LOG.warn("Received unexpected event of type '" + event.geteventType() + "'"); } } /* * @see org.societies.api.comm.xmpp.pubsub.Subscriber#pubsubEvent(org.societies.api.identity.IIdentity, java.lang.String, java.lang.String, java.lang.Object) */ @Override public void pubsubEvent(IIdentity pubsubService, String node, String itemId, Object item) { LOG.debug("Received pubsub event: {}", item); if (item instanceof CssFriendEvent) { final String myCssIdStr = pubsubService.getBareJid(); final String newFriendIdStr = ((CssFriendEvent) item).getCssAdvert().getId(); if (newFriendIdStr == null || newFriendIdStr.length() == 0) { LOG.error("Could not handle external 'CssFriendEvent' event: " + "Expected non-empty friend JID but was " + newFriendIdStr); return; } this.executorService.execute(new CssFriendedHandler(myCssIdStr, newFriendIdStr)); } else if (item instanceof MarshaledActivity) { final String cssIdStr = ((MarshaledActivity) item).getActor(); final String cisIdStr = ((MarshaledActivity) item).getObject(); final String verb = ((MarshaledActivity) item).getVerb(); if (VERB_CSS_JOINED.equals(verb)) { this.executorService.execute( new CssJoinedMyCisHandler(cssIdStr, cisIdStr)); } else if (VERB_CSS_LEFT.equals(verb)) { this.executorService.execute( new CssLeftMyCisHandler(cssIdStr, cisIdStr)); } else { LOG.debug("Ignoring CIS Activity Feed pubsub event with verb '{}'", verb); } } else { LOG.warn("Received unexpected pubsubevent with item of type " + ((item != null) ? item.getClass() : "null")); } } private class CssFriendedHandler implements Runnable { private final String myCssIdStr; private final String newFriendIdStr; private CssFriendedHandler(final String myCssIdStr, final String newFriendIdStr) { this.myCssIdStr = myCssIdStr; this.newFriendIdStr = newFriendIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("CSS '{}' friended '{}", this.myCssIdStr, this.newFriendIdStr); try { final IIdentity myCssId = commMgr.getIdManager().fromJid(this.myCssIdStr); final IndividualCtxEntity myCssEnt = ctxBroker.retrieveIndividualEntity(myCssId).get(); final IIdentity newFriendId = commMgr.getIdManager().fromJid(this.newFriendIdStr); final CtxEntityIdentifier newFriendEntId = ctxBroker.retrieveIndividualEntityId( new Requestor(myCssId), newFriendId).get(); final CtxAssociation isFriendsWithAssoc; if (myCssEnt.getAssociations(CtxAssociationTypes.IS_FRIENDS_WITH).isEmpty()) { isFriendsWithAssoc = ctxBroker.createAssociation( new Requestor(myCssId), myCssId, CtxAssociationTypes.IS_FRIENDS_WITH).get(); isFriendsWithAssoc.setParentEntity(myCssEnt.getId()); } else { isFriendsWithAssoc = (CtxAssociation) ctxBroker.retrieve( myCssEnt.getAssociations(CtxAssociationTypes.IS_FRIENDS_WITH).iterator().next()).get(); } if (!isFriendsWithAssoc.getChildEntities().contains(newFriendEntId)) { isFriendsWithAssoc.addChildEntity(newFriendEntId); ctxBroker.update(isFriendsWithAssoc); } else { LOG.warn("IS_FRIENDS_WITH context association '" + isFriendsWithAssoc.getId() + "' already contains the individual entity id of CSS '" + newFriendIdStr + "'" + " - Nothing to do"); } } catch (InvalidFormatException ife) { LOG.error("Invalid CSS IIdentity found in CSS record: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } private class MyCssJoinedCisHandler implements Runnable { private final String myCssIdStr; private final String cisIdStr; private MyCssJoinedCisHandler(final String myCssIdStr, final String cisIdStr) { this.myCssIdStr = myCssIdStr; this.cisIdStr = cisIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("My CSS '{}' joined CIS '{}'", this.myCssIdStr, this.cisIdStr); try { final IIdentity myCssId = commMgr.getIdManager().fromJid( this.myCssIdStr); final IndividualCtxEntity myCssEnt = ctxBroker.retrieveIndividualEntity(myCssId).get(); if (myCssEnt == null) { LOG.error("Failed to retrieve the IndividualCtxEntity of my CSS '" + myCssId + "'"); return; } final IIdentity cisId = commMgr.getIdManager().fromJid(this.cisIdStr); final CtxEntityIdentifier cisEntId = ctxBroker.retrieveCommunityEntityId( new Requestor(myCssId), cisId).get(); if (cisEntId == null) { LOG.error("Failed to retrieve the CommunityCtxEntity id of CIS '" + cisId + "'"); return; } final CtxAssociation isMemberOfAssoc; if (myCssEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).isEmpty()) { isMemberOfAssoc = ctxBroker.createAssociation( new Requestor(myCssId), myCssId, CtxAssociationTypes.IS_MEMBER_OF).get(); isMemberOfAssoc.setParentEntity(myCssEnt.getId()); } else { isMemberOfAssoc = (CtxAssociation) ctxBroker.retrieve( myCssEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()).get(); } isMemberOfAssoc.addChildEntity(cisEntId); ctxBroker.update(isMemberOfAssoc); } catch (InvalidFormatException ife) { LOG.error("Invalid CSS/CIS IIdentity: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } private class MyCssLeftCisHandler implements Runnable { private final String myCssIdStr; private final String cisIdStr; private MyCssLeftCisHandler(final String myCssIdStr, final String cisIdStr) { this.myCssIdStr = myCssIdStr; this.cisIdStr = cisIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("My CSS '{}' left CIS '{}'", this.myCssIdStr, this.cisIdStr); try { final IIdentity myCssId = commMgr.getIdManager().fromJid( this.myCssIdStr); final IndividualCtxEntity myCssEnt = ctxBroker.retrieveIndividualEntity(myCssId).get(); if (myCssEnt == null) { LOG.error("Failed to retrieve the IndividualCtxEntity of my CSS '" + myCssId + "'"); return; } final IIdentity cisId = commMgr.getIdManager().fromJid(cisIdStr); final CtxEntityIdentifier cisEntId = ctxBroker.retrieveCommunityEntityId( new Requestor(myCssId), cisId).get(); if (cisEntId == null) { LOG.error("Failed to retrieve the CommunityCtxEntity id of CIS '" + cisId + "'"); return; } final CtxAssociation isMemberOfAssoc; if (!myCssEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).isEmpty()) { isMemberOfAssoc = (CtxAssociation) ctxBroker.retrieve( myCssEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()).get(); isMemberOfAssoc.removeChildEntity(cisEntId); ctxBroker.update(isMemberOfAssoc); } } catch (InvalidFormatException ife) { LOG.error("Invalid CSS/CIS IIdentity: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } private class CssJoinedMyCisHandler implements Runnable { private final String cssIdStr; private final String myCisIdStr; private CssJoinedMyCisHandler(String cssIdStr, String myCisIdStr) { this.cssIdStr = cssIdStr; this.myCisIdStr = myCisIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("CSS '{}' joined my CIS '{}'", this.cssIdStr, this.myCisIdStr); try { // Retrieve CommunityCtxEntity of CIS final IIdentity myCisId = commMgr.getIdManager().fromJid(this.myCisIdStr); final CtxEntityIdentifier cisEntityId = ctxBroker.retrieveCommunityEntityId(myCisId).get(); if (cisEntityId == null) { LOG.error("Failed to retrieve the CommunityCtxEntity id of my CIS '" + myCisId + "'"); } final CommunityCtxEntity cisEntity = (CommunityCtxEntity) ctxBroker.retrieve(cisEntityId).get(); if (cisEntity == null) { LOG.error("Failed to retrieve CommunityCtxEntity with id '" + cisEntityId + "'"); } // Retrieve CIS owner // TODO do it the right way (TM) final IIdentity cisOwnerId = commMgr.getIdManager().fromJid( commMgr.getIdManager().getThisNetworkNode().getBareJid()); // Retrieve IndividualCtxEntity identifier of new member (CSS) final IIdentity cssId = commMgr.getIdManager().fromJid(this.cssIdStr); final CtxEntityIdentifier cssEntId = ctxBroker.retrieveIndividualEntityId( new RequestorCis(cisOwnerId, myCisId), cssId).get(); if (cssEntId == null) { LOG.error("Failed to retrieve IndividualCtxEntity of new CIS member " + cssId); return; } LOG.info("Adding member '{}' to community '{}'", cssEntId, cisEntity.getId()); final CtxAssociationIdentifier hasMembersAssocId = cisEntity.getAssociations(CtxAssociationTypes.HAS_MEMBERS).iterator().next(); final CtxAssociation hasMembersAssoc = (CtxAssociation) ctxBroker.retrieve(hasMembersAssocId).get(); hasMembersAssoc.addChildEntity(cssEntId); ctxBroker.update(hasMembersAssoc); } catch (InvalidFormatException ife) { LOG.error("Failed to instantiate IIdentity from String representation: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } private class CssLeftMyCisHandler implements Runnable { private final String cssIdStr; private final String myCisIdStr; private CssLeftMyCisHandler(String cssIdStr, String myCisIdStr) { this.cssIdStr = cssIdStr; this.myCisIdStr = myCisIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("CSS '{}' left my CIS '{}'", this.cssIdStr, this.myCisIdStr); try { // Retrieve CommunityCtxEntity of CIS final IIdentity myCisId = commMgr.getIdManager().fromJid(this.myCisIdStr); final CtxEntityIdentifier cisEntityId = ctxBroker.retrieveCommunityEntityId(myCisId).get(); if (cisEntityId == null) { LOG.error("Failed to retrieve the CommunityCtxEntity id of my CIS '" + myCisId + "'"); } final CommunityCtxEntity cisEntity = (CommunityCtxEntity) ctxBroker.retrieve(cisEntityId).get(); if (cisEntity == null) { LOG.error("Failed to retrieve CommunityCtxEntity with id '" + cisEntityId + "'"); } // Retrieve CIS owner // TODO do it the right way (TM) final IIdentity cisOwnerId = commMgr.getIdManager().fromJid( commMgr.getIdManager().getThisNetworkNode().getBareJid()); // Retrieve IndividualCtxEntity identifier of ex-member (CSS) final IIdentity cssId = commMgr.getIdManager().fromJid(this.cssIdStr); final CtxEntityIdentifier cssEntId = ctxBroker.retrieveIndividualEntityId( new RequestorCis(cisOwnerId, myCisId), cssId).get(); if (cssEntId == null) { LOG.error("Failed to retrieve IndividualCtxEntity of ex CIS member " + cisOwnerId); return; } if (LOG.isInfoEnabled()) LOG.info("Removing member '" + cssEntId + "' from community '" + cisEntity.getId() + "'"); final CtxAssociationIdentifier hasMembersAssocId = cisEntity.getAssociations(CtxAssociationTypes.HAS_MEMBERS).iterator().next(); final CtxAssociation hasMembersAssoc = (CtxAssociation) ctxBroker.retrieve(hasMembersAssocId).get(); hasMembersAssoc.removeChildEntity(cssEntId); ctxBroker.update(hasMembersAssoc); } catch (InvalidFormatException ife) { LOG.error("Failed to instantiate IIdentity from String representation: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } /** * (1) Create community ctx entity * (2) Create community ctx attributes * (3) Update community ctx entity HAS_MEMBERS association * (4) Update community owner ctx entity IS_MEMBER_OF association * (5) Update community owner ctx entity IS_ADMIN_OF association * (6) Subscribe for CIS Activity Feed to monitor membership changes */ private class CisCreatedHandler implements Runnable { private final Community cis; private CisCreatedHandler(Community cis) { this.cis = cis; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("CIS '{}' created", this.cis.getCommunityJid()); try { // (1) Create community ctx entity final String cisIdStr = this.cis.getCommunityJid(); final IIdentity cisId = commMgr.getIdManager().fromJid(cisIdStr); final CommunityCtxEntity cisEntity = ctxBroker.createCommunityEntity(cisId).get(); // (2) Create community ctx attributes final String cisOwnerCssIdStr = cis.getOwnerJid(); final IIdentity cisOwnerId = commMgr.getIdManager().fromJid(cisOwnerCssIdStr); // NAME createCtxAttribute(cisEntity.getId(), CtxAttributeTypes.NAME, this.cis.getCommunityName()); // ABOUT createCtxAttribute(cisEntity.getId(), CtxAttributeTypes.ABOUT, this.cis.getDescription()); // add ctx attributes based on privacy policy final List<String> attrTypesList = getPrivPolicyAttributeTypes(cisOwnerId, cisId); LOG.debug("CisCreatedHandler: attrTypes={}", attrTypesList); for (final String attrType : attrTypesList) { createCtxAttribute(cisEntity.getId(), attrType, null); } // (3) Update community ctx entity HAS_MEMBERS association final IndividualCtxEntity cisOwnerEntity = ctxBroker.retrieveIndividualEntity(cisOwnerId).get(); if (cisOwnerEntity == null) { LOG.error("Could not retrieve IndividualCtxEntity for CIS creator " + cisOwnerId); return; } LOG.info("Adding member '{}' to community '{}'", cisOwnerEntity.getId(), cisEntity.getId()); final CtxAssociationIdentifier hasMembersAssocId = cisEntity.getAssociations(CtxAssociationTypes.HAS_MEMBERS).iterator().next(); final CtxAssociation hasMembersAssoc = (CtxAssociation) ctxBroker.retrieve(hasMembersAssocId).get(); hasMembersAssoc.addChildEntity(cisOwnerEntity.getId()); ctxBroker.update(hasMembersAssoc); // TODO membership criteria / bonds // (4) Update community owner ctx entity IS_MEMBER_OF association final CtxAssociation isMemberOfAssoc; if (cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).isEmpty()) { isMemberOfAssoc = ctxBroker.createAssociation( cisOwnerId, CtxAssociationTypes.IS_MEMBER_OF).get(); } else { isMemberOfAssoc = (CtxAssociation) ctxBroker.retrieve( cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()).get(); } isMemberOfAssoc.setParentEntity(cisOwnerEntity.getId()); isMemberOfAssoc.addChildEntity(cisEntity.getId()); ctxBroker.update(isMemberOfAssoc); // (5) Update community owner ctx entity IS_ADMIN_OF association final CtxAssociation isAdminOfAssoc; if (cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_ADMIN_OF).isEmpty()) { isAdminOfAssoc = ctxBroker.createAssociation( cisOwnerId, CtxAssociationTypes.IS_ADMIN_OF).get(); } else { isAdminOfAssoc = (CtxAssociation) ctxBroker.retrieve( cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_ADMIN_OF).iterator().next()).get(); } isAdminOfAssoc.setParentEntity(cisOwnerEntity.getId()); isAdminOfAssoc.addChildEntity(cisEntity.getId()); ctxBroker.update(isAdminOfAssoc); // (6) Subscribe for CIS Activity Feed to monitor membership changes LOG.info("Subscribing for the ActivityFeed of CIS '{}'", cisIdStr); pubsubClient.subscriberSubscribe(cisOwnerId, cisIdStr, CssCisCtxMonitor.this); } catch (InvalidFormatException ife) { LOG.error("Invalid IIdentity found in CIS record: " + ife.getLocalizedMessage(), ife); } catch (CtxException ce) { LOG.error("Failed to access context data: " + ce.getLocalizedMessage(), ce); } catch (Exception e) { LOG.error("Failed to subscribe for CIS ActivityFeed: " + e.getLocalizedMessage(), e); } } } /** * (1) Unsubscribe from CIS Activity Feed to stop monitoring membership changes * (2) Update community owner's ctx entity IS_MEMBER_OF association * (3) Update community owner's ctx entity IS_ADMIN_OF association * (4) Remove community's HAS_MEMBERS/IS_MEMBER_OF ctx association * (5) Remove community's ctx entity */ private class CisRemovedHandler implements Runnable { private final Community cis; private CisRemovedHandler(Community cis) { this.cis = cis; } /* * @see java.lang.Runnable#run() */ @Override public void run() { LOG.info("CIS '{}' deleted", this.cis.getCommunityJid()); try { final String cisIdStr = this.cis.getCommunityJid(); final IIdentity cisId = commMgr.getIdManager().fromJid(cisIdStr); final String cisOwnerIdStr = this.cis.getOwnerJid(); final IIdentity cisOwnerId = commMgr.getIdManager().fromJid(cisOwnerIdStr); // (1) Unsubscribe from CIS Activity Feed to stop monitoring membership changes LOG.info("Unsubscribing from the ActivityFeed of CIS '{}'", cisIdStr); pubsubClient.subscriberUnsubscribe(cisOwnerId, cisIdStr, CssCisCtxMonitor.this); final CtxEntityIdentifier cisEntityId = ctxBroker.retrieveCommunityEntityId(cisId).get(); if (cisEntityId == null) { LOG.error("Failed to retrieve CommunityCtxEntity ID of CIS '" + cisId + "'"); return; } final IndividualCtxEntity cisOwnerEntity = ctxBroker.retrieveIndividualEntity(cisOwnerId).get(); if (cisOwnerEntity == null) { LOG.error("Could not retrieve IndividualCtxEntity for CIS creator '" + cisOwnerId + "'"); return; } // (2) Update owner's ctx entity IS_MEMBER_OF association if (cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().hasNext()) { final CtxAssociation isMemberOfAssoc = (CtxAssociation) ctxBroker.retrieve( cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()).get(); if (isMemberOfAssoc != null && isMemberOfAssoc.getChildEntities().contains(cisEntityId)) { LOG.info("Removing '{}' from IS_MEMBER_OF association of '{}'", cisEntityId, cisOwnerEntity.getId()); isMemberOfAssoc.removeChildEntity(cisEntityId); ctxBroker.update(isMemberOfAssoc); } } // (3) Update owner's ctx entity IS_ADMIN_OF association if (cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_ADMIN_OF).iterator().hasNext()) { final CtxAssociation isAdminOfAssoc = (CtxAssociation) ctxBroker.retrieve( cisOwnerEntity.getAssociations(CtxAssociationTypes.IS_ADMIN_OF).iterator().next()).get(); if (isAdminOfAssoc != null && isAdminOfAssoc.getChildEntities().contains(cisEntityId)) { LOG.info("Removing '{}' from IS_ADMIN_OF association of '{}'", cisEntityId, cisOwnerEntity.getId()); isAdminOfAssoc.removeChildEntity(cisEntityId); ctxBroker.update(isAdminOfAssoc); } } // (4) Remove community HAS_MEMBERS/IS_MEMBER_OF ctx associations final CommunityCtxEntity cisEntity = (CommunityCtxEntity) ctxBroker.retrieve(cisEntityId).get(); if (cisEntity == null) { LOG.error("Failed to retrieve CommunityCtxEntity with ID '" + cisEntityId + "'"); return; } if (cisEntity.getAssociations(CtxAssociationTypes.HAS_MEMBERS).iterator().hasNext()) { LOG.info("Removing HAS_MEMBERS association of '{}'", cisEntity.getId()); ctxBroker.remove(cisEntity.getAssociations(CtxAssociationTypes.HAS_MEMBERS).iterator().next()); } if (cisEntity.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().hasNext()) { LOG.info("Removing IS_MEMBER_OF association of '{}'", cisEntity.getId()); ctxBroker.remove(cisEntity.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()); } // TODO membership criteria / bonds // (5) Remove community ctx entity LOG.info("Removing CommunityCtxEntity with ID '{}'", cisEntityId); ctxBroker.remove(cisEntityId); } catch (InvalidFormatException ife) { LOG.error("Invalid IIdentity found in CIS record: " + ife.getLocalizedMessage(), ife); } catch (CtxException ce) { LOG.error("Failed to access context data: " + ce.getLocalizedMessage(), ce); } catch (Exception e) { LOG.error("Failed to unsubscribe from CIS ActivityFeed: " + e.getLocalizedMessage(), e); } } } /** * (1) Subscribe for CIS Activity Feed to monitor membership changes */ private class CisRestoredHandler implements Runnable { private final Community cis; private CisRestoredHandler(Community cis) { this.cis = cis; } /* * @see java.lang.Runnable#run() */ @Override public void run() { try { LOG.info("CIS '{}' restored", this.cis.getCommunityJid()); // (1) Subscribe for CIS Activity Feed to monitor membership changes final String cisIdStr = this.cis.getCommunityJid(); final IIdentity cisId = commMgr.getIdManager().fromJid(cisIdStr); final String cisOwnerIdStr = cis.getOwnerJid(); final IIdentity cisOwnerId = commMgr.getIdManager().fromJid(cisOwnerIdStr); LOG.info("Subscribing for the ActivityFeed of CIS '{}'", cisId); pubsubClient.subscriberSubscribe(cisOwnerId, cisIdStr, CssCisCtxMonitor.this); } catch (InvalidFormatException ife) { LOG.error("Invalid IIdentity found in CIS record: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to subscribe for CIS ActivityFeed: " + e.getLocalizedMessage(), e); } } } private void createCtxAttribute(CtxEntityIdentifier ownerCtxId, String type, String value) throws Exception { LOG.debug("Creating '{}' attribute under entity '{}' with value '{}'", new Object[] { type, ownerCtxId, value }); final CtxAttribute attr = this.ctxBroker.createAttribute(ownerCtxId, type).get(); if (value != null) { attr.setStringValue(value); this.ctxBroker.update(attr); } } @SuppressWarnings("deprecation") private List<String> getPrivPolicyAttributeTypes(final IIdentity ownerJid, final IIdentity cisId){ final List<String> result = new ArrayList<String>(); final RequestorCis reqCIs = new RequestorCis(ownerJid, cisId); final int hash = reqCIs.hashCode(); final String hashString = String.valueOf(hash); final String privacyPolicyType = "policyOf"+hashString; try { final List<CtxIdentifier> privacyPolicyList = this.ctxBroker.lookup( ownerJid, CtxModelType.ATTRIBUTE, privacyPolicyType).get(); if (!privacyPolicyList.isEmpty()) { final CtxIdentifier ctxId = privacyPolicyList.get(0); final CtxAttribute privacyPolicyAttr = (CtxAttribute) this.ctxBroker.retrieve(ctxId).get(); final RequestPolicy privacyPolicybean = (RequestPolicy) SerialisationHelper.deserialise(privacyPolicyAttr.getBinaryValue(), this.getClass().getClassLoader()); result.addAll(RequestPolicyUtils.getDataTypes( DataIdentifierScheme.CONTEXT, privacyPolicybean)); } } catch (Exception e) { LOG.error("Could not determine context attribute types specified in CIS privacy policy: " + e.getLocalizedMessage(), e); } return result; } private String eventToString(final InternalEvent event) { final StringBuffer sb = new StringBuffer(); sb.append("["); sb.append("name="); sb.append(event.geteventName()); sb.append(","); sb.append("type="); sb.append(event.geteventType()); sb.append(","); sb.append("source="); sb.append(event.geteventSource()); sb.append(","); sb.append("info="); sb.append(event.geteventInfo()); sb.append("]"); return sb.toString(); } private class PubsubEventSubscriber implements Runnable { /* * @see java.lang.Runnable#run() */ @Override public void run() { try { final IIdentity pubsubId = commMgr.getIdManager().getThisNetworkNode(); final Set<String> topicsToRegister = new HashSet<String>(Arrays.asList(EXTERNAL_EVENT_TYPES)); while (!topicsToRegister.isEmpty()) { LOG.debug("Pubsub events to register for '{}'", topicsToRegister); final List<String> existingTopics = pubsubClient.discoItems(pubsubId, null); for (final String topic : EXTERNAL_EVENT_TYPES) { if (existingTopics.contains(topic)) { LOG.info("Registering for pubsub event '{}'" + " under pubsub id {}", topic, pubsubId); pubsubClient.subscriberSubscribe(pubsubId, topic, CssCisCtxMonitor.this); topicsToRegister.remove(topic); } } if (!topicsToRegister.isEmpty()) { LOG.warn("Pubsub topics '" + topicsToRegister + "' were not available -" + " Sleeping for 15 seconds until next pubsub node discovery..."); Thread.sleep(15000); } } LOG.debug("Registered for all pubsub events"); } catch (Exception e) { LOG.error("Could not register for pubsub events '" + Arrays.asList(EXTERNAL_EVENT_TYPES) + "':" + e.getLocalizedMessage(), e); } } } }