«1. Обзор

Иногда нам нужно работать с графическими приложениями на Java без фактического дисплея, клавиатуры или мыши, скажем, на сервере или в контейнере.

В этом кратком руководстве мы собираемся узнать о безголовом режиме Java для решения этого сценария. Мы также посмотрим, что мы можем делать в безголовом режиме, а что нет.

2. Настройка безголового режима

Есть много способов явно настроить безголовый режим в Java:

    Программно установить для системного свойства java.awt.headless значение true Используя аргумент командной строки: java -Djava .awt.headless=true Добавление -Djava.awt.headless=true к переменной среды JAVA_OPTS в сценарии запуска сервера

Если среда на самом деле безголовая, JVM будет знать об этом неявно. Однако в некоторых сценариях будут небольшие различия. Мы скоро увидим их.

3. Примеры компонентов пользовательского интерфейса в автономном режиме

Типичным примером использования компонентов пользовательского интерфейса, работающих в автономной среде, может быть приложение для преобразования изображений. Хотя для обработки изображений ему нужны графические данные, дисплей на самом деле не нужен. Приложение можно запускать на сервере и преобразовывать файлы, сохраненные или отправленные по сети, на другой компьютер для отображения.

Давайте посмотрим на это в действии.

Сначала мы включим безголовый режим программно в классе JUnit:

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

Чтобы убедиться, что он настроен правильно, мы можем использовать java.awt.GraphicsEnvironment#isHeadless:

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

~~ ~ Мы должны иметь в виду, что приведенный выше тест будет успешным в безголовой среде, даже если режим явно не включен.

Теперь давайте посмотрим на наш простой конвертер изображений:

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

В следующем примере мы видим, что информация обо всех шрифтах, включая метрики шрифтов, также доступна нам:

@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

Есть компоненты, которые требуют периферийных устройств и не будут работать в автономном режиме. Они вызывают исключение HeadlessException при использовании в неинтерактивной среде:

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)

Этот тест утверждает, что использование Frame в автономном режиме действительно вызовет исключение HeadlessException:

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

Как правило, помните, что вверх Компоненты уровня, такие как рамка и кнопка, всегда нуждаются в интерактивной среде и будут вызывать это исключение. Однако это будет выдано как неисправимая ошибка, если безголовый режим не установлен явно.

5. Обход тяжеловесных компонентов в безголовом режиме

В этот момент мы могли бы задать себе вопрос: а что, если у нас есть код с компонентами графического интерфейса для запуска в обоих типах сред? производственная машина и безголовый сервер анализа исходного кода?

В приведенных выше примерах мы видели, что тяжеловесные компоненты не будут работать на сервере и вызовут исключение.

Итак, мы можем использовать условный подход:

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

Используя этот шаблон, мы можем создать гибкое приложение, которое настраивает свое поведение в зависимости от среды.

6. Заключение

На разных примерах кода мы увидели, как и почему безголовый режим в java. В этой технической статье представлен полный список того, что можно делать при работе в автономном режиме.

Как обычно, исходный код приведенных выше примеров доступен на GitHub.