Saturday, September 18, 2010

Command Line Interface CLI from Spring

Command Line Interface (CLI) is useful when you want to script certain actions. While I prefer Perl and Python for this kind of tasks sometimes you have reduced team of developers that are just comfortable with one language. In addition what happens when you have a lot of libraries already built in that language? You might be able to interact with those still but let us face it native is faster and so better.

Let us say you are using Spring and have a lot of investment in Java. You can really easy integrate with your existing libraries and provide complex command line driven actions built completely in Java. You can later then run those commands still from Python, Ruby, Perl, AWK, bash etc.

The only thing you need to do from your main() method is to get access to the spring application context and get a service bean. The rest of annotations and definitions will be wired provided the spring context file allows for correct dependency injection. This is nothing different from what you would do in regular spring programming.

Below is a sample java class intended to rename files from an input folder to an output folder. The actual implementation is not provided as the only purpose of this post is clarify how to interconnect a main() java class with existing autowired (let's say from an included jar file) spring resources.
package com.nestorurquiza.cli;

import java.text.ParseException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.nestorurquiza.service.FilesService;

public class RenameFiles {
    private final static Logger log = LoggerFactory.getLogger(RenameFiles.class);
    
    /**
     * @param args
     * @throws ParseException 
     */
    public static void main(String[] args) {
  log.info("Starting ...");
  
        //get spring context first
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("cli-context.xml");
        
        //Get then a reference to a service bean
        FilesService filesService = (FilesService) applicationContext.getBean("filesService");
        
        // parse command line parameters to CmdParser
        FilesCommandParser commandParser = new FilesCommandParser(args);
        
        String inFolderPath = commandParser.getInFolderPath();
        String outFolderPath = commandParser.getOutFolderPath();
        
        //Invoke the service method that executes the task
        fileService.renameFiles(inFolderPath, outFolderPath);
        
        //Exit the shell process
  log.info("... Done");
        System.exit(0);
    }
}

Here is the command line parser
package com.nestorurquiza.cli;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class FilesCommandParser {
    private Options options = new Options();
    private CommandLine cl;
    
    private String inFolderPath;
    private String outFolderPath;
   
    public FilesCommandParser(String[] args) {
        options.addOption(new Option("inFolderPath", true, "input folder path"));
        options.addOption(new Option("outFolderPath", true, "output folder path"));
        parseCommandline(args);
        help();
        mandatory();
    }

    public String getInFolderPath() {
        return inFolderPath;
    }

    public void setInFolderPath(String inFolderPath) {
        this.inFolderPath = inFolderPath;
    }

    public String getOutFolderPath() {
        return outFolderPath;
    }

    public void setOutFolderPath(String outFolderPath) {
        this.outFolderPath = outFolderPath;
    }
    
    private void parseCommandline(String[] args) {
        CommandLineParser parser = new BasicParser();
        try {
            cl = parser.parse(options, args);
        } catch (ParseException e) {
            System.out.println("Parsing failed. Reason: " + e.getMessage());
            this.generateHelp();
            System.exit(1);
        }
    }
    
    private void generateHelp() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("RenameFiles", options);
    }
    
    private void help() {
        if (cl.hasOption("Help")) {
            this.generateHelp();
            System.exit(0);
        }
    }
    
    private void mandatory() {
        if (!cl.hasOption("inFolderPath") || !cl.hasOption("outFolderPath")) {
            this.generateHelp();
            System.exit(0);
        } else{
            setInFolderPath(cl.getOptionValue("inFolderPath"));
            setOutFolderPath(cl.getOptionValue("outFolderPath"));
        }
    }

}

No comments:

Followers