com.gemstone.gemfire.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 gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
The newest version!
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.management.internal.cli;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
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 com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.management.cli.CliMetaData;
import com.gemstone.gemfire.management.internal.cli.annotation.CliArgument;
import com.gemstone.gemfire.management.internal.cli.help.CliTopic;
import com.gemstone.gemfire.management.internal.cli.parser.Argument;
import com.gemstone.gemfire.management.internal.cli.parser.AvailabilityTarget;
import com.gemstone.gemfire.management.internal.cli.parser.CommandTarget;
import com.gemstone.gemfire.management.internal.cli.parser.GfshMethodTarget;
import com.gemstone.gemfire.management.internal.cli.parser.GfshOptionParser;
import com.gemstone.gemfire.management.internal.cli.parser.Option;
import com.gemstone.gemfire.management.internal.cli.parser.jopt.JoptOptionParser;
import com.gemstone.gemfire.management.internal.cli.util.ClasspathScanLoadHelper;
/**
*
* @author Abhishek Chaudhari
* @author Nikhil Jadhav
* @since 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 = "gemfire." + DistributionConfig.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(DistributionConfig.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, e); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "User Command");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load User Commands.", e);
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load User Commands.", e);
throw e;
} catch (IllegalStateException e) {
logWrapper.warning(e.getMessage(), e);
throw e;
}
}
}
private void loadCommands() throws ClassNotFoundException, IOException {
loadUserCommands();
//CommandMarkers
Set> foundClasses = null;
try {
foundClasses = ClasspathScanLoadHelper.loadAndGet("com.gemstone.gemfire.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, e); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "Commands");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load Commands.", e);
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load Commands.", e);
throw e;
} catch (IllegalStateException e) {
logWrapper.warning(e.getMessage(), e);
throw e;
}
//Converters
try {
foundClasses = ClasspathScanLoadHelper.loadAndGet("com.gemstone.gemfire.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, e); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "Converters");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load Converters.", e);
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load Converters.", e);
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, e); // continue
}
}
raiseExceptionIfEmpty(foundClasses, "Basic Converters");
} catch (ClassNotFoundException e) {
logWrapper.warning("Could not load Default Converters.", e);//TODO - Abhishek: Should these converters be moved in GemFire?
throw e;
} catch (IOException e) {
logWrapper.warning("Could not load Default Converters.", e);//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