I have a config server returning values correctly, and I can pull values without any issue except for this one script which I presume is due to using #PostConstruct. However, I have tried multiple solutions without success. Does anyone have any suggestions for changes to make this work?
Output from Config Server
`
{
"name": "auth-service",
"profiles": [
"jdbc"
],
"label": "1.0",
"version": null,
"state": null,
"propertySources": [
{
"name": "auth-service-jdbc",
"source": {
"spring.datasource.url": "jdbc:mysql://x.x.x.x:3306/test?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=UTC&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false",
"spring.datasource.username": "xxxx",
"spring.datasource.password": "xxxxx",
"jwt.secret": "xxxxxxx",
"jwt.expiration": "86400"
}
}
]
}
`
The script with which I am having issues, secret and expirationTime are always null unless contained within Bootstrap.yml, which is not practical.
`
#Component
public class JwtUtil {
#Value("${jwt.secret}")
String secret;
#Value("${jwt.expiration}")
String expirationTime;
private Key key;
#PostConstruct
public void init() {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
}
`
So far, I have tried using Environment instead of #Value, constructors and with and without #PostConstruct. The only way I have managed to get it to grab the values is when they are in Application.yml or Bootstrap.yml
Related
What does the following setting in application.properties in a Spring application do?
server.error.include-binding-errors=on-param
I can't find it in the documentation.
The always and never values are pretty self-explanatory, but I don't understand on-param.
To begin, what I did is to do a global search on server.error.include-binding-errors in the library, which lead me to spring-configuration-metadata.json
{
"name": "server.error.include-binding-errors",
"type": "org.springframework.boot.autoconfigure.web.ErrorProperties$IncludeAttribute",
"description": "When to include \"errors\" attribute.",
"sourceType": "org.springframework.boot.autoconfigure.web.ErrorProperties",
"defaultValue": "never"
},
We see the related attribute is errors here, and the value class is IncludeAttribute. By checking the documentation in IncludeAttribute#ON_PARAM
public static final ErrorProperties.IncludeAttribute ON_PARAM
Add error attribute when the appropriate request parameter is not "false".
We know that the errors attribute will be added when there is a request parameter that is not "false".
If you want something concrete, let's consider the following example:
Controller
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
#RestController
public class TestController {
#PostMapping("/dummy")
public String test(#Valid #RequestBody DummyDTO dummyDTO) {
return "";
}
}
DTO
import javax.validation.constraints.NotNull;
public class DummyDTO {
#NotNull(message = "mandatoryText can not be null")
private String mandatoryText;
private String canBeNullText;
public String getMandatoryText() {
return mandatoryText;
}
public void setMandatoryText(String mandatoryText) {
this.mandatoryText = mandatoryText;
}
public String getCanBeNullText() {
return canBeNullText;
}
public void setCanBeNullText(String canBeNullText) {
this.canBeNullText = canBeNullText;
}
}
Suppose we set
server.error.include-binding-errors=on-param
When we run with parameter errors=false
curl -H "Content-Type: application/json" --data '{"canBeNullText":""}' -X POST http://localhost:8080/dummy?errors=false
the result will not include errors
{
"timestamp": "2021-06-11T13:38:29.868+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/dummy"
}
When we run with parameter errors=true
curl -H "Content-Type: application/json" --data '{"canBeNullText":""}' -X POST http://localhost:8080/dummy?errors=true
the result will include errors as
{
"timestamp": "2021-06-11T13:51:00.649+00:00",
"status": 400,
"error": "Bad Request",
"errors": [{
"codes": ["NotNull.dummyDTO.mandatoryText", "NotNull.mandatoryText", "NotNull.java.lang.String", "NotNull"],
"arguments": [{
"codes": ["dummyDTO.mandatoryText", "mandatoryText"],
"arguments": null,
"defaultMessage": "mandatoryText",
"code": "mandatoryText"
}
],
"defaultMessage": "mandatoryText can not be null",
"objectName": "dummyDTO",
"field": "mandatoryText",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
}
],
"path": "/dummy"
}
References:
Implementation for web reactive
DefaultErrorWebExceptionHandler#isIncludeBindingErrors
AbstractErrorWebExceptionHandler#isBindingErrorsEnabled
Implementation for web servlet
BaseErrorController#isIncludeBindingErrors
AbstractErrorController#isBindingErrorsEnabled
I am generating response classes that could provide links.
I'd like to avoid the links attribute to show in case no link is provided, as for example now I'm getting:
{
"links": [], <--not the best
"id": 1,
"category": {
"links": [],
"id": 3
}
I would instead like to have:
{
"id": 1,
"category": {
"id": 3
},
I tried defining a global mapper:
#Configuration
public class JacksonConfig {
#Autowired
private ObjectMapper objectMapper; //reuse the pre-configured mapper
#PostConstruct
public void setup() {
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);
}
}
And for every other dto I have it works...except for the hateoas links!
What am I doing wrong?
edit:
I think I made a step further. It only happens when the RepresentationModel is in a List:
#GetMapping("/pet")
public ResponseEntity<List<Pet>> getPets(){ <-- this returns empty links
#GetMapping("/pet")
public ResponseEntity<Pet> getPet(){ <-- this doesn't
I created a DTO to map message came from AWS SES to SQS through SNS but the timestamp converter didn't work properly with objectMapper created by spring boot. I tried to map the timestamp with (ZonedDateTime/ LocalDateTime / Instant) and I always get an error
I used these versions
spring-boot version is 2.2.5
spring-cloud version is Hoxton.SR3
I explicitly specify Jackson version to be 2.9
here is the message
{
"notificationType": "Bounce",
"bounce": {
"bounceType": "Permanent",
"bounceSubType": "Suppressed",
"bouncedRecipients": [
{
"emailAddress": "blacklist#simulator.amazonses.com",
"action": "failed",
"status": "5.1.1",
"diagnosticCode": "Amazon SES has suppressed sending to this address because it has a recent history of bouncing as an invalid address. For more information about how to remove an address from the suppression list, see the Amazon SES Developer Guide: http://docs.aws.amazon.com/ses/latest/DeveloperGuide/remove-from-suppressionlist.html "
}
],
"timestamp": "2020-03-20T21:00:42.217Z",
"feedbackId": "01070170f9bf8131-9cbe20b1-7997-4d33-8e02-7e828eb01edc-000000",
"reportingMTA": "dns; amazonses.com"
},
"mail": {
"timestamp": "2020-03-20T21:00:42.217Z",
"source": "tester#####.com",
"sourceArn": "arn:aws:ses:eu-central-1:425905684163:identity/####",
"sourceIp": "54.239.6.46",
"sendingAccountId": "425905684163",
"messageId": "01070170f9bf8091-79e59712-3c2d-424c-9eba-3e221ef336ff-000000",
"destination": [
"blacklist#simulator.amazonses.com"
],
"headersTruncated": false,
"headers": [
{
"name": "From",
"value": "tester#####.com"
},
{
"name": "To",
"value": "blacklist#simulator.amazonses.com"
},
{
"name": "Subject",
"value": "lkasnlkaslknaslkn"
},
{
"name": "MIME-Version",
"value": "1.0"
},
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"commonHeaders": {
"from": [
"*****"
],
"to": [
"blacklist#simulator.amazonses.com"
],
"subject": "lkasnlkaslknaslkn"
}
}
}
The mapping for the bounce object is
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Bounce {
private BounceType bounceType;
private BounceSubType bounceSubType;
private List<Recipient> bouncedRecipients;
private ZonedDateTime timestamp;
private String feedbackId;
private String remoteMtaIp;
private String reportingMTA;
public static enum BounceType {
Permanent, Transient
}
public static enum BounceSubType {
General, Suppressed
}
}
jackson-modules-java8 can do that. This works for me:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
String message = "{ \"timestamp\": \"2020-03-20T21:00:42.217Z\" }";
Dto obj = mapper.readValue(message, Dto.class);
System.out.println(obj.getTimestamp());
Output is (as you may have expected):
2020-03-20T21:00:42.217Z
Note that I am explicitly registering the Jackson java.time module on the mapper. If you were to use the mapper for serialization too, you would additionally need:
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
For the sake of a complete example, The Dto class I was using is very simple, just a field with getter and setter:
public class Dto {
private Instant timestamp;
public Instant getTimestamp() {
return timestamp;
}
public void setTimestamp(Instant timestamp) {
this.timestamp = timestamp;
}
}
I did not try with a field of type ZonedDateTime nor OffsetDateTime, but I’d be very surprised if they didn’t work too. In any case, if your timestamp is always in UTC and always has the trailing Z for UTC, then I would consider Instant the most correct type for it in Java.
Link
jackson-modules-java8
I am trying to write some integration tests (Junit5) where I will need to fetch some data from a repository. I would like to pre-populate the database with some json data (I want to avoid sql) before the test start.
After some research I manage to create the test-database.json file inside my resource folder:
[
{
"_class": "com.marcellorvalle.scheduler.entity.Professional",
"id": 1,
"name": "James Holden"
},
{
"_class": "com.marcellorvalle.scheduler.entity.Professional",
"id": 2,
"name": "Gerald of Rivia"
},
{
"_class": "com.marcellorvalle.scheduler.entity.Professional",
"id": 3,
"name": "Christjen Avassalara"
}
]
And also the configuration file:
#Configuration
public class TestConfiguration {
#Bean
public Jackson2RepositoryPopulatorFactoryBean getRepositoryPopulator() {
final Jackson2RepositoryPopulatorFactoryBean factory = new Jackson2RepositoryPopulatorFactoryBean();
factory.setResources(new Resource[]{new ClassPathResource("test-database.json")});
return factory;
}
}
I have a dummy test just to check if everything is ok:
#DataJpaTest
#ExtendWith(SpringExtension.class)
public class IntegrationTest {
final private ProfessionalRepository repository;
#Autowired
public IntegrationTest(ProfessionalRepository professionalRepository) {
repository = professionalRepository;
}
#Test
public void testFindAll() {
final List<Professional> result = repository.findAll();
Assertions.assertEquals(3, result.size());
}
}
When I run the test it will fail with no results. Next I added the following to the TestClass:
#ContextConfiguration(classes=TestConfiguration.class)
Now the TestConfiguration is loaded but it can not resolve ProfessionalRepository:
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.marcellorvalle.scheduler.repository.ProfessionalRepository professionalRepository] (...) Failed to load ApplicationContext
Is there any way to read the TestConfiguration without messing the application context?
Instead of using #ContextConfiguration that wipe all your default configs and replace them by the specified classes, try using #Import over your class. That will aggregate your config.
You should check out #ComponentScan("base-package-where-scan-starts'") annotation and put it at your TestConfiguration class
https://www.javarticles.com/2016/01/spring-componentscan-annotation-example.html
I see strange behavior in Intellij and JBoss server.
Short info about application:
- spring 4.3.1
- jersey 1.18.1
- jboss EAP 6.4.0
I have an issue with marshaling, so I tried to dig deeper and try hints from: https://docs.jboss.org/resteasy/docs/1.0.0.GA/userguide/html/Content_Marshalling_Providers.html
So I created class:
#Named
#Provider
#Primary
#Produces(MediaType.APPLICATION_JSON)
public class JerseyJAXBConfiguration implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = { SomeClass.class};
public JerseyJAXBConfiguration() throws Exception {
JSONConfiguration.MappedBuilder builder = JSONConfiguration.mapped();
builder.arrays("invite");
builder.rootUnwrapping(false);
this.context = new JSONJAXBContext(builder.build(), types);
}
public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
And I have two JBoss run configurations in IntelliJ. First that deploy simple 'war' and second that deploy 'war exploded'. And here is a strange thing. Because in both cases this bean is loaded when the application is starting, but only for a case without exploded 'getContext' method is executed. In both cases, everything is the same. Nothing in code is changed between runs.
Can someone explain to me why we have this different? I would like to know what JAXB implementation is executed in a case for 'war exploded'.
The main reason why I looking at it is because Json that is created by web service also is different in metioned cases. On have "named" array second don't have.
In case 'war':
{"functionGroupAutoFill": [
{
"desc": "0. General",
"id": "0"
},
{
"desc": "00. General",
"id": "00"
},
...
]}
In case 'war exploded':
[
{
"desc": "0. General",
"id": "0"
},
{
"desc": "00. General",
"id": "00"
},
...
]