diff --git a/src/main/java/io/swagger/inflector/SwaggerInflector.java b/src/main/java/io/swagger/inflector/SwaggerInflector.java index 5d55dce4..886c7528 100644 --- a/src/main/java/io/swagger/inflector/SwaggerInflector.java +++ b/src/main/java/io/swagger/inflector/SwaggerInflector.java @@ -28,11 +28,7 @@ import io.swagger.inflector.converters.Converter; import io.swagger.inflector.converters.InputConverter; import io.swagger.inflector.models.InflectResult; -import io.swagger.inflector.processors.JsonExampleProvider; -import io.swagger.inflector.processors.JsonNodeExampleSerializer; -import io.swagger.inflector.processors.JsonProvider; -import io.swagger.inflector.processors.XMLExampleProvider; -import io.swagger.inflector.processors.YamlExampleProvider; +import io.swagger.inflector.processors.*; import io.swagger.inflector.utils.DefaultContentTypeProvider; import io.swagger.inflector.utils.DefaultSpecFilter; import io.swagger.inflector.utils.ResolverUtil; @@ -53,18 +49,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - import javax.servlet.ServletContext; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; +import java.util.*; public class SwaggerInflector extends ResourceConfig { private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerInflector.class); @@ -194,6 +184,7 @@ protected void init(Configuration configuration) { register(new DefaultContentTypeProvider(MediaType.APPLICATION_JSON_TYPE), ContextResolver.class); } + enableProcessor(JacksonProcessor.class, MediaType.APPLICATION_JSON_TYPE); enableSwaggerJSON(swagger, configuration.getSwaggerProcessors()); } else if ("xml".equalsIgnoreCase(item)) { // XML @@ -201,12 +192,14 @@ protected void init(Configuration configuration) { register(new DefaultContentTypeProvider(MediaType.APPLICATION_XML_TYPE), ContextResolver.class); } + enableProcessor(JacksonProcessor.class, MediaType.APPLICATION_XML_TYPE); register(JacksonJaxbXMLProvider.class); register(XMLExampleProvider.class); } else if ("yaml".equalsIgnoreCase(item)) { // YAML Yaml.mapper().registerModule(simpleModule); register(YamlExampleProvider.class); + enableProcessor(JacksonProcessor.class, JacksonProcessor.APPLICATION_YAML_TYPE); enableSwaggerYAML(swagger, configuration.getSwaggerProcessors()); } } @@ -332,6 +325,23 @@ private String basePath(String basePath, String path) { return basePath + path; } + private void enableProcessor(Class cls, MediaType type) { + List processors = EntityProcessorFactory.getProcessors(); + for(EntityProcessor processor : processors) { + if (processor.getClass().equals(cls)) { + processor.enableType(type); + return; + } + } + try { + EntityProcessor processor = (EntityProcessor) cls.newInstance(); + processor.enableType(type); + } + catch (Exception e) { + LOGGER.error("unable to initialize class " + cls); + } + } + private void enableSwaggerJSON(Swagger swagger, List swaggerProcessors) { final Resource.Builder builder = Resource.builder(); builder.path(basePath(originalBasePath, config.getSwaggerBase() + "swagger.json")) diff --git a/src/main/java/io/swagger/inflector/config/Configuration.java b/src/main/java/io/swagger/inflector/config/Configuration.java index 532bd86e..8d28817b 100644 --- a/src/main/java/io/swagger/inflector/config/Configuration.java +++ b/src/main/java/io/swagger/inflector/config/Configuration.java @@ -58,6 +58,15 @@ public class Configuration { private Set validatePayloads = Collections.emptySet(); public String getSwaggerBase() { + if("".equals(swaggerBase) || "/".equals(swaggerBase)) { + return swaggerBase; + } + + if(swaggerBase != null) { + if(swaggerBase.endsWith("/")) { + return swaggerBase.substring(0, swaggerBase.length() - 1); + } + } return swaggerBase; } diff --git a/src/main/java/io/swagger/inflector/controllers/SwaggerOperationController.java b/src/main/java/io/swagger/inflector/controllers/SwaggerOperationController.java index cc848b07..9a409efa 100644 --- a/src/main/java/io/swagger/inflector/controllers/SwaggerOperationController.java +++ b/src/main/java/io/swagger/inflector/controllers/SwaggerOperationController.java @@ -607,28 +607,35 @@ else if(output instanceof ObjectExample) { // get acceptable content types List processors = EntityProcessorFactory.getProcessors(); + MediaType responseMediaType = null; + // take first compatible one for (EntityProcessor processor : processors) { + if(responseMediaType != null) { + break; + } for (MediaType mt : requestContext.getAcceptableMediaTypes()) { LOGGER.debug("checking type " + mt.toString() + " against " + processor.getClass().getName()); if (processor.supports(mt)) { builder.type(mt); + responseMediaType = mt; break; } } } - // no match based on Accept header, use first processor in list - for (EntityProcessor processor : processors) { - List supportedTypes = processor.getSupportedMediaTypes(); - if(supportedTypes.size() > 0) { - builder.type(supportedTypes.get(0)); - break; + if(responseMediaType == null) { + // no match based on Accept header, use first processor in list + for (EntityProcessor processor : processors) { + List supportedTypes = processor.getSupportedMediaTypes(); + if (supportedTypes.size() > 0) { + builder.type(supportedTypes.get(0)); + break; + } } } } - builder.entity(output); } return builder.build(); diff --git a/src/main/java/io/swagger/inflector/processors/BinaryProcessor.java b/src/main/java/io/swagger/inflector/processors/BinaryProcessor.java index 403b27c2..bbe70d8b 100644 --- a/src/main/java/io/swagger/inflector/processors/BinaryProcessor.java +++ b/src/main/java/io/swagger/inflector/processors/BinaryProcessor.java @@ -31,14 +31,26 @@ import java.util.List; public class BinaryProcessor implements EntityProcessor { - private static final Logger LOGGER = LoggerFactory.getLogger(BinaryProcessor.class); + private static List SUPPORTED_TYPES = new ArrayList<>(); + + static { + SUPPORTED_TYPES.add(MediaType.APPLICATION_OCTET_STREAM_TYPE); + } @Override public List getSupportedMediaTypes() { - List supportedTypes = new ArrayList<>(); - supportedTypes.add(MediaType.APPLICATION_OCTET_STREAM_TYPE); - return supportedTypes; + return new ArrayList(SUPPORTED_TYPES); + } + + @Override + public void enableType(MediaType type) { + MediaType t = type; + if(t != null) { + if(!SUPPORTED_TYPES.contains(t)) { + SUPPORTED_TYPES.add(type); + } + } } @Override diff --git a/src/main/java/io/swagger/inflector/processors/EntityProcessor.java b/src/main/java/io/swagger/inflector/processors/EntityProcessor.java index fcb81713..3ec82f39 100644 --- a/src/main/java/io/swagger/inflector/processors/EntityProcessor.java +++ b/src/main/java/io/swagger/inflector/processors/EntityProcessor.java @@ -28,6 +28,8 @@ public interface EntityProcessor { List getSupportedMediaTypes(); + void enableType(MediaType type); + Object process(MediaType mediaType, InputStream entityStream, Class cls) throws ConversionException; Object process(MediaType mediaType, InputStream entityStream, JavaType javaType); diff --git a/src/main/java/io/swagger/inflector/processors/EntityProcessorFactory.java b/src/main/java/io/swagger/inflector/processors/EntityProcessorFactory.java index 7deacca9..9c72ae11 100644 --- a/src/main/java/io/swagger/inflector/processors/EntityProcessorFactory.java +++ b/src/main/java/io/swagger/inflector/processors/EntityProcessorFactory.java @@ -17,6 +17,8 @@ package io.swagger.inflector.processors; import io.swagger.inflector.converters.ConversionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.ws.rs.core.MediaType; import java.io.InputStream; @@ -24,6 +26,8 @@ import java.util.List; public class EntityProcessorFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(EntityProcessorFactory.class); + private static List PROCESSORS = new ArrayList(); static { @@ -32,8 +36,21 @@ public class EntityProcessorFactory { PROCESSORS.add(new BinaryProcessor()); } - public static void addProcessor(EntityProcessor processor) { - PROCESSORS.add(processor); + public static void addProcessor(Class cls, MediaType type) { + for(EntityProcessor entityProcessor : PROCESSORS) { + if(entityProcessor.getClass().equals(cls)) { + entityProcessor.enableType(type); + return; + } + } + try { + EntityProcessor processor = (EntityProcessor) cls.newInstance(); + PROCESSORS.add(processor); + processor.enableType(type); + } + catch (Exception e) { + LOGGER.debug("unable to add processor " + cls.getName()); + } } public static Object readValue(MediaType mediaType, InputStream entityStream, Class class1) throws ConversionException { diff --git a/src/main/java/io/swagger/inflector/processors/JacksonProcessor.java b/src/main/java/io/swagger/inflector/processors/JacksonProcessor.java index 05ba5015..243dc491 100644 --- a/src/main/java/io/swagger/inflector/processors/JacksonProcessor.java +++ b/src/main/java/io/swagger/inflector/processors/JacksonProcessor.java @@ -37,21 +37,31 @@ public class JacksonProcessor implements EntityProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(JacksonProcessor.class); + public static MediaType APPLICATION_YAML_TYPE = new MediaType("application", "yaml"); + private static XmlMapper XML = new XmlMapper(); - private static MediaType APPLICATION_YAML = new MediaType("application", "yaml"); - private static Collection SUPPORTED_TYPES = Collections - .unmodifiableList(Arrays.asList(MediaType.APPLICATION_JSON_TYPE, - MediaType.APPLICATION_XML_TYPE, APPLICATION_YAML, MediaType.TEXT_PLAIN_TYPE)); + private static List SUPPORTED_TYPES = new ArrayList<>(); + + static { + SUPPORTED_TYPES.add(MediaType.APPLICATION_JSON_TYPE); + } @Override public List getSupportedMediaTypes() { return new ArrayList(SUPPORTED_TYPES); } + @Override + public void enableType(MediaType type) { + if(!SUPPORTED_TYPES.contains(type)) { + SUPPORTED_TYPES.add(type); + } + } + @Override public boolean supports(MediaType mediaType) { for (MediaType item : SUPPORTED_TYPES) { - if (item.isCompatible(mediaType)) { + if (item.isCompatible(mediaType) && !mediaType.isWildcardType()) { return true; } } @@ -68,7 +78,7 @@ public Object process(MediaType mediaType, InputStream entityStream, if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) { return XML.readValue(entityStream, javaType); } - if (APPLICATION_YAML.isCompatible(mediaType)) { + if (APPLICATION_YAML_TYPE.isCompatible(mediaType)) { return Yaml.mapper().readValue(entityStream, javaType); } } catch (IOException e) { @@ -92,7 +102,7 @@ public Object process(MediaType mediaType, InputStream entityStream, Class cl if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) { return XML.readValue(entityStream, cls); } - if (APPLICATION_YAML.isCompatible(mediaType)) { + if (APPLICATION_YAML_TYPE.isCompatible(mediaType)) { return Yaml.mapper().readValue(entityStream, cls); } } catch (Exception e) { diff --git a/src/main/java/io/swagger/inflector/utils/DefaultExceptionMapper.java b/src/main/java/io/swagger/inflector/utils/DefaultExceptionMapper.java index 9ace7c9e..8eaecd22 100644 --- a/src/main/java/io/swagger/inflector/utils/DefaultExceptionMapper.java +++ b/src/main/java/io/swagger/inflector/utils/DefaultExceptionMapper.java @@ -17,6 +17,8 @@ package io.swagger.inflector.utils; import io.swagger.inflector.models.ApiError; +import io.swagger.inflector.processors.EntityProcessor; +import io.swagger.inflector.processors.EntityProcessorFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +32,7 @@ import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Providers; +import java.util.List; @Provider public class DefaultExceptionMapper implements ExceptionMapper { @@ -50,9 +53,45 @@ public Response toResponse(Exception exception) { } } else { LOGGER.error(error.getMessage(), exception); - } final Response.ResponseBuilder builder = Response.status(code).entity(error); + } + + final Response.ResponseBuilder builder = Response.status(code).entity(error); + + MediaType responseMediaType = null; + List processors = EntityProcessorFactory.getProcessors(); + for (EntityProcessor processor : processors) { + if(responseMediaType != null) { + break; + } + for (MediaType mt : headers.getAcceptableMediaTypes()) { + LOGGER.debug("checking type " + mt.toString() + " against " + processor.getClass().getName()); + if (processor.supports(mt)) { + builder.type(mt); + responseMediaType = mt; + break; + } + } + } + + if(responseMediaType == null) { + // no match based on Accept header, use first processor in list + for (EntityProcessor processor : processors) { + List supportedTypes = processor.getSupportedMediaTypes(); + if (supportedTypes.size() > 0) { + MediaType mt = supportedTypes.get(0); + builder.type(mt); + responseMediaType = mt; + break; + } + } + } + + if(responseMediaType == null) { + responseMediaType = MediaType.WILDCARD_TYPE; + } + final ContextResolver selector = providers.getContextResolver( - ContentTypeSelector.class, MediaType.WILDCARD_TYPE); + ContentTypeSelector.class, responseMediaType); if (selector != null) { selector.getContext(getClass()).apply(headers.getAcceptableMediaTypes(), builder); } diff --git a/src/test/java/io/swagger/test/processors/JacksonProcessorTest.java b/src/test/java/io/swagger/test/processors/JacksonProcessorTest.java index c0a42f54..74c31a1f 100644 --- a/src/test/java/io/swagger/test/processors/JacksonProcessorTest.java +++ b/src/test/java/io/swagger/test/processors/JacksonProcessorTest.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.inflector.processors.EntityProcessorFactory; +import io.swagger.inflector.processors.JacksonProcessor; import org.testng.annotations.Test; import javax.ws.rs.core.MediaType; @@ -33,6 +34,8 @@ public class JacksonProcessorTest { public void testConvertXMLContent() throws Exception { String input = "1fehguy"; + EntityProcessorFactory.addProcessor(JacksonProcessor.class, MediaType.APPLICATION_XML_TYPE); + InputStream is = new ByteArrayInputStream(input.getBytes()); ObjectNode o = (ObjectNode) EntityProcessorFactory.readValue(MediaType.APPLICATION_XML_TYPE, is, JsonNode.class); assertEquals(o.getClass(), ObjectNode.class); @@ -42,6 +45,7 @@ public void testConvertXMLContent() throws Exception { @Test public void testConvertJsonContent() throws Exception { String input = "{\"name\":\"fehguy\"}"; + EntityProcessorFactory.addProcessor(JacksonProcessor.class, MediaType.APPLICATION_JSON_TYPE); InputStream is = new ByteArrayInputStream(input.getBytes()); ObjectNode o = (ObjectNode) EntityProcessorFactory.readValue(MediaType.APPLICATION_JSON_TYPE, is, JsonNode.class); @@ -52,6 +56,7 @@ public void testConvertJsonContent() throws Exception { @Test public void testConvertYamlContent() throws Exception { String input = "name: fehguy\nuserId: 42"; + EntityProcessorFactory.addProcessor(JacksonProcessor.class, JacksonProcessor.APPLICATION_YAML_TYPE); InputStream is = new ByteArrayInputStream(input.getBytes()); MediaType t = MediaType.valueOf("application/yaml");