Skip to content

Instantly share code, notes, and snippets.

@RavuAlHemio
Created October 30, 2014 14:03
Show Gist options
  • Save RavuAlHemio/0dd8bbefd21f2e7cbf91 to your computer and use it in GitHub Desktop.
Save RavuAlHemio/0dd8bbefd21f2e7cbf91 to your computer and use it in GitHub Desktop.
package ds3;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.apache.log4j.Logger;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
/**
* Abstract base class for programs' main classes.
*
* @author Ondřej Hošek
*/
public abstract class MainClass
{
/** The logger for this class. */
private static Logger l = Logger.getLogger(MainClass.class);
/** Array of strings detected as cries for help on the command line. */
protected static final String[] HELP_STRINGS = {
"--help",
"-h",
"-?"
};
/**
* Checks the number of commandline arguments as well as requests for help.
* May call System.exit().
*
* @param args Command-line arguments as specified by the user.
* @param expectedNum Expected number of arguments.
*/
public static void checkArgs(String[] args, int expectedNum, Class<?> mainClass)
{
if (args.length > 0)
{
for (String s : HELP_STRINGS)
{
if (args[0].equals(s))
{
callUsage(mainClass);
System.exit(0);
}
}
}
if (args.length != expectedNum)
{
System.err.println("Invalid number of arguments.");
callUsage(mainClass);
System.exit(1);
}
}
/**
* Checks the range of an integer. Prints to standard
* error and exits with code 1 on failure.
*
* @param val value to check
* @param low lowest allowed value (= inclusive)
* @param high highest allowed value (= inclusive)
* @param paramName parameter name
*/
protected static void checkRange(long val, long low, long high, String paramName)
{
if (val < low || val > high)
{
System.err.println(String.format("Parameter %s must lie between %d and %d.", paramName, low, high));
System.exit(1);
}
}
/**
* Combines the functionality of {@literal toInt} and {@literal checkRange}.
* @param s string to convert
* @param low lowest allowed value (= inclusive)
* @param high highest allowed value (= inclusive)
* @param paramName parameter name
* @return {@literal s} converted to string
*/
protected static int toIntInRange(String s, int low, int high, String paramName)
{
long ret = toLong(s, paramName);
checkRange(ret, low, high, paramName);
return (int)ret;
}
/**
* Call usage() on the actual main class. Slight trickery to get around
* Java's lack of static method overriding.
*
* @param mainClass The actual main class of the program.
*/
private static void callUsage(Class<?> mainClass)
{
try
{
Method usageMethod = mainClass.getMethod("usage");
usageMethod.invoke(null);
}
catch (Exception e)
{
defaultUsage();
}
}
/**
* Prints program usage information to standard error.
*/
private static void defaultUsage()
{
System.err.println("Uh-oh. This class doesn't specify usage information.");
}
/**
* Converts a parameter string to a long. Prints to
* standard error and exits with code 1 on failure.
*
* @param s string value
* @param paramName parameter name
* @return {@literal s} converted to string.
*/
protected static long toLong(String s, String paramName)
{
try
{
return Long.parseLong(s);
}
catch (NumberFormatException nfe)
{
System.err.println(String.format("Parameter %s must be an integer.", paramName));
System.exit(1);
return -1;
}
}
protected static void commandLineLoop(GracefullyExitable primaryTask, Class<?> mainClass)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true)
{
String line;
// FIXME: is a TTY?
System.out.print("> ");
System.out.flush();
try
{
line = br.readLine();
}
catch (EOFException eofe)
{
// assume user wants to terminate gracefully
if (primaryTask != null)
primaryTask.pleaseStop();
return;
}
catch (IOException ioe)
{
System.err.println("Error reading from standard input: " + ioe.toString());
continue;
}
if (line == null)
{
// EOF; assume user wants to terminate gracefully
if (primaryTask != null)
primaryTask.pleaseStop();
return;
}
Method pc;
try
{
pc = mainClass.getMethod("processCommand", String.class);
}
catch (NoSuchMethodException nsme)
{
System.err.println(String.format("Error: %s must implement public static boolean processCommand(String cmd).", mainClass));
if (primaryTask != null)
primaryTask.pleaseStop();
return;
}
// trim bounding spaces away
line = line.trim();
Boolean ret = Boolean.FALSE;
try
{
ret = (Boolean)pc.invoke(null, line);
}
catch (InvocationTargetException ite)
{
Throwable te = ite.getTargetException();
System.err.println("processCommand failed: " + te.toString());
te.printStackTrace(System.err);
}
catch (Exception e)
{
System.err.println("Error invoking processCommand: " + e.toString());
}
if (!ret.booleanValue())
{
// processCommand returned "false"; stop
if (primaryTask != null)
primaryTask.pleaseStop();
return;
}
}
}
/**
* Fetches the private key from the given file.
* @param filename the file containing the private key
* @return the JCA-compatible private key
*/
public static PrivateKey getPrivateKey(String filename)
{
FileReader frdr;
PEMReader rdr;
try
{
frdr = new FileReader(filename);
}
catch (FileNotFoundException fnfe)
{
l.error("Private key file not found.", fnfe);
System.exit(2);
return null;
}
rdr = new PEMReader(frdr, new PasswordFinder() {
@Override
public char[] getPassword()
{
System.out.print("Enter private key pass phrase: ");
System.out.flush();
try
{
return new BufferedReader(new InputStreamReader(System.in)).readLine().toCharArray();
}
catch (IOException ex)
{
l.error("Error reading private key pass phrase.", ex);
return new char[0];
}
}
});
KeyPair keys;
try
{
keys = (KeyPair)rdr.readObject();
}
catch (IOException ioe)
{
l.error("Error reading private key.", ioe);
System.exit(2);
return null;
}
return keys.getPrivate();
}
/**
* Fetches the public key from the given file.
* @param filename the file containing the public key
* @return the JCA-compatible public key
*/
public static PublicKey getPublicKey(String filename)
{
FileReader frdr;
PEMReader rdr;
try
{
frdr = new FileReader(filename);
}
catch (FileNotFoundException fnfe)
{
l.error("Public key file not found.", fnfe);
System.exit(2);
return null;
}
rdr = new PEMReader(frdr);
PublicKey ret;
try
{
ret = (PublicKey)rdr.readObject();
}
catch (IOException ioe)
{
l.error("Error reading public key.", ioe);
System.exit(2);
return null;
}
return ret;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment