From eaad93bb28ffb50a8b33915696c61475181de25d Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 16 Apr 2025 11:28:19 +0200 Subject: [PATCH 1/8] hooks via annotations --- CHANGELOG.md | 20 +++++ .../com/condation/cms/api/hooks/Action.java | 14 ++- .../condation/cms/api/hooks/ActionHook.java | 31 +++++++ .../com/condation/cms/api/hooks/Filter.java | 14 ++- .../condation/cms/api/hooks/FilterHook.java | 31 +++++++ .../condation/cms/api/hooks/HookSystem.java | 90 ++++++++++++++----- .../cms/api/hooks/HookSystemTest.java | 44 +++++++++ 7 files changed, 218 insertions(+), 26 deletions(-) create mode 100644 cms-api/src/main/java/com/condation/cms/api/hooks/ActionHook.java create mode 100644 cms-api/src/main/java/com/condation/cms/api/hooks/FilterHook.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e14582048..816390241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,26 @@ 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 + +```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 + ## 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/hooks/Action.java b/cms-api/src/main/java/com/condation/cms/api/hooks/Action.java index e1e849a3d..a39b4e0cd 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/Action.java @@ -4,7 +4,7 @@ * #%L * cms-api * %% - * Copyright (C) 2023 - 2024 CondationCMS + * 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 @@ -22,10 +22,18 @@ * #L% */ +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * - * @author t.marx + * @author thorstenmarx */ -public record Action(String name, int priority, ActionFunction function) implements Hook { +@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/hooks/ActionHook.java b/cms-api/src/main/java/com/condation/cms/api/hooks/ActionHook.java new file mode 100644 index 000000000..281d421da --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/hooks/ActionHook.java @@ -0,0 +1,31 @@ +package com.condation.cms.api.hooks; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2024 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% + */ + + +/** + * + * @author t.marx + */ +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/Filter.java index 1ae6e0b22..84345a36f 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/Filter.java @@ -4,7 +4,7 @@ * #%L * cms-api * %% - * Copyright (C) 2023 - 2024 CondationCMS + * 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 @@ -22,10 +22,18 @@ * #L% */ +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * - * @author t.marx + * @author thorstenmarx */ -public record Filter(String name, int priority, FilterFunction function) implements Hook { +@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/hooks/FilterHook.java b/cms-api/src/main/java/com/condation/cms/api/hooks/FilterHook.java new file mode 100644 index 000000000..2e3a2f3a5 --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/hooks/FilterHook.java @@ -0,0 +1,31 @@ +package com.condation.cms.api.hooks; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2024 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% + */ + + +/** + * + * @author t.marx + */ +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..e3e699272 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,9 @@ * . * #L% */ - 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 +40,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 +141,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 +161,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/hooks/HookSystemTest.java b/cms-api/src/test/java/com/condation/cms/api/hooks/HookSystemTest.java index ae5081095..670655d8e 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 @@ -26,6 +26,7 @@ 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 +111,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(); + } + } } From d930d3a4fc24bb99446cb36672466e66bb500be9 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 16 Apr 2025 15:39:23 +0200 Subject: [PATCH 2/8] register routes via annotations --- .../cms/api/extensions/HttpHandler.java | 8 ++ .../cms/api/extensions/routes/Route.java | 35 ++++++++ .../routes/RoutesExtensionPoint.java | 34 ++++++++ .../api/extensions/routes/RoutesManager.java | 82 +++++++++++++++++++ .../extensions/routes/RoutesManagerTest.java | 79 ++++++++++++++++++ .../server/handler/http/RoutesHandler.java | 24 +++++- 6 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 cms-api/src/main/java/com/condation/cms/api/extensions/routes/Route.java create mode 100644 cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesExtensionPoint.java create mode 100644 cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java create mode 100644 cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java 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/routes/Route.java b/cms-api/src/main/java/com/condation/cms/api/extensions/routes/Route.java new file mode 100644 index 000000000..a2aced9fd --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/routes/Route.java @@ -0,0 +1,35 @@ +package com.condation.cms.api.extensions.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 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/extensions/routes/RoutesExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesExtensionPoint.java new file mode 100644 index 000000000..90ba32384 --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesExtensionPoint.java @@ -0,0 +1,34 @@ +package com.condation.cms.api.extensions.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; + +/** + * + * @author thorstenmarx + */ +public abstract class RoutesExtensionPoint extends AbstractExtensionPoint { + + abstract public void registerRoutes (RoutesManager routesManager); +} diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java b/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java new file mode 100644 index 000000000..b13490523 --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java @@ -0,0 +1,82 @@ +package com.condation.cms.api.extensions.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.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 Map handlerMapping = new HashMap<>(); + 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/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java b/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java new file mode 100644 index 000000000..5ce98603c --- /dev/null +++ b/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java @@ -0,0 +1,79 @@ +/* + * 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.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.google.common.base.Stopwatch; +import java.util.concurrent.TimeUnit; +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-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..da442d11e 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.routes.RoutesExtensionPoint; +import com.condation.cms.api.extensions.routes.RoutesManager; import com.condation.cms.api.request.RequestContext; import com.condation.cms.api.utils.RequestUtil; import com.condation.cms.extensions.HttpHandlerExtension; @@ -62,8 +64,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 +73,24 @@ 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() + .forEach(extension -> extension.registerRoutes(routesManager)); + + 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); From ba040702ec268f0ec31bbb2241685bf4faee64e6 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 16 Apr 2025 15:39:58 +0200 Subject: [PATCH 3/8] refactor package --- .../condation/cms/api/extensions/{ => http}/routes/Route.java | 2 +- .../extensions/{ => http}/routes/RoutesExtensionPoint.java | 2 +- .../cms/api/extensions/{ => http}/routes/RoutesManager.java | 2 +- .../cms/api/extensions/routes/RoutesManagerTest.java | 2 ++ .../com/condation/cms/server/handler/http/RoutesHandler.java | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) rename cms-api/src/main/java/com/condation/cms/api/extensions/{ => http}/routes/Route.java (95%) rename cms-api/src/main/java/com/condation/cms/api/extensions/{ => http}/routes/RoutesExtensionPoint.java (94%) rename cms-api/src/main/java/com/condation/cms/api/extensions/{ => http}/routes/RoutesManager.java (98%) diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/Route.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/Route.java similarity index 95% rename from cms-api/src/main/java/com/condation/cms/api/extensions/routes/Route.java rename to cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/Route.java index a2aced9fd..abfac457c 100644 --- a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/Route.java +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/Route.java @@ -1,4 +1,4 @@ -package com.condation.cms.api.extensions.routes; +package com.condation.cms.api.extensions.http.routes; /*- * #%L diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesExtensionPoint.java similarity index 94% rename from cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesExtensionPoint.java rename to cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesExtensionPoint.java index 90ba32384..b795087d0 100644 --- a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesExtensionPoint.java +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesExtensionPoint.java @@ -1,4 +1,4 @@ -package com.condation.cms.api.extensions.routes; +package com.condation.cms.api.extensions.http.routes; /*- * #%L diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesManager.java similarity index 98% rename from cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java rename to cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesManager.java index b13490523..af6dbfbad 100644 --- a/cms-api/src/main/java/com/condation/cms/api/extensions/routes/RoutesManager.java +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/RoutesManager.java @@ -1,4 +1,4 @@ -package com.condation.cms.api.extensions.routes; +package com.condation.cms.api.extensions.http.routes; /*- * #%L diff --git a/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java b/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java index 5ce98603c..3674e9ff9 100644 --- a/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java +++ b/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java @@ -26,6 +26,8 @@ * #L% */ +import com.condation.cms.api.extensions.http.routes.Route; +import com.condation.cms.api.extensions.http.routes.RoutesManager; import com.google.common.base.Stopwatch; import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; 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 da442d11e..28d8008e1 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,8 +24,8 @@ import com.condation.cms.api.extensions.HttpRoutesExtensionPoint; import com.condation.cms.api.extensions.Mapping; -import com.condation.cms.api.extensions.routes.RoutesExtensionPoint; -import com.condation.cms.api.extensions.routes.RoutesManager; +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; From 55e2b5cac8f9936e6424c75985c1c3547597a8fb Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 16 Apr 2025 15:53:12 +0200 Subject: [PATCH 4/8] refactor test, update changelog --- CHANGELOG.md | 11 +++++++++++ .../{ => http}/routes/RoutesManagerTest.java | 6 +----- 2 files changed, 12 insertions(+), 5 deletions(-) rename cms-api/src/test/java/com/condation/cms/api/extensions/{ => http}/routes/RoutesManagerTest.java (89%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 816390241..456cad960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ 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) { @@ -31,6 +33,15 @@ public void action1 (ActionContext context) { #### HTTP-Controllers +The new RoutesExtensionPoint gives developers access to the RoutesManager, which includes a register method for adding routes via annotated methods. + +```java +@Route("/test2") +public boolean handle2 (Request request, Response response, Callback callback) { + return true; +} +``` + ## 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/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java b/cms-api/src/test/java/com/condation/cms/api/extensions/http/routes/RoutesManagerTest.java similarity index 89% rename from cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java rename to cms-api/src/test/java/com/condation/cms/api/extensions/http/routes/RoutesManagerTest.java index 3674e9ff9..1a76be070 100644 --- a/cms-api/src/test/java/com/condation/cms/api/extensions/routes/RoutesManagerTest.java +++ b/cms-api/src/test/java/com/condation/cms/api/extensions/http/routes/RoutesManagerTest.java @@ -2,7 +2,7 @@ * 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.routes; +package com.condation.cms.api.extensions.http.routes; /*- * #%L @@ -26,10 +26,6 @@ * #L% */ -import com.condation.cms.api.extensions.http.routes.Route; -import com.condation.cms.api.extensions.http.routes.RoutesManager; -import com.google.common.base.Stopwatch; -import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; From 3bec077562aee54b275ddfd0d5ca92485515616b Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 17 Apr 2025 14:35:09 +0200 Subject: [PATCH 5/8] rafactor extensionpoint --- CHANGELOG.md | 3 ++- .../api/extensions/http/routes/RoutesExtensionPoint.java | 4 ++-- .../cms/api/extensions/http/routes/RoutesManager.java | 3 +-- .../condation/cms/server/handler/http/RoutesHandler.java | 6 +++++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 456cad960..3c02338dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,8 @@ public void action1 (ActionContext context) { #### HTTP-Controllers -The new RoutesExtensionPoint gives developers access to the RoutesManager, which includes a register method for adding routes via annotated methods. +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") 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 index b795087d0..496cd7da4 100644 --- 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 @@ -23,12 +23,12 @@ */ import com.condation.cms.api.extensions.AbstractExtensionPoint; +import java.util.List; /** * * @author thorstenmarx */ public abstract class RoutesExtensionPoint extends AbstractExtensionPoint { - - abstract public void registerRoutes (RoutesManager routesManager); + 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 index af6dbfbad..d535c7226 100644 --- 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 @@ -37,8 +37,7 @@ @Slf4j public class RoutesManager { - //private final Map handlerMapping = new HashMap<>(); - private final PathMapping pathMapping = new PathMapping(); + private final PathMapping pathMapping = new PathMapping(); public Optional findFirst (String path, String method) { return pathMapping.getMatchingHandler(path, method); 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 28d8008e1..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 @@ -34,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; @@ -81,7 +82,10 @@ private boolean tryRoutesManager (Request request, Response response, Callback c moduleManager.extensions(RoutesExtensionPoint.class) .stream() - .forEach(extension -> extension.registerRoutes(routesManager)); + .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()) { From 8ea4e6c068fd30217d64c89074a516a09f4a17bd Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Tue, 22 Apr 2025 15:50:40 +0200 Subject: [PATCH 6/8] add annotations for shortcodes and template components --- .../api/{hooks => annotations}/Action.java | 2 +- .../api/{hooks => annotations}/Filter.java | 2 +- .../http/routes => annotations}/Route.java | 2 +- .../cms/api/annotations/ShortCode.java | 38 ++++++++ .../api/annotations/TemplateComponent.java | 38 ++++++++ .../RegisterShortCodesExtensionPoint.java | 9 +- ...gisterTemplateComponentExtensionPoint.java | 9 +- .../extensions/http/routes/RoutesManager.java | 1 + .../condation/cms/api/hooks/HookSystem.java | 2 + .../http/routes/RoutesManagerTest.java | 1 + .../cms/api/hooks/HookSystemTest.java | 2 + .../cms/content/shortcodes/ShortCodes.java | 87 ++++++++++++++++--- .../content/shortcodes/ShortCodesTest.java | 38 ++++++-- .../cms/request/RequestContextFactory.java | 28 +++--- .../cms/templates/DynamicConfiguration.java | 2 +- .../templates/components/ComponentMap.java | 9 +- .../components/TemplateComponents.java | 38 +++++++- .../module/CMSModuleTemplateEngine.java | 14 ++- .../TemplateEngineComponentTest.java | 22 +++-- .../cms/templates/TemplateFeatureTest.java | 25 +++--- 20 files changed, 292 insertions(+), 77 deletions(-) rename cms-api/src/main/java/com/condation/cms/api/{hooks => annotations}/Action.java (96%) rename cms-api/src/main/java/com/condation/cms/api/{hooks => annotations}/Filter.java (96%) rename cms-api/src/main/java/com/condation/cms/api/{extensions/http/routes => annotations}/Route.java (95%) create mode 100644 cms-api/src/main/java/com/condation/cms/api/annotations/ShortCode.java create mode 100644 cms-api/src/main/java/com/condation/cms/api/annotations/TemplateComponent.java 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/annotations/Action.java similarity index 96% 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/annotations/Action.java index a39b4e0cd..5700ad687 100644 --- a/cms-api/src/main/java/com/condation/cms/api/hooks/Action.java +++ b/cms-api/src/main/java/com/condation/cms/api/annotations/Action.java @@ -1,4 +1,4 @@ -package com.condation.cms.api.hooks; +package com.condation.cms.api.annotations; /*- * #%L 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/annotations/Filter.java similarity index 96% 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/annotations/Filter.java index 84345a36f..f0afee6fe 100644 --- a/cms-api/src/main/java/com/condation/cms/api/hooks/Filter.java +++ b/cms-api/src/main/java/com/condation/cms/api/annotations/Filter.java @@ -1,4 +1,4 @@ -package com.condation.cms.api.hooks; +package com.condation.cms.api.annotations; /*- * #%L diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/Route.java b/cms-api/src/main/java/com/condation/cms/api/annotations/Route.java similarity index 95% rename from cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/Route.java rename to cms-api/src/main/java/com/condation/cms/api/annotations/Route.java index abfac457c..8f82df0d3 100644 --- a/cms-api/src/main/java/com/condation/cms/api/extensions/http/routes/Route.java +++ b/cms-api/src/main/java/com/condation/cms/api/annotations/Route.java @@ -1,4 +1,4 @@ -package com.condation.cms.api.extensions.http.routes; +package com.condation.cms.api.annotations; /*- * #%L 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/RegisterShortCodesExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/RegisterShortCodesExtensionPoint.java index 1e02a5f53..1b3b907a6 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,7 @@ import com.condation.cms.api.model.Parameter; +import java.util.Collections; import java.util.Map; import java.util.function.Function; @@ -33,5 +34,11 @@ */ public abstract class RegisterShortCodesExtensionPoint extends AbstractExtensionPoint { - public abstract Map> shortCodes (); + public Map> shortCodes () { + return Collections.emptyMap(); + } + + public Object shortCodeHandler () { + return new Object(); + } } 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..b4b0bdb1c 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,7 @@ import com.condation.cms.api.model.Parameter; +import java.util.Collections; import java.util.Map; import java.util.function.Function; @@ -33,5 +34,11 @@ */ public abstract class RegisterTemplateComponentExtensionPoint extends AbstractExtensionPoint { - public abstract Map> components (); + public Map> components () { + return Collections.emptyMap(); + } + + public Object handler () { + return new Object(); + } } 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 index d535c7226..d3ef35d61 100644 --- 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 @@ -22,6 +22,7 @@ * #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; 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 e3e699272..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,6 +21,8 @@ * . * #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; 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 index 1a76be070..9139f8b1f 100644 --- 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 @@ -26,6 +26,7 @@ * #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; 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 670655d8e..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,6 +23,8 @@ */ +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; 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..4c03b6e97 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,12 @@ * . * #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.Map; import java.util.Set; import java.util.function.Function; @@ -43,29 +44,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 +79,66 @@ 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(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..bc1455337 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,22 @@ 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.shortCodeHandler()); + }); + 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 +225,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 +259,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-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..b592b44f2 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 @@ -23,8 +23,10 @@ */ +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.Map; import java.util.Set; import java.util.function.Function; @@ -39,11 +41,45 @@ 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 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(); } 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..087d9c587 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.handler()); + }); + 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); From f0de54c4e67800aab9818720b3b8111f8cdee42c Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 23 Apr 2025 09:50:48 +0200 Subject: [PATCH 7/8] 1. minor refactorings 2. changelog --- CHANGELOG.md | 31 +++++++++++++++--- .../RegisterShortCodesExtensionPoint.java | 5 +-- ...gisterTemplateComponentExtensionPoint.java | 5 +-- .../cms/content/shortcodes/ShortCodes.java | 11 +++++++ .../cms/request/RequestContextFactory.java | 3 +- .../components/TemplateComponents.java | 32 +++++++++++-------- .../module/CMSModuleTemplateEngine.java | 2 +- 7 files changed, 66 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c02338dd..28fb5b13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ In this release we introduced some features to make life of developers easier. It is now possible to pass an object with annotated hook definitions to the HookSystem.register method. -```java +´´´java @Filter("test/annotation/filter1") public List filter (FilterContext> context) { context.value().remove("2"); @@ -29,19 +29,42 @@ public List filter (FilterContext> context) { 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 +´´´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 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 1b3b907a6..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 @@ -25,6 +25,7 @@ import com.condation.cms.api.model.Parameter; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.function.Function; @@ -38,7 +39,7 @@ public Map> shortCodes () { return Collections.emptyMap(); } - public Object shortCodeHandler () { - return new Object(); + 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 b4b0bdb1c..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 @@ -25,6 +25,7 @@ import com.condation.cms.api.model.Parameter; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.function.Function; @@ -38,7 +39,7 @@ public Map> components () { return Collections.emptyMap(); } - public Object handler () { - return new Object(); + public List componentDefinitions () { + return Collections.emptyList(); } } 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 4c03b6e97..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 @@ -27,6 +27,7 @@ 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; @@ -108,6 +109,16 @@ public Builder register (Map> 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; 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 bc1455337..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 @@ -178,7 +178,8 @@ private ShortCodes initShortCodes(RequestContext requestContext) { injector.getInstance(ModuleManager.class).extensions(RegisterShortCodesExtensionPoint.class) .forEach(extension -> { builder.register(extension.shortCodes()); - builder.register(extension.shortCodeHandler()); + + builder.register(extension.shortCodeDefinitions()); }); var codes = new HashMap>(); 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 b592b44f2..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,12 +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; @@ -41,18 +40,25 @@ public class TemplateComponents { private final ComponentMap componentMap; - public TemplateComponents () { + public TemplateComponents() { this.componentMap = new ComponentMap(); } - - public void register (String name, Function templateComponentFN) { + + public void register(String name, Function templateComponentFN) { componentMap.put(name, templateComponentFN); } - - public void register (Map> components) { + + public void register(Map> components) { this.componentMap.putAll(components); } - + + public void register(List handlers) { + if (handlers == null || handlers.isEmpty()) { + return; + } + handlers.forEach(this::register); + } + public void register(Object handler) { if (handler == null) { return; @@ -79,12 +85,12 @@ public void register(Object handler) { } } } - - public Set getComponentNames () { + + 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 ""; } @@ -97,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 087d9c587..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 @@ -136,7 +136,7 @@ private TemplateComponents createTemplateComponents(RequestContext requestContex .extensions(RegisterTemplateComponentExtensionPoint.class) .forEach(extension -> { templateComponents.register(extension.components()); - templateComponents.register(extension.handler()); + templateComponents.register(extension.componentDefinitions()); }); Map> components = new HashMap<>(); From 07fbddf9e2980b2751a31721b2e66f4f4cd51e7b Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 23 Apr 2025 09:52:48 +0200 Subject: [PATCH 8/8] fix changelog code formatting --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28fb5b13b..bc3f5044c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ In this release we introduced some features to make life of developers easier. It is now possible to pass an object with annotated hook definitions to the HookSystem.register method. -´´´java +```java @Filter("test/annotation/filter1") public List filter (FilterContext> context) { context.value().remove("2"); @@ -29,42 +29,42 @@ public List filter (FilterContext> context) { 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 +```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 +```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 +```java @TemplateComponent("tag3") public String tag3 (Parameter parameter) { return "
%s
".formatted(parameter.get("_content")); } -´´´ +``` ## 7.8.0