Archive for ‘Java’

March 19, 2012

Clamshell-Cli: A Framework for Creating Console-Based Shell Apps

Sometimes, you try to solve one problem and end up creating a solution for something completely different.  Such is the story of Clamshell-Cli.  On several occasions I came across the need of a purely console-based JMX shell.  Since I did some work in JMX before, I decided to create one that was usable and basically provided some of the more useful features of JConsole.  After looking around for a framework to build comand-line shells-based apps, it became clear that I would have to create one (yes, I considered OSGi and runtime implementations such as Felix,  but OSGi comes with its own set of constraints that solve problems other than what I wanted to solve).

So I created my own framework to build command-line shell tools. My requirements were simple: create a flexible, component-based, highly-extensible framework that would not get in developer’s way.  The API had to be light, easy to learn, and the runtime had to be super simple to use.

Enter: Clamshell-Cli –  http://code.google.com/p/clamshell-cli/

Clamshell-Cli Features

  • Easy to get started
  • Small API footprint with low learning curve
  • Ability to build complex CLI tools such as REPL using plugin architecture
  • Simple component model that imposes little constraints on your design
  • The plugin architecture is designed for extensibility and feature-scalability:
    • If you don’t like how the default implementation works, you can change it completely
    • Implement the components you want to change and your feature will be included next time the console is restarted
  • Each extension point is mapped to a Java type for easy implementation
  • Plugins are deployed as simple jar files
  • Support for input hints (tab-press at the console)
  • Support for input buffer history

The Design

The Clamshell-Cli API is kept simple on purpose.  It uses a plugin paradigm based on Java’s ServiceLoader API.  The idea is not to make Clamshell-Cli a bloated piece of software trying to handle everything, but rather provide an extensible platform that lets developers build console-based tools by implementing pieces of functionality via plugins.

All major aspects of a working command-line shell are represented by statically defined interfaces. For instance, if you want to change the console prompt, you simply implement the Prompt interface to return the prompt you want displayed.  The Clamshell-Cli interfaces include :

  • SplashScreen - interface to render the first splash screen of console-app
  • ConsoleIO - interface to handle input and output streams
  • Prompt - interface to provide command prompt for the shell tool
  • InputController - Interface to handle text input at command prompt
  • Command - interface to handle action to be taken based on command input

The Default Runtime

When you download the default runtime, you get a basic shell environment with the following directory structure:

  • cli.config - Clamshell-Cli configuration file
  • cli.jar - the launcher jar file
  • clilib - lib files to boot Clamshell-Cli
  • lib - place your dependency jars here
  • plugins - location for Clamshell-Cli plugin jars

When you start the default runtime, you immediately encounter the SplashScreen plugin and the Pormpt plugin:

 
 .d8888b.  888                         .d8888b.  888               888 888
d88P  Y88b 888                        d88P  Y88b 888               888 888
888    888 888                        Y88b.      888               888 888
888        888  8888b.  88888b.d88b.   :Y888b.   88888b.   .d88b.  888 888
888        888     :88b 888 :888 :88b     :Y88b. 888 :88b d8P  Y8b 888 888
888    888 888 .d888888 888  888  888       :888 888  888 88888888 888 888
Y88b  d88P 888 888  888 888  888  888 Y88b  d88P 888  888 Y8b.     888 888
 :Y8888P:  888 :Y888888 888  888  888  :Y8888P:  888  888  :Y8888  888 888

Command-Line Interpreter

Java version: 1.6.0_22
Java Home: /usr/lib/jvm/java-6-openjdk/jre
OS: Linux, Version: 2.6.38-10-generic

prompt> _

The defaul runtime implements an input controller that interprets the input one line at time.  It assumes that the first word of the input is the command.   The controller then delegates the handling of the command to one of the registered Command instances.  For instance, if you type ‘help’ at the prompt, the InputController a) locates the Command mapped to command word ‘help’, then delegate handling of the command to the HelpCmd plugin:

prompt> help

Available Commands
------------------
      exit       Exits ClamShell.
      help       Displays help information for available commands.
   sysinfo       Displays current JVM runtime information.
      time       Prints current date/time

prompt> _

Using the API

To extend the default runtime and add your own command,  a developer would simply provide a plugin that implements the Command interface.  Package the class as jar that obeys the configuration rules of a Service Loader / Service Provider (see here), then place the jar in the plugins directory.  Next time the Clamshell-Cli console is restarted, the command would be available.  The following code shows how simple it is to create your own command using the Command interface. Class TimeCmd, shown below, implements the ‘time’ command as part of the default runtime::

public class TimeCmd implements Command {
    private static final String NAMESPACE = "syscmd";
    private static final String ACTION_NAME = "time";

    @Override
    public Object execute(Context ctx) {
        IOConsole console = ctx.getIoConsole();
        console.writeOutput(String.format("%n%s%n%n",new Date().toString()));
        return null;
    }

    @Override
    public void plug(Context plug) {
        // no load-time setup needed
    }

    @Override
    public Command.Descriptor getDescriptor(){
        return new Command.Descriptor() {
            @Override public String getNamespace() {return NAMESPACE;}

            @Override
            public String getName() {
                return ACTION_NAME;
            }

            @Override
            public String getDescription() {
               return "Prints current date/time";
            }

            @Override
            public String getUsage() {
                return "Type 'time'";
            }

            @Override
            public Map<String, String> getArguments() {
                return Collections.emptyMap();
            }
        };
    }
}

A quick explanation of the code is in order:

  • Method execute() - invoked by the input controller instance when it detects the String time from the command-line. The method retrieves the IOConsole from the context object and use it to print the time. It returns null to the controller (indicating the command did not generate a result).
  • Method plug() - a lifecycle method that is invoked by the framework when the command is first initialized. For our example, there nothing to do.
  • Method getDescriptor() - returns an instance of interface Command.Descriptor which is used to describe the features and document the Command. For our example, the Descriptor interface is implemented anonymously with the following methods:
    • Method Descriptor.getNamespace() - returns a string identifying the command’s namespace. This value can be used by input controllers to avoid command name collisions.
    • Method Descriptor.getName() - returns the string mapped to this command object. In our implementation, it returns “time”.
    • Method Descriptor.getUsage() - intended to provide a descriptive way of using the command.
    • Method Descriptor.getArguments() - returns a Map containing the description for each arguments that may be attached to the command. This example uses none.

Clamshell-Cli Examples

The best way to learn how to use the Clamshell-Cli API is to download the source code and look at how the plugins are implemented. You can also check out:

References

http://code.google.com/p/clamshell-cli/ - Clamshell-Cli Home

http://code.google.com/p/clamshell-cli/wiki/CreatingCommandPlugin - How to create a Command plugin

http://code.google.com/p/clamshell-cli/wiki/DeployPlugins - How to deploy a Plugin

https://github.com/vladimirvivien/jmx-cli - A JMX command-line tool (yes I did build it) – built using Clamshell-Cli (discussed in future post)

http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html - Documentation on ServiceLoader API

http://java.sun.com/developer/technicalArticles/javase/extensible/ - Article on creating extensible application using Java


October 7, 2011

Loader/Launcher – A Pattern to Bootstrap Java Applications

There is still a great number of Java developers out there who are not doing web apps. They use the JDK’s Java launcher directly to bootstrap their apps using public static void main() (abbreviated thereafter as PSVM). And if you are one of those developers, you understand the implications of having a large classpath. It is not uncommon to see shell command with no less than a dozen jars listed on the classpath.

Of course over the years many options have been provided to help with this issue. One of the most recent is from Java 6 where you can reduce the length of the command to launch your Java application by specifying wildcards values in the classpath as shown below:

$ java -cp path1/*.jar:path2:/*:path3/*.jar package.name.ClassName

This write up proposes an alternative approach where your code loads your application’s classes programatically. This pattern, named Loader/Launcher, separates the loading of your application’s classes from the booting of your application logic. The idea is to provide your own loader class that will load your classpath then delegates further bootstrapping responsibilities to a launcher class. One benefit of this approach is that your command to launch your application can be reduced to something like this (no matter the size of your class dependency graph):

$ java -jar package.name.ClassName

The Loader/Launcher Pattern

The way that the Loader/Launcher pattern works is to de-entangle class-loading concerns from application execution concerns. The loading of classes is handled by a Main class with a PSVM method. The native Java command-line launcher loads the Main class. The execution of the application is delegated to a launcher class that implements the Launcher interface. The Launcher is instantiated and invoked by the Main class.

Loader/Launcher Sequence

Loader/Launcher Sequence

To implement this pattern, you will need the following high-level components:

  • The Launcher interface that will be used as a starting point for your app.
  • The Main class where a PSVM method is defined.
  • A Launcher implementation to execute the application.

The Launcher Interface

Implementation of this interface is intended to be the starting point of your application’s bootup process. Instead of starting your application directly in the PSVM method, as is done traditionally, you would relocate the logic for your application’s boot up sequence in a class that implements this interface. When the PSVM method is invoked by the native Java launcher, it would delegate the boot sequence of your application to your Launcher instance (see interface below Listing-1).

public interface Launcher {
	public int launch(Object ... params);
}

Listing-1

This is a simple interface with a single method, launch(). The method takes an array of objects that can be used to pass in arguments to launcher. The method’s signature makes easy to maintain the semantic of PSVM when using the Launcher.

The Main Class

The Main class is designed to be the starting point for the native Java launcher by exposing a PSVM method. The role of this class, in the Loader/Launcher Pattern, is summed up below:
It creates and loads the application’s classpath. Internally, it instantiates a ClassLoader that is used to load the application’s classpath from a specified location.
Once the classpath is in place, it creates an instance of Launcher, from the classpath, to boot up the application by calling launch().

Listing-2 shows the content of a Main class.

public class Main {
	private static String CLASSPATH_DIR = "lib";
	private static String LIB_EXT = ".jar";
	private static String LAUNCHER_CLASS = "demo.launcher.AppLauncher";

	private static ClassLoader cl;
	static{
		try {
			cl = getClassLoaderFromPath(
				new File(CLASSPATH_DIR),
				Thread.currentThread().getContextClassLoader()
			);
			Thread.currentThread().setContextClassLoader(cl);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// Returns a ClassLoader that for the provided path.
	private static ClassLoader getClassLoaderFromPath(File path, ClassLoader parent) throws Exception {
		// get jar files from jarPath
		File[] jarFiles = path.listFiles(new FileFilter() {
			public boolean accept(File file) {
				return file.getName().endsWith(Main.LIB_EXT);
			}
		});
		URL[] classpath = new URL[jarFiles.length];
		for (int j = 0; j < jarFiles.length; j++) {
			classpath[j] = jarFiles[j].toURI().toURL();
		}
		return new URLClassLoader(classpath, parent);
	}

	public static void main(String[] args) throws Exception{
		Launcher launcher = Launcher.class.cast(
			Class.forName(LAUNCHER_CLASS, true, cl).newInstance()
		);
		launcher.launch(new Object[]{"this string is capitalized"});
	}
}

Listing-2

The first thing to notice is the static declarations at the start of the listing. The first three declarations setups the “lib” directory as the location for the classpath, provides “.jar” as the file extension, and specifies demo.launcher.AppLauncher as the name of the Launcher class to load from the classpath. The static code block uses method getClassLoaderFromPath() to initialize a URLClassLoader instance (that points to the lib directory) that will serve as the class loader for the rest of the application.

When the public static void main() method in the Main class is invoked (by the Java launcher), it searches and loads an instance of class demo.launcher.AppLauncher which implements Launcher. Then, the code calls Launcher.launch() to delegate the execution of the rest of the application by passing in a String parameter.

The Launcher Class

The Launcher class is responsible for starting up the application-specific logic. Implementation of the launch() method maintains the same signature as the the PSVM method from the Main class to maintain the familiar semantic. Parameters are passed in as arrays of objects and the method is expected to return an integer. A return value of 0 means everything is OK while anything else means something up to the discretion of the implementor. Listing-3 shows a simple implementation of the Launcher class.

public class AppLauncher implements Launcher {
	public int launch(Object ... args) {
		String result = org.apache.commons.lang3.text.WordUtils.capitalize((String)args[0]);
		System.out.println (result);
		return 0;
	}
}

Listing-3

How It Works

This implementation uses Apache Commons-Lang to capitalize the value of an argument that was passed in. While this is a simple example, it shows exactly how the pattern would work.  When the application is invoked from the command-line using

$ java -jar demo.launcher.Main

The Main class resolves the classpath by loading jars from the jar directory.  The classpath directory contains all jars that satisfies the dependency graph of the application.  In this example the application depends on the Apache-Commons Lang jar.  When Main instantiates its ClassLoader instance, the jar will be added on the classpath and thus be available for use.

An Example

You can download example code that shows how this works from the location below:
An example – https://github.com/vladimirvivien/workbench/tree/master/CustomLauncher

The example comes in three separate projects:

  • Launcher-Api – contains the definition of the Launcher interface.
  • Launcher-Impl – contains an implementation of the Launcher interface.
  • Laucnher-Main – contains the Main class that is used as the starting point of the application.

Conclusion

The Loader/Launcher Pattern is an attempt to decouple two distinct activities that occur when a Java application is started: that of class loading and and application start up. The pattern uses a Main class as the entry point from the Java native launcher and is used to  load the application’s classpath from a given location.  The act of activating the application is then relegated to a Launcher class.  The launcher is responsible for actually starting up the application-specific logic in the code.  Some of the benefit of adopting this pattern is, firstly, the tighter control over how classes are loaded.  You no longer have to rely on the native Java launcher to resolve your classpath.  Another benefit is the separation of concerns for the start up sequence of the app.  The pattern provides a location, the Launcher interface, where to define what should happen when the application itself (not loading of classpath) is starting.  Hope this was helpful.

Reference

https://github.com/vladimirvivien/workbench/tree/master/CustomLauncher - the example

http://code.google.com/p/clamshell-cli/ - tool that uses this pattern

Tags:
Follow

Get every new post delivered to your Inbox.