1. Overview
On occasion, we need to work with graphics-based applications in Java without an actual display, keyboard, or mouse, let's say, on a server or a container.
In this short tutorial, we're going to learn about Java's headless mode to address this scenario. We'll also look at what we can do in headless mode and what we can't.
2. Setting up Headless Mode
There are many ways we can set up headless mode in Java explicitly:
- Programmatically setting the system property java.awt.headless to true
- Using the command line argument: java -Djava.awt.headless=true
- Adding -Djava.awt.headless=true to the JAVA_OPTS environment variable in a server startup script
If the environment is actually headless, the JVM would be aware of it implicitly. However, there will be subtle differences in some scenarios. We'll see them shortly.
3. Examples of UI Components in Headless Mode
A typical use case of UI components running in a headless environment could be an image converter app. Though it needs graphics data for image processing, a display is not really necessary. The app could be run on a server and converted files saved or sent over the network to another machine for display.
Let's see this in action.
First, we'll turn the headless mode on programmatically in a JUnit class:
@Before public void setUpHeadlessMode() { System.setProperty("java.awt.headless", "true"); }
To make sure it is set up correctly, we can use java.awt.GraphicsEnvironment#isHeadless:
@Test public void whenSetUpSuccessful_thenHeadlessIsTrue() { assertThat(GraphicsEnvironment.isHeadless()).isTrue(); }
We should bear in mind that the above test will succeed in a headless environment even if the mode is not explicitly turned on.
Now let's see our simple image converter:
@Test public void whenHeadlessMode_thenImagesWork() { boolean result = false; try (InputStream inStream = HeadlessModeUnitTest.class.getResourceAsStream(IN_FILE); FileOutputStream outStream = new FileOutputStream(OUT_FILE)) { BufferedImage inputImage = ImageIO.read(inStream); result = ImageIO.write(inputImage, FORMAT, outStream); } assertThat(result).isTrue(); }
In this next sample, we can see that information of all fonts, including font metrics, is also available to us:
@Test public void whenHeadless_thenFontsWork() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String fonts[] = ge.getAvailableFontFamilyNames(); assertThat(fonts).isNotEmpty(); Font font = new Font(fonts[0], Font.BOLD, 14); FontMetrics fm = (new Canvas()).getFontMetrics(font); assertThat(fm.getHeight()).isGreaterThan(0); assertThat(fm.getAscent()).isGreaterThan(0); assertThat(fm.getDescent()).isGreaterThan(0); }
4. HeadlessException
There are components that require peripheral devices and won't work in the headless mode. They throw a HeadlessException when used in a non-interactive environment:
Exception in thread "main" java.awt.HeadlessException at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204) at java.awt.Window.<init>(Window.java:536) at java.awt.Frame.<init>(Frame.java:420)
This test asserts that using Frame in a headless mode will indeed throw a HeadlessException:
@Test public void whenHeadlessmode_thenFrameThrowsHeadlessException() { assertThatExceptionOfType(HeadlessException.class).isThrownBy(() -> { Frame frame = new Frame(); frame.setVisible(true); frame.setSize(120, 120); }); }
As a rule of thumb, remember that top-level components such as Frame and Button always need an interactive environment and will throw this exception. However, it will be thrown as an irrecoverable Error if the headless mode is not explicitly set.
5. Bypassing Heavyweight Components in Headless Mode
At this point, we might be asking a question to ourselves – but what if we have code with GUI components to run on both types of environments – a headed production machine and a headless source code analysis server?
In the above examples, we have seen that the heavyweight components won't work on the server and will throw an exception.
So, we can use a conditional approach:
public void FlexibleApp() { if (GraphicsEnvironment.isHeadless()) { System.out.println("Hello World"); } else { JOptionPane.showMessageDialog(null, "Hello World"); } }
Using this pattern, we can create a flexible app that adjusts its behavior as per the environment.
6. Conclusion
With different code samples, we saw the how and why of headless mode in java. This technical article provides a complete list of what all can be done while operating in headless mode.
As usual, the source code for the above examples is available over on GitHub.