/* * Copyright (c) 2013-2017 Cinchapi Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cinchapi.concourse.server.cli; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Map; import org.reflections.Reflections; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.cinchapi.common.base.CheckedExceptions; import com.cinchapi.common.reflect.Reflection; import com.cinchapi.common.unsafe.RuntimeDynamics; import com.google.common.collect.Maps; /** * A management CLI to add/remove/upgrade/etc plugins. * * @author Jeff Nelson */ public class ManagePluginsCli { static { Reflections.log = null; } /** * Run the program... * * @param args */ public static void main(String... args) { // NOTE: We only use JCommaner here to get the usage message JCommander parser = new JCommander(new Object()); parser.setProgramName("plugin"); // Reflectively get all the commands that can be used for plugin // management. Map<String, Class<? extends PluginCli>> commands = Maps.newHashMap(); Reflections reflections = new Reflections( ManagePluginsCli.class.getPackage().getName()); reflections.getSubTypesOf(PluginCli.class).forEach((clazz) -> { // This is over engineering at its finest. The logic below use a ton // of reflection hacks to properly configure JCommander // auto-magically so that every CLI that extends PluginCli just // works. Normally, I wouldn't recommend suffering the performance // hit that this requires for developer convenience, but CLIs are // short lived and run in a separate jvm so its okay in this case :) // - Jeff Nelson String command = PluginCli.getCommand(clazz); commands.put(command, clazz); String description = command + " a plugin"; for (Annotation annotation : clazz.getDeclaredAnnotations()) { if(annotation .annotationType() == CommandLineInterfaceInformation.class) { description = ((CommandLineInterfaceInformation) annotation) .description(); break; } } final String _description = description; Parameters annotation = new Parameters() { @Override public Class<? extends Annotation> annotationType() { return Parameters.class; } @Override public String resourceBundle() { return ""; } @Override public String separators() { return null; } @Override public String optionPrefixes() { return null; } @Override public String commandDescription() { return _description; } @Override public String commandDescriptionKey() { return null; } @Override public String[] commandNames() { return null; } }; try { Object object = RuntimeDynamics.newAnonymousObject(); Method method = Class.class.getDeclaredMethod("annotationData"); method.setAccessible(true); Object annotationData = method.invoke(object.getClass()); Field field = annotationData.getClass() .getDeclaredField("annotations"); field.setAccessible(true); Map<Class<? extends Annotation>, Annotation> annotations = Maps .newHashMapWithExpectedSize(1); annotations.put(Parameters.class, annotation); field.set(annotationData, annotations); parser.addCommand(command, object); } catch (ReflectiveOperationException e) { throw CheckedExceptions.throwAsRuntimeException(e); } }); try { parser.parse(args); } catch (ParameterException e) { // We're not using JCommander properly anyway, so just ignore any // exceptions it throws. } String command = args.length > 0 ? args[0].toLowerCase() : null; args = args.length > 1 ? (String[]) Arrays.copyOfRange(args, 1, args.length) : new String[] {}; Class<?> clazz = command != null ? commands.get(command) : null; if(clazz != null) { PluginCli cli = (PluginCli) Reflection.newInstance(clazz, new Object[] { args }); cli.run(); } else { parser.usage(); } } }