복붙노트

[SPRING] 서버 측 이미지 생성 용 JavaFX

SPRING

서버 측 이미지 생성 용 JavaFX

이상하게 들릴지 모르지만 JavaFX를 사용하여 서버 측에서 차트 이미지를 생성하려고합니다. JavaFX에는 이미지 변환 조인과 위치 지정을 수행하는 멋진 캔버스 API가 있기 때문입니다.

특히 봄 MVC 서비스를 사용하여 내 차트를 이미지로 생성합니다. 주된 문제점은 편리한 Spring 빈에서 javaFX API를 호출하는 방법이다. Java 응용 프로그램 (javaFX 응용 프로그램 클래스를 확장하지 않음)에서 javafx 코드를 실행하려고하면

java.lang.IllegalStateException: Toolkit not initialized

이 문제를 해결하는 방법에 대한 제안이나 아이디어가 있습니까?

해결법

  1. ==============================

    1.그래서 몇 가지 연구를 한 후에 JavaFX로 캔버스 그리기를 구현했으며 여기에 간단한 예제가 있습니다.

    그래서 몇 가지 연구를 한 후에 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);
            }
        }
    
    }
    

    그런 다음 Spring 애플리케이션이 시작될 때 initialize () 메소드를 호출한다.

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

    물론이 솔루션은 비 Spring 애플리케이션으로 포팅 될 수 있습니다.

    이것은 단일 스레드 솔루션 (제 경우에는 충분합니다)이지만 다중 스레드 사용 (아마도 draw 메소드를 호출하기 위해 RMI를 사용할 수도 있습니다)에 채택 될 수 있다고 생각합니다.

    또한이 솔루션은 내 Windows 워크 스테이션에서 "있는 그대로"작동하지만 Linux 서버 환경에서 몇 가지 추가 작업이 호출되어야합니다.

    추신 이 솔루션을 사용하여 서버 측에서 다른 JavaFX 기능 (예 : HTML 내보내기)을 사용할 수도 있습니다.

  2. ==============================

    2.다른 사람들이 이것을 찾고있는 경우에, 이것은 훨씬 간단한 방법입니다. JavaFX 2.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 링크는 위에있다.

  3. ==============================

    3.아마도이 솔루션과 비슷한 것이 도움이 될까요?

    아마도이 솔루션과 비슷한 것이 도움이 될까요?

    JavaFX 2.1 : 툴킷이 초기화되지 않았습니다.

    그렇지 않으면 서비스를 만들고 데이터 저장소로 이미지를 푸시하고 스프링 응용 프로그램에서 이미지를 가져 오는 것을 고려할 것입니다.

    희망은 적어도 조금 도움을 제공합니다!

  4. from https://stackoverflow.com/questions/17940329/javafx-for-server-side-image-generation by cc-by-sa and MIT license