2013-07-30 4 views
9

Это может показаться странным, но я хочу сгенерировать свои графические изображения на стороне сервера с помощью JavaFX. Поскольку JavaFX имеет хороший API холста для выполнения преобразований изображений, объединяет и позиционирует.JavaFX для генерации изображения на стороне сервера

В частности, у меня есть функция Spring MVC для генерации диаграмм как изображений. Основная проблема заключается в том, как вызывать javaFX API из удобного компонента Spring. Если я пытаюсь просто запустить JavaFX код из приложения Java (не распространяющие JavaFX класса Application) я получаю

java.lang.IllegalStateException: Toolkit not initialized 

У вас есть какие-либо предложения/идеи, как решить эту проблему?

ответ

6

Таким образом, после некоторых исследований я реализовал холст рисовать с помощью JavaFX и здесь упрощенный пример:

Сначала я сделал приложение JavaFX, которая запускается в отдельном потоке (я использую Spring taskExecutor но простой Java поток может использоваться).

public class ChartGenerator extends Application { 

    private static Canvas canvas; 

    private static volatile byte[] result; 

    public static void initialize(TaskExecutor taskExecutor) { 
     taskExecutor.execute(new Runnable() { 
      @Override 
      public void run() { 
       launch(ChartGenerator.class); 
      } 
     }); 
    } 

    public static synchronized byte[] generateChart(final Object... params) { 
     Platform.runLater(new Runnable() { 
      @Override 
      public void run() { 
       ByteArrayOutputStream baos = null; 
       try { 
        GraphicsContext gc = canvas.getGraphicsContext2D(); 
        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); 
        /** 
        * Do the work with canvas 
        **/ 
        final SnapshotParameters snapshotParameters = new SnapshotParameters(); 
        snapshotParameters.setFill(Color.TRANSPARENT); 
        WritableImage image = canvas.snapshot(snapshotParameters, null); 
        BufferedImage bImage = SwingFXUtils.fromFXImage(image, null); 
        baos = new ByteArrayOutputStream(); 
        ImageIO.write(bImage, chartType.outputFormat, baos); 
        result = baos.toByteArray(); 
       } catch (InstantiationException e) { 
        throw new ChartGenerationException(e); 
       } catch (IllegalAccessException e) { 
        throw new ChartGenerationException(e); 
       } catch (NoSuchMethodException e) { 
        throw new ChartGenerationException(e); 
       } catch (InvocationTargetException e) { 
        throw new ChartGenerationException(e); 
       } catch (IOException e) { 
        throw new ChartGenerationException(e); 
       } finally { 
        IOUtils.closeQuietly(baos); 
       } 
      } 
     }); 
     while (result == null) { 
      //wait 
     } 
     byte[] ret = result; 
     result = null; 
     return ret; 
    } 


    @Override 
    public void start(Stage stage) { 
     canvas = new Canvas(); 
    } 

    public static class ChartGenerationException extends RuntimeException { 
     public ChartGenerationException(String message) { 
      super(message); 
     } 
     public ChartGenerationException(Throwable cause) { 
      super(cause); 
     } 
    } 

} 

Затем я вызвать метод Initialize() при запуске приложения Spring:

@Autowired private TaskExecutor taskExecutor; 

@PostConstruct private void initChartGenerator() { 
    ChartGenerator.initialize(taskExecutor); 
} 

Это решение может быть курсе, переносимого к приложению, не Spring.

Это однопоточное решение (в моем случае это достаточно), но я думаю, что он может быть применен к многопоточному использованию (возможно, использовать RMI для вызова метода draw).

Кроме того, это решение работает «как есть» на моем окне рабочей станции, а на среде Linux Server некоторые дополнительные действия должны быть вызваны:

  1. Вы не можете использовать JavaFX на OpenJDK (по состоянию на август 2013 года) - нужно переключить в Oracle JDK
  2. Java версия должна быть не меньше, чем Java 7u6
  3. самый сложный - вы должны использовать виртуальный дисплей, чтобы сделать JavaFX работать на безголовых средах:

    APT-получить установку Xvfb

    // затем на старте сервера приложений:

    экспорт DISPLAY = ": 99"

    старт-стоп-демон --start --background --user молы --exec «/ USR/бен/Судо»- -u пристань/USR/бен/Xvfb: 99 -screen 0 1024x768x24


PSВы также можете использовать другие возможности JavaFX на стороне сервера (например, экспортировать html в изображение) с помощью этого решения.

+1

Это классно. Я так взволнован, чтобы попробовать. Спасибо за попытку! – GGrec

0

Возможно, что-то похожее на это решение было бы полезно?

JavaFX 2.1: Toolkit not initialized

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

Надежды, обеспечивающие хотя бы небольшую помощь!

2

В случае, если другие люди ищут это, это намного проще. Используя JavaFX 2.2, я смог выполнить следующие операции.

waitForInit = new Semaphore(0); 
    root = new Group(); 
    root.getChildren().add(jfxnode); 
    FxPlatformExecutor.runOnFxApplication(() -> { 
     snapshot = jfxnode.snapshot(new SnapshotParameters(), null); 
     waitForInit.release(); 
    }); 

    waitForInit.acquireUninterruptibly(); 
    BufferedImage bi = SwingFXUtils.fromFXImage(snapshot, null); 

Нет необходимости добавлять узел в группу. Оттуда вы можете выполнить любую операцию с изображением.

FxPlatformExecutor из библиотеки JME3-JFX, которую я использую для своего проекта. https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java

Вы можете легко создать метод runOnFxApplication() или создать класс FxPlatformExecutor.

Вот код.

package com.jme3x.jfx; 

import javafx.application.Platform; 

/** 
* TODO This Class should be replaced by some Workmanager implemntation 
* in the future 
* @author Heist 
*/ 
public class FxPlatformExecutor { 

    public static void runOnFxApplication(Runnable task) { 
     if (Platform.isFxApplicationThread()) { 
      task.run(); 
     } else { 
      Platform.runLater(task); 
     } 
    } 
} 

Я не писал этот код, ссылка github выше.

+0

Это неполный пример без ссылки на класс FxPlatformExecutor - который не может быть найден. –

+0

Спасибо, что указали это. Импорт представляет собой библиотеку сторонних разработчиков в моем проекте, см. Ссылку [github link] (https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor. Ява). Его можно легко реализовать. Я добавлю это к ответу. – nucklehead

Смежные вопросы