Skip to content

Blog

Three simple ways to read properties of spring boot configuration files

1. The purpose of this post

In this post, I will demonstrate three simple ways to read properties files into spring boot applications,including the @Value ,@Configuration and @PropertySource annotations.

2. Environments

  • springboot
  • java 1.8+

3. Way 1: Read from application.properties by @Value annotation

Add these properties to your default configuration file:

src/main/resources/application.properties
info.name=Mike
info.desc=Good boy

Use @Value like this:

src/main/java/com/test/sb1jt/commands/MyCommand.java
package com.test.sb1jt.commands;
import com.test.sb1jt.config.InfoConfig;
import com.test.sb1jt.config.SysConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommand implements CommandLineRunner {
private static Logger logger = LoggerFactory.getLogger(MyCommand.class);
@Value("${info.name}") //1.Auto configure name property by reading info.name from application.properties
private String name;
@Value("${info.desc}") //2.Auto configure desc property by reading info.desc from application.properties
private String desc;
@Override
public void run(String... strings) throws Exception {
logger.info("MyCommand run");
logger.info("name is {},desc is {}",name,desc); //3. print the name and desc
}
}

Run the code ,we get this:

Terminal window
2019-05-23 12:45:58.827 INFO 31595 --- [ main] com.test.sb1jt.commands.MyCommand : name is Mike,desc is Good boy

4. Way 2: Read from application.properties by @ConfigurationProperties annotation

Instead of @Value annotion, you can use @ConfigurationProperties, which can read some specific prefixed properties from your application.properties to your class properties.

Add these properties to your spring boot application.properties:

src/main/resources/application.properties
info.name=Mike
info.desc=Good boy

Add a class which use @ConfigurationProperties like this:

src/main/java/com/test/sb1jt/config/InfoConfig.java
package com.test.sb1jt.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "info")
public class InfoConfig {
private String name; // info.name mapped to this property
private String desc; // info.desc mapped to this property
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}

Then print properties like this:

src/main/java/com/test/sb1jt/commands/MyCommand.java
package com.test.sb1jt.commands;
import com.test.sb1jt.config.InfoConfig;
import com.test.sb1jt.config.SysConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.test.sb1jt.config.InfoConfig;
@Component
public class MyCommand implements CommandLineRunner {
private static Logger logger = LoggerFactory.getLogger(MyCommand.class);
@Autowired
private InfoConfig infoConfig;
@Override
public void run(String... strings) throws Exception {
logger.info("MyCommand run");
logger.info("infoconfig name is {},desc is {}",infoConfig.getName(),
infoConfig.getDesc());
}
}

Run the code ,we get this:

Terminal window
2019-05-23 12:45:58.827 INFO 31595 --- [ main] com.test.sb1jt.commands.MyCommand : infoconfig name is Mike,desc is Good boy

5. Way 3: Read from your custom xxx.properties by @ProperySource annotation

By reading previous sections, you can see that by using @Value or @ConfigurationProperties, it’s easy to read properties in springboot’s default application.properties, but what should we do if we want read properties from a custom properties file?

According to this document,Spring provides @PropertySource annotation to read custom file properties.

Create a new file named sysconfig.properties in src/main/resources/ , add these properties to it:

src/main/resources/sysconfig.properties
sysname = BDS
sysdesc = Big data system

Create a new class to use the @PropertySource like this:

package com.test.sb1jt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource(value={"sysconfig.properties"}) //read from your custom src/main/resources/sysconfig.properties
public class SysConfig {
@Value("${sysname}") // mapping from sysname to this property
private String sysName;
@Value("${sysdesc}") // mapping from sysdesc to this property
private String sysDesc;
public String getSysName() {
return sysname;
}
public void setSysName(String sysName) {
this.sysName = sysName;
}
public String getSysDesc() {
return sysDesc;
}
public void setSysDesc(String sysDesc) {
this.sysDesc = sysDesc;
}
}

Print the custom properties like this:

package com.test.sb1jt.commands;
import com.test.sb1jt.config.InfoConfig;
import com.test.sb1jt.config.SysConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommand implements CommandLineRunner {
private static Logger logger = LoggerFactory.getLogger(MyCommand.class);
@Autowired
private SysConfig sysConfig; //inject the sysConfig instance
@Override
public void run(String... strings) throws Exception {
logger.info("MyCommand run");
logger.info("sysconfig sysname is {},sysdesc is {}",
sysConfig.getSysName(),sysConfig.getSysDesc());
}
}

Run the code ,we get this:

Terminal window
2019-05-23 12:45:58.827 INFO 31595 --- [ main] com.test.sb1jt.commands.MyCommand : sysconfig sysname is BDS,sysdesc is Big data system

6. Conclusion

You can use @Value ,@Configuration to read default application.properties or use the @PropertySource in your custom properties file in SpringBoot apps.

Final Words + More Resources

My intention with this article was to help others who might be considering solving such a problem. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

How to resolve 502 bad gateway error when using proxy_pass in Nginx and Tomcat?

1. Introduction

In this post, I will demonstrate how to resolve the 502 bad gateway error when using proxy_pass in Nginx.

2. Environment

  • Nginx 1+

3. The nginx.conf

We can define an upstream to reverse proxy local Tomcat services running on localhost as follows:

The service layout: image desc

nginx.conf
HTTP {
...
# define an upstream to be reversely proxied
# There are two local tomcat services that are listening to port 8081 and 8082
upstream service {
server localhost:8081; # tomcat 1
server localhost:8082; # tomcat 2
}
...
#define a server to proxy the services
server{
listen 80;
...
location /myservice {
proxy_pass http://service;
}
}
}

When a user navigates to http://xxx.com/myservice, the request will be reverse proxied to the Tomcat instances running on localhost (ports 8081 and 8082).

4. The problem

When your business grows, the number of concurrent HTTP requests may surge in a short period, as a result, your users might encounter an error like this when attempting to access your services:

<html>
<head>
<title>502 Bad Gateway</title>
</head>
<body bgcolor="white">
<center>
<h1>502 Bad Gateway</h1>
</center>
<hr>
<center>nginx</center>
</body>
</html>

5. How to resolve this 502 bad gateway problem?

5.1 Why did this happen?

Default HTTP 1.0 connections and requests: image desc

You can see that every request create a new connection,so your server would suffer from the surge requests.

The HTTP 1.1 connections and requests: image desc

You can see that there are only a few connections between the nginx and local service, many of them are reused between HTTP requests.

5.2 Try to use HTTP Keep-Alive feature

Change your server settings in nginx.conf, try to activate HTTP 1.1 keep-alive features like this:

nginx.conf
#define a server to proxy the services
server{
listen 80;
...
location /myservice {
proxy_HTTP_version 1.1; #use HTTP 1.1 to connect local service
proxy_set_header Connection ""; #remove client connection headers
proxy_pass http://service;
}
}

The explanation:

  • proxy_HTTP_version 1.1 indicates the use of HTTP 1.1, as the keep-alive feature is a default feature in HTTP 1.1 protocol.
  • proxy_set_header Connection "" is used to clear the client’s connection headers and to utilize the HTTP/1.1 header.

5.3 Set the maximum number of idle Keep-Alive connections for the upstream server

Change your upstream settings like this:

nginx.conf
HTTP {
...
# define an upstream to be reversely proxied
# There are two local tomcat services that are listening to port 8081 and 8082
upstream service {
# The keepalive parameter sets the maximum number of idle keepalive connections
# to upstream servers that are preserved in the cache of each worker process. When
# this number is exceeded, the least recently used connections are closed.
keepalive 50;
server localhost:8081; # tomcat 1
server localhost:8082; # tomcat 2
}
...
}

And,

6. The final nginx.conf

Here is the final nginx.conf file content:

nginx.conf
HTTP {
#change the default 15 seconds to 5 seconds to decrease the load of the server
keepalive_timeout 5;
...
# define an upstream to be reversely proxied
# There are two local tomcat services that are listening to port 8081 and 8082
upstream service {
# The keepalive parameter sets the maximum number of idle keepalive connections
# to upstream servers that are preserved in the cache of each worker process. When
# this number is exceeded, the least recently used connections are closed.
keepalive 50;
server localhost:8081; # tomcat 1
server localhost:8082; # tomcat 2
}
...
#define a server to proxy the services
server{
listen 80;
...
location /myservice {
proxy_HTTP_version 1.1; #use HTTP 1.1 to connect local service
proxy_set_header Connection ""; #remove client connection headers
proxy_pass HTTP://service;
}
}
}

6. Summary

You should always try to use the keep-alive feature of HTTP, it will help you to obtain good user experience and good revenue too.

How to install wrk on linux system?

1. Purpose

In this post, I will demo how to install wrk, a web url performance test tool on linux/unix/mac systems.

2. The solution

2.1 How to install wrk?

On linux systems, to ensures that your package manager (APT) has the most up-to-date information about available packages, versions, and dependencies, we should update the local cache first:

Terminal window
root@launch-advisor-20191120:/etc/nginx# apt update
....

Then we install the wrk tool using apt install command:

root@launch-advisor-20191120:/etc/nginx# apt install wrk
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package wrk

You can see that we got an error E: Unable to locate package wrk, why?

And wrk can only run on Unix-like systems. Such as linux, mac, solaris, etc. It can only be compiled on these systems.

So we can install it from source:

Download wrk source code:

Terminal window
sudo apt-get install build-essential libssl-dev git -y
git clone https://github.com/wg/wrk.git wrk

Then build it:

Terminal window
cd wrk
sudo make

Then we can use the binary:

Terminal window
# move the wrk executable to your PATH
sudo cp wrk /usr/local/bin

2.2 Verify the installation of wrk

Terminal window
root@launch-advisor-20191120:~/wrk# wrk
Usage: wrk <options> <url>
Options:
-c, --connections <N> Connections to keep open
-d, --duration <T> Duration of test
-t, --threads <N> Number of threads to use
-s, --script <S> Load Lua script file
-H, --header <H> Add header to request
--latency Print latency statistics
--timeout <T> Socket/request timeout
-v, --version Print version details
Numeric arguments may include a SI unit (1k, 1M, 1G)
Time arguments may include a time unit (2s, 2m, 2h)
root@launch-advisor-20191120:~/wrk#

It works.

3. Summary

In this post, I demonstrated how to install wrk , a great performance test tool . That’s it, thanks for your reading.

Final Words + More Resources

My intention with this article was to help others who might be considering solving such a problem. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

How to solve 'BPF' object has no attribute 'get_syscall_fnname'?

This post will show you how to solve ‘BPF’ object has no attribute ‘get_syscall_fnname’ when run bpf program in linux ?

Problem

When you run a python bpf_program in linux, you run this command:

For example, if our bpf program’s name is example.py:

Terminal window
python example.py

The example.py content is:

example.py
from bcc import BPF
bpf_source = """
#include <uapi/linux/ptrace.h>
int do_sys_execve(struct pt_regs *ctx) {
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_trace_printk("executing program: %s\\n", comm);
return 0;
}
"""
bpf = BPF(text=bpf_source)
execve_function = bpf.get_syscall_fnname("execve")
bpf.attach_kprobe(event=execve_function, fn_name="do_sys_execve")
bpf.trace_print()

You get this error:

Terminal window
root@launch:~/linux-observability-with-bpf/code/chapter-4/kprobes# python example.py
Traceback (most recent call last):
File "example.py", line 15, in <module>
execve_function = bpf.get_syscall_fnname("execve")
AttributeError: 'BPF' object has no attribute 'get_syscall_fnname'

The error AttributeError: 'BPF' object has no attribute 'get_syscall_fnname' indicates that the BPF class from the bcc module does not have a method named get_syscall_fnname.

Environment

You check your os version by this command:

Terminal window
root@launch:~# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"

Python version:

Terminal window
Python 2.7.17 (default, Apr 15 2020, 17:20:14)
[GCC 7.5.0] on linux2

Solution: Install the bcc dependencies

According to python bcc documents, you should install the libbcc and python bcc into system.

Terminal window
# add key server
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D4284CDD
# add iovisor to repo
echo "deb https://repo.iovisor.org/apt/bionic bionic main" | sudo tee /etc/apt/sources.list.d/iovisor.list
# update the repo
sudo apt-get update
# install libbcc
sudo apt-get install libbcc
# install python-bcc
sudo apt-get install python-bcc

what is libbcc?

and what is python-bcc:

After all done, you can run the python bpf script again:

Terminal window
root@launch:~/linux-observability-with-bpf/code/chapter-4/kprobes# python example.py
bash-12522 [001] .... 330817.825407: 0x00000001: executing program: bash

It works!

Final Words + More Resources

My intention with this article was to help others who might be considering solving such problem. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

How to solve Error assembling WAR webxml attribute is required exception when building spring boot wars

1. The purpose of this post

When build springboot app as war file, sometime we encounter this exception:

Terminal window
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war (default-war) on project: Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode) -> [Help 1]

The error shows that :

  • it needs a webxml attribute to construct the WAR file
  • Or you should provide a WEB-INF/web.xml to build the WAR file

2. Environments

  • spring boot 1.x and 2.x

3. The solution

3.1 The original pom.xml

Before solve , we can look at the original pom.xml

pom.xml
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</build>

We use the spring-boot-maven-plugin to execute the goal repackage with exec classifier.

The classifier, which uses exec here, indicating that the generated jar file will have a -EXEC suffix.

In summary, this configuration uses spring-boot-maven-plugin plug-in to reintegrate the project and generate a executable jar file with the -EXEC suffix.

But, we want to WAR file to be generated, not a executable jar file!

So , we need to change the pom.xml to generate a WAR.

3.2 The right pom.xml

We should add maven-war-plugin to build springboot as a WAR file:

pom.xml
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>

Please pay attention to the version of the maven-war-plugin, it must be 3.0.0+, or else you must add as follows:

<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>

Hope it helps for you.

Final Words + More Resources

My intention with this article was to help others who might be considering solving such problem. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: [email protected].

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!