El modo sin cabeza de Java

1. Información general

En ocasiones, necesitamos trabajar con aplicaciones basadas en gráficos en Java sin una pantalla, teclado o mouse , digamos, en un servidor o un contenedor.

En este breve tutorial, aprenderemos sobre el modo sin cabeza de Java para abordar este escenario. También veremos qué podemos hacer en modo sin cabeza y qué no.

2. Configuración del modo sin cabeza

Hay muchas formas de configurar el modo sin cabeza en Java de forma explícita:

  • Establecer mediante programación la propiedad del sistema java.awt.headless en true
  • Usando el argumento de la línea de comando: java -Djava.awt.headless = true
  • Agregar -Djava.awt.headless = true a la variable de entorno JAVA_OPTS en un script de inicio del servidor

Si el entorno es realmente sin cabeza, la JVM lo reconocería implícitamente. Sin embargo, habrá diferencias sutiles en algunos escenarios. Los veremos en breve.

3. Ejemplos de componentes de la interfaz de usuario en modo sin cabeza

Un caso de uso típico de los componentes de la interfaz de usuario que se ejecutan en un entorno sin cabeza podría ser una aplicación de conversión de imágenes. Aunque necesita datos gráficos para el procesamiento de imágenes, no es realmente necesaria una pantalla. La aplicación podría ejecutarse en un servidor y convertir los archivos guardados o enviados a través de la red a otra máquina para su visualización.

Veamos esto en acción.

Primero, activaremos el modo sin cabeza mediante programación en una clase JUnit :

@Before public void setUpHeadlessMode() { System.setProperty("java.awt.headless", "true"); } 

Para asegurarnos de que esté configurado correctamente, podemos usar java.awt.GraphicsEnvironment # isHeadless :

@Test public void whenSetUpSuccessful_thenHeadlessIsTrue() { assertThat(GraphicsEnvironment.isHeadless()).isTrue(); } 

Debemos tener en cuenta que la prueba anterior tendrá éxito en un entorno sin cabeza incluso si el modo no está activado explícitamente.

Ahora veamos nuestro sencillo convertidor de imágenes:

@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(); }

En esta siguiente muestra, podemos ver que la información de todas las fuentes, incluidas las métricas de fuentes, también está disponible para nosotros:

@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

Hay componentes que requieren dispositivos periféricos y no funcionan en el modo sin cabeza. Lanzan una HeadlessException cuando se utilizan en un entorno no interactivo:

Exception in thread "main" java.awt.HeadlessException at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204) at java.awt.Window.(Window.java:536) at java.awt.Frame.(Frame.java:420)

Esta prueba afirma que usar Frame en modo sin cabeza arrojará una HeadlessException :

@Test public void whenHeadlessmode_thenFrameThrowsHeadlessException() { assertThatExceptionOfType(HeadlessException.class).isThrownBy(() -> { Frame frame = new Frame(); frame.setVisible(true); frame.setSize(120, 120); }); } 

Como regla general, recuerde que los componentes de nivel superior como Frame y Button siempre necesitan un entorno interactivo y generarán esta excepción. Sin embargo, se arrojará como un Error irrecuperable si el modo sin cabeza no se establece explícitamente .

5. Evitar componentes pesados ​​en modo sin cabeza

En este punto, podríamos estar haciéndonos una pregunta, pero ¿qué pasa si tenemos código con componentes GUI para ejecutar en ambos tipos de entornos: una máquina de producción con cabeza y un servidor de análisis de código fuente sin cabeza?

En los ejemplos anteriores, hemos visto que los componentes pesados ​​no funcionarán en el servidor y generarán una excepción.

Entonces, podemos usar un enfoque condicional:

public void FlexibleApp() { if (GraphicsEnvironment.isHeadless()) { System.out.println("Hello World"); } else { JOptionPane.showMessageDialog(null, "Hello World"); } }

Usando este patrón, podemos crear una aplicación flexible que ajuste su comportamiento según el entorno.

6. Conclusión

Con diferentes ejemplos de código, vimos el cómo y el por qué del modo sin cabeza en java. Este artículo técnico proporciona una lista completa de todo lo que se puede hacer mientras se opera en modo sin cabeza.

Como de costumbre, el código fuente de los ejemplos anteriores está disponible en GitHub.