Resource CenterIntroduction to CRaSHThe Common Reusable ShellIntroductionIt's been a year now since I started the CRaSH project. We use Java Content Repository (JCR) technology a lot at eXo, and I realized we all spent too much time and effort trying to interact with content repositories. We needed a tool to make this easier - so I decided to write a shell for JCR. While this new project, CRaSH, started as an interactive shell for browsing, querying and modifying JCR repositories, it has evolved into more than that.
CRaSH started very simply, so the first usable version took me only a few days to write. In this first version, I remember I used the Netty library to provide connectivity, as it had basic support for the telnet protocol (I didn’t need anything more at the time). I also selected Groovy language for writing shell commands, thinking it was the perfect match for two reasons. First, Groovy is dynamic and easy to compile, and second, you only need a little knowledge of Groovy to begin using it. Since then, CRaSH has evolved to become richer and offer more capabilities. Netty was dropped because its telnet support was too basic; instead, Wimpi Telnetd and Apache SSHD were adopted to provide a real shell experience. CRaSH benefited from a couple of contributions as well (it’s always nice to have people in the open source community helping you), so it is pretty mature as of the recent 1.0.0-beta18 release (the only missing feature I would like is command line completion). CRaSH is now a valuable tool to interact with a JVM runtime. The latest release provides two bundles. The first one, the core bundle, can be deployed in any servlet container. The second one is the GateIn bundle, which is built specifically for the GateIn portal server to add a powerful set of JCR features. In this tutorial, we will focus on explaining basic CRaSH development, and demonstrate this by coding a command that will display a nice list of the JVM system properties. Setting up a CRaSH environmentThe following steps will provide you with a running CRaSH instance on GateIn (you could also use a plain Tomcat instance if you decide to use the core bundle).
At this point you can read the documentation if you have not done so already. It is included in the CRaSH archive, and is also available online. To check that GateIn is running well, hit http://localhost:8080/portal with your browser - you should see the GateIn home page. Using CRaSHOpen your terminal and connect to CRaSH: > telnet localhost 5000 Trying ::1... Connected to localhost. Follow and support the project on http://crsh.googlecode.com Welcome to julien.local + ! It is Tue Dec 07 22:39:37 CET 2010 now % Next, let’s look at a particularly useful function, which will make the server very verbose. Change the logging level of all loggers to trace: % logls | logset --level trace Verify that this is working properly by refreshing the GateIn page in the browser. This request should produce more logging on the console than before, which is pretty useful when you need to get more information about the request processing. That combines two commands: the logls will find all loggers available and send them to the logset command, which sets the trace logging level to those loggers. Each command can also be used separately:
More information is available when you execute the command with the help option: % logls -h List the available loggers -f (--filter) VAL: Filter the logger with a regular expression You can also learn about all the available commands by using the help command: % help Try one of these commands with the -h or --help switch (help, log, logadd, loginfo, logls, logset, threadls, threadstop). You can now test various commands in order to get more familiar with them. Developing your first commandWhen deploying the crash.war file, Tomcat unpacks it by default. The unpacked war allows us to play with CRaSH and add new commands. The webapps/crash/WEB-INF/groovy/commands directory contains all available commands, grouped together in sub-directories (a single level is supported). In the webapps/crash/WEB-INF/groovy/commands/base directory, copy the help.groovy file and rename it propls.groovy. Open it, and you should see:
@Description("Provides basic help")
class help extends org.crsh.command.ClassCommand {
public Object execute() throws ScriptException {
def ret = "Try one of these commands with the -h or --help (";
shellContext.listResourceId(SCRIPT).eachWithIndex() {
cmd, index ->
if (index > 0)
ret += ",";
ret += cmd;
}
ret += ")";
return ret;
}
}
Let’s look a little closer at the command script:
The first step is to change the classname to propls to match the propls.groovy file, then save it. Now this class is instantly available as a command; try to type propls in the shell and you will see the result immediatly. Further modifications can be done to the class, and CRaSH will always use the latest saved version. Now let’s have have a look at the command implementation. We’ll start by simply displaying the JVM properties, and then add the bells and whistles after. Just replace the method body with the following:
def out = "";
System.properties.each() {
key, value ->
out += "${key} = ${value}\n";
}
return out;
We use Groovy’s way of iterating over the JVM properties, as well as its feature for embedding expressions in strings. The command is now basically working, and can be tested by: % propls java.vendor = Apple Inc. … os.name = Mac OS X Now we can make our command implementation a bit nicer. To show how trivial it is to add an option, we’ll add one to filter the property keys by a regular expression. A simple args4j annotation @Option is enough: @Option(name="-k",usage="Filter the key with a regular expression", required=false) def String keyFilter; To make it operational, we need to modify the closure body and wrap the output concatenation with the condition:
if (keyFilter == null || key =~ keyFilter)
out += "${key} = ${value}\n";
Here again, we leverage Groovy’s power with its builtin regular expression syntax. We can now test our command: % propls -k java.* java.vendor = Apple Inc. sun.java.launcher = SUN_STANDARD … java.specification.version = 1.6 Finally, here is the entire code we developed:
import org.kohsuke.args4j.Option;
import org.crsh.command.Description;
@Description("Display Java System Properties")
class propls extends org.crsh.command.ClassCommand {
@Option(name="-k",usage="Filter the key with a regular expression", required=false)
def String keyFilter;
public Object execute() throws ScriptException {
def out = "";
System.properties.each() {
key, value ->
if (keyFilter == null || key =~ keyFilter)
out += "${key} = ${value}\n";
}
return out;
}
}
Of course it’s still possible to add other useful features (such as adding regular expression validation, filtering the entries by value, or sorting the properties). At this point you should have the key concepts down, so you’ll be able to perform these exercises on your own. Resources |
Questions about eXo products and services? Talk to sales.
Subscribe to our newsletter and keep up with the latest eXo news and events.