package org.lilyproject.tools;
import com.google.common.base.Preconditions;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.lilyproject.cli.BaseZkCliTool;
import org.lilyproject.client.LilyClient;
import org.lilyproject.repository.api.*;
import org.lilyproject.tools.import_.cli.JsonImport;
import org.lilyproject.util.io.Closer;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.*;
public class KeepAliveCheck extends BaseZkCliTool {
private final static String NAME = "lily-keepalive-check";
private boolean keepAlive = false;
private long timeout = 10000;
private int numberOfAttempts = 3;
private RecordId recordId;
private LilyClient lilyClient;
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private Option keepAliveOption;
private Option timeoutOption;
private Option numberOfAttemptsOption;
public static void main(String[] args) throws Exception {
new KeepAliveCheck().start(args);
}
@Override
public List<Option> getOptions() {
List<Option> options = super.getOptions();
keepAliveOption = OptionBuilder
.withDescription("Send keep alive signal")
.withLongOpt("keep-alive")
.create("k");
timeoutOption = OptionBuilder
.withArgName("timeout")
.hasArg()
.withDescription("Delay to check if connection is still alive")
.withLongOpt("timeout")
.create("t");
numberOfAttemptsOption = OptionBuilder
.withArgName("attempts")
.hasArg()
.withDescription("Number of times to poll the lily server. At least 2")
.withLongOpt("attempts")
.create("a");
options.add(keepAliveOption);
options.add(numberOfAttemptsOption);
options.add(timeoutOption);
return options;
}
@Override
protected int processOptions(CommandLine cmd) throws Exception {
int result = super.processOptions(cmd);
if (result != 0) {
return result;
}
keepAlive = cmd.hasOption(keepAliveOption.getOpt());
if (cmd.hasOption(timeoutOption.getOpt())) {
timeout = Long.parseLong(cmd.getOptionValue(timeoutOption.getOpt()));
Preconditions.checkArgument(timeout >= 1, "The timeout must be at least 1ms./");
}
if (cmd.hasOption(numberOfAttemptsOption.getOpt())) {
numberOfAttempts = Integer.parseInt(cmd.getOptionValue(numberOfAttemptsOption.getOpt()));
Preconditions.checkArgument(numberOfAttempts >= 2, "The number of attempts must be at least 2");
}
return 0;
}
@Override
public int run(CommandLine cmd) throws Exception {
init();
final ScheduledFuture<?> checkHandle = executor.scheduleWithFixedDelay(new CheckTask(recordId, lilyClient), 5, timeout, TimeUnit.MILLISECONDS);
executor.schedule(new Runnable() {
@Override
public void run() {
checkHandle.cancel(false);
}
}, (timeout * (numberOfAttempts - 1)) + 100, TimeUnit.MILLISECONDS);
while (!checkHandle.isDone()) {
try {
checkHandle.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
return 1;
} catch (CancellationException e) {
System.out.println("Done.");
}
}
return 0;
}
@Override
protected void cleanup() {
super.cleanup();
if (lilyClient != null) {
System.out.println("Deleting record : " + recordId);
try {
lilyClient.getDefaultRepository().getDefaultTable().delete(recordId);
} catch (RepositoryException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Closer.close(lilyClient);
}
executor.shutdown();
try {
executor.awaitTermination(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void init() throws Exception{
//
// Instantiate Lily client
//
lilyClient = new LilyClient(this.zkConnectionString, 20000, keepAlive);
lilyClient.getRetryConf().setRetryMaxTime(5000);
LRepository repository = lilyClient.getDefaultRepository();
LTable table = repository.getDefaultTable();
//
// Create a schema
//
// We do this using the import library: this is easier than writing
// out the schema construction code in Java, and automatically handles
// the case where the schema would already exist (on second run of this app).
//
System.out.println("Importing schema");
InputStream is = KeepAliveCheck.class.getResourceAsStream("schema.json");
JsonImport.loadSchema(repository, is);
is.close();
System.out.println("Schema successfully imported");
//
// Create a record
//
System.out.println("Creating a record");
Record record = table.newRecord();
record.setId(repository.getIdGenerator().newRecordId());
record.setRecordType(q("Type1"));
record.setField(q("field1"), "value1");
// We use the createOrUpdate method as that one can automatically recover
// from connection errors (idempotent behavior, like PUT in HTTP).
table.createOrUpdate(record);
System.out.println("Record created: " + record.getId());
this.recordId = record.getId();
}
private static class CheckTask implements Runnable {
private final LilyClient client;
private final RecordId recordId;
public CheckTask(RecordId recordId, LilyClient client) {
this.client = client;
this.recordId = recordId;
}
@Override
public void run() {
try {
LRepository repository = client.getDefaultRepository();
LTable table = repository.getDefaultTable();
System.out.print("Polling ... ");
table.getVariants(recordId);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RepositoryException e) {
System.out.println("Fail.");
e.printStackTrace();
throw new RuntimeException(e);
} finally {
}
System.out.println("Success.");
}
}
private static QName q(String name) {
return new QName("org.lilyproject.keepalivecheck", name);
}
@Override
protected String getCmdName() {
return NAME;
}
@Override
protected String getVersion() {
return org.lilyproject.util.Version.readVersion("org.lilyproject", "lily-keepalive-check");
}
}