Intellij different behavior depends on deployed war exploded or not - spring

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"
},
...
]

Related

Spring Config Server #Value returns null

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

How to prevent spring hateoas to return empty links?

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

Loading json into the database during integration tests

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

Spring Cloud Contract with Jersey

I have a simple project Spring boot project. It contains one Jersey based Controller:
#Path("persons")
#Produces(MediaType.APPLICATION_JSON)
public class PersonsController {
#GET
public Person get() {
return new Person("James", 20);
}
}
It returns json response as expected (url: http://localhost:PORT/persons):
{
"name": "James",
"age": 20
}
My aim is to add Spring Cloud Contract tests for this controller. I have added all required mvn configurations, and test:
public class MvcTest {
#Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new PersonsController());
}
}
Here is contract (groovy file):
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'GET'
url('persons')
}
response {
status 200
body(
"name": "James",
"age": 20
)
}
}
When I run mvn clean package following error always is returned:
Failed tests:
ContractVerifierTest.validate_getTest:26 expected:<[200]> but was:<[404]>
I believe this should be related to the ServletDispatcher as it doesn't see Jersey's paths. The same project with replaced #Path to #RequestMapping works. However, I need to make it working with Jersey.
Have I missed something?
Have you checked the section about jaxrs support? https://cloud.spring.io/spring-cloud-contract/1.0.x/spring-cloud-contract.html#_jax_rs_support. Here you have an example how you can use it https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject

How to remove the "_embedded" property in Spring HATEOAS

I'm using Spring Boot and HATEOAS to build a REST API and when my API returns a collection, it is wrapped inside a "_embedded" property, like so:
{
"_links":{
"self":{
"href":"http://localhost:8080/technologies"
}
},
"_embedded":{
"technologies":[
{
"id":1,
"description":"A",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/1"
}
}
},
{
"id":2,
"description":"B",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/2"
}
}
}
]
}
}
I want the response to be like this:
{
"_links":{
"self":{
"href":"http://localhost:8080/technologies"
}
},
"technologies":[
{
"id":1,
"description":"A",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/1"
}
}
},
{
"id":2,
"description":"B",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/2"
}
}
}
]
}
My TechnologiesController:
#RestController
#ExposesResourceFor(Technology.class)
#RequestMapping(value = "/technologies")
public class TechnologiesController {
...
#ResquestMapping(method = RequestMethod.GET, produces = "application/vnd.xpto-technologies.text+json")
public Resources<Resource<Technology>> getAllTechnologies() {
List<Technology> technologies = technologyGateway.getAllTechnologies();
Resources<<Resource<Technology>> resources = new Resources<Resource<Technology>>(technologyResourceAssembler.toResources(technologies));
resources.add(linkTo(methodOn(TechnologiesController.class).getAllTechnologies()).withSelfRel());
return resources;
}
The configuration class has the annotation #EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL).
What is the best way to produce the response without the "_embedded"?
As the documentation says
application/hal+json responses should be sent to requests that accept
application/json
In order to omit _embedded in you response you'll need to add
spring.hateoas.use-hal-as-default-json-media-type=false
to application.properties.
I close HAL feature, because it is hard to using Resources/Resource by restTemplate. I disable this feature by following code:
public class SpringRestConfiguration implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultMediaType(MediaType.APPLICATION_JSON);
config.useHalAsDefaultJsonMediaType(false);
}
}
It work for me. HAL is good if there are more support with restTemplate.
Adding this Accept header to the request:
Accept : application/x-spring-data-verbose+json
For those who use Spring Data, and consider it as a problem - solution is to set
spring.data.rest.defaultMediaType = application/json
in application properties.
There still links will be available, but no _embedded any more.
What you're describing in the produced and expected results are semantically different things. The former thing is the HAL representation of a Collection<Technology>. The latter, which you expect is the representation of:
class Wrapper {
Resources<Technology> technologies;
}
Note how this is how we actually create the top level technologies property that you would like to see in your response. You don't create any of the latter in your controller. A top-level Resourcesinstance is basically a collection and the only way to represent a top-level collection in HAL is _embedded. Apparently you don't want that but that's what you have written in your controller method.
Assuming you have Wrapper, something like this should work (untested):
Wrapper wrapper = new Wrapper(assembler.toCollectionModel(technologies);
EntityModel<Wrapper> model = EntityModel.of(wrapper);
model.add(linkTo(…));
PS: As of Spring HATEOAS 1.0, Resources is CollectionModel and Resourceis EntityModel.
You can use this code in the service
constructor(
private httpClient: HttpClient
) { }
retrieveAllStudents(){
return this.httpClient.get<any[]>(`http://localhost:8080/students`);
}
This will deal with the _embedded part of Json and extract the desired data.
export class ListStudentsComponent implements OnInit {
// declaring variables to be used
student: Student;
students: Student[];
message: string;
// injecting student service into the constuctor
constructor(
private studentService: StudentService,
) { }
ngOnInit() {
this.refreshStudents();
}
refreshStudents(){
this.studentService.retrieveAllStudents().subscribe(
response => {
console.log(response);
this.students = response._embedded.students as Student[];
}
);
}
For latest versions in Spring RepositoryRestConfigurer doesn't include the method public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) you'd need to override the default method on RepositoryRestConfigurer which include cors parameter.
public class RestConfiguration implements RepositoryRestConfigurer {
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.setDefaultMediaType(MediaType.APPLICATION_JSON);
config.useHalAsDefaultJsonMediaType(false);
}
}

Resources