Demonstration of multiple dynamically instantiated beans
authorKai Moritz <kai@juplo.de>
Fri, 20 Nov 2020 20:40:28 +0000 (21:40 +0100)
committerKai Moritz <kai@juplo.de>
Sat, 21 Nov 2020 14:24:09 +0000 (15:24 +0100)
14 files changed:
pom.xml
src/main/java/de/juplo/demos/multiplebeans/HomeController.java [new file with mode: 0644]
src/main/java/de/juplo/demos/multiplebeans/MultipleBeansApplication.java
src/main/java/de/juplo/demos/multiplebeans/MultipleBeansApplicationContextInitializer.java [new file with mode: 0644]
src/main/java/de/juplo/demos/multiplebeans/MultipleBeansEnvironmentPostProcessor.java [new file with mode: 0644]
src/main/java/de/juplo/demos/multiplebeans/SiteController.java [new file with mode: 0644]
src/main/resources/META-INF/spring.factories [new file with mode: 0644]
src/main/resources/application.properties [deleted file]
src/main/resources/application.yml [new file with mode: 0644]
src/main/resources/templates/400.html [new file with mode: 0644]
src/main/resources/templates/error.html [new file with mode: 0644]
src/main/resources/templates/home.html [new file with mode: 0644]
src/main/resources/templates/layout.html [new file with mode: 0644]
src/main/resources/templates/site.html [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index c5a9621..0c9e4dc 100644 (file)
--- a/pom.xml
+++ b/pom.xml
                        <artifactId>lombok</artifactId>
                        <optional>true</optional>
                </dependency>
+               <dependency>
+                       <groupId>org.webjars</groupId>
+                       <artifactId>bootstrap</artifactId>
+                       <version>4.5.2</version>
+                       <scope>runtime</scope>
+               </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
diff --git a/src/main/java/de/juplo/demos/multiplebeans/HomeController.java b/src/main/java/de/juplo/demos/multiplebeans/HomeController.java
new file mode 100644 (file)
index 0000000..5a488cf
--- /dev/null
@@ -0,0 +1,23 @@
+package de.juplo.demos.multiplebeans;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.Controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequiredArgsConstructor
+public class HomeController implements Controller {
+
+    private final String[] sites;
+
+
+    @Override
+    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+        ModelAndView mav = new ModelAndView("home");
+        mav.addObject("sites", sites);
+        return mav;
+    }
+}
index 618ba8e..16f82eb 100644 (file)
@@ -1,13 +1,21 @@
 package de.juplo.demos.multiplebeans;
 
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
 
 @SpringBootApplication
 public class MultipleBeansApplication {
 
+       @Bean("/")
+       public HomeController homeController(@Value("${juplo.sites}")String[] sites) {
+
+               return new HomeController(sites);
+       }
+
+
        public static void main(String[] args) {
                SpringApplication.run(MultipleBeansApplication.class, args);
        }
-
 }
diff --git a/src/main/java/de/juplo/demos/multiplebeans/MultipleBeansApplicationContextInitializer.java b/src/main/java/de/juplo/demos/multiplebeans/MultipleBeansApplicationContextInitializer.java
new file mode 100644 (file)
index 0000000..28f22df
--- /dev/null
@@ -0,0 +1,24 @@
+package de.juplo.demos.multiplebeans;
+
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+
+@AllArgsConstructor
+public class MultipleBeansApplicationContextInitializer
+        implements
+        ApplicationContextInitializer<ConfigurableApplicationContext> {
+
+    private final String[] sites;
+
+
+    @Override
+    public void initialize(ConfigurableApplicationContext context) {
+        ConfigurableListableBeanFactory factory = context.getBeanFactory();
+        for (String site : sites) {
+            SiteController controller = new SiteController(site, "Descrition of site " + site);
+            factory.registerSingleton("/" + site, controller);
+        }
+    }
+}
diff --git a/src/main/java/de/juplo/demos/multiplebeans/MultipleBeansEnvironmentPostProcessor.java b/src/main/java/de/juplo/demos/multiplebeans/MultipleBeansEnvironmentPostProcessor.java
new file mode 100644 (file)
index 0000000..0e31555
--- /dev/null
@@ -0,0 +1,29 @@
+package de.juplo.demos.multiplebeans;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.env.EnvironmentPostProcessor;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.PropertySource;
+
+import java.util.Arrays;
+
+public class MultipleBeansEnvironmentPostProcessor implements EnvironmentPostProcessor {
+
+    @Override
+    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
+        String sites = null;
+        for (PropertySource source : environment.getPropertySources()) {
+            Object found = source.getProperty("juplo.sites");
+            if (found != null)
+                sites = found.toString();
+        }
+
+        if (sites == null)
+            throw new IllegalArgumentException("Parameter juplo.sites is not set!");
+
+        application.addInitializers(new MultipleBeansApplicationContextInitializer(
+                Arrays.stream(sites.split(","))
+                .map(site -> site.trim())
+                .toArray(size -> new String[size])));
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/de/juplo/demos/multiplebeans/SiteController.java b/src/main/java/de/juplo/demos/multiplebeans/SiteController.java
new file mode 100644 (file)
index 0000000..99e1ece
--- /dev/null
@@ -0,0 +1,26 @@
+package de.juplo.demos.multiplebeans;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.Controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequiredArgsConstructor
+public class SiteController implements Controller {
+
+    private final String name;
+    private final String description;
+
+
+    public ModelAndView handleRequest(
+            HttpServletRequest request,
+            HttpServletResponse response) throws Exception {
+
+        ModelAndView mav = new ModelAndView("site");
+        mav.addObject("name", name);
+        mav.addObject("description", description);
+        return mav;
+    }
+}
diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories
new file mode 100644 (file)
index 0000000..cb8c09a
--- /dev/null
@@ -0,0 +1,2 @@
+org.springframework.boot.env.EnvironmentPostProcessor=\
+  de.juplo.demos.multiplebeans.MultipleBeansEnvironmentPostProcessor
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644 (file)
index 0000000..01ec1b0
--- /dev/null
@@ -0,0 +1,2 @@
+juplo:
+  sites: peter, ute, franz
diff --git a/src/main/resources/templates/400.html b/src/main/resources/templates/400.html
new file mode 100644 (file)
index 0000000..36ba0c0
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layout :: pagelayout(~{:: title}, ~{:: h1}, ~{:: div.card-text})}">
+  <head>
+    <title th:text="'400: ' + ${exception.getClass().getSimpleName()}">Testing Exception-Handling - Template for 400</title>
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+  <body>
+    <h1 th:text="'400: ' + ${exception.getClass().getSimpleName()}">Template for 400</h1>
+    <div class="card-text table">
+      <p><strong th:text="'Catched exception: ' + ${exception}">EXCEPTION</strong></p>
+      <p><a href="#" th:href="@{/}" class="btn btn-primary">Back to HOME</a></p>
+    </div>
+  </body>
+</html>
diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html
new file mode 100644 (file)
index 0000000..a5e8149
--- /dev/null
@@ -0,0 +1,51 @@
+<!doctype html>
+<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layout :: pagelayout(~{:: title}, ~{:: h1}, ~{:: table.card-text})}">
+  <head>
+    <title th:text="|${status}: ${error}">XXX: ERROR</title>
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+  <body>
+    <h1 th:text="|${status}: ${error}">XXX: ERROR</h1>
+      <table class="card-text table">
+        <tbody>
+          <tr class="border-top-0">
+            <th scope="row">Status</th>
+            <td th:text="${status}">ERROR.STATUS</td>
+          </tr>
+          <tr>
+            <th scope="row">Error</th>
+            <td th:text="${error}">ERROR.ERROR</td>
+          </tr>
+          <tr th:if="!${#strings.isEmpty(message)}">
+            <th scope="row">Message</th>
+            <td th:text="${message}">ERROR.MESSAGE</td>
+          </tr>
+          <tr th:if="!${#strings.isEmpty(requestId)}">
+            <th class="text-nowrap" scope="row">Request-ID</th>
+            <td th:text="${requestId}">ERROR.REQUEST_ID</td>
+          </tr>
+          <tr>
+            <th scope="row">Timstamp</th>
+            <td th:text="${timestamp}">ERROR.TIMESTAMP</td>
+          </tr>
+          <tr th:if="!${#strings.isEmpty(path)}">
+            <th scope="row">Path</th>
+            <td th:text="${path}">ERROR.PATH</td>
+          </tr>
+          <!--/**-->
+          <tr th:if="!${#strings.isEmpty(trace)}">
+            <th scope="row">Trace</th>
+            <td>
+              <div class="overflow-hidden">
+                <pre class="overflow-hidden" th:text="${trace}">ERROR.TRACE</pre>
+              </div>
+            </td>
+          </tr>
+          <!--**/-->
+        </tbody>
+      </table>
+      <p><a href="#" th:href="@{/}" class="btn btn-primary">Back to HOME</a>
+    </div>
+  </body>
+</html>
diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html
new file mode 100644 (file)
index 0000000..cb75a1d
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layout :: pagelayout(~{:: title}, ~{:: h1}, ~{:: div.card-text})}">
+  <head>
+    <title>Home</title>
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+  <body>
+    <h1>Home</h1>
+    <div class="card-text">
+      <ul>
+        <li th:each="site : ${sites}">
+          <a href="#" th:href="'/' + ${site}" th:text="${site}">SITE</a>
+        </li>
+      </ul>
+    </div>
+    <div class="card-text">
+      <p>
+        <em>
+          This page links to the pages, that are served by the dynamically created beans.
+        </em>
+      </p>
+    </div>
+  </body>
+</html>
diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html
new file mode 100644 (file)
index 0000000..3ad68ed
--- /dev/null
@@ -0,0 +1,20 @@
+<!doctype html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" th:fragment="pagelayout(title,header,body)">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <title th:replace="${title}">TITLE</title>
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.5.2/css/bootstrap.min.css}">
+  </head>
+  <body>
+    <nav class="navbar navbar-dark bg-primary navbar-expand navbar-dark flex-column">
+      <h2 class="navbar-brand">Demo: Creating Multiple Beans In Spring-Boot Pogrammatically</h2>
+    </nav>
+    <main class="container mt-5">
+      <div id="content" class="card">
+        <div class="card-header"><h1 th:replace="${header}">HEADER</h1></div>
+        <div class="card-body"><div th:replace="${body}">BODY</div></div>
+      </div>
+    </main>
+  </body>
+</html>
diff --git a/src/main/resources/templates/site.html b/src/main/resources/templates/site.html
new file mode 100644 (file)
index 0000000..6ea90f5
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layout :: pagelayout(~{:: title}, ~{:: h1}, ~{:: div.card-text})}">
+  <head>
+    <title th:text="${name}">SITE-TEMPLATE</title>
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+  <body>
+    <h1 th:text="${name}">SITE</h1>
+    <div class="card-text">
+      <p th:text="${description}">DESCRIPTION</p>
+    </div>
+    <div class="card-text">
+      <p>
+        <em>
+          Each site is defined in the configuration with name and description inside an array.
+          This demo shows, how to create multiple beans of the same type based on the configuration.
+        </em>
+      </p>
+    </div>
+    <div class="card-text">Back <a href="#" th:href="@{/}">Home</a></div>
+  </body>
+</html>