/**
* 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.css.impl;
import java.util.Arrays;
import java.util.List;
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.context.model.CtxAssociation;
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.CtxIdentifier;
import org.societies.api.context.model.CtxModelType;
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.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.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.cis.community.Community;
import org.societies.api.schema.cssmanagement.CssRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
/**
* This class is used to update the CSS owner context based on CSS record
* changes.
*
* @author <a href="mailto:nicolas.liampotis@cn.ntua.gr">Nicolas Liampotis</a> (ICCS)
* @since 0.4
*/
@Service
@Lazy(false)
public class CssCtxMonitor extends EventListener {
/** The logging facility. */
private static final Logger LOG = LoggerFactory.getLogger(CssCtxMonitor.class);
private static final String[] EVENT_TYPES = { EventTypes.CSS_RECORD_EVENT,
EventTypes.CSS_FRIENDED_EVENT, EventTypes.CIS_CREATION,
EventTypes.CIS_SUBS, EventTypes.CIS_UNSUBS };
/** The internal Context Broker service. */
@Autowired(required=true)
private ICtxBroker ctxBroker;
/** The Event Mgr service. */
private IEventMgr eventMgr;
/** The Comm Mgr service. */
private ICommManager commMgr;
/** The executor service. */
private ExecutorService executorService = Executors.newSingleThreadExecutor();
@Autowired(required=true)
CssCtxMonitor(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) {
if (LOG.isWarnEnabled())
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.isDebugEnabled())
LOG.debug("Received internal " + event.geteventType() + " event: " + event);
if (EventTypes.CSS_RECORD_EVENT.equals(event.geteventType())) {
if (!(event.geteventInfo() instanceof CssRecord)) {
LOG.error("Could not handle internal " + event.geteventType() + " event: "
+ "Expected event info of type " + CssRecord.class.getName()
+ " but was " + event.geteventInfo().getClass());
return;
}
final CssRecord cssRecord = (CssRecord) event.geteventInfo();
this.executorService.execute(new CssRecordUpdateHandler(cssRecord));
} else if (EventTypes.CSS_FRIENDED_EVENT.equals(event.geteventType())) {
if (event.geteventSource() == null || event.geteventSource().isEmpty()) {
LOG.error("Could not handle internal " + event.geteventType() + " event: "
+ "Expected non-empty event source "
+ " but was " + event.geteventSource());
return;
}
if (!(event.geteventInfo() instanceof String)) {
LOG.error("Could not handle internal " + event.geteventType() + " event: "
+ "Expected event info of type " + CssRecord.class.getName()
+ " but was " + event.geteventInfo().getClass());
return;
}
this.executorService.execute(new CssFriendedHandler(event.geteventSource(),
(String) event.geteventInfo()));
} else if (EventTypes.CIS_CREATION.equals(event.geteventType())
|| 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_CREATION.equals(event.geteventType())
|| EventTypes.CIS_SUBS.equals(event.geteventType()))
this.executorService.execute(new CssJoinedCisHandler(
event.geteventSource(), cisRecord.getCommunityJid()));
else //if (EventTypes.CIS_UNSUBS.equals(event.geteventType()))
this.executorService.execute(new CssLeftCisHandler(
event.geteventSource(), cisRecord.getCommunityJid()));
} else {
if (LOG.isWarnEnabled())
LOG.warn("Received unexpeted event of type '" + event.geteventType() + "'");
}
}
private class CssRecordUpdateHandler implements Runnable {
private final CssRecord cssRecord;
private CssRecordUpdateHandler(final CssRecord cssRecord) {
this.cssRecord = cssRecord;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (LOG.isInfoEnabled())
LOG.info("Updated CSS record: " + cssRecord);
final String cssIdStr = cssRecord.getCssIdentity();
try {
IIdentity cssId = commMgr.getIdManager().fromJid(cssIdStr);
CtxEntityIdentifier ownerCtxId =
ctxBroker.retrieveIndividualEntity(cssId).get().getId();
String value;
// NAME
value = cssRecord.getName();
if (value != null && !value.isEmpty())
updateCtxAttribute(ownerCtxId, CtxAttributeTypes.NAME, value);
// EMAIL
value = cssRecord.getEmailID();
if (value != null && !value.isEmpty())
updateCtxAttribute(ownerCtxId, CtxAttributeTypes.EMAIL, value);
// ADDRESS_HOME_CITY
value = cssRecord.getHomeLocation();
if (value != null && !value.isEmpty())
updateCtxAttribute(ownerCtxId, CtxAttributeTypes.ADDRESS_HOME_CITY, value);
} 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 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() {
if (LOG.isInfoEnabled())
LOG.info("CSS '" + myCssIdStr + "' friended '" + newFriendIdStr + "'");
try {
final IIdentity myCssId = commMgr.getIdManager().fromJid(myCssIdStr);
final IndividualCtxEntity myCssEnt =
ctxBroker.retrieveIndividualEntity(myCssId).get();
final IIdentity newFriendId = commMgr.getIdManager().fromJid(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();
else
isFriendsWithAssoc = (CtxAssociation) ctxBroker.retrieve(
myCssEnt.getAssociations(CtxAssociationTypes.IS_FRIENDS_WITH).iterator().next()).get();
isFriendsWithAssoc.setParentEntity(myCssEnt.getId());
isFriendsWithAssoc.addChildEntity(newFriendEntId);
ctxBroker.update(isFriendsWithAssoc);
} 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 CssJoinedCisHandler implements Runnable {
private final String myCssIdStr;
private final String cisIdStr;
private CssJoinedCisHandler(final String myCssIdStr, final String cisIdStr) {
this.myCssIdStr = myCssIdStr;
this.cisIdStr = cisIdStr;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (LOG.isInfoEnabled())
LOG.info("CSS '" + this.myCssIdStr + "' joined CIS '" + this.cisIdStr + "'");
try {
final IIdentity myCssId = commMgr.getIdManager().fromJid(
this.myCssIdStr);
final IndividualCtxEntity myCssEnt =
ctxBroker.retrieveIndividualEntity(myCssId).get();
final IIdentity cisId = commMgr.getIdManager().fromJid(this.cisIdStr);
///////////////////////////////////////////////////////////////
// The CommunityCtxEntity might not be available right after
// the CIS_CREATION event:
// (1) check if it can be retrieved, otherwise
// (2) wait for its creation
///////////////////////////////////////////////////////////////
// (1) check if the CommunityCtxEntity has already been created
CtxEntityIdentifier cisEntId =
ctxBroker.retrieveCommunityEntityId(
new Requestor(myCssId), cisId).get();
// (2) if not available, wait until it's created
// TODO find better way (event-based?)
if (cisEntId == null) {
if (LOG.isDebugEnabled())
LOG.debug("Waiting for the CommunityCtxEntity of CIS '"
+ cisId + "' to be created");
int retries = 10;
while (retries > 0) {
Thread.sleep(500);
cisEntId = ctxBroker.retrieveCommunityEntityId(
new Requestor(myCssId), cisId).get();
retries--;
if (cisEntId != null)
break;
}
}
if (cisEntId == null) {
LOG.error("CommunityCtxEntity of CIS '" + cisId + "' is not available!!");
return;
}
final CtxAssociation isMemberOfAssoc;
if (myCssEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).isEmpty())
isMemberOfAssoc = ctxBroker.createAssociation(
new Requestor(myCssId), myCssId, CtxAssociationTypes.IS_MEMBER_OF).get();
else
isMemberOfAssoc = (CtxAssociation) ctxBroker.retrieve(
myCssEnt.getAssociations(CtxAssociationTypes.IS_MEMBER_OF).iterator().next()).get();
isMemberOfAssoc.setParentEntity(myCssEnt.getId());
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 CssLeftCisHandler implements Runnable {
private final String cssIdStr;
private final String cisIdStr;
private CssLeftCisHandler(final String cssIdStr, final String cisIdStr) {
this.cssIdStr = cssIdStr;
this.cisIdStr = cisIdStr;
}
/*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (LOG.isInfoEnabled())
LOG.info("CSS '" + this.cssIdStr + "' left CIS '" + this.cisIdStr + "'");
try {
final IIdentity myCssId = commMgr.getIdManager().fromJid(
this.cssIdStr);
final IndividualCtxEntity myCssEnt =
ctxBroker.retrieveIndividualEntity(myCssId).get();
final IIdentity cisId = commMgr.getIdManager().fromJid(cisIdStr);
final CtxEntityIdentifier cisEntId =
ctxBroker.retrieveCommunityEntityId(
new Requestor(myCssId), cisId).get();
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.setParentEntity(myCssEnt.getId());
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 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)).get();
else
attr = this.ctxBroker.createAttribute(ownerCtxId, type).get();
attr.setStringValue(value);
attr.setValueType(CtxAttributeValueType.STRING);
attr.getQuality().setOriginType(CtxOriginType.MANUALLY_SET);
this.ctxBroker.update(attr);
}
}