Archive | December 2014

cjmx: A command-line version of JConsole

JConsole is a nice tool when it comes to monitoring a running Java application. But when it is not possible to connect to a JVM with JConsole directly (due to network restrictions for example) and SSH tunneling is not possible, then it would be great to have a command line version of JConsole.

jcmx is such a command line version of JConsole. After having downloaded the single jar file cjmx_2.10-2.1.0-app.jar you can start it by including the tools.jar into the classpath:

java -cp $JAVA_HOME/lib/tools.jar:cjmx_2.10-2.1.0-app.jar cjmx.Main

This will open a “JMX shell” with the following basic commands:

  • help: This shows a basic help screen that explains the available commands.
  • jps/list: Like the jps tool from the JDK this command prints out all java processes with their process id.
  • connect: You can use this command to connect to a running JVM process.
  • format: Let’s you specify whether you want your output in a simple text format or as a JSON string.
  • exit: Quits the application.

To learn more about cjmx let us start a session and connect to the JVM that is running cjmx itself:

> jps
13198 cjmx.Main
> connect 13198
Connected to local virtual machine 13198
Connection id: rmi://0:0:0:0:0:0:0:1  2
Default domain: DefaultDomain
5 domains registered consisting of 19 total MBeans
>
describe     disconnect   exit         format       help         invoke       mbeans       names        names        sample       select       status

After the last appearance of > you see a great feature of cjmx: auto-completion. Every time you do not know which commands are available, you can just type [TAB] and cjmx will list them. This even works for MBean names as we will see.

Now that we are connected to our JVM we can let cjmx describe an available MBean. With auto-completion we can just start typing describe '[TAB] to retrieve a list of all available packages:

> describe '
:                     JMImplementation:     com.sun.management:   java.lang:            java.nio:             java.util.logging:

This way we can dig through the MBean names until we have found what we are looking for. In this example we are interested in the MBean ‘java.lang:type=OperatingSystem’:

> describe 'java.lang:type=OperatingSystem'
Object name: java.lang:type=OperatingSystem
-------------------------------------------
Description: Information on the management interface of the MBean

Attributes:
  MaxFileDescriptorCount: long
  OpenFileDescriptorCount: long
  FreePhysicalMemorySize: long
  CommittedVirtualMemorySize: long
  FreeSwapSpaceSize: long
  ProcessCpuLoad: double
  ProcessCpuTime: long
  SystemCpuLoad: double
  TotalPhysicalMemorySize: long
  TotalSwapSpaceSize: long
  AvailableProcessors: int
  Arch: String
  SystemLoadAverage: double
  Name: String
  Version: String
  ObjectName: ObjectName

As we can see, the MBean ‘java.lang:type=OperatingSystem’ provides information about the number of open files and the current CPU load, etc.. So let’s query the number of open files by invoking the command mbeans with the name of the MBean as well as the sub-command select and the MBean’s attribute:

> mbeans 'java.lang:type=OperatingSystem' select OpenFileDescriptorCount
java.lang:type=OperatingSystem
------------------------------
  OpenFileDescriptorCount: 35

We can even query all available attributes by using the star instead of the concrete name of an attribute. Please note that using the cursor up key recalls the last issued command, hence we do not have to type it again. Instead we just replace the attribute’s name with the star:

> mbeans 'java.lang:type=OperatingSystem' select *
java.lang:type=OperatingSystem
------------------------------
  MaxFileDescriptorCount: 10240
  OpenFileDescriptorCount: 36
...

By using the sub-command invoke we can even invoke MBean methods like in the following example:

> mbeans 'java.lang:type=Memory' invoke gc()
java.lang:type=Memory: null

Now that we know how to query attributes and invoke methods, we can start to script this functionality in order to monitor the application. To support this kind of scripting, cjmx provides the feature that one can pass all “commands” also as an argument to the application itself, hence you can invoke cjmx in the following way (where <PID> has to be replaced by a concrete process id of a running JVM):

java -cp $JAVA_HOME/lib/tools.jar:cjmx_2.10-2.1.0-app.jar cjmx.Main &amp;amp;lt;PID&amp;amp;gt; &amp;amp;quot;mbeans 'java.lang:type=OperatingSystem' select OpenFileDescriptorCount&amp;amp;quot;
java.lang:type=OperatingSystem
------------------------------
  OpenFileDescriptorCount: 630

With this knowledge we can write a simple bash script that queries the JVM each second for the number of open files:

#!/bin/bash
while [ true ] ; do
        echo `date` | tr -d '\n'
        java -cp /usr/java/default/lib/tools.jar:cjmx_2.10-2.1.0-app.jar cjmx.Main $1 &amp;amp;quot;mbeans 'java.lang:type=OperatingSystem' select OpenFileDescriptorCount&amp;amp;quot;|grep OpenFileDescriptorCount|cut -f 2 -d :
		sleep 1
done

This produces each second a new line with a timestamp and the the current number of open files. When redirected into a file, we have a simple log file and can evaluate it later on.

Conclusion: cjmx is a great alternative to JConsole when the latter cannot be used due to network restrictions on a server machine. The ability to even issue commands by passing them on the command line makes it suitable for small monitoring scripts.

Writing an interpreter in Ceylon

Ceylon is a statically typed language for the Java Virtual Machine (JVM) that comes with a powerful set of features that the Java language does not have or is missing (depending on your point of view). However, about three years after the first release and about one year after the version 1.0.0 release it is time to take a closer look.

A good way to get in touch with a new language is to start with a simple project, just for fun. One thing that came into my mind was to implement an interpreter for the programming language Brainfuck. The language consists of only eight simple commands and one instruction pointer. More details about Brainfuck can be found here.

This first surprising point about Ceylon is that we do not have the typically “main” method we know from other C-like languages. We can just implement a “global” method (here we call it “run”):

"Run the module `brainfuck`."
shared void run() {
	String? programFile = process.namedArgumentValue("program");
	if (exists programFile) {
		value bytes = readFile(programFile);
		BrainFuckInterpreter interpreter = BrainFuckInterpreter(bytes);
		interpreter.run();
	} else {
		print("Please provide the argument 'program'.");
	}
}

The string before the method definition is just the short form for the doc(“…”) annotation, the counterpart of the javadoc feature. To format the comments we can use the means of Markdown formatting.

Another surprising fact is the access modifier “shared” that we see in this example. Instead of using public in order to make the method accessible by other code, we use the keyword shared. In Ceylon the visibility hierarchy starts with modules that consist of one or more packages. A package consists of one or more source files, which again can share their elements. Sharing an element from one level of the hierarchy means to make it visible to the next higher level. This way even packages can be invisible to other modules, as we see in the following snippet from the package.celyon file, which is placed within each package and which declares in our case the package brainfuck as shared:

shared package brainfuck;

By the way: The package declarations that all Java classes start with are not necessary in Ceylon. Instead the package name is derived from the names of the folders the source file resides in, hence the redundant information can be left out. But let’s go back to the run() method above. As we do not have a special method which gets the command line arguments from the runtime environment passed in, we ask the global process instance for the named argument “program”. The returned String is an “optional” String, i.e. the value may be or may not be set. In Java that would be modelled by a String reference that may be null. But in order to circumvent possible NullPointerExceptions, the compiler ensures that you can only access the value of an “optional” references when you have checked them using the exists keyword.

Now that we have retrieved the path to our brainfuck “code”, we can read the bytes from the file using the following method:

ArrayList<Byte> readFile(String programFile) {
	value bytes = ArrayList<Byte>();
	print("Reading file '" + programFile + "'.");
	Path path = parsePath(programFile);
	OpenFile openfile = newOpenFile(path.resource);
	ByteBuffer buffer = newByteBuffer(1024);
	variable Integer bytesRead = openfile.read(buffer);
	print("bytesRead=" + bytesRead.string);
	while (bytesRead != -1) {
		buffer.flip();
		for (Byte byte in buffer) {
			bytes.add(byte);
		}
		buffer.clear();
		bytesRead = openfile.read(buffer);
	}
	openfile.close();
	return bytes;
}

As a storage for the bytes read from the program file, we utilize an ArrayList from the Java SDK. In order to do that, we have to import them in our module definition. This one resides in our module.celyon file:

module brainfuck "1.0.0" {
	shared import ceylon.io "1.1.0";
	shared import java.base "8";
}

Next to the name of the module and its version we can import other modules. Line 3 actually imports the java “base” module, i.e. the Java packages java.lang, java.util, java.io, java.net, java.text, NIO and security. Now that we have imported the java.base module, we can import the ArrayList at the beginning of our source file:

import java.util {
	ArrayList
}

Interestingly Ceylon emphasizes immutability, hence you can assign a value to every reference per default only once. If you want to change its value, you have to mark the reference as mutable using the keyword variable. You can see this in the code of the readFile() method as the reference to path, openfile and buffer are not marked as variable. In these cases the value is only assigned once. In contrast to that the number of bytes read from the file is of course variable, hence we mark it so.

After having read the code of the brainfuck program into memory, we can start its execution. This is done by creating an instance of the Brainfuck interpreter and calling its run() method:

BrainFuckInterpreter interpreter = BrainFuckInterpreter(bytes);
interpreter.run();

The beginning of this class is shown in the following:

shared class BrainFuckInterpreter(ArrayList<Byte> buffer) {
	ArrayList<Integer> field = ArrayList<Integer>();
	variable Integer ptr = 0;
	
	shared void run() {
		variable Integer bufferPos = 0;
		while (bufferPos >= 0 && bufferPos < buffer.size() - 1) {
			value oldBufferPos = bufferPos;
			value byte = buffer.get(bufferPos);
			switch (byte.unsigned)
			case (43) {
				ensureFieldCapacity();
				value oldValue = field.get(ptr);
				field.set(ptr, oldValue + 1);
			}
			...
			else {
				print("Ignoring char " + byte.string);
			}
			bufferPos++;
		}
		...
	}
	...
}

If you are used to Java, you might be missing the constructor. Where do we assign the passed in argument buffer to the corresponding member variable? The Ceylon feature here is that we can define some of our member variables within the braces after the class name. In our case we define a member with the name buffer of type ArrayList. The constructor we would have to write in Java that assigns the given value to the member variable is obsolete in Ceylon. The compiler creates some kind of “default” constructor that does this job for us. Additional members of the class, that are not passed to the constructor, can be defined as we would do that in Java (see field and ptr in our example). And as long as we do not declare them to be shared, they are only visible to the class members. Defining the “signature” of the constructor already within the class name also means that we can have only one constructor per class. In case we also want to execute additional code during the “construction”, we can put that code into the class body.

Our run() method now iterates of the buffer with the instructions that we have read from the file. Depending on which value the current byte has, we execute the appropriate operation. This is implemented by a switch statement. In contrast to Java we can omit the curly braces around all cases, but we cannot omit the “default” case, which is modelled in Ceylon as “else” clause of the switch statement. In this “else” case we just print the input byte that we are ignoring. Interesting about this point for Java developers is the fact that we cannot just “add” the byte to the previous String, as we would do in Java. Instead we have to access the String representation of the byte by writing byte.string. An implicit invocation of a method like toString() in Java does not exist in Ceylon.

Conclusion: The interpreter does not utilize all language features, but at least it covers a few interesting points. As someone who has worked with Java for years, the Ceylon features seem to tackle many well known issues of the Java language (versioned modules, package visibility, immutability, optional pattern, etc.). Although features like the absence of multiple constructors is a thing you have to get used to, I must admit that I liked to work with Ceylon.

PS: The sources can be found here.