package sagan; import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.ErrorPage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Scope; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter; import org.springframework.web.servlet.resource.VersionResourceResolver; import org.springframework.web.util.UrlPathHelper; import sagan.projects.support.ProjectMetadataService; import sagan.support.ResourceNotFoundException; import sagan.support.StaticPagePathFinder; import sagan.support.nav.Navigation; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Properties; import static org.springframework.http.HttpStatus.NOT_FOUND; /** * Site-wide MVC infrastructure configuration. See also {@link SiteApplication} where certain * additional web infrastructure is configured. */ abstract class MvcConfig extends WebMvcConfigurerAdapter { @Autowired private StaticPagePathFinder staticPagePathFinder; @Bean(name = {"uih", "viewRenderingHelper"}) @Scope("request") public ViewRenderingHelper viewRenderingHelper() { return new ViewRenderingHelper(); } public abstract AppVersionHelper appVersionHelper(); @ExceptionHandler @ResponseStatus(NOT_FOUND) public void handleException(ResourceNotFoundException ex) { } @Override public void addViewControllers(ViewControllerRegistry registry) { try { for (StaticPagePathFinder.PagePaths paths : staticPagePathFinder.findPaths()) { String urlPath = paths.getUrlPath(); registry.addViewController(urlPath).setViewName("pages" + paths.getFilePath()); if (!urlPath.isEmpty()) { registry.addViewController(urlPath + "/").setViewName("pages" + paths.getFilePath()); } } } catch (IOException e) { throw new RuntimeException("Unable to locate static pages: " + e.getMessage(), e); } } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptorAdapter() { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Navigation navSection = handlerMethod.getBean().getClass().getAnnotation(Navigation.class); if (navSection != null && modelAndView != null) { modelAndView.addObject("navSection", navSection.value().toString().toLowerCase()); } } } }); } @Configuration public static class ErrorConfig implements EmbeddedServletContainerCustomizer { @Override public void customize(ConfigurableEmbeddedServletContainer factory) { factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404")); factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500")); } } static class ViewRenderingHelper { private final UrlPathHelper urlPathHelper = new UrlPathHelper(); private HttpServletRequest request; @Autowired public void setRequest(HttpServletRequest request) { this.request = request; } public String navClass(String active, String current) { if (active.equals(current)) { return "navbar-link active"; } else { return "navbar-link"; } } public String blogClass(String active, String current) { if (active.equals(current)) { return "blog-category active"; } else { return "blog-category"; } } public String path() { return urlPathHelper.getPathWithinApplication(request); } } static class AppVersionHelper { private final String version; public AppVersionHelper(String version) { this.version = version; } public String version() { return version; } } } @Configuration @ControllerAdvice @Profile("cloudfoundry") class CloudFoundryMvcConfig extends MvcConfig { @Value("${spring.git.properties:classpath:git.properties}") private Resource gitProperties; public String getGitCommitId() { try { if (this.gitProperties.exists()) { Properties properties = PropertiesLoaderUtils.loadProperties(this.gitProperties); return properties.getProperty("git.commit.id.abbrev"); } } catch (IOException exc) { } throw new IllegalStateException("Missing git.properties file on classpath"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { VersionResourceResolver versionResourceResolver = new VersionResourceResolver() .addFixedVersionStrategy(getGitCommitId(), "/app/**") .addContentVersionStrategy("/**"); registry.addResourceHandler("/**") .addResourceLocations("classpath:/static/") .resourceChain(true) .addResolver(versionResourceResolver); } @Bean public ResourceUrlEncodingFilter resourceUrlEncodingFilter() { return new ResourceUrlEncodingFilter(); } @Bean(name = "saganApp") @Override public AppVersionHelper appVersionHelper() { return new AppVersionHelper(getGitCommitId()); } } @Configuration @ControllerAdvice @Profile("standalone") class StandaloneMvcConfig extends MvcConfig { @Bean(name = "saganApp") @Override public AppVersionHelper appVersionHelper() { return new AppVersionHelper(""); } }