package org.societies.comm.xmpp.client.impl;
import static android.content.Context.BIND_AUTO_CREATE;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.convert.AnnotationStrategy;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.strategy.Strategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.societies.api.comm.xmpp.exceptions.CommunicationException;
import org.societies.api.comm.xmpp.exceptions.XMPPError;
import org.societies.api.comm.xmpp.pubsub.Affiliation;
import org.societies.api.comm.xmpp.pubsub.PubsubClient;
import org.societies.api.comm.xmpp.pubsub.Subscriber;
import org.societies.api.comm.xmpp.pubsub.Subscription;
import org.societies.api.comm.xmpp.pubsub.SubscriptionState;
import org.societies.api.identity.IIdentity;
import org.societies.api.identity.InvalidFormatException;
import org.societies.comm.android.ipc.IMethodInvocation;
import org.societies.comm.android.ipc.MethodInvocationServiceConnection;
import org.societies.identity.IdentityManagerImpl;
import org.societies.pubsub.interfaces.ISubscriber;
import org.societies.pubsub.interfaces.Pubsub;
import org.societies.pubsub.interfaces.SubscriptionParcelable;
import org.w3c.dom.Element;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
public class PubsubClientAndroid implements PubsubClient {
private static final Logger LOG = LoggerFactory.getLogger(PubsubClientAndroid.class);
private static final ComponentName serviceCN = new ComponentName("org.societies.comms", "org.societies.comms.PubsubService"); // TODO
private MethodInvocationServiceConnection<Pubsub> miServiceConnection;
private Serializer serializer;
private Map<String,Class<?>> elementToClass;
private Map<Subscriber, SubscriberAdapter> subscribersMap = new HashMap<Subscriber, SubscriberAdapter>();
public PubsubClientAndroid(Context androidContext) {
Intent intent = new Intent();
intent.setComponent(serviceCN);
miServiceConnection = new MethodInvocationServiceConnection<Pubsub>(intent, androidContext, BIND_AUTO_CREATE, Pubsub.class);
elementToClass = new HashMap<String, Class<?>>();
Strategy strategy = new AnnotationStrategy();
serializer = new Persister(strategy);
}
public void ownerCreate(IIdentity pubsubService, final String node) throws XMPPError, CommunicationException {
final String pubsubServiceJid = pubsubService.getJid();
invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
pubsub.ownerCreate(pubsubServiceJid, node);
return null;
}
});
}
public List<String> discoItems(final IIdentity pubsubService, final String node)
throws XMPPError, CommunicationException {
return (List<String>)invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
return pubsub.discoItems(pubsubService.getJid(), node);
}
});
}
public Subscription subscriberSubscribe(IIdentity pubsubService,
final String node, final Subscriber subscriber) throws XMPPError,
CommunicationException {
final String pubsubServiceJid = pubsubService.getJid();
final SubscriberAdapter subscriberAdapter = new SubscriberAdapter(subscriber);
Subscription subscription = ((SubscriptionParcelable)invokeRemoteMethodAndKeepBound(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
return pubsub.subscriberSubscribe(pubsubServiceJid, node, subscriberAdapter);
}
})).subscription();
subscribersMap.put(subscriber, subscriberAdapter);
return subscription;
}
public void subscriberUnsubscribe(final IIdentity pubsubService, final String node,
Subscriber subscriber) throws XMPPError, CommunicationException {
final String pubsubServiceJid = pubsubService.getJid();
final SubscriberAdapter subscriberAdapter = subscribersMap.get(subscriber);
invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
pubsub.subscriberUnsubscribe(pubsubServiceJid, node, subscriberAdapter);
return null;
}
});
miServiceConnection.unbind();
}
public List<Element> subscriberRetrieveLast(IIdentity pubsubService,
String node, String subId) throws XMPPError, CommunicationException {
// TODO Auto-generated method stub
return null;
}
public List<Element> subscriberRetrieveSpecific(IIdentity pubsubService,
String node, String subId, List<String> itemIdList)
throws XMPPError, CommunicationException {
// TODO Auto-generated method stub
return null;
}
public String publisherPublish(IIdentity pubsubService, final String node,
final String itemId, Object item) throws XMPPError,
CommunicationException {
final String pubsubServiceJid = pubsubService.getJid();
final String itemXml;
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
serializer.write(item, os);
itemXml = os.toString();
} catch (TransformerException e) {
throw new CommunicationException(e.getMessage(), e);
} catch (ParserConfigurationException e) {
throw new CommunicationException("ParserConfigurationException while marshalling item to publish", e);
} catch (Exception e) {
throw new CommunicationException("Exception while marshalling item to publish", e);
}
return (String)invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
return pubsub.publisherPublish(pubsubServiceJid, node, itemId, itemXml);
}
});
}
public void publisherDelete(final IIdentity pubsubService, final String node, final String itemId) throws XMPPError, CommunicationException {
invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
pubsub.publisherDelete(pubsubService.getJid(), node, itemId);
return null;
}
});
}
public void ownerDelete(IIdentity pubsubService, final String node)
throws XMPPError, CommunicationException {
final String pubsubServiceJid = pubsubService.getJid();
invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
pubsub.ownerDelete(pubsubServiceJid, node);
return null;
}
});
}
public void ownerPurgeItems(final IIdentity pubsubService, final String node)
throws XMPPError, CommunicationException {
invokeRemoteMethod(new IMethodInvocation<Pubsub>() {
public Object invoke(Pubsub pubsub) throws Throwable {
pubsub.ownerPurgeItems(pubsubService.getJid(), node);
return null;
}
});
}
public Map<IIdentity, SubscriptionState> ownerGetSubscriptions(
IIdentity pubsubService, String node) throws XMPPError,
CommunicationException {
// TODO Auto-generated method stub
return null;
}
public Map<IIdentity, Affiliation> ownerGetAffiliations(
IIdentity pubsubService, String node) throws XMPPError,
CommunicationException {
// TODO Auto-generated method stub
return null;
}
public void ownerSetSubscriptions(IIdentity pubsubService, String node,
Map<IIdentity, SubscriptionState> subscriptions) throws XMPPError,
CommunicationException {
// TODO Auto-generated method stub
}
public void ownerSetAffiliations(IIdentity pubsubService, String node,
Map<IIdentity, Affiliation> affiliations) throws XMPPError,
CommunicationException {
// TODO Auto-generated method stub
}
public synchronized void addSimpleClasses(List<String> classList) throws ClassNotFoundException {
for (String c : classList) {
Class<?> clazz = Class.forName(c);
Root rootAnnotation = clazz.getAnnotation(Root.class);
Namespace namespaceAnnotation = clazz.getAnnotation(Namespace.class);
if (rootAnnotation!=null && namespaceAnnotation!=null) {
elementToClass.put("{"+namespaceAnnotation.reference()+"}"+rootAnnotation.name(),clazz);
}
}
}
private Object invokeRemoteMethod(IMethodInvocation<Pubsub> methodInvocation) throws XMPPError, CommunicationException {
try {
return miServiceConnection.invoke(methodInvocation);
} catch (Throwable e) {
if(e instanceof XMPPError)
throw (XMPPError)e;
if(e instanceof CommunicationException)
throw (CommunicationException)e;
throw new CommunicationException(e.getMessage(), e);
}
}
private Object invokeRemoteMethodAndKeepBound(IMethodInvocation<Pubsub> methodInvocation) throws XMPPError, CommunicationException {
try {
return miServiceConnection.invokeAndKeepBound(methodInvocation);
} catch (Throwable e) {
if(e instanceof XMPPError)
throw (XMPPError)e;
if(e instanceof CommunicationException)
throw (CommunicationException)e;
throw new CommunicationException(e.getMessage(), e);
}
}
private class SubscriberAdapter implements ISubscriber {
private Subscriber subscriber;
public SubscriberAdapter(Subscriber subscriber) {
this.subscriber = subscriber;
}
public void pubsubEvent(String pubsubService, String node, String itemId, String item) {
try {
IIdentity pubsubServiceIdentity = IdentityManagerImpl.staticfromJid(pubsubService);
Class<?> c = elementToClass.get(getElementIdentifier(item));
Object bean = serializer.read(c, new ByteArrayInputStream(item.getBytes()));
subscriber.pubsubEvent(pubsubServiceIdentity, node, itemId, bean);
} catch(InvalidFormatException e) {
LOG.error("InvalidFormatException parsing pubsub service JID.", e);
} catch (Exception e) {
LOG.error("Exception while unmarshalling pubsub event payload",e);
}
}
private String getElementIdentifier(String item) {
String trimmedItem = item.trim();
String elementName = trimmedItem.substring(1, trimmedItem.indexOf(" "));
String nsStr = trimmedItem.substring(trimmedItem.indexOf("xmlns=")+7);
int endIndex = nsStr.indexOf("\"");
if (endIndex<0)
endIndex = nsStr.indexOf("'");
nsStr = nsStr.substring(0,endIndex);
return "{"+nsStr+"}"+elementName;
}
}
public void addJaxbPackages(List<String> packageList) throws JAXBException {
// TODO DEPRECATED
}
}