From 9238bb65a891884985738ba9626368e5b894ed40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2025 18:14:30 +0000 Subject: [PATCH 01/32] Bump org.springdoc:springdoc-openapi-starter-webmvc-ui Bumps [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) from 2.8.0 to 2.8.3. - [Release notes](https://github.com/springdoc/springdoc-openapi/releases) - [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md) - [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.8.0...v2.8.3) --- updated-dependencies: - dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ef520dc6..62b27dce8 100644 --- a/pom.xml +++ b/pom.xml @@ -525,7 +525,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.8.0 + 2.8.3 From 1297190a5f89db29dd0d06af5782c5f107bf4bef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2025 18:14:42 +0000 Subject: [PATCH 02/32] Bump jooq.version from 3.19.16 to 3.19.18 Bumps `jooq.version` from 3.19.16 to 3.19.18. Updates `org.jooq:jooq-meta` from 3.19.16 to 3.19.18 Updates `org.jooq:jooq-codegen` from 3.19.16 to 3.19.18 Updates `org.jooq:jooq` from 3.19.16 to 3.19.18 Updates `org.jooq:jooq-codegen-maven` from 3.19.16 to 3.19.18 --- updated-dependencies: - dependency-name: org.jooq:jooq-meta dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jooq:jooq-codegen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jooq:jooq dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jooq:jooq-codegen-maven dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ef520dc6..ea95bf31b 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 17 UTF-8 - 3.19.16 + 3.19.18 11.1.0 4.1.0 6.2.0 From 789369502029c9eb3006f331c71b4716391240dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 19 Jan 2025 23:52:39 +0100 Subject: [PATCH 03/32] fix swagger ui issues behind proxy https://stackoverflow.com/questions/60625494/ --- .../java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java index 2b1d65076..5c61b8a4a 100644 --- a/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; import org.springdoc.core.configuration.SpringDocConfiguration; import org.springdoc.core.properties.SwaggerUiConfigProperties; import org.springdoc.core.properties.SwaggerUiOAuthProperties; @@ -36,6 +37,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import java.util.List; + /** * https://stackoverflow.com/a/65557714 * @@ -87,6 +90,8 @@ public OpenAPI apiDocs() { ) .version(SteveConfiguration.CONFIG.getSteveVersion()) ) + // https://stackoverflow.com/a/68185254 + .servers(List.of(new Server().url("/").description("Default Server URL"))) // define a security schema .components(new Components().addSecuritySchemes(securityName, securityScheme)) // and activate it for all endpoints From 1a13cac577c8921eda1c2987196c74582a4e93f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 18:21:50 +0000 Subject: [PATCH 04/32] Bump org.jetbrains:annotations from 26.0.1 to 26.0.2 Bumps [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations) from 26.0.1 to 26.0.2. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/26.0.1...26.0.2) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ef520dc6..03501068a 100644 --- a/pom.xml +++ b/pom.xml @@ -546,7 +546,7 @@ org.jetbrains annotations - 26.0.1 + 26.0.2 compile From 73fd6f6dad2e4cc6cd60177bf8239475a5b175c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 18:21:56 +0000 Subject: [PATCH 05/32] Bump net.bytebuddy:byte-buddy from 1.15.11 to 1.16.1 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.15.11 to 1.16.1. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.11...byte-buddy-1.16.1) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ef520dc6..63ab62aa5 100644 --- a/pom.xml +++ b/pom.xml @@ -793,7 +793,7 @@ net.bytebuddy byte-buddy - 1.15.11 + 1.16.1 test From 25894b64e84a24170dd297686ed6d09a14433ad9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 18:22:08 +0000 Subject: [PATCH 06/32] Bump com.mysql:mysql-connector-j from 9.1.0 to 9.2.0 Bumps [com.mysql:mysql-connector-j](https://github.com/mysql/mysql-connector-j) from 9.1.0 to 9.2.0. - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/9.x/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/9.1.0...9.2.0) --- updated-dependencies: - dependency-name: com.mysql:mysql-connector-j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ef520dc6..0800c4a8a 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ 4.1.0 6.2.0 6.4.2 - 9.1.0 + 9.2.0 12.0.16 1.18.36 2.18.2 From 4703d1ca818935f0e2672f2cdce9b3ec00819992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 26 Jan 2025 14:27:19 +0100 Subject: [PATCH 07/32] add "note" to chargepoint and ocpptag queries --- .../steve/repository/impl/ChargePointRepositoryImpl.java | 4 ++++ .../steve/repository/impl/OcppTagRepositoryImpl.java | 5 +++++ .../de/rwth/idsg/steve/web/dto/ChargePointQueryForm.java | 6 ++++++ .../de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java | 9 +++++++++ .../webapp/WEB-INF/views/data-man/chargepoints.jsp | 4 ++++ .../resources/webapp/WEB-INF/views/data-man/ocppTags.jsp | 4 ++++ 6 files changed, 32 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java index b6af1ca6f..005fa0f86 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java @@ -158,6 +158,10 @@ private Result> getOverviewIn selectQuery.addConditions(includes(CHARGE_BOX.CHARGE_BOX_ID, form.getChargeBoxId())); } + if (form.isSetNote()) { + selectQuery.addConditions(includes(CHARGE_BOX.NOTE, form.getNote())); + } + switch (form.getHeartbeatPeriod()) { case ALL: break; diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java index 2f8a42d41..d1a3ccb7f 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.stream.Collectors; +import static de.rwth.idsg.steve.utils.CustomDSL.includes; import static de.rwth.idsg.steve.utils.DateTimeUtils.humanize; import static de.rwth.idsg.steve.utils.DateTimeUtils.toDateTime; import static jooq.steve.db.tables.OcppTag.OCPP_TAG; @@ -98,6 +99,10 @@ public List getOverview(OcppTagQueryForm form) { selectQuery.addConditions(OCPP_TAG_ACTIVITY.PARENT_ID_TAG.eq(form.getParentIdTag())); } + if (form.isNoteSet()) { + selectQuery.addConditions(includes(OCPP_TAG_ACTIVITY.NOTE, form.getNote())); + } + switch (form.getExpired()) { case ALL: break; diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointQueryForm.java index eb73fc3ec..8b703ca83 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointQueryForm.java @@ -18,6 +18,7 @@ */ package de.rwth.idsg.steve.web.dto; +import com.google.common.base.Strings; import de.rwth.idsg.steve.ocpp.OcppVersion; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -35,6 +36,7 @@ public class ChargePointQueryForm { private String chargeBoxId; private String description; + private String note; private OcppVersion ocppVersion; private QueryPeriodType heartbeatPeriod; @@ -57,6 +59,10 @@ public boolean isSetChargeBoxId() { return chargeBoxId != null; } + public boolean isSetNote() { + return !Strings.isNullOrEmpty(note); + } + @RequiredArgsConstructor public enum QueryPeriodType { ALL("All"), diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java index 90ce2b1a1..aa54ac737 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java @@ -18,6 +18,7 @@ */ package de.rwth.idsg.steve.web.dto; +import com.google.common.base.Strings; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -53,6 +54,9 @@ public class OcppTagQueryForm { @Schema(description = "Return blocked, not blocked, or all Ocpp tags? Defaults to ALL") private BooleanType blocked = BooleanType.FALSE; + @Schema(description = "Query by the note associated with the OCPP tag. The value of this field does not have to exactly match the note. A substring is also accepted.") + private String note; + @Schema(hidden = true) public boolean isOcppTagPkSet() { return ocppTagPk != null; @@ -68,6 +72,11 @@ public boolean isParentIdTagSet() { return parentIdTag != null; } + @Schema(hidden = true) + public boolean isNoteSet() { + return !Strings.isNullOrEmpty(note); + } + public BooleanType getExpired() { return Objects.requireNonNullElse(expired, BooleanType.ALL); } diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/chargepoints.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/chargepoints.jsp index 1c26cd3d6..989095314 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/chargepoints.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/chargepoints.jsp @@ -98,6 +98,10 @@ + + Note: + + diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/ocppTags.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/ocppTags.jsp index 5a0a9cdf1..0d4b5f59e 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/ocppTags.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/ocppTags.jsp @@ -112,6 +112,10 @@ + + Note: + + From d89ae17027df9ae5c7989691b7781e1039881f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 26 Jan 2025 14:41:35 +0100 Subject: [PATCH 08/32] refactor DTO class names reason: swagger UI cannot infer the proper schema, if there are overlapping inner classes with the same name, such as: - OcppTagQueryForm.ForApi - TransactionQueryForm.ForApi --- .../steve/repository/OcppTagRepository.java | 2 +- .../idsg/steve/repository/dto/OcppTag.java | 2 +- .../impl/OcppTagRepositoryImpl.java | 10 +++---- .../idsg/steve/service/OcppTagService.java | 3 +- .../steve/web/api/OcppTagsRestController.java | 20 ++++++------- .../web/api/TransactionsRestController.java | 2 +- .../idsg/steve/web/dto/OcppTagQueryForm.java | 4 +-- .../steve/web/dto/TransactionQueryForm.java | 4 +-- .../de/rwth/idsg/steve/issues/Issue1219.java | 2 +- .../web/api/OcppTagsRestControllerTest.java | 30 +++++++++---------- .../api/TransactionRestControllerTest.java | 8 ++--- 11 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java b/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java index f27b5f30a..c446ad21a 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java @@ -31,7 +31,7 @@ * @since 19.08.2014 */ public interface OcppTagRepository { - List getOverview(OcppTagQueryForm form); + List getOverview(OcppTagQueryForm form); Result getRecords(); Result getRecords(List idTagList); diff --git a/src/main/java/de/rwth/idsg/steve/repository/dto/OcppTag.java b/src/main/java/de/rwth/idsg/steve/repository/dto/OcppTag.java index f52909d17..638d09bb5 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/dto/OcppTag.java +++ b/src/main/java/de/rwth/idsg/steve/repository/dto/OcppTag.java @@ -35,7 +35,7 @@ public final class OcppTag { @Getter @Builder @ToString - public static final class Overview { + public static final class OcppTagOverview { @Schema(description = "PK of the OCPP tag") private final Integer ocppTagPk; diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java index d1a3ccb7f..95696f449 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java @@ -20,7 +20,7 @@ import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.repository.OcppTagRepository; -import de.rwth.idsg.steve.repository.dto.OcppTag.Overview; +import de.rwth.idsg.steve.repository.dto.OcppTag.OcppTagOverview; import de.rwth.idsg.steve.web.dto.OcppTagForm; import de.rwth.idsg.steve.web.dto.OcppTagQueryForm; import jooq.steve.db.tables.OcppTagActivity; @@ -66,7 +66,7 @@ public OcppTagRepositoryImpl(DSLContext ctx) { @Override @SuppressWarnings("unchecked") - public List getOverview(OcppTagQueryForm form) { + public List getOverview(OcppTagQueryForm form) { SelectQuery selectQuery = ctx.selectQuery(); selectQuery.addFrom(OCPP_TAG_ACTIVITY); @@ -258,10 +258,10 @@ private void processBooleanType(SelectQuery selectQuery, } private static class UserMapper - implements RecordMapper, Overview> { + implements RecordMapper, OcppTagOverview> { @Override - public Overview map(Record10 r) { - return Overview.builder() + public OcppTagOverview map(Record10 r) { + return OcppTagOverview.builder() .ocppTagPk(r.value1()) .parentOcppTagPk(r.value2()) .idTag(r.value3()) diff --git a/src/main/java/de/rwth/idsg/steve/service/OcppTagService.java b/src/main/java/de/rwth/idsg/steve/service/OcppTagService.java index fe029ca4d..5bfcb5138 100644 --- a/src/main/java/de/rwth/idsg/steve/service/OcppTagService.java +++ b/src/main/java/de/rwth/idsg/steve/service/OcppTagService.java @@ -22,7 +22,6 @@ import static de.rwth.idsg.steve.utils.OcppTagActivityRecordUtils.isExpired; import com.google.common.base.Strings; -import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.repository.OcppTagRepository; import de.rwth.idsg.steve.repository.dto.OcppTag; import de.rwth.idsg.steve.service.dto.UnidentifiedIncomingObject; @@ -56,7 +55,7 @@ public class OcppTagService { private final OcppTagRepository ocppTagRepository; private final AuthTagService authTagService; - public List getOverview(OcppTagQueryForm form) { + public List getOverview(OcppTagQueryForm form) { return ocppTagRepository.getOverview(form); } diff --git a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java index 258e8f141..3da064ea6 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java @@ -19,11 +19,11 @@ package de.rwth.idsg.steve.web.api; import de.rwth.idsg.steve.SteveException; -import de.rwth.idsg.steve.repository.dto.OcppTag; +import de.rwth.idsg.steve.repository.dto.OcppTag.OcppTagOverview; import de.rwth.idsg.steve.service.OcppTagService; import de.rwth.idsg.steve.web.api.ApiControllerAdvice.ApiErrorResponse; import de.rwth.idsg.steve.web.dto.OcppTagForm; -import de.rwth.idsg.steve.web.dto.OcppTagQueryForm; +import de.rwth.idsg.steve.web.dto.OcppTagQueryForm.OcppTagQueryFormForApi; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -80,7 +80,7 @@ public class OcppTagsRestController { ) @GetMapping(value = "") @ResponseBody - public List get(OcppTagQueryForm.ForApi params) { + public List get(OcppTagQueryFormForApi params) { log.debug("Read request for query: {}", params); var response = ocppTagService.getOverview(params); @@ -100,7 +100,7 @@ public List get(OcppTagQueryForm.ForApi params) { ) @GetMapping("/{ocppTagPk}") @ResponseBody - public OcppTag.Overview getOne(@PathVariable("ocppTagPk") Integer ocppTagPk) { + public OcppTagOverview getOne(@PathVariable("ocppTagPk") Integer ocppTagPk) { log.debug("Read request for ocppTagPk: {}", ocppTagPk); var response = getOneInternal(ocppTagPk); @@ -123,7 +123,7 @@ public OcppTag.Overview getOne(@PathVariable("ocppTagPk") Integer ocppTagPk) { @PostMapping @ResponseBody @ResponseStatus(HttpStatus.CREATED) - public OcppTag.Overview create(@RequestBody @Valid OcppTagForm params) { + public OcppTagOverview create(@RequestBody @Valid OcppTagForm params) { log.debug("Create request: {}", params); int ocppTagPk = ocppTagService.addOcppTag(params); @@ -145,7 +145,7 @@ public OcppTag.Overview create(@RequestBody @Valid OcppTagForm params) { ) @PutMapping("/{ocppTagPk}") @ResponseBody - public OcppTag.Overview update(@PathVariable("ocppTagPk") Integer ocppTagPk, @RequestBody @Valid OcppTagForm params) { + public OcppTagOverview update(@PathVariable("ocppTagPk") Integer ocppTagPk, @RequestBody @Valid OcppTagForm params) { params.setOcppTagPk(ocppTagPk); // the one from incoming params does not matter log.debug("Update request: {}", params); @@ -169,7 +169,7 @@ public OcppTag.Overview update(@PathVariable("ocppTagPk") Integer ocppTagPk, @Re ) @DeleteMapping("/{ocppTagPk}") @ResponseBody - public OcppTag.Overview delete(@PathVariable("ocppTagPk") Integer ocppTagPk) { + public OcppTagOverview delete(@PathVariable("ocppTagPk") Integer ocppTagPk) { log.debug("Delete request for ocppTagPk: {}", ocppTagPk); var response = getOneInternal(ocppTagPk); @@ -179,11 +179,11 @@ public OcppTag.Overview delete(@PathVariable("ocppTagPk") Integer ocppTagPk) { return response; } - private OcppTag.Overview getOneInternal(int ocppTagPk) { - OcppTagQueryForm.ForApi params = new OcppTagQueryForm.ForApi(); + private OcppTagOverview getOneInternal(int ocppTagPk) { + OcppTagQueryFormForApi params = new OcppTagQueryFormForApi(); params.setOcppTagPk(ocppTagPk); - List results = ocppTagService.getOverview(params); + List results = ocppTagService.getOverview(params); if (results.isEmpty()) { throw new SteveException.NotFound("Could not find this ocppTag"); } diff --git a/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java index f92f340c6..cdf13c6d1 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java @@ -70,7 +70,7 @@ public class TransactionsRestController { ) @GetMapping(value = "") @ResponseBody - public List get(@Valid TransactionQueryForm.ForApi params) { + public List get(@Valid TransactionQueryForm.TransactionQueryFormForApi params) { log.debug("Read request for query: {}", params); if (params.isReturnCSV()) { diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java index aa54ac737..dd4e3eaf9 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/OcppTagQueryForm.java @@ -116,9 +116,9 @@ public static BooleanType fromValue(String v) { } @ToString(callSuper = true) - public static class ForApi extends OcppTagQueryForm { + public static class OcppTagQueryFormForApi extends OcppTagQueryForm { - public ForApi () { + public OcppTagQueryFormForApi() { super(); setExpired(BooleanType.ALL); setInTransaction(BooleanType.ALL); diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java index d750928ad..c6d29d816 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java @@ -119,9 +119,9 @@ public static QueryPeriodType fromValue(String v) { } @ToString(callSuper = true) - public static class ForApi extends TransactionQueryForm { + public static class TransactionQueryFormForApi extends TransactionQueryForm { - public ForApi() { + public TransactionQueryFormForApi() { super(); setType(QueryType.ALL); setPeriodType(QueryPeriodType.ALL); diff --git a/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java b/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java index 6546fc86d..21626ed8c 100644 --- a/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java +++ b/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java @@ -94,7 +94,7 @@ private void realTest() { var repository = new OcppTagRepositoryImpl(ctx); long start = System.currentTimeMillis(); - List values = repository.getOverview(new OcppTagQueryForm()); + List values = repository.getOverview(new OcppTagQueryForm()); long stop = System.currentTimeMillis(); System.out.println("took " + Duration.millis(stop - start)); diff --git a/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java index 05781f34e..c5d9d3042 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java @@ -87,7 +87,7 @@ public void setup() { @DisplayName("GET all: Test with empty results, expected 200") public void test1() throws Exception { // given - List results = Collections.emptyList(); + List results = Collections.emptyList(); // when when(ocppTagService.getOverview(any())).thenReturn(results); @@ -102,7 +102,7 @@ public void test1() throws Exception { @DisplayName("GET all: Test with one result, expected 200") public void test2() throws Exception { // given - List results = List.of(OcppTag.Overview.builder().ocppTagPk(96).build()); + List results = List.of(OcppTag.OcppTagOverview.builder().ocppTagPk(96).build()); // when when(ocppTagService.getOverview(any())).thenReturn(results); @@ -141,7 +141,7 @@ public void test4() throws Exception { public void test5() throws Exception { // given DateTime someDate = DateTime.parse("2020-10-01T00:00:00.000Z"); - OcppTag.Overview result = OcppTag.Overview.builder() + OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder() .ocppTagPk(121) .idTag("id-1") .parentOcppTagPk(454) @@ -206,7 +206,7 @@ public void test7() throws Exception { @DisplayName("GET one: One entity found, expected 200") public void test8() throws Exception { // given - OcppTag.Overview result = OcppTag.Overview.builder().ocppTagPk(12).build(); + OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder().ocppTagPk(12).build(); // when when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); @@ -265,7 +265,7 @@ public void test11() throws Exception { OcppTagForm form = new OcppTagForm(); form.setIdTag("id-123"); - OcppTag.Overview result = OcppTag.Overview.builder() + OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder() .ocppTagPk(ocppTagPk) .idTag(form.getIdTag()) .build(); @@ -296,7 +296,7 @@ public void test12() throws Exception { form.setIdTag("id-123"); form.setNote("note-1"); - OcppTag.Overview result = OcppTag.Overview.builder() + OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder() .ocppTagPk(ocppTagPk) .idTag(form.getIdTag()) .note(form.getNote()) @@ -367,7 +367,7 @@ public void test15() throws Exception { // given int ocppTagPk = 123; - OcppTag.Overview result = OcppTag.Overview.builder() + OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder() .ocppTagPk(ocppTagPk) .idTag("id-123") .note("note-2") @@ -390,7 +390,7 @@ public void test16() throws Exception { // given int ocppTagPk = 123; - OcppTag.Overview result = OcppTag.Overview.builder() + OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder() .ocppTagPk(ocppTagPk) .idTag("id-123") .note("note-2") @@ -445,14 +445,14 @@ public void test18() throws Exception { .andExpectAll(errorJsonMatchers()); verify(ocppTagService, times(0)).removeUnknown(anyList()); - verify(ocppTagService, times(0)).getOverview(any(OcppTagQueryForm.ForApi.class)); + verify(ocppTagService, times(0)).getOverview(any(OcppTagQueryForm.OcppTagQueryFormForApi.class)); } @Test @DisplayName("GET all: Query param 'expired' is translated correctly, while others are defaulted") public void test19() throws Exception { // given - ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.ForApi.class); + ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); // when when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); @@ -463,7 +463,7 @@ public void test19() throws Exception { .andExpect(status().isOk()); verify(ocppTagService).getOverview(formToCapture.capture()); - OcppTagQueryForm.ForApi capturedForm = formToCapture.getValue(); + OcppTagQueryForm.OcppTagQueryFormForApi capturedForm = formToCapture.getValue(); assertEquals(capturedForm.getExpired(), OcppTagQueryForm.BooleanType.FALSE); assertEquals(capturedForm.getInTransaction(), OcppTagQueryForm.BooleanType.ALL); @@ -474,7 +474,7 @@ public void test19() throws Exception { @DisplayName("GET all: Query param 'inTransaction' is translated correctly, while others are defaulted") public void test20() throws Exception { // given - ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.ForApi.class); + ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); // when when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); @@ -485,7 +485,7 @@ public void test20() throws Exception { .andExpect(status().isOk()); verify(ocppTagService).getOverview(formToCapture.capture()); - OcppTagQueryForm.ForApi capturedForm = formToCapture.getValue(); + OcppTagQueryForm.OcppTagQueryFormForApi capturedForm = formToCapture.getValue(); assertEquals(capturedForm.getExpired(), OcppTagQueryForm.BooleanType.ALL); assertEquals(capturedForm.getInTransaction(), OcppTagQueryForm.BooleanType.TRUE); @@ -496,7 +496,7 @@ public void test20() throws Exception { @DisplayName("GET all: Query param 'inTransaction' is translated correctly, while others are defaulted") public void test21() throws Exception { // given - ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.ForApi.class); + ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); // when when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); @@ -507,7 +507,7 @@ public void test21() throws Exception { .andExpect(status().isOk()); verify(ocppTagService).getOverview(formToCapture.capture()); - OcppTagQueryForm.ForApi capturedForm = formToCapture.getValue(); + OcppTagQueryForm.OcppTagQueryFormForApi capturedForm = formToCapture.getValue(); assertEquals(capturedForm.getExpired(), OcppTagQueryForm.BooleanType.ALL); assertEquals(capturedForm.getInTransaction(), OcppTagQueryForm.BooleanType.ALL); diff --git a/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java index 9f6fafdff..386fbf0e4 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java @@ -213,7 +213,7 @@ public void test9() throws Exception { @DisplayName("GET all: Query param 'type' is translated correctly, while others are defaulted") public void test10() throws Exception { // given - ArgumentCaptor formToCapture = ArgumentCaptor.forClass(TransactionQueryForm.ForApi.class); + ArgumentCaptor formToCapture = ArgumentCaptor.forClass(TransactionQueryForm.TransactionQueryFormForApi.class); // when when(transactionRepository.getTransactions(any())).thenReturn(Collections.emptyList()); @@ -224,7 +224,7 @@ public void test10() throws Exception { .andExpect(status().isOk()); verify(transactionRepository).getTransactions(formToCapture.capture()); - TransactionQueryForm.ForApi capturedForm = formToCapture.getValue(); + TransactionQueryForm.TransactionQueryFormForApi capturedForm = formToCapture.getValue(); assertEquals(capturedForm.getType(), TransactionQueryForm.QueryType.ACTIVE); assertEquals(capturedForm.getPeriodType(), TransactionQueryForm.QueryPeriodType.ALL); @@ -234,7 +234,7 @@ public void test10() throws Exception { @DisplayName("GET all: Query param 'periodType' is translated correctly, while others are defaulted") public void test11() throws Exception { // given - ArgumentCaptor formToCapture = ArgumentCaptor.forClass(TransactionQueryForm.ForApi.class); + ArgumentCaptor formToCapture = ArgumentCaptor.forClass(TransactionQueryForm.TransactionQueryFormForApi.class); // when when(transactionRepository.getTransactions(any())).thenReturn(Collections.emptyList()); @@ -245,7 +245,7 @@ public void test11() throws Exception { .andExpect(status().isOk()); verify(transactionRepository).getTransactions(formToCapture.capture()); - TransactionQueryForm.ForApi capturedForm = formToCapture.getValue(); + TransactionQueryForm.TransactionQueryFormForApi capturedForm = formToCapture.getValue(); assertEquals(capturedForm.getType(), TransactionQueryForm.QueryType.ALL); assertEquals(capturedForm.getPeriodType(), TransactionQueryForm.QueryPeriodType.LAST_30); From ac12b1e4f5060d36beb2f9f62779f96e7b39a8c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:11:38 +0000 Subject: [PATCH 09/32] Bump flyway.version from 11.1.0 to 11.2.0 Bumps `flyway.version` from 11.1.0 to 11.2.0. Updates `org.flywaydb:flyway-mysql` from 11.1.0 to 11.2.0 Updates `org.flywaydb:flyway-maven-plugin` from 11.1.0 to 11.2.0 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.1.0...flyway-11.2.0) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.flywaydb:flyway-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e58cf3142..20d565498 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ UTF-8 3.19.18 - 11.1.0 + 11.2.0 4.1.0 6.2.0 6.4.2 From 227d77cb0365c8877d217d7d3bdf6c963dfd7752 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:35:08 +0000 Subject: [PATCH 10/32] Bump net.bytebuddy:byte-buddy from 1.16.1 to 1.17.0 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.16.1 to 1.17.0. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.16.1...byte-buddy-1.17.0) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 20d565498..606287bb4 100644 --- a/pom.xml +++ b/pom.xml @@ -793,7 +793,7 @@ net.bytebuddy byte-buddy - 1.16.1 + 1.17.0 test From 246c6cdfc70f35a640a80c59b94c63460de68f03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:35:20 +0000 Subject: [PATCH 11/32] Bump org.springdoc:springdoc-openapi-starter-webmvc-ui Bumps [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) from 2.8.3 to 2.8.4. - [Release notes](https://github.com/springdoc/springdoc-openapi/releases) - [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md) - [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.8.3...v2.8.4) --- updated-dependencies: - dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 20d565498..3ab7ce4d2 100644 --- a/pom.xml +++ b/pom.xml @@ -525,7 +525,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.8.3 + 2.8.4 From 4ccae1013e76d98cff466d7544209f2f891c043d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:35:29 +0000 Subject: [PATCH 12/32] Bump flyway.version from 11.2.0 to 11.3.0 Bumps `flyway.version` from 11.2.0 to 11.3.0. Updates `org.flywaydb:flyway-mysql` from 11.2.0 to 11.3.0 Updates `org.flywaydb:flyway-maven-plugin` from 11.2.0 to 11.3.0 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.2.0...flyway-11.3.0) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.flywaydb:flyway-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 20d565498..93b126650 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ UTF-8 3.19.18 - 11.2.0 + 11.3.0 4.1.0 6.2.0 6.4.2 From 5434ac7f653366635ede47c8a2db15668d3c6fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 2 Feb 2025 12:23:36 +0100 Subject: [PATCH 13/32] refactor task/job executors * migrate from java's executor impl to spring's abstraction: let spring handle the bean lifecycle, graceful shutdown etc. * separate on interface-level between scheduled and async tasks: we only have 1 use case for scheduled tasks (websocket ping-pongs), whereas all other usages of ScheduledExecutorService were just async job submissions. separation might be useful in future, if we want to have distinct thread pools as well. --- .../idsg/steve/config/BeanConfiguration.java | 50 ++++--------------- .../steve/config/WebSocketConfiguration.java | 2 +- .../ocpp/soap/MessageHeaderInterceptor.java | 6 +-- .../ocpp/ws/AbstractWebSocketEndpoint.java | 14 +++--- .../idsg/steve/service/BackgroundService.java | 12 ++--- .../service/ChargePointServiceClient.java | 43 ++++++++-------- .../rwth/idsg/steve/service/MailService.java | 6 +-- 7 files changed, 52 insertions(+), 81 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java index 12d0d885b..4ab2ef3fa 100644 --- a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.mysql.cj.conf.PropertyKey; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -45,6 +44,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -55,16 +55,11 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; -import jakarta.annotation.PreDestroy; import jakarta.validation.Validator; import javax.sql.DataSource; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.Executor; import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; @@ -81,8 +76,6 @@ @ComponentScan("de.rwth.idsg.steve") public class BeanConfiguration implements WebMvcConfigurer { - private ScheduledThreadPoolExecutor executor; - /** * https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration */ @@ -144,13 +137,15 @@ public DSLContext dslContext(DataSource dataSource) { return DSL.using(conf); } - @Bean - public ScheduledExecutorService scheduledExecutorService() { - ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("SteVe-Executor-%d") - .build(); - - executor = new ScheduledThreadPoolExecutor(5, threadFactory); - return executor; + @Bean(name = {"asyncTaskScheduler", "asyncTaskExecutor"}) + public ThreadPoolTaskScheduler asyncTaskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(5); + scheduler.setThreadNamePrefix("SteVe-Executor-"); + scheduler.setWaitForTasksToCompleteOnShutdown(true); + scheduler.setAwaitTerminationSeconds(30); + scheduler.initialize(); + return scheduler; } @Bean @@ -173,29 +168,6 @@ public ReleaseCheckService releaseCheckService() { } } - @PreDestroy - public void shutDown() { - if (executor != null) { - gracefulShutDown(executor); - } - } - - private void gracefulShutDown(ExecutorService executor) { - try { - executor.shutdown(); - executor.awaitTermination(30, TimeUnit.SECONDS); - - } catch (InterruptedException e) { - log.error("Termination interrupted", e); - - } finally { - if (!executor.isTerminated()) { - log.warn("Killing non-finished tasks"); - } - executor.shutdownNow(); - } - } - // ------------------------------------------------------------------------- // Web config // ------------------------------------------------------------------------- diff --git a/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java index 917304f9b..e93d64a63 100644 --- a/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java @@ -51,7 +51,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer { @Autowired private Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint; public static final String PATH_INFIX = "/websocket/CentralSystemService/"; - public static final long PING_INTERVAL = TimeUnit.MINUTES.toMinutes(15); + public static final Duration PING_INTERVAL = Duration.ofMinutes(15); public static final Duration IDLE_TIMEOUT = Duration.ofHours(2); public static final int MAX_MSG_SIZE = 8_388_608; // 8 MB for max message size diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java index bb61dda49..498fdb2ac 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java @@ -41,7 +41,7 @@ import javax.xml.namespace.QName; import java.util.Optional; -import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Executor; import static org.apache.cxf.ws.addressing.JAXWSAConstants.ADDRESSING_PROPERTIES_INBOUND; @@ -62,7 +62,7 @@ public class MessageHeaderInterceptor extends AbstractPhaseInterceptor @Autowired private OcppServerRepository ocppServerRepository; @Autowired private ChargePointHelperService chargePointHelperService; - @Autowired private ScheduledExecutorService executorService; + @Autowired private Executor asyncTaskExecutor; private static final String BOOT_OPERATION_NAME = "BootNotification"; private static final String CHARGEBOX_ID_HEADER = "ChargeBoxIdentity"; @@ -93,7 +93,7 @@ public void handleMessage(Message message) throws Fault { // 2. update endpoint // ------------------------------------------------------------------------- - executorService.execute(() -> { + asyncTaskExecutor.execute(() -> { try { String endpointAddress = getEndpointAddress(message); if (endpointAddress != null) { diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java index c8e266340..0d1929d4b 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java @@ -31,6 +31,7 @@ import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.PongMessage; @@ -39,14 +40,13 @@ import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** @@ -55,7 +55,7 @@ */ public abstract class AbstractWebSocketEndpoint extends ConcurrentWebSocketHandler implements SubProtocolCapable { - @Autowired private ScheduledExecutorService service; + @Autowired private ThreadPoolTaskScheduler asyncTaskScheduler; @Autowired private OcppServerRepository ocppServerRepository; @Autowired private FutureResponseContextStore futureResponseContextStore; @Autowired private ApplicationEventPublisher applicationEventPublisher; @@ -131,11 +131,11 @@ public void onOpen(WebSocketSession session) throws Exception { // Just to keep the connection alive, such that the servers do not close // the connection because of a idle timeout, we ping-pong at fixed intervals. - ScheduledFuture pingSchedule = service.scheduleAtFixedRate( + ScheduledFuture pingSchedule = asyncTaskScheduler.scheduleAtFixedRate( new PingTask(chargeBoxId, session), - WebSocketConfiguration.PING_INTERVAL, - WebSocketConfiguration.PING_INTERVAL, - TimeUnit.MINUTES); + Instant.now().plus(WebSocketConfiguration.PING_INTERVAL), + WebSocketConfiguration.PING_INTERVAL + ); futureResponseContextStore.addSession(session); diff --git a/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java b/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java index 13857de2d..4b11b11de 100644 --- a/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java +++ b/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java @@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor; import java.util.List; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -32,10 +32,10 @@ */ @RequiredArgsConstructor public class BackgroundService { - private final ExecutorService executorService; + private final Executor asyncTaskExecutor; - public static BackgroundService with(ExecutorService executorService) { - return new BackgroundService(executorService); + public static BackgroundService with(Executor asyncTaskExecutor) { + return new BackgroundService(asyncTaskExecutor); } public Runner forFirst(List list) { @@ -56,7 +56,7 @@ private class BackgroundSingleRunner implements Runner { @Override public void execute(Consumer consumer) { - executorService.execute(() -> consumer.accept(cps)); + asyncTaskExecutor.execute(() -> consumer.accept(cps)); } } @@ -66,7 +66,7 @@ private class BackgroundListRunner implements Runner { @Override public void execute(Consumer consumer) { - executorService.execute(() -> list.forEach(consumer)); + asyncTaskExecutor.execute(() -> list.forEach(consumer)); } } } diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java index 32633f3da..f85f7420c 100644 --- a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java @@ -21,7 +21,6 @@ import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.ocpp.ChargePointServiceInvokerImpl; import de.rwth.idsg.steve.ocpp.OcppCallback; -import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.ocpp.task.CancelReservationTask; import de.rwth.idsg.steve.ocpp.task.ChangeAvailabilityTask; import de.rwth.idsg.steve.ocpp.task.ChangeConfigurationTask; @@ -75,7 +74,7 @@ import org.springframework.stereotype.Service; import java.util.List; -import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Executor; /** * @author Sevket Goekay @@ -90,7 +89,7 @@ public class ChargePointServiceClient { private final ReservationRepository reservationRepository; private final OcppTagService ocppTagService; - private final ScheduledExecutorService executorService; + private final Executor asyncTaskExecutor; private final TaskStore taskStore; private final ChargePointServiceInvokerImpl invoker; @@ -107,7 +106,7 @@ public final int changeAvailability(ChangeAvailabilityParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.changeAvailability(c, task)); @@ -123,7 +122,7 @@ public final int changeConfiguration(ChangeConfigurationParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.changeConfiguration(c, task)); @@ -139,7 +138,7 @@ public final int clearCache(MultipleChargePointSelect params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.clearCache(c, task)); @@ -155,7 +154,7 @@ public final int getDiagnostics(GetDiagnosticsParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.getDiagnostics(c, task)); @@ -171,7 +170,7 @@ public final int reset(ResetParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.reset(c, task)); @@ -187,7 +186,7 @@ public final int updateFirmware(UpdateFirmwareParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.updateFirmware(c, task)); @@ -207,7 +206,7 @@ public final int remoteStartTransaction(RemoteStartTransactionParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forFirst(task.getParams().getChargePointSelectList()) .execute(c -> invoker.remoteStartTransaction(c, task)); @@ -223,7 +222,7 @@ public final int remoteStopTransaction(RemoteStopTransactionParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forFirst(task.getParams().getChargePointSelectList()) .execute(c -> invoker.remoteStopTransaction(c, task)); @@ -239,7 +238,7 @@ public final int unlockConnector(UnlockConnectorParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forFirst(task.getParams().getChargePointSelectList()) .execute(c -> invoker.unlockConnector(c, task)); @@ -259,7 +258,7 @@ public final int dataTransfer(DataTransferParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.dataTransfer(c, task)); @@ -275,7 +274,7 @@ public final int getConfiguration(GetConfigurationParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.getConfiguration(c, task)); @@ -291,7 +290,7 @@ public final int getLocalListVersion(MultipleChargePointSelect params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.getLocalListVersion(c, task)); @@ -307,7 +306,7 @@ public final int sendLocalList(SendLocalListParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.sendLocalList(c, task)); @@ -341,7 +340,7 @@ public final int reserveNow(ReserveNowParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forFirst(task.getParams().getChargePointSelectList()) .execute(c -> invoker.reserveNow(c, task)); @@ -357,7 +356,7 @@ public final int cancelReservation(CancelReservationParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forFirst(task.getParams().getChargePointSelectList()) .execute(c -> invoker.cancelReservation(c, task)); @@ -377,7 +376,7 @@ public final int triggerMessage(TriggerMessageParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.triggerMessage(c, task)); @@ -398,7 +397,7 @@ public final int setChargingProfile(SetChargingProfileParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.setChargingProfile(c, task)); @@ -414,7 +413,7 @@ public final int clearChargingProfile(ClearChargingProfileParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.clearChargingProfile(c, task)); @@ -430,7 +429,7 @@ public final int getCompositeSchedule(GetCompositeScheduleParams params, task.addCallback(callback); } - BackgroundService.with(executorService) + BackgroundService.with(asyncTaskExecutor) .forEach(task.getParams().getChargePointSelectList()) .execute(c -> invoker.getCompositeSchedule(c, task)); diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index e5c87c623..c8101a261 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -36,7 +36,7 @@ import jakarta.mail.internet.MimeMessage; import java.util.Properties; -import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Executor; /** * @author Sevket Goekay @@ -47,7 +47,7 @@ public class MailService { @Autowired private SettingsRepository settingsRepository; - @Autowired private ScheduledExecutorService executorService; + @Autowired private Executor asyncTaskExecutor; public MailSettings getSettings() { return settingsRepository.getMailSettings(); @@ -62,7 +62,7 @@ public void sendTestMail() { } public void sendAsync(String subject, String body) { - executorService.execute(() -> { + asyncTaskExecutor.execute(() -> { try { send(subject, body); } catch (MessagingException e) { From 535d87c749e6811ebaf133f7225fa75a6a6dca9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 2 Feb 2025 15:59:49 +0100 Subject: [PATCH 14/32] separate TaskExecutor and TaskScheduler usage --- .../idsg/steve/config/BeanConfiguration.java | 23 +++++++-- .../steve/config/DelegatingTaskExecutor.java | 48 +++++++++++++++++ .../steve/config/DelegatingTaskScheduler.java | 51 +++++++++++++++++++ .../ocpp/soap/MessageHeaderInterceptor.java | 4 +- .../ocpp/ws/AbstractWebSocketEndpoint.java | 4 +- .../idsg/steve/service/BackgroundService.java | 7 +-- .../service/ChargePointServiceClient.java | 4 +- .../rwth/idsg/steve/service/MailService.java | 4 +- 8 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/config/DelegatingTaskExecutor.java create mode 100644 src/main/java/de/rwth/idsg/steve/config/DelegatingTaskScheduler.java diff --git a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java index 4ab2ef3fa..6f0f39a42 100644 --- a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java @@ -44,6 +44,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.accept.ContentNegotiationManager; @@ -59,7 +60,6 @@ import javax.sql.DataSource; import java.util.List; -import java.util.concurrent.Executor; import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; @@ -137,15 +137,28 @@ public DSLContext dslContext(DataSource dataSource) { return DSL.using(conf); } - @Bean(name = {"asyncTaskScheduler", "asyncTaskExecutor"}) - public ThreadPoolTaskScheduler asyncTaskScheduler() { + @Bean(destroyMethod = "close") + public DelegatingTaskScheduler asyncTaskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(5); - scheduler.setThreadNamePrefix("SteVe-Executor-"); + scheduler.setThreadNamePrefix("SteVe-TaskScheduler-"); scheduler.setWaitForTasksToCompleteOnShutdown(true); scheduler.setAwaitTerminationSeconds(30); scheduler.initialize(); - return scheduler; + + return new DelegatingTaskScheduler(scheduler); + } + + @Bean(destroyMethod = "close") + public DelegatingTaskExecutor asyncTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setThreadNamePrefix("SteVe-TaskExecutor-"); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAwaitTerminationSeconds(30); + executor.initialize(); + + return new DelegatingTaskExecutor(executor); } @Bean diff --git a/src/main/java/de/rwth/idsg/steve/config/DelegatingTaskExecutor.java b/src/main/java/de/rwth/idsg/steve/config/DelegatingTaskExecutor.java new file mode 100644 index 000000000..0defde9d5 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/config/DelegatingTaskExecutor.java @@ -0,0 +1,48 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * 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 . + */ +package de.rwth.idsg.steve.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.io.Closeable; +import java.io.IOException; + +/** + * @author Sevket Goekay + * @since 02.02.2025 + */ +@Slf4j +@RequiredArgsConstructor +public class DelegatingTaskExecutor implements Closeable { + + private final ThreadPoolTaskExecutor delegate; + + @Override + public void close() throws IOException { + log.info("Shutting down"); + delegate.shutdown(); + } + + public void execute(Runnable task) { + delegate.execute(task); + } + +} diff --git a/src/main/java/de/rwth/idsg/steve/config/DelegatingTaskScheduler.java b/src/main/java/de/rwth/idsg/steve/config/DelegatingTaskScheduler.java new file mode 100644 index 000000000..5f6110413 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/config/DelegatingTaskScheduler.java @@ -0,0 +1,51 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * 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 . + */ +package de.rwth.idsg.steve.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +import java.io.Closeable; +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.ScheduledFuture; + +/** + * @author Sevket Goekay + * @since 02.02.2025 + */ +@Slf4j +@RequiredArgsConstructor +public class DelegatingTaskScheduler implements Closeable { + + private final ThreadPoolTaskScheduler delegate; + + @Override + public void close() throws IOException { + log.info("Shutting down"); + delegate.shutdown(); + } + + public ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { + return delegate.scheduleAtFixedRate(task, startTime, period); + } + +} diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java index 498fdb2ac..d0b3040aa 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java @@ -18,6 +18,7 @@ */ package de.rwth.idsg.steve.ocpp.soap; +import de.rwth.idsg.steve.config.DelegatingTaskExecutor; import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.repository.OcppServerRepository; import de.rwth.idsg.steve.repository.impl.ChargePointRepositoryImpl; @@ -41,7 +42,6 @@ import javax.xml.namespace.QName; import java.util.Optional; -import java.util.concurrent.Executor; import static org.apache.cxf.ws.addressing.JAXWSAConstants.ADDRESSING_PROPERTIES_INBOUND; @@ -62,7 +62,7 @@ public class MessageHeaderInterceptor extends AbstractPhaseInterceptor @Autowired private OcppServerRepository ocppServerRepository; @Autowired private ChargePointHelperService chargePointHelperService; - @Autowired private Executor asyncTaskExecutor; + @Autowired private DelegatingTaskExecutor asyncTaskExecutor; private static final String BOOT_OPERATION_NAME = "BootNotification"; private static final String CHARGEBOX_ID_HEADER = "ChargeBoxIdentity"; diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java index 0d1929d4b..5f4782578 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java @@ -20,6 +20,7 @@ import com.google.common.base.Strings; import de.rwth.idsg.steve.config.WebSocketConfiguration; +import de.rwth.idsg.steve.config.DelegatingTaskScheduler; import de.rwth.idsg.steve.ocpp.OcppTransport; import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.ocpp.ws.data.CommunicationContext; @@ -31,7 +32,6 @@ import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.PongMessage; @@ -55,7 +55,7 @@ */ public abstract class AbstractWebSocketEndpoint extends ConcurrentWebSocketHandler implements SubProtocolCapable { - @Autowired private ThreadPoolTaskScheduler asyncTaskScheduler; + @Autowired private DelegatingTaskScheduler asyncTaskScheduler; @Autowired private OcppServerRepository ocppServerRepository; @Autowired private FutureResponseContextStore futureResponseContextStore; @Autowired private ApplicationEventPublisher applicationEventPublisher; diff --git a/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java b/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java index 4b11b11de..3f60476f6 100644 --- a/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java +++ b/src/main/java/de/rwth/idsg/steve/service/BackgroundService.java @@ -18,12 +18,12 @@ */ package de.rwth.idsg.steve.service; +import de.rwth.idsg.steve.config.DelegatingTaskExecutor; import de.rwth.idsg.steve.repository.dto.ChargePointSelect; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import java.util.List; -import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -32,9 +32,10 @@ */ @RequiredArgsConstructor public class BackgroundService { - private final Executor asyncTaskExecutor; - public static BackgroundService with(Executor asyncTaskExecutor) { + private final DelegatingTaskExecutor asyncTaskExecutor; + + public static BackgroundService with(DelegatingTaskExecutor asyncTaskExecutor) { return new BackgroundService(asyncTaskExecutor); } diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java index f85f7420c..ed5088a2d 100644 --- a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java @@ -19,6 +19,7 @@ package de.rwth.idsg.steve.service; import de.rwth.idsg.steve.SteveException; +import de.rwth.idsg.steve.config.DelegatingTaskExecutor; import de.rwth.idsg.steve.ocpp.ChargePointServiceInvokerImpl; import de.rwth.idsg.steve.ocpp.OcppCallback; import de.rwth.idsg.steve.ocpp.task.CancelReservationTask; @@ -74,7 +75,6 @@ import org.springframework.stereotype.Service; import java.util.List; -import java.util.concurrent.Executor; /** * @author Sevket Goekay @@ -89,7 +89,7 @@ public class ChargePointServiceClient { private final ReservationRepository reservationRepository; private final OcppTagService ocppTagService; - private final Executor asyncTaskExecutor; + private final DelegatingTaskExecutor asyncTaskExecutor; private final TaskStore taskStore; private final ChargePointServiceInvokerImpl invoker; diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index c8101a261..bdd88a589 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -20,6 +20,7 @@ import com.google.common.base.Strings; import de.rwth.idsg.steve.SteveException; +import de.rwth.idsg.steve.config.DelegatingTaskExecutor; import de.rwth.idsg.steve.repository.SettingsRepository; import de.rwth.idsg.steve.repository.dto.MailSettings; import lombok.extern.slf4j.Slf4j; @@ -36,7 +37,6 @@ import jakarta.mail.internet.MimeMessage; import java.util.Properties; -import java.util.concurrent.Executor; /** * @author Sevket Goekay @@ -47,7 +47,7 @@ public class MailService { @Autowired private SettingsRepository settingsRepository; - @Autowired private Executor asyncTaskExecutor; + @Autowired private DelegatingTaskExecutor asyncTaskExecutor; public MailSettings getSettings() { return settingsRepository.getMailSettings(); From 251a1ab86ac49a36c32381a85f4b029afcbfd2d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2025 18:22:41 +0000 Subject: [PATCH 15/32] Bump flyway.version from 11.3.0 to 11.3.1 Bumps `flyway.version` from 11.3.0 to 11.3.1. Updates `org.flywaydb:flyway-mysql` from 11.3.0 to 11.3.1 Updates `org.flywaydb:flyway-maven-plugin` from 11.3.0 to 11.3.1 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.3.0...flyway-11.3.1) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af0f0ff2c..e211bfcb2 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ UTF-8 3.19.18 - 11.3.0 + 11.3.1 4.1.0 6.2.0 6.4.2 From a4947879f6d46c74ecd7e0b16cb9a23e22a0cb9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2025 18:22:45 +0000 Subject: [PATCH 16/32] Bump joda-time:joda-time from 2.13.0 to 2.13.1 Bumps [joda-time:joda-time](https://github.com/JodaOrg/joda-time) from 2.13.0 to 2.13.1. - [Release notes](https://github.com/JodaOrg/joda-time/releases) - [Changelog](https://github.com/JodaOrg/joda-time/blob/main/RELEASE-NOTES.txt) - [Commits](https://github.com/JodaOrg/joda-time/compare/v2.13.0...v2.13.1) --- updated-dependencies: - dependency-name: joda-time:joda-time dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af0f0ff2c..98511da15 100644 --- a/pom.xml +++ b/pom.xml @@ -559,7 +559,7 @@ joda-time joda-time - 2.13.0 + 2.13.1 org.hibernate.validator From b0ab383d92d58df5737924997f49b58b79653436 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2025 18:23:05 +0000 Subject: [PATCH 17/32] Bump org.apache.httpcomponents.client5:httpclient5 from 5.4.1 to 5.4.2 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.4.1 to 5.4.2. - [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.4.2/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.4.1...rel/v5.4.2) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af0f0ff2c..a9cf15911 100644 --- a/pom.xml +++ b/pom.xml @@ -595,7 +595,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.1 + 5.4.2 jakarta.websocket From 887b55c8f51cb6bc82bb160ce5850b314b6d0b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sat, 8 Feb 2025 23:34:28 +0100 Subject: [PATCH 18/32] extract interface FutureResponseContextStore reason: to enable multiple implementations --- .../ocpp/ws/FutureResponseContextStore.java | 65 ++----------- .../ws/FutureResponseContextStoreImpl.java | 95 +++++++++++++++++++ 2 files changed, 101 insertions(+), 59 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStoreImpl.java diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStore.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStore.java index 899f87892..27043e681 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStore.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStore.java @@ -19,73 +19,20 @@ package de.rwth.idsg.steve.ocpp.ws; import de.rwth.idsg.steve.ocpp.ws.data.FutureResponseContext; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.Nullable; -import org.springframework.stereotype.Service; import org.springframework.web.socket.WebSocketSession; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiFunction; - /** - * Presumption: The responses must be sent using the same connection as the requests! - * * @author Sevket Goekay - * @since 21.03.2015 + * @since 08.02.2025 */ -@Slf4j -@Service -public class FutureResponseContextStore { - - // We store for each chargeBox connection, multiple pairs of (messageId, context) - // (session, (messageId, context)) - private final Map> lookupTable = new ConcurrentHashMap<>(); - - public void addSession(WebSocketSession session) { - addIfAbsent(session); - } - - public void removeSession(WebSocketSession session) { - log.debug("Deleting the store for sessionId '{}'", session.getId()); - lookupTable.remove(session); - } - - public void add(WebSocketSession session, String messageId, FutureResponseContext context) { - Map map = addIfAbsent(session); - map.put(messageId, context); - log.debug("Store size for sessionId '{}': {}", session.getId(), map.size()); - } - - @Nullable - public FutureResponseContext get(WebSocketSession session, String messageId) { - RemoveFunction removeFunction = new RemoveFunction(messageId); - lookupTable.computeIfPresent(session, removeFunction); - return removeFunction.removedContext; - } +public interface FutureResponseContextStore { - private Map addIfAbsent(WebSocketSession session) { - return lookupTable.computeIfAbsent(session, innerSession -> { - log.debug("Creating new store for sessionId '{}'", innerSession.getId()); - return new ConcurrentHashMap<>(); - }); - } + void addSession(WebSocketSession session); - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - private static class RemoveFunction implements - BiFunction, Map> { + void removeSession(WebSocketSession session); - private final String messageId; - @Nullable private FutureResponseContext removedContext; + void add(WebSocketSession session, String messageId, FutureResponseContext context); - @Override - public Map apply(WebSocketSession session, - Map map) { - removedContext = map.remove(messageId); - log.debug("Store size for sessionId '{}': {}", session.getId(), map.size()); - return map; - } - } + @Nullable FutureResponseContext get(WebSocketSession session, String messageId); } diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStoreImpl.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStoreImpl.java new file mode 100644 index 000000000..9ecf5233c --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/FutureResponseContextStoreImpl.java @@ -0,0 +1,95 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * 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 . + */ +package de.rwth.idsg.steve.ocpp.ws; + +import de.rwth.idsg.steve.ocpp.ws.data.FutureResponseContext; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Nullable; +import org.springframework.stereotype.Service; +import org.springframework.web.socket.WebSocketSession; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; + +/** + * Presumption: The responses must be sent using the same connection as the requests! + * + * @author Sevket Goekay + * @since 21.03.2015 + */ +@Slf4j +@Service +public class FutureResponseContextStoreImpl implements FutureResponseContextStore { + + // We store for each chargeBox connection, multiple pairs of (messageId, context) + // (session, (messageId, context)) + private final Map> lookupTable = new ConcurrentHashMap<>(); + + @Override + public void addSession(WebSocketSession session) { + addIfAbsent(session); + } + + @Override + public void removeSession(WebSocketSession session) { + log.debug("Deleting the store for sessionId '{}'", session.getId()); + lookupTable.remove(session); + } + + @Override + public void add(WebSocketSession session, String messageId, FutureResponseContext context) { + Map map = addIfAbsent(session); + map.put(messageId, context); + log.debug("Store size for sessionId '{}': {}", session.getId(), map.size()); + } + + @Nullable + @Override + public FutureResponseContext get(WebSocketSession session, String messageId) { + RemoveFunction removeFunction = new RemoveFunction(messageId); + lookupTable.computeIfPresent(session, removeFunction); + return removeFunction.removedContext; + } + + private Map addIfAbsent(WebSocketSession session) { + return lookupTable.computeIfAbsent(session, innerSession -> { + log.debug("Creating new store for sessionId '{}'", innerSession.getId()); + return new ConcurrentHashMap<>(); + }); + } + + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + private static class RemoveFunction implements + BiFunction, Map> { + + private final String messageId; + @Nullable private FutureResponseContext removedContext; + + @Override + public Map apply(WebSocketSession session, + Map map) { + removedContext = map.remove(messageId); + log.debug("Store size for sessionId '{}': {}", session.getId(), map.size()); + return map; + } + } +} From 477975452b57033f0419f413902ebe7629850071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sat, 8 Feb 2025 23:55:33 +0100 Subject: [PATCH 19/32] extract interface SessionContextStore reason: to enable multiple implementations --- .../ocpp/ws/AbstractWebSocketEndpoint.java | 2 +- .../steve/ocpp/ws/SessionContextStore.java | 122 +------------- .../ocpp/ws/SessionContextStoreImpl.java | 159 ++++++++++++++++++ 3 files changed, 169 insertions(+), 114 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java index 5f4782578..8a55a0079 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java @@ -62,7 +62,7 @@ public abstract class AbstractWebSocketEndpoint extends ConcurrentWebSocketHandl public static final String CHARGEBOX_ID_KEY = "CHARGEBOX_ID_KEY"; - private final SessionContextStore sessionContextStore = new SessionContextStore(); + private final SessionContextStore sessionContextStore = new SessionContextStoreImpl(); private final List> connectedCallbackList = new ArrayList<>(); private final List> disconnectedCallbackList = new ArrayList<>(); private final Object sessionContextLock = new Object(); diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStore.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStore.java index 872007e1c..ab97a9f28 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStore.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStore.java @@ -18,135 +18,31 @@ */ package de.rwth.idsg.steve.ocpp.ws; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.Striped; -import de.rwth.idsg.steve.SteveException; -import de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategy; import de.rwth.idsg.steve.ocpp.ws.data.SessionContext; -import lombok.extern.slf4j.Slf4j; -import org.joda.time.DateTime; import org.springframework.web.socket.WebSocketSession; -import java.util.ArrayDeque; -import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.Lock; - -import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; /** * @author Sevket Goekay - * @since 17.03.2015 + * @since 08.02.2025 */ -@Slf4j -public class SessionContextStore { - - /** - * Key (String) = chargeBoxId - * Value (Deque) = WebSocket session contexts - */ - private final ConcurrentHashMap> lookupTable = new ConcurrentHashMap<>(); - - private final Striped locks = Striped.lock(16); - - private final WsSessionSelectStrategy wsSessionSelectStrategy = CONFIG.getOcpp().getWsSessionSelectStrategy(); - - public void add(String chargeBoxId, WebSocketSession session, ScheduledFuture pingSchedule) { - Lock l = locks.get(chargeBoxId); - l.lock(); - try { - SessionContext context = new SessionContext(session, pingSchedule, DateTime.now()); - - Deque endpointDeque = lookupTable.computeIfAbsent(chargeBoxId, str -> new ArrayDeque<>()); - endpointDeque.addLast(context); // Adding at the end - - log.debug("A new SessionContext is stored for chargeBoxId '{}'. Store size: {}", - chargeBoxId, endpointDeque.size()); - } finally { - l.unlock(); - } - } - - public void remove(String chargeBoxId, WebSocketSession session) { - Lock l = locks.get(chargeBoxId); - l.lock(); - try { - Deque endpointDeque = lookupTable.get(chargeBoxId); - if (endpointDeque == null) { - log.debug("No session context to remove for chargeBoxId '{}'", chargeBoxId); - return; - } +public interface SessionContextStore { - // Prevent "java.util.ConcurrentModificationException: null" - // Reason: Cannot modify the set (remove the item) we are iterating - // Solution: Iterate the set, find the item, remove the item after the for-loop - // - SessionContext toRemove = null; - for (SessionContext context : endpointDeque) { - if (context.getSession().getId().equals(session.getId())) { - toRemove = context; - break; - } - } + void add(String chargeBoxId, WebSocketSession session, ScheduledFuture pingSchedule); - if (toRemove != null) { - // 1. Cancel the ping task - toRemove.getPingSchedule().cancel(true); - // 2. Delete from collection - if (endpointDeque.remove(toRemove)) { - log.debug("A SessionContext is removed for chargeBoxId '{}'. Store size: {}", - chargeBoxId, endpointDeque.size()); - } - // 3. Delete empty collection from lookup table in order to correctly calculate - // the number of connected chargeboxes with getNumberOfChargeBoxes() - if (endpointDeque.size() == 0) { - lookupTable.remove(chargeBoxId); - } - } - } finally { - l.unlock(); - } - } + void remove(String chargeBoxId, WebSocketSession session); - public WebSocketSession getSession(String chargeBoxId) { - Lock l = locks.get(chargeBoxId); - l.lock(); - try { - Deque endpointDeque = lookupTable.get(chargeBoxId); - if (endpointDeque == null) { - throw new NoSuchElementException(); - } - return wsSessionSelectStrategy.getSession(endpointDeque); - } catch (NoSuchElementException e) { - throw new SteveException("No session context for chargeBoxId '%s'", chargeBoxId, e); - } finally { - l.unlock(); - } - } + WebSocketSession getSession(String chargeBoxId); - public int getSize(String chargeBoxId) { - Deque endpointDeque = lookupTable.get(chargeBoxId); - if (endpointDeque == null) { - return 0; - } else { - return endpointDeque.size(); - } - } + int getSize(String chargeBoxId); - public int getNumberOfChargeBoxes() { - return lookupTable.size(); - } + int getNumberOfChargeBoxes(); - public List getChargeBoxIdList() { - return Collections.list(lookupTable.keys()); - } + List getChargeBoxIdList(); - public Map> getACopy() { - return ImmutableMap.copyOf(lookupTable); - } + Map> getACopy(); } diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java new file mode 100644 index 000000000..6b6696ca7 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java @@ -0,0 +1,159 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * 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 . + */ +package de.rwth.idsg.steve.ocpp.ws; + +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.Striped; +import de.rwth.idsg.steve.SteveException; +import de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategy; +import de.rwth.idsg.steve.ocpp.ws.data.SessionContext; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; +import org.springframework.web.socket.WebSocketSession; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.locks.Lock; + +import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; + +/** + * @author Sevket Goekay + * @since 17.03.2015 + */ +@Slf4j +public class SessionContextStoreImpl implements SessionContextStore { + + /** + * Key (String) = chargeBoxId + * Value (Deque) = WebSocket session contexts + */ + private final ConcurrentHashMap> lookupTable = new ConcurrentHashMap<>(); + + private final Striped locks = Striped.lock(16); + + private final WsSessionSelectStrategy wsSessionSelectStrategy = CONFIG.getOcpp().getWsSessionSelectStrategy(); + + @Override + public void add(String chargeBoxId, WebSocketSession session, ScheduledFuture pingSchedule) { + Lock l = locks.get(chargeBoxId); + l.lock(); + try { + SessionContext context = new SessionContext(session, pingSchedule, DateTime.now()); + + Deque endpointDeque = lookupTable.computeIfAbsent(chargeBoxId, str -> new ArrayDeque<>()); + endpointDeque.addLast(context); // Adding at the end + + log.debug("A new SessionContext is stored for chargeBoxId '{}'. Store size: {}", + chargeBoxId, endpointDeque.size()); + } finally { + l.unlock(); + } + } + + @Override + public void remove(String chargeBoxId, WebSocketSession session) { + Lock l = locks.get(chargeBoxId); + l.lock(); + try { + Deque endpointDeque = lookupTable.get(chargeBoxId); + if (endpointDeque == null) { + log.debug("No session context to remove for chargeBoxId '{}'", chargeBoxId); + return; + } + + // Prevent "java.util.ConcurrentModificationException: null" + // Reason: Cannot modify the set (remove the item) we are iterating + // Solution: Iterate the set, find the item, remove the item after the for-loop + // + SessionContext toRemove = null; + for (SessionContext context : endpointDeque) { + if (context.getSession().getId().equals(session.getId())) { + toRemove = context; + break; + } + } + + if (toRemove != null) { + // 1. Cancel the ping task + toRemove.getPingSchedule().cancel(true); + // 2. Delete from collection + if (endpointDeque.remove(toRemove)) { + log.debug("A SessionContext is removed for chargeBoxId '{}'. Store size: {}", + chargeBoxId, endpointDeque.size()); + } + // 3. Delete empty collection from lookup table in order to correctly calculate + // the number of connected chargeboxes with getNumberOfChargeBoxes() + if (endpointDeque.size() == 0) { + lookupTable.remove(chargeBoxId); + } + } + } finally { + l.unlock(); + } + } + + @Override + public WebSocketSession getSession(String chargeBoxId) { + Lock l = locks.get(chargeBoxId); + l.lock(); + try { + Deque endpointDeque = lookupTable.get(chargeBoxId); + if (endpointDeque == null) { + throw new NoSuchElementException(); + } + return wsSessionSelectStrategy.getSession(endpointDeque); + } catch (NoSuchElementException e) { + throw new SteveException("No session context for chargeBoxId '%s'", chargeBoxId, e); + } finally { + l.unlock(); + } + } + + @Override + public int getSize(String chargeBoxId) { + Deque endpointDeque = lookupTable.get(chargeBoxId); + if (endpointDeque == null) { + return 0; + } else { + return endpointDeque.size(); + } + } + + @Override + public int getNumberOfChargeBoxes() { + return lookupTable.size(); + } + + @Override + public List getChargeBoxIdList() { + return Collections.list(lookupTable.keys()); + } + + @Override + public Map> getACopy() { + return ImmutableMap.copyOf(lookupTable); + } +} From 15c48ae97b29e2d8c380ae9e48c7cf20ef8d57ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:11:28 +0000 Subject: [PATCH 20/32] Bump flyway.version from 11.3.1 to 11.3.2 Bumps `flyway.version` from 11.3.1 to 11.3.2. Updates `org.flywaydb:flyway-mysql` from 11.3.1 to 11.3.2 Updates `org.flywaydb:flyway-maven-plugin` from 11.3.1 to 11.3.2 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.3.1...flyway-11.3.2) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7bd70580c..d297b31e3 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ UTF-8 3.19.18 - 11.3.1 + 11.3.2 4.1.0 6.2.0 6.4.2 From dec5e94bc904ee4b1077d384f78fcdc57159b28a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:11:36 +0000 Subject: [PATCH 21/32] Bump net.bytebuddy:byte-buddy from 1.17.0 to 1.17.1 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.17.0 to 1.17.1. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.17.0...byte-buddy-1.17.1) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7bd70580c..40d2d9cce 100644 --- a/pom.xml +++ b/pom.xml @@ -793,7 +793,7 @@ net.bytebuddy byte-buddy - 1.17.0 + 1.17.1 test From a45cedc7c15cc088c9ccf95f7adaa24cde84e311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 16 Feb 2025 13:38:36 +0100 Subject: [PATCH 22/32] enable adhoc SetChargingProfile calls --- .../ocpp/task/SetChargingProfileTask.java | 93 +------------- .../task/SetChargingProfileTaskAdhoc.java | 61 +++++++++ .../task/SetChargingProfileTaskFromDB.java | 120 ++++++++++++++++++ .../service/ChargePointServiceClient.java | 23 ++-- .../dto/EnhancedSetChargingProfileParams.java | 45 ------- 5 files changed, 199 insertions(+), 143 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java create mode 100644 src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java delete mode 100644 src/main/java/de/rwth/idsg/steve/service/dto/EnhancedSetChargingProfileParams.java diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java index ffaf89c5b..65a737dd4 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java @@ -19,100 +19,15 @@ package de.rwth.idsg.steve.ocpp.task; import de.rwth.idsg.steve.ocpp.Ocpp16AndAboveTask; -import de.rwth.idsg.steve.ocpp.OcppCallback; -import de.rwth.idsg.steve.repository.ChargingProfileRepository; -import de.rwth.idsg.steve.service.dto.EnhancedSetChargingProfileParams; -import jooq.steve.db.tables.records.ChargingProfileRecord; -import ocpp.cp._2015._10.ChargingProfile; -import ocpp.cp._2015._10.ChargingProfileKindType; -import ocpp.cp._2015._10.ChargingProfilePurposeType; -import ocpp.cp._2015._10.ChargingRateUnitType; -import ocpp.cp._2015._10.ChargingSchedule; -import ocpp.cp._2015._10.ChargingSchedulePeriod; -import ocpp.cp._2015._10.RecurrencyKindType; -import ocpp.cp._2015._10.SetChargingProfileRequest; - -import jakarta.xml.ws.AsyncHandler; - -import java.util.List; -import java.util.stream.Collectors; +import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; /** * @author Sevket Goekay - * @since 13.03.2018 + * @since 06.02.2025 */ -public class SetChargingProfileTask extends Ocpp16AndAboveTask { +public abstract class SetChargingProfileTask extends Ocpp16AndAboveTask { - private final ChargingProfileRepository chargingProfileRepository; - - public SetChargingProfileTask(EnhancedSetChargingProfileParams params, - ChargingProfileRepository chargingProfileRepository) { + public SetChargingProfileTask(SetChargingProfileParams params) { super(params); - this.chargingProfileRepository = chargingProfileRepository; - } - - @Override - public OcppCallback defaultCallback() { - return new DefaultOcppCallback() { - @Override - public void success(String chargeBoxId, String statusValue) { - addNewResponse(chargeBoxId, statusValue); - - if ("Accepted".equalsIgnoreCase(statusValue)) { - int chargingProfilePk = params.getDetails().getProfile().getChargingProfilePk(); - int connectorId = params.getDelegate().getConnectorId(); - chargingProfileRepository.setProfile(chargingProfilePk, chargeBoxId, connectorId); - } - } - }; - } - - @Override - public ocpp.cp._2015._10.SetChargingProfileRequest getOcpp16Request() { - ChargingProfileRecord profile = params.getDetails().getProfile(); - - List schedulePeriods = - params.getDetails().getPeriods() - .stream() - .map(k -> { - ChargingSchedulePeriod p = new ChargingSchedulePeriod(); - p.setStartPeriod(k.getStartPeriodInSeconds()); - p.setLimit(k.getPowerLimit()); - p.setNumberPhases(k.getNumberPhases()); - return p; - }) - .collect(Collectors.toList()); - - ChargingSchedule schedule = new ChargingSchedule() - .withDuration(profile.getDurationInSeconds()) - .withStartSchedule(profile.getStartSchedule()) - .withChargingRateUnit(ChargingRateUnitType.fromValue(profile.getChargingRateUnit())) - .withMinChargingRate(profile.getMinChargingRate()) - .withChargingSchedulePeriod(schedulePeriods); - - ChargingProfile ocppProfile = new ChargingProfile() - .withChargingProfileId(profile.getChargingProfilePk()) - .withStackLevel(profile.getStackLevel()) - .withChargingProfilePurpose(ChargingProfilePurposeType.fromValue(profile.getChargingProfilePurpose())) - .withChargingProfileKind(ChargingProfileKindType.fromValue(profile.getChargingProfileKind())) - .withRecurrencyKind(profile.getRecurrencyKind() == null ? null : RecurrencyKindType.fromValue(profile.getRecurrencyKind())) - .withValidFrom(profile.getValidFrom()) - .withValidTo(profile.getValidTo()) - .withChargingSchedule(schedule); - - return new SetChargingProfileRequest() - .withConnectorId(params.getDelegate().getConnectorId()) - .withCsChargingProfiles(ocppProfile); - } - - @Override - public AsyncHandler getOcpp16Handler(String chargeBoxId) { - return res -> { - try { - success(chargeBoxId, res.get().getStatus().value()); - } catch (Exception e) { - failed(chargeBoxId, e); - } - }; } } diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java new file mode 100644 index 000000000..2321e57b7 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java @@ -0,0 +1,61 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * 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 . + */ +package de.rwth.idsg.steve.ocpp.task; + +import de.rwth.idsg.steve.ocpp.OcppCallback; +import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; +import ocpp.cp._2015._10.SetChargingProfileRequest; + +import jakarta.xml.ws.AsyncHandler; + +/** + * @author Sevket Goekay + * @since 16.02.2025 + */ +public class SetChargingProfileTaskAdhoc extends SetChargingProfileTask { + + private final SetChargingProfileRequest request; + + public SetChargingProfileTaskAdhoc(SetChargingProfileParams params, + SetChargingProfileRequest request) { + super(params); + this.request = request; + } + + @Override + public OcppCallback defaultCallback() { + return new StringOcppCallback(); + } + + @Override + public SetChargingProfileRequest getOcpp16Request() { + return request; + } + + @Override + public AsyncHandler getOcpp16Handler(String chargeBoxId) { + return res -> { + try { + success(chargeBoxId, res.get().getStatus().value()); + } catch (Exception e) { + failed(chargeBoxId, e); + } + }; + } +} diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java new file mode 100644 index 000000000..e35024a71 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java @@ -0,0 +1,120 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * 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 . + */ +package de.rwth.idsg.steve.ocpp.task; + +import de.rwth.idsg.steve.ocpp.OcppCallback; +import de.rwth.idsg.steve.repository.ChargingProfileRepository; +import de.rwth.idsg.steve.repository.dto.ChargingProfile; +import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; +import jooq.steve.db.tables.records.ChargingProfileRecord; +import ocpp.cp._2015._10.ChargingProfileKindType; +import ocpp.cp._2015._10.ChargingProfilePurposeType; +import ocpp.cp._2015._10.ChargingRateUnitType; +import ocpp.cp._2015._10.ChargingSchedule; +import ocpp.cp._2015._10.ChargingSchedulePeriod; +import ocpp.cp._2015._10.RecurrencyKindType; +import ocpp.cp._2015._10.SetChargingProfileRequest; + +import jakarta.xml.ws.AsyncHandler; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Sevket Goekay + * @since 13.03.2018 + */ +public class SetChargingProfileTaskFromDB extends SetChargingProfileTask { + + private final ChargingProfile.Details details; + private final ChargingProfileRepository chargingProfileRepository; + + public SetChargingProfileTaskFromDB(SetChargingProfileParams params, + ChargingProfile.Details details, + ChargingProfileRepository chargingProfileRepository) { + super(params); + this.details = details; + this.chargingProfileRepository = chargingProfileRepository; + } + + @Override + public OcppCallback defaultCallback() { + return new DefaultOcppCallback() { + @Override + public void success(String chargeBoxId, String statusValue) { + addNewResponse(chargeBoxId, statusValue); + + if ("Accepted".equalsIgnoreCase(statusValue)) { + int chargingProfilePk = details.getProfile().getChargingProfilePk(); + int connectorId = params.getConnectorId(); + chargingProfileRepository.setProfile(chargingProfilePk, chargeBoxId, connectorId); + } + } + }; + } + + @Override + public SetChargingProfileRequest getOcpp16Request() { + ChargingProfileRecord profile = details.getProfile(); + + List schedulePeriods = + details.getPeriods() + .stream() + .map(k -> { + ChargingSchedulePeriod p = new ChargingSchedulePeriod(); + p.setStartPeriod(k.getStartPeriodInSeconds()); + p.setLimit(k.getPowerLimit()); + p.setNumberPhases(k.getNumberPhases()); + return p; + }) + .collect(Collectors.toList()); + + ChargingSchedule schedule = new ChargingSchedule() + .withDuration(profile.getDurationInSeconds()) + .withStartSchedule(profile.getStartSchedule()) + .withChargingRateUnit(ChargingRateUnitType.fromValue(profile.getChargingRateUnit())) + .withMinChargingRate(profile.getMinChargingRate()) + .withChargingSchedulePeriod(schedulePeriods); + + ocpp.cp._2015._10.ChargingProfile ocppProfile = new ocpp.cp._2015._10.ChargingProfile() + .withChargingProfileId(profile.getChargingProfilePk()) + .withStackLevel(profile.getStackLevel()) + .withChargingProfilePurpose(ChargingProfilePurposeType.fromValue(profile.getChargingProfilePurpose())) + .withChargingProfileKind(ChargingProfileKindType.fromValue(profile.getChargingProfileKind())) + .withRecurrencyKind(profile.getRecurrencyKind() == null ? null : RecurrencyKindType.fromValue(profile.getRecurrencyKind())) + .withValidFrom(profile.getValidFrom()) + .withValidTo(profile.getValidTo()) + .withChargingSchedule(schedule); + + return new SetChargingProfileRequest() + .withConnectorId(params.getConnectorId()) + .withCsChargingProfiles(ocppProfile); + } + + @Override + public AsyncHandler getOcpp16Handler(String chargeBoxId) { + return res -> { + try { + success(chargeBoxId, res.get().getStatus().value()); + } catch (Exception e) { + failed(chargeBoxId, e); + } + }; + } +} diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java index ed5088a2d..24575c2da 100644 --- a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java @@ -38,6 +38,7 @@ import de.rwth.idsg.steve.ocpp.task.ResetTask; import de.rwth.idsg.steve.ocpp.task.SendLocalListTask; import de.rwth.idsg.steve.ocpp.task.SetChargingProfileTask; +import de.rwth.idsg.steve.ocpp.task.SetChargingProfileTaskFromDB; import de.rwth.idsg.steve.ocpp.task.TriggerMessageTask; import de.rwth.idsg.steve.ocpp.task.UnlockConnectorTask; import de.rwth.idsg.steve.ocpp.task.UpdateFirmwareTask; @@ -48,7 +49,6 @@ import de.rwth.idsg.steve.repository.dto.ChargingProfile; import de.rwth.idsg.steve.repository.dto.InsertReservationParams; import de.rwth.idsg.steve.service.dto.EnhancedReserveNowParams; -import de.rwth.idsg.steve.service.dto.EnhancedSetChargingProfileParams; import de.rwth.idsg.steve.web.dto.ocpp.CancelReservationParams; import de.rwth.idsg.steve.web.dto.ocpp.ChangeAvailabilityParams; import de.rwth.idsg.steve.web.dto.ocpp.ChangeConfigurationParams; @@ -384,15 +384,8 @@ public final int triggerMessage(TriggerMessageParams params, } @SafeVarargs - public final int setChargingProfile(SetChargingProfileParams params, + public final int setChargingProfile(SetChargingProfileTask task, OcppCallback... callbacks) { - ChargingProfile.Details details = chargingProfileRepository.getDetails(params.getChargingProfilePk()); - - checkAdditionalConstraints(params, details); - - EnhancedSetChargingProfileParams enhancedParams = new EnhancedSetChargingProfileParams(params, details); - SetChargingProfileTask task = new SetChargingProfileTask(enhancedParams, chargingProfileRepository); - for (var callback : callbacks) { task.addCallback(callback); } @@ -404,6 +397,18 @@ public final int setChargingProfile(SetChargingProfileParams params, return taskStore.add(task); } + @SafeVarargs + public final int setChargingProfile(SetChargingProfileParams params, + OcppCallback... callbacks) { + ChargingProfile.Details details = chargingProfileRepository.getDetails(params.getChargingProfilePk()); + + checkAdditionalConstraints(params, details); + + SetChargingProfileTaskFromDB task = new SetChargingProfileTaskFromDB(params, details, chargingProfileRepository); + + return setChargingProfile(task, callbacks); + } + @SafeVarargs public final int clearChargingProfile(ClearChargingProfileParams params, OcppCallback... callbacks) { diff --git a/src/main/java/de/rwth/idsg/steve/service/dto/EnhancedSetChargingProfileParams.java b/src/main/java/de/rwth/idsg/steve/service/dto/EnhancedSetChargingProfileParams.java deleted file mode 100644 index 5b34d7ba6..000000000 --- a/src/main/java/de/rwth/idsg/steve/service/dto/EnhancedSetChargingProfileParams.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * 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 . - */ -package de.rwth.idsg.steve.service.dto; - -import de.rwth.idsg.steve.repository.dto.ChargePointSelect; -import de.rwth.idsg.steve.repository.dto.ChargingProfile; -import de.rwth.idsg.steve.web.dto.ocpp.ChargePointSelection; -import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.List; - -/** - * @author Sevket Goekay - * @since 12.11.2018 - */ -@Getter -@RequiredArgsConstructor -public class EnhancedSetChargingProfileParams implements ChargePointSelection { - - private final SetChargingProfileParams delegate; - private final ChargingProfile.Details details; - - @Override - public List getChargePointSelectList() { - return delegate.getChargePointSelectList(); - } -} From 2ee8160eb35f08c4777fee8bddc15c3fade0efe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 16 Feb 2025 13:49:06 +0100 Subject: [PATCH 23/32] move validation to abstract parent class --- .../ocpp/task/SetChargingProfileTask.java | 20 ++++++++++++++++++ .../task/SetChargingProfileTaskAdhoc.java | 1 + .../task/SetChargingProfileTaskFromDB.java | 6 +++++- .../service/ChargePointServiceClient.java | 21 ------------------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java index 65a737dd4..2320b10fe 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java @@ -18,8 +18,11 @@ */ package de.rwth.idsg.steve.ocpp.task; +import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.ocpp.Ocpp16AndAboveTask; import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; +import ocpp.cp._2015._10.ChargingProfilePurposeType; +import ocpp.cp._2015._10.SetChargingProfileRequest; /** * @author Sevket Goekay @@ -30,4 +33,21 @@ public abstract class SetChargingProfileTask extends Ocpp16AndAboveTask 0"); + } + } } diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java index 2321e57b7..b93a723a4 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java @@ -36,6 +36,7 @@ public SetChargingProfileTaskAdhoc(SetChargingProfileParams params, SetChargingProfileRequest request) { super(params); this.request = request; + checkAdditionalConstraints(request); } @Override diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java index e35024a71..b5d1a8182 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java @@ -102,9 +102,13 @@ public SetChargingProfileRequest getOcpp16Request() { .withValidTo(profile.getValidTo()) .withChargingSchedule(schedule); - return new SetChargingProfileRequest() + var request = new SetChargingProfileRequest() .withConnectorId(params.getConnectorId()) .withCsChargingProfiles(ocppProfile); + + checkAdditionalConstraints(request); + + return request; } @Override diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java index 24575c2da..561f1c8df 100644 --- a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java @@ -18,7 +18,6 @@ */ package de.rwth.idsg.steve.service; -import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.config.DelegatingTaskExecutor; import de.rwth.idsg.steve.ocpp.ChargePointServiceInvokerImpl; import de.rwth.idsg.steve.ocpp.OcppCallback; @@ -402,8 +401,6 @@ public final int setChargingProfile(SetChargingProfileParams params, OcppCallback... callbacks) { ChargingProfile.Details details = chargingProfileRepository.getDetails(params.getChargingProfilePk()); - checkAdditionalConstraints(params, details); - SetChargingProfileTaskFromDB task = new SetChargingProfileTaskFromDB(params, details, chargingProfileRepository); return setChargingProfile(task, callbacks); @@ -441,22 +438,4 @@ public final int getCompositeSchedule(GetCompositeScheduleParams params, return taskStore.add(task); } - /** - * Do some additional checks defined by OCPP spec, which cannot be captured with javax.validation - */ - private static void checkAdditionalConstraints(SetChargingProfileParams params, ChargingProfile.Details details) { - ChargingProfilePurposeType purpose = ChargingProfilePurposeType.fromValue(details.getProfile().getChargingProfilePurpose()); - - if (ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE == purpose - && params.getConnectorId() != null - && params.getConnectorId() != 0) { - throw new SteveException("ChargePointMaxProfile can only be set at Charge Point ConnectorId 0"); - } - - if (ChargingProfilePurposeType.TX_PROFILE == purpose - && params.getConnectorId() != null - && params.getConnectorId() < 1) { - throw new SteveException("TxProfile should only be set at Charge Point ConnectorId > 0"); - } - } } From 7a6805bfb42daf3e1bd601ebf6b68624409e1572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 16 Feb 2025 14:11:51 +0100 Subject: [PATCH 24/32] refactor --- .../ocpp/task/SetChargingProfileTask.java | 22 ++++++++++++++++--- .../task/SetChargingProfileTaskAdhoc.java | 17 ++------------ .../task/SetChargingProfileTaskFromDB.java | 18 +++------------ 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java index 2320b10fe..6123b46cb 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java @@ -20,20 +20,36 @@ import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.ocpp.Ocpp16AndAboveTask; -import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; +import de.rwth.idsg.steve.web.dto.ocpp.MultipleChargePointSelect; import ocpp.cp._2015._10.ChargingProfilePurposeType; import ocpp.cp._2015._10.SetChargingProfileRequest; +import ocpp.cp._2015._10.SetChargingProfileResponse; + +import jakarta.xml.ws.AsyncHandler; /** * @author Sevket Goekay * @since 06.02.2025 */ -public abstract class SetChargingProfileTask extends Ocpp16AndAboveTask { +public abstract class SetChargingProfileTask extends Ocpp16AndAboveTask { - public SetChargingProfileTask(SetChargingProfileParams params) { + public SetChargingProfileTask(MultipleChargePointSelect params) { super(params); } + public abstract SetChargingProfileRequest getOcpp16Request(); + + @Override + public AsyncHandler getOcpp16Handler(String chargeBoxId) { + return res -> { + try { + success(chargeBoxId, res.get().getStatus().value()); + } catch (Exception e) { + failed(chargeBoxId, e); + } + }; + } + /** * Do some additional checks defined by OCPP spec, which cannot be captured with javax.validation */ diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java index b93a723a4..b2ed6f5f1 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskAdhoc.java @@ -19,11 +19,9 @@ package de.rwth.idsg.steve.ocpp.task; import de.rwth.idsg.steve.ocpp.OcppCallback; -import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; +import de.rwth.idsg.steve.web.dto.ocpp.MultipleChargePointSelect; import ocpp.cp._2015._10.SetChargingProfileRequest; -import jakarta.xml.ws.AsyncHandler; - /** * @author Sevket Goekay * @since 16.02.2025 @@ -32,7 +30,7 @@ public class SetChargingProfileTaskAdhoc extends SetChargingProfileTask { private final SetChargingProfileRequest request; - public SetChargingProfileTaskAdhoc(SetChargingProfileParams params, + public SetChargingProfileTaskAdhoc(MultipleChargePointSelect params, SetChargingProfileRequest request) { super(params); this.request = request; @@ -48,15 +46,4 @@ public OcppCallback defaultCallback() { public SetChargingProfileRequest getOcpp16Request() { return request; } - - @Override - public AsyncHandler getOcpp16Handler(String chargeBoxId) { - return res -> { - try { - success(chargeBoxId, res.get().getStatus().value()); - } catch (Exception e) { - failed(chargeBoxId, e); - } - }; - } } diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java index b5d1a8182..a109f450e 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTaskFromDB.java @@ -31,8 +31,6 @@ import ocpp.cp._2015._10.RecurrencyKindType; import ocpp.cp._2015._10.SetChargingProfileRequest; -import jakarta.xml.ws.AsyncHandler; - import java.util.List; import java.util.stream.Collectors; @@ -42,6 +40,7 @@ */ public class SetChargingProfileTaskFromDB extends SetChargingProfileTask { + private final int connectorId; private final ChargingProfile.Details details; private final ChargingProfileRepository chargingProfileRepository; @@ -49,6 +48,7 @@ public SetChargingProfileTaskFromDB(SetChargingProfileParams params, ChargingProfile.Details details, ChargingProfileRepository chargingProfileRepository) { super(params); + this.connectorId = params.getConnectorId(); this.details = details; this.chargingProfileRepository = chargingProfileRepository; } @@ -62,7 +62,6 @@ public void success(String chargeBoxId, String statusValue) { if ("Accepted".equalsIgnoreCase(statusValue)) { int chargingProfilePk = details.getProfile().getChargingProfilePk(); - int connectorId = params.getConnectorId(); chargingProfileRepository.setProfile(chargingProfilePk, chargeBoxId, connectorId); } } @@ -103,22 +102,11 @@ public SetChargingProfileRequest getOcpp16Request() { .withChargingSchedule(schedule); var request = new SetChargingProfileRequest() - .withConnectorId(params.getConnectorId()) + .withConnectorId(connectorId) .withCsChargingProfiles(ocppProfile); checkAdditionalConstraints(request); return request; } - - @Override - public AsyncHandler getOcpp16Handler(String chargeBoxId) { - return res -> { - try { - success(chargeBoxId, res.get().getStatus().value()); - } catch (Exception e) { - failed(chargeBoxId, e); - } - }; - } } From e7f25fc2e59d3d241a43b045c829dc1c4e15051a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:58:42 +0000 Subject: [PATCH 25/32] Bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.6.6 to 4.9.1.0 Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.8.6.6 to 4.9.1.0. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.6.6...spotbugs-maven-plugin-4.9.1.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0053ff4d..8f30f209d 100644 --- a/pom.xml +++ b/pom.xml @@ -212,7 +212,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.6 + 4.9.1.0 false de.rwth.idsg.steve.- From 6dc1a31f29a84a5cbb1898f483f6d687a86ce89e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:58:49 +0000 Subject: [PATCH 26/32] Bump flyway.version from 11.3.2 to 11.3.3 Bumps `flyway.version` from 11.3.2 to 11.3.3. Updates `org.flywaydb:flyway-mysql` from 11.3.2 to 11.3.3 Updates `org.flywaydb:flyway-maven-plugin` from 11.3.2 to 11.3.3 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.3.2...flyway-11.3.3) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0053ff4d..9c2e81bc1 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ UTF-8 3.19.18 - 11.3.2 + 11.3.3 4.1.0 6.2.0 6.4.2 From 70386841bbebb431e29378c624836f7dd59d61cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:58:54 +0000 Subject: [PATCH 27/32] Bump org.springdoc:springdoc-openapi-starter-webmvc-ui Bumps [org.springdoc:springdoc-openapi-starter-webmvc-ui](https://github.com/springdoc/springdoc-openapi) from 2.8.4 to 2.8.5. - [Release notes](https://github.com/springdoc/springdoc-openapi/releases) - [Changelog](https://github.com/springdoc/springdoc-openapi/blob/main/CHANGELOG.md) - [Commits](https://github.com/springdoc/springdoc-openapi/compare/v2.8.4...v2.8.5) --- updated-dependencies: - dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0053ff4d..a3fea24fb 100644 --- a/pom.xml +++ b/pom.xml @@ -525,7 +525,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.8.4 + 2.8.5 From e89c4429a872430f8eb4328bd711742c7ed383ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:59:11 +0000 Subject: [PATCH 28/32] Bump spring.security.version from 6.4.2 to 6.4.3 Bumps `spring.security.version` from 6.4.2 to 6.4.3. Updates `org.springframework.security:spring-security-web` from 6.4.2 to 6.4.3 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.4.2...6.4.3) Updates `org.springframework.security:spring-security-config` from 6.4.2 to 6.4.3 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.4.2...6.4.3) --- updated-dependencies: - dependency-name: org.springframework.security:spring-security-web dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.springframework.security:spring-security-config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0053ff4d..90e6cea8c 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 11.3.2 4.1.0 6.2.0 - 6.4.2 + 6.4.3 9.2.0 12.0.16 1.18.36 From f3ebc0145770720094057a87343d3f0163feacd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:59:24 +0000 Subject: [PATCH 29/32] Bump org.apache.maven.plugins:maven-compiler-plugin Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.13.0 to 3.14.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0053ff4d..d84ef1786 100644 --- a/pom.xml +++ b/pom.xml @@ -177,7 +177,7 @@ maven-compiler-plugin - 3.13.0 + 3.14.0 ${java.version} ${java.version} From 1039a5595e91225d285f9b3b1beca2ef2ad7dfd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:59:30 +0000 Subject: [PATCH 30/32] Bump org.junit:junit-bom from 5.11.4 to 5.12.0 Bumps [org.junit:junit-bom](https://github.com/junit-team/junit5) from 5.11.4 to 5.12.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.11.4...r5.12.0) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0053ff4d..ceefb4745 100644 --- a/pom.xml +++ b/pom.xml @@ -507,7 +507,7 @@ org.junit junit-bom - 5.11.4 + 5.12.0 pom import From 9d0574b231ff892db6b736c824a061acba03327c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 23 Feb 2025 15:33:39 +0100 Subject: [PATCH 31/32] add ability to query only stopped transactions --- .../idsg/steve/repository/impl/TransactionRepositoryImpl.java | 3 +++ .../java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 26e928c91..c3dfe43a1 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -290,6 +290,9 @@ private SelectQuery addConditions(SelectQuery selectQuery, TransactionQueryForm if (form.getType() == TransactionQueryForm.QueryType.ACTIVE) { selectQuery.addConditions(TRANSACTION.STOP_TIMESTAMP.isNull()); + + } else if (form.getType() == TransactionQueryForm.QueryType.STOPPED) { + selectQuery.addConditions(TRANSACTION.STOP_TIMESTAMP.isNotNull()); } processType(selectQuery, form); diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java index c6d29d816..ef16d8d7c 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java @@ -75,7 +75,8 @@ public QueryPeriodType getPeriodType() { @RequiredArgsConstructor public enum QueryType { ALL("All"), - ACTIVE("Active"); + ACTIVE("Active"), + STOPPED("Stopped"); @Getter private final String value; From e3eba4638061a6cd1f273581c0108158c5e1ed34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sevket=20G=C3=B6kay?= Date: Sun, 23 Feb 2025 15:54:34 +0100 Subject: [PATCH 32/32] fix the way request params are rendered on Swagger UI --- .../de/rwth/idsg/steve/web/api/OcppTagsRestController.java | 3 ++- .../de/rwth/idsg/steve/web/api/TransactionsRestController.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java index 3da064ea6..f028188c9 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java @@ -32,6 +32,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.DeleteMapping; @@ -80,7 +81,7 @@ public class OcppTagsRestController { ) @GetMapping(value = "") @ResponseBody - public List get(OcppTagQueryFormForApi params) { + public List get(@ParameterObject OcppTagQueryFormForApi params) { log.debug("Read request for query: {}", params); var response = ocppTagService.getOverview(params); diff --git a/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java index cdf13c6d1..640f2aaf6 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java @@ -31,6 +31,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -70,7 +71,7 @@ public class TransactionsRestController { ) @GetMapping(value = "") @ResponseBody - public List get(@Valid TransactionQueryForm.TransactionQueryFormForApi params) { + public List get(@Valid @ParameterObject TransactionQueryForm.TransactionQueryFormForApi params) { log.debug("Read request for query: {}", params); if (params.isReturnCSV()) {