org.apache.geode.management.internal.cli.CommandManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of geode-core Show documentation
Show all versions of geode-core Show documentation
Apache Geode provides a database-like consistency model, reliable transaction processing and a shared-nothing architecture to maintain very low latency performance with high concurrency processing
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.management.internal.cli;
import org.apache.geode.distributed.ConfigurationProperties;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.geode.management.cli.CliMetaData;
import org.apache.geode.management.internal.cli.annotation.CliArgument;
import org.apache.geode.management.internal.cli.help.CliTopic;
import org.apache.geode.management.internal.cli.parser.*;
import org.apache.geode.management.internal.cli.parser.jopt.JoptOptionParser;
import org.apache.geode.management.internal.cli.util.ClasspathScanLoadHelper;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
import static org.apache.geode.distributed.ConfigurationProperties.*;
/**
*
* @since GemFire 7.0
*/
public class CommandManager {
// 1. Load Commands, availability indicators - Take from GfshParser
// 2. Load Converters - Take from GfshParser
// 3. Load Result Converters - Add
private static final Object INSTANCE_LOCK = new Object();
private static CommandManager INSTANCE = null;
public static final String USER_CMD_PACKAGES_PROPERTY =
DistributionConfig.GEMFIRE_PREFIX + USER_COMMAND_PACKAGES;
public static final String USER_CMD_PACKAGES_ENV_VARIABLE = "GEMFIRE_USER_COMMAND_PACKAGES";
private Properties cacheProperties;
private LogWrapper logWrapper;
private CommandManager(final boolean loadDefaultCommands, final Properties cacheProperties)
throws ClassNotFoundException, IOException {
if (cacheProperties != null) {
this.cacheProperties = cacheProperties;
}
logWrapper = LogWrapper.getInstance();
if (loadDefaultCommands) {
loadCommands();
if (logWrapper.fineEnabled()) {
logWrapper.fine("Commands Loaded: " + commands.keySet());
logWrapper
.fine("Command Availability Indicators Loaded: " + availabilityIndicators.keySet());
logWrapper.fine("Converters Loaded: " + converters);
}
}
}
private void loadUserCommands() throws ClassNotFoundException, IOException {
final Set userCommandPackages = new HashSet();
// Find by packages specified by the system property
if (System.getProperty(USER_CMD_PACKAGES_PROPERTY) != null) {
StringTokenizer tokenizer =
new StringTokenizer(System.getProperty(USER_CMD_PACKAGES_PROPERTY), ",");
while (tokenizer.hasMoreTokens()) {
userCommandPackages.add(tokenizer.nextToken());
}
}
// Find by packages specified by the environment variable
if (System.getenv().containsKey(USER_CMD_PACKAGES_ENV_VARIABLE)) {
StringTokenizer tokenizer =
new StringTokenizer(System.getenv().get(USER_CMD_PACKAGES_ENV_VARIABLE), ",");
while (tokenizer.hasMoreTokens()) {
userCommandPackages.add(tokenizer.nextToken());
}
}
// Find by packages specified in the distribution config
if (this.cacheProperties != null) {
String cacheUserCmdPackages =
this.cacheProperties.getProperty(ConfigurationProperties.USER_COMMAND_PACKAGES);
if (cacheUserCmdPackages != null && !cacheUserCmdPackages.isEmpty()) {
StringTokenizer tokenizer = new StringTokenizer(cacheUserCmdPackages, ",");
while (tokenizer.hasMoreTokens()) {
userCommandPackages.add(tokenizer.nextToken());
}
}
}
// Load commands found in all of the packages
for (String userCommandPackage : userCommandPackages) {
try {
Set> foundClasses =
ClasspathScanLoadHelper.loadAndGet(userCommandPackage, CommandMarker.class, true);
for (Class klass : foundClasses) {
try {
add((CommandMarker) klass.newInstance());
} catch (Exception e) {
logWrapper.warning("Could not load User Commands from: " + klass + " due to "
+ e.getLocalizedMessage()); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "User Command");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load User Commands due to " + e.getLocalizedMessage());
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load User Commands due to " + e.getLocalizedMessage());
throw e;
} catch (IllegalStateException e) {
logWrapper.warning(e.getMessage(), e);
throw e;
}
}
}
/**
* Loads commands via {@link ServiceLoader} from {@link ClassPathLoader}.
*
* @since GemFire 8.1
*/
private void loadPluginCommands() {
final Iterator iterator = ServiceLoader
.load(CommandMarker.class, ClassPathLoader.getLatest().asClassLoader()).iterator();
while (iterator.hasNext()) {
try {
final CommandMarker commandMarker = iterator.next();
try {
add(commandMarker);
} catch (Exception e) {
logWrapper.warning("Could not load Command from: " + commandMarker.getClass() + " due to "
+ e.getLocalizedMessage(), e); // continue
}
} catch (ServiceConfigurationError e) {
logWrapper.severe("Could not load Command: " + e.getLocalizedMessage(), e); // continue
}
}
}
private void loadCommands() throws ClassNotFoundException, IOException {
loadUserCommands();
loadPluginCommands();
// CommandMarkers
Set> foundClasses = null;
try {
foundClasses = ClasspathScanLoadHelper.loadAndGet(
"org.apache.geode.management.internal.cli.commands", CommandMarker.class, true);
for (Class klass : foundClasses) {
try {
add((CommandMarker) klass.newInstance());
} catch (Exception e) {
logWrapper.warning(
"Could not load Command from: " + klass + " due to " + e.getLocalizedMessage()); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "Commands");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load Commands due to " + e.getLocalizedMessage());
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load Commands due to " + e.getLocalizedMessage());
throw e;
} catch (IllegalStateException e) {
logWrapper.warning(e.getMessage(), e);
throw e;
}
// Converters
try {
foundClasses = ClasspathScanLoadHelper
.loadAndGet("org.apache.geode.management.internal.cli.converters", Converter.class, true);
for (Class klass : foundClasses) {
try {
add((Converter) klass.newInstance());
} catch (Exception e) {
logWrapper.warning(
"Could not load Converter from: " + klass + " due to " + e.getLocalizedMessage()); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "Converters");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load Converters due to " + e.getLocalizedMessage());
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load Converters due to " + e.getLocalizedMessage());
throw e;
} catch (IllegalStateException e) {
logWrapper.warning(e.getMessage(), e);
throw e;
}
// Roo's Converters
try {
foundClasses = ClasspathScanLoadHelper.loadAndGet("org.springframework.shell.converters",
Converter.class, true);
for (Class klass : foundClasses) {
try {
if (!SHL_CONVERTERS_TOSKIP.contains(klass.getName())) {
add((Converter) klass.newInstance());
}
} catch (Exception e) {
logWrapper.warning(
"Could not load Converter from: " + klass + " due to " + e.getLocalizedMessage()); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "Basic Converters");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load Default Converters due to " + e.getLocalizedMessage());// TODO
// -
// Abhishek:
// Should
// these
// converters
// be
// moved
// in
// GemFire?
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load Default Converters due to " + e.getLocalizedMessage());// TODO
// -
// Abhishek:
// Should
// these
// converters
// be
// moved
// in
// GemFire?
throw e;
} catch (IllegalStateException e) {
logWrapper.warning(e.getMessage(), e);
throw e;
}
}
private static void raiseExceptionIfEmpty(Set> foundClasses, String errorFor)
throws IllegalStateException {
if (foundClasses == null || foundClasses.isEmpty()) {
throw new IllegalStateException(
"Required " + errorFor + " classes were not loaded. Check logs for errors.");
}
}
public static CommandManager getInstance() throws ClassNotFoundException, IOException {
return getInstance(true);
}
public static CommandManager getInstance(Properties cacheProperties)
throws ClassNotFoundException, IOException {
return getInstance(true, cacheProperties);
}
// For testing.
public static void clearInstance() {
synchronized (INSTANCE_LOCK) {
INSTANCE = null;
}
}
// This method exists for test code use only ...
/* package */static CommandManager getInstance(boolean loadDefaultCommands)
throws ClassNotFoundException, IOException {
return getInstance(loadDefaultCommands, null);
}
private static CommandManager getInstance(boolean loadDefaultCommands, Properties cacheProperties)
throws ClassNotFoundException, IOException {
synchronized (INSTANCE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new CommandManager(loadDefaultCommands, cacheProperties);
}
return INSTANCE;
}
}
public static CommandManager getExisting() {
// if (INSTANCE == null) {
// throw new IllegalStateException("CommandManager doesn't exist.");
// }
return INSTANCE;
}
/** Skip some of the Converters from Spring Shell for our customization */
private static List SHL_CONVERTERS_TOSKIP = new ArrayList();
static {
// Over-ridden by cggm.internal.cli.converters.BooleanConverter
SHL_CONVERTERS_TOSKIP.add("org.springframework.shell.converters.BooleanConverter");
// Over-ridden by cggm.internal.cli.converters.EnumConverter
SHL_CONVERTERS_TOSKIP.add("org.springframework.shell.converters.EnumConverter");
}
/**
* List of converters which should be populated first before any command can be added
*/
private final List> converters = new ArrayList>();
/**
* Map of command string and actual CommandTarget object
*
* This map can also be implemented as a trie to support command abbreviation
*/
private final Map commands = new TreeMap();
/**
* This method will store the all the availabilityIndicators
*/
private final Map availabilityIndicators =
new HashMap();
/**
*/
private final Map topics = new TreeMap();
/**
* Method to add new Converter
*
* @param converter
*/
public void add(Converter converter) {
converters.add(converter);
}
/**
* Method to add new Commands to the parser
*
* @param commandMarker
*/
public void add(CommandMarker commandMarker) {
// First we need to find out all the methods marked with
// Command annotation
Method[] methods = commandMarker.getClass().getMethods();
for (Method method : methods) {
if (method.getAnnotation(CliCommand.class) != null) {
//
// First Build the option parser
//
// Create the empty LinkedLists for storing the argument and
// options
LinkedList arguments = new LinkedList();
LinkedList