package org.societies.comm.xmpp.xc.impl;
import org.jivesoftware.whack.ExternalComponentManager;
import org.societies.api.comm.xmpp.datatypes.Stanza;
import org.societies.api.comm.xmpp.datatypes.XMPPNode;
import org.societies.api.comm.xmpp.exceptions.CommunicationException;
import org.societies.api.comm.xmpp.exceptions.XMPPError;
import org.societies.api.comm.xmpp.interfaces.ICommCallback;
import org.societies.api.comm.xmpp.interfaces.ICommManager;
import org.societies.api.comm.xmpp.interfaces.IFeatureServer;
import org.societies.api.identity.*;
import org.societies.api.internal.comm.ICommManagerController;
import org.societies.api.internal.privacytrust.privacyprotection.model.privacyassessment.IPrivacyLogAppender;
import org.societies.identity.IdentityManagerImpl;
import org.springframework.osgi.service.ServiceUnavailableException;
import org.xmpp.component.AbstractComponent;
import org.xmpp.component.ComponentException;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Message;
import org.xmpp.packet.Message.Type;
import org.xmpp.packet.Presence;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class XCCommunicationMgr extends AbstractComponent implements ICommManagerController, ICommManager {
private final CommManagerHelper helper;
private String host;
private String subDomain;
private String secretKey;
private String daNode;
private ExternalComponentManager manager;
private IIdentity thisIdentity;
private IIdentityManager idm;
private Set<INetworkNode> otherNodes;
private IPrivacyLogAppender privacyLog;
private boolean privacyLogEnabled = false;
public XCCommunicationMgr(String host, String subDomain,
String secretKey, String daNode) {
this.helper = new CommManagerHelper();
this.host = host;
this.subDomain = subDomain;
this.secretKey = secretKey;
this.daNode = daNode;
otherNodes = new HashSet<INetworkNode>();
}
@Override
public INetworkNode login(String identifier, String domain, String password) {
this.host = domain;
this.subDomain = identifier + "." + domain;
this.secretKey = password;
initWhackCommManager();
if (idm != null)
return idm.getThisNetworkNode();
else
return null;
}
@Override
public INetworkNode loginFromConfig() {
initWhackCommManager();
if (idm != null)
return idm.getThisNetworkNode();
else
return null;
}
@Override
public boolean logout() {
return UnRegisterCommManager();
}
private void initWhackCommManager() {
manager = new ExternalComponentManager(host);
manager.setSecretKey(subDomain, secretKey);
log.info("Connecting...");
try {
manager.addComponent(subDomain, this);
idm = new IdentityManagerImpl(subDomain, daNode);
thisIdentity = idm.getThisNetworkNode();
if (thisIdentity.getType() == IdentityType.CSS || thisIdentity.getType() == IdentityType.CSS_RICH)
probePresence();
log.info("Connected " + thisIdentity.getType().toString() + " '" + subDomain + "' to domain '" + host + "'!");
} catch (ComponentException e) {
log.warn("Could not connect to '" + host + "' as '" + subDomain + "': " + e.getMessage());
e.printStackTrace();
} catch (InvalidFormatException e) {
log.warn("Could not connect to '" + host + "' as '" + subDomain + "': " + e.getMessage());
e.printStackTrace();
}
}
private void probePresence() {
Presence subscribe = new Presence();
String bareJid = thisIdentity.getIdentifier() + "@" + thisIdentity.getDomain();
subscribe.setFrom(subDomain);
subscribe.setTo(bareJid);
subscribe.setType(org.xmpp.packet.Presence.Type.subscribe);
log.info("Sending presence subscribe: " + subscribe.toXML());
this.send(subscribe);
Presence probe = new Presence();
probe.setFrom(subDomain);
probe.setTo(bareJid);
probe.setType(org.xmpp.packet.Presence.Type.probe);
log.info("Sending presence probe: " + probe.toXML());
this.send(probe);
}
/**
* Unregisters the XC Manager as an external component from the XMPP Server
*/
@Override
public boolean UnRegisterCommManager() {
try {
manager.removeComponent(subDomain);
log.info("'" + subDomain + "' disconnected!");
} catch (ComponentException e) {
e.printStackTrace();
return false;
}
return true;
}
/*
* Implementation of AbstractComponent methods
*/
@Override
public String getDescription() {
return "this external component acts as a dispatcher to "
+ "facilitate the communication among Societies components";
}
@Override
public String getName() {
return "Societies Communication Manager";
}
@Override
protected String[] discoInfoFeatureNamespaces() {
return helper.getSupportedNamespaces();
}
@Override
protected IQ handleDiscoItems(IQ iq) {
log.info("IQ disco#items received: " + iq.toXML());
IQ response = helper.handleDiscoItems(iq);
response.setFrom(thisIdentity.getJid());
log.info("Returning disco#items response: " + response.toXML());
return response;
}
@Override
protected IQ handleIQGet(IQ iq) {
if (log.isDebugEnabled())
log.debug("IQ GET received: " + iq.toXML());
else if (log.isDebugEnabled()) {
String msg = "IQ GET received: id=%s node=%s";
log.debug(String.format(msg,
iq.getID(),
iq.getChildElement() != null ? iq.getChildElement().getName() : "null"
));
}
IQ response = helper.dispatchIQ(iq);
response.setFrom(thisIdentity.getJid());
// TODO mitja
if (log.isDebugEnabled())
log.debug("Sending IQ response: " + response.toXML());
return response;
}
@Override
protected IQ handleIQSet(IQ iq) throws Exception {
return handleIQGet(iq); // TODO
}
@Override
protected void handleIQResult(IQ iq) {
if (log.isDebugEnabled())
log.debug("IQ Result received: " + iq.toXML());
else if (log.isDebugEnabled()) {
String msg = "IQ Result received: id=%s node=%s";
log.debug(String.format(msg,
iq.getID(),
iq.getChildElement() != null ? iq.getChildElement().getName() : "null"
));
}
helper.dispatchIQResult(iq);
}
@Override
protected void handleIQError(IQ iq) {
if (log.isDebugEnabled())
log.debug("IQ Error: " + iq.toXML());
helper.dispatchIQError(iq);
}
@Override
protected void handleMessage(Message message) {
if (log.isDebugEnabled())
log.debug("Message received: " + message.toXML());
helper.dispatchMessage(message);
}
@Override
protected void handlePresence(Presence presence) {
if (log.isDebugEnabled())
log.debug("Received presence: " + presence.toXML());
try {
IIdentity nodeIdentity = idm.fromJid(presence.getFrom().toString());
if (nodeIdentity instanceof INetworkNode) {
INetworkNode node = (INetworkNode) nodeIdentity;
if (presence.getType() == null) {
otherNodes.add(node);
} else if (presence.getType().equals(Presence.Type.unavailable)) {
otherNodes.remove(node);
}
}
} catch (InvalidFormatException e) {
log.warn("Error handling presence", e);
e.printStackTrace();
}
log.info("OtherNodes: " + Arrays.toString(otherNodes.toArray()));
}
/*
* Implementation of CommunicationManager methods
*/
@Override
public void register(IFeatureServer fs) throws CommunicationException {
helper.register(fs);
}
@Override
public void register(ICommCallback messageCallback) throws CommunicationException {
helper.register(messageCallback);
}
private void checkConnectivity() throws CommunicationException {
if (idm != null)
return;
initWhackCommManager();
if (idm == null) {
boolean hostReachable = false;
try {
for (InetAddress addr : InetAddress.getAllByName(host)) {
hostReachable = addr.isReachable(4000);
}
} catch (UnknownHostException e) {
log.error("UnknownHostException while resolving XMPP Server host '" + host + "'", e);
} catch (IOException e) {
log.error("IOException while pinging XMPP Server host '" + host + "'", e);
}
if (hostReachable)
throw new CommunicationException("CommunicationManager could not connect to XMPP server at '" + host + "' for subDomain '" + subDomain + "' with secretKey '" + secretKey + "'");
else
throw new CommunicationException("CommunicationManager could not find an XMPP server at '" + host + "'");
}
}
@Override
public void sendMessage(Stanza stanza, String type, Object payload)
throws CommunicationException {
checkConnectivity();
stanza.setFrom(thisIdentity);
Type mType = Message.Type.valueOf(type);
Message m = helper.sendMessage(stanza, mType, payload);
if (log.isDebugEnabled())
log.debug("Sending message: " + m.toXML());
privacyLog(stanza, payload);
this.send(m);
}
@Override
public void sendMessage(Stanza stanza, Object payload)
throws CommunicationException {
checkConnectivity();
stanza.setFrom(thisIdentity);
Message m = helper.sendMessage(stanza, null, payload);
if (log.isDebugEnabled())
log.debug("Sending message: " + m.toXML());
privacyLog(stanza, payload);
this.send(m);
}
@Override
public void sendIQGet(Stanza stanza, Object payload, ICommCallback callback)
throws CommunicationException {
checkConnectivity();
stanza.setFrom(thisIdentity);
IQ iq = helper.sendIQ(stanza, IQ.Type.get, payload, callback);
if (log.isDebugEnabled())
log.debug("Sending IQ: " + iq.toXML());
else if (log.isDebugEnabled()) {
String msg = "Sending IQ: id=%s node=%s";
log.debug(String.format(msg,
iq.getID(),
iq.getChildElement() != null ? iq.getChildElement().getName() : "null"
));
}
privacyLog(stanza, payload);
this.send(iq);
}
@Override
public void sendIQSet(Stanza stanza, Object payload, ICommCallback callback)
throws CommunicationException {
checkConnectivity();
stanza.setFrom(thisIdentity);
IQ iq = helper.sendIQ(stanza, IQ.Type.set, payload, callback);
if (log.isDebugEnabled())
log.debug("Sending IQ: " + iq.toXML());
else if (log.isDebugEnabled()) {
String msg = "Sending IQ: id=%s node=%s";
log.debug(String.format(msg,
iq.getID(),
iq.getChildElement() != null ? iq.getChildElement().getName() : "null"
));
}
privacyLog(stanza, payload);
this.send(iq);
}
@Override
public void addRootNode(XMPPNode newNode) {
helper.addRootNode(newNode);
}
@Override
public void removeRootNode(XMPPNode node) {
helper.removeRootNode(node);
}
@Override
public String getInfo(IIdentity entity, String node, ICommCallback callback) throws CommunicationException {
checkConnectivity();
IQ iq = helper.buildInfoIq(entity, node, callback);
iq.setFrom(thisIdentity.getJid());
this.send(iq);
return iq.getID();
}
@Override
public String getItems(IIdentity entity, String node, ICommCallback callback) throws CommunicationException {
checkConnectivity();
IQ iq = helper.buildItemsIq(entity, node, callback);
iq.setFrom(thisIdentity.getJid());
this.send(iq);
return iq.getID();
}
@Override
public IIdentityManager getIdManager() {
return idm;
}
// Controll methods
@Override
public INetworkNode newMainIdentity(String identifier, String domain,
String password) throws XMPPError {
// TODO Auto-generated method stub
// TODO when identity is created it needs to add the component to the roster for presence
return null;
}
@Override
public boolean destroyMainIdentity() {
// TODO Auto-generated method stub
return false;
}
@Override
public Set<INetworkNode> getOtherNodes() {
return otherNodes;
}
@Override
public boolean isConnected() {
if (idm != null && idm.getThisNetworkNode() != null)
return true;
else
return false;
}
public IPrivacyLogAppender getPrivacyLog() {
return privacyLog;
}
public void setPrivacyLog(IPrivacyLogAppender privacyLog) {
if (privacyLog != null) {
this.privacyLog = privacyLog;
privacyLogEnabled = true;
}
}
private void privacyLog(Stanza stanza, Object payload) {
if (privacyLogEnabled) {
try {
privacyLog.logCommsFw(stanza.getFrom(), stanza.getTo(), payload);
} catch (ServiceUnavailableException e) {
log.warn("Privacy Logger unavailable. Resuming without Privacy Logger...", e);
privacyLogEnabled = false;
}
}
}
}