From efea34b44f9550986a88fdffe4145d4b17094db3 Mon Sep 17 00:00:00 2001 From: Thorsten Hoeger Date: Sun, 23 Sep 2018 12:00:02 +0200 Subject: [PATCH] add YAML mapper to JAX-RS --- CHANGELOG.md | 1 + .../de/taimos/dvalin/jaxrs/MapperFactory.java | 19 ++++++++++ .../jaxrs/providers/JacksonProvider.java | 37 ++++++++++++++++--- .../dvalin/jaxrs/swagger/ApiListing.java | 27 +++++++------- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a32022a6..6079f0d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * lazy-loading and refresh for Cognito JWT keys * add property provider for EC2 metadata service * BREAKING: Migrate Swagger to OpenAPI +* add YAML mapper to JAX-RS # Version 1.28 * Update dependencies diff --git a/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/MapperFactory.java b/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/MapperFactory.java index 5aacf337..bc8c8ba8 100644 --- a/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/MapperFactory.java +++ b/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/MapperFactory.java @@ -25,6 +25,8 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.fasterxml.jackson.datatype.joda.JodaModule; @@ -46,4 +48,21 @@ public static ObjectMapper createDefault() { return m; } + public static ObjectMapper createDefaultYaml() { + YAMLFactory factory = new YAMLFactory(); + factory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER); + factory.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES); + factory.enable(YAMLGenerator.Feature.SPLIT_LINES); + factory.enable(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS); + ObjectMapper m = new ObjectMapper(factory); + m.registerModule(new JodaModule()); + m.registerModule(new GuavaModule()); + m.setSerializationInclusion(Include.NON_NULL); + m.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + m.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); + m.enable(MapperFeature.AUTO_DETECT_GETTERS); + return m; + } + } diff --git a/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/providers/JacksonProvider.java b/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/providers/JacksonProvider.java index 690f986e..35f0f912 100644 --- a/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/providers/JacksonProvider.java +++ b/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/providers/JacksonProvider.java @@ -28,6 +28,7 @@ import javax.annotation.Priority; import javax.ws.rs.Consumes; +import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.Priorities; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; @@ -48,12 +49,13 @@ @Produces(MediaType.WILDCARD) public class JacksonProvider implements MessageBodyReader, MessageBodyWriter { - private final ObjectMapper mapper = MapperFactory.createDefault(); + private final ObjectMapper jsonMapper = MapperFactory.createDefault(); + private final ObjectMapper yamlMapper = MapperFactory.createDefaultYaml(); @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return mediaType.getSubtype().equals("json") || mediaType.getSubtype().endsWith("+json"); + return this.isJson(mediaType) || this.isYaml(mediaType); } @Override @@ -63,20 +65,43 @@ public long getSize(Object t, Class type, Type genericType, Annotation[] anno @Override public void writeTo(Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { - entityStream.write(this.mapper.writeValueAsBytes(t)); + if (this.isYaml(mediaType)) { + entityStream.write(this.yamlMapper.writeValueAsBytes(t)); + } else if (this.isJson(mediaType)) { + entityStream.write(this.jsonMapper.writeValueAsBytes(t)); + } else { + throw new InternalServerErrorException("Mapping error"); + } } @Override public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { - return mediaType.getSubtype().equals("json") || mediaType.getSubtype().endsWith("+json"); + return this.isJson(mediaType) || this.isYaml(mediaType); } @Override public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + final ObjectMapper mapper; + if (this.isYaml(mediaType)) { + mapper = this.yamlMapper; + } else if (this.isJson(mediaType)) { + mapper = this.jsonMapper; + } else { + throw new InternalServerErrorException("Mapping error"); + } + if (genericType == null) { - return this.mapper.readValue(entityStream, type); + return mapper.readValue(entityStream, type); } - return this.mapper.readValue(entityStream, TypeFactory.defaultInstance().constructType(genericType)); + return mapper.readValue(entityStream, TypeFactory.defaultInstance().constructType(genericType)); + } + + private boolean isYaml(MediaType mediaType) { + return mediaType.getSubtype().equals("yaml") || mediaType.getSubtype().endsWith("+yaml"); + } + + private boolean isJson(MediaType mediaType) { + return mediaType.getSubtype().equals("json") || mediaType.getSubtype().endsWith("+json"); } } diff --git a/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/swagger/ApiListing.java b/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/swagger/ApiListing.java index 2a99c50b..4d5f7818 100644 --- a/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/swagger/ApiListing.java +++ b/jaxrs/src/main/java/de/taimos/dvalin/jaxrs/swagger/ApiListing.java @@ -37,15 +37,12 @@ import javax.ws.rs.ext.Provider; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import de.taimos.daemon.DaemonProperties; import de.taimos.dvalin.jaxrs.JaxRsComponent; import de.taimos.dvalin.jaxrs.ServiceAnnotationClassesProvider; import de.taimos.dvalin.jaxrs.SpringCXFProperties; -import io.swagger.v3.core.util.Yaml; import io.swagger.v3.jaxrs2.Reader; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.models.OpenAPI; @@ -62,15 +59,21 @@ @JaxRsComponent public class ApiListing { - @Autowired - private ServiceAnnotationClassesProvider annotationProvider; + private final ServiceAnnotationClassesProvider annotationProvider; + + private final AtomicReference swaggerCache = new AtomicReference<>(); - @Autowired(required = false) private OpenApiModification config; - private final AtomicReference swaggerCache = new AtomicReference<>(); + @Autowired + public ApiListing(ServiceAnnotationClassesProvider annotationProvider) { + this.annotationProvider = annotationProvider; + } - private static final Logger LOGGER = LoggerFactory.getLogger(ApiListing.class); + @Autowired(required = false) + public void setConfig(OpenApiModification config) { + this.config = config; + } protected synchronized OpenAPI scan() { Set> classes = this.classes(); @@ -160,12 +163,8 @@ public Response getListingJson() { @Operation(hidden = true) public Response getListingYaml() { OpenAPI openAPI = this.process(); - try { - if (openAPI != null) { - return Response.ok(Response.Status.OK).entity(Yaml.mapper().writeValueAsString(openAPI)).type("application/yaml").build(); - } - } catch (Exception e) { - LOGGER.error("Failed to create YAML", e); + if (openAPI != null) { + return Response.ok().entity(openAPI).type("application/yaml").build(); } return Response.status(404).build(); }