Search code examples
javajavafxresttemplate

JavaFX : Retrieving Image from server, no suitable constructor found


I am working on a JavaFX app in which I would like to retrieve Image from a server running in Localhost. The image is a javafx.scene.Image. But when I try to retrieve the Image without any class encompassing it, I get no suitable message-convertor found, and I when I put the Image inside another object, I get a different error. So, How can I retrieve image from server? Kindly let me know.

Error log :

org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: No suitable constructor found for type [simple type, class javafx.scene.image.Image]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: java.io.PushbackInputStream@1eebd84c; line: 1, column: 17] (through reference chain: Model.RestImage["canvasImage"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class javafx.scene.image.Image]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: java.io.PushbackInputStream@1eebd84c; line: 1, column: 17] (through reference chain: Model.RestImage["canvasImage"])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:225)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:209)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:835)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:819)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:599)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:475)
    at Controller.AccountController$4.call(AccountController.java:140)
    at Controller.AccountController$4.call(AccountController.java:127)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class javafx.scene.image.Image]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: java.io.PushbackInputStream@1eebd84c; line: 1, column: 17] (through reference chain: Model.RestImage["canvasImage"])

Code JavaFX side:

Retrieve Image from server :

  private Task<List<RestImage>> fetchCanvasImagesFromServer = new Task<List<RestImage>>() {

        @Override
        protected List<RestImage> call() throws Exception {
            List<RestImage> imageList = new ArrayList<>();
            try {
                for(RestCanvas restCanvas : restCanvases) {
                    RestTemplate rest = StaticRestTemplate.getRest();
                    HttpHeaders requestHeaders = new HttpHeaders();
                    requestHeaders.add("Cookie", "JSESSIONID=" + StaticRestTemplate.jsessionid);
                    HttpEntity<RestImage> requestEntity = new HttpEntity<>(requestHeaders);
                    rest.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
                    rest.getMessageConverters().add(new ByteArrayHttpMessageConverter());
                    ResponseEntity<RestImage> responseEntity = rest.exchange(getCanvasImage+restCanvas.getMcanvasid(), HttpMethod.GET, requestEntity, RestImage.class);
                    imageList.add(responseEntity.getBody());
                }
                return imageList;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return imageList;
        }
    };

RestImage model :

import javafx.scene.image.Image;

public class RestImage {

    private Image canvasImage;

    private int someId;
// getters and setters
}

Server-side controller code :

@RequestMapping(value = "/canvasimage/{canvasid}")
    public @ResponseBody
    RestImage getBoardImageAsImage(@PathVariable("canvasid") int canvasid) {
        System.out.println("Board image was called.");
        Person person = this.personService.getCurrentlyAuthenticatedUser();
        RestImage restImage = new RestImage();
        restImage.setSomeId(canvasid);
BufferedImage bufferedImage = ImageIO.read(file);

                    restImage.setCanvasImage(SwingFXUtils.toFXImage(bufferedImage,null));

 return restImage;
}

Solution

  • The underlying issue with this approach is that the framework does not seem to know how to pass an image over network. In short, you need to find some format that can be easily transferred in an intermediate container. Typically for image transfer one can extract raw bytes from the image and send byte[], which is easily transportable. Once received on the other end, byte[] can be read back into an image. One way of converting JavaFX Image into byte[] is first to convert it to BufferedImage using SwingFXUtils, subsequently use ImageIO.write() to write into a ByteArrayOutputStream and obtain byte[] via toByteArray(). Finally, you can use ImageIO.read() and ByteArrayInputStream to convert byte[] into a BufferredImage and then JavaFX Image. Also depending on how you do processing on the client side, you can read data into JavaFX Image directly, using this constructor.

    Alternatively, considering that you are reading the image from a file anyway, you can read the image into byte[] directly with Files.readAllBytes(). This approach will be significantly faster as you skip multiple parsing steps.