package org.gbif.checklistbank.service.mybatis;
import org.gbif.api.model.checklistbank.ParsedName;
import org.gbif.api.service.checklistbank.NameParser;
import org.gbif.checklistbank.service.CitationService;
import org.gbif.checklistbank.service.ParsedNameService;
import org.gbif.checklistbank.service.mybatis.guice.ChecklistBankServiceMyBatisModule;
import org.gbif.checklistbank.service.mybatis.guice.InternalChecklistBankServiceMyBatisModule;
import org.gbif.nameparser.GBIFNameParser;
import org.gbif.utils.file.properties.PropertiesUtil;
import java.io.PrintStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Before;
import org.junit.Test;
public class ConcurrentCreateOrGetIT {
private static final String PROPERTY_FILE = "checklistbank.properties";
private final int threads = 10;
private Properties props;
@Before
public void init() throws Exception {
props = PropertiesUtil.loadProperties(PROPERTY_FILE);
}
static class ClbMybatisCallable {
private final Properties props;
private Injector inj;
private HikariDataSource hds;
public ClbMybatisCallable(Properties props) {
this.props = props;
}
private void init() {
// init mybatis layer and solr from cfgN instance
inj = Guice.createInjector(new ChecklistBankServiceMyBatisModule(props));
hds = (HikariDataSource) inj.getInstance(InternalChecklistBankServiceMyBatisModule.DATASOURCE_KEY);
}
public Connection getConnection() throws SQLException {
if (hds == null) {
init();
}
return hds.getConnection();
}
public Injector getInj() {
if (inj == null) {
init();
}
return inj;
}
public void shutdown() {
if (hds != null) {
hds.close();
}
}
}
static class ParsedNameCallable extends ClbMybatisCallable implements Callable<ParsedName> {
private final String name;
private static final NameParser PARSER = new GBIFNameParser();
public ParsedNameCallable(Properties props, String name) {
super(props);
this.name = name;
}
@Override
public ParsedName call() throws Exception {
ParsedNameService pservice = getInj().getInstance(ParsedNameService.class);
CitationService cservice = getInj().getInstance(CitationService.class);
for (int x = 0; x < 100; x++) {
pservice.createOrGet(PARSER.parse(name + " banales" + x, null));
cservice.createOrGet(name + " citation #" + x);
}
ParsedName pn = pservice.createOrGet(PARSER.parse(name, null));
shutdown();
return pn;
}
}
@Test
public void writeNamesInParallel() throws Exception {
final int tasks = 100;
PrintStream log = System.out;
// truncate tables
log.println("Truncate existing data");
ClbMybatisCallable mabat = new ClbMybatisCallable(props);
Connection cn = mabat.getConnection();
java.sql.Statement st = cn.createStatement();
st.execute("truncate name_usage cascade");
st.execute("truncate name cascade");
st.execute("truncate citation cascade");
st.close();
cn.close();
ExecutorCompletionService<ParsedName> ecs = new ExecutorCompletionService(Executors.<ParsedName>newFixedThreadPool(threads));
LinkedList<Future<ParsedName>> futures = Lists.newLinkedList();
for (int i = 0; i < tasks; i++) {
ParsedNameCallable pnc = new ParsedNameCallable(props, "Umberto");
log.println("Submitting task");
futures.add(ecs.submit(pnc));
}
while (!futures.isEmpty()) {
Future<ParsedName> f = futures.pop();
ParsedName pn = f.get();
if (pn != null) {
log.println(pn.getKey() + " - " + pn.getScientificName());
} else {
log.println(pn);
}
}
log.println("Finished all tasks. Done");
}
}