0

I'm experimenting with an ability of Spring Boot to start a main class supplied to it via -Dloader.main parameter, as described in its documentation.

Currently we use Spring Boot 2.7.13.

Here's a simple shell for a sample Boot application with a main() being executed:

@Slf4j
@SpringBootApplication
@PropertySource(value = "classpath:feeds-config.yml", factory = YamlPropertySourceFactory.class)
public class DownstreamDataFactoryApplication implements CommandLineRunner { 
            
        
    @Autowired
    private DataAcquisitionService dataAcquisitionService;
    
    @Autowired
    private DataMarshallerService dataMarshallerService;
  
    public static void main(String[] args) {

        new SpringApplicationBuilder(DownstreamDataFactoryApplication.class)
          .web(WebApplicationType.NONE)
          .run(args);
    }

    @Override
    public void run(String... args) throws Exception {
        log.debug("Starting execution");
        
        for (int i = 0; i < args.length; ++i) {
            log.info("args[{}]: {}", i, args[i]);
        }
        
        //read data
        Map<String, Object> context = new HashMap<>();
        //Flux<?> data = dataAcquisitionService.obtainData(context);
        List<?> data = dataAcquisitionService.obtainData(context);
        log.info("Obtained data: "+data);
        
        //process data
        log.info("Processed data: ");
        
        //write data
        //log.debug("Report saved to {}", dataMarshallerService.getOutputFileLocation());
        //log.debug("Ending execution");
        log.info("Marshalled data to a file: ");
        
        //distribute data
        log.info("Send marshalled data to a remote location: ");
    }

}

My goal is to have several independent Spring Boot applications segregated by package structure in the codebase such that depending on what's being passed via -Dloader.maindifferent Boot application is being run.

Consequently, the mainClass stanza in spring-boot-maven-plugin's section is disabled:

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
            <execution>
                <goals>
                    <goal>repackage</goal>
                </goals>
                <configuration>
                    <mainClass>
                     <!-- 
                    com.p.g.f.d.DownstreamDataFactoryApplication
                    -->
                    </mainClass>
                </configuration>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M6</version>
    </plugin>

So far, so good.

What I'm noticing is that the runtime seems to behave exactly the same whether I'm passing the org.springframework.boot.loader.PropertiesLauncher as a second argument or not.

Here's what the output looks like with the org.springframework.boot.loader.PropertiesLauncher being supplied:

>java -jar target\downstream-data-factory-0.0.1-SNAPSHOT.jar -Dloader.main=com.xxx.globalpayments.feeds.downstream.DownstreamDataFactoryApplication org.springframework.boot.loader.PropertiesLauncher


2023-07-31 16:14:03.287 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Started DownstreamDataFactoryApplication in 2.981 seconds (JVM running for 4.425)
2023-07-31 16:14:03.291 [main] DEBUG o.s.b.a.ApplicationAvailabilityBean - Application availability state LivenessState changed to CORRECT
2023-07-31 16:14:03.292 [main] DEBUG c.p.g.f.d.DownstreamDataFactoryApplication - Starting execution
2023-07-31 16:14:03.293 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - args[0]: -Dloader.main=com.pru.globalpayments.feeds.downstream.DownstreamDataFactoryApplication
2023-07-31 16:14:03.293 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - args[1]: org.springframework.boot.loader.PropertiesLauncher
2023-07-31 16:14:03.294 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Obtained data: null
2023-07-31 16:14:03.295 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Processed data:
2023-07-31 16:14:03.296 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Marshalled data to a file:
2023-07-31 16:14:03.297 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Marshalled data to a remote location:
2023-07-31 16:14:03.300 [main] DEBUG o.s.b.a.ApplicationAvailabilityBean - Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2023-07-31 16:14:03.306 [SpringApplicationShutdownHook] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6c130c45, started on Mon Jul 31 16:14:01 EDT 2023
2023-07-31 16:14:03.310 [SpringApplicationShutdownHook] DEBUG o.s.c.s.DefaultLifecycleProcessor - Stopping beans in phase -2147483647
2023-07-31 16:14:03.311 [SpringApplicationShutdownHook] DEBUG o.s.c.s.DefaultLifecycleProcessor - Bean 'springBootLoggingLifecycle' completed its stop procedure

and without it being supplied:

>java -jar target\downstream-data-factory-0.0.1-SNAPSHOT.jar -Dloader.main=com.xxx.globalpayments.feeds.downstream.DownstreamDataFactoryApplication


2023-07-31 16:18:39.720 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Started DownstreamDataFactoryApplication in 2.931 seconds (JVM running for 4.313)
2023-07-31 16:18:39.725 [main] DEBUG o.s.b.a.ApplicationAvailabilityBean - Application availability state LivenessState changed to CORRECT
2023-07-31 16:18:39.726 [main] DEBUG c.p.g.f.d.DownstreamDataFactoryApplication - Starting execution
2023-07-31 16:18:39.726 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - args[0]: -Dloader.main=com.pru.globalpayments.feeds.downstream.DownstreamDataFactoryApplication
2023-07-31 16:18:39.728 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Obtained data: null
2023-07-31 16:18:39.728 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Processed data:
2023-07-31 16:18:39.729 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Marshalled data to a file:
2023-07-31 16:18:39.729 [main] INFO  c.p.g.f.d.DownstreamDataFactoryApplication - Marshalled data to a remote location:
2023-07-31 16:18:39.731 [main] DEBUG o.s.b.a.ApplicationAvailabilityBean - Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2023-07-31 16:18:39.734 [SpringApplicationShutdownHook] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6c130c45, started on Mon Jul 31 16:18:37 EDT 2023
2023-07-31 16:18:39.739 [SpringApplicationShutdownHook] DEBUG o.s.c.s.DefaultLifecycleProcessor - Stopping beans in phase -2147483647
2023-07-31 16:18:39.740 [SpringApplicationShutdownHook] DEBUG o.s.c.s.DefaultLifecycleProcessor - Bean 'springBootLoggingLifecycle' completed its stop procedure

My assumption was that the org.springframework.boot.loader.PropertiesLaunchercarries within itself certain keys that the mainclass-less invocation via -Dloader.main=AppwithMainMethod would be dependent upon and would otherwise failed, but it doesn't seem to be the case.

What are the proper use cases for this PropertiesLauncher parameter with some samples and documentation? Is it optional? Required? Can one safely skip supplying it? When?

Thank you in advance.

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
  • The `-jar` does exactly what it is supposed to do, launch a jar, it will ignore everything else. If you want to use the `PropertiesLauncher` you have to use `-cp` (classpath) to make the jar part of the classpath instead for it to being launched. Removing the `Main-Class` will not remove it from the manifest, as the main is alway the `JarLauncher` and it will detect the `Start-Class` (IIRC). – M. Deinum Aug 01 '23 at 12:58
  • Try this: https://stackoverflow.com/a/76811967/3710490 and you can run just with `java -jar target\downstream-data-factory-0.0.1-SNAPSHOT.jar` – Valijon Aug 01 '23 at 13:12

1 Answers1

-1

In the command line:

java -jar target\downstream-data-factory-0.0.1-SNAPSHOT.jar -Dloader.main=com.xxx.globalpayments.feeds.downstream.DownstreamDataFactoryApplication org.springframework.boot.loader.PropertiesLauncher

The strings -Dloader.main=com.xxx.globalpayments.feeds.downstream.DownstreamDataFactoryApplication and org.springframework.boot.loader.PropertiesLauncher are passed to main as command line parameters. They aren't interpreted by java or Spring Boot.

The correct command line is:

java -Dloader.main=com.example.demo.DemoApplication3 \
  -cp target/demo-0.0.1-SNAPSHOT.jar \
  org.springframework.boot.loader.PropertiesLauncher

assuming that your application class is com.example.demo.DemoApplication3

I would have expected that setting the mainClass to org.springframework.boot.loader.PropertiesLauncher in the spring-boot-maven-plugin configuration would have allowed this to work with -jar, but that gives me a stack overflow error.

That jar was built with a plain config:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.demo.DemoApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

You can set mainClass to whatever you like -- one of the applications, to use as a default, or a class which gives a usage message if loader.main hasn't been set and the jar is run with -jar.

tgdavies
  • 10,307
  • 4
  • 35
  • 40