package com.workshare.msnos.core;
import static com.workshare.msnos.core.Message.Type.DSC;
import static com.workshare.msnos.core.Message.Type.PIN;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.workshare.msnos.core.Cloud.Listener;
import com.workshare.msnos.core.MsnosException.Code;
import com.workshare.msnos.core.payloads.Presence;
import com.workshare.msnos.core.protocols.ip.Endpoint;
import com.workshare.msnos.soup.json.Json;
import com.workshare.msnos.soup.time.SystemTime;
public class LocalAgent implements Agent {
private static Logger log = LoggerFactory.getLogger(LocalAgent.class);
private final Iden iden;
private final Set<Endpoint> endpoints;
transient private Listener listener;
transient private Cloud cloud;
transient private Ring ring;
public LocalAgent(UUID uuid) {
this(new Iden(Iden.Type.AGT, uuid), Collections.<Endpoint>emptySet());
}
LocalAgent(Iden iden, Set<Endpoint> endpoints) {
this.iden = iden;
this.endpoints = new CopyOnWriteArraySet<Endpoint>(endpoints);
}
@Override
public Set<Endpoint> getEndpoints() {
return Collections.unmodifiableSet(endpoints);
}
@Override
public Iden getIden() {
return iden;
}
@Override
public synchronized Cloud getCloud() {
return cloud;
}
@Override
public void touch() {
}
@Override
public long getAccessTime() {
return SystemTime.asMillis();
}
public Ring getRing() {
return ring;
}
public synchronized LocalAgent join(Cloud cloud) throws MsnosException {
if (this.cloud != null)
throw new MsnosException("The same agent cannot join different clouds!", Code.JOIN_FAILED);
this.endpoints.addAll(Gateways.allPublicEndpoints());
this.ring = cloud.getRing();
this.cloud = cloud;
cloud.onJoin(this);
log.debug("Joined: {} as Agent: {}", getCloud(), this);
listener = cloud.addListener(new Listener() {
@Override
public void onMessage(Message message) {
log.debug("Message received.");
process(message);
}
});
return this;
}
public synchronized void leave() throws MsnosException {
if (this.cloud == null) {
throw new MsnosException("Cannot leave a cloud I never joined!", MsnosException.Code.INVALID_STATE);
}
log.debug("Leaving cloud {}", cloud);
cloud.onLeave(this);
cloud.removeListener(listener);
cloud = null;
log.debug("So long {}", cloud);
}
public Receipt send(Message message) throws MsnosException {
return cloud.send(message);
}
void registerEndpoint(Endpoint newEndpoint) {
endpoints.add(newEndpoint);
}
private void process(Message message) {
if (isDiscovery(message)) processDiscovery(message);
else if (isPing(message)) processPing(message);
}
private boolean isPing(Message message) {
return message.getType() == PIN;
}
private boolean isDiscovery(Message message) {
return message.getType() == DSC;
}
private void processDiscovery(Message message) {
log.debug("Processing discovery: {}", message);
try {
send(new MessageBuilder(Message.Type.PRS, this, cloud).with(new Presence(true, this)).make());
} catch (MsnosException e) {
log.warn("Could not send message. ", e);
}
}
private void processPing(Message message) {
log.debug("Processing ping: {} ", message);
try {
send(new MessageBuilder(Message.Type.PON, this, cloud).make());
} catch (MsnosException e) {
log.warn("Could not send message. ", e);
}
}
@Override
public String toString() {
return Json.toJsonString(this);
}
@Override
public boolean equals(Object other) {
try {
return this.iden.equals(((Agent) (other)).getIden());
} catch (Exception any) {
return false;
}
}
@Override
public int hashCode() {
return iden.hashCode();
}
}