package org.archive.format.gzip.zipnum;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.archive.util.ArchiveUtils;
import org.archive.util.GeneralURIStreamFactory;
import org.archive.util.binsearch.SeekableLineReaderFactory;
import org.archive.util.binsearch.SeekableLineReaderIterator;
public class LocationUpdater implements Runnable {
final static Logger LOGGER = Logger.getLogger(LocationUpdater.class.getName());
protected HashMap<String, String[]> locMap = null;
protected SeekableLineReaderFactory locReaderFactory = null;
protected String locUri;
protected long lastModTime = 0;
protected int checkInterval = 30000;
protected Thread updaterThread;
public final static String EARLIEST_TIMESTAMP = "_EARLIEST";
public final static String LATEST_TIMESTAMP = "_LATEST";
public final static String OFF = "OFF";
protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
protected Date startDate, endDate;
protected ZipNumBlockLoader blockLoader = null;
protected Date newStartDate, newEndDate;
protected boolean newIsDisabled = false;
protected boolean isDisabled = false;
public LocationUpdater(String locUri, ZipNumBlockLoader blockLoader) throws IOException
{
this.locUri = locUri;
this.blockLoader = blockLoader;
locMap = new HashMap<String, String[]>();
locReaderFactory = GeneralURIStreamFactory.createSeekableStreamFactory(locUri, false);
lastModTime = locReaderFactory.getModTime();
loadPartLocations(locMap);
isDisabled = newIsDisabled;
startDate = newStartDate;
endDate = newEndDate;
if (checkInterval > 0) {
updaterThread = new Thread(this, "LocationUpdaterThread");
updaterThread.start();
}
}
protected void syncLoad(long newModTime)
{
HashMap<String, String[]> destMap = new HashMap<String, String[]>();
try {
loadPartLocations(destMap);
} catch (IOException e) {
LOGGER.warning(e.toString());
return;
}
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("*** Location Update: " + locUri);
}
ArrayList<String[]> filesToClose = new ArrayList<String[]>();
synchronized (this) {
for (Entry<String, String[]> files : destMap.entrySet()) {
String[] existingFiles = locMap.get(files.getKey());
if ((existingFiles != null) && !Arrays.equals(existingFiles, files.getValue())) {
filesToClose.add(existingFiles);
}
locMap.put(files.getKey(), files.getValue());
}
//locMap.putAll(destMap);
startDate = newStartDate;
endDate = newEndDate;
isDisabled = newIsDisabled;
}
closeExistingFiles(filesToClose);
lastModTime = newModTime;
}
private void closeExistingFiles(ArrayList<String[]> filesToClose) {
for (String[] files : filesToClose) {
for (String file : files) {
try {
blockLoader.closeFileFactory(file);
} catch (IOException e) {
LOGGER.warning(e.toString());
}
}
}
}
public synchronized String[] getLocations(String key)
{
return locMap.get(key);
}
protected Date parseDate(String date)
{
try {
return dateFormat.parse(date);
} catch (ParseException e) {
return null;
}
}
public boolean dateRangeCheck(String key)
{
// Allow a cluster to be "disabled" by specifying an empty ALL.loc
if (isDisabled) {
return false;
}
if ((startDate == null) && (endDate == null)) {
return true;
}
int spaceIndex = key.indexOf(' ');
if (spaceIndex < 0) {
return true;
}
String dateStr = key.substring(spaceIndex + 1);
Date reqDate = null;
try {
reqDate = ArchiveUtils.getDate(dateStr);
} catch (ParseException e) {
return true;
}
if ((startDate != null) && reqDate.before(startDate)) {
return false;
}
if ((endDate != null) && reqDate.after(endDate)) {
return false;
}
return true;
}
protected void loadPartLocations(HashMap<String, String[]> destMap) throws IOException
{
SeekableLineReaderIterator lines = null;
newStartDate = newEndDate = null;
newIsDisabled = false;
try {
lines = new SeekableLineReaderIterator(locReaderFactory.get());
while (lines.hasNext()) {
String line = lines.next();
if (line.isEmpty()) {
continue;
}
String[] parts = line.split("\\t");
if (parts[0].equals(OFF)) {
newIsDisabled = true;
break;
}
if (parts.length < 2) {
String msg = "Bad line(" + line + ") in (" + locUri + ")";
LOGGER.warning(msg);
continue;
}
if (parts[0].equals(EARLIEST_TIMESTAMP)) {
newStartDate = parseDate(parts[1]);
continue;
} else if (parts[0].equals(LATEST_TIMESTAMP)) {
newEndDate = parseDate(parts[1]);
continue;
}
String locations[] = new String[parts.length - 1];
for (int i = 1; i < parts.length; i++) {
locations[i-1] = parts[i];
}
destMap.put(parts[0], locations);
}
} finally {
if (lines != null) {
lines.close();
}
}
}
@Override
public void run() {
try {
while (true) {
long currModTime = locReaderFactory.getModTime();
if (currModTime != lastModTime) {
syncLoad(currModTime);
}
Thread.sleep(checkInterval);
}
} catch (InterruptedException ie) {
}
}
public int getCheckInterval() {
return checkInterval;
}
public void setCheckInterval(int checkInterval) {
this.checkInterval = checkInterval;
}
}