package se.unlogic.eagledns.zoneproviders.file;
import org.apache.log4j.Logger;
import org.xbill.DNS.Name;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Zone;
import se.unlogic.eagledns.SecondaryZone;
import se.unlogic.eagledns.ZoneChangeCallback;
import se.unlogic.eagledns.ZoneProvider;
import se.unlogic.eagledns.ZoneProviderUpdatable;
import se.unlogic.standardutils.numbers.NumberUtils;
import se.unlogic.standardutils.timer.RunnableTimerTask;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
/**
* This class loads primary zones from zone files in the file system.
* The zone files have to formated accordingly to RFC 1035 (http://tools.ietf.org/html/rfc1035)
* and RFC 1034 (http://tools.ietf.org/html/rfc1034).
*
* @author Robert "Unlogic" Olofsson
* @author Michael Neale, Red Hat (JBoss division)
*
*/
public class FileZoneProvider implements ZoneProvider, ZoneProviderUpdatable, Runnable {
private final Logger log = Logger.getLogger(this.getClass());
private String name;
private String zoneFileDirectory;
private boolean autoReloadZones;
private Long pollingInterval;
private Map<String, Long> lastFileList = new HashMap<String, Long>();
private ZoneChangeCallback changeCallback;
private Timer watcher;
public void init(String name) {
this.name = name;
if(autoReloadZones && pollingInterval != null){
watcher = new Timer(true);
watcher.schedule(new RunnableTimerTask(this), 5000, pollingInterval);
}
}
public void run() {
if (changeCallback != null && hasDirectoryChanged()){
log.info("Changes in directory " + zoneFileDirectory + " detected");
changeCallback.zoneDataChanged();
}
}
private boolean hasDirectoryChanged() {
File folder = new File(this.zoneFileDirectory);
File[] files = folder.listFiles();
if (files.length != lastFileList.size()) {
return true;
}
for (File f : folder.listFiles()) {
if (!lastFileList.containsKey(f.getName())) {
return true;
}
if (f.lastModified() > lastFileList.get(f.getName())) {
return true;
}
}
return false;
}
/** Refresh our list of zone files for watching */
private void updateZoneFiles(File[] files) {
lastFileList = new HashMap<String, Long>();
for (File f : files) {
lastFileList.put(f.getName(), f.lastModified());
}
}
public Collection<Zone> getPrimaryZones() {
File zoneDir = new File(this.zoneFileDirectory);
if(!zoneDir.exists() || !zoneDir.isDirectory()){
log.error("Zone file directory specified for FileZoneProvider " + name + " does not exist!");
return null;
}else if(!zoneDir.canRead()){
log.error("Zone file directory specified for FileZoneProvider " + name + " is not readable!");
return null;
}
File[] files = zoneDir.listFiles();
updateZoneFiles(files);
if(files == null || files.length == 0){
log.info("No zone files found for FileZoneProvider " + name + " in directory " + zoneDir.getPath());
return null;
}
ArrayList<Zone> zones = new ArrayList<Zone>(files.length);
for(File zoneFile : files){
if(!zoneFile.canRead()){
log.error("FileZoneProvider " + name + " unable to access zone file " + zoneFile );
continue;
}
Name origin;
try {
origin = Name.fromString(zoneFile.getName(), Name.root);
Zone zone = new Zone(origin, zoneFile.getPath());
log.debug("FileZoneProvider " + name + " successfully parsed zone file " + zoneFile.getName());
zones.add(zone);
} catch (TextParseException e) {
log.error("FileZoneProvider " + name + " unable to parse zone file " + zoneFile.getName(),e);
} catch (IOException e) {
log.error("Unable to parse zone file " + zoneFile + " in FileZoneProvider " + name,e);
}
}
if(!zones.isEmpty()){
return zones;
}
return null;
}
public void unload() {
}
public String getZoneFileDirectory() {
return zoneFileDirectory;
}
public void setZoneFileDirectory(String zoneFileDirectory) {
this.zoneFileDirectory = zoneFileDirectory;
log.debug("zoneFileDirectory set to " + zoneFileDirectory);
}
public Collection<SecondaryZone> getSecondaryZones() {
//Not supported
return null;
}
public void zoneUpdated(SecondaryZone secondaryZone) {
//Not supported
}
public void zoneChecked(SecondaryZone secondaryZone) {
//Not supported
}
public void setChangeListener(ZoneChangeCallback ev) {
this.changeCallback = ev;
}
public void setAutoReloadZones(String autoReloadZones) {
this.autoReloadZones = Boolean.parseBoolean(autoReloadZones);
}
public void setPollingInterval(String pollingInterval) {
Long value = NumberUtils.toLong(pollingInterval);
if(value != null && value > 0){
this.pollingInterval = value;
}else{
log.warn("Invalid polling interval specified: " + pollingInterval);
}
}
}