Installation
Check out sources from http://anonsvn.jboss.org/repos/jbossas/projects/fresh/trunk/
Use maven2 to build:
mvn install
Copy generated deployment archive fresh-jar/target/jboss-fresh.jar to JBoss5 deploy directory.
This will automatically start ssh server on port 2022. Use ssh to connect and start typing commands.
System Components
- VFS
- ThreadPool
- SystemShell
- Shell
- Executables
- SSH Service
- RemoteShell
VFS
A component that abstracts the file system. There are different implementations available. There is MemVFS which stores
everything in memory, there is DiskVFS that exposes the native file system. One possible implementation for example is
DB based that stores the FS content into the database.
The role of the VFS is to provide a familiar way to organize commands and configuration files, and perform command lookup,
but at the same time avoid the requirement of using the native filesystem for that purpose.
VFS is there to provide a familiar unix shell experience.
ThreadPool
Purely a performance enhancing component, to avoid continuous creation of new threads as commands are executed.
SystemShell
SystemShell is the main component of the system. It controls creation and destruction of shell sessions, processes, executables ...
It depends on VFS and ThreadPool.
SystemShell provides local programmatic access to shell.
Shell
Shell represents a concepts of a shell session. Every shell instance represents an environment in which all command executions are performed.
Several executables can run simultaneously within the same shell instance. Shell is responsible for parsing the command line, performing command lookup, setting up and invoking the executables representing the commands, setting up the environment settings, stdin, and stdout buffers, properly channeling inputs and outputs when redirection is used.
Shell instances are created through SystemShell.
Executables
All the commands except some special ones already built into the shell are implemented as Executables.
Executable interface defines a component that can be instanciated, initialized and invoked by Shell as its container.
Executable receives command line parameters, it has access to its own stdin, and stdout buffers, it has access to per-process environment ...
It is very easy to write an executable and then use it through shell.
SSH Service
This component provides remote terminal access to shell through ssh protocol.
RemoteShell
This is an EJB3 stateful session that provides remote programatic access to shell through standard java RMI.
TODO: EJB3 not yet implemented - currently it's a EJB 2.1 (is it commited yet?)
Shell Command Line
Command line syntax and features are a subset of unix bash shell.
Command line parser understands basic constructs:
- Input redirect
Use '<' to read from VFS file:
cat < /some_file.txt
Use '<<' with EOF marker specification to enter multiline text interactively:
cat << EOF
Eenie Meenie
Minee Moe
EOF
- Output redirect
Use '>' to write to a file in VFS, truncating any already existing file:
echo 'Merry Christmas' > some_file.txt
Use '>>' to write to a file in VFS, appending content to any already existing file:
echo ', Ho ho ho ...' >> some_file.txt
Use '|' to redirect the output of one executable as the input of another:
cat < /tmp/cities.txt | grep 'San Francisco' > results.txt
- Pathname Expansion
Shell understands unix style regex based filename wildcards, and automatically expands command parameters using such wildcards into a space separated list of filenames.
Example:
ls [a-c]*
- Environment Variables
To set a shell scoped environment variable use 'set'. The set value will be visible to all subsequent executions.
Example:
set PATH=/bin
echo $PATH
To expand an environment variable in command line use '$' prefix:
echo "The path is set to: $PATH"
Environment variable expansion _does not_ happen when using '$' inside single quoted parameters:
echo 'Use "echo $PATH" to display PATH env variable'
Command Lookup
Lookup mechanism mimics Bash. Every shell contains an environment and within it a special environment variable called PATH, which determines a list of directories used for command lookup. These are VFS directories, not native FS directories (can be native FS directories if filesystem based VFS implementation is mounted as VFS root). Commands are represented as empty files with additional metainfo (called Attributes in VFS terminology). A VFS file attribute named Class has to be present on the file, and contain a full class name of the class that implements Executable interface. If file with the name of the command is not found on the PATH a list of internal command mappings is checked. If command is not
one of the commands in that list, the final fallback is to try and load a class with command's name.
Therefore, for testing new commands it's not necessary to 'install' them by creating a command file and setting Class attribute on it. You can just type the classname as command name.
Basic commands
Most of the commands are found and invoked through files inside VFS directory '/bin'. To learn more about a command, call it with '--help'.
VFS commands
cd (org.jboss.fresh.shell.commands.CDExe)
Changes current directory
ls (org.jboss.fresh.shell.commands.LsExe)
List contents of a directory
cat (org.jboss.fresh.shell.commands.CatExe)
Streams content of one or more files to stdout
cp (org.jboss.fresh.shell.commands.CpExe)
Copies a file
mv (org.jboss.fresh.shell.commands.MvExe)
Moves a file
rm (org.jboss.fresh.shell.commands.RmExe)
Removes a file
touch (org.jboss.fresh.shell.commands.TouchExe)
Creates a new empty file, or changes lastModified of the existing file
mkdir (org.jboss.fresh.shell.commands.MkDirExe)
Creates a directory
mount (org.jboss.fresh.shell.commands.MountExe)
Mounts a VFS instance to a mount point
ln (org.jboss.fresh.shell.commands.LnExe)
Creates a symbolic link
info (org.jboss.fresh.shell.commands.InfoExe)
Prints file info
setattr (org.jboss.fresh.shell.commands.SetAttrExe)
Sets attribute on the file
File commands
bzip2 (org.jboss.fresh.shell.commands.Bzip2Exe)
Compresses input with bzip2
bunzip2 (org.jboss.fresh.shell.commands.Bunzip2Exe)
Uncompresses input using bzip2
gzip (org.jboss.fresh.shell.commands.GzipExe)
Compresses input using gzip
gunzip (org.jboss.fresh.shell.commands.GunzipExe)
Uncompresses input using gzip
Shell commands
set (org.jboss.fresh.shell.commands.SetCommand)
Sets shell environment variable, or prints them all
date (org.jboss.fresh.shell.commands.DateExe)
Prints current time
echo (org.jboss.fresh.shell.commands.EchoExe)
Prints text to stdout
history (org.jboss.fresh.shell.commands.HistoryExe)
Prints the history of executed commands
ps (org.jboss.fresh.shell.commands.PsExe)
Prints a list of running executables
kill (org.jboss.fresh.shell.commands.PsExe)
Forces a specific executable to end execution
run (org.jboss.fresh.shell.commands.RunExe)
Executes shell commands read frominput
version (org.jboss.fresh.shell.commands.VersionExe)
Prints current Fresh version.
Util commands
grep (org.jboss.fresh.shell.commands.GrepExe)
Filter input to only display lines that match a search pattern
wget (org.jboss.fresh.shell.commands.util.WebGetExe)
Outputs the content of a URL
resize (org.jboss.fresh.shell.commands.util.ImgResizeExe)
Reads an image from input, and outputs resized image
diag (org.jboss.fresh.shell.commands.util.DiagExe)
Prints out basic information about the server
urlenc (org.jboss.fresh.shell.commands.util.URLEncExe)
Performs url encoding on input
jndi (org.jboss.fresh.shell.commands.JNDIExe)
Performs JNDI operations - list, lookup, bind, unbind
Runtime commands
mbinvoke (org.jboss.fresh.shell.commands.MBeanInvokeExe)
Invoke a mbean; mbinvoke 'jboss.vfs:service=VFSCacheStatistics' listCachedContexts
mbquery (org.jboss.fresh.shell.commands.MBeanQueryExe)
Query for mbeans; mbquery 'jboss.vfs:*'
mcinvoke (org.jboss.fresh.shell.commands.MCBeanInvokeExe)
Invoke a MC bean; mcinvoke VFSCacheStatistics listCachedContexts
Usage Examples
Example 1:
echo Am bam > /tmp/1
echo Tra la la > /tmp/2
cat /tmp/1 /tmp/2
Example 2:
history | grep echo
Example 3:
cat > /tmp/3 << EOF
Lorem Ipsum
dolor sit amet
EOF
cat < /tmp/3
Example 4:
cat > /tmp/4 << EOF
ls /bin
EOF
run --out /tmp/4
Example 5:
wget http://rss.slashdot.org/Slashdot/slashdotIT | grep '<title>'
Example 6:
mbquery 'jboss.jca:*'
Example 7:
mbinvoke 'jboss:service=JNDIView' listXML
Example 8:
mcinvoke MainDeployer getTopLevel
Example 9:
set MYVAR=100
echo $MYVAR
MYVAR=200 echo $MYVAR
echo $MYVAR
Executable API
Commands are implemented as classes of type Executable.
org.jboss.fresh.shell.Executable interface
This interface defines a contract between executable component (Executable) and its container (Shell - see chapter Shell API).
public void setStdOut(OutBuffer out);
public OutBuffer getStdOut();
public void setStdIn(InBuffer in);
public InBuffer getStdIn();
public void setShell(Shell shell);
public Shell getShell();
public void setProcess(Process proc);
public Process getProcess();
public void execute(String exepath, String [] args) throws Exception;
public void sendMessage(String msg);
The container (shell) sets InBuffer, OutBuffer, Shell, and Process properties on the Executable before executing it by calling its execute() method.
There is a signalling mechanism that allows clients to send arbitrary signals to a running process in the form of Strings. Method sendMessage() is used for that purpose.
When writing custom executables an abstract class org.jboss.fresh.shell.AbstractExecutable should be used as a base class for your own commands. It's very easy to write your own command.
Example:
public class HelloExe extends AbstractExecutable {
public void process(String exename, String[] params) throws Exception {
BufferWriter out = new BufferWriter(getStdOut());
out.println(exename + ": " + Arrays.asList(params));
out.close();
}
}
For a more serious command we should at least provide a friendly usage message.
A neat thing about stdout and stdin in Fresh is that they are not streams of bytes, but are general purpose Buffers, that can be used to channel objects. Buffers are not supposed to be used directly. Developer has to decide how to treat input and output and choose a buffer wrapper class accordingly. For stdin one of the following three wrappers can be used:
- BufferInputStream which extends java.io.InputStream
- BufferedReader which extends java.io.Reader
- BufferObjectReader which is used to channel objects directly
Analogously there are three wrappers to be used in combination with stdout buffers:
- BufferOutputStream which extends java.io.OutputStream
- BufferWriter which extends java.io.Writer
- BufferObjectWriter which is used to channel objects directly
An executable can support different modes of treating stdin or stdout, and can use command line switches (parameters) to control the mode. For example, an executable that performs an EJBQL query can write results to stdout as entity objects using BufferObjectWriter, or it can format entities in the result as strings and write them out line by line using BufferWriter.
Shell API
Shell API is used when you want to use shell programatically to execute commands through it.
Shell object represents a session used to execute commands that are implemented as Executables. When using SSH client to connect to SSH Service component, creation of shells is automatically taken care of behind the scene. That's also the case when RemoteShell EJB Session is used for remote programatic shell access. Therefore, if you only want to invoke commands via ssh terminal session, then you don't need Shell API. And in the case of remote programatic access, what you really need is RemoteShell API.
- org.jboss.fresh.shell.Shell interface
Shell commands get invoked through one of the execute() methods on Shell interface. The most basic execute() method is the following:
public ProcessInfo execute(String cmdline) throws ShellException;
This method takes a command line, parses it, identifies commands, performs command lookup, creates processes and executes them. The processes are executed asynchronously - execute() returns immediately. The returned ProcessInfo object is used to communicate with the running process.
When there are several commands in the command line we can talk about a compound command that comprises of several processes. The whole command line outwardly acts as a single process. It has a stdin, which is stdin of the first process, and it has a stdout, which is a stdout of the last process in the command line.
After execute() returns, ProcessInfo can be used to access stdin and stdout of the command line.
If you want to wait for the command line to finish executing, and retrieve results from it then you can do synchronous invocation by using:
public Object executeAsObject(String cmdline) throws ShellException;
There are methods for communicating with a running process when you invoke it asynchronously:
public Object read(String id) throws IOException;
public LinkedList readBuffer(String id, int maxSize) throws IOException;
public void write(String id, Object obj) throws IOException;
public void writeBuffer(String id, LinkedList obj) throws IOException;
public void close(String id, int streamId) throws IOException;
You don't normally invoke these methods directly. There are helper classes that act as Streams / Readers / Writers that you use as an elegant and familiar abstraction:
ShellInputStream
ShellReader
ShellObjectReader
ShellOutputStream
ShellWriter
ShellObjectWriter
These helper classes are analogous to different buffer wrappers in the Executable API. They are the other side of the same coin. Executable uses buffers to read/write data. And this data is channeled through Shell API to clients that use these analogous classes listed above.
The choice of abstraction on the client therefore depends on what the command expects on stdin or outputs to stdout.
Example:
RemoteShell API
Example:
InitialContext ctx = new InitialContext();
RemoteShellHome home = (RemoteShellHome) ctx.lookup("RemoteShell");
RemoteShell shell = home.create();
ProcessInfo inf = shell.execute("cat > /tmp/test");
ShellOutputStream out = new ShellOutputStream(shell, inf.procId);
IOUtils.copy(in, out);
out.close();
Comments