Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.sumologic.client.searchjob;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import au.com.bytecode.opencsv.CSVWriter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import com.sumologic.client.Credentials;
import com.sumologic.client.SumoLogicClient;
import com.sumologic.client.model.LogMessage;
import com.sumologic.client.searchjob.model.GetMessagesForSearchJobResponse;
import com.sumologic.client.searchjob.model.GetRecordsForSearchJobResponse;
import com.sumologic.client.searchjob.model.GetSearchJobStatusResponse;
import com.sumologic.client.searchjob.model.SearchJobRecord;
/**
* A small but useful tool that executes a search job and dumps the results to
* the standard output. Based on the commandline arguments, the tool will either
* dump the messages returned by the search job, or the records ("aggregates").
* In order to reliably dump results for large time ranges, the tool can also
* operate in incremental mode, splitting a large time range into multiple
* queries each covering one or more day.
*
* @author Christian Beedgen ([email protected])
*/
public class SearchJobResultDumper {
enum OutputFormat {
CSV,
JSON
}
public static void main(String[] args) throws Exception {
//
// Get the commandline arguments.
//
// The URL of the Sumo Logic API endpoint. This will usually be
// https://api.sumologic.com
String url = null;
// The credentials of the user for which to execute the query.
String accessId = null;
String accessKey = null;
// The start timestamp and end timestamp for the search job.
// This can either be an ISO8601 timestamp (without timezone),
// or milliseconds since the epoch.
String startTimestamp = null;
String endTimestamp = null;
// The timezone to interpret from and to in if given as ISO8601
String timezone = null;
// In chunk mode, the number of hours to execute the search query in.
long chunkIncrementMillis = -1;
// The search query.
String searchQuery = null;
// The file containing the search query.
String searchQueryFilename = null;
// The output format.
OutputFormat outputFormat = OutputFormat.CSV;
// Whether to dump aggregates ("records") or not. By default
// ("false"), the messages returned by the search job will be
// dumped. Setting this to "true" will instead dump the records.
boolean dumpAggregates = false;
// The name of the file to which to write the end timestamp
// of the last successful run.
String lastEndFile = null;
// How many times to retry if the query fails.
int retry = 1;
// Create the command line options.
Options options = createOptions();
try {
// Parse the command line.
CommandLineParser parser = new GnuParser();
CommandLine commandLine = parser.parse(options, args);
url = commandLine.getOptionValue("url");
accessId = commandLine.getOptionValue("accessid");
accessKey = commandLine.getOptionValue("accesskey");
if (commandLine.hasOption("catchup")) {
long thisHour =
((long) Math.floor(System.currentTimeMillis() / 60 / 60 / 1000d))
* (60 * 60 * 1000L);
endTimestamp = Long.toString(thisHour);
timezone = "UTC";
if (commandLine.hasOption("catchup-file")) {
String catchupFile = commandLine.getOptionValue("catchup-file");
try {
// Read the timestamp from the file.
startTimestamp = readTimestampFromFile(catchupFile);
} catch (IOException ioe) {
if (!commandLine.hasOption("ignore-missing-catchup-file")) {
throw new ParseException(String.format(
"Error reading catchup file: '%s' ('%s')", catchupFile, ioe.getMessage()));
} else {
System.err.printf(
"Ignoring missing catchup file: '%s' ('%s')\n", catchupFile, ioe.getMessage());
}
}
}
if (startTimestamp == null) {
// Figure out the catch-up time range.
Long catchupHours = Long.parseLong(commandLine.getOptionValue("catchup"));
startTimestamp = Long.toString(thisHour - (catchupHours * 60 * 60 * 1000L));
}
} else {
if (!commandLine.hasOption("from")) {
throw new ParseException("-f/--from required if -c/--catchup is omitted");
}
startTimestamp = commandLine.getOptionValue("from");
if (!commandLine.hasOption("to")) {
throw new ParseException("-t/--to required if -c/--catchup is omitted");
}
endTimestamp = commandLine.getOptionValue("to");
if (!commandLine.hasOption("tz")) {
throw new ParseException("-tz/--timezone required if -c/--catchup is omitted");
}
timezone = commandLine.getOptionValue("timezone");
}
if (commandLine.hasOption("hours") && commandLine.hasOption("minutes")) {
throw new ParseException("Please specify only one of --hours and --minutes");
}
if (commandLine.hasOption("hours")) {
chunkIncrementMillis =
1000L * 60 * 60 * Long.parseLong(commandLine.getOptionValue("hours"));
}
if (commandLine.hasOption("minutes")) {
chunkIncrementMillis =
1000L * 60 * Long.parseLong(commandLine.getOptionValue("minutes"));
}
searchQuery = commandLine.getOptionValue("query");
searchQueryFilename = commandLine.getOptionValue("file");
if (commandLine.hasOption("json")) {
outputFormat = OutputFormat.JSON;
}
if (commandLine.hasOption("aggregates")) {
dumpAggregates = true;
}
if (searchQuery == null && searchQueryFilename == null) {
throw new ParseException("Either -q/--query or --file needs to be specified");
}
if (commandLine.hasOption("last-end-file")) {
lastEndFile = commandLine.getOptionValue("last-end-file");
}
if (commandLine.hasOption("retry")) {
String retryValue = commandLine.getOptionValue("retry");
retry = Integer.parseInt(retryValue);
}
} catch (ParseException exp) {
System.err.println(exp.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("SearchJobResultsDumper", options);
System.exit(1);
}
// Create the Sumo client.
Credentials credential = new Credentials(accessId, accessKey);
SumoLogicClient sumoClient = new SumoLogicClient(credential);
sumoClient.setURL(url);
// Is the search query a reference to a file?
if (searchQuery == null && searchQueryFilename != null) {
searchQuery = readQueryStringFromFile(searchQueryFilename);
}
// Translate the timestamps, if necessary from ISO8601.
long startMillis = getMillis(startTimestamp, timezone);
long endMillis = getMillis(endTimestamp, timezone);
// Figure out whether the time range should be covered in a
// single search job, or if there's an argument telling us
// to incrementally query in chunks of as many hours as
// defined by the argument. This last argument is optional.
long chunkIncrementMillisToUse = endMillis - startMillis;
if (chunkIncrementMillis != -1) {
chunkIncrementMillisToUse = chunkIncrementMillis;
}
//
// Main execution.
//
// When dumping aggregates/records, we need a CSV writer.
CSVWriter csvWriter = null;
AtomicBoolean headerWritten = new AtomicBoolean(false);
csvWriter = new CSVWriter(new OutputStreamWriter(System.out));
// For JSON output, we need an object mapper.
ObjectMapper objectMapper = new ObjectMapper();
long overallStartTimestamp = System.currentTimeMillis();
boolean failure = false;
try {
do {
// Figure out the start and end milliseconds since the epoch
// for the next incremental chunk. If no chunk size was
// specified, the first chunk is simply the difference
// between the end timestamp and the start timestamp.
long chunkStartMillis = startMillis;
long chunkEndMillis = startMillis + chunkIncrementMillisToUse;
// The chunk end milliseconds since the epoch should never
// be further than the actual specified end timestamp.
chunkEndMillis = Math.min(chunkEndMillis, endMillis);
// Now we are ready to execute the search job.
long executionStartMillis = System.currentTimeMillis();
String prefix = String.format("Chunk from: '%s' to: '%s'",
new Date(chunkStartMillis), new Date(chunkEndMillis));
System.err.printf("--> %s\n", prefix);
failure = executeSearchJobWithRetry(
csvWriter,
headerWritten,
objectMapper,
outputFormat,
prefix,
sumoClient,
searchQuery,
dumpAggregates,
"" + chunkStartMillis,
"" + chunkEndMillis,
timezone,
retry,
lastEndFile);
if (failure) {
break;
}
long elapsed = System.currentTimeMillis() - executionStartMillis;
System.err.printf("-----> Done in millis: '%d'\n", elapsed);
// Set the next start milliseconds since the epoch based
// on the chunk increment.
startMillis += chunkIncrementMillisToUse;
} while (startMillis < endMillis);
// We are done when the start milliseconds since the epoch
// has incrementally reached or surpassed the specified
// end timestamp.
} finally {
try {
// Close the CSV writer, if any.
csvWriter.close();
} catch (IOException ioe) {
System.err.printf("Error closing CSV writer: '%s'", ioe.getMessage());
ioe.printStackTrace(System.err);
}
}
// Red Rover Red Rover All Over.
long overallElapsed = System.currentTimeMillis() - overallStartTimestamp;
System.err.printf("======> Done overall in millis: '%d'\n", overallElapsed);
if (failure) {
System.err.println(
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
System.err.println("Finished with errors");
System.err.println(
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
System.exit(1);
}
}
private static Options createOptions() {
Options options = new Options();
options.addOption(
OptionBuilder.withLongOpt("url")
.withArgName("url")
.withDescription("URL of the Sumo Logic API endpoint")
.hasArg()
.isRequired()
.create("u"));
options.addOption(
OptionBuilder.withLongOpt("accessid")
.withArgName("accessid")
.withDescription("Access id of the user to login as")
.hasArg()
.isRequired()
.create("i"));
options.addOption(
OptionBuilder.withLongOpt("accesskey")
.withArgName("accesskey")
.withDescription("Access key of the user to login as")
.hasArg()
.isRequired()
.create("k"));
options.addOption(
OptionBuilder.withLongOpt("from")
.withArgName("from")
.withDescription("Start timestamp in ISO8601 format, or in milliseconds since the epoch")
.hasArg()
.create("f"));
options.addOption(
OptionBuilder.withLongOpt("to")
.withArgName("to")
.withDescription("End timestamp in ISO8601 format, or in milliseconds since the epoch")
.hasArg()
.create("t"));
options.addOption(
OptionBuilder.withLongOpt("timezone")
.withArgName("timezone")
.withDescription("The timezone to interpret from and to in")
.hasArg()
.create("tz"));
options.addOption(
OptionBuilder.withLongOpt("hours")
.withArgName("hours")
.withDescription("The number of hours to chunk the search query")
.hasArg()
.create("h"));
options.addOption(
OptionBuilder.withLongOpt("minutes")
.withArgName("minutes")
.withDescription("The number of minutes to chunk the search query")
.hasArg()
.create("m"));
options.addOption(
OptionBuilder.withLongOpt("catchup")
.withArgName("catchup")
.withDescription("The number of hours to catchup")
.hasArg()
.create("c"));
options.addOption(
OptionBuilder.withLongOpt("catchup-file")
.withArgName("catchup-file")
.withDescription("The file containing the timestamp from which to catch up from")
.hasArg()
.create("cf"));
options.addOption(
OptionBuilder.withLongOpt("ignore-missing-catchup-file")
.withArgName("ignore-missing-catchup-file")
.withDescription("If the specified catchup file is missing, ignore and use the --catchup value")
.create("imcf"));
options.addOption(
OptionBuilder.withLongOpt("query")
.withArgName("query")
.withDescription("The query to execute")
.hasArg()
.create("q"));
options.addOption(
OptionBuilder.withLongOpt("file")
.withArgName("file")
.withDescription("The file containing the query to execute")
.hasArg()
.create());
options.addOption(
OptionBuilder.withLongOpt("csv")
.withArgName("csv")
.withDescription("Format the output as CSV")
.create());
options.addOption(
OptionBuilder.withLongOpt("aggregates")
.withArgName("aggregates")
.withDescription("Output the aggregate results, not the messages")
.create());
options.addOption(
OptionBuilder.withLongOpt("json")
.withArgName("json")
.withDescription("Format the output as JSON")
.create());
options.addOption(
OptionBuilder.withLongOpt("last-end-file")
.withArgName("last-end-file")
.withDescription("Name of the file to write the end timestamp of the last successful run")
.hasArg()
.create("le"));
options.addOption(
OptionBuilder.withLongOpt("retry")
.withArgName("retry")
.withDescription("Number of times to retry a query in case of an error")
.hasArg()
.create("r"));
return options;
}
private static String readQueryStringFromFile(String fileName) throws IOException {
InputStream is = null;
if (fileName.startsWith("http")) {
is = new URL(fileName).openStream();
} else {
is = new FileInputStream(fileName);
}
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String line = null;
StringBuilder sb = new StringBuilder(1024);
while ((line = in.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
private static String readTimestampFromFile(String filename) throws IOException {
BufferedReader reader =
new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
try {
return reader.readLine();
} finally {
reader.close();
}
}
private static boolean executeSearchJobWithRetry(CSVWriter csvWriter,
AtomicBoolean headerWritten,
ObjectMapper objectMapper,
OutputFormat outputFormat,
String prefix,
SumoLogicClient sumoClient,
String searchQuery,
boolean dumpAggregates,
String startTimestamp,
String endTimestamp,
String timeZone,
int retry,
String lastEndFile) {
int triesLeft = retry;
int attempt = 1;
boolean failure = true;
while (triesLeft > 0 && failure) {
// One less try...
triesLeft--;
// Execute the search.
failure = executeSearch(csvWriter,
headerWritten,
objectMapper,
outputFormat,
prefix,
sumoClient,
searchQuery,
dumpAggregates,
startTimestamp,
endTimestamp,
timeZone,
attempt,
lastEndFile);
if (failure) {
System.err.println(String.format(
"Got an error on attempt: '%d', tries left: '%d'", attempt, triesLeft));
}
// Increment attempt number...
attempt++;
}
return failure;
}
private static boolean executeSearch(CSVWriter csvWriter,
AtomicBoolean headerWritten,
ObjectMapper objectMapper,
OutputFormat outputFormat,
String prefix,
SumoLogicClient sumoClient,
String searchQuery,
boolean dumpAggregates,
String startTimestamp,
String endTimestamp,
String timeZone,
int attempt,
String lastEndFile) {
// Create the search job.
String searchJobId = sumoClient.createSearchJob(
searchQuery,
startTimestamp,
endTimestamp,
timeZone);
System.err.printf("[%s] %s - Search job ID: '%s', attempt: '%d'\n",
new Date(), prefix, searchJobId, attempt);
try {
int messageCount = 0;
int recordCount = 0;
int offset = 0;
GetSearchJobStatusResponse getSearchJobStatusResponse = null;
while (getSearchJobStatusResponse == null ||
(!isDone(getSearchJobStatusResponse) &&
!isCancelled(getSearchJobStatusResponse))) {
long startMillis = System.currentTimeMillis();
// Get the job status and the latest counts from the status.
getSearchJobStatusResponse = sumoClient.getSearchJobStatus(searchJobId);
if (isCancelled(getSearchJobStatusResponse)) {
System.err.println("Ugh. Search job was cancelled. Exiting...");
System.err.flush();
System.exit(1);
}
// Get any pending warnings.
List warnings = getSearchJobStatusResponse.getPendingWarnings();
if (warnings != null && warnings.size() > 0) {
System.err.println("WARNINGS:");
for (String warning : warnings) {
System.err.println(warning);
}
}
// Get any pending errors.
List errors = getSearchJobStatusResponse.getPendingErrors();
if (errors != null && errors.size() > 0) {
System.err.println("ERROR:");
for (String error : errors) {
System.err.println(error);
}
System.err.flush();
return true;
}
messageCount = getSearchJobStatusResponse.getMessageCount();
recordCount = getSearchJobStatusResponse.getRecordCount();
System.err.printf(
"[%s] %s - Search job ID: '%s', attempt: '%d', messages: '%d', records: '%d'\n",
new Date(), prefix, searchJobId, attempt, messageCount, recordCount);
// Catch up with the raw messages, unless we should just dump aggregates.
if (!dumpAggregates) {
offset = getMessages(
csvWriter,
headerWritten,
objectMapper,
outputFormat,
prefix,
sumoClient,
searchJobId,
offset,
messageCount);
}
// Wait if necessary.
long endMillis = System.currentTimeMillis();
if (!isDone(getSearchJobStatusResponse)) {
gracePeriod(prefix, searchJobId, startMillis, endMillis);
}
}
// If we should dump the aggregate results rather than the raw messages,
// we will do this here, after the search job has finished computing.
if (dumpAggregates) {
getRecords(
csvWriter,
headerWritten,
objectMapper,
outputFormat,
prefix,
sumoClient,
searchJobId,
0,
recordCount);
}
if (lastEndFile != null) {
try {
BufferedWriter writer =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(lastEndFile)));
writer.write(endTimestamp);
writer.close();
} catch (IOException ioe) {
// Yikes. We has an error.
System.err.printf("Uncaught exception: '%s'", ioe.getMessage());
ioe.printStackTrace(System.err);
System.err.flush();
}
}
// No error.
return false;
} catch (Throwable t) {
// Yikes. We has an error.
System.err.printf("Uncaught exception: '%s'", t.getMessage());
t.printStackTrace(System.err);
System.err.flush();
// Exit with error.
return true;
} finally {
try {
sumoClient.cancelSearchJob(searchJobId);
} catch (Throwable t) {
System.err.printf("Error cancelling search job: '%s'", t.getMessage());
t.printStackTrace(System.err);
System.err.flush();
}
}
}
private static int getMessages(CSVWriter csvWriter,
AtomicBoolean headerWritten,
ObjectMapper objectMapper,
OutputFormat outputFormat,
String prefix,
SumoLogicClient sumoClient,
String searchJobId,
int messageOffset,
int messageCount) {
int messageLength = 0;
while ((messageLength = messageCount - messageOffset) > 0) {
// Did we print the headers already?
if (outputFormat == OutputFormat.CSV) {
if (!headerWritten.get()) {
// Get the first record so we get the schema.
GetMessagesForSearchJobResponse getMessagesForSearchJobResponse =
sumoClient.getMessagesForSearchJob(searchJobId, 0, 1);
List messages = getMessagesForSearchJobResponse.getMessages();
List fieldNames = new ArrayList(messages.get(0).getFieldNames());
Collections.sort(fieldNames);
String[] headers = new String[fieldNames.size()];
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
headers[i] = fieldName;
}
csvWriter.writeNext(headers);
headerWritten.set(true);
}
}
messageLength = Math.min(messageLength, 1000);
if (messageLength > 0) {
System.err.printf(
"[%s] %s - Search job ID: '%s', messages: '%s', getting offset: '%d', length: '%d'\n",
new Date(), prefix, searchJobId, messageCount, messageOffset, messageLength);
System.err.flush();
GetMessagesForSearchJobResponse getMessagesForSearchJobResponse =
sumoClient.getMessagesForSearchJob(
searchJobId, messageOffset, messageLength);
messageOffset += messageLength;
try {
List messages = getMessagesForSearchJobResponse.getMessages();
for (LogMessage message : messages) {
Map fields = message.getMap();
List fieldNames = new ArrayList(message.getFieldNames());
Collections.sort(fieldNames);
// Write as CSV.
if (outputFormat == OutputFormat.CSV) {
String[] csv = new String[fields.size()];
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
String fieldValue = fields.get(fieldName);
csv[i] = fieldValue;
}
csvWriter.writeNext(csv);
}
// Write as JSON.
if (outputFormat == OutputFormat.JSON) {
String json = objectMapper.writeValueAsString(fields);
System.out.println(json);
}
}
} catch (IOException ioe) {
System.err.printf("Error writing JSON: '%s'", ioe.getMessage());
ioe.printStackTrace(System.err);
}
}
}
return messageOffset;
}
private static int getRecords(CSVWriter csvWriter,
AtomicBoolean headerWritten,
ObjectMapper objectMapper,
OutputFormat outputFormat,
String prefix,
SumoLogicClient sumoClient,
String searchJobId,
int recordOffset,
int recordCount) {
// Did we print the headers already?
if (outputFormat == OutputFormat.CSV) {
if (!headerWritten.get()) {
// Get the first record so we get the schema.
GetRecordsForSearchJobResponse getRecordsForSearchJobResponse =
sumoClient.getRecordsForSearchJob(searchJobId, 0, 1);
List records = getRecordsForSearchJobResponse.getRecords();
List fieldNames = new ArrayList(records.get(0).getFieldNames());
Collections.sort(fieldNames);
String[] headers = new String[fieldNames.size()];
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
headers[i] = fieldName;
}
csvWriter.writeNext(headers);
headerWritten.set(true);
}
}
int recordLength = 0;
while ((recordLength = recordCount - recordOffset) > 0) {
recordLength = Math.min(recordLength, 1000);
if (recordLength > 0) {
System.err.printf(
"[%s] %s - Search job ID: '%s', records: '%s', getting offset: '%d', length: '%d'\n",
new Date(), prefix, searchJobId, recordCount, recordOffset, recordLength);
System.err.flush();
GetRecordsForSearchJobResponse getRecordsForSearchJobResponse =
sumoClient.getRecordsForSearchJob(
searchJobId, recordOffset, recordLength);
recordOffset += recordLength;
try {
List records = getRecordsForSearchJobResponse.getRecords();
for (SearchJobRecord record : records) {
Map fields = record.getMap();
List fieldNames = new ArrayList(record.getFieldNames());
Collections.sort(fieldNames);
// Write as CSV.
if (outputFormat == OutputFormat.CSV) {
String[] csv = new String[fields.size()];
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
String fieldValue = fields.get(fieldName);
csv[i] = fieldValue;
}
csvWriter.writeNext(csv);
}
// Write as JSON.
if (outputFormat == OutputFormat.JSON) {
String json = objectMapper.writeValueAsString(fields);
System.out.println(json);
}
}
} catch (IOException ioe) {
System.err.printf("Error writing JSON: '%s'", ioe.getMessage());
ioe.printStackTrace(System.err);
}
}
}
// Flush the CSV writer.
if (outputFormat == OutputFormat.CSV) {
try {
csvWriter.flush();
} catch (IOException ioe) {
System.err.printf("Error flushing CSV writer: '%s'", ioe.getMessage());
ioe.printStackTrace(System.err);
}
}
return recordOffset;
}
private static long getMillis(String timestamp, String timezone) {
if (isLong(timestamp)) {
return Long.parseLong(timestamp);
} else {
return iso8601toMillis(timestamp, timezone);
}
}
private static boolean isLong(String s) {
try {
long dummy = Long.parseLong(s);
return true;
} catch (Throwable t) {
return false;
}
}
private static long iso8601toMillis(String timestamp, String timezone) {
DateTimeFormatter formatter = ISODateTimeFormat.dateTimeNoMillis();
TimeZone tz = TimeZone.getTimeZone(timezone);
int offset = tz.getRawOffset();
if (tz.inDaylightTime(new Date())) {
offset = offset + tz.getDSTSavings();
}
int offsetHours = offset / 1000 / 60 / 60;
int offsetMinutes = offset / 1000 / 60 % 60;
String timestampWithTimezone =
String.format("%s%+03d:%02d", timestamp, offsetHours, offsetMinutes);
return formatter.parseDateTime(timestampWithTimezone).getMillis();
}
private static boolean isCancelled(GetSearchJobStatusResponse getSearchJobStatusResponse) {
return getSearchJobStatusResponse.getState().equals("CANCELLED");
}
private static boolean isDone(GetSearchJobStatusResponse getSearchJobStatusResponse) {
return getSearchJobStatusResponse.getState().equals("DONE GATHERING RESULTS");
}
private static void gracePeriod(String prefix,
String searchJobId,
long startMillis,
long endMillis)
throws InterruptedException {
long maxWaitMillis = 5000;
long delta = endMillis - startMillis;
long waitMillis = Math.max(0, Math.min(maxWaitMillis - delta, maxWaitMillis));
System.err.printf(
"[%s] %s - Search job ID: '%s', sleeping for: '%d' milliseconds\n",
new Date(), prefix, searchJobId, waitMillis);
System.err.flush();
Thread.sleep(waitMillis);
}
}