diff --git a/CHANGELOG.md b/CHANGELOG.md
index e14582048..bc3f5044c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,61 @@ see wiki for more information: [wiki](https://github.com/thmarx/cms/wiki)
* **MAINTENANCE** multiple dependencies updated
* **MAINTENANCE** maven wrapper added to project
+### Developer experience
+
+In this release we introduced some features to make life of developers easier.
+
+#### Registering hooks via annotations
+
+It is now possible to pass an object with annotated hook definitions to the HookSystem.register method.
+
+```java
+@Filter("test/annotation/filter1")
+public List filter (FilterContext> context) {
+ context.value().remove("2");
+ return context.value();
+}
+@Action("test/annotation/action1")
+public void action1 (ActionContext> context) {
+ // do something
+}
+```
+
+#### HTTP-Controllers
+
+The RoutesExtensionPoint is an extension point for defining HTTP routes.
+It allows developers to provide a list of objects whose methods can be registered as routes using annotations.
+
+```java
+@Route("/test2")
+public boolean handle2 (Request request, Response response, Callback callback) {
+ return true;
+}
+```
+
+#### ShortCodes
+
+The RegisterShortCodesExtensionPoint interface now includes a new method, shortCodeDefinitions, which returns a list of objects that contain shortcode definitions provided through annotations.
+
+```java
+@ShortCode("printHello")
+public String printHello (Parameter parameter) {
+ return "hello " + parameter.getOrDefault("name", "");
+}
+```
+
+#### TemplateComponents
+
+A new method, componentDefinitions, has been added to the RegisterTemplateComponentExtensionPoint interface. It returns a list of objects that define template components using annotations.
+
+```java
+@TemplateComponent("tag3")
+public String tag3 (Parameter parameter) {
+ return "%s
".formatted(parameter.get("_content"));
+}
+
+```
+
## 7.8.0
* **BUG** Namespaces not set when executing content pipeline [#416](https://github.com/CondationCMS/cms-server/pull/416)
diff --git a/cms-api/src/main/java/com/condation/cms/api/annotations/Action.java b/cms-api/src/main/java/com/condation/cms/api/annotations/Action.java
new file mode 100644
index 000000000..5700ad687
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/annotations/Action.java
@@ -0,0 +1,39 @@
+package com.condation.cms.api.annotations;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author thorstenmarx
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Action {
+ String value ();
+ int priority () default 10;
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/annotations/Filter.java b/cms-api/src/main/java/com/condation/cms/api/annotations/Filter.java
new file mode 100644
index 000000000..f0afee6fe
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/annotations/Filter.java
@@ -0,0 +1,39 @@
+package com.condation.cms.api.annotations;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author thorstenmarx
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Filter {
+ String value ();
+ int priority () default 10;
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/annotations/Route.java b/cms-api/src/main/java/com/condation/cms/api/annotations/Route.java
new file mode 100644
index 000000000..8f82df0d3
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/annotations/Route.java
@@ -0,0 +1,35 @@
+package com.condation.cms.api.annotations;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Route {
+ String value();
+ String method() default "GET"; // Optional: GET, POST, PUT, DELETE ...
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/annotations/ShortCode.java b/cms-api/src/main/java/com/condation/cms/api/annotations/ShortCode.java
new file mode 100644
index 000000000..9cef1bf04
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/annotations/ShortCode.java
@@ -0,0 +1,38 @@
+package com.condation.cms.api.annotations;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author thorstenmarx
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface ShortCode {
+ String value ();
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/annotations/TemplateComponent.java b/cms-api/src/main/java/com/condation/cms/api/annotations/TemplateComponent.java
new file mode 100644
index 000000000..b7250e70a
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/annotations/TemplateComponent.java
@@ -0,0 +1,38 @@
+package com.condation.cms.api.annotations;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author thorstenmarx
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface TemplateComponent {
+ String value ();
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/HttpHandler.java b/cms-api/src/main/java/com/condation/cms/api/extensions/HttpHandler.java
index 7916688f1..9006a97bc 100644
--- a/cms-api/src/main/java/com/condation/cms/api/extensions/HttpHandler.java
+++ b/cms-api/src/main/java/com/condation/cms/api/extensions/HttpHandler.java
@@ -33,5 +33,13 @@
*/
public interface HttpHandler {
+ /**
+ *
+ * @param request
+ * @param response
+ * @param callback
+ * @return true if the request is handled by the HttpHandler, otherwise false
+ * @throws Exception
+ */
boolean handle (Request request, Response response, Callback callback) throws Exception;
}
diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterShortCodesExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterShortCodesExtensionPoint.java
index 1e02a5f53..a5adadf36 100644
--- a/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterShortCodesExtensionPoint.java
+++ b/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterShortCodesExtensionPoint.java
@@ -24,6 +24,8 @@
import com.condation.cms.api.model.Parameter;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -33,5 +35,11 @@
*/
public abstract class RegisterShortCodesExtensionPoint extends AbstractExtensionPoint {
- public abstract Map> shortCodes ();
+ public Map> shortCodes () {
+ return Collections.emptyMap();
+ }
+
+ public List shortCodeDefinitions () {
+ return Collections.emptyList();
+ }
}
diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterTemplateComponentExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterTemplateComponentExtensionPoint.java
index 21f099930..d980b6175 100644
--- a/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterTemplateComponentExtensionPoint.java
+++ b/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterTemplateComponentExtensionPoint.java
@@ -24,6 +24,8 @@
import com.condation.cms.api.model.Parameter;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -33,5 +35,11 @@
*/
public abstract class RegisterTemplateComponentExtensionPoint extends AbstractExtensionPoint {
- public abstract Map> components ();
+ public Map> components () {
+ return Collections.emptyMap();
+ }
+
+ public List componentDefinitions () {
+ return Collections.emptyList();
+ }
}
diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesExtensionPoint.java
new file mode 100644
index 000000000..496cd7da4
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesExtensionPoint.java
@@ -0,0 +1,34 @@
+package com.condation.cms.api.extensions.http.routes;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import com.condation.cms.api.extensions.AbstractExtensionPoint;
+import java.util.List;
+
+/**
+ *
+ * @author thorstenmarx
+ */
+public abstract class RoutesExtensionPoint extends AbstractExtensionPoint {
+ abstract public List getRouteDefinitions ();
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesManager.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesManager.java
new file mode 100644
index 000000000..d3ef35d61
--- /dev/null
+++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesManager.java
@@ -0,0 +1,82 @@
+package com.condation.cms.api.extensions.http.routes;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import com.condation.cms.api.annotations.Route;
+import com.condation.cms.api.extensions.http.HttpHandler;
+import com.condation.cms.api.extensions.http.PathMapping;
+import org.eclipse.jetty.http.pathmap.PathSpec;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
+
+@Slf4j
+public class RoutesManager {
+
+ private final PathMapping pathMapping = new PathMapping();
+
+ public Optional findFirst (String path, String method) {
+ return pathMapping.getMatchingHandler(path, method);
+ }
+
+ public void register(Object controller) {
+ Class> clazz = controller.getClass();
+
+ for (Method method : clazz.getDeclaredMethods()) {
+ Route route = method.getAnnotation(Route.class);
+ if (route != null && isValidHandlerMethod(method)) {
+ method.setAccessible(true);
+
+ PathSpec pathSpec = PathSpec.from(route.value());
+
+ HttpHandler handler = (request, response, callback) -> {
+ try {
+ return (Boolean) method.invoke(controller, request, response, callback);
+ } catch (Exception e) {
+ log.error("", e);
+ response.setStatus(500);
+ return true;
+ }
+ };
+ pathMapping.add(pathSpec, route.method(), handler);
+ }
+ }
+ }
+
+ private boolean isValidHandlerMethod(Method method) {
+ // Muss "boolean handle(Request, Response, Callback)" sein
+ if (!Modifier.isPublic(method.getModifiers())) return false;
+ if (!method.getReturnType().equals(boolean.class)) return false;
+
+ Class>[] params = method.getParameterTypes();
+ return params.length == 3 &&
+ Request.class.isAssignableFrom(params[0]) &&
+ Response.class.isAssignableFrom(params[1]) &&
+ Callback.class.isAssignableFrom(params[2]);
+ }
+}
diff --git a/cms-api/src/main/java/com/condation/cms/api/hooks/Action.java b/cms-api/src/main/java/com/condation/cms/api/hooks/ActionHook.java
similarity index 89%
rename from cms-api/src/main/java/com/condation/cms/api/hooks/Action.java
rename to cms-api/src/main/java/com/condation/cms/api/hooks/ActionHook.java
index e1e849a3d..281d421da 100644
--- a/cms-api/src/main/java/com/condation/cms/api/hooks/Action.java
+++ b/cms-api/src/main/java/com/condation/cms/api/hooks/ActionHook.java
@@ -27,5 +27,5 @@
*
* @author t.marx
*/
-public record Action(String name, int priority, ActionFunction function) implements Hook {
+record ActionHook(String name, int priority, ActionFunction function) implements Hook {
}
diff --git a/cms-api/src/main/java/com/condation/cms/api/hooks/Filter.java b/cms-api/src/main/java/com/condation/cms/api/hooks/FilterHook.java
similarity index 89%
rename from cms-api/src/main/java/com/condation/cms/api/hooks/Filter.java
rename to cms-api/src/main/java/com/condation/cms/api/hooks/FilterHook.java
index 1ae6e0b22..2e3a2f3a5 100644
--- a/cms-api/src/main/java/com/condation/cms/api/hooks/Filter.java
+++ b/cms-api/src/main/java/com/condation/cms/api/hooks/FilterHook.java
@@ -27,5 +27,5 @@
*
* @author t.marx
*/
-public record Filter(String name, int priority, FilterFunction function) implements Hook {
+record FilterHook(String name, int priority, FilterFunction function) implements Hook {
}
diff --git a/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java b/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
index bd714568f..b173e4d6e 100644
--- a/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
+++ b/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
@@ -21,9 +21,11 @@
* .
* #L%
*/
-
+import com.condation.cms.api.annotations.Filter;
+import com.condation.cms.api.annotations.Action;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -40,40 +42,90 @@
@RequiredArgsConstructor
public class HookSystem {
- Multimap actions = ArrayListMultimap.create();
-
- Multimap filters = ArrayListMultimap.create();
-
- private HookSystem (HookSystem source) {
+ Multimap actions = ArrayListMultimap.create();
+
+ Multimap filters = ArrayListMultimap.create();
+
+ private HookSystem(HookSystem source) {
this.actions.putAll(source.actions);
this.filters.putAll(source.filters);
}
-
- public HookSystem clone () {
+
+ public HookSystem clone() {
return new HookSystem(this);
}
+ public void register(Object sourceObject) {
+ Class> objectClass = sourceObject.getClass();
+ for (Method method : objectClass.getDeclaredMethods()) {
+ // regsiter actions
+ var actionAnnotation = method.getAnnotation(Action.class);
+ if (actionAnnotation != null) {
+
+ var parameters = method.getParameterTypes();
+ if (parameters.length != 1 || !ActionContext.class.isAssignableFrom(parameters[0])) {
+ log.warn("Method {}.{} ignored: must have exactly one parameter of type ActionContext",
+ objectClass.getSimpleName(), method.getName());
+ } else {
+ if (!method.canAccess(sourceObject)) {
+ method.setAccessible(true);
+ }
+
+ registerAction(actionAnnotation.value(), context -> {
+ try {
+ return method.invoke(sourceObject, context);
+ } catch (Exception e) {
+ log.error("Error invoking action method {}.{}", objectClass.getSimpleName(), method.getName(), e);
+ throw new RuntimeException(e);
+ }
+ }, actionAnnotation.priority());
+ }
+ }
+
+ // register filters
+ var filterAnnotation = method.getAnnotation(Filter.class);
+ if (filterAnnotation != null) {
+ var parameters = method.getParameterTypes();
+ if (parameters.length != 1 || !FilterContext.class.isAssignableFrom(parameters[0])) {
+ log.warn("Method {}.{} ignored for Filter: must have exactly one parameter of type FilterContext",
+ objectClass.getSimpleName(), method.getName());
+ } else {
+ if (!method.canAccess(sourceObject)) {
+ method.setAccessible(true);
+ }
+ registerFilter(filterAnnotation.value(), context -> {
+ try {
+ return method.invoke(sourceObject, context);
+ } catch (Exception e) {
+ log.error("Error invoking filter method {}.{}", objectClass.getSimpleName(), method.getName(), e);
+ throw new RuntimeException(e);
+ }
+ }, filterAnnotation.priority());
+ }
+ }
+ }
+ }
+
public void registerAction(final String name, final ActionFunction hookFunction) {
registerAction(name, hookFunction, 10);
}
public void registerAction(final String name, final ActionFunction hookFunction, int priority) {
- actions.put(name, new Action<>(name, priority, hookFunction));
+ actions.put(name, new ActionHook<>(name, priority, hookFunction));
}
-
+
public void registerFilter(final String name, final FilterFunction hookFunction) {
registerFilter(name, hookFunction, 10);
}
public void registerFilter(final String name, final FilterFunction hookFunction, int priority) {
- filters.put(name, new Filter<>(name, priority, hookFunction));
+ filters.put(name, new FilterHook<>(name, priority, hookFunction));
}
-
+
public ActionContext execute(final String name) {
return execute(name, Map.of());
}
-
-
+
public ActionContext execute(final String name, final Map arguments) {
var context = new ActionContext(new HashMap<>(arguments), new ArrayList<>());
actions.get(name).stream()
@@ -91,15 +143,15 @@ public ActionContext execute(final String name, final Map
* @param name
* @param parameters
- * @return
+ * @return
*/
public FilterContext filter(final String name, final T parameters) {
final FilterContext returnContext = new FilterContext(
@@ -111,7 +163,7 @@ public FilterContext filter(final String name, final T parameters) {
try {
var context = new FilterContext(returnContext.value());
var result = action.function().apply(context);
- returnContext.value((T)result);
+ returnContext.value((T) result);
} catch (Exception e) {
log.error("error on filter", e);
}
diff --git a/cms-api/src/test/java/com/condation/cms/api/extensions/http/routes/RoutesManagerTest.java b/cms-api/src/test/java/com/condation/cms/api/extensions/http/routes/RoutesManagerTest.java
new file mode 100644
index 000000000..9139f8b1f
--- /dev/null
+++ b/cms-api/src/test/java/com/condation/cms/api/extensions/http/routes/RoutesManagerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit5TestClass.java to edit this template
+ */
+package com.condation.cms.api.extensions.http.routes;
+
+/*-
+ * #%L
+ * cms-api
+ * %%
+ * Copyright (C) 2023 - 2025 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+
+import com.condation.cms.api.annotations.Route;
+import org.assertj.core.api.Assertions;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author thorstenmarx
+ */
+public class RoutesManagerTest {
+
+ @Test
+ public void testRegister() {
+ Object controller = new MyRoutes();
+ RoutesManager instance = new RoutesManager();
+ instance.register(controller);
+
+ var handler1 = instance.findFirst("/test1", "GET");
+ Assertions.assertThat(handler1).isPresent();
+ var handler2 = instance.findFirst("/test2", "GET");
+ Assertions.assertThat(handler2).isPresent();
+ }
+
+ @Test
+ public void test_no_handler() {
+ Object controller = new MyRoutes();
+ RoutesManager instance = new RoutesManager();
+ instance.register(controller);
+
+ var handler = instance.findFirst("/test3", "GET");
+ Assertions.assertThat(handler).isEmpty();
+ }
+
+ public class MyRoutes {
+
+ @Route("/test1")
+ public boolean handle1 (Request request, Response response, Callback callback) {
+ return true;
+ }
+
+ @Route("/test2")
+ public boolean handle2 (Request request, Response response, Callback callback) {
+ return true;
+ }
+ }
+
+
+}
diff --git a/cms-api/src/test/java/com/condation/cms/api/hooks/HookSystemTest.java b/cms-api/src/test/java/com/condation/cms/api/hooks/HookSystemTest.java
index ae5081095..1f2155166 100644
--- a/cms-api/src/test/java/com/condation/cms/api/hooks/HookSystemTest.java
+++ b/cms-api/src/test/java/com/condation/cms/api/hooks/HookSystemTest.java
@@ -23,9 +23,12 @@
*/
+import com.condation.cms.api.annotations.Filter;
+import com.condation.cms.api.annotations.Action;
import com.condation.cms.api.hooks.HookSystem;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -110,4 +113,47 @@ public void test_filter_remove () {
var context = hookSystem.filter("test/list", new ArrayList<>(List.of("1", "2", "3")));
Assertions.assertThat(context.value()).containsExactly("1", "3");
}
+
+ @Test
+ void test_action_annotation () {
+ var actionObject = new MyActions();
+
+ hookSystem.register(actionObject);
+
+ hookSystem.execute("test/annotation/action1");
+
+ Assertions.assertThat(actionObject.counter).hasValue(2);
+ }
+
+ @Test
+ void test_filter_annotations () {
+ var myFilters = new MyFilters();
+
+ hookSystem.register(myFilters);
+
+ var context = hookSystem.filter("test/annotation/filter1", new ArrayList<>(List.of("1", "2", "3")));
+ Assertions.assertThat(context.value()).containsExactly("1", "3");
+ }
+
+ public class MyFilters {
+ @Filter("test/annotation/filter1")
+ public List filter (FilterContext> context) {
+ context.value().remove("2");
+ return context.value();
+ }
+ }
+
+ public class MyActions {
+
+ private AtomicInteger counter = new AtomicInteger(0);
+
+ @Action("test/annotation/action1")
+ public void action1 (ActionContext> context) {
+ counter.incrementAndGet();
+ }
+ @Action("test/annotation/action1")
+ public void action2 (ActionContext> context) {
+ counter.incrementAndGet();
+ }
+ }
}
diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java
index 047a5e36a..ba0d1c6a4 100644
--- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java
+++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java
@@ -21,11 +21,13 @@
* .
* #L%
*/
-
-
+import com.condation.cms.api.annotations.ShortCode;
import com.condation.cms.api.model.Parameter;
import com.condation.cms.api.request.RequestContext;
+import java.lang.reflect.Method;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -43,29 +45,29 @@ public class ShortCodes {
private final TagMap tagMap;
private final TagParser parser;
- public ShortCodes (Map> codes, TagParser tagParser) {
+ public ShortCodes(Map> codes, TagParser tagParser) {
this.parser = tagParser;
this.tagMap = new TagMap();
this.tagMap.putAll(codes);
}
-
- public Set getShortCodeNames () {
+
+ public Set getShortCodeNames() {
return tagMap.names();
}
-
- public String replace (final String content) {
+
+ public String replace(final String content) {
return replace(content, Collections.emptyMap(), null);
}
-
- public String replace (final String content, Map contextModel) {
+
+ public String replace(final String content, Map contextModel) {
return replace(content, contextModel, null);
}
-
- public String replace (final String content, Map contextModel, RequestContext requestContext) {
+
+ public String replace(final String content, Map contextModel, RequestContext requestContext) {
return parser.parse(content, tagMap, contextModel, requestContext);
}
-
- public String execute (String name, Map parameters, RequestContext requestContext) {
+
+ public String execute(String name, Map parameters, RequestContext requestContext) {
if (!tagMap.has(name)) {
return "";
}
@@ -78,8 +80,76 @@ public String execute (String name, Map parameters, RequestConte
}
return tagMap.get(name).apply(params);
} catch (Exception e) {
- log.error("",e);
+ log.error("", e);
}
return "";
}
+
+ public static ShortCodes.Builder builder(TagParser tagParser) {
+ return new Builder(tagParser);
+ }
+
+ public static class Builder {
+
+ private final TagParser tagParser;
+
+ private final Map> codes = new HashMap<>();
+
+ private Builder(TagParser tagParser) {
+ this.tagParser = tagParser;
+ }
+
+ public Builder register(String name, Function shortCodeFN) {
+ codes.put(name, shortCodeFN);
+ return this;
+ }
+
+ public Builder register (Map> codes) {
+ this.codes.putAll(codes);
+ return this;
+ }
+
+ public Builder register (List handlers) {
+ if (handlers == null || handlers.isEmpty()) {
+ return this;
+ }
+
+ handlers.forEach(this::register);
+
+ return this;
+ }
+
+ public Builder register(Object handler) {
+ if (handler == null) {
+ return this;
+ }
+
+ Class> clazz = handler.getClass();
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (method.isAnnotationPresent(ShortCode.class)) {
+ if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == Parameter.class) {
+ method.setAccessible(true); // falls private
+ ShortCode annotation = method.getAnnotation(ShortCode.class);
+ String key = annotation.value();
+
+ codes.put(key, param -> {
+ try {
+ return (String) method.invoke(handler, param);
+ } catch (Exception e) {
+ throw new RuntimeException("Error calling shortcode: " + key, e);
+ }
+ });
+ } else {
+ log.error("ignore methode" + method.getName() + " – wrong signature.");
+ }
+ }
+ }
+
+ return this;
+ }
+
+ public ShortCodes build() {
+ return new ShortCodes(codes, tagParser);
+ }
+ }
}
diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java
index 8c0924ba3..618577a5a 100644
--- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java
+++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java
@@ -23,6 +23,7 @@
*/
+import com.condation.cms.api.annotations.ShortCode;
import com.condation.cms.api.model.Parameter;
import com.condation.cms.api.request.RequestContext;
import com.condation.cms.content.ContentBaseTest;
@@ -43,44 +44,47 @@ public class ShortCodesTest extends ContentBaseTest {
@BeforeEach
public void init () {
- Map> tags = new HashMap<>();
- tags.put(
+ var builder = ShortCodes.builder(getTagParser());
+
+ builder.register(
"youtube",
(params) -> " ".formatted(params.getOrDefault("id", "")));
- tags.put(
+ builder.register(
"hello_from",
(params) -> "
%s from %s
".formatted(params.getOrDefault("name", ""), params.getOrDefault("from", "")));
- tags.put(
+ builder.register(
"mark",
params -> "%s ".formatted(params.get("_content"))
);
- tags.put(
+ builder.register(
"mark2",
params -> "%s ".formatted(params.get("class"), params.get("_content"))
);
- tags.put(
+ builder.register(
"exp",
params -> "%s ".formatted(params.get("expression"))
);
- tags.put(
+ builder.register(
"set_var",
params -> {
params.getRequestContext().getVariables().put("myVar", "Hello world!");
return "";
}
);
- tags.put(
+ builder.register(
"get_var",
params -> {
return (String)params.getRequestContext().getVariables().getOrDefault("myVar", "DEFAULT");
}
);
- shortCodes = new ShortCodes(tags, getTagParser());
+ builder.register(new ShortCodesHandler());
+
+ shortCodes = builder.build();
}
@@ -229,4 +233,20 @@ void test_variables() {
Assertions.assertThat(result).isEqualTo("Hello world!");
}
+
+ @Test
+ void test_handler () {
+ RequestContext requestContext = new RequestContext();
+
+ var result = shortCodes.replace("[[printHello name='CondationCMS' /]]", Map.of(), requestContext);
+
+ Assertions.assertThat(result).isEqualTo("hello CondationCMS");
+ }
+
+ public static class ShortCodesHandler {
+ @ShortCode("printHello")
+ public String printHello (Parameter parameter) {
+ return "hello " + parameter.getOrDefault("name", "");
+ }
+ }
}
diff --git a/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java
index c8fdc93f4..151485564 100644
--- a/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java
+++ b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java
@@ -171,16 +171,23 @@ private HookSystem setupAndGetHookSystem() {
}
private ShortCodes initShortCodes(RequestContext requestContext) {
- Map> codes = new HashMap<>();
+ var parser = injector.getInstance(TagParser.class);
+ var builder = ShortCodes.builder(parser);
+
injector.getInstance(ModuleManager.class).extensions(RegisterShortCodesExtensionPoint.class)
- .forEach(extension -> codes.putAll(extension.shortCodes()));
+ .forEach(extension -> {
+ builder.register(extension.shortCodes());
+
+ builder.register(extension.shortCodeDefinitions());
+ });
+ var codes = new HashMap>();
var wrapper = requestContext.get(ContentHooks.class).getShortCodes(codes);
- var parser = injector.getInstance(TagParser.class);
+ builder.register(wrapper.getShortCodes());
- return new ShortCodes(wrapper.getShortCodes(), parser);
+ return builder.build();
}
public RequestContext create() throws IOException {
@@ -219,7 +226,7 @@ public RequestContext create() throws IOException {
RenderContext renderContext = new RenderContext(
markdownRenderer,
- createShortCodes(requestContext),
+ initShortCodes(requestContext),
theme);
requestContext.add(RenderContext.class, renderContext);
requestContext.add(MarkdownRendererFeature.class, new MarkdownRendererFeature(markdownRenderer));
@@ -253,16 +260,4 @@ public RequestContext create(
return requestContext;
}
-
- private ShortCodes createShortCodes(RequestContext requestContext) {
- Map> codes = new HashMap<>();
-
- injector.getInstance(ModuleManager.class).extensions(RegisterShortCodesExtensionPoint.class)
- .forEach(extension -> codes.putAll(extension.shortCodes()));
-
- var wrapper = requestContext.get(ContentHooks.class).getShortCodes(codes);
- var parser = injector.getInstance(TagParser.class);
- return new ShortCodes(wrapper.getShortCodes(), parser);
- }
-
}
diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java
index 2171ded65..04c52d49f 100644
--- a/cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java
+++ b/cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java
@@ -24,6 +24,8 @@
import com.condation.cms.api.extensions.HttpRoutesExtensionPoint;
import com.condation.cms.api.extensions.Mapping;
+import com.condation.cms.api.extensions.http.routes.RoutesExtensionPoint;
+import com.condation.cms.api.extensions.http.routes.RoutesManager;
import com.condation.cms.api.request.RequestContext;
import com.condation.cms.api.utils.RequestUtil;
import com.condation.cms.extensions.HttpHandlerExtension;
@@ -32,6 +34,7 @@
import com.condation.cms.server.filter.CreateRequestContextFilter;
import com.condation.modules.api.ModuleManager;
import com.google.inject.Inject;
+import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -62,8 +65,8 @@ public boolean handle(Request request, Response response, Callback callback) thr
if (tryModuleRoutes(request, response, callback)) {
return true;
}
-
- return false;
+
+ return tryRoutesManager(request, response, callback);
} catch (Exception e) {
log.error(null, e);
callback.failed(e);
@@ -71,6 +74,27 @@ public boolean handle(Request request, Response response, Callback callback) thr
}
}
+ private boolean tryRoutesManager (Request request, Response response, Callback callback) throws Exception {
+ String route = "/" + RequestUtil.getContentPath(request);
+
+ RoutesManager routesManager = new RoutesManager();
+
+
+ moduleManager.extensions(RoutesExtensionPoint.class)
+ .stream()
+ .map(RoutesExtensionPoint::getRouteDefinitions)
+ .filter(routeDefinitions -> routeDefinitions != null && !routeDefinitions.isEmpty())
+ .flatMap(List::stream)
+ .forEach(controller -> routesManager.register(controller));
+
+ var handler = routesManager.findFirst(route, request.getMethod());
+ if (handler.isPresent()) {
+ return handler.get().handle(request, response, callback);
+ }
+
+ return false;
+ }
+
private boolean tryModuleRoutes(Request request, Response response, Callback callback) throws Exception {
String route = "/" + RequestUtil.getContentPath(request);
diff --git a/cms-templates/src/main/java/com/condation/cms/templates/DynamicConfiguration.java b/cms-templates/src/main/java/com/condation/cms/templates/DynamicConfiguration.java
index bca613a7d..945c62cda 100644
--- a/cms-templates/src/main/java/com/condation/cms/templates/DynamicConfiguration.java
+++ b/cms-templates/src/main/java/com/condation/cms/templates/DynamicConfiguration.java
@@ -37,7 +37,7 @@
public record DynamicConfiguration(TemplateComponents templateComponents, Map components, RequestContext requestContext) {
public static final DynamicConfiguration EMPTY = new DynamicConfiguration(
- new TemplateComponents(Collections.emptyMap()),
+ new TemplateComponents(),
Collections.emptyMap(),
null
);
diff --git a/cms-templates/src/main/java/com/condation/cms/templates/components/ComponentMap.java b/cms-templates/src/main/java/com/condation/cms/templates/components/ComponentMap.java
index 185fa791d..75223403f 100644
--- a/cms-templates/src/main/java/com/condation/cms/templates/components/ComponentMap.java
+++ b/cms-templates/src/main/java/com/condation/cms/templates/components/ComponentMap.java
@@ -21,26 +21,27 @@
* .
* #L%
*/
-
import com.condation.cms.api.model.Parameter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
+import lombok.extern.slf4j.Slf4j;
/**
*
* @author t.marx
*/
+@Slf4j
public class ComponentMap {
private final Map> tags = new HashMap<>();
- public Set names () {
+ public Set names() {
return Collections.unmodifiableSet(tags.keySet());
}
-
+
public void put(String codeName, Function function) {
tags.put(codeName, function);
}
@@ -48,7 +49,7 @@ public void put(String codeName, Function function) {
public void putAll(Map> tags) {
this.tags.putAll(tags);
}
-
+
public boolean has(String codeName) {
return tags.containsKey(codeName);
}
diff --git a/cms-templates/src/main/java/com/condation/cms/templates/components/TemplateComponents.java b/cms-templates/src/main/java/com/condation/cms/templates/components/TemplateComponents.java
index 5055f9764..93befd56e 100644
--- a/cms-templates/src/main/java/com/condation/cms/templates/components/TemplateComponents.java
+++ b/cms-templates/src/main/java/com/condation/cms/templates/components/TemplateComponents.java
@@ -21,10 +21,11 @@
* .
* #L%
*/
-
-
+import com.condation.cms.api.annotations.TemplateComponent;
import com.condation.cms.api.model.Parameter;
import com.condation.cms.api.request.RequestContext;
+import java.lang.reflect.Method;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -39,16 +40,57 @@ public class TemplateComponents {
private final ComponentMap componentMap;
- public TemplateComponents (Map> components) {
+ public TemplateComponents() {
this.componentMap = new ComponentMap();
+ }
+
+ public void register(String name, Function templateComponentFN) {
+ componentMap.put(name, templateComponentFN);
+ }
+
+ public void register(Map> components) {
this.componentMap.putAll(components);
}
-
- public Set getComponentNames () {
+
+ public void register(List handlers) {
+ if (handlers == null || handlers.isEmpty()) {
+ return;
+ }
+ handlers.forEach(this::register);
+ }
+
+ public void register(Object handler) {
+ if (handler == null) {
+ return;
+ }
+
+ Class> clazz = handler.getClass();
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (method.isAnnotationPresent(TemplateComponent.class)) {
+ if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == Parameter.class) {
+ method.setAccessible(true);
+ var annotation = method.getAnnotation(TemplateComponent.class);
+ String key = annotation.value();
+
+ componentMap.put(key, param -> {
+ try {
+ return (String) method.invoke(handler, param);
+ } catch (Exception e) {
+ throw new RuntimeException("Error calling component: " + key, e);
+ }
+ });
+ } else {
+ log.error("ignore methode" + method.getName() + " – wrong signature.");
+ }
+ }
+ }
+ }
+
+ public Set getComponentNames() {
return componentMap.names();
}
-
- public String execute (String name, Map parameters, RequestContext requestContext) {
+
+ public String execute(String name, Map parameters, RequestContext requestContext) {
if (!componentMap.has(name)) {
return "";
}
@@ -61,7 +103,7 @@ public String execute (String name, Map parameters, RequestConte
}
return componentMap.get(name).apply(params);
} catch (Exception e) {
- log.error("",e);
+ log.error("", e);
}
return "";
}
diff --git a/cms-templates/src/main/java/com/condation/cms/templates/module/CMSModuleTemplateEngine.java b/cms-templates/src/main/java/com/condation/cms/templates/module/CMSModuleTemplateEngine.java
index 3951e64c1..9f9ac3ee5 100644
--- a/cms-templates/src/main/java/com/condation/cms/templates/module/CMSModuleTemplateEngine.java
+++ b/cms-templates/src/main/java/com/condation/cms/templates/module/CMSModuleTemplateEngine.java
@@ -129,16 +129,22 @@ private DynamicConfiguration createDynamicConfiguration(Model model) {
}
private TemplateComponents createTemplateComponents(RequestContext requestContext) {
- Map> components = new HashMap<>();
-
var injector = requestContext.get(InjectorFeature.class).injector();
+ var templateComponents = new TemplateComponents();
injector.getInstance(ModuleManager.class)
.extensions(RegisterTemplateComponentExtensionPoint.class)
- .forEach(extension -> components.putAll(extension.components()));
+ .forEach(extension -> {
+ templateComponents.register(extension.components());
+ templateComponents.register(extension.componentDefinitions());
+ });
+ Map> components = new HashMap<>();
var wrapper = requestContext.get(TemplateHooks.class).getComponents(components);
- return new TemplateComponents(wrapper.getComponents());
+
+ templateComponents.register(wrapper.getComponents());
+
+ return templateComponents;
}
@Override
diff --git a/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineComponentTest.java b/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineComponentTest.java
index e72981be5..7b205476f 100644
--- a/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineComponentTest.java
+++ b/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineComponentTest.java
@@ -21,8 +21,8 @@
* .
* #L%
*/
-import com.condation.cms.content.shortcodes.ShortCodes;
-import com.condation.cms.content.shortcodes.TagParser;
+import com.condation.cms.api.annotations.TemplateComponent;
+import com.condation.cms.api.model.Parameter;
import com.condation.cms.templates.components.TemplateComponents;
import com.condation.cms.templates.exceptions.ParserException;
import com.condation.cms.templates.exceptions.RenderException;
@@ -44,20 +44,24 @@ public class TemplateEngineComponentTest extends AbstractTemplateEngineTest {
@BeforeAll
public void setupShortCodes() {
- components = new TemplateComponents(
- Map.of(
+ components = new TemplateComponents();
+ components.register(Map.of(
"tag1", (params) -> {
return "Hello";
},
"tag2", (param) -> {
return "Hello " + param.get("name") + "!";
- },
- "tag3", (param) -> {
- return "%s
".formatted(param.get("_content"));
- })
- );
+ }));
+ components.register(new MyComponents());
dynamicConfiguration = new DynamicConfiguration(components, null);
}
+
+ public static class MyComponents {
+ @TemplateComponent("tag3")
+ public String tag3 (Parameter parameter) {
+ return "%s
".formatted(parameter.get("_content"));
+ }
+ }
@Override
public TemplateLoader getLoader() {
diff --git a/cms-templates/src/test/java/com/condation/cms/templates/TemplateFeatureTest.java b/cms-templates/src/test/java/com/condation/cms/templates/TemplateFeatureTest.java
index 998c233a5..4e122296a 100644
--- a/cms-templates/src/test/java/com/condation/cms/templates/TemplateFeatureTest.java
+++ b/cms-templates/src/test/java/com/condation/cms/templates/TemplateFeatureTest.java
@@ -21,7 +21,6 @@
* .
* #L%
*/
-import com.condation.cms.content.shortcodes.TagParser;
import com.condation.cms.templates.components.TemplateComponents;
import com.condation.cms.templates.loaders.StringTemplateLoader;
import com.google.gson.Gson;
@@ -43,7 +42,7 @@
public class TemplateFeatureTest extends AbstractTemplateEngineTest {
private StringTemplateLoader templateLoader = new StringTemplateLoader();
-
+
private Gson gson = new GsonBuilder()
.setStrictness(Strictness.LENIENT)
.create();
@@ -66,7 +65,7 @@ void test_features(String templateFile) throws Exception {
var expectedContent = readContent(templateFile + "_expected.html");
var data = getData(templateFile);
-
+
templateLoader.add(templateFile, templateContent);
var template = SUT.getTemplate(templateFile);
@@ -76,25 +75,25 @@ void test_features(String templateFile) throws Exception {
Assertions.assertThat(rendered).isEqualToIgnoringWhitespace(expectedContent);
}
- private DynamicConfiguration createDynamicConfig () {
- TemplateComponents components = new TemplateComponents(
- Map.of(
- "hello", (params) -> "hello " + params.get("name")
- ));
+ private DynamicConfiguration createDynamicConfig() {
+ TemplateComponents components = new TemplateComponents();
+ components.register(Map.of(
+ "hello", (params) -> "hello " + params.get("name")
+ ));
return new DynamicConfiguration(components, null);
}
-
- private Map getData (String filename) throws IOException {
+
+ private Map getData(String filename) throws IOException {
String dataFile = filename + "_data.json";
if (!exists(dataFile)) {
return Collections.emptyMap();
}
-
+
var dataContent = readContent(dataFile);
-
+
return gson.fromJson(dataContent, HashMap.class);
}
-
+
private boolean exists(String filename) {
var resourcePath = "testdata/" + filename;
var url = TemplateFeatureTest.class.getResource(resourcePath);