Spring Boot: Restart programtatically including VM and program arguments and classpath - java

We have a spring boot web application that we start with a modified, extended start command similar to this:
java -classpath "..." -Dlogging.config="..." org.springframework.boot.loader.JarLauncher --debug --spring.config.location="..."
Now, we need to restart this application programatically (e.g. via an endpoint), but we need to ensure that the "same start command is used".
I found this solution on bealdung:
#SpringBootApplication
public class Application {
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(Application.class, args);
}
public static void restart() {
ApplicationArguments args = context.getBean(ApplicationArguments.class);
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(Application.class, args.getSourceArgs());
});
thread.setDaemon(false);
thread.start();
}
}
In this case, the program arguments can be extracted with the bean ApplicationArguments.
So far I did not find a way to do the same for VM arguments and also the extension of the classpath as seen in the command above.
Is that even possible? Is there a more convenient way to restart a spring boot application with all these requirements in mind?

The way you've found in tutorial does not restart the whole JVM process, it closes the application context and re-creates it. This means that all the beans get loaded again which is logically equivalent to restart as far as all the objects are managed by Spring or course.
There as also a way of making such a restart without writing custom code - use Spring Boot's actuator that has a restart endpoint. You can invoke the endpoint and the application will be restarted in the same sense as you've described. See this tutorial for example there are many others.
In both of the ways, there will be the same JVM process (jvm does not restart), so both classpath and JVM parameters will remain just the same (do not change).
So if you want to restart the JVM you can wrap the long command into some sort of script (like bash if you're working on linux, or *.bat file for windows for example).
Other than that spring boot can't help much - from this perspective its a regular java application.

Related

Java application not reading environment variables from .env file

I am creating a Slack bot using Bolt framework in Java + Maven following this tutorial on their official website. My bot requires some keys that should be present as environment variables.
The application works just fine when I export those variables in the terminal prior to executing the application like this.
export SLACK_BOT_TOKEN=xoxb-...your-own-valid-one
export SLACK_SIGNING_SECRET=123abc...your-own-valid-one
mvn compile exec:java -Dexec.mainClass="hello.MyApp"
But when I don't, and instead save them in a .env file, the App constructor apparently doesn't get those keys even though I have loaded them in the main method from .env file.
Here is my main method:
package hello;
import com.slack.api.bolt.App;
import com.slack.api.bolt.jetty.SlackAppServer;
import io.github.cdimascio.dotenv.Dotenv;
public class MyApp {
public static void main(String[] args) throws Exception
{
Dotenv dotenv = Dotenv.configure().directory("./").load();
System.out.println(dotenv.get("SLACK_BOT_TOKEN"));
System.out.println(dotenv.get("SLACK_SIGNING_SECRET"));
// CREATING THE SLACK APP
App slackApp = new App();
slackApp.command("/mytesthello", (req, ctx)-> {
return ctx.ack(":wave: Hello!");
});
SlackAppServer slackAppServer = new SlackAppServer(slackApp);
slackAppServer.start();
}
}
The console is able to show the correct output using System.out.println indicating that the variables are accessible, but most probably the App() constructor is not able to get them, since whenever I try to make POST request from my slack app, the ngrok interface as well as my terminal show unauthorized access. (again, this is the case when I don't export the environment variables)
What should I do to make sure that the environment variables are available from .env file? Otherwise it would be very cumbersome to keep exporting them whenever I run the application.

IntelliJ is not running Spring Boot Debugger

I am trying to debug a Spring boot project with IntelliJ. Suddenly, my lines do not debug after working for last few years.
It will debug through the class, but when i try to run a method, it will not see the breakpoint. I got an application context message earlier, which I fixed, but its still not working. https://www.jetbrains.com/help/idea/2021.2/spring-support.html#spring-file-set
It will see/execute these lines in the background (like its one another thread), but not will debug/catch the method lines.
#Autowired
public EncounterFacade(SuperbillRepository superbillRepository, EncounterRepository encounterRepository) {
this.superbillRepository = superbillRepository;
this.encounterRepository = encounterRepository;
}
Currently using a Mac system. It was already running Local host port in the background,
Ran npx kill-port 8080 to resolve the issue and it worked.

Start Dropwizard with config.yaml from resources

I have a dropwizard question. I use Dropwizard with SBT (which works pretty fine).
If I run my application i package it with:
$ sbt clean assembly
And than run the application with:
$ java -jar APPLICATION.jar server
The problem is with this command Dropwizard doesnt load my config file (config.yaml), which is in the resources located.
Regarding the Dropwizard Docs I always have to give the config file as parameter like:
$ java -jar APPLICATION.jar server config.yaml
This works fine and it loads the application but is there any possibility to tell Dropwizard to load directly the config.yaml file, because my configuration in the config.yaml file is static and it is always the same. Settings like Database etc which are changing from Server Stage to Server Stage are made as Enviroment Variable which I load with EnvironmentVariableSubstitutor.
Thanks
Use class ResourceConfigurationSourceProvider:
#Override
public void initialize(final Bootstrap<ExampleConfiguration> bootstrap) {
bootstrap.setConfigurationSourceProvider(new ResourceConfigurationSourceProvider());
// The rest of initialize...
}
And then invoke the application like:
java -jar APPLICATION.jar server /resource-config.yaml
(note the initial /)
While this answer is very late, just thought I'd put this here. There is a dirty little hack to make it work so that you don't have to provide config.yaml in your application arguments.
Basically, you can submit a new String[] args to the run() method in the dropwizard application.
public class ApplicationServer extends Application<Config> {
public static void main(String[] args) {
String[] appArgs = new String[2];
appArgs[0] = args[0]; // This will be the usual server argument
appArgs[1] = "config.yaml";
new ApplicationServer().run(appArgs);
}
#Override
public void run(Config configuration, Environment environment) {
// Configure your resources and other application related things
}
}
I used this little trick to specify which config file I wanted to run with. So instead of specifying config.yaml, I would give my second argument as DEV/UAT/STAGE/PROD and pass on the appropriate config file to the run method.
Also interesting to have a look at:
earlye/dropwizard-multi-config
<dependency>
<groupId>com.thenewentity</groupId>
<artifactId>dropwizard-multi-config</artifactId>
<version>{version}</version>
</dependency>
It allows overriding and merging multiple config-files passed on the java command-line like:
java -jar sample.jar server -- sample.yaml override.yaml
Here you pass (1) sample.yaml as the primary configuration (e.g. having default values) and (2) override.yaml as the override. The effective config is a result from merging both in order of appearance: (1) defaults will be overwritten and merged with (2).

Bundled JavaFX application runs with non-English arguments throws "Could not start JVM"

I have a problem with running simple JavaFX application. I'm using native packager to build exe. I want to run my application from windows file context menu, but when I try to run application with params:
> JavaFXApplication1.exe ąęć
and i'm getting 2 errors:
1) , and after click "OK":
2)
When i run application as jar file:
> java -jar JavaFXApplication1.jar ąęć
Application started with success and it's working fine.
Also, when I run application from this command:
> JavaFXApplication1.exe aec
everything is ok and application's working fine.
Example application code:
package test;
import javafx.application.Application;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
System.out.println("Started");
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Args count: " + args.length);
launch(args);
}
}
Ignore, that application doesn't close.
I think that it's exactly the same problem as described here: https://bugs.openjdk.java.net/browse/JDK-8133034
Did somebody resolve this? Or is there someone, who knows a workaround of this problem? I tried java versions from 1.8.40 up to 1.8.72, but it failed on each JVM. It's also not a enviroment problem, because i've tested it on 2 different machines (and also operating systems).
I would appreciate any help.
The Jira issue mentions in a comment that it is a regression starting JDK version 8u40b06. If possible one workaround is to use an older one. Try 8u40b05.
For the moment, maybe you can store all command arguments in a file (named in ASCII characters) and pass the file location as an argument in order to read the actual arguments of the application.
Another workaround (last resort?) is to pass characters as escaped Unicode. See Convert escaped Unicode character back to actual character. Although admittedly it's an ugly and tedious thing to do.

maven jetty does not shutdown properly in eclipse

I am currently running jetty from eclipse as an external java program. The problem is when I terminate jetty and I try to relaunch it again, it cannot due to the port still being in use.
What I have to do is open up Windows Task Manager and kill the java process manually. How do you get jetty to shutdown/terminate/end nicely?
The following are my maven jetty application settings
Arguments: jetty:run-war
MAVEN_OPTS: -Xdebug -Xnoagent
-Djava.compiler=NONE -Xrunjdwp:transport=dt_socket, address=8080,server=y, suspend=y
Setting suspend=n doesn't seem to solve the problem either.
If a java application does not shutdown it is because of an alive non-daemon thread. Try getting a thread dump of the running maven process, e.g. using VisualVM and see what keeps the application alive.
You could run the application via the 'Run Jetty Run' Eclipse plugin, rather than the Maven jetty plugin. Eclipse has more direct control over the new JVM then.
I'm not familiar with the maven plug-in, but when starting jetty with start.jar, you can use stop.jar to have it shutdown gracefully (since version 4.something).
Stopping Jetty
Programs started with the start.jar mechanism may be stopped with the
stop.jar:
java -jar stop.jar
This connects via a local port to stop the server. The default port can be set
with the STOP.PORT system property
Source
Alternatively, and maybe more to your liking, you can do it within eclipse by writing a class like this, and running the main method:
package mypackage;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class Stop {
public static final int PORT = 8079; //Change to whatever your port is, 8079 is default
public static void main(String[] args) throws Exception {
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), PORT);
OutputStream out = s.getOutputStream();
System.out.println("*** sending jetty stop request");
out.write(("\r\n").getBytes());
out.flush();
s.close();
}
}
Source
It seems that I forgot to tick "Allow termination of remote VM". Strange that it didn't seem to work before.

Categories