2013-07-30 140 views
9

這可能聽起來很奇怪,但我想生成使用JavaFX我的圖表圖像在服務器端。因爲JavaFX具有很好的畫布API來執行圖像轉換連接和定位。JavaFX的服務器端圖像生成

特別是我有一個春天MVC服務來生成我的圖表爲圖像。 主要問題是如何從方便的Spring bean調用javaFX API。 如果我嘗試只是從運行Java應用程序JavaFX代碼(不延長JavaFX應用程序類)我得到

java.lang.IllegalStateException: Toolkit not initialized 

你有任何建議/想法如何解決這個問題呢?

回答

6

所以經過一番研究,我實現了帆布使用JavaFX畫,這裏是一個簡單的例子:

首先,我做這在一個單獨的線程(我使用Spring taskExecutor的,但一個普通的Java正在啓動的JavaFX應用程序線程可以使用)。

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

} 

然後我打電話時Spring應用程序被啓動初始化()方法:

@Autowired private TaskExecutor taskExecutor; 

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

五言的這種溶液可以移植到非Spring應用程序。

這是一個單線程的解決方案(在我的情況下,這是不夠的),但我認爲這可能是採用多線程的使用(可能使用RMI調用Draw方法)。

而且這個解決方案工作「的是」我的Windows工作站上,而在Linux服務器環境的一些額外的行動應被調用:

  1. 不能OpenJDK的使用JavaFX的(如2013年8月的) - 必須切換到Oracle JDK
  2. Java版本必須是沒有比Java少7u6
  3. 最複雜的 - 你必須使用虛擬的顯示,使JavaFX的無頭環境中運行:

    的apt-get安裝xvfb的

    //然後在應用程序服務器啓動:

    出口顯示器= 「99」

    啓動 - 停止 - 守護--start --background --user碼頭--exec「在/ usr/bin中/須藤」 - -u碼頭的/ usr /斌/的Xvfb:99 -screen 0 1024x768x24


PS您也可以使用此解決方案在服務器端使用其他JavaFX功能(例如,將html導出爲圖像)。

+1

這很酷。我很高興嘗試它。感謝您的努力! – GGrec

0

也許類似這樣的解決方案的東西將是有益的?

JavaFX 2.1: Toolkit not initialized

否則,我會考慮創建一個服務和形象推到數據存儲,並在Spring應用程序取回。

希望至少提供一點幫助!

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鏈接](https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor。 JAVA)。 它可以很容易地實施。我會將其添加到答案中。 – nucklehead