-
Notifications
You must be signed in to change notification settings - Fork 24
Spring boot 4 support #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,3 +48,6 @@ replay_pid* | |
| .springBeans | ||
| .sts4-cache | ||
| .dbeaver | ||
|
|
||
| # Claude | ||
| .claude | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| A UI dashboard extension for [db-scheduler](https://github.com/kagkarlsson/db-scheduler) — a Java library for persistent task scheduling. The UI provides monitoring and administration (view, run, delete, reschedule tasks) via a Spring Boot auto-configured web interface. | ||
|
|
||
| ## Build & Development Commands | ||
|
|
||
| ```bash | ||
| # Full build (compiles frontend + backend, runs tests, checks license headers) | ||
| ./mvnw clean install -q | ||
|
|
||
| # Run tests only | ||
| ./mvnw test -q | ||
|
|
||
| # Run a single test class | ||
| ./mvnw test -pl example-app -am -q -DskipTests && ./mvnw test -pl example-app -q -Dtest="SmokeTest" | ||
|
|
||
| # Format Java code (Google Java Format via Spotless — required before PR) | ||
| ./mvnw spotless:apply | ||
|
|
||
| # Sort pom.xml files | ||
| ./mvnw sortpom:sort | ||
|
|
||
| # Check license headers | ||
| ./mvnw license:check | ||
|
|
||
| # Add missing license headers | ||
| ./mvnw license:format | ||
|
|
||
| # Frontend dev server (proxies API to localhost:8081) | ||
| cd db-scheduler-ui-frontend && npm run dev | ||
|
|
||
| # Frontend lint | ||
| cd db-scheduler-ui-frontend && npm run lint | ||
| ``` | ||
|
|
||
| ## Module Architecture | ||
|
|
||
| Multi-module Maven project (`pom.xml` at root): | ||
|
|
||
| - **db-scheduler-ui** — Core library. REST controllers (`/db-scheduler-api/**`), service logic (`TaskLogic`, `LogLogic`), query/filter/sort utilities, and task-to-model mapping. Contains the bundled frontend in `src/main/resources/static/`. No Spring Boot auto-configuration here — just plain Spring `@RestController` beans. | ||
| - `TaskController` (GET endpoints: `/all`, `/details`, `/poll`) | ||
| - `TaskAdminController` (POST endpoints: `/rerun`, `/rerunGroup`, `/delete`) — disabled when `read-only=true` | ||
| - `LogController` — task execution history (requires `history=true`) | ||
| - `ConfigController` — exposes UI config (history enabled, read-only mode) | ||
| - `Caching` — in-memory cache of scheduler executions for polling/status changes | ||
|
|
||
| - **db-scheduler-ui-starter** — Spring Boot 3 auto-configuration (`UiApiAutoConfiguration`). Wires up all beans from `db-scheduler-ui`, handles context-path/servlet-path resolution, SPA fallback routing, and index.html rewriting. Properties class: `DbSchedulerUiProperties`. | ||
|
|
||
| - **db-scheduler-ui-spring-boot-4-starter** — Spring Boot 4 variant of the starter. Near-identical to the Boot 3 starter (only `WebMvcRegistrations` import differs due to Boot 4 package relocation). | ||
|
|
||
| - **db-scheduler-ui-frontend** — React/TypeScript SPA (Vite + Chakra UI + React Query). Built output is copied into `db-scheduler-ui/src/main/resources/static/db-scheduler/` during `mvn install`. Dev server runs on port 51373 and proxies `/db-scheduler-api` to `localhost:8081`. | ||
|
|
||
| - **example-app** — Spring Boot app with H2, sample tasks, and integration tests (smoke tests, read-only, Spring Security, context-path). Main class: `ExampleApp`. Runs on port 8081. | ||
|
|
||
| - **example-app-boot4** — Spring Boot 4 variant of the example app (uses Jackson 3/`tools.jackson` packages, `@AutoConfigureTestRestTemplate`, Boot 4 starter renames). | ||
|
|
||
| - **example-app-webflux** — WebFlux variant of the example app. | ||
|
|
||
geirsagberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## Key Patterns | ||
|
|
||
| - The frontend build is triggered during Maven's `generate-resources` phase via `exec-maven-plugin` (runs `npm install` and `npm run build` in `db-scheduler-ui-frontend/`), then output is copied to backend resources via `maven-resources-plugin`. | ||
| - Controllers are not annotated with `@Component` — they're instantiated as `@Bean` in `UiApiAutoConfiguration` so auto-configuration controls their lifecycle. | ||
| - Java code uses Lombok (`@Data`, `@Builder`, etc.) and Google Java Format style. | ||
| - All `.java`, `.ts`, `.tsx` source files (except tests) require Apache 2.0 license headers (enforced by `license-maven-plugin`). | ||
| - CI tests against Spring Boot 3.3, 3.4, 3.5, and 4.0 for compatibility. | ||
| - Frontend uses path alias `src` → `/src` (configured in vite.config.ts). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>no.bekk.db-scheduler-ui</groupId> | ||
| <artifactId>db-scheduler-ui-parent</artifactId> | ||
| <version>main-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>db-scheduler-ui-spring-boot-4-starter</artifactId> | ||
| <name>db-scheduler-ui-spring-boot-4-starter</name> | ||
| <description>Spring Boot 4 starter for db-scheduler-ui</description> | ||
| <url>https://github.com/bekk/db-scheduler-ui</url> | ||
|
|
||
| <dependencyManagement> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-dependencies</artifactId> | ||
| <version>${spring-boot-4.version}</version> | ||
| <type>pom</type> | ||
| <scope>import</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.github.kagkarlsson</groupId> | ||
| <artifactId>db-scheduler</artifactId> | ||
| <version>${db-scheduler-boot4.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.github.kagkarlsson</groupId> | ||
| <artifactId>db-scheduler-spring-boot-4-starter</artifactId> | ||
| <version>${db-scheduler-boot4.version}</version> | ||
| </dependency> | ||
| </dependencies> | ||
| </dependencyManagement> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-configuration-processor</artifactId> | ||
| <optional>true</optional> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.github.kagkarlsson</groupId> | ||
| <artifactId>db-scheduler-spring-boot-4-starter</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-webmvc</artifactId> | ||
| <optional>true</optional> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>no.bekk.db-scheduler-ui</groupId> | ||
| <artifactId>db-scheduler-ui</artifactId> | ||
| <version>${project.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-test</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> |
198 changes: 198 additions & 0 deletions
198
...ter/src/main/java/no/bekk/dbscheduler/uistarter/autoconfigure/UiApiAutoConfiguration.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| /* | ||
| * Copyright (C) Bekk | ||
| * | ||
| * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file | ||
| * except in compliance with the License. You may obtain a copy of the License at | ||
| * | ||
| * <p>http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * <p>Unless required by applicable law or agreed to in writing, software distributed under the | ||
| * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
| * express or implied. See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package no.bekk.dbscheduler.uistarter.autoconfigure; | ||
|
|
||
| import static no.bekk.dbscheduler.uistarter.config.DbSchedulerUiUtil.normalizePath; | ||
| import static no.bekk.dbscheduler.uistarter.config.DbSchedulerUiUtil.normalizePaths; | ||
|
|
||
| import com.github.kagkarlsson.scheduler.Scheduler; | ||
| import com.github.kagkarlsson.scheduler.boot.config.DbSchedulerCustomizer; | ||
| import com.github.kagkarlsson.scheduler.serializer.Serializer; | ||
| import java.io.IOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import javax.sql.DataSource; | ||
| import no.bekk.dbscheduler.ui.controller.ConfigController; | ||
| import no.bekk.dbscheduler.ui.controller.IndexHtmlController; | ||
| import no.bekk.dbscheduler.ui.controller.LogController; | ||
| import no.bekk.dbscheduler.ui.controller.SpaFallbackMvc; | ||
| import no.bekk.dbscheduler.ui.controller.TaskAdminController; | ||
| import no.bekk.dbscheduler.ui.controller.TaskController; | ||
| import no.bekk.dbscheduler.ui.service.LogLogic; | ||
| import no.bekk.dbscheduler.ui.service.TaskLogic; | ||
| import no.bekk.dbscheduler.ui.util.Caching; | ||
| import no.bekk.dbscheduler.uistarter.config.DbSchedulerUiProperties; | ||
| import no.bekk.dbscheduler.uistarter.config.DbSchedulerUiWebConfiguration; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.beans.factory.annotation.Qualifier; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; | ||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.core.io.ClassPathResource; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.web.reactive.function.server.RequestPredicates; | ||
| import org.springframework.web.reactive.function.server.RouterFunction; | ||
| import org.springframework.web.reactive.function.server.RouterFunctions; | ||
| import org.springframework.web.reactive.function.server.ServerResponse; | ||
|
|
||
| @AutoConfiguration | ||
| @ConditionalOnProperty(value = "db-scheduler-ui.enabled", matchIfMissing = true) | ||
| @EnableConfigurationProperties(DbSchedulerUiProperties.class) | ||
| public class UiApiAutoConfiguration { | ||
|
|
||
| private static final Logger logger = LoggerFactory.getLogger(UiApiAutoConfiguration.class); | ||
| private final String servletContextPath; | ||
| private final String mvcServletPath; | ||
| private final String dbSchedulerContextPath; | ||
|
|
||
| UiApiAutoConfiguration( | ||
| @Value("${server.servlet.context-path:}") String servletContextPath, | ||
| @Value("${spring.mvc.servlet.path:}") String mvcServletPath, | ||
| @Value("${db-scheduler-ui.context-path:}") String dbSchedulerContextPath) { | ||
| logger.info("UiApiAutoConfiguration created"); | ||
| this.servletContextPath = normalizePaths(servletContextPath); | ||
| this.dbSchedulerContextPath = normalizePaths(dbSchedulerContextPath); | ||
| this.mvcServletPath = normalizePaths(mvcServletPath); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| Caching caching() { | ||
| return new Caching(); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| TaskLogic taskLogic(Scheduler scheduler, Caching caching, DbSchedulerUiProperties properties) { | ||
| return new TaskLogic(scheduler, caching, properties.isTaskData()); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| @ConditionalOnProperty( | ||
| prefix = "db-scheduler-ui", | ||
| name = "history", | ||
| havingValue = "true", | ||
| matchIfMissing = false) | ||
| LogLogic logLogic( | ||
| DataSource dataSource, | ||
| Caching caching, | ||
| DbSchedulerCustomizer customizer, | ||
| DbSchedulerUiProperties properties, | ||
| @Value("${db-scheduler-log.table-name:scheduled_execution_logs}") String logTableName, | ||
| @Value("${db-scheduler-ui.log-limit:0}") int logLimit) { | ||
| return new LogLogic( | ||
| customizer.dataSource().orElse(dataSource), | ||
| customizer.serializer().orElse(Serializer.DEFAULT_JAVA_SERIALIZER), | ||
| caching, | ||
| properties.isTaskData(), | ||
| logTableName, | ||
| logLimit); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| @ConditionalOnProperty( | ||
| prefix = "db-scheduler-ui", | ||
| name = "read-only", | ||
| havingValue = "false", | ||
| matchIfMissing = true) | ||
| TaskAdminController taskAdminController(TaskLogic taskLogic) { | ||
| return new TaskAdminController(taskLogic); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| TaskController taskController(TaskLogic taskLogic) { | ||
| return new TaskController(taskLogic); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| @ConditionalOnProperty( | ||
| prefix = "db-scheduler-ui", | ||
| name = "history", | ||
| havingValue = "true", | ||
| matchIfMissing = false) | ||
| LogController logController(LogLogic logLogic) { | ||
| return new LogController(logLogic); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnWebApplication(type = Type.SERVLET) | ||
| @ConditionalOnMissingBean | ||
| SpaFallbackMvc spaFallbackMvc( | ||
| @Value("${db-scheduler-ui.context-path:}") String contextPath, | ||
| @Qualifier("indexHtml") String indexHtml) { | ||
| return new SpaFallbackMvc(normalizePath(contextPath), indexHtml); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnWebApplication(type = Type.REACTIVE) | ||
| @ConditionalOnMissingBean | ||
| public RouterFunction<ServerResponse> dbSchedulerRouter( | ||
| @Qualifier("indexHtml") String indexHtml) { | ||
| return RouterFunctions.route( | ||
| RequestPredicates.GET("/db-scheduler/**").and(request -> !request.path().contains(".")), | ||
| request -> ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(indexHtml)); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| ConfigController configController(DbSchedulerUiProperties properties) { | ||
| return new ConfigController(properties.isHistory(), properties::isReadOnly); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnMissingBean | ||
| IndexHtmlController indexHtmlController( | ||
| @Qualifier("indexHtml") String indexHtml, @Qualifier("contextPath") String contextPath) { | ||
| return new IndexHtmlController(indexHtml, contextPath); | ||
| } | ||
|
|
||
| @Bean | ||
| @ConditionalOnProperty(prefix = "db-scheduler-ui", name = "context-path") | ||
| DbSchedulerUiWebConfiguration dbSchedulerUiWebConfiguration( | ||
| @Value("${db-scheduler-ui.context-path:}") String contextPath) { | ||
| return new DbSchedulerUiWebConfiguration(normalizePath(contextPath)); | ||
| } | ||
|
|
||
| @Bean(name = "contextPath") | ||
| public String contextPath() { | ||
| return normalizePaths(servletContextPath, mvcServletPath, dbSchedulerContextPath); | ||
| } | ||
|
|
||
| @Bean(name = "indexHtml") | ||
| public String indexHtml(@Qualifier("contextPath") String contextPath) throws IOException { | ||
| String indexHtml = | ||
| new ClassPathResource(SpaFallbackMvc.DEFAULT_STARTING_PAGE) | ||
| .getContentAsString(StandardCharsets.UTF_8); | ||
|
|
||
| String contextPathScript = contextPath + "/db-scheduler/js/context-path.js"; | ||
|
|
||
| return indexHtml | ||
| .replaceAll("/db-scheduler", contextPath + "/db-scheduler") | ||
| .replaceAll( | ||
| "<head>", | ||
| """ | ||
| <head> | ||
| <script src='%s'></script>""" | ||
| .formatted(contextPathScript)); | ||
| } | ||
| } | ||
geirsagberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.