package me.osm.gazetter.join;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import me.osm.gazetter.Options;
import me.osm.gazetter.join.out_handlers.JoinOutHandler;
import me.osm.gazetter.join.util.JoinFailuresHandler;
import me.osm.gazetter.join.util.MemorySupervizor;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JoinExecutor implements JoinFailuresHandler{
private AddrJointHandler addrPointFormatter = new AddrPointFormatter();
private static final Logger log = LoggerFactory.getLogger(JoinExecutor.class.getName());
private AtomicInteger stripesCounter;
private Set<String> filter;
private boolean dropHghNetGeometries = true;
private boolean cleanStripes = false;
public JoinExecutor(Boolean skipHghNets, Boolean keepHghNetGeometries, Boolean cleanStripes, Set<String> filter) {
this.filter = filter;
if(skipHghNets != null) {
this.buildStreetNetworks = !skipHghNets;
}
if(keepHghNetGeometries != null) {
this.dropHghNetGeometries = !dropHghNetGeometries;
}
if(cleanStripes != null) {
this.cleanStripes = cleanStripes;
}
}
private JoinBoundariesExecutor jbe = new JoinBoundariesExecutor();
public static class StripeFilenameFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
return name.matches("stripe[\\.\\d-]+\\.gjson(\\.gz)?(?!.)");
}
}
public static final StripeFilenameFilter STRIPE_FILE_FN_FILTER = new StripeFilenameFilter();
public void run(String stripesFolder, String coomonPartFile) {
long start = (new Date()).getTime();
try {
List<JSONObject> common = getCommonPart(coomonPartFile);
joinStripes(stripesFolder, common);
log.info(
"Join stripes done in {}",
DurationFormatUtils.formatDurationHMS(new Date().getTime()
- start));
if (cleanStripes) {
log.info("Clean stripes in {}", stripesFolder);
File folder = new File(stripesFolder);
File[] stripesFiles = folder.listFiles(STRIPE_FILE_FN_FILTER);
for (File f : stripesFiles) {
f.delete();
}
log.info("Removed {} stripes files", stripesFiles.length);
}
start = new Date().getTime();
jbe.run(stripesFolder, common, filter);
} catch (Exception e) {
throw new RuntimeException(e);
}
log.info(
"Join boundaries done in {}",
DurationFormatUtils.formatDurationHMS(new Date().getTime()
- start));
start = new Date().getTime();
for(JoinOutHandler h : Options.get().getJoinOutHandlers()) {
h.allDone();
}
log.info(
"All handlers done in {}",
DurationFormatUtils.formatDurationHMS(new Date().getTime()
- start));
}
private final List<File> fails = Collections.synchronizedList(new ArrayList<File>());
private boolean buildStreetNetworks = true;
private void joinStripes(String stripesFolder, List<JSONObject> common) {
int threads = Options.get().getNumberOfThreads();
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(threads);
ExecutorService executorService = new ThreadPoolExecutor(threads, threads, 0L,
TimeUnit.MILLISECONDS, queue);
File folder = new File(stripesFolder);
File[] stripesFiles = folder.listFiles(STRIPE_FILE_FN_FILTER);
if (stripesFiles == null) {
log.info("Data directory is empty, nothing to join");
return;
}
stripesCounter = new AtomicInteger(stripesFiles.length);
fails.clear();
for(File stripeF : stripesFiles) {
tryToExecute(common, threads, queue, executorService, stripeF);
}
executorService.shutdown();
try {
while(!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
//still waiting
}
} catch (InterruptedException e) {
throw new RuntimeException("Executor service shutdown failed.", e);
}
if(!fails.isEmpty()) {
log.info("Rerun join for {} from {} files. In one thread.", fails.size(), stripesFiles.length);
}
ArrayList<File> oneThread = new ArrayList<File>(fails);
fails.clear();
for(File stripeF : oneThread ) {
new JoinSliceRunable(addrPointFormatter, stripeF, common,
filter, buildStreetNetworks, dropHghNetGeometries, this, this).run();
}
if(!fails.isEmpty()) {
log.error("Failed to join: {}", fails);
}
}
private void tryToExecute(List<JSONObject> common, int threads,
LinkedBlockingQueue<Runnable> queue,
ExecutorService executorService, File stripeF) {
long avaibleRAMMeg = MemorySupervizor.getAvaibleRAMMeg();
if(queue.size() < threads && avaibleRAMMeg > 500) {
log.trace("Send {} to execution queue. Free mem: {}meg", stripeF, avaibleRAMMeg);
executorService.execute(new JoinSliceRunable(addrPointFormatter, stripeF,
common, filter, buildStreetNetworks, dropHghNetGeometries, this, this));
}
else {
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
tryToExecute(common, threads, queue, executorService, stripeF);
}
}
public static List<JSONObject> getCommonPart(String coomonPartFile) {
List<JSONObject> common = new ArrayList<>();
if(coomonPartFile != null) {
File cpf = new File(coomonPartFile);
if(cpf.exists()) {
try {
JSONArray commonArray = new JSONArray(IOUtils.toString(new FileInputStream(cpf)));
for(int i = 0; i < commonArray.length(); i++) {
common.add(commonArray.getJSONObject(i));
}
} catch (Exception e) {
throw new RuntimeException("Failed to read coomon part.", e);
}
}
}
return common;
}
public AtomicInteger getStripesCounter() {
return stripesCounter;
}
@Override
public void failed(File f) {
fails.add(f);
}
}