2013-07-30 6 views
9

Może to zabrzmieć dziwnie, ale chcę wygenerować obrazy mojego wykresu po stronie serwera przy użyciu JavaFX. Ponieważ JavaFX ma ładne płótno API do wykonywania sprzężeń i pozycjonowania transformacji obrazu.JavaFX do generowania obrazu po stronie serwera

W szczególności mam usługę wiosny MVC do generowania moich wykresów jako obrazy. Głównym problemem jest wywoływanie interfejsu API javaFX z wygodnego komponentu Spring bean. Gdy próbuję uruchomić tylko JavaFX kod z aplikacji java (nie rozszerzenie JavaFX klasy Application) pojawia

java.lang.IllegalStateException: Toolkit not initialized 

Czy macie jakieś sugestie/pomysły jak rozwiązać ten problem?

Odpowiedz

6

Więc po kilku badań zostały wdrożone płótno rysować JavaFX i tu jest uproszczony przykład:

Najpierw zrobiłem aplikację JavaFX, która jest uruchomiona w osobnym wątku (używam Wiosna taskExecutor ale zwykły java wątek może być użyty).

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

} 

Następnie wywołać sposób inicjowania() gdy aplikacja Wiosna rozpoczętych

@Autowired private TaskExecutor taskExecutor; 

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

Roztwór cource mogą być przeniesione do stosowania bez sprężyny.

To jest rozwiązanie z pojedynczym gwintem (w moim przypadku to wystarczające), ale myślę, że można go zaadaptować do użycia wielowątkowego (może użyć RMI do wywołania metody rysowania).

Również to rozwiązanie działa „jak jest” na moje okna stacji roboczej, ale na środowisku serwera linux pewne dodatkowe działania powinny być powoływane:

  1. Nie można użyć JavaFX na OpenJDK (od sierpnia 2013) - trzeba przełączyć Oracle JDK
  2. wersja Java musi być nie mniej niż Java 7u6
  3. najbardziej skomplikowane - trzeba użyć wirtualnego ekranu, aby uruchomić na JavaFX środowiskach bezgłowe:

    apt-get install Xvfb

    // następnie na początku aplikacji serwera:

    export DISPLAY = ": 99"

    start-stop-daemon --start --background --user pomost --exec „/ usr/bin/sudo”- -u molo/usr/bin/Xvfb: 99 -screen 0 1024x768x24


PSMożna również użyć innych funkcji JavaFX po stronie serwera (np. Eksportować html do obrazu) za pomocą tego rozwiązania.

+1

To jest fajne. Jestem tak podekscytowany, że mogę tego spróbować. Dzięki za wysiłek! – GGrec

0

Być może coś podobnego do tego rozwiązania byłoby pomocne?

JavaFX 2.1: Toolkit not initialized

W przeciwnym razie, chciałbym rozważyć utworzenie usługi i przesuwając obraz do magazynu danych i pobieranie go w aplikacji wiosny.

Nadzieję, że zapewnia przynajmniej niewielką pomoc!

2

W przypadku, gdy inni ludzie tego szukają, jest to znacznie prostszy sposób. Używając JavaFX 2.2 mogłem wykonać następujące operacje.

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

Nie ma potrzeby dodawania węzła do grupy. Stamtąd można wykonać dowolną operację z obrazem.

FxPlatformExecutor pochodzi z biblioteki JME3-JFX używanej do mojego projektu. Zobacz: https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java

Możesz łatwo utworzyć metodę runOnFxApplication() lub utworzyć klasę FxPlatformExecutor.

Oto kod.

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

nie pisałem ten kod, link github jest powyżej.

+0

Jest to niekompletny przykład bez odniesienia do klasy FxPlatformExecutor - której nie można znaleźć. –

+0

Dzięki za wskazanie tego. Import jest z biblioteki strony trzeciej w moim projekcie, patrz link [github link] (https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor. Jawa). Można go łatwo wdrożyć. Dodam to do odpowiedzi. – nucklehead