From e3639ef805f1e525415c43cbda80ed71cc43a70c Mon Sep 17 00:00:00 2001
From: Thomas <>
Date: Sun, 9 Jan 2022 15:01:28 +0100
Subject: feature(core): add synch. and asynch. GUI builder implementation that
 use Spring MVC architecture

---
 .../impl/gui/builder/AsynchGuiFormBuilderImpl.java |  72 ++++++++
 .../gui/builder/SpringMvcGuiFormBuilderImpl.java   |  57 ++++++
 .../AsynchSpringMvcGuiBuilderIntercepter.java      |  63 +++++++
 .../core/impl/gui/utils/MvcGuiRenderUtils.java     | 199 +++++++++++++++++++++
 4 files changed, 391 insertions(+)
 create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/AsynchGuiFormBuilderImpl.java
 create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/SpringMvcGuiFormBuilderImpl.java
 create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/interceptor/AsynchSpringMvcGuiBuilderIntercepter.java
 create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/utils/MvcGuiRenderUtils.java

(limited to 'eaaf_core/src/main/java')

diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/AsynchGuiFormBuilderImpl.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/AsynchGuiFormBuilderImpl.java
new file mode 100644
index 00000000..6ed50955
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/AsynchGuiFormBuilderImpl.java
@@ -0,0 +1,72 @@
+package at.gv.egiz.eaaf.core.impl.gui.builder;
+
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.ViewResolver;
+
+import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder;
+import at.gv.egiz.eaaf.core.exceptions.GuiBuildException;
+import at.gv.egiz.eaaf.core.impl.gui.utils.MvcGuiRenderUtils;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class AsynchGuiFormBuilderImpl implements ISpringMvcGuiFormBuilder {
+
+  private @Autowired(required = false) ViewResolver[] viewResolvers;
+  private @Autowired(required = false) LocaleResolver localeResolver;
+  
+  @Override
+  public void build(final HttpServletRequest httpReq, final HttpServletResponse httpResp,
+      final IGuiBuilderConfiguration config, final String loggerName) throws GuiBuildException {
+    build(httpReq, httpResp, config, null, loggerName);
+
+  }
+
+  @Override
+  public void build(final HttpServletRequest httpReq, final HttpServletResponse httpResp,
+      final IGuiBuilderConfiguration config, final String contentType, final String loggerName)
+      throws GuiBuildException {
+    if (config.isWriteAsynch()) {    
+      log.debug("{} injects GUI model: {} for asynch. rendering", loggerName, config.getViewName());
+      MvcGuiRenderUtils.setMvcForAsynchRendering(httpReq, config);
+      
+    } else {
+      renderGuiDirectly(httpReq, httpResp, config, loggerName);
+      
+    }
+
+  }
+
+  @Override
+  public String evaluateResponseContentType(HttpServletRequest httpReq, IGuiBuilderConfiguration config, 
+      String loggerName) throws GuiBuildException {
+    try {
+      return MvcGuiRenderUtils.evaluateResponseContentType(httpReq, config, viewResolvers, localeResolver);
+      
+    } catch (final Exception e) {
+      log.info("Can NOT evaluate contentType for response GUI: {}", loggerName, e);
+      throw new GuiBuildException(e.getMessage(), e);
+
+    }          
+  }
+  
+  private void renderGuiDirectly(final HttpServletRequest httpReq, final HttpServletResponse httpResp,
+      final IGuiBuilderConfiguration config, final String loggerName) throws GuiBuildException {
+    try {
+      MvcGuiRenderUtils.render(MvcGuiRenderUtils.prepareSpringGuiModel(config), 
+          httpReq, httpResp, viewResolvers, localeResolver);
+
+    } catch (final Exception e) {
+      log.info("Can NOT generate GUI for: {}", loggerName, e);
+      throw new GuiBuildException(e.getMessage(), e);
+
+    }
+    
+  }
+
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/SpringMvcGuiFormBuilderImpl.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/SpringMvcGuiFormBuilderImpl.java
new file mode 100644
index 00000000..9fae07e2
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/builder/SpringMvcGuiFormBuilderImpl.java
@@ -0,0 +1,57 @@
+package at.gv.egiz.eaaf.core.impl.gui.builder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.ViewResolver;
+
+import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder;
+import at.gv.egiz.eaaf.core.exceptions.GuiBuildException;
+import at.gv.egiz.eaaf.core.impl.gui.utils.MvcGuiRenderUtils;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class SpringMvcGuiFormBuilderImpl implements ISpringMvcGuiFormBuilder {
+
+  private @Autowired(required = false) ViewResolver[] viewResolvers;
+  private @Autowired(required = false) LocaleResolver localeResolver;
+
+  @Override
+  public void build(final HttpServletRequest httpReq, final HttpServletResponse httpResp,
+      final IGuiBuilderConfiguration config, final String loggerName) throws GuiBuildException {
+    build(httpReq, httpResp, config, null, loggerName);
+
+  }
+
+  @Override
+  public void build(final HttpServletRequest httpReq, final HttpServletResponse httpResp,
+      final IGuiBuilderConfiguration config, final String contentType, final String loggerName)
+      throws GuiBuildException {
+    try {
+      MvcGuiRenderUtils.render(MvcGuiRenderUtils.prepareSpringGuiModel(config), 
+          httpReq, httpResp, viewResolvers, localeResolver);
+
+    } catch (final Exception e) {
+      log.info("Can NOT generate GUI for: {}", loggerName, e);
+      throw new GuiBuildException(e.getMessage(), e);
+
+    }
+  }
+  
+  @Override
+  public String evaluateResponseContentType(HttpServletRequest httpReq, IGuiBuilderConfiguration config, 
+      String loggerName) throws GuiBuildException {
+    try {
+      return MvcGuiRenderUtils.evaluateResponseContentType(httpReq, config, viewResolvers, localeResolver);
+      
+    } catch (final Exception e) {
+      log.info("Can NOT evaluate contentType for response GUI: {}", loggerName, e);
+      throw new GuiBuildException(e.getMessage(), e);
+
+    }          
+  }
+
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/interceptor/AsynchSpringMvcGuiBuilderIntercepter.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/interceptor/AsynchSpringMvcGuiBuilderIntercepter.java
new file mode 100644
index 00000000..708e3b84
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/interceptor/AsynchSpringMvcGuiBuilderIntercepter.java
@@ -0,0 +1,63 @@
+package at.gv.egiz.eaaf.core.impl.gui.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.ViewResolver;
+
+import at.gv.egiz.eaaf.core.exceptions.GuiBuildException;
+import at.gv.egiz.eaaf.core.impl.gui.utils.MvcGuiRenderUtils;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Intercepter to render GUI in response processing.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class AsynchSpringMvcGuiBuilderIntercepter implements HandlerInterceptor {
+
+  private @Autowired(required = false) ViewResolver[] viewResolvers;
+  private @Autowired(required = false) LocaleResolver localeResolver;
+
+  /*
+   * (non-Javadoc)
+   *
+   * @see
+   * org.springframework.web.servlet.HandlerInterceptor#postHandle(javax.servlet.
+   * http. HttpServletRequest, javax.servlet.http.HttpServletResponse,
+   * java.lang.Object, org.springframework.web.servlet.ModelAndView)
+   */
+  @Override
+  public void postHandle(final HttpServletRequest httpReq, final HttpServletResponse httpResp,
+      final Object handler, final ModelAndView modelAndView) throws Exception {
+
+    final ModelAndView model = MvcGuiRenderUtils.getMvcForAsynchRendering(httpReq);
+    if (model != null) {
+      log.debug("Find GUI model: {} Starting model rendering ... ", model.getViewName());
+      renderGui(model, httpReq, httpResp);
+
+    } else {
+      log.trace("No GUI model skipping asynch model rendering");
+
+    }
+
+  }
+
+  private void renderGui(final ModelAndView mav,
+      final HttpServletRequest httpReq, final HttpServletResponse httpResp) throws GuiBuildException {
+    try {
+      MvcGuiRenderUtils.render(mav, httpReq, httpResp, viewResolvers, localeResolver);
+
+    } catch (final Exception e) {
+      log.info("Can NOT generate GUI for illustration", e);
+      throw new GuiBuildException(e.getMessage(), e);
+
+    }
+  }
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/utils/MvcGuiRenderUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/utils/MvcGuiRenderUtils.java
new file mode 100644
index 00000000..5a3f14cb
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/gui/utils/MvcGuiRenderUtils.java
@@ -0,0 +1,199 @@
+package at.gv.egiz.eaaf.core.impl.gui.utils;
+
+import java.util.Locale;
+
+import javax.annotation.Nullable;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.http.HttpStatus;
+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 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 <code>null</code> 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 <code>null</code> 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();
+    
+  }
+  
+  /**
+   * 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 HttpStatus 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 Locale getLocaleByRequest(LocaleResolver localeResolver, @NonNull HttpServletRequest request) {
+    return localeResolver != null ? localeResolver.resolveLocale(request) : request.getLocale();
+    
+  }
+  
+  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
+  }
+  
+}
-- 
cgit v1.2.3