RefreshScoped bean creation - spring

I have configuration class like this with validation on name field.
#Component
#ConfigurationProperties("person")
#RefreshScope
#Validated
public class PersonConfiguration {
#NotBlank
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#RestController
class MessageRestController {
#Autowired
private PersonConfiguration personConfig;
#RequestMapping("/message")
String getMessage() {
return personConfig.getName();
}
}
person:
name: aaaa
I was able to successfully start the spring boot application with the above details.
When i change the configuration to this
person:
name:
with an empty name field i don't see a binding exception on PersonConfiguration because of the empty name field right away.
I am seeing the binding exception only when i do a rest call like this localhost:8080/message which is accessing PersonConfiguration. It's like i have to wait till something accesses PersonConfiguration to see binding excpetion.
Is it how it should work?
Is there a way to throw the binding exception instantly when there are git config changes that are not valid, without waiting for something to access configuration

Related

repo.save not storing data in H2

`I have created simple spring project where I am using requestmapping to store data in my H2 database. I am getting parameters in URL but data is getting saved.
I am new in spring boot and facing this issue. Please help me with this. Thanks
//Controller Class
#Controller
public class TrialTwoController {
#Autowired
Repository repos;
#RequestMapping("/")
public String home() {
return "home.jsp";
}
#RequestMapping(path="/addUser")
public TrailTwo post(#RequestBody TrailTwo trail) {
repos.save(trail);
return trail;
}
}
//Model class
#Entity
public class TrailTwo {
#Id
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Repository Interface
public interface Repository extends JpaRepository<TrailTwo, Integer> {
}
application properties
spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:anshul
`
Inside your controller, you want to return TrailTwo entity. However, because you use #Controller and not #RestController, Spring won't be able to return the entity and will try to render the view instead which will result in the exception.
In order to fix this problem, you need to add #ResponseBody for in your method.
#RequestMapping(path="/addUser")
public #ResponseBody TrailTwo post(#RequestBody TrailTwo trail) {
repos.save(trail);
return trail;
}
You can also separate the concerns and have UserController which will be annotated with #RestController and ViewController which will be annotated with #Controller. This way, you won't have to add #ResponseBody annotation inside #RestController.
On another note, make sure to use POST http method for adding new entities. Instead of using #RequestMapping, you can use #PostMapping which will limit the supported http methods to POST. For POST, parameters need to be passed as a part of request body.
Make sure you understand http methods and how to use them to build robust REST apis. It will make your life easier. You can read about different http methods and how to use them here https://restfulapi.net/http-methods/
For the full working example, you can take a look here: https://github.com/CaptainAye/repository-h2-sample

Spring Boot Yaml configuration: list of typed properties

I'm following the 24.8.3 Merging Complex Types section of Spring Boot's 24. Externalized Configuration documentation.
I have this config.yaml file:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
The Properties file looks like this:
#ConfigurationProperties("acme")
#YamlPropertySource(value = { "classpath:/config.yaml" })
public class AcmeProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
The MyPojo class:
public class MyPojo {
private String name;
private String description;
public MyPojo(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The test, which fails, looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { AcmeProperties.class })
public class AcmePropertiesTest {
#Autowired
private AcmeProperties properties;
#Test
public void getOpScoringClusters() {
Assert.assertEquals(2, properties.getList().size()); // FAIL!
}
}
Spring Boot version 1.5.6.
Basically I want to have a list of typed properties. What am I doing wrong?
Several comments have highlighted multiple issues with the code presented.
Firstly, the fields inside a configuration properties can't be final as spring uses the setter to set the value.
Secondly, #YamlPropertySource is not something provided by spring so won't do anything in this context.
Thirdly, even if you did use the spring PropertySource annotation, unfortunately you can't use it with yaml files.
YAML files cannot be loaded by using the #PropertySource annotation.
I've created a sample project that uses the code you presented and has been modified so that it passes the unit test. It's using spring boot 2.x instead of 1.x but the only significant difference should be the annotations used in the test class.
https://github.com/michaelmcfadyen/spring-boot-config-props-demo

#value not able to read from application.properties in springboot

I am trying to read value from properties file using #value as follows.
#Value("${abc}")
private String abc;
public List<Record> fetchRecords(String label, String predicate) {
System.out.println(abc);
}
but value of abc is coming as null. Whereas when I try to print the same using #PostConstruct, I am getting the expected value.
#PostConstruct
public void postconstruct() {
System.out.println(abc);
}
Any lead why I am not able to get the value in fetchRecords() method?
For reference, here goes the code
#Component
public class AuditRecord {
private String subject;
private String predicate;
private String oldObject;
private String newObject;
private String readOnlyAuthInfo;
#Value("${registry.system.base}")
private String registrySystemContext;
public void record(DatabaseProvider provider) throws AuditFailedException {
System.out.println("---registrySystemContext value showing null here---"+registrySystemContext);
...
}
#PostConstruct
public void postconstruct() {
System.out.println("---registrySystemContext value showing here as expected---"+registrySystemContext);
}
}
The way I am calling is as follows:
#Component
public class RegistryDaoImpl implements RegistryDao {
...
private void addOrUpdateVertexAndEdge(Vertex v, Vertex dbVertex, GraphTraversalSource dbGraph, String methodOrigin){
...
AuditRecord record = new AuditRecord();
record
.subject(dbVertex.label())
.predicate(e.label())
.oldObject(null)
.newObject(existingV.label())
.record(databaseProvider);
}
}
P.S. registry.system.base is in application.yml.
You need to autowire AuditRecord and not use new directly. Only that way you will have your class in Spring's context.
We don't know your exact usage of the class but you might be interested in Spring's FactoryBean.

ehcache error serializing key for distributed config with terracotta server

I'm trying to configure terracotta server to work with a spring/mybatis application and I'm getting the following error. I'm not sure if that means the key itself or the value returned from the key could not be serialized. The caching worked fine as a local cache, but now has the issue trying to work with the server. I need a clue why this is not able to be serialized. Thanks.
So I got a clue from this How do you serialize a Spring Bean (spring 3) that it may be something to do with lack of session scope. These errors happen when I am starting up Tomcat and the first webpage is loading. I added implements java.io.Serializable to the Site bean class and that moved me past that error to the next bean that was getting called. After adding this to many beans I'm wondering if A, is this the right thing to do and will there be any side effects from forcing the implements java.io.Serializable on these spring classes? And B: is there a better way do this as I have many beans in this application?
SEVERE: Servlet.service() for servlet [ezreg] in context with path [] threw exception [Request processing failed; nested exception is net.sf.ehcache.CacheException: The value com.trifecta.src.ezreg.beans.Site#655ad5d5 for key getSiteByHostname127.0.0.1 is not Serializable. Consider using Element.getObjectValue()] with root cause
net.sf.ehcache.CacheException: The value com.trifecta.src.ezreg.beans.Site#655ad5d5 for key getSiteByHostname127.0.0.1 is not Serializable. Consider using Element.getObjectValue()
at net.sf.ehcache.Element.getValue(Element.java:326)
at net.sf.ehcache.ElementData.<init>(ElementData.java:35)
at net.sf.ehcache.EternalElementData.<init>(EternalElementData.java:19)
at org.terracotta.modules.ehcache.store.ValueModeHandlerSerialization.createElementData(ValueModeHandlerSerialization.java:48)
at org.terracotta.modules.ehcache.store.ClusteredStore.doPut(ClusteredStore.java:745)
at org.terracotta.modules.ehcache.store.ClusteredStore.putInternal(ClusteredStore.java:291)
at org.terracotta.modules.ehcache.store.ClusteredStore.put(ClusteredStore.java:263)
at org.terracotta.modules.ehcache.store.ClusteredSafeStore.put(ClusteredSafeStore.java:247)
at org.terracotta.modules.ehcache.store.nonstop.NonStopStoreWrapper.put(NonStopStoreWrapper.java:820)
at net.sf.ehcache.Cache.putInternal(Cache.java:1617)
at net.sf.ehcache.Cache.put(Cache.java:1543)
at net.sf.ehcache.Cache.put(Cache.java:1508)
at org.springframework.cache.ehcache.EhCacheCache.put(EhCacheCache.java:121)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:85)
at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:784)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:417)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:327)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy56.getSiteByHostname(Unknown Source)
at com.trifecta.src.ezreg.daos.SiteDaoImpl.getSiteByHostname(SiteDaoImpl.java:35)
doa method:
public Site getSiteByHostname(String hostname) {
return getSiteMapper().getSiteByHostname(hostname);
}
mapper method:
#Cacheable(cacheNames="siteCache", key="#root.methodName.concat(#root.args)")
Site getSiteByHostname(String hostname);
Site bean returned:
package com.trifecta.src.ezreg.beans;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement
public class Site {
public static String ADMIN_CURRENT_SITE = "adminCurrentSite";
public static String _CURRENT_SITE = "currentSite";
#XmlAttribute
private Long id;
#XmlAttribute
private String name;
#XmlAttribute
private String supportphonenumber;
#XmlElement
private SitePreference sitePreference;
#XmlElement
private SiteInterfaceDevice siteInterfaceDevice;
#XmlElement
private SitePdfFormat sitePdfFormat;
#XmlAttribute
private boolean ecum;
#XmlTransient
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#XmlTransient
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlTransient
public SiteInterfaceDevice getSiteInterfaceDevice() {
return siteInterfaceDevice;
}
public void setSiteInterfaceDevice(SiteInterfaceDevice siteInterfaceDevice) {
this.siteInterfaceDevice = siteInterfaceDevice;
}
#XmlTransient
public SitePdfFormat getSitePdfFormat() {
return sitePdfFormat;
}
public void setSitePdfFormat(SitePdfFormat sitePdfFormat) {
this.sitePdfFormat = sitePdfFormat;
}
#XmlTransient
public SitePreference getSitePreference() {
return sitePreference;
}
public void setSitePreference(SitePreference sitePreference) {
this.sitePreference = sitePreference;
}
#XmlTransient
public String getSupportphonenumber() {
return supportphonenumber;
}
public void setSupportphonenumber(String supportphonenumber) {
this.supportphonenumber = supportphonenumber;
}
#XmlTransient
public boolean getEcum() {
return ecum;
}
public void setEcum(boolean ecum) {
this.ecum = ecum;
}
}
public class Site implements java.io.Serializable{
public static String ADMIN_CURRENT_SITE = "adminCurrentSite";
public static String _CURRENT_SITE = "currentSite";
When caching locally, you can have a setup using heap memory only and thus there is no requirement on your keys / values stored in the cache.
However the moment you move to clustering, or actually any other caching tier than heap, then your keys and values must implement Serializable as this is the only way we can store / ship over the wire your mappings.
And so clearly in your case the type com.trifecta.src.ezreg.beans.Site does not implement Serializable.
You have two options:
Update the type definition to implement Serializable and make sure it is true - that is it only has Serializable fields
Convert the non serializable value in one that can be serialized.
Note that with Ehcache 3 you would have the option of specifying a custom serializer for your type that would not limit you to Java serialization.

Spring rest Json issue

I found below answered question
Different names of JSON property during serialization and deserialization
Unfortunately this does not work when we use Spring Restful webservice. I am not sure what is cauisng the issue but it gives some Field abiguity exception.
What I want to do is Serialize and deserialize a field name with different names.
For e.g.
class Test {
private String name;
#JsonProperty("myName")
public String getName() {
return name;
}
#JsonProperty("yourName")
public void setName(String name) {
this.name = name;
}
}
This does not work in Spring rest
You can not set #JsonProperty for both (getter & setter). You can set for the field or setter method.
But you want different name for request and response, Create two classes like this.
class StudentResponse{
#JsonProperty(name="student_name)
private String name;
//getter & setter
}
class StudentRequest{
#JsonProperty(name="name)
private String name;
//getter & setter
}
Damith is right, you seem to not be able to mark both methods within the same class, however there is a way to solve this:
First off, you will have to Create a custom deserializer (or serializer, depends on your preference).
My example object:
#JsonDeserialize(using = ObjectDeserializer.class)
public class MyObject {
private String name;
public void setName(String name) {
this.name = name;
}
#JsonProperty("SomeOtherName")
public String getName() {
return name;
}
}
Note, i mark the getter as the property with the first name. And I give the class a custom deserializer. Which looks like that:
public class ObjectDeserializer extends JsonDeserializer<MyObject> {
#Override
public MyObject deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
MyObject object = new MyObject();
JsonNode node = jp.getCodec().readTree(jp);
JsonNode jsonNode = node.get("MyCustomSerializeName");
object.setName(jsonNode.getTextValue());
return object;
}
}
This class will create my custom object and get the name of the setter field description (rather than relying on the property name).
Put together, i get:
public class DeserializeTest {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
MyObject o = new MyObject();
o.setName("Hello");
String writeValueAsString = mapper.writeValueAsString(o);
System.out.println(writeValueAsString);
String jsonObj = "{\"MyCustomSerializeName\":\"Other Test\"}";
MyObject readValue = mapper.readValue(jsonObj, MyObject.class);
System.out.println(readValue.getName());
}
}
And this outputs:
{"SomeOtherName":"Hello"}
Other Test
I hope that helps you.

Resources