All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.management.internal.cli.CommandManager Maven / Gradle / Ivy

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy