diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vespajlib/src/main/java/com/yahoo/system |
Publish
Diffstat (limited to 'vespajlib/src/main/java/com/yahoo/system')
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; |