Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context don't close #863

Closed
denberr95 opened this issue Sep 5, 2023 · 4 comments
Closed

Context don't close #863

denberr95 opened this issue Sep 5, 2023 · 4 comments
Labels
branch/3.1.x Issue for a branch for/backport For backporting type/enhancement Is an enhancement request
Milestone

Comments

@denberr95
Copy link

HOW TO USE

  1. VSCode
  2. Spring Boot 3.1.3
  3. Spring Shell 3.1.3

WHAT I'M DOING

I'm using new annotation model for implement a command line.
In my use case i need that my commands are executable with non-interactive mode.
I need after command completion that session will be closed automatically.

WHAT HAPPEN

This is my custom command

@Command(group = "Profiles Commands", command = "profile")
public class ProfilesCmd {

    @Autowired
    private ShellCommandsHandler component;

    @Command(description = "Register a cluster into a credential file", command = "create", interactionMode = InteractionMode.ALL)
    public void create(
            @Option(description = "Name of the cluster", required = true) String name,
            @Option(description = "Setup topology of API Management (Accepted values: CLOUD, LOCAL)", required = true) @Pattern(message = "Accepted values: CLOUD, LOCAL", regexp = "CLOUD|LOCAL") String envType,
            @Option(description = "API host", defaultValue = "api.mashery.com") String apiHost,
            @Option(description = "API port", defaultValue = "443") int apiPort,
            @Option(description = "API Base Path", defaultValue = "/v3") String apiBasePath,
            @Option(description = "Enable HTTPS", defaultValue = "true") boolean useSsl,
            @Option(description = "Flag to run command in interactive or non-interactive mode, default is true", defaultValue = "true") boolean interactive) {
        component.createProfile(name, envType, apiHost, apiPort, apiBasePath, useSsl);
    }
}

This is my application.properties where I disabled scrips and interactive mode:

################### SPRING SHELL CONFIG ###################
spring.shell.command.version.enabled=true
spring.shell.command.version.show-build-artifact=false
spring.shell.command.version.show-build-group=false
spring.shell.command.version.show-build-name=true
spring.shell.command.version.show-build-time=true
spring.shell.command.version.show-build-version=true
spring.shell.command.version.show-git-commit-id=true
spring.shell.command.version.show-git-commit-time=false
spring.shell.command.version.show-git-short-commit-id=true
spring.shell.command.history.enabled=true
spring.shell.history.name=logs/shell-history.log
spring.shell.command.clear.enabled=true
spring.shell.command.completion.enabled=true
spring.shell.command.help.enabled=true
spring.shell.command.help.grouping-mode=GROUP
spring.shell.command.quit.enabled=true
spring.shell.command.stacktrace.enabled=true
spring.shell.interactive.enabled=false
spring.shell.noninteractive.enabled=true
spring.shell.script.enabled=false

When I run the command in non-interactive mode The program wait the SIGTERM:
image

@github-actions github-actions bot added the status/need-triage Team needs to triage and take a first look label Sep 5, 2023
@jvalkeal
Copy link
Contributor

jvalkeal commented Sep 9, 2023

Do you have something in ApplicationContext which prevents it to shutdown automatically? If that's the case I've been thinking if we should try to close it automatically when runner logic in non-interactive completes.

@denberr95
Copy link
Author

I have not modified or customized the application context.
My ApplicationContext was loaded by SpringRunner

package com.tam;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.ApplicationPidFileWriter;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.shell.command.annotation.CommandScan;

@CommandScan
@EnableScheduling
@EnableAspectJAutoProxy
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
		springApplication.addListeners(new ApplicationPidFileWriter());
		springApplication.run(args);
    }
}

Now I have implemented a Work Around like this:

package com.tam.cli.components.aspects;

import com.tam.cli.utils.ShellHelper;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ShellAspects {

    @Autowired
    ShellHelper shellHelper;

    @Pointcut("@annotation(com.tam.cli.components.annotations.AIShellExecutionTime)")
    public void shellLogPointCut() {
        // Pointcut
    }

    @Pointcut("@annotation(org.springframework.shell.command.annotation.Command) && execution(* com.tam.cli.components.cmd..* (..))")
    public void shellRunModePointCut() {
        // Pointcut
    }

    @Around(value = "shellLogPointCut()")
    public Object shellExecution(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object proceed = null;
        long startTime = System.currentTimeMillis();
        try {
            proceed = proceedingJoinPoint.proceed();
        } catch (Exception ex) {
            shellHelper.printError("Operation got exception");
            throw ex;
        }
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        shellHelper.printInfo("Operation executed in ".concat(String.valueOf(executionTime)).concat("ms"));
        return proceed;
    }

    @After("shellRunModePointCut() && args(.., interactive)")
    public void shellRunMode(JoinPoint joinPoint, boolean interactive) {
        if (!interactive) {
            shellHelper.printInfo("Command completed !");
            System.exit(0);
        }
    }
}

With an Aspect shellRunModePointCut I check if the method contains interactive param and if it is true the program force exit.

@jvalkeal
Copy link
Contributor

It's @EnableScheduling which keeps context alive.

One other more clean workaround is to request context close when boot notifies ApplicationReadyEvent.

@Component
public class DemoApplicationListener implements ApplicationListener<ApplicationEvent>{

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationReadyEvent e) {
			e.getApplicationContext().close();
		}
	}

}

This works as shell logic is executed with boot's ApplicationRunner and ApplicationReadyEvent is posted after shell has completed it's stuff. We could add this to spring-shell itself behind a configuration property. spring-cloud-task has similar logic build-in and it has worked relatively well.

@denberr95
Copy link
Author

Thanks for your help.
I've used your sample and it's work well.
It would be good to make it configurable so as to avoid writing code and simplify use.

@jvalkeal jvalkeal changed the title Running non-interactive mode does not work Context don't close Sep 11, 2023
@jvalkeal jvalkeal added type/enhancement Is an enhancement request and removed status/need-triage Team needs to triage and take a first look labels Sep 11, 2023
@jvalkeal jvalkeal added this to the 3.2.0-M2 milestone Sep 11, 2023
@jvalkeal jvalkeal added for/backport For backporting branch/3.1.x Issue for a branch labels Sep 11, 2023
jvalkeal added a commit to jvalkeal/spring-shell that referenced this issue Sep 12, 2023
- spring.shell.context.close=true adds ApplicationListerner
  which attempts to close context with ApplicationReadyEvent.
- Relates spring-projects#863
jvalkeal added a commit to jvalkeal/spring-shell that referenced this issue Sep 12, 2023
jvalkeal added a commit that referenced this issue Sep 21, 2023
- spring.shell.context.close=true adds ApplicationListerner
  which attempts to close context with ApplicationReadyEvent.
- Backport #863
- Relates #866
jvalkeal added a commit that referenced this issue Sep 21, 2023
- Backport #863
- Relates #866
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
branch/3.1.x Issue for a branch for/backport For backporting type/enhancement Is an enhancement request
Projects
None yet
Development

No branches or pull requests

2 participants