From 78422a10de417d592baf3c4fdb18d2fe2391f30b Mon Sep 17 00:00:00 2001 From: Rick Herrick <jrherrick@wustl.edu> Date: Thu, 15 Sep 2016 13:19:09 -0500 Subject: [PATCH] XNAT-4521 Moved MVC and REST controller to later in the process to allow plugins to initialize before running scan with Swagger init. --- .../nrg/xapi/configuration/RestApiConfig.java | 55 ++++++++++++ .../org/nrg/xapi/rest/dicom/AnonymizeApi.java | 12 ++- .../xnat/configuration/ApplicationConfig.java | 3 +- .../org/nrg/xnat/configuration/WebConfig.java | 61 +++---------- .../xnat/initialization/ControllerConfig.java | 10 +++ .../initialization/XnatWebAppInitializer.java | 14 +-- .../controllers/ManageElementsController.java | 87 ------------------- 7 files changed, 92 insertions(+), 150 deletions(-) create mode 100644 src/main/java/org/nrg/xapi/configuration/RestApiConfig.java create mode 100644 src/main/java/org/nrg/xnat/initialization/ControllerConfig.java delete mode 100644 src/main/java/org/nrg/xnat/spawner/controllers/ManageElementsController.java diff --git a/src/main/java/org/nrg/xapi/configuration/RestApiConfig.java b/src/main/java/org/nrg/xapi/configuration/RestApiConfig.java new file mode 100644 index 00000000..bcad3ce5 --- /dev/null +++ b/src/main/java/org/nrg/xapi/configuration/RestApiConfig.java @@ -0,0 +1,55 @@ +package org.nrg.xapi.configuration; + +import org.nrg.framework.annotations.XapiRestController; +import org.nrg.xnat.configuration.WebConfig; +import org.nrg.xnat.services.XnatAppInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Locale; + +@Configuration +@EnableSwagger2 +@ComponentScan({"org.nrg.xapi.rest", "org.nrg.xnat.spawner.configuration"}) +public class RestApiConfig { + @Bean + public Docket api(final XnatAppInfo info, final MessageSource messageSource) { + _log.debug("Initializing the Swagger Docket object"); + // TODO: When updating to Swagger 2.5.0 or later, remove the pathMapping("/xapi") call at the end. + return new Docket(DocumentationType.SWAGGER_2).select() + .apis(RequestHandlerSelectors.withClassAnnotation(XapiRestController.class)) + .paths(PathSelectors.any()) + .build() + .apiInfo(apiInfo(info, messageSource)) + .pathMapping("/xapi"); + } + + private ApiInfo apiInfo(final XnatAppInfo info, final MessageSource messageSource) { + return new ApiInfo(getMessage(messageSource, "apiInfo.title"), + getMessage(messageSource, "apiInfo.description"), + info.getVersion(), + getMessage(messageSource, "apiInfo.termsOfServiceUrl"), + new Contact(getMessage(messageSource, "apiInfo.contactName"), + getMessage(messageSource, "apiInfo.contactUrl"), + getMessage(messageSource, "apiInfo.contactEmail")), + getMessage(messageSource, "apiInfo.license"), + getMessage(messageSource, "apiInfo.licenseUrl")); + } + + private String getMessage(final MessageSource messageSource, final String messageId) { + return messageSource.getMessage(messageId, null, Locale.getDefault()); + } + + private static final Logger _log = LoggerFactory.getLogger(WebConfig.class); +} diff --git a/src/main/java/org/nrg/xapi/rest/dicom/AnonymizeApi.java b/src/main/java/org/nrg/xapi/rest/dicom/AnonymizeApi.java index bbf497ed..daf70cd6 100644 --- a/src/main/java/org/nrg/xapi/rest/dicom/AnonymizeApi.java +++ b/src/main/java/org/nrg/xapi/rest/dicom/AnonymizeApi.java @@ -1,9 +1,6 @@ package org.nrg.xapi.rest.dicom; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.*; import org.apache.commons.lang3.StringUtils; import org.nrg.config.exceptions.ConfigServiceException; import org.nrg.framework.annotations.XapiRestController; @@ -78,7 +75,7 @@ public class AnonymizeApi extends AbstractXapiProjectRestController { @ApiResponse(code = 403, message = "Insufficient permissions to modify the site-wide anonymization script settings."), @ApiResponse(code = 500, message = "An unexpected error occurred.")}) @RequestMapping(value = "site/enabled", consumes = MediaType.TEXT_PLAIN_VALUE, method = RequestMethod.PUT) - public ResponseEntity<Void> setSiteWideAnonScriptEnabled(@RequestBody final boolean enable) throws ConfigServiceException { + public ResponseEntity<Void> setSiteWideAnonScriptEnabled(@ApiParam(value = "Whether the site-wide anonymization script should be enabled or disabled.", required = true) @RequestParam(required= false, defaultValue = "true") final boolean enable) throws ConfigServiceException { final HttpStatus status = isPermitted(); if (status != null) { return new ResponseEntity<>(status); @@ -119,7 +116,8 @@ public class AnonymizeApi extends AbstractXapiProjectRestController { @ApiResponse(code = 404, message = "The specified project wasn't found."), @ApiResponse(code = 500, message = "An unexpected error occurred.")}) @RequestMapping(value = "projects/{projectId}", consumes = MediaType.TEXT_PLAIN_VALUE, method = RequestMethod.PUT) - public ResponseEntity<Void> setProjectAnonScript(@PathVariable("projectId") final String projectId, @RequestBody final String script) throws NrgServiceException { + public ResponseEntity<Void> setProjectAnonScript(@ApiParam(value = "Indicates the ID of the project to be enabled or disabled.", required = true) @PathVariable("projectId") final String projectId, + @ApiParam(value = "Whether the specified project's anonymization script should be enabled or disabled.", required = true) @RequestBody final String script) throws NrgServiceException { final HttpStatus status; try { status = canEditProject(projectId); @@ -161,7 +159,7 @@ public class AnonymizeApi extends AbstractXapiProjectRestController { @ApiResponse(code = 403, message = "Insufficient permissions to modify the project-specific anonymization script settings."), @ApiResponse(code = 500, message = "An unexpected error occurred.")}) @RequestMapping(value = "projects/{projectId}/enabled", consumes = MediaType.TEXT_PLAIN_VALUE, method = RequestMethod.PUT) - public ResponseEntity<Void> setProjectAnonScriptEnabled(@PathVariable("projectId") final String projectId, @RequestBody final boolean enable) throws NrgServiceException { + public ResponseEntity<Void> setProjectAnonScriptEnabled(@PathVariable("projectId") final String projectId, @RequestParam(required= false, defaultValue = "true") final boolean enable) throws NrgServiceException { final HttpStatus status; try { status = canEditProject(projectId); diff --git a/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java b/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java index d3347eba..59df0a15 100644 --- a/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java +++ b/src/main/java/org/nrg/xnat/configuration/ApplicationConfig.java @@ -36,8 +36,7 @@ import java.util.List; "org.nrg.xdat.daos", "org.nrg.xdat.services.impl.hibernate", "org.nrg.xft.daos", "org.nrg.xft.event.listeners", "org.nrg.xft.services", "org.nrg.xnat.configuration", "org.nrg.xnat.daos", "org.nrg.xnat.event.listeners", "org.nrg.xnat.helpers.merge", - "org.nrg.xnat.initialization.tasks", "org.nrg.xnat.services.impl.hibernate", - "org.nrg.xnat.spawner.repositories"}) + "org.nrg.xnat.initialization.tasks", "org.nrg.xnat.services.impl.hibernate"}) @Import({FeaturesConfig.class, ReactorConfig.class}) @ImportResource("WEB-INF/conf/mq-context.xml") public class ApplicationConfig { diff --git a/src/main/java/org/nrg/xnat/configuration/WebConfig.java b/src/main/java/org/nrg/xnat/configuration/WebConfig.java index 7d39bc1f..fbdb7e03 100644 --- a/src/main/java/org/nrg/xnat/configuration/WebConfig.java +++ b/src/main/java/org/nrg/xnat/configuration/WebConfig.java @@ -1,17 +1,10 @@ package org.nrg.xnat.configuration; -import org.nrg.framework.annotations.XapiRestController; import org.nrg.xdat.preferences.SiteConfigPreferences; -import org.nrg.xnat.services.XnatAppInfo; -import org.nrg.xnat.spawner.configuration.SpawnerConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; @@ -28,22 +21,14 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.Contact; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; import javax.xml.bind.Marshaller; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @Configuration @EnableWebMvc -@EnableSwagger2 -@Import(SpawnerConfig.class) -@ComponentScan({"org.nrg.xapi.rest", "org.nrg.xnat.spawner.controllers"}) public class WebConfig extends WebMvcConfigurerAdapter { @Autowired public void setJackson2ObjectMapperBuilder(final Jackson2ObjectMapperBuilder objectMapperBuilder) { @@ -51,7 +36,7 @@ public class WebConfig extends WebMvcConfigurerAdapter { } @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { + public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler("**/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } @@ -63,6 +48,11 @@ public class WebConfig extends WebMvcConfigurerAdapter { converters.add(stringHttpMessageConverter()); } + @Override + public void configurePathMatch(final PathMatchConfigurer matcher) { + matcher.setUseRegisteredSuffixPatternMatch(true); + } + @Bean public HttpMessageConverter<?> mappingJackson2HttpMessageConverter() { return new MappingJackson2HttpMessageConverter(_objectMapperBuilder.build()); @@ -83,11 +73,6 @@ public class WebConfig extends WebMvcConfigurerAdapter { return new StandardServletMultipartResolver(); } - @Override - public void configurePathMatch(final PathMatchConfigurer matcher) { - matcher.setUseRegisteredSuffixPatternMatch(true); - } - @Bean public ViewResolver viewResolver() { return new InternalResourceViewResolver() {{ @@ -104,30 +89,9 @@ public class WebConfig extends WebMvcConfigurerAdapter { }}; } - @Bean - public Docket api(final XnatAppInfo info, final MessageSource messageSource) { - _log.debug("Initializing the Swagger Docket object"); - return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.withClassAnnotation(XapiRestController.class)).paths(PathSelectors.any()).build().apiInfo(apiInfo(info, messageSource)).pathMapping("/xapi"); - } - - private ApiInfo apiInfo(final XnatAppInfo info, final MessageSource messageSource) { - return new ApiInfo(getMessage(messageSource, "apiInfo.title"), - getMessage(messageSource, "apiInfo.description"), - info.getVersion(), - getMessage(messageSource, "apiInfo.termsOfServiceUrl"), - new Contact(getMessage(messageSource, "apiInfo.contactName"), - getMessage(messageSource, "apiInfo.contactUrl"), - getMessage(messageSource, "apiInfo.contactEmail")), - getMessage(messageSource, "apiInfo.license"), - getMessage(messageSource, "apiInfo.licenseUrl")); - } - - private String getMessage(final MessageSource messageSource, final String messageId) { - return messageSource.getMessage(messageId, null, Locale.getDefault()); - } - - private static final Logger _log = LoggerFactory.getLogger(WebConfig.class); - private static final Map<String, Object> MARSHALLER_PROPERTIES = new HashMap<String, Object>() {{ put(Marshaller.JAXB_FORMATTED_OUTPUT, true); }}; + private static final Map<String, Object> MARSHALLER_PROPERTIES = new HashMap<String, Object>() {{ + put(Marshaller.JAXB_FORMATTED_OUTPUT, true); + }}; private Jackson2ObjectMapperBuilder _objectMapperBuilder; @@ -135,5 +99,4 @@ public class WebConfig extends WebMvcConfigurerAdapter { setClassesToBeBound(SiteConfigPreferences.class); setMarshallerProperties(MARSHALLER_PROPERTIES); }}; - } diff --git a/src/main/java/org/nrg/xnat/initialization/ControllerConfig.java b/src/main/java/org/nrg/xnat/initialization/ControllerConfig.java new file mode 100644 index 00000000..1db54a0a --- /dev/null +++ b/src/main/java/org/nrg/xnat/initialization/ControllerConfig.java @@ -0,0 +1,10 @@ +package org.nrg.xnat.initialization; + +import org.nrg.xapi.configuration.RestApiConfig; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import(RestApiConfig.class) +public class ControllerConfig { +} diff --git a/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java b/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java index b073f426..c302c4e1 100644 --- a/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java +++ b/src/main/java/org/nrg/xnat/initialization/XnatWebAppInitializer.java @@ -5,8 +5,8 @@ import org.apache.axis.transport.http.AxisHTTPSessionListener; import org.apache.axis.transport.http.AxisServlet; import org.apache.commons.lang3.StringUtils; import org.apache.turbine.Turbine; -import org.nrg.framework.exceptions.NrgServiceRuntimeException; import org.nrg.framework.beans.XnatPluginBean; +import org.nrg.framework.exceptions.NrgServiceRuntimeException; import org.nrg.xdat.servlet.XDATAjaxServlet; import org.nrg.xdat.servlet.XDATServlet; import org.nrg.xnat.restlet.servlet.XNATRestletServlet; @@ -69,14 +69,16 @@ public class XnatWebAppInitializer extends AbstractAnnotationConfigDispatcherSer @Override protected Class<?>[] getRootConfigClasses() { - return new Class<?>[] { RootConfig.class }; + final List<Class<?>> configClasses = new ArrayList<>(); + configClasses.add(RootConfig.class); + configClasses.addAll(getPluginConfigs()); + configClasses.add(ControllerConfig.class); + return configClasses.toArray(new Class[configClasses.size()]); } @Override protected Class<?>[] getServletConfigClasses() { - final List<Class<?>> configClasses = new ArrayList<>(); - configClasses.addAll(getPluginConfigs()); - return configClasses.toArray(new Class[configClasses.size()]); + return EMPTY_ARRAY; } @Override @@ -175,5 +177,7 @@ public class XnatWebAppInitializer extends AbstractAnnotationConfigDispatcherSer } private static final Logger _log = LoggerFactory.getLogger(XnatWebAppInitializer.class); + private static final Class<?>[] EMPTY_ARRAY = new Class<?>[0]; + private ServletContext _context; } diff --git a/src/main/java/org/nrg/xnat/spawner/controllers/ManageElementsController.java b/src/main/java/org/nrg/xnat/spawner/controllers/ManageElementsController.java deleted file mode 100644 index 6df849ad..00000000 --- a/src/main/java/org/nrg/xnat/spawner/controllers/ManageElementsController.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.nrg.xnat.spawner.controllers; - -import org.apache.commons.lang3.StringUtils; -import org.nrg.xnat.spawner.entities.SpawnerElement; -import org.nrg.xnat.spawner.exceptions.InvalidElementIdException; -import org.nrg.xnat.spawner.services.SpawnerService; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.ModelAndView; - -import javax.inject.Inject; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@SuppressWarnings("SpringMVCViewInspection") -@Controller -@RequestMapping(value = "/spawner/manage", produces = "application/json") -public class ManageElementsController { - - @RequestMapping - public ModelAndView getNamespaces() { - final List<String> namespaces = _service.getNamespaces(); - return new ModelAndView("spawner/elements", "namespaces", namespaces); - } - - @RequestMapping("elements") - public ModelAndView getDefaultElements() { - return getNamespacedElements(SpawnerElement.DEFAULT_NAMESPACE); - } - - @RequestMapping("elements/{namespace}") - public ModelAndView getNamespacedElements(@PathVariable("namespace") final String namespace) { - final Map<String, Object> models = new HashMap<>(); - models.put("namespace", namespace); - models.put("namespaces", _service.getNamespaces()); - models.put("elements", _service.getDefaultElements()); - return new ModelAndView("spawner/elements", models); - } - - @RequestMapping(value = "element/{elementId}", method = RequestMethod.GET) - public ModelAndView getDefaultElement(@PathVariable final String elementId) { - return getNamespacedElement(SpawnerElement.DEFAULT_NAMESPACE, elementId); - } - - @RequestMapping(value = "element/{namespace}/{elementId}", method = RequestMethod.GET) - public ModelAndView getNamespacedElement(@PathVariable("namespace") final String namespace, @PathVariable final String elementId) { - final SpawnerElement element = _service.retrieve(namespace, elementId); - return new ModelAndView("spawner/element", element == null ? "error" : "elementId", element == null ? "The ID element " + elementId + " was not found in the system." : elementId); - } - - @RequestMapping(value = "element/{elementId}", method = RequestMethod.PUT) - public ModelAndView setDefaultElement(@PathVariable final String elementId, @RequestBody final SpawnerElement element) { - return setNamespacedElement(SpawnerElement.DEFAULT_NAMESPACE, elementId, element); - } - - @RequestMapping(value = "element/{namespace}/{elementId}", method = RequestMethod.PUT) - public ModelAndView setNamespacedElement(@PathVariable final String namespace, @PathVariable final String elementId, @RequestBody final SpawnerElement element) { - if (element == null) { - return new ModelAndView("spawner/element", "error", "No valid spawner element was found in your submitted data."); - } - final SpawnerElement existing = _service.retrieve(elementId); - final boolean isModElementId = !StringUtils.equals(existing.getElementId(), element.getElementId()); - final boolean isModYaml = !StringUtils.equals(existing.getYaml(), element.getYaml()); - if (isModElementId || isModYaml) { - if (isModElementId) { - try { - existing.setElementId(element.getElementId()); - } catch (InvalidElementIdException e) { - return new ModelAndView("spawner/element", "error", "The element ID " + element.getElementId() + " in your submitted data is invalid. Check for duplicated or invalid values."); - } - } - if (isModYaml) { - existing.setYaml(element.getYaml()); - } - _service.update(existing); - return new ModelAndView("spawner/element", "element", element); - } - return new ModelAndView("spawner/element", "error", "The submitted spawner element wasn't modified, not updated."); - } - - @Inject - private SpawnerService _service; -} -- GitLab