Spring Boot: can't get API key into the docker container - java

I created an spring boot application that makes API calls. I have been given an API key that I added to my system environment variables as API_KEY
At first, I had my urls as
String uri = "http://api-site.com/.../key="+System.getenv("API_KEY")+"&..."
and this works just fine when I run it locally.
However, when I build and run the image, I get 403 error.
My Dockerfile looks like:
FROM maven:3.5.4-jdk-8-alpine AS builder
COPY pom.xml pom.xml
COPY src/ src/
RUN mvn clean package
FROM openjdk:11 AS runner
**ENV API_KEY $API_KEY**
EXPOSE 8080
COPY --from=builder target/app-0.0.1.jar app.jar
ENTRYPOINT [ "java", "-jar", "app.jar" ]
I'm guessing System.getenv("API_KEY") does not work in containers.
Is there a way to get it to work?
I've tried the other syntax for the environment variable in the Dockerfile: ENV API_KEY=${API_KEY}
I have also tried using application.properties but I have not been able to get that to work (this might need to be a whole other question). I can't even get test variables to print.
I have test.name=testname in applications.properties
In a class I have:
#Component
#PropertySource("classpath:application.properties")
public class AC {
#Value("${test.name}")
public String test;
public static void main(String[] args){
System.out.println(test);
}
}
This just prints out null every time.
Thank you in advance!

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.

Spring Boot App does not recognize environment variables in application.yml file

there. I'm new to Java Spring Boot and I'm trying to set environment variables in application.yml.
I've added dotenv maven dependency:
<!-- https://mvnrepository.com/artifact/io.github.cdimascio/java-dotenv -->
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>java-dotenv</artifactId>
<version>5.1.3</version>
</dependency>
I've set variables in the .env file:
SPRING_DATABASE_URL = jdbc://db_url
SPRING_DATABASE_USERNAME = username
SPRING_DATABASE_PASSWORD = password
And in my application.yml:
spring:
datasource:
url: ${SPRING_DATABASE_URL}
username: ${env.SPRING_DATABASE_USERNAME}
password: ${env.SPRING_DATABASE_PASSWORD}
While running application I'm getting jdbc error:
java.lang.RuntimeException: Driver org.postgresql.Driver claims to not accept jdbcUrl, ${SPRING_DATABASE_URL}
I've tried some solutions like:
export SPRING_DATABASE_URL = jdbc://db_url
or in application.yml:
url: ${env.SPRING_DATABASE_URL}
or
url: ${env.SPRING.DATABASE.URL}
or
url: ${SPRING.DATABASE.URL}
Am I doing something wrong or missing? I appreciate your help, thank you.
I recently ran in a similar issue and wanted to set environment variables via .env with application.yml - here is what I found out:
First, as you mentioned you have to add the java-dotenv dependency to pom.xml:
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>2.2.0</version>
</dependency>
Then create your .env file in the root of your project (where pom.xml is located) and write your environment variables like e.g. ENV_PORT=8081.
Before you can use this environment variable, you have to "bind" the content of the .env file with Spring Boot when you start the app to make it globally available. According to this thread, this can be achieved by simply altering your Main entry point of spring (where you init the framework) like so:
#SpringBootApplication
public class MySpringApplication {
public static void main(String[] args) {
Map<String, Object> env = Dotenv.load()
.entries()
.stream()
.collect(
Collectors.toMap(DotenvEntry::getKey, DotenvEntry::getValue));
new SpringApplicationBuilder(MySpringApplication.class)
.environment(new StandardEnvironment() {
#Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
propertySources.addLast(new MapPropertySource("dotenvProperties", env));
}
}).run(args);
}
}
That's it, now you can reference to your environment variables in application.yml like so:
server:
port: ${ENV_PORT}
Hope this helps! If you are interested, here is also a full working example where I am using this approach.
java-dotenv you use has been renamed to kotlin-dotenv. If you look at its documentation, you'll see how it's used:
With Java
import io.github.cdimascio.dotenv.Dotenv;
Dotenv dotenv = Dotenv.load();
dotenv.get("MY_ENV_VAR1")
That's not what spring boot does - spring boot will not use any dotenv to fetch environment variables, so naturally the plugin won't work for your use case so .env files will not work.
If you start your app defining the environment variable when starting it app, it will work. To see how environment variables are passed using vscode, see this thread.

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).

How to start a spring boot tomcat server on a specified port through command prompt

I have a very simple "Hello World" kind of REST api created using Spring Boot that is accessible through http://localhost:8080/greeting/world without any problem.
I would like to start two more instances of this API on ports 8081 and 8082 but not able to do so. It says java.net.BindException: Address already in use: bind
Command Used:
mvn spring-boot:run -Dserver.port=8081
application.yml
spring:
application:
name: world-greeting-service
WorldGreetingServiceApplication.java
#RestController
#SpringBootApplication
public class WorldGreetingServiceApplication {
public static void main(String[] args) {
SpringApplication.run(WorldGreetingServiceApplication.class, args);
}
#RequestMapping("/greeting/world")
public String greetWorld() {
return "Hello World!";
}
}
Am I missing anything?
Try using,
mvn spring-boot:run -Drun.jvmArguments='-Dserver.port=8081'
java -jar world-greeting-service.jar --server.port=8081
This command is an equivalent of above answer and can be used when you don't have maven installed

How to pass JVM options from bootRun

I'm developing simple Spring web application that communicates with remote host and I would like to test it locally behind corporate proxy.
I use "Spring Boot" gradle plugin and the question is how can I specify proxy settings for JVM?
I have try several ways to do it:
gradle -Dhttp.proxyHost=X.X.X.X -Dhttp.proxyPort=8080 bootRun
export JAVA_OPTS="-Dhttp.proxyHost=X.X.X.X -Dhttp.proxyPort=8080"
export GRADLE_OPTS="-Dhttp.proxyHost=X.X.X.X -Dhttp.proxyPort=8080"
But it seems like none of them work - "NoRouteToHostException" throws in "network" code.
Also, I have added some extra code to debug JVM start arguments:
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List<String> arguments = runtimeMxBean.getInputArguments();
for (String arg: arguments) System.out.println(arg);
And only one argument was printed: "-Dfile.encoding=UTF-8".
If I set system property in code:
System.setProperty("http.proxyHost", "X.X.X.X");
System.setProperty("http.proxyPort", "8080");
Everything works just fine!
Original Answer (using Gradle 1.12 and Spring Boot 1.0.x):
The bootRun task of the Spring Boot gradle plugin extends the gradle JavaExec task. See this.
That means that you can configure the plugin to use the proxy by adding:
bootRun {
jvmArgs = "-Dhttp.proxyHost=xxxxxx", "-Dhttp.proxyPort=xxxxxx"
}
to your build file.
Of course you could use the systemProperties instead of jvmArgs
If you want to conditionally add jvmArgs from the command line you can do the following:
bootRun {
if ( project.hasProperty('jvmArgs') ) {
jvmArgs project.jvmArgs.split('\\s+')
}
}
gradle bootRun -PjvmArgs="-Dwhatever1=value1 -Dwhatever2=value2"
Updated Answer:
After trying out my solution above using Spring Boot 1.2.6.RELEASE and Gradle 2.7 I observed that it was not working as some of the comments mention.
However, a few minor tweaks can be made to recover the working state.
The new code is:
bootRun {
jvmArgs = ["-Dhttp.proxyHost=xxxxxx", "-Dhttp.proxyPort=xxxxxx"]
}
for hard-coded arguments, and
bootRun {
if ( project.hasProperty('jvmArgs') ) {
jvmArgs = (project.jvmArgs.split("\\s+") as List)
}
}
for arguments provided from the command line
bootRun {
// support passing -Dsystem.property=value to bootRun task
systemProperties = System.properties
}
This should pass all JVM options to the app started via bootRun.
In gradle build script, define systemProperties for run task.
//to provide the properties while running the application using spring-boot's run task
run {
systemProperties['property name'] = 'value'
}
and gradle run should accept this value.
Or define a project level property as mentioned in
http://forums.gradle.org/gradle/topics/how_can_i_provide_command_line_args_to_application_started_with_gradle_run
#marvin, thanks for your post it was very helpful.
Sharing how I used it:
test {
// support passing -Dsystem.property=value to bootRun task
systemProperties = System.properties
}
I have JUnit tests that I wanted to skip unless a property was used to include such tests. Using JUnit Assume for including the tests conditionally:
//first line of test
assumeThat(Boolean.parseBoolean(System.getProperty("deep.test.run","false"),true)
Doing this with gradle required that the system property provided at the time of running gradle build, shown here,
gradle build -Ddeep.test.run=true
was indeed passed through to the tests.
Hope this helps others trying out this approach for running tests conditionally.
bootRun {
args = ['myProgramArgument1', 'myProgramArgument2']
}
Using jvmArgs may cause JVM start issues. Using args allows you to pass your custom program arguments
It seems to work:
bootRun {
systemProperties "property1": "value1", "property2": "value2"
}
I got into a similar problem, bootRun needed some parameters but I wouldn't feel like modifying bootRun as I want to keep some flexibility and stick to standard bootRun behaviour. My suggestion is to add some custom tasks (let's say bootRunDev, bootRunProxy) that extends bootRun, as described in the following code snippet
task bootRunPxy(type: org.springframework.boot.gradle.run.BootRunTask, dependsOn: 'build') {
group = 'Application'
doFirst() {
main = project.mainClassName
classpath = sourceSets.main.runtimeClasspath
systemProperty 'http.proxyHost', 'xxxxx'
systemProperty 'http.proxyPort', 'yyyyy'
}
}
I don't have an environment to exercise the script but I used this approach to pass profile to spring using the property spring.profiles.active.
Credits should go to Karol KaliƄski
It's worth mentioning, here, that some systems that use Gradle and Spring Boot are starting JVM outside of build.gradle, e.g. in a Dockerfile.
It's not pointless to mention this on a thread specifically about bootRun! I wound up here because this particular post is a magnet for searches about jvm options in the context of a Spring Boot app compiled / run under gradle. (All the advice I found for adding java.net.http.httpclient logging said "add it to bootRun's jvmArgs". Nothing happened, though.
So, if you happen to run your gradle-built Spring Boot app from a Docker container, you'll want to add your JVM params to an env var in your project's Dockerfile, like so, e.g.-
...
ENV JAVA_OPTS "${JAVA_OPTS} \
-server \
-Duser.timezone=UTC \
-XX:InitialRAMPercentage=50 \
-XX:MaxRAMPercentage=50 \
-Djavax.net.ssl.trustStorePassword=elvislives \
-Djavax.net.ssl.trustStoreProvider=BCFIPS \
-Djavax.net.ssl.trustStoreType=BCFKS \
-Djdk.internal.httpclient.debug=true \
-Djava.util.logging.manager=org.apache.logging.log4j2.jul.LogManager \
-Djdk.httpclient.HttpClient.log=errors,requests,headers,frames[:control:data:window:all..],content,ssl,trace,channel \
"
...
ENTRYPOINT java ${JAVA_OPTS} -cp app:app/lib/* com.mygreatcompany.theapp
For development as Docker Container add to run_script.sh as JAVA_OPTS
JAVA_OPTS="-XX:+UseG1GC
-Xms512m
-Xmx2048m
--add-opens java.base/java.util=ALL-UNNAMED -Dspring.profiles.active=$PROFILE,discovery"

Categories