I have a spring boot 2 app with embedded vuejs font-end at /static, using gradle 5.1. The app is designed to be hosted on different servers, so the API url for vuejs app must be dynamic.
The most reasonable solution i can think of is to set API_IP environment variables at the servers and let the vuejs app retrieve them like this:
let url = `http://${process.env.API_IP}:9090/api`;
if (process.env.NODE_ENV === 'development') {
url = `http://localhost:8090/api`;
}
gradle build script:
bootJar {
dependsOn ':ui:jar'
into('BOOT-INF/classes/static') {
from "${project(':ui').projectDir}/dist"
}
launchScript {
properties 'inlinedConfScript': 'startScript'
}
}
However, i'm failing to pass the environment variable to vue app. Each try ends up with process.env.API_IP being undefined. process.env always consists only of BASE_URL and NODE_ENV.
tried:
export API_IP=*ip* and then run the spring boot app.
set the variable in /etc/environment, logout, login, run the spring boot app
pass it as run argument -DAPI_IP=*api*
set JAVA_OPTS=-DAPI_IP=*api*
according to https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html :
create file for inlinedConfScript containing API_IP=*api* and JAVA_OPTS=-DAPI_IP=*api* or only one of each.
create .conf file in the same dir with the .jar file withe the same value options as in #5.
Please help :)
Partial solution was in removing the ip/domain from the vuejs requests as the #tianzhipeng's comment. However i can run the back-end only at port 8080 in this case.
Related
I explain my problem;
I have a web app developed using Vue.js and Spring Boot, this application working a PDF sheet and saves the file that is generated by Java, I use two lines of code to separate my development part from the production part (I leave you the 2 lines of code like this you understand the concept well)
FileReader leggoFile = new FileReader(System.getProperty("user.dir") + "/temp/webapps/foolder/foolder/file.pdf");
// FileReader leggoFile = new FileReader(System.getProperty("catalina.base") + "/temp/webapps/foolder/foolder/file.pdf");
This whole application is built using the "bootWar gradle plugin" which returns me a .war which I will then upload to a Tomcat server;
My goal is this:
I would like to set a single environment variable so that if I want to build the project I don't have to comment/uncomment that line for example:
FileReader leggoFile = new FileReader({{variableEnvironment}} + "/temp/webapps/foolder/foolder/file.pdf")
my question is this:
How dp Gradle and Spring Boot handle environments? Is there a way to separate environments? Is this possible or should I start thinking differently?
I tried to search on something but unfortunately I was tied to the problem that I don't understand how the .war file is generated through the BootWar Gradle plugin, also searching on the internet I understood that environment Gradle and environment Spring are two separate things but in general even if I know the line of code is wrong in the beginning my question is always the same:
How are environment variables handled in Spring and Gradle?
With Spring Boot, you can add properties to your application by adding an file named application.yaml to your resources folder (src/resources/). In addition you can add properties through application-{profile}.yaml to add properties only for given Spring profiles. For instance application-test.yaml would only be read if "test" is an active profile. When booting up the application, Spring will first read application.yaml, then any profile-specific YAML-files, such that any overlapping properties are replaced.
There are several approaches to injecting the property. A simple solution is to add a field to your component annotated with #Value("${PATH}) and replace PATH with the property's path in the YAML.
I have a spring boot app hosted on AWS Ubuntu instance, deployed using WAR deployment from Jenkins, in Apache tomcat(8.5.14) container.
I have one environment variable defined in application-test.properties file as below-
drehr.config.upload.image = /home/ubuntu/ehrFiles/
(application-test.properties file is used because active profile is set to test)
My java code accesses this property using following syntax:-
import org.springframework.beans.factory.annotation.Value;
#Value("${drehr.config.upload.image}")
private String imagePath;
But I am unable to get its value in imagePath variable, it is coming blank and the code is failing. Please provide some way to the solution for this.
Also tried changing the property value inside apache-tomcat-x/webapps/drehrweb-0.0.1/WEB-INF/classes/application-test.properties. Still doesn't work.
The strange thing is the same code works fine on development environment(using spring tool suite).
The situation is that I have two api projects, API A does HTTP requests to API B. Both API's are deployed to a development and production environment.
What I want to achieve is the following: Build the project based on a specific profile (dev or prod) so that the code can use a particular baseurl to talk with the correct api on the correct environment.
So if I build API A based on prod flag, I want it to use the specific url to make http requests to API B that is deployed on it's own prod environment.
It looks like you're referring to profiles of maven, however you should probably check out spring profiles. The concept should change :
You're not supposed to build different artifacts for different environments.
Instead create a spring profile in service A:
application-dev.properties:
url.addr=dev-service-host:1234
application-prod.properties:
url.addr=prod-service-b-host:4321
Then run the application with --spring.profiles.active=dev (or prod) flag.
Spring boot will load the correct definitions automatically because the dev/prod matches the suffix of properties file
You can define Spring-Boot profile as:
spring.profiles.active=prod
You also should have profiled .properties files in resources:
in application-dev.properties you should have api.b.url={api_b_url_on_dev_environment}
in application-prod.properties you should have api.b.url={api_b_url_on_prod_environment}
Or if you don't want to recompile your application after changing properties you may use outside .properties files.
In order for them to be included during app's deployment do the following:
in some config directory add application-dev.properties and application-prod.properties
deploy you app with the following properties: --spring.profiles.active=dev and --spring.config.additional-location=config/application.properties
This way the outside profiled properties will be included in deployment process. These .properties files have the highest priority in Spring.
My Spring Boot app runs perfectly at localhost but when I'm deploying it to Heroku, my app's controller stops seeing the views which are normally located at /templates/ directory. Why does this happen? How can I be sure that heroku actually uploads and compiles my views? If it does, should I change actual values of #RequestMapping of my #Controller class in order to make them reachable when they are at heroku?
You can find my whole working webapp here: https://github.com/slavicketernity/testik56
Here is my uploaded and runnung app: https://testik56app.herokuapp.com/login
In my case it was fault of the slashes on the beginning of the String returned by Controller's method with template location.
After I changed the String returned by controller's methods from
#RequestMapping(value = "/orders/{orderId}/create_entry")
String create(#PathVariable String orderId) {
return "/order_entries/create";
}
to
#RequestMapping(value = "/orders/{orderId}/create_entry")
String create(#PathVariable String orderId) {
return "order_entries/create";
}
then it started to work.
Are you deploying this application as a .jar? I have seen some infrastructures require that you deploy applications as a .war to provide access to your webpage directory.
If that is the issue you can apply the war plugin via gradle.
https://docs.gradle.org/current/userguide/war_plugin.html
I have an AWS lambda RequestHandler class which is invoked directly by AWS. Eventually I need to get it working with Spring Boot because I need it to be able to retrieve data from Spring Cloud configuration server.
The problem is that the code works if I run it locally from my own dev environment but fails to inject config values when deployed on AWS.
#Configuration
#EnableAutoConfiguration
#ComponentScan("my.package")
public class MyClass implements com.amazonaws.services.lambda.runtime.RequestHandler<I, O> {
public O handleRequest(I input, Context context) {
ApplicationContext applicationContext = new SpringApplicationBuilder()
.main(getClass())
.showBanner(false)
.web(false)
.sources(getClass())
.addCommandLineProperties(false)
.build()
.run();
log.info(applicationContext.getBean(SomeConfigClass.class).foo);
// prints cloud-injected value when running from local dev env
//
// prints "${path.to.value}" literal when running from AWS
// even though Spring Boot starts successfully without errors
}
}
#Configuration
public class SomeConfigClass {
#Value("${path.to.value}")
public String foo;
}
src/main/resources/bootstrap.yml:
spring:
application:
name: my_service
cloud:
config:
uri: http://my.server
failFast: true
profile: localdev
What have I tried:
using regular Spring MVC, but this doesn't have integration with #Value injection/Spring cloud.
using #PropertySource - but found out it doesn't support .yml files
verified to ensure the config server is serving requests to any IP address (there's no IP address filtering)
running curl to ensure the value is brought back
verified to ensure that .jar actually contains bootstrap.yml at jar root
verified to ensure that .jar actually contains Spring Boot classes. FWIW I'm using Maven shade plugin which packages the project into a fat .jar with all dependencies.
Note: AWS Lambda does not support environment variables and therefore I can not set anything like spring.application.name (neither as environment variable nor as -D parameter). Nor I can control the underlying classes which actually launch MyClass - this is completely transparent to the end user. I just package the jar and provide the entry point (class name), rest is taken care of.
Is there anything I could have missed? Any way I could debug this better?
After a bit of debugging I have determined that the issue is with using the Maven Shade plugin. Spring Boot looks in its autoconfigure jar for a META-INF/spring.factories jar see here for some information on this. In order to package a Spring Boot jar correctly you need to use the Spring Boot Maven Plugin and set it up to run during the maven repackage phase. The reason it works in your local IDE is because you are not running the Shade packaged jar. They do some special magic in their plugin to get things in the right spot that the Shade plugin is unaware of.
I was able to create some sample code that initially was not injecting values but works now that I used the correct plugin. See this GitHub repo to check out what I have done.
I did not connect it with Spring Cloud but now that the rest of the Spring Boot injection is working I think it should be straightforward.
As I mentioned in the comments you may want to consider just a simple REST call to get the cloud configuration and inject it yourself to save on the overhead of loading a Spring application with every request.
UPDATE: For Spring Boot 1.4.x you must provide this configuration in the Spring Boot plugin:
<configuration>
<layout>MODULE</layout>
</configuration>
If you do not then by default the new behavior of the plugin is to put all of your jars under BOOT-INF as the intent is for the jar to be executable and have the bootstrap process load it. I found this out while addressing adding a warning for the situation that was encountered here. See https://github.com/spring-projects/spring-boot/issues/5465 for reference.