package net.windwards.dnsfrontend.backend;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.windwards.dnsfrontend.api.Backend;
import net.windwards.dnsfrontend.api.BackendCommunicationException;
import net.windwards.dnsfrontend.api.Configuration;
import net.windwards.dnsfrontend.api.NoSuchDomainException;
import net.windwards.dnsfrontend.api.Protocol;
import net.windwards.dnsfrontend.dialog.ProtocolCodingException;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;
public class JGroupsBackend extends ReceiverAdapter implements Backend {
Logger logger = LoggerFactory.getLogger(JGroupsBackend.class);
private Ehcache cache;
private JChannel channel;
private Configuration configuration;
private Protocol protocol = new JSONBackendProtocol();
private String clusterName = "dns-frontend";
@Override
public void setCache(Ehcache cache) {
this.cache = cache;
}
@Override
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
public void setClusterName(String clusterName) { this.clusterName = clusterName; }
@Override
public void start() {
if(this.channel != null) {
logger.debug("Already started");
return;
}
try {
this.channel = new JChannel();
this.channel.setReceiver(this);
this.channel.connect(clusterName);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.warn("Backend started");
}
@Override
public void stop() {
this.channel.disconnect();
}
@Override
public String makeKey(Record record) {
String type = Type.string(record.getType());
return type + ":" + record.getName().toString();
}
@Override
public void receive(Message message) {
if (message.getSrc().equals(this.channel.getAddress())) {
// Ignore our own messages
return;
}
Record record;
try {
record = protocol.decode(message.getBuffer(), this.configuration);
} catch (Exception e) {
logger.warn("Failed to decode message", e);
return;
}
if(record == null) {
logger.debug("Ignoring message [me={}, message={}]", this.channel.getName(), message);
return;
}
logger.debug("Update from authority [me={}, update={}]", this.channel.getName(), record);
Element lmnt = new Element(this.makeKey(record), record);
lmnt.setTimeToLive((int) record.getTTL());
this.cache.put(lmnt);
}
@Override
public void notify(Record question)
throws BackendCommunicationException, ProtocolCodingException, NoSuchDomainException {
byte[] data = this.protocol.encode(question, this.configuration);
Message message = new Message(null, data);
try {
logger.debug("Request to authority [me={}, query={}]", this.channel.getName(), question);
this.channel.send(message);
} catch (Exception e) {
logger.warn("Failed to communicate with authority", e);
// Jgroups goes "throws Exception" >[
throw new BackendCommunicationException(e);
}
}
}