spring-boot icon indicating copy to clipboard operation
spring-boot copied to clipboard

Headless mode is forced when banner.* file is present.

Open jfarjona opened this issue 4 years ago • 2 comments

Create any SpringBoot project and add a banner.png on the resources folder. Set this as the main application class:

@SpringBootApplication
public class RunSimulators implements ApplicationRunner {
    private static final Logger                   log = LoggerFactory.getLogger(RunSimulators.class);
    
    /**
     * Method description
     *
     * @param osArgs
     */
    public static void main(String[] osArgs) {
        SpringApplication app = new SpringApplicationBuilder(RunSimulators.class).web(WebApplicationType.NONE)
                                                                                 .headless(false)
                                                                                 .build();

        app.setWebApplicationType(WebApplicationType.NONE);
        app.setHeadless(false);        
        ctx = app.run(osArgs);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {       
        System.out.println("Headless run: " + String.valueOf(GraphicsEnvironment.isHeadless()));
    }
}

You would expect " Headless run: false ", but you get true. Remove the banner.png file, and now it is false.

Somehow having a banner forces the application to run headless, no matter what we do.

jfarjona avatar Nov 24 '21 17:11 jfarjona

The headless property is set and its previous value reinstated as part of printing the image-based banner:

https://github.com/spring-projects/spring-boot/blob/4eed637481186b0b85bc1cf0b1e77090139477dc/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ImageBanner.java#L74-L94

Given that SpringApplication already sets the system property, I'm not sure that this is necessary although changing it could break code using ImageBanner outside of SpringApplication.

wilkinsona avatar Nov 24 '21 17:11 wilkinsona

I think the problem we have is that we can't actually reset java.awt.headless once a call to GraphicsEnvironment.isHeadless has been called. The GraphicsEnvironment caches the value in a private static Boolean.

When using the ImageBanner, the ImageReader triggers Rectangle.<clinit>() which calls GraphicsEnvironment.isHeadless and sets things in stone.

Thread [main] (Suspended (breakpoint at line 167 in GraphicsEnvironment))	
	GraphicsEnvironment.getHeadlessProperty() line: 167	
	GraphicsEnvironment.isHeadless() line: 145	
	Rectangle.<clinit>() line: 174	
	ImageReader.getDestination(ImageReadParam, Iterator<ImageTypeSpecifier>, int, int) line: 2865	
	GIFImageReader.read(int, ImageReadParam) line: 879	
	ImageBanner.readFrame(int, int, ImageReader, int, ImageReadParam) line: 156	
	ImageBanner.readFrames(int, int, ImageInputStream) line: 145	
	ImageBanner.readFrames(int, int) line: 130	
	ImageBanner.printBanner(Environment, PrintStream) line: 103	
	ImageBanner.printBanner(Environment, Class<?>, PrintStream) line: 79	
	SpringApplicationBannerPrinter$Banners.printBanner(Environment, Class<?>, PrintStream) line: 146	
	SpringApplicationBannerPrinter.print(Environment, Class<?>, PrintStream) line: 72	
	SpringApplication.printBanner(ConfigurableEnvironment) line: 561	
	SpringApplication.run(String...) line: 298	
	DemoApplication.main(String[]) line: 31	

I think our best option is to drop the reset code and only call System.setProperty("java.awt.headless", "true"); if the existing property is null. To only other solution I can think of is to render the image using a new classloader that we then throw away.

philwebb avatar Nov 29 '21 21:11 philwebb

We now only set this property if not already set.

mhalbritter avatar Jan 18 '23 13:01 mhalbritter