/*
* The MIT License
*
* Copyright (c) 2014 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.olivergondza.dumpling.cli;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.OptionDef;
import org.kohsuke.args4j.OptionHandlerRegistry;
import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
import org.reflections.Reflections;
import com.github.olivergondza.dumpling.model.ProcessRuntime;
/**
* Cli entry point.
*
* @author ogondza
*/
public class Main {
// TODO metaVar is not extracted from handler in: Argument "COMMAND" is required
@Argument(required = true, metaVar = "COMMAND")
private CliCommand handler;
public static void main(@Nonnull String[] args) {
int exitCode = new Main().run(args, ProcessStream.system());
System.exit(exitCode);
}
/*package*/ int run(@Nonnull String[] args, @Nonnull final ProcessStream system) {
OptionHandlerRegistry.getRegistry().registerHandler(CliCommand.class, CliCommandOptionHandler.class);
OptionHandlerRegistry.getRegistry().registerHandler(ProcessRuntime.class, new OptionHandlerRegistry.OptionHandlerFactory() {
public OptionHandler<?> getHandler(CmdLineParser parser, OptionDef o, Setter setter) {
return new ProcessRuntimeOptionHandler(parser, o, setter, system);
}
});
CmdLineParser parser = new CmdLineParser(this);
try {
parser.parseArgument(args);
return handler.run(system);
} catch (CmdLineException ex) {
system.err().println(ex.getMessage());
HelpCommand.printUsage(handler, system.err(), ex);
} catch (CommandFailedException ex) {
system.err().println(ex.getMessage());
} catch (RuntimeException ex) {
ex.printStackTrace(system.err());
}
return -1;
}
public static class ProcessRuntimeOptionHandler extends OptionHandler<ProcessRuntime<?, ?, ?>> {
private final @Nonnull ProcessStream streams;
public ProcessRuntimeOptionHandler(CmdLineParser parser, OptionDef option, Setter<? super ProcessRuntime<?, ?, ?>> setter, ProcessStream streams) {
super(parser, option, setter);
this.streams = streams;
}
@Override
public int parseArguments(Parameters params) throws CmdLineException {
String scheme = namedParameter("KIND", params, 0);
String locator = null;
int delim = scheme.indexOf(':');
if (delim == -1) { // No scheme provided - guess
try {
Integer.parseInt(scheme);
locator = scheme;
scheme = "process";
} catch (NumberFormatException ex) {
File file = new File(scheme);
if (file.exists() && !file.isDirectory()) {
locator = scheme;
scheme = "threaddump";
}
}
} else {
locator = scheme.substring(delim + 1);
scheme = scheme.substring(0, delim);
}
if (locator == null) throw new UnknownRuntimeKind(owner, "Unknown runtime source kind: " + scheme);
CliRuntimeFactory<?> factory = getFactory(scheme);
if (factory == null) throw new UnknownRuntimeKind(owner, "Unknown runtime source kind: " + scheme);
ProcessRuntime<?, ?, ?> runtime = factory.createRuntime(locator, streams);
if (runtime == null) throw new AssertionError(factory.getClass() + " failed to create runtime");
setter.addValue(runtime);
return 1;
}
private @Nonnull String namedParameter(String name, Parameters params, int index) throws CmdLineException {
try {
return params.getParameter(index);
} catch (CmdLineException ex) {
throw new CmdLineException(owner, ex.getMessage() + " " + name);
}
}
@Override
public String getDefaultMetaVariable() {
return "KIND:LOCATOR";
}
/*package*/ static @Nonnull List<CliRuntimeFactory<?>> getFactories() {
List<CliRuntimeFactory<?>> factories = new ArrayList<CliRuntimeFactory<?>>();
for (Class<? extends CliRuntimeFactory<?>> type: factoryTypes()) {
factories.add(instantiateFactory(type));
}
return factories;
}
public static @CheckForNull CliRuntimeFactory<?> getFactory(String name) {
for (Class<? extends CliRuntimeFactory<?>> type: factoryTypes()) {
CliRuntimeFactory<?> factory = instantiateFactory(type);
if (name.equals(factory.getKind())) return factory;
}
return null;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static Set<Class<? extends CliRuntimeFactory<?>>> factoryTypes() {
return (Set) new Reflections("com.github.olivergondza.dumpling").getSubTypesOf(CliRuntimeFactory.class);
}
private static CliRuntimeFactory<?> instantiateFactory(Class<? extends CliRuntimeFactory<?>> type) {
try {
return type.newInstance();
} catch (InstantiationException ex) {
AssertionError e = new AssertionError("Cli handler " + type.getName() + " does not declare default constructor");
e.initCause(ex);
throw e;
} catch (IllegalAccessException ex) {
AssertionError e = new AssertionError("Cli handler " + type.getName() + " does not declare default constructor");
e.initCause(ex);
throw e;
}
}
/*package*/ static final class UnknownRuntimeKind extends CmdLineException {
public UnknownRuntimeKind(CmdLineParser owner, String message) {
super(owner, message);
}
}
}
}