I have a cache class that is loading properties from a .properties file before the application startup, in a simple java project. It logs out everything.I need to convert this project to springboot application.
What annotations can i use to achieve the Cache Loading??
Currently I wrote the code like my spring boot app starts with the #postconstruct , since i am not using web.xml for loading servlets.
#RestController
public class ConfigServlet {
#PostConstruct
public void init() {
//business logic
}
}
And this servlet starts up first. So how can i load cache along this??
It is supposed to load the Cache even before this servlet class loads. How can i achieve this concept??
Suppose you have below properties in your application properties. You can load them as below. The properties will be loaded during the application startup.
application.properties
test.a = 10
test.b =20
test1.a = 30
test1.b = 40
#Configuration
#ConfigurationProperties
Class CacheProperties {
Map<String,String> test;
Map<String,String> test1;
public String getTestB() {
return test.get("b");
}
public String getTestA() {
return test.get("a");
}
public String getTest1B() {
return test1.get("b");
}
public String getTest1A() {
return test1.get("a");
}
//setters
}
Related
I'm working on a Spring project with Gradle, I use mysql-connector-java v-8.0.30, javax.persistence-api v-2.2, and spring-boot-starter-data-jpa dependencies. My project was running correctly, but from one moment to another it stopped compiling, only the Spring logo appears in the console with the following warning: RN 19232 --- [ restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
What I thought at first is that the application was not compiling, however when I access it, I execute a class method annotated with #RestController and I paint a message in the HTMl, it is displayed correctly.
#SpringBootApplication
#RestController
public class CompraventaspringApplication {
public static void main(String[] args) {
SpringApplication.run(CompraventaspringApplication.class, args);
}
#GetMapping("/")
public static String message(#RequestParam(value = "saludo", defaultValue =
"i'm a CRUD system") String saludo )
{
return String.format("<h1>Hello %s ! </h1>", saludo);
}
However, when I try to bind any route from a controller to an HTML template the route doesn't map properly. I've already tried to remove the Gradle dependencies and rebuild them but still can't find my HTML:
#Controller
#RequestMapping
public class ControllerProductos {
#Autowired
public ProductoServicesClass service;
public static String UPLOADS = "src/main/resources/static/uploads";
// Listar todos los productos diponibles
#GetMapping("/home")
public String listar(Model model) {
List<Productos> productos = service.listar();
model.addAttribute("productos", productos);
return "HomePage";
}
}
I already checked several times and yes, the name of the template is the same as the one associated with the return of the method.
I'm using Spring Boot and I have a properties file p.properties:
p1 = some val1
p2 = some val2
Configuration class:
#Configuration
#PropertySource("classpath:p.properties")
public class myProperties {
public myProperties () {
super();
}
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
And I'm using this in order to access the property:
#Value("${p1}")
private String mProperty;
Everything works great.
I want to change p1 in p.properties file from outside of the app and the next time that I'll use mProperty, it will contains the new value without restarting the app.
Is it possible?
Thanks,
Avi
You can simply use spring boot actuator.
Just add the actuator dependency in your maven/gradle config and you should be seeing live reloads when you update the property file.
Note: You won't have to restart the app but actuator will do live reloads on its own.
If you want to change the properties at runtime and don't want to restart the server then follow the below steps:
Application.properties
app.name= xyz
management.endpoints.web.exposure.include=*
Add below dependencies in pom.xml
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-context
2.0.1.RELEASE
3)Place application.properties in config folder . The config folder must be in the location from where you will run the jar.
Add ApplcationProperties.java
#RefreshScope
#Component
#ConfigurationProperties(prefix = "app")
public class ApplcationProperties {
private String name;
//getter and setter
}
Write ApplicationController.java and inject ApplcationProperties
#Autowired
ApplcationProperties applcationProperties;
#RestController
public Class ApplicationController
{
#GetMapping("/find")
String getValue()
{
return applicationProperties.getName();
}
}
Run the spring boot application
Call localhost:XXXX/find from your browser
Output : xyz
Change the value in application.properties from xyz to abc
Using postman send a put /options request to localhost:XXXX/actuator/refresh
--Note this request should be either PUT/OPTIONS
Call localhost:XXXX/find from your browser
Output : abc
I think, in this case, it is advisable to keep it in the database so that, it can be changed & accessed seamlessly. We have a similar scenario where we keep the encrypted password for database in the properties file. While connecting to db, it needs to be decrypted. We do that by extending PropertyPlaceholderConfigurer as follows.
public class MyPropertyConfigurer extends PropertyPlaceholderConfigurer{
protected void convertProperties(Properties props){
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
if(propertyName.indexOf("db.password") != -1){
decryptAndSetPropValue(props,propertyName,propertyValue);
}
}
}
}
But, this is done only once while loading the properties file.
I am not able to read a property from properties file through Spring Boot. I have a REST service which is working through the browser and Postman both and returning me a valid 200 response with data.
However, I am not able to read a property through this Spring Boot client using #Value annotation and getting following exception.
Exception:
helloWorldUrl = null
Exception in thread "main" java.lang.IllegalArgumentException: URI must not be null
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.springframework.web.util.UriComponentsBuilder.fromUriString(UriComponentsBuilder.java:189)
at org.springframework.web.util.DefaultUriTemplateHandler.initUriComponentsBuilder(DefaultUriTemplateHandler.java:114)
at org.springframework.web.util.DefaultUriTemplateHandler.expandInternal(DefaultUriTemplateHandler.java:103)
at org.springframework.web.util.AbstractUriTemplateHandler.expand(AbstractUriTemplateHandler.java:106)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:612)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
at com.example.HelloWorldClient.main(HelloWorldClient.java:19)
HelloWorldClient.java
public class HelloWorldClient {
#Value("${rest.uri}")
private static String helloWorldUrl;
public static void main(String[] args) {
System.out.println("helloWorldUrl = " + helloWorldUrl);
String message = new RestTemplate().getForObject(helloWorldUrl, String.class);
System.out.println("message = " + message);
}
}
application.properties
rest.uri=http://localhost:8080/hello
There are several problems in your code.
From the samples you posted, it seems that Spring is not even started. The main class should run the context in your main method.
#SpringBootApplication
public class HelloWorldApp {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApp.class, args);
}
}
It isn't possible to inject a value into a static field. You should start with changing it into a regular class field.
The class must be managed by Spring container in order to make value injection available. If you use default component scan you can simply annotate the newly created client class with the #Component annotation.
#Component
public class HelloWorldClient {
// ...
}
If you don't want to annotate the class you can create a bean in one of your configuration classes or your main Spring Boot class.
#SpringBootApplication
public class HelloWorldApp {
// ...
#Bean
public HelloWorldClient helloWorldClient() {
return new HelloWorldClient();
}
}
However, if you are the owner of the class, the first option is preferable. No matter which way you choose, the goal is to make the Spring context aware of class existence so the injection process can happen.
I am used to Spring on Tomcat/Jetty and I now work on an existing JAX-RS project running on WildFly (RESTEasy).
I would like to know where do the application/deployment property files go on WildFly, standalone/configuration/myapp.properties?
Then how does the application load them? I tried in our class extending javax.ws.rs.core.Application:
#javax.ws.rs.ApplicationPath("")
public class ApplicationConfig extends Application {
#Override
public Map<String, Object> getProperties() {
System.out.println(">>>>>>>>>>>>>>>> get properties");
// I added this method but nothing is printed...
}
#Override
public Set<Class<?>> getClasses() {
System.out.println(">>>>>>>>>>>>>>>> get classes");
// This is printed
...
// classes are loaded correctly
}
}
Then how would I access the properties in the controllers? By the way we don't use dependency injection.
Thanks!
Some Investigation...
Normally what should work
The getProperties() should be called on startup to load any required application properties.
You should be able to inject javax.ws.rs.core.Configuration into your resource classes (with #Context) and retrieve properties through that object. This is stated in the javadoc
This interface can be injected using the Context annotation.
Test
#ApplicationPath("/api")
public class RestApplication extends Application {
#Override
public Map<String, Object> getProperties() {
System.out.println(">>>>>>>>>>>>>>>> get properties");
Map<String, Object> props = new HashMap<>();
props.put("message", "Hello Configuration Properties!");
return props;
}
}
#Path("config")
public class ConfigResource {
#Context
private Configuration configuration;
#GET
public Response getProperty(#QueryParam("prop") String prop) {
String propValue = (String)configuration.getProperty(prop);
return Response.ok(propValue).build();
}
}
Discoveries
The above doesn't work from what I tested with Resteasy 3.0.9.Final. I get some error about no context for this type. I don't know why. Might be a bug, I don't know. Maybe something you can look into.
The above works fine with Jersey 2.16
What works with Resteasy
What I could get to work with Resteasy is to inject Application (as mentioned here into the resource (also with #Context) and get the properties that way.
#Path("config")
public class ConfigResource {
#Context
Application application;
#GET
public Response getProperty(#QueryParam("prop") String prop) {
String propValue = (String)application.getProperties().get(prop);
return Response.ok(propValue).build();
}
}
Is there any way to load a class marked with #ConfigurationProperties without using a Spring Context directly? Basically I want to reuse all the smart logic that Spring does but for a bean I manually instantiate outside of the Spring lifecycle.
I have a bean that loads happily in Spring (Boot) and I can inject this into my other Service beans:
#ConfigurationProperties(prefix="my")
public class MySettings {
String property1;
File property2;
}
See the spring docco for more info http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args
But now I need to access this bean from a class that is created outside of Spring (by Hibernate). The class is created so early in the app startup process that Spring Boot has not yet made the application context available through the classic lookup helper methods or roll-my-own static references.
So I instead want to do something like:
MySettings mySettings = new MySettings();
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);
And have MySettings end up with all its values loaded, from the command line, system properties, app.properties, etc. Is there some class in Spring that does something like this or is it all too interwoven with the application context?
Obviously I could just load the Properties file myself, but I really want to keep Spring Boot's logic around using command line variables (e.g. --my.property1=xxx), or system variables, or application.properties or even a yaml file, as well as its logic around relaxed binding and type conversion (e.g. property2 is a File) so that it all works exactly the same as when used in the Spring context.
Possible or pipe dream?
Thanks for your help!
I had the same "issue".
Here is how I solved it in SpringBoot version 1.3.xxx and 1.4.1.
Let's say we have the following yaml configuration file:
foo:
apis:
-
name: Happy Api
path: /happyApi.json?v=bar
-
name: Grumpy Api
path: /grumpyApi.json?v=grrr
and we have the following ConfigurationProperties:
#ConfigurationProperties(prefix = "foo")
public class ApisProperties {
private List<ApiPath> apis = Lists.newArrayList();
public ApisProperties() {
}
public List<ApiPath> getApis() {
return apis;
}
public static class ApiPath {
private String name;
private String path;
public String getName() {
return name;
}
public void setName(final String aName) {
name = aName;
}
public String getPath() {
return path;
}
public void setPath(final String aPath) {
path = aPath;
}
}
}
Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:
private static ApisProperties apiProperties() {
try {
ClassPathResource resource;
resource = new ClassPathResource("/config/application.yml");
YamlPropertiesFactoryBean factoryBean;
factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setSingleton(true); // optional depends on your use-case
factoryBean.setResources(resource);
Properties properties;
properties = factoryBean.getObject();
MutablePropertySources propertySources;
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
ApisProperties apisProperties;
apisProperties = new ApisProperties();
PropertiesConfigurationFactory<ApisProperties> configurationFactory;
configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the #ConfigurationProperties
configurationFactory.bindPropertiesToTarget();
return apisProperties; // apiProperties are fed with the values defined in the application.yaml
} catch (BindException e) {
throw new IllegalArgumentException(e);
}
}
Here's an update to ctranxuan's answer for Spring Boot 2.x. In our situation, we avoid spinning up a Spring context for unit tests, but do like to test our configuration classes (which is called AppConfig in this example, and its settings are prefixed by app):
public class AppConfigTest {
private static AppConfig config;
#BeforeClass
public static void init() {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yaml"));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
config = binder.bind("app", AppConfig.class).get(); // same prefix as #ConfigurationProperties
}
}
The "magic" class you are looking for is PropertiesConfigurationFactory. But I would question your need for it - if you only need to bind once, then Spring should be able to do it for you, and if you have lifecycle issues it would be better to address those (in case they break something else).
This post is going into similar direction but extends the last answer with also validation and property placeholder resolutions.
Spring Boot Binder API support for #Value Annotations
#Value annotations in ConfigurationPropertys don't seem to bind properly though (at least if the referenced values are not part of the ConfigurationProperty's prefix namespace).
import org.springframework.boot.context.properties.bind.Binder
val binder = Binder.get(environment)
binder.bind(prefix, MySettings.class).get