package at.gv.egiz.eaaf.core.impl.gui.utils; import java.util.Locale; import org.springframework.http.HttpStatusCode; import org.springframework.util.Assert; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfiguration; import jakarta.annotation.Nullable; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; /** * Utils GUI generation by using Spring MVC. * @author tlenz * */ @Slf4j public class MvcGuiRenderUtils { private static final String REQ_ATTR_MODEL_HOLDER = "GUI_MODEL_ASYNCH_RENDERING"; /** * Build Spring MVC model from GUI configuration. * * @param config GUI configuration * @return MVC model */ @NonNull public static ModelAndView prepareSpringGuiModel(@NonNull final IGuiBuilderConfiguration config) { final ModelAndView mav = new ModelAndView(config.getViewName()); if (config.getViewParameters() != null) { mav.addAllObjects(config.getViewParameters()); } return mav; } /** * Inject GUI-builder configuration into http request to render it asynchronous in Spring intercepter. * * @param request Current HttpServletRequest * @param config Configuration for GUI rendering */ public static void setMvcForAsynchRendering(@NonNull final HttpServletRequest request, @NonNull final IGuiBuilderConfiguration config) { request.setAttribute(REQ_ATTR_MODEL_HOLDER, prepareSpringGuiModel(config)); } /** * Get GUI model for asynchronous rendering. * * @param request Current HttpServletRequest * @return GUI model, or null of model was set */ @Nullable public static ModelAndView getMvcForAsynchRendering(@NonNull final HttpServletRequest request) { return (ModelAndView) request.getAttribute(REQ_ATTR_MODEL_HOLDER); } /** * Analyze GUI template and HTTP request to evaluate ContentType of HTTP response generated by this builder. * * @param httpReq Current HttpServletRequest * @param config GUI configuration * @param viewResolvers Spring based view-resolvers * @param localeResolver Spring based locale-resolvers * @return Response content-type, or null if evaluation has no result * @throws Exception In case of a GUI rendering error */ @Nullable public static String evaluateResponseContentType(HttpServletRequest httpReq, IGuiBuilderConfiguration config, @Nullable final ViewResolver[] viewResolvers, @Nullable final LocaleResolver localeResolver) throws Exception { return buildViewFromModel(viewResolvers, getLocaleByRequest(localeResolver, httpReq), prepareSpringGuiModel(config)).getContentType(); } /** * Get i18n information from HTTP request. * * @param localeResolver Resolver for localization * @param request HTTP request * @return Current locale */ public static Locale getLocaleByRequest(LocaleResolver localeResolver, @NonNull HttpServletRequest request) { return localeResolver != null ? localeResolver.resolveLocale(request) : request.getLocale(); } /** * Render a GUI by using Spring based ModelAndView (MVC) components. * * @param mv GUI model * @param request Current HttpServletRequest * @param response Current HttpServletResponse * @param viewResolvers Spring based view-resolvers * @param localeResolver Spring based locale-resolvers * @throws Exception In case of a GUI rendering error */ public static void render(@NonNull final ModelAndView mv, @NonNull final HttpServletRequest request, @NonNull final HttpServletResponse response, @Nullable final ViewResolver[] viewResolvers, @Nullable final LocaleResolver localeResolver) throws Exception { Assert.notNull(request, "http request object"); Assert.notNull(response, "http response object"); Assert.notNull(mv, "Model and view"); // Determine locale for request and apply it to the response. final Locale locale = getLocaleByRequest(localeResolver, request); response.setLocale(locale); // Determine view for response View view = buildViewFromModel(viewResolvers, locale, mv); final HttpStatusCode modelStatus = mv.getStatus(); if (modelStatus != null) { response.setStatus(modelStatus.value()); } else { log.trace("HttpStatus is 'null' Set {} as default", HttpServletResponse.SC_OK); response.setStatus(HttpServletResponse.SC_OK); } // Delegate to the View object for rendering. if (log.isTraceEnabled()) { log.trace("Rendering view [{}] ", view); } try { view.render(mv.getModelMap(), request, response); } catch (final Exception ex) { if (log.isDebugEnabled()) { log.debug("Error rendering view [{}]", view, null, ex); } throw ex; } } private static View buildViewFromModel(ViewResolver[] viewResolvers, Locale locale, @NonNull ModelAndView mv) throws Exception { final String viewName = mv.getViewName(); View view; if (viewName != null) { // We need to resolve the view name. view = resolveViewName(viewResolvers, viewName, locale); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' "); } } else { // No need to lookup: the ModelAndView object contains the actual View object. view = mv.getView(); if (view == null) { throw new ServletException( "ModelAndView [" + mv + "] neither contains a view name nor a " + "View object "); } } return view; } private static View resolveViewName(@Nullable final ViewResolver[] viewResolvers, final String viewName, final Locale locale) throws Exception { if (viewResolvers != null) { for (final ViewResolver viewResolver : viewResolvers) { final View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null; } private MvcGuiRenderUtils() { // private constructor for class with only static methods } }