I'm having a simple Spring Boot application with one REST endpoint to return a "Job" object, which contains a list of polymorphics, next to other stuff.
We go Code First approach and try to create the API models to fit our needs. But the generated Api Doc does not represent our model the in it's full complexity, as it does not resolve the list of polymorphics.
The Job object looks like
#Data // Lombok Getters and Setters
public final class Job {
private String foo;
private String bar;
private List<Condition> conditionList;
}
Condition is a parent object for a set of different conditions
public abstract class Condition {
}
Two example implementations of a Condition would be
#Data
public final class Internal extends Condition {
private String nodeId;
}
and
#Data
public final class Timed extends Condition {
private ZonedDateTime timestamp;
}
The REST controller is stupidly simple:
#RestController
#RequestMapping("/hello")
public class MyController {
#GetMapping
public ResponseEntity<Job> getJob() {
return new ResponseEntity<>(new Job(), HttpStatus.OK);
}
}
Now, when I open the Swagger UI and look at the generated definition, the element conditionList is an empty object {}
I tried to use the #JsonSubTypes and #ApiModel on the classed, but there was no difference in the output. I might not have used them correctly, or maybe Swagger is just not able to fulfill the job, or maybe I'm just blind or stupid.
How can I get Swagger to include the Subtypes into the generated api doc?
We "fixed" the problem by changing the structure. So it's more of a workaround.
Instead of using a List of polymorphics, we now use a "container" class, which contains each type as it's own type.
The Condition object became a "container" or "manager" class, instead of a List.
In the Job class, the field is now defined as:
private Condition condition;
The Condition class itself is now
public final class Condition{
private List<Internal> internalConditions;
// etc...
}
And, as example, the Internal lost it's parent type and is now just
public final class Internal{
// Logic...
}
The Swagger generated JSON now looks like this (excerpt):
"Job": {
"Condition": {
"Internal": {
}
"External": {
}
//etc...
}
}
Useful display of polymorphic responses in Swagger UI with Springfox 2.9.2 seems hard (impossible?). Workaround feels reasonable.
OpenAPI 3.0 appears to improve support for polymorphism. To achieve your original goal, I would either
Wait for Springfox to get Open API 3.0 support (issue 2022 in Springfox Github). Unfortunately, the issue has been open since Sept 2017 and there is no indication of Open API 3.0 support being added soon (in Aug 2019).
Change to Spring REST Docs, perhaps adding the restdocs-api-spec extension to generate Open API 3.0.
We have run into similar problems with polymorphism but have not yet attempted to implement a solution based on Spring REST Docs + restdocs-api-spec.
Related
I'm trying to create a Springboot Get API endpoint but I get a 404 not found error' here is my code
profile.java
#Getter
#Setter
public class Profile {
private String slackUsername;
private Boolean backend;
private Integer age;
private String bio;
ProfileController
#RestController
public class ProfileController {
#Autowired
private Profile profile;
#GetMapping(path = "/profile")
private ResponseEntity<String> userInfo(){
profile.setSlackUsername("Ajava");
profile.setBackend(true);
profile.setAge(00);
profile.setBio("My name is Anakhe Ajayi, I'm learning Java everyday and I love Jesus");
return ResponseEntity.ok(profile.toString());
}
Main
#SpringBootApplication
#ComponentScan("com/ajavacode/HNGBackendStage1/api.profile")
public class HngBackendStage1Application {
public static void main(String[] args) {
SpringApplication.run(HngBackendStage1Application.class, args);
}
}
porm.xml
Please fix the value in #ComponentScan annotation like this and try again.
#SpringBootApplication
#ComponentScan(basePackages = "com.ajavacode.HNGBackendStage1")
public class HngBackendStage1Application {
public static void main(String[] args) {
SpringApplication.run(HngBackendStage1Application.class, args);
}
}
Also there is an issue in ProfileController class, you are auto wiring Profile class inside it. Profile class is not a bean so this is incorrect and userInfo() method is private, it should be public. Here is the fixed version.
#RestController
public class ProfileController {
#GetMapping(path = "/profile")
public ResponseEntity<String> userInfo(){
Profile profile=new Profile();
profile.setSlackUsername("Ajava");
profile.setBackend(true);
profile.setAge(00);
profile.setBio("My name is Anakhe Ajayi, I'm learning Java everyday and I love Jesus");
return ResponseEntity.ok(profile.toString());
}
You have to check some items in your code;
Make sure the endpoint you are sending request , is correct .
Add #RequestMapping("Profile") to the controller to avoid repeated endpoint and reduce the ambiguity
Make sure your pom is correct
Looking at your code, there are a few things to mention.
First of all, your package structure looks good (besides the fact that I, personally, would keep everything lowercase).
With the package structure that you have in place, you actually don't need any #ComponentScan annotation at all. The annotation #SpringBootApplication at your main class by default scans for components with the package of that class as the base package. So you only need to set something if you want to explicitly scan for components in some other package, e.g., either at a higher level or if you want to skip packages in the hierarchy.
Next thing is the controller. Question here is: What do you actually want to achieve?
I assume that you want to build an application that provides a GET /profile endpoint that returns a response object like the example below:
{
"slackUsername": "alice",
"backend": false,
"age": 42,
"bio": "I'm just an example"
}
If my understanding is correct, there is at least one thing that is a bit odd: Currently, you defined a controller that would return the String representation of the Profile object. That isn't necessarily something as shown in the example above. If you do not override the toString() method, the result would be something like com.ajavacode.HNGBackendStage1.api.Profile#6d06d69c (see this Baeldung article for instance). And even if you use Lombok's #Data or #ToString annotations, the result will not be a JSON or XML representation but something that is suitable for logging, for instance.
Spring will already take care of the serialization into JSON (or XML) format. In your controller you can just return the Profile object or, alternatively, a ResponseEntity<Profile>:
#GetMapping(path = "/profile")
public Profile userInfo(){
Profile profile=new Profile();
profile.setSlackUsername("Ajava");
profile.setBackend(true);
profile.setAge(00);
profile.setBio("My name is Anakhe Ajayi, I'm learning Java everyday and I love Jesus");
return profile;
}
The above example would create a response with the profile as the response body and HTTP status code 200 OK. In case you use ResponseEntity, you could also adjust the HTTP status code but in your case that probably is not necessary (yet).
Autowiring the Profile class also is not correct, as already mentioned. You only need to autowire classes beans, i.e., classes that are annotated with #Component, #Service, or #Repository. The class you "autowired" is just a POJO class representing some "data object", nothing that provides any kind of business logic.
Currently we're usign jax-ws to create and consume SOAP web services, generating java classes through the wsimport goal.
We then, create a Service class to call this web service. The code in the Service class is like this:
public WebServiceRS webServiceCall() {
Security security = SecurityFactory.getTokenSecurity();
MessageHeader header = MessageHeaderFactory.getMessageHeader(SERVICE_ACTION);
LOGGER.info("Calling CreatePassengerNameRecordRQ webService ...");
Holder<Security> securityHolder = new Holder<>(security);
Holder<MessageHeader> headerHolder = new Holder<>(header);
addHandlers(port);
WebServiceRS webServiceRS = port.webServiceRQ(
headerHolder,
securityHolder,
getRequestBody()
);
...
return webServiceRS
}
So what we noticed and that could be problematic is that this Service class depends on the .wsdl generated files like Security, MessageHeader, WebServiceRS and so on. And so when we update the .wsdl file to a newer version the code would eventually break because of this dependency.
What we're trying to achieve, then, is a way to invert this dependency so that we can update the web service (i.e .wsdl generated classes) without changing our Service class. We're having problems because we haven't figured a pattern to solve this issue. The main difficulty seems to arise from the fact that we can't change these generated classe to implement an interface as an example.
We're interested in patterns or best practices that can loose this coupling.
Solution 1. Use Adapter pattern
In your case, the contract of service will change and you need to sync two different objects. Adapter will provide a different interface to its subject.
Create an interface for your client model
public interface ClientModel {
String getValue();
}
Create Adapter which will convert DTO to ClientModel
public class ClientModelAdapter implements ClientModel {
private DTO dto;
public ClientModelAdapter(DTO dto) {
this.dto = dto;
}
#Override
public String getValue() {
return dto.getValue();
}
}
public class DTO {
private String value;
public String getValue() {
return value;
}
}
What is the exact difference between Adapter and Proxy patterns?
Solution 2. Use Mapper Pattern
You can simply create a mapper between DTO and ClientModel
public class ClientModelMapper {
ClientModel map(DTO dto) {
ClientModel clientModel = new ClientModel();
clientModel.setValue(dto.getValue());
return clientModel;
}
}
Mapper component that transfers the data, making sure that both DTO and client model don't need to know about each other.
In this case, you can use MapStruct framework to avoid handmade mapping.
Solution 3. Make service API backward compatible
Probably you can avoid breaking changes in SOAP service and make API backward compatible. Backwards compatibility and Web Services
You could put in behind a proxy class. That way the only thing you would need to adapt to the change of .wsdl file would be the proxy class.
I am starting with Flux today for it is pretty powerful. Now I have set up a whole simple Spring boot 2 project to work with this but the returned objects are empty.
I started a very simple Spring Boot project with some dependencies:
Reactive Web (embedded Netty + Spring WebFlux)
Reactive MongoDB (Spring Data MongoDB)
Thymeleaf template engine
Lombok (to simplify writing POJOs)
And added some code:
controller:
#RestController
public class ChapterController {
#Autowired
private ChapterRepository repository;
#GetMapping("/chapters")
public Flux<Chapter> listing() {
return repository.findAll();
}
}
Repository:
public interface ChapterRepository extends ReactiveCrudRepository<Chapter, String> {}
Configuration: (to load some data in the embeded Mongodb)
#Configuration
public class LoadDatabase {
#Bean
CommandLineRunner init(ChapterRepository repository){
return args -> {
Flux.just(
new Chapter("The life of Batman"),
new Chapter("Batmans most glorious' code"),
new Chapter("The hero we needed but didn't deserve, Batman."))
.flatMap(repository::save)
.subscribe(System.out::println);
};
}
}
Data class:
#Data
#Document
public class Chapter {
#Id
private String id;
private String name;
public Chapter(String name) {
this.name = name;
}
}
Ok, now when I start the application and access the endpoint: http://localhost:8080/chapters it returns this:
[
{},
{},
{}
]
It is displaying the same amount of objects that I created in the LoadDatabase class. When I change the amount of objects created, it shows that amount on the endpoint.
I don't know what I did wrong, I tried debugging the flux object returned. But I can't make anything out of it.
I hope someone can spot my mistake!
You are getting empty objects because the data is not saved and something went wrong.
You are using #Data lombok annotation which is like having implicit #Getter, #Setter, #ToString, #EqualsAndHashCode and #RequiredArgsConstructor annotations on the class (except that no constructor will be generated if any explicitly written constructor exists). but sometimes it doesn't work if not configured in IDE correctly so try once with manual getters and setters for the properties.
If the manual getters/setters work then try below to troubleshoot for lombok.
Ensure that your IDE is aware of lombok.
IntelliJ : Lombok added but getters and setters not recognized in Intellij IDEA
Eclipse : Lombok is not generating getter and setter
If the problem still exists then follow this similar thread's one of the comments here
This is one of those topics I don't even know how to search in google (tried already, most of the results were for C#), so here I go:
I'm messing around with our huge application, trying to get to work a brand new DAO/Entity/Service/DTO.. euh...thing. I've been left more or less on my own, and, again, more or less, I'm getting to understand some of the hows and maybe one or two of the whys.
The thing is that I got all, the way "up", from the DB to the Service:
I got a DAO class which executes a query stored on an Entity class. After executing it, it returns the Entity with the values.
The service receives the Entity and, somehow, transforms the Entity to a DTO and returns it to whenever is needed.
My problem is with the "somehow" thing the code goes like this:
DTOClass dto = ClassTransformerFromEntityToDTO.INSTANCE.apply(entityQueryResult);
I went into ClassTransformerFromEntityToDTO and found this:
public enum ClassTransfomerFromEntityToDTO implements Function<EntityClass,DTO Class> ) {
INSTANCE;
#Override
public DTOClass apply(EntityClass entityInstance) {
/*Code to transform the Entity to DTO and the return*/
}
}
The class that this... thing, implements, is this:
package com. google .common . base;
import com. google .common . annotations. GwtCompatible ;
import javax. annotation .Nullable ;
#GwtCompatible
public abstract interface Function <F , T >
{
#Nullable
public abstract T apply (#Nullable F paramF) ;
public abstract boolean equals (#Nullable Object paramObject) ;
}
I'm in the classic "everyone who where at the beginning of the project fled", and no one knows why is this or what is this (The wisest one told me that maybe it had something to do with Spring), so, I have two main questions (which can be more or less answered in the same side):
1) What's this? What's the point of using an enum with a function to make a conversion?
2) What's the point of this? Why can I just make a class with a single function and forget about this wizardry?
not sure there's much to answer here... And I'm adding an answer to illustrate my thoughts with some code I've seen, but that you have is horrible. I've actually seem similar stuff. My guess is that that codes actually precedes Spring. It's used as some sort of Singleton.
I have seen code like this, which is worse:
public interface DTO {
find(Object args)
}
public class ConcreteDTO1 implements DTO {
...
}
public class ConcreteDTO2 implements DTO {
...
}
public enum DTOType {
CONCRETE_DTO1(new ConcreteDTO1(someArgs)),
CONCRETE_DTO2(new ConcreteDTO2(someOtherArgs))
private DTO dto;
public DTOType(DTO dto) {
this.dto = dto;
}
public DTO dto() {
return dto;
}
}
and then the DTOs are basically accessed through the Enum Type:
DTOType.CONCRETE_DTO1.dto().find(args);
So everyone trying to get hold of a DTO accesses it through the enum. With Spring, you don't need any of that. The IoC container is meant to avoid this kind of nonsense, that's why my guess is that it precedes Spring, from some ancient version of the app when Spring was not there. But it could be that someone was wired to do such things regardless of whether Spring was already in the app or not.
For that kind of stuff you're trying to do, you're better of with the Visitor pattern. Here's an example from a different answer: passing different type of objects dynamically on same method
It's me. From the future.
Turns out that this construct is a propossed Singleton Implementation, at least on "Effective Java 2nd edition".
So, yeah, Ulise's guess was well oriented.
When I call fooMethod, I want to process first class annotation (with First.class - in my project this checks if user is logged) and then method annotation (with Second.class - in my project this checks if uses has desired rights to access this specific method. So I need to ensure user is logged first). Is there a way to do that?
#With(First.class)
public class Foo{
#With(Second.class)
public static void fooMethod(){
}
}
Also I wonder why custum action ignores annotation. Code below doesn't process anotation #With(First.class).
public class Foo2 extends Action<CustomAnnotation> {
#Override
#With(First.class)
public Promise<Result> call(Http.Context context) throws Throwable {
return delegate.call(context);
}
}
}
Similar unanswered question: Java + Play Framework 2 with nested action compositions in the same class
In Play 2.4 there appears to be an option for this from the docs here:
Note: If you want the action composition annotation(s) put on a
Controller class to be executed before the one(s) put on action
methods set play.http.actionComposition.controllerAnnotationsFirst =
true in application.conf. However, be aware that if you use a third
party module in your project it may rely on a certain execution order
of its annotations.
The note is only present in the 2.4 docs, so presumably it doesn't work in previous versions.
It looks like at least in Java 8 you have an order in annotation of the same kind: Java Annotations Reflection Ordering.