summaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/system
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vespajlib/src/main/java/com/yahoo/system
Publish
Diffstat (limited to 'vespajlib/src/main/java/com/yahoo/system')
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/CatchSigTerm.java70
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/CommandLineParser.java216
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/ForceLoad.java31
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/ForceLoadError.java19
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/ProcessExecuter.java56
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/package-info.java5
6 files changed, 397 insertions, 0 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/system/CatchSigTerm.java b/vespajlib/src/main/java/com/yahoo/system/CatchSigTerm.java
new file mode 100644
index 00000000000..f58c161941a
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/CatchSigTerm.java
@@ -0,0 +1,70 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+import java.lang.reflect.*;
+
+// import sun.misc.Signal;
+// import sun.misc.SignalHandler;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class CatchSigTerm {
+ /**
+ * Sets up a signal handler for SIGTERM, where a given AtomicBoolean
+ * gets a true value when the TERM signal is caught.
+ *
+ * Callers basically have two options for acting on the TERM signal:
+ *
+ * They may choose to synchronize and wait() on this variable,
+ * and they will be notified when it changes state to true. To avoid
+ * problems with spurious wakeups, use a while loop and wait()
+ * again if the state is still false. As soon as the caller has been
+ * woken up and the state is true, the application should exit as
+ * soon as possible.
+ *
+ * They may also choose to poll the state of this variable. As soon
+ * as its state becomes true, the signal has been received, and the
+ * application should exit as soon as possible.
+ *
+ * @param signalCaught set to false initially, will be set to true when SIGTERM is caught.
+ */
+ @SuppressWarnings("rawtypes")
+ public static void setup(final AtomicBoolean signalCaught) {
+ signalCaught.set(false);
+ try {
+ Class shc = Class.forName("sun.misc.SignalHandler");
+ Class ssc = Class.forName("sun.misc.Signal");
+
+ InvocationHandler ihandler = new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ synchronized (signalCaught) {
+ signalCaught.set(true);
+ signalCaught.notifyAll();
+ }
+ return null;
+ }
+ };
+ Object shandler = Proxy.newProxyInstance(CatchSigTerm.class.getClassLoader(),
+ new Class[] { shc },
+ ihandler);
+ Constructor[] c = ssc.getDeclaredConstructors();
+ assert c.length == 1;
+ Object sigterm = c[0].newInstance("TERM");
+ Method m = findMethod(ssc, "handle");
+ assert m != null; // "NoSuchMethodException"
+ m.invoke(null, sigterm, shandler);
+ } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ System.err.println("FAILED setting up signal catching: "+e);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static Method findMethod(Class c, String name) {
+ for (Method m : c.getDeclaredMethods()) {
+ if (m.getName().equals(name)) {
+ return m;
+ }
+ }
+ return null;
+ }
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/CommandLineParser.java b/vespajlib/src/main/java/com/yahoo/system/CommandLineParser.java
new file mode 100644
index 00000000000..9f6922e5084
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/CommandLineParser.java
@@ -0,0 +1,216 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+import java.util.*;
+
+/**
+ * Simple command line parser, handling multiple arguments and multiple unary and binary switches starting with -.
+ *
+ * Terms used:
+ *
+ * progname -binaryswitch foo -unaryswitch argument1 argument2
+ *
+ * @author vegardh
+ *
+ */
+public class CommandLineParser {
+ private List<String> inputStrings = new ArrayList<>();
+ private Map<String, String> legalUnarySwitches = new HashMap<>();
+ private Map<String, String> legalBinarySwitches = new HashMap<>();
+ private List<String> unarySwitches = new ArrayList<>();
+ private Map<String, String> binarySwitches = new HashMap<>();
+ private List<String> arguments = new ArrayList<>();
+ private Map<String, String> requiredUnarySwitches = new HashMap<>();
+ private Map<String, String> requiredBinarySwitches = new HashMap<>();
+ private String progname = "progname";
+ private String argumentExplanation;
+ private int minArguments=0;
+ private int maxArguments=Integer.MAX_VALUE;
+ private String helpText;
+ private static HashSet<String> helpSwitches = new HashSet<>();
+ private boolean helpSwitchUsed = false;
+
+ static {
+ helpSwitches.add("-h");
+ helpSwitches.add("-help");
+ helpSwitches.add("--help");
+ helpSwitches.add("-?");
+ }
+
+ public CommandLineParser(String[] cmds) {
+ inputStrings = Arrays.asList(cmds);
+ }
+
+ public CommandLineParser(String progname, String[] cmds) {
+ this.progname=progname;
+ inputStrings = Arrays.asList(cmds);
+ }
+
+ /**
+ * Parses the command line
+ * @throws IllegalArgumentException if a parse error occured
+ */
+ public void parse() {
+ for (Iterator<String> it = inputStrings.iterator() ; it.hasNext() ; ) {
+ String i = it.next();
+ if (isHelpSwitch(i)) {
+ helpSwitchUsed = true;
+ usageAndThrow();
+ }
+ if (i.startsWith("-")) {
+ if (!isLegalSwitch(i)) {
+ usageAndThrow();
+ } else if (legalUnarySwitches.keySet().contains(i)) {
+ unarySwitches.add(i);
+ } else if (legalBinarySwitches.keySet().contains(i)) {
+ if (!it.hasNext()) {
+ throw new IllegalArgumentException(i+ " requires value");
+ } else {
+ String val = it.next();
+ binarySwitches.put(i, val);
+ }
+ }
+ } else {
+ arguments.add(i);
+ }
+ }
+ if (!requiredUnarySwitches.isEmpty() && !getUnarySwitches().containsAll(requiredUnarySwitches.keySet())) {
+ usageAndThrow();
+ }
+ if (!requiredBinarySwitches.isEmpty() && !getBinarySwitches().keySet().containsAll(requiredBinarySwitches.keySet())) {
+ usageAndThrow();
+ }
+ if (getArguments().size()<minArguments || getArguments().size()>maxArguments) {
+ usageAndThrow();
+ }
+ }
+
+ private boolean isHelpSwitch(String i) {
+ return helpSwitches.contains(i);
+ }
+
+ void usageAndThrow() {
+ StringBuffer error_sb = new StringBuffer();
+ error_sb.append("\nusage: ").append(progname).append(" ");
+ if (argumentExplanation!=null) {
+ error_sb.append(argumentExplanation);
+ }
+ if (!legalUnarySwitches.isEmpty()) error_sb.append("\nSwitches:\n");
+ error_sb.append("-h This help text\n");
+ for (Map.Entry<String, String> e : legalUnarySwitches.entrySet()) {
+ error_sb.append(e.getKey()).append(" ").append(e.getValue()).append("\n");
+ }
+ for (Map.Entry<String, String> e : legalBinarySwitches.entrySet()) {
+ error_sb.append(e.getKey()).append(" <").append(e.getValue()).append(">\n");
+ }
+ if (helpText!=null) {
+ error_sb.append("\n").append(helpText).append("\n");
+ }
+ throw new IllegalArgumentException(error_sb.toString());
+ }
+
+ private boolean isLegalSwitch(String s) {
+ return (legalUnarySwitches.containsKey(s) || legalBinarySwitches.containsKey(s));
+ }
+
+ /**
+ * Add a legal unary switch such as "-d"
+ */
+ public void addLegalUnarySwitch(String s, String explanation) {
+ if (legalBinarySwitches.containsKey(s)) {
+ throw new IllegalArgumentException(s +" already added as a binary switch");
+ }
+ legalUnarySwitches.put(s, explanation);
+ }
+
+ public void addLegalUnarySwitch(String s) {
+ addLegalUnarySwitch(s, null);
+ }
+
+ /**
+ * Adds a required switch, such as -p
+ */
+ public void addRequiredUnarySwitch(String s, String explanation) {
+ addLegalUnarySwitch(s, explanation);
+ requiredUnarySwitches.put(s, explanation);
+ }
+
+ /**
+ * Add a legal binary switch such as "-f /foo/bar"
+ */
+ public void addLegalBinarySwitch(String s, String explanation) {
+ if (legalUnarySwitches.containsKey(s)) {
+ throw new IllegalArgumentException(s +" already added as a unary switch");
+ }
+ legalBinarySwitches.put(s, explanation);
+ }
+
+ /**
+ * Adds a legal binary switch without explanation
+ */
+ public void addLegalBinarySwitch(String s) {
+ addLegalBinarySwitch(s, null);
+ }
+
+ /**
+ * Adds a required binary switch
+ */
+ public void addRequiredBinarySwitch(String s, String explanation) {
+ addLegalBinarySwitch(s, explanation);
+ requiredBinarySwitches.put(s, explanation);
+ }
+
+ /**
+ * The unary switches that were given on the command line
+ */
+ public List<String> getUnarySwitches() {
+ return unarySwitches;
+ }
+
+ /**
+ * The binary switches that were given on the command line
+ */
+ public Map<String, String> getBinarySwitches() {
+ return binarySwitches;
+ }
+
+ /**
+ * All non-switch strings that were given on the command line
+ */
+ public List<String> getArguments() {
+ return arguments;
+ }
+
+ /**
+ * Sets the argument explanation used in printing method, i.e. "names,..."
+ */
+ public void setArgumentExplanation(String argumentExplanation) {
+ this.argumentExplanation = argumentExplanation;
+ }
+
+ public void setExtendedHelpText(String text) {
+ this.helpText=text;
+ }
+
+ public String getHelpText() {
+ return helpText;
+ }
+
+ /**
+ * Sets minimum number of required arguments
+ */
+ public void setMinArguments(int minArguments) {
+ this.minArguments = minArguments;
+ }
+
+ /**
+ * Sets the maximum number of allowed arguments
+ */
+ public void setMaxArguments(int maxArguments) {
+ this.maxArguments = maxArguments;
+ }
+
+ public boolean helpSwitchUsed() {
+ return helpSwitchUsed;
+ }
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/ForceLoad.java b/vespajlib/src/main/java/com/yahoo/system/ForceLoad.java
new file mode 100644
index 00000000000..f924740321f
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/ForceLoad.java
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+/**
+ * Utility class used to force the loading of other classes.
+ **/
+public class ForceLoad {
+
+ /**
+ * Force the loading of the given classes. If any of the named
+ * classes can not be loaded, an error will be thrown.
+ *
+ * @param packageName the name of the package for which
+ * we want to forceload classes.
+ * @param classNames array of names of classes (without package prefix)
+ * to force load.
+ **/
+ public static void forceLoad(String packageName, String[] classNames)
+ throws ForceLoadError
+ {
+ String fullClassName = "";
+ try {
+ for (String className : classNames) {
+ fullClassName = packageName + "." + className;
+ Class.forName(fullClassName);
+ }
+ } catch (Exception e) {
+ throw new ForceLoadError(fullClassName, e);
+ }
+ }
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/ForceLoadError.java b/vespajlib/src/main/java/com/yahoo/system/ForceLoadError.java
new file mode 100644
index 00000000000..376374a510d
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/ForceLoadError.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+/**
+ * Special error to be propagated when force-loading a class fails.
+ **/
+@SuppressWarnings("serial")
+public class ForceLoadError extends java.lang.Error {
+
+ /**
+ * Create a new force load error
+ *
+ * @param className full name of offending class
+ * @param cause what caused the failure
+ **/
+ public ForceLoadError(String className, Throwable cause) {
+ super("Force loading class '" + className + "' failed", cause);
+ }
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/ProcessExecuter.java b/vespajlib/src/main/java/com/yahoo/system/ProcessExecuter.java
new file mode 100644
index 00000000000..bb2909b346a
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/ProcessExecuter.java
@@ -0,0 +1,56 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import com.yahoo.collections.Pair;
+
+/**
+ * Executes a system command synchronously.
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc">Jon S Bratseth</a>
+ */
+public class ProcessExecuter {
+
+ /**
+ * Executes the given command synchronously without timeout.
+ * @return Retcode and stdout/stderr merged
+ */
+ public Pair<Integer, String> exec(String command) throws IOException {
+ StringTokenizer tok = new StringTokenizer(command);
+ List<String> tokens = new ArrayList<>();
+ while (tok.hasMoreElements()) tokens.add(tok.nextToken());
+ return exec(tokens.toArray(new String[0]));
+ }
+
+ /**
+ * Executes the given command synchronously without timeout.
+ * @param command tokens
+ * @return Retcode and stdout/stderr merged
+ */
+ public Pair<Integer, String> exec(String[] command) throws IOException {
+ ProcessBuilder pb = new ProcessBuilder(command);
+ StringBuilder ret = new StringBuilder();
+ pb.environment().remove("VESPA_LOG_TARGET");
+ pb.redirectErrorStream(true);
+ Process p = pb.start();
+ InputStream is = p.getInputStream();
+ while (true) {
+ int b = is.read();
+ if (b==-1) break;
+ ret.append((char)b);
+ }
+ int rc=0;
+ try {
+ rc = p.waitFor();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return new Pair<>(rc, ret.toString());
+ }
+
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/package-info.java b/vespajlib/src/main/java/com/yahoo/system/package-info.java
new file mode 100644
index 00000000000..397f0f5d791
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.system;
+
+import com.yahoo.osgi.annotation.ExportPackage;