/** * 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.cis.impl; import java.util.Arrays; 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.CtxAttributeValueType; import org.societies.api.context.model.CtxEntityIdentifier; import org.societies.api.context.model.CtxOriginType; import org.societies.api.context.model.IndividualCtxEntity; import org.societies.api.identity.IIdentity; import org.societies.api.identity.InvalidFormatException; 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.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.schema.activity.MarshaledActivity; import org.societies.api.schema.cis.community.Community; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; /** * This class is used to update CIS context data based on CIS life-cycle events. * * @author <a href="mailto:nicolas.liampotis@cn.ntua.gr">Nicolas Liampotis</a> (ICCS) * @since 0.5 */ @Service @Lazy(false) public class CisCtxMonitor extends EventListener implements Subscriber { /** The logging facility. */ private static final Logger LOG = LoggerFactory.getLogger(CisCtxMonitor.class); private static final String[] EVENT_TYPES = { EventTypes.CIS_CREATION, EventTypes.CIS_DELETION }; 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 reference. */ private IEventMgr eventMgr; /** The PubsubClient service reference. */ @Autowired private PubsubClient pubsubClient; /** The Comm Mgr service reference. */ private ICommManager commMgr; /** The executor service. */ private ExecutorService executorService = Executors.newSingleThreadExecutor(); @Autowired(required=true) CisCtxMonitor(IEventMgr eventMgr, ICommManager commMgr) { if (LOG.isInfoEnabled()) LOG.info(this.getClass() + " instantiated"); this.eventMgr = eventMgr; this.commMgr = commMgr; if (LOG.isInfoEnabled()) LOG.info("Registering for '" + Arrays.asList(EVENT_TYPES) + "' events"); this.eventMgr.subscribeInternalEvent(this, EVENT_TYPES, null); // TODO unsubscribe when stopped } /* * @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) { if (LOG.isInfoEnabled()) // TODO DEBUG LOG.info("Received internal " + event.geteventType() + " event: " + event); 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 CisCreationCtxHandler((Community) event.geteventInfo())); } else if (EventTypes.CIS_DELETION.equals(event.geteventType())) { this.executorService.execute( new CisRemovalCtxHandler((Community) event.geteventInfo())); } else { LOG.error("Could not handle internal event: " + "Unexpected event type " + event.geteventType()); return; } } /* * @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) { if (LOG.isInfoEnabled()) // TODO DEBUG LOG.info("Received CIS Activity Feed pubsub event: " + item); if (!(item instanceof MarshaledActivity)) { LOG.error("Could not handle CIS Activity Feed pubsub event: " + "Expected item of type " + MarshaledActivity.class.getName() + " but was " + ((item != null) ? item.getClass() : "null")); return; } final String cisIdStr = ((MarshaledActivity) item).getObject(); final String cssIdStr = ((MarshaledActivity) item).getActor(); final String verb = ((MarshaledActivity) item).getVerb(); if (verb.equals(VERB_CSS_JOINED)) { this.executorService.execute( new CisMemberJoinedCtxHandler(cisIdStr, cssIdStr)); } else if (verb.equals(VERB_CSS_LEFT)) { this.executorService.execute( new CisMemberLeftCtxHandler(cisIdStr, cssIdStr)); } else { if (LOG.isDebugEnabled()) LOG.debug("Ignoring CIS Activity Feed pubsub event with verb '" + verb + "'"); } } private class CisCreationCtxHandler implements Runnable { private final Community cis; private CisCreationCtxHandler(Community cis) { this.cis = cis; } /* * @see java.lang.Runnable#run() */ @Override public void run() { try { final String cisIdStr = this.cis.getCommunityJid(); final IIdentity cisId = commMgr.getIdManager().fromJid(cisIdStr); final CommunityCtxEntity cisEntity = ctxBroker.createCommunityEntity(cisId).get(); String value; // NAME value = this.cis.getCommunityName(); if (value != null && !value.isEmpty()) updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.NAME, value); // ABOUT value = this.cis.getDescription(); if (value != null && !value.isEmpty()) updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.ABOUT, value); // TODO add attribute types defined in privacy policy, // adding some attributes for testing purposes // INTERESTS updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.INTERESTS, null); // LOCATION SYMBOLIC updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.LOCATION_SYMBOLIC, null); // LOCATION COORDINATES updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.LOCATION_COORDINATES, null); // BOOKS updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.BOOKS, null); // MOVIES updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.MOVIES, null); //LANGUAGES updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.LANGUAGES , null); //FAVOURITE_QUOTES updateCtxAttribute(cisEntity.getId(), CtxAttributeTypes.FAVOURITE_QUOTES , null); final String cisOwnerIdStr = cis.getOwnerJid(); final IIdentity cisOwnerId = commMgr.getIdManager().fromJid(cisOwnerIdStr); final IndividualCtxEntity cisOwnerEntity = ctxBroker.retrieveIndividualEntity(cisOwnerId).get(); if (cisOwnerEntity == null) { LOG.error("Could not retrieve IndividualCtxEntity for CIS creator " + cisOwnerId); return; } if (LOG.isInfoEnabled()) LOG.info("Adding member '" + cisOwnerEntity.getId() + "' to community '" + 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 owning CSS ? // TODO administrating CSS ? // TODO membership criteria / bonds if (LOG.isInfoEnabled()) LOG.info("Subscribing for the ActivityFeed of CIS " + cisIdStr); pubsubClient.subscriberSubscribe(cisOwnerId, cisIdStr, CisCtxMonitor.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); } } } private class CisRemovalCtxHandler implements Runnable { @SuppressWarnings("unused") private final Community cis; private CisRemovalCtxHandler(Community cis) { this.cis = cis; } /* * @see java.lang.Runnable#run() */ @Override public void run() { // TODO Auto-generated method stub } } private class CisMemberJoinedCtxHandler implements Runnable { private final String cisIdStr; private final String cssIdStr; private CisMemberJoinedCtxHandler(String cisIdStr, String cssIdStr) { this.cisIdStr = cisIdStr; this.cssIdStr = cssIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { if (LOG.isInfoEnabled()) // TODO debug LOG.info("CSS '" + this.cssIdStr + "' joined CIS '" + this.cisIdStr + "'"); try { // Retrieve CommunityCtxEntity of CIS final IIdentity cisId = commMgr.getIdManager().fromJid(this.cisIdStr); final CtxEntityIdentifier cisEntityId = ctxBroker.retrieveCommunityEntityId(cisId).get(); if (cisEntityId == null) { LOG.error("Could not retrieve CommunityCtxEntity id for CIS '" + cisId + "'"); } final CommunityCtxEntity cisEntity = (CommunityCtxEntity) ctxBroker.retrieve(cisEntityId).get(); if (cisEntity == null) { LOG.error("Could not 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, cisId), cssId).get(); if (cssEntId == null) { LOG.error("Could not retrieve IndividualCtxEntity for new CIS member " + cisOwnerId); return; } if (LOG.isInfoEnabled()) LOG.info("Adding member '" + cssEntId + "' to community '" + 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("Could not instantiate IIdentity from String representation: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } private class CisMemberLeftCtxHandler implements Runnable { private final String cisIdStr; private final String cssIdStr; private CisMemberLeftCtxHandler(String cisIdStr, String cssIdStr) { this.cisIdStr = cisIdStr; this.cssIdStr = cssIdStr; } /* * @see java.lang.Runnable#run() */ @Override public void run() { if (LOG.isInfoEnabled()) // TODO debug LOG.info("CSS '" + this.cssIdStr + "' left CIS '" + this.cisIdStr + "'"); try { // Retrieve CommunityCtxEntity of CIS final IIdentity cisId = commMgr.getIdManager().fromJid(this.cisIdStr); final CtxEntityIdentifier cisEntityId = ctxBroker.retrieveCommunityEntityId(cisId).get(); if (cisEntityId == null) { LOG.error("Could not retrieve CommunityCtxEntity id for CIS '" + cisId + "'"); } final CommunityCtxEntity cisEntity = (CommunityCtxEntity) ctxBroker.retrieve(cisEntityId).get(); if (cisEntity == null) { LOG.error("Could not 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, cisId), cssId).get(); if (cssEntId == null) { LOG.error("Could not retrieve IndividualCtxEntity for 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("Could not instantiate IIdentity from String representation: " + ife.getLocalizedMessage(), ife); } catch (Exception e) { LOG.error("Failed to access context data: " + e.getLocalizedMessage(), e); } } } private void updateCtxAttribute(CtxEntityIdentifier ownerCtxId, String type, String value) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("Updating '" + type + "' of entity " + ownerCtxId + " to '" + value + "'"); /* final List<CtxIdentifier> ctxIds = this.ctxBroker.lookup(ownerCtxId, CtxModelType.ATTRIBUTE, type).get(); final CtxAttribute attr; if (!ctxIds.isEmpty()) attr = (CtxAttribute) this.ctxBroker.retrieve(ctxIds.get(0)); else*/ final CtxAttribute attr = this.ctxBroker.createAttribute(ownerCtxId, type).get(); attr.setStringValue(value); attr.setValueType(CtxAttributeValueType.STRING); attr.getQuality().setOriginType(CtxOriginType.MANUALLY_SET); this.ctxBroker.update(attr); } }