Conversation
WalkthroughReplace custom bootstrap and file-based configuration with a Spring Boot application: introduce SteveProperties bound to YAML, switch packaging to WAR and Spring Boot starters, remove legacy boot/Jetty/property loaders, refactor CXF SOAP/WebSocket wiring and validators, migrate JOOQ/Flyway wiring, and convert many tests to SpringBootTest/DI. (49 words) Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Operator
participant SteveApp as SteveApplication
participant Spring as Spring Boot
participant Props as SteveProperties
participant Jooq as JooqConfiguration
participant CXF as OcppSoapConfiguration
participant WS as OcppWebSocketConfiguration
Operator->>SteveApp: java -jar target/steve.war
SteveApp->>Spring: SpringApplication.run(...)
Spring->>Props: bind application.yml -> steve.*
Spring->>Jooq: create DataSource & DSLContext (use SteveProperties)
Spring->>CXF: publish EndpointImpl beans (addresses from SteveProperties)
Spring->>WS: register WebSocket handlers (pathInfix from SteveProperties)
Spring-->>Operator: application started
sequenceDiagram
autonumber
participant Client as OCPP Client
participant Router as Router Endpoint
participant Mediator as MediatorInInterceptor
participant Map as Endpoint Lookup (EndpointImpl list)
participant Service as Target OCPP Service
Client->>Router: SOAP request to routerEndpointPath
Router->>Mediator: inbound message
Mediator->>Map: resolve target by namespace/action
Map-->>Mediator: EndpointImpl (target)
Mediator->>Service: invoke matched OCPP service
Service-->>Client: SOAP response
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (12)
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (1)
91-103: Only the first appender is processed; multi-appender support is broken.
getActiveLogFilePaths()uses a singleif (hasNext())and returns at most one path, makingrollTheDice()effectively unreachable. Iterate all appenders.Apply:
- var logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - var appenderIterator = logger.iteratorForAppenders(); - - var fileNameList = new ArrayList<Path>(); - if (appenderIterator.hasNext()) { - var appender = appenderIterator.next(); - var fileName = extractFileName(appender); - if (fileName != null) { - fileNameList.add(Paths.get(fileName)); - } - } - return fileNameList; + var logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + var appenderIterator = logger.iteratorForAppenders(); + + var fileNameList = new ArrayList<Path>(); + while (appenderIterator.hasNext()) { + var appender = appenderIterator.next(); + var fileName = extractFileName(appender); + if (fileName != null) { + fileNameList.add(Paths.get(fileName)); + } + } + return fileNameList;steve-jooq/pom.xml (1)
183-191: Ensure jOOQ codegen uses the same version as runtimesteve-jooq/pom.xml — plugin dependencies use ${jooq.version} (lines 182–191); runtime dependency org.jooq:jooq is declared without a version (lines 31–33) and no <jooq.version> property is defined in the repository. Add/define <jooq.version> in the parent POM (pom.xml) and set it to the Spring Boot-managed jOOQ version, or make the plugin dependencies use that same runtime version.
pom.xml (1)
37-48: Build break: ${lombok.version} is referenced but not defined.annotationProcessorPaths requires an explicit version. Define the property and use it consistently.
Apply:
@@ <properties> <java.version>21</java.version> @@ - <cxf.version>4.1.3</cxf.version> + <cxf.version>4.1.3</cxf.version> + <lombok.version>1.18.34</lombok.version> @@ <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> - <version>${lombok.version}</version> + <version>${lombok.version}</version> </path>Optionally also pin the dependency for coherence:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> + <version>${lombok.version}</version> <scope>provided</scope> </dependency>Also applies to: 191-196
steve-api/pom.xml (1)
23-25: Mark jakarta.servlet-api as provided.Servlet API should be container-provided; avoid bundling it into artifacts.
Apply:
<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> + <scope>provided</scope> </dependency>steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
79-83: Fix stray Checkstyle text in Javadoc and wrap the long line.The embedded “Check failure…” text will re-trigger line-length violations.
- /** - * https://docs.spring.io/spring-framework/reference/web/websocket/server.html#websocket-server-runtime-configurationCheck failure[checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Check failure: [checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Line is longer than 120 characters (found 121).build and run tests / checkstyleView detailsCode has alerts. Press enter to view. - * - * Otherwise, defaults come from {@link WebSocketConstants} - */ + /** + * See Spring docs: + * https://docs.spring.io/spring-framework/reference/web/websocket/server.html#websocket-server-runtime-configuration + * Otherwise, defaults come from {@link WebSocketConstants}. + */steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (1)
76-82: Be robust to trailing slashes and URL-encoding when extracting chargeBoxId.Current logic returns empty for subtle path variants or includes a trailing “/”. Decode and trim.
- var chargeBoxId = getLastBitFromUrl(pathInfix, request.getURI().getPath()); + var chargeBoxId = getLastBitFromUrl(pathInfix, request.getURI().getPath()); + if (chargeBoxId.endsWith("/")) { + chargeBoxId = chargeBoxId.substring(0, chargeBoxId.length() - 1); + } + chargeBoxId = java.net.URLDecoder.decode(chargeBoxId, java.nio.charset.StandardCharsets.UTF_8);steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
55-73: Fix servlet/filter URL pattern normalization (avoid broken mappings).
Risk: wrong/duplicate wildcards (e.g., "/steve/" + "") and DispatcherServlet mapped to an exact path ("/steve/") instead of a path pattern ("/steve/*"). Normalize once and reuse for both servlet and filter.Apply this diff and helper:
- var web = servletContext.addServlet("spring-dispatcher", new DispatcherServlet(springContext)); - web.setLoadOnStartup(1); - web.addMapping(steveProperties.getPaths().getRootMapping()); + var web = servletContext.addServlet("spring-dispatcher", new DispatcherServlet(springContext)); + web.setLoadOnStartup(1); + var rootPattern = normalizeMapping(steveProperties.getPaths().getRootMapping()); + web.addMapping(rootPattern); - var cxf = servletContext.addServlet("cxf", new CXFServlet()); - cxf.setLoadOnStartup(1); - cxf.addMapping(steveProperties.getPaths().getSoapMapping() + "/*"); + var cxf = servletContext.addServlet("cxf", new CXFServlet()); + cxf.setLoadOnStartup(1); + var soapPattern = normalizeMapping(steveProperties.getPaths().getSoapMapping()); + cxf.addMapping(soapPattern); - .addMappingForUrlPatterns( + .addMappingForUrlPatterns( EnumSet.allOf(DispatcherType.class), true, - steveProperties.getPaths().getRootMapping() + "*"); + rootPattern);Add this helper inside the class:
private static String normalizeMapping(String path) { if (path == null || path.isEmpty() || "/".equals(path)) return "/"; if (path.endsWith("/*")) return path; return path.endsWith("/") ? path + "*" : path + "/*"; }steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
76-82: Unify duplicate PasswordEncoder fields.
Both passwordEncoder and encoder are injected; keep one to avoid confusion and DI surprises.Apply this diff:
- private final PasswordEncoder passwordEncoder; + private final PasswordEncoder passwordEncoder; private final ObjectMapper mapper; private final WebUserRepository webUserRepository; private final SecurityContextHolderStrategy securityContextHolderStrategy = getContextHolderStrategy(); - private final PasswordEncoder encoder;And replace usages:
- webUserRepository.changePassword(username, encoder.encode(newPassword)); + webUserRepository.changePassword(username, passwordEncoder.encode(newPassword));- webUserRepository.changePassword(form.getWebUserPk(), encoder.encode(form.getPassword())); + webUserRepository.changePassword(form.getWebUserPk(), passwordEncoder.encode(form.getPassword()));- newPassword = encoder.encode(form.getApiPassword()); + newPassword = passwordEncoder.encode(form.getApiPassword());- encPw = encoder.encode(rawPassword); + encPw = passwordEncoder.encode(rawPassword);steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (1)
44-51: Null-safe validation for list value (avoid NPE).Bean Validation convention: null value is considered valid; current loop NPEs on null lists.
File: steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (lines 44-51)
@Override public boolean isValid(List<String> value, ConstraintValidatorContext context) { - for (var s : value) { + if (value == null) { + return true; + } + for (var s : value) { if (!validator.isValid(s, context)) { return false; } } return true; }Do we want to treat null elements inside the list as invalid? If yes, add
if (s == null) return false;inside the loop.steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
114-117: Ensure jOOQ participates in Spring-managed transactionsWrap the DataSource with TransactionAwareDataSourceProxy so @transactional boundaries are honored; otherwise jOOQ may operate on non-transactional connections.
Apply:
@@ - .set(new DataSourceConnectionProvider( - // new org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy() - dataSource)) + .set(new DataSourceConnectionProvider( + new org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy(dataSource)))steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (1)
101-105: Path variable authorization likely broken without MVC matchersUsing WebExpressionAuthorizationManager with "#name" requires MvcRequestMatcher so path variables are exposed. With simple String matchers, "#name" is null, denying legit requests.
Apply:
@@ - public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveProperties steveProperties) + public SecurityFilterChain securityFilterChain( + HttpSecurity http, + SteveProperties steveProperties, + org.springframework.web.servlet.handler.HandlerMappingIntrospector introspector) throws Exception { @@ - .requestMatchers(prefix + "/webusers/password/{name}") - .access(new WebExpressionAuthorizationManager("#name == authentication.name")) - .requestMatchers(prefix + "/webusers/apipassword/{name}") - .access(new WebExpressionAuthorizationManager("#name == authentication.name")) + .requestMatchers(new org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher.Builder(introspector) + .pattern(prefix + "/webusers/password/{name}") + .build()) + .access(new WebExpressionAuthorizationManager("#name == authentication.name")) + .requestMatchers(new org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher.Builder(introspector) + .pattern(prefix + "/webusers/apipassword/{name}") + .build()) + .access(new WebExpressionAuthorizationManager("#name == authentication.name"))steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
586-592: Fix assertion: sampledValues cannot be empty; prevents IndexOutOfBounds.
assertThat(sampledValues).isEmpty()contradictssampledValues.get(0). Replace with a non‑empty assertion.- var sampledValues = meterValue.getSampledValue(); - assertThat(sampledValues).isEmpty(); + var sampledValues = meterValue.getSampledValue(); + assertThat(sampledValues).isNotEmpty();
🧹 Nitpick comments (41)
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (2)
91-92: Optional: guard against non‑Logback backends to avoid ClassCastException.If SLF4J binding changes, the cast to
ch.qos.logback.classic.Loggerwill fail. Guard and degrade gracefully.- var logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - var appenderIterator = logger.iteratorForAppenders(); + var root = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + if (!(root instanceof Logger logger)) { + log.debug("Root logger is not Logback; cannot resolve file appenders."); + return List.of(); + } + var appenderIterator = logger.iteratorForAppenders();
102-102: Optional: return an unmodifiable snapshot.Prevents accidental mutation of internal state.
- return fileNameList; + return List.copyOf(fileNameList);steve/src/main/resources/logback-spring.xml (2)
3-5: Prefer system property user.home for portable LOG_DIR default.
${USER_HOME}isn’t standard;${HOME}may be missing on Windows. Use${user.home}to avoid platform surprises.Apply:
- <springProperty scope="context" name="LOG_DIR" source="logging.file.path" defaultValue="${USER_HOME:-${HOME:-.}}/logs"/> + <springProperty scope="context" name="LOG_DIR" source="logging.file.path" defaultValue="${user.home:-.}/logs"/>
13-27: Consider size-and-time rolling to cap single-day file growth.Current policy limits history and total size but not per‑day file size. If traffic spikes, a single daily file can explode. Size‑and‑time policy mitigates this.
Example:
- <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_DIR}/steve.log</file> - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${LOG_DIR}/steve-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern> + <maxFileSize>100MB</maxFileSize> <maxHistory>30</maxHistory> <totalSizeCap>3GB</totalSizeCap> </rollingPolicy>steve-jooq/pom.xml (1)
26-29: De-duplicate jOOQ runtime: prefer Boot starter or plain jOOQ, not both.spring-boot-starter-jooq already provides org.jooq:jooq; keeping the explicit jooq dependency can cause version drift.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jooq</artifactId> </dependency> - <dependency> - <groupId>org.jooq</groupId> - <artifactId>jooq</artifactId> - </dependency>Also applies to: 31-33
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java (1)
31-31: Constructor path exercised with null; add a SteveProperties-backed test.To guard the new configuration path, add a test constructing the validator via SteveProperties to ensure regex parity with the legacy constructor.
steve-api/pom.xml (1)
33-35: Avoid redundant test libs with spring-boot-starter-test.The starter already brings JUnit Jupiter, Mockito, AssertJ, and JsonPath (transitively). Keeping duplicates risks version skew.
Apply:
<dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>com.jayway.jsonpath</groupId> - <artifactId>json-path</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-params</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-junit-jupiter</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <scope>test</scope> - </dependency>If any are intentionally newer/different, keep only those and exclude from the starter.
Also applies to: 43-61
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
44-44: Avoid redundant client creation in beforeRepeat.Reuse the ThreadLocal client instead of creating a second proxy.
Apply:
- var boot = getForOcpp16(soapPath) + var boot = client.get() .bootNotification( new BootNotificationRequest()Also applies to: 57-57, 60-61
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (2)
40-53: Move logging out of static initializer; keep only system setup here.Logback may not be fully initialized this early. Set timezone/provider in static block, but log on ApplicationStartedEvent.
static { // Apache CXF LogUtils.setLoggerClass(Slf4jLogger.class); // For Hibernate validator System.setProperty("org.jboss.logging.provider", "slf4j"); var zoneId = ZoneOffset.UTC; TimeZone.setDefault(TimeZone.getTimeZone(zoneId)); - log.info( - "Date/time zone of the application is set to {}. Current date/time: {}", - zoneId, - ZonedDateTime.now(zoneId)); } + +@EventListener(org.springframework.boot.context.event.ApplicationStartedEvent.class) +public void logStartup() { + var zoneId = ZoneOffset.UTC; + log.info("Date/time zone set to {}. Now: {}", zoneId, ZonedDateTime.now(zoneId)); +}
28-31: UTC default TZ: verify DB/session expectations.Global TZ change is intentional; ensure DB/JDBC and cron/scheduler semantics expect UTC to avoid subtle timestamp shifts.
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (2)
91-97: Internet check on bean creation may slow startup and couples to network.Consider lazy init or scheduling the check post-startup; default to Dummy when offline and let a background task swap to GithubReleaseCheckService.
-@Bean -public ReleaseCheckService releaseCheckService(SteveProperties steveProperties) { - if (InternetChecker.isInternetAvailable(steveProperties.getSteveVersion())) { - return new GithubReleaseCheckService(steveProperties); - } else { - return new DummyReleaseCheckService(); - } -} +@Bean +public ReleaseCheckService releaseCheckService(SteveProperties steveProperties) { + return new DummyReleaseCheckService(); // fast, offline-safe default +} +// Option: publish an event that swaps the impl after a successful async reachability check.
103-113: ObjectMapper tweaks: good; consider centralizing.Config here mirrors the @primary bean below. Prefer configuring only the @primary mapper and letting Spring reuse it to avoid drift.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (2)
120-123: Use isEmpty() and remove map entry atomically.Small polish and intent clarity.
- if (endpointDeque.size() == 0) { - lookupTable.remove(chargeBoxId); - } + if (endpointDeque.isEmpty()) { + lookupTable.remove(chargeBoxId); + }
166-168: getACopy returns live Deques.ImmutableMap.copyOf wraps the map, not the Deque values. Expose unmodifiable copies to prevent external mutation.
- public Map<String, Deque<SessionContext>> getACopy() { - return ImmutableMap.copyOf(lookupTable); - } + public Map<String, Deque<SessionContext>> getACopy() { + return lookupTable.entrySet().stream() + .collect(ImmutableMap.toImmutableMap( + Map.Entry::getKey, + e -> new ArrayDeque<>(e.getValue()))); // copy each deque + }steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (1)
91-99: Use the same client instance within the loop to reduce connection overhead.You already create httpClient; reuse getForOcpp16(soapPath) via threadLocalClient for all calls in toRepeat as well to avoid extra stubs per repeat.
Also applies to: 101-109
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (2)
64-66: Normalize slashes when building pathInfix to avoid handshake mismatches.Different property values (with/without leading/trailing “/”) can break getLastBitFromUrl().
- var pathInfix = steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/"; + var mapping = steveProperties.getPaths().getWebsocketMapping(); + var router = steveProperties.getPaths().getRouterEndpointPath(); + var pathInfix = "/" + strip(mapping) + "/" + strip(router) + "/"; + // helper: + // private static String strip(String s){ return s.replaceAll("^/+|/+$",""); }
74-76: Don’t hardcode allowed origins.Make it configurable (and default to “*” only in dev).
- registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") - .setHandshakeHandler(handshakeHandler) - .setAllowedOrigins("*"); + registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") + .setHandshakeHandler(handshakeHandler) + .setAllowedOrigins(steveProperties.getWeb().getAllowedOrigins().toArray(String[]::new));steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (1)
110-116: Nit: typo in local name ‘requestedProcotol’.Rename to requestedProtocol for clarity (no behavior change).
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
94-101: Minor: use isBlank() for header value check.
Slightly clearer and trims whitespace.- var encodedApiPassword = headerVal == null || headerVal.isEmpty() ? null : passwordEncoder.encode(headerVal); + var encodedApiPassword = (headerVal == null || headerVal.isBlank()) ? null : passwordEncoder.encode(headerVal);steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (2)
70-86: Initialize Mockito mocks to satisfy NullAway.
Explicitly open mocks in setUp so static analysis recognizes initialization before use.@@ @BeforeEach - public void setUp() { + public void setUp() { + org.mockito.MockitoAnnotations.openMocks(this); var ctx = new AnnotationConfigApplicationContext(); ctx.register(ValidationConfig.class); ctx.register(SteveProperties.class); ctx.refresh(); var validator = ctx.getBean(Validator.class); mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService)) .setValidator(validator)); }
60-68: Optional: return LocalValidatorFactoryBean as Validator bean directly.
Slight simplification; avoids hunting by type and is idiomatic.@Configuration static class ValidationConfig { - @Bean - public ValidatorFactory validator(ApplicationContext applicationContext) { - var factory = new LocalValidatorFactoryBean(); - factory.setApplicationContext(applicationContext); - return factory; - } + @Bean + public LocalValidatorFactoryBean validator(ApplicationContext applicationContext) { + var factory = new LocalValidatorFactoryBean(); + factory.setApplicationContext(applicationContext); + return factory; + } }steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (2)
41-44: @Autowired on single constructor is unnecessary.Spring will inject the only constructor automatically. Dropping the annotation reduces noise.
- @Autowired - public ChargeBoxIdValidator(SteveProperties steveProperties) { + public ChargeBoxIdValidator(SteveProperties steveProperties) {
46-47: Consider guarding against invalid regex in config.If the admin supplies a malformed regex, Pattern.compile throws at context creation. If you prefer a clearer error, catch PatternSyntaxException and wrap with the property key for quicker diagnosis.
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java (1)
26-33: Add a Javadoc summary line to satisfy the linter.Current Javadoc lacks the required first summary sentence.
/** + * Central application configuration properties bound under the "steve" prefix. * @author Sevket Goekay <sevketgokay@gmail.com> * @since 19.08.2014 */steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (2)
62-70: Avoid hard-coding MySQL driver properties that can break environmentsForcing useSSL=true and characterEncoding may fail against non-SSL dev DBs or conflict with URL parameters. Prefer configuring via the JDBC URL or a property.
Example adjustment:
@@ - hc.addDataSourceProperty(PropertyKey.characterEncoding.getKeyName(), "utf8"); hc.addDataSourceProperty(PropertyKey.connectionTimeZone.getKeyName(), dbTimeZoneId); - hc.addDataSourceProperty(PropertyKey.useSSL.getKeyName(), true); + // Prefer configuring encoding/SSL via JDBC URL (e.g., sslMode=REQUIRED/PREFERRED, useUnicode=true&characterEncoding=utf8mb4)
45-52: Expose pool sizing from propertiesConsider sourcing maxPoolSize/minIdle/connectionTimeout from configuration to avoid hidden defaults in different environments.
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (2)
120-137: Make the exception assertion deterministicCurrently the lambda contains multiple calls; assert on the specific call that is expected to throw to avoid false positives.
- assertThatExceptionOfType(WebServiceException.class).isThrownBy(() -> { - var boot = client.bootNotification( + assertThatExceptionOfType(WebServiceException.class).isThrownBy(() -> + client.bootNotification( new ocpp.cs._2015._10.BootNotificationRequest() .withChargePointVendor(getRandomString()) .withChargePointModel(getRandomString()), - getRandomString()); - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isEqualTo(ocpp.cs._2015._10.RegistrationStatus.REJECTED); - - var auth = client.authorize( - new ocpp.cs._2015._10.AuthorizeRequest().withIdTag(getRandomString()), getRandomString()); - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(ocpp.cs._2015._10.AuthorizationStatus.INVALID); - }); + getRandomString()) + );
68-73: Handle null server.address in getSoapPath — default to localhost or set it for testsHelpers.getSoapPath concatenates serverProperties.getAddress() directly; application.yml leaves server.address as a placeholder (@server.host@) and no application-test.* was found, so serverProperties.getAddress() can be null and produce URLs like http://null:port/... Either set server.address for the test profile or default the host to "localhost" in Helpers.getSoapPath.
Locations:
- steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java : getSoapPath
- steve/src/main/resources/application.yml : server.address placeholder
- steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java : calls getSoapPath(...)
Suggested minimal change:
- return prefix + serverProperties.getAddress() + ":" + serverProperties.getPort() + var host = serverProperties.getAddress() != null ? serverProperties.getAddress() : "localhost"; + return prefix + host + ":" + serverProperties.getPort()steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (3)
67-74: Hard‑coded schema name is brittleSCHEMA_TO_TRUNCATE is fixed. If test schema changes via properties, prepare() will fail. Prefer deriving from ctx.meta()/DefaultCatalog or passing the schema via constructor/config.
- private static final String SCHEMA_TO_TRUNCATE = "stevedb_test_2aa6a783d47d"; + private final String schemaToTruncate; @@ - public void prepare() { + public void prepare() { runOperation(ctx -> { - truncateTables(ctx); + truncateTables(ctx, schemaToTruncate); insertChargeBox(ctx); insertOcppIdTag(ctx); }); }And overload truncateTables to accept the schema name.
158-160: Group prepare operations in a single transactiontruncate + inserts should be atomic to keep test DB consistent on failures.
- private void runOperation(Consumer<DSLContext> consumer) { - consumer.accept(dslContext); - } + private void runOperation(Consumer<DSLContext> consumer) { + dslContext.transaction(cfg -> consumer.accept(DSL.using(cfg))); + }
162-189: Skip list relies on generated table constantsIf codegen settings change (e.g., view/table renames), skipping by constants may break. Consider using table.getType().isView() where supported, or an allowlist derived from metadata.
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (2)
73-89: Use Spring’s static resource matcher to future‑proof assetsPrefer PathRequest.toStaticResources() to avoid maintaining explicit /static/** etc.
- .requestMatchers( - "/", // we have RootRedirectController to redirect "/" to "/manager" - "/static/**", - steveProperties.getPaths().getSoapMapping() + "/**", - steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/**") + .requestMatchers("/") + .permitAll() + .requestMatchers(org.springframework.boot.autoconfigure.security.servlet.PathRequest.toStaticResources().atCommonLocations()) .permitAll()Retain SOAP/WS permits as separate matchers.
59-61: BCrypt strength configurabilityDefault strength 10 is fine; consider making it configurable if login volume or hardware varies.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (2)
613-618: Remove unused helper or use truncation API.
nowWithoutMillis()is unused. Either remove it or usetruncatedTo(ChronoUnit.SECONDS)if kept.- /** - * https://github.com/steve-community/steve/issues/1371 - */ - private static OffsetDateTime nowWithoutMillis() { - return OffsetDateTime.now().with(ChronoField.MILLI_OF_SECOND, 0); - } + // (Optional) If needed later: + // private static OffsetDateTime nowWithoutMillis() { + // return OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS); + // }
74-99: Consider RANDOM_PORT to avoid CI port contention.
@SpringBootTest(webEnvironment = DEFINED_PORT)can flake if the port is busy. If feasible, switch toRANDOM_PORTand derive the URL from@LocalServerPort(or adaptgetSoapPathaccordingly).steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (2)
103-115: Avoid direct @bean method call; inject LoggingFeature.Calling
loggingFeature()inside the config relies on proxying and can create multiple instances ifproxyBeanMethods=false. Inject it instead.- private EndpointImpl createDefaultEndpoint( - Bus bus, - MessageIdInterceptor messageIdInterceptor, - MessageHeaderInterceptor messageHeaderInterceptor, - Object serviceBean, - String address) { + private EndpointImpl createDefaultEndpoint( + Bus bus, + MessageIdInterceptor messageIdInterceptor, + MessageHeaderInterceptor messageHeaderInterceptor, + LoggingFeature loggingFeature, + Object serviceBean, + String address) { return createEndpoint( bus, serviceBean, address, List.of(messageIdInterceptor, messageHeaderInterceptor), - List.of(loggingFeature())); + List.of(loggingFeature)); }And pass the bean to callers:
- public EndpointImpl ocpp16Endpoint(Bus bus, MessageIdInterceptor messageIdInterceptor, - MessageHeaderInterceptor messageHeaderInterceptor, - ocpp.cs._2015._10.CentralSystemService ocpp16Server) { - return createDefaultEndpoint(bus, messageIdInterceptor, messageHeaderInterceptor, ocpp16Server, "/CentralSystemServiceOCPP16"); + public EndpointImpl ocpp16Endpoint(Bus bus, MessageIdInterceptor messageIdInterceptor, + MessageHeaderInterceptor messageHeaderInterceptor, + LoggingFeature loggingFeature, + ocpp.cs._2015._10.CentralSystemService ocpp16Server) { + return createDefaultEndpoint(bus, messageIdInterceptor, messageHeaderInterceptor, loggingFeature, + ocpp16Server, "/CentralSystemServiceOCPP16"); }(Apply similarly to ocpp12/ocpp15.)
88-101: Reduce hard dependency on ocpp12 for router.Router uses an ocpp12 service instance as a dummy just to publish an endpoint. Consider a trivial no-op service bean to decouple.
steve/pom.xml (2)
65-90: Rely on Boot BOM; trim duplicate/managed deps.Many artifacts are already pulled by Spring Boot starters (Jackson, SLF4J, Spring Security config, Hikari). Keeping explicit entries risks version drift, especially with Jetty where direct Jetty modules may conflict with
spring-boot-starter-jetty.
- Prefer: spring-boot-starter-web (+ jetty starter), spring-boot-starter-websocket, spring-boot-starter-security, spring-boot-starter-jdbc.
- Remove or justify: direct Jetty modules, jackson-*, slf4j-api, jcl-over-slf4j, spring-security-config, HikariCP, byte-buddy, junit engines (all provided by starters).
- Keep: mysql-connector-j, CXF transport (test), any Jetty extras not covered by starters (only if required).
Also applies to: 109-128, 129-159, 170-190
1-1: Address CI failure: license headers.
license-maven-plugin:checkfailed. Runmvn license:format(or project’s wrapper task) and commit updated headers.steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (2)
25-25: Avoid leaking CXF impl types in the interceptor APIDepending on
org.apache.cxf.jaxws.EndpointImplin the public ctor couples this class to a specific CXF/JAX‑WS impl. Prefer takingCollection<Server>or an already-builtMap<String, Server>and build the lookup in configuration.Example alternative signature:
- public MediatorInInterceptor(List<EndpointImpl> endpoints) { + public MediatorInInterceptor(Map<String, Server> serversByNamespace) { super(Phase.POST_STREAM); super.addBefore(StaxInInterceptor.class.getName()); - actualServers = initServerLookupMap(endpoints); + this.actualServers = Map.copyOf(serversByNamespace); }
85-94: No routing target ⇒ silent dropWhen
targetServeris null we silently abort the chain. At minimum log; ideally return a SOAP Fault.Apply:
- if (targetServer != null) { - targetServer.getDestination().getMessageObserver().onMessage(message); - } + if (targetServer != null) { + targetServer.getDestination().getMessageObserver().onMessage(message); + } else { + log.warn("No server mapped for namespace '{}'", schemaNamespace); + }If you want to hard‑fail, consider throwing a CXF
Faultwith a client error instead of aborting silently.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttfsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.icosteve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svgsteve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (59)
AGENTS.md(1 hunks)pom.xml(4 hunks)steve-api/pom.xml(1 hunks)steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks)steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks)steve-core/pom.xml(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks)steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks)steve-jooq/pom.xml(4 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(4 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks)steve-ocpp-1-x/pom.xml(2 hunks)steve-ocpp-transport-soap/pom.xml(1 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks)steve-ocpp-transport-websocket/pom.xml(0 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(2 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(6 hunks)steve-ocpp/pom.xml(0 hunks)steve-ui-jsp/pom.xml(2 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks)steve/README.md(1 hunks)steve/pom.xml(4 hunks)steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks)steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks)steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks)steve/src/main/resources/application.yml(1 hunks)steve/src/main/resources/config/main.properties(0 hunks)steve/src/main/resources/logback-spring.xml(2 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(4 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(4 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(5 hunks)steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks)
💤 Files with no reviewable changes (11)
- steve-ocpp-transport-websocket/pom.xml
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
- steve-ocpp/pom.xml
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
- steve/src/main/resources/config/main.properties
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/java/de/rwth/idsg/steve/Application.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.javasteve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.javasteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.javasteve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.javasteve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.javasteve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue81.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.javasteve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.javasteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/main/resources/application.ymlsteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javaAGENTS.mdsteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/README.mdsteve/src/main/resources/logback-spring.xmlsteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/pom.xml
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve-jooq/pom.xmlsteve/src/test/java/de/rwth/idsg/steve/StressTest.javaAGENTS.mdpom.xml
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-jooq/pom.xmlAGENTS.md
📚 Learning: 2025-09-15T15:31:15.967Z
Learnt from: juherr
PR: juherr/evolve#91
File: steve-core/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java:125-129
Timestamp: 2025-09-15T15:31:15.967Z
Learning: In the steve-core OCPP project, JAXB bindings are customized in ocpp_binding.xml to map xs:dateTime XML types directly to java.time.OffsetDateTime using the JavaDateTimeConverter adapter, eliminating the need for XMLGregorianCalendar conversions that would normally be required in standard JAXB bindings.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
🧬 Code graph analysis (5)
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
Configuration(39-121)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
Slf4j(39-74)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(55-226)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java (1)
RequiredArgsConstructor(56-213)steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (1)
RequiredArgsConstructor(313-317)
🪛 GitHub Actions: analyze and review code
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-1-x/pom.xml
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-transport-soap/pom.xml
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
[error] 1-1: license-maven-plugin:check failed. Some files do not have the expected license header. Run 'license:format' to update them.
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 2-2: syntax error: found character '@' that cannot start any token
(syntax)
🪛 GitHub Actions: build and run tests
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
[warning] 76-76: NullAway: initializer may not guarantee @nonnull field chargePointsService; ensure proper initialization.
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
[warning] 27-27: MissingSummary: A summary line is required on public/protected Javadocs.
[warning] 63-63: NullAway: initializer method does not guarantee @nonnull field schemaSource is initialized along all control-flow paths.
pom.xml
[warning] 1-1: Build warning: Maven project has deprecated property usage in effective model (generic deprecation warnings).
🪛 markdownlint-cli2 (0.18.1)
steve/README.md
77-77: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
78-78: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
79-79: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
80-80: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: build (ubuntu-22.04, 24, mysql:8.0)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.6.14)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.3)
- GitHub Check: build (ubuntu-24.04, 24, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-24.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.3)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.5.21)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (java-kotlin)
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java
Show resolved
Hide resolved
| private static @Nullable TLSClientParameters create(Ssl ssl) throws GeneralSecurityException, IOException { | ||
| if (!ssl.isEnabled()) { | ||
| return null; | ||
| } | ||
| var tlsClientParams = new TLSClientParameters(); | ||
|
|
||
| var socketFactory = SslContextBuilder.builder() | ||
| .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) | ||
| .usingTLS() | ||
| .usingDefaultAlgorithm() | ||
| .usingKeyManagerPasswordFromKeyStore() | ||
| .buildMergedWithSystem() | ||
| .getSocketFactory(); | ||
| tlsClientParams.setSSLSocketFactory(socketFactory); | ||
|
|
||
| return tlsClientParams; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
🧩 Analysis chain
Null‑safe SSL setup and trust store support.
Avoid NPE when SSL is unset and honor optional trust store for server cert validation.
- private static @Nullable TLSClientParameters create(Ssl ssl) throws GeneralSecurityException, IOException {
- if (!ssl.isEnabled()) {
- return null;
- }
+ private static @Nullable TLSClientParameters create(@Nullable Ssl ssl)
+ throws GeneralSecurityException, IOException {
+ if (ssl == null || !ssl.isEnabled()) {
+ return null;
+ }
var tlsClientParams = new TLSClientParameters();
- var socketFactory = SslContextBuilder.builder()
- .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword())
+ var builder = SslContextBuilder.builder();
+ if (ssl.getKeyStore() != null) {
+ builder = builder.keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword());
+ }
+ if (ssl.getTrustStore() != null) {
+ builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword());
+ }
+ var socketFactory = builder
.usingTLS()
.usingDefaultAlgorithm()
.usingKeyManagerPasswordFromKeyStore()
.buildMergedWithSystem()
.getSocketFactory();
tlsClientParams.setSSLSocketFactory(socketFactory);
return tlsClientParams;
}Please confirm the Spring Boot version. If using a variant where Ssl has getEnabled() instead of isEnabled(), adjust accordingly.
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "=== locate ClientProvider.java ==="
rg -n --hidden --no-ignore-vcs 'ClientProvider\.java' -S || true
FILE=$(rg -n --hidden --no-ignore-vcs 'ClientProvider\.java' -S --files | head -n1 || true)
if [ -n "$FILE" ] && [ -f "$FILE" ]; then
echo "FOUND: $FILE"
echo "---- file start (first 220 lines) ----"
sed -n '1,220p' "$FILE"
echo "---- file end ----"
else
echo "ClientProvider.java not found in repository."
fi
echo
echo "=== search for Ssl type definitions ==="
rg -n --hidden --no-ignore-vcs 'class\s+Ssl\b|interface\s+Ssl\b|record\s+Ssl\b' -S || true
echo
echo "=== search for usages of 'Ssl' ==="
rg -n --hidden --no-ignore-vcs '\bSsl\b' -S || true
echo
echo "=== locate pom/build files and Spring Boot references ==="
rg -n --hidden --no-ignore-vcs '<artifactId>spring-boot|spring-boot-starter|spring-boot.version|spring.boot.version|springBootVersion' -S --glob '!**/target/**' || true
rg -n --hidden --no-ignore-vcs 'org.springframework.boot' -S --glob '!**/target/**' || true
if [ -f pom.xml ]; then
echo
echo "---- top-level pom.xml (first 200 lines) ----"
sed -n '1,200p' pom.xml || true
else
echo
echo "No top-level pom.xml; listing pom.xml files (paths):"
rg -n --hidden --no-ignore-vcs '^<project' -S --glob '!**/target/**' -g 'pom.xml' || true
fi
echo
echo "=== done ==="Length of output: 7655
Null‑safe SSL setup and optional trust‑store support.
Make create(...) accept a nullable Ssl, return null when ssl is null or disabled, and only add keyStore/trustStore to the builder when configured. Ssl exposes isEnabled() (and static isEnabled(Ssl) since 3.1.0) and has trust-store getters — not getEnabled(). (docs.spring.io)
Location: steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (create method, ~lines 76–92). Spring Boot parent in pom.xml is 3.5.6.
- private static @Nullable TLSClientParameters create(Ssl ssl) throws GeneralSecurityException, IOException {
- if (!ssl.isEnabled()) {
- return null;
- }
+ private static @Nullable TLSClientParameters create(@Nullable Ssl ssl)
+ throws GeneralSecurityException, IOException {
+ if (ssl == null || !ssl.isEnabled()) {
+ return null;
+ }
var tlsClientParams = new TLSClientParameters();
- var socketFactory = SslContextBuilder.builder()
- .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword())
+ var builder = SslContextBuilder.builder();
+ if (ssl.getKeyStore() != null) {
+ builder = builder.keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword());
+ }
+ if (ssl.getTrustStore() != null) {
+ builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword());
+ }
+ var socketFactory = builder
.usingTLS()
.usingDefaultAlgorithm()
.usingKeyManagerPasswordFromKeyStore()
.buildMergedWithSystem()
.getSocketFactory();
tlsClientParams.setSSLSocketFactory(socketFactory);
return tlsClientParams;
}🤖 Prompt for AI Agents
In
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java
around lines 76–92, make create(...) null-safe and support optional trust-store:
change the signature to accept a nullable Ssl, return null immediately if ssl is
null or !ssl.isEnabled(), and only call the SslContextBuilder
keyStore/trustStore methods when the corresponding getters are non-null (e.g.
call keyStoreFromFile(...) only when ssl.getKeyStore() != null and include key
manager password only if ssl.getKeyStorePassword() is present; likewise add a
trustStoreFromFile(...) only when ssl.getTrustStore() != null and its password
when present). Keep the rest of the TLSClientParameters setup the same and
ensure imports/annotations reflect the nullable parameter.
| public MediatorInInterceptor(List<EndpointImpl> endpoints) { | ||
| super(Phase.POST_STREAM); | ||
| super.addBefore(StaxInInterceptor.class.getName()); | ||
| actualServers = initServerLookupMap(bus, config); | ||
| actualServers = initServerLookupMap(endpoints); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
Validate constructor input early
Null endpoints will NPE later. Add a defensive check.
Apply:
public MediatorInInterceptor(List<EndpointImpl> endpoints) {
super(Phase.POST_STREAM);
super.addBefore(StaxInInterceptor.class.getName());
- actualServers = initServerLookupMap(endpoints);
+ java.util.Objects.requireNonNull(endpoints, "endpoints must not be null");
+ actualServers = initServerLookupMap(endpoints);
}If you keep the current signature, also document that endpoints must be already published.
🏁 Script executed:
#!/bin/bash
# Locate the MediatorInInterceptor source file and snippet around class declaration and constructor
rg -nP --type=java "class\s+MediatorInInterceptor" -C3 .
# Show constructor and initServerLookupMap usage
rg -n "initServerLookupMap" -C3 --type java steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
# Search for calls to the constructor
rg -n "new MediatorInInterceptor" -C3 --type java .
# Search for occurrences of endpoints parameter in this file
rg -n "endpoints" -C3 --type java steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javaLength of output: 3547
Validate constructor input early
Null endpoints will NPE in initServerLookupMap when iterating; add a defensive check.
File: steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (lines 49–53)
Apply:
public MediatorInInterceptor(List<EndpointImpl> endpoints) {
super(Phase.POST_STREAM);
super.addBefore(StaxInInterceptor.class.getName());
- actualServers = initServerLookupMap(endpoints);
+ java.util.Objects.requireNonNull(endpoints, "endpoints must not be null");
+ actualServers = initServerLookupMap(endpoints);
}If you keep the current signature, also document that endpoints must be already published.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public MediatorInInterceptor(List<EndpointImpl> endpoints) { | |
| super(Phase.POST_STREAM); | |
| super.addBefore(StaxInInterceptor.class.getName()); | |
| actualServers = initServerLookupMap(bus, config); | |
| actualServers = initServerLookupMap(endpoints); | |
| } | |
| public MediatorInInterceptor(List<EndpointImpl> endpoints) { | |
| super(Phase.POST_STREAM); | |
| super.addBefore(StaxInInterceptor.class.getName()); | |
| java.util.Objects.requireNonNull(endpoints, "endpoints must not be null"); | |
| actualServers = initServerLookupMap(endpoints); | |
| } |
🤖 Prompt for AI Agents
In
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
around lines 49–53, the constructor accepts a List<EndpointImpl> endpoints but
does not validate it and will NPE inside initServerLookupMap when endpoints is
null; add an early defensive check that throws a clear IllegalArgumentException
(or NullPointerException with a descriptive message) if endpoints is null, then
proceed to call initServerLookupMap; alternatively, if you want to keep the
current signature without a runtime check, add JavaDoc to the constructor
stating that endpoints must be non-null and already published before calling
this constructor.
| var bis = new BufferedInputStream(is); | ||
| bis.mark(bis.available()); | ||
| message.setContent(InputStream.class, bis); | ||
|
|
||
| String encoding = (String) message.get(Message.ENCODING); | ||
| XMLStreamReader reader = StaxUtils.createXMLStreamReader(bis, encoding); | ||
| DepthXMLStreamReader xmlReader = new DepthXMLStreamReader(reader); | ||
| var encoding = (String) message.get(Message.ENCODING); | ||
| var reader = StaxUtils.createXMLStreamReader(bis, encoding); | ||
| var xmlReader = new DepthXMLStreamReader(reader); | ||
|
|
There was a problem hiding this comment.
Fragile mark/reset: available() can be 0 → reset() may fail and drop the request
Using bis.mark(bis.available()) is unreliable for network streams. If reset() fails, the message may be partially consumed while we still abort the chain.
Use a fixed, generous read limit and handle missing content:
- var is = message.getContent(InputStream.class);
- var bis = new BufferedInputStream(is);
- bis.mark(bis.available());
+ var is = message.getContent(InputStream.class);
+ if (is == null) {
+ log.warn("No InputStream on message; cannot route");
+ return;
+ }
+ var bis = new BufferedInputStream(is);
+ // mark enough bytes to parse envelope + first body element
+ bis.mark(64 * 1024);Optionally, wrap the scan/reset in a try/finally and on reset failure log and return without aborting.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var bis = new BufferedInputStream(is); | |
| bis.mark(bis.available()); | |
| message.setContent(InputStream.class, bis); | |
| String encoding = (String) message.get(Message.ENCODING); | |
| XMLStreamReader reader = StaxUtils.createXMLStreamReader(bis, encoding); | |
| DepthXMLStreamReader xmlReader = new DepthXMLStreamReader(reader); | |
| var encoding = (String) message.get(Message.ENCODING); | |
| var reader = StaxUtils.createXMLStreamReader(bis, encoding); | |
| var xmlReader = new DepthXMLStreamReader(reader); | |
| var is = message.getContent(InputStream.class); | |
| if (is == null) { | |
| log.warn("No InputStream on message; cannot route"); | |
| return; | |
| } | |
| var bis = new BufferedInputStream(is); | |
| // mark enough bytes to parse envelope + first body element | |
| bis.mark(64 * 1024); | |
| message.setContent(InputStream.class, bis); | |
| var encoding = (String) message.get(Message.ENCODING); | |
| var reader = StaxUtils.createXMLStreamReader(bis, encoding); | |
| var xmlReader = new DepthXMLStreamReader(reader); |
🤖 Prompt for AI Agents
In
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
around lines 62 to 69, the code uses bis.mark(bis.available()) which is fragile
for network streams and can be zero causing reset() to fail and drop requests;
replace the use of available() with a fixed generous read limit (e.g. 64KB or
larger depending on expected payloads) or fully buffer the stream into a byte
array/ByteArrayOutputStream before creating the XML reader, wrap the
mark/scan/reset in a try/finally, catch IllegalStateException/IOException from
reset(), log a clear warning including context, and on reset failure return
without aborting the interceptor chain (or reattach the best-effort buffered
content) so the request is not lost.
| public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) { | ||
| var prefix = "http://"; | ||
| if (serverProperties.getSsl().isEnabled()) { | ||
| prefix = "https://"; | ||
| port = config.getJetty().getHttpsPort(); | ||
| } else { | ||
| throw new RuntimeException(); | ||
| } | ||
|
|
||
| return prefix + config.getJetty().getServerHost() + ":" + port | ||
| + config.getPaths().getContextPath() | ||
| + config.getPaths().getSoapMapping() | ||
| + config.getPaths().getRouterEndpointPath(); | ||
| return prefix + serverProperties.getAddress() + ":" + serverProperties.getPort() | ||
| + serverProperties.getServlet().getContextPath() | ||
| + steveProperties.getPaths().getSoapMapping() | ||
| + steveProperties.getPaths().getRouterEndpointPath() + "/"; | ||
| } |
There was a problem hiding this comment.
Null/format pitfalls building SOAP URL (NPEs, “null” host/port, IPv6).
- serverProperties.getSsl() may be null → NPE.
- address/port can be null → "null:null".
- IPv6 literals require [brackets] in URIs.
- servlet/contextPath may be null.
Harden path building:
- public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) {
- var prefix = "http://";
- if (serverProperties.getSsl().isEnabled()) {
- prefix = "https://";
- }
-
- return prefix + serverProperties.getAddress() + ":" + serverProperties.getPort()
- + serverProperties.getServlet().getContextPath()
- + steveProperties.getPaths().getSoapMapping()
- + steveProperties.getPaths().getRouterEndpointPath() + "/";
- }
+ public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) {
+ var ssl = serverProperties.getSsl();
+ var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "https://" : "http://";
+
+ var addr = serverProperties.getAddress();
+ var host = (addr != null) ? addr.getHostAddress() : "localhost";
+ if (host.indexOf(':') >= 0) { // IPv6
+ host = "[" + host + "]";
+ }
+
+ Integer port = serverProperties.getPort();
+ int p = (port != null) ? port : 8080;
+
+ var servlet = serverProperties.getServlet();
+ var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : "";
+
+ return scheme + host + ":" + p
+ + ctx
+ + steveProperties.getPaths().getSoapMapping()
+ + steveProperties.getPaths().getRouterEndpointPath() + "/";
+ }Alternatively, use UriComponentsBuilder to avoid manual escaping.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) { | |
| var prefix = "http://"; | |
| if (serverProperties.getSsl().isEnabled()) { | |
| prefix = "https://"; | |
| port = config.getJetty().getHttpsPort(); | |
| } else { | |
| throw new RuntimeException(); | |
| } | |
| return prefix + config.getJetty().getServerHost() + ":" + port | |
| + config.getPaths().getContextPath() | |
| + config.getPaths().getSoapMapping() | |
| + config.getPaths().getRouterEndpointPath(); | |
| return prefix + serverProperties.getAddress() + ":" + serverProperties.getPort() | |
| + serverProperties.getServlet().getContextPath() | |
| + steveProperties.getPaths().getSoapMapping() | |
| + steveProperties.getPaths().getRouterEndpointPath() + "/"; | |
| } | |
| public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) { | |
| var ssl = serverProperties.getSsl(); | |
| var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "https://" : "http://"; | |
| var addr = serverProperties.getAddress(); | |
| var host = (addr != null) ? addr.getHostAddress() : "localhost"; | |
| if (host.indexOf(':') >= 0) { // IPv6 | |
| host = "[" + host + "]"; | |
| } | |
| Integer port = serverProperties.getPort(); | |
| int p = (port != null) ? port : 8080; | |
| var servlet = serverProperties.getServlet(); | |
| var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : ""; | |
| return scheme + host + ":" + p | |
| ctx | |
| steveProperties.getPaths().getSoapMapping() | |
| steveProperties.getPaths().getRouterEndpointPath() + "/"; | |
| } |
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java around lines 49 to
59, the current manual concatenation can throw NPEs and produce invalid URIs
(ssl may be null, address/port may be null, IPv6 literals need brackets, servlet
contextPath may be null); fix by validating and defaulting values before
building the URL: guard serverProperties.getSsl() for null and choose "https"
only if non-null and enabled, treat null address/port by throwing a clear
IllegalArgumentException or using sensible defaults, wrap IPv6 addresses in
square brackets when they contain ':'; ensure servlet contextPath and other path
segments are non-null and normalized (leading slash, no double slashes, ensure
final slash), and preferably replace the manual concat with a
UriComponentsBuilder (or java.net.URI/URL builder) to assemble scheme, host,
port and path safely to avoid manual escaping and formatting mistakes.
03a2285 to
8a5f43b
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
585-596: Fix assertion: sampledValues cannot be empty if accessed at index 0.This would throw IndexOutOfBoundsException.
Apply this diff:
- assertThat(sampledValues).isEmpty(); + assertThat(sampledValues).isNotEmpty(); var thisValueFound = false; // and check, if it can be found in the DB for (var values : details.getValues()) { if (values.getValue().equals(sampledValues.get(0).getValue())) { thisValueFound = true; break; } }
🧹 Nitpick comments (16)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
57-57: Normalize servlet/filter/CXF mappings to avoid malformed patternsGuard against root/soap mappings that already include a trailing wildcard or lack a leading slash. This prevents cases like "/app//" or "/app*" and ensures "/" vs "/*" are applied correctly.
Apply:
- web.addMapping(steveProperties.getPaths().getRootMapping()); + web.addMapping(normalizeDispatcherMapping(steveProperties.getPaths().getRootMapping())); ... - cxf.addMapping(steveProperties.getPaths().getSoapMapping() + "/*"); + cxf.addMapping(normalizeFilterMapping(steveProperties.getPaths().getSoapMapping())); ... - steveProperties.getPaths().getRootMapping() + "*"); + normalizeFilterMapping(steveProperties.getPaths().getRootMapping()));Add these helpers in the class (outside the changed ranges):
private static String normalizeDispatcherMapping(String mapping) { if (mapping == null || mapping.isBlank() || "/".equals(mapping) || "/*".equals(mapping)) { return "/"; } String m = mapping.startsWith("/") ? mapping : "/" + mapping; if (m.endsWith("/*")) return m; if (m.endsWith("/")) return m + "*"; return m + "/*"; } private static String normalizeFilterMapping(String mapping) { String dm = normalizeDispatcherMapping(mapping); return "/".equals(dm) ? "/*" : dm; }Also applies to: 62-62, 72-72
steve-jooq/pom.xml (1)
26-29: Avoid duplicate jOOQ inclusion with the Boot starterspring-boot-starter-jooq already brings jOOQ; keeping both is redundant.
Apply:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jooq</artifactId> </dependency> - <dependency> - <groupId>org.jooq</groupId> - <artifactId>jooq</artifactId> - </dependency> + <!-- jOOQ is provided via the Spring Boot starter -->Also applies to: 30-33
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (2)
60-68: ReturnValidatorfrom the bean for clarity and direct useThe bean is consumed as
Validator. ReturningValidatoravoids ambiguity.- public ValidatorFactory validator(ApplicationContext applicationContext) { + public Validator validator(ApplicationContext applicationContext) { var factory = new LocalValidatorFactoryBean(); factory.setApplicationContext(applicationContext); return factory; }
75-86: Close the ad-hoc ApplicationContext to avoid resource leaks in testsRegister a shutdown hook so the context is closed after the test JVM ends.
ctx.register(ValidationConfig.class); ctx.register(SteveProperties.class); ctx.refresh(); + ctx.registerShutdownHook();steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (1)
125-132: Typo: requestedProcotol -> requestedProtocol- for (var requestedProcotol : requestedProtocols) { + for (var requestedProtocol : requestedProtocols) { - for (var item : endpoints) { - if (item.getVersion().getValue().equals(requestedProcotol)) { + for (var item : endpoints) { + if (item.getVersion().getValue().equals(requestedProtocol)) { return item; } } }steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (1)
147-153: GuardgetSizewith the same per-ID lockReading
ArrayDeque#size()without the stripe lock can race with writers. Lock for the queried chargeBoxId.@Override public int getSize(String chargeBoxId) { - var endpointDeque = lookupTable.get(chargeBoxId); - if (endpointDeque == null) { - return 0; - } - return endpointDeque.size(); + var l = locks.get(chargeBoxId); + l.lock(); + try { + var endpointDeque = lookupTable.get(chargeBoxId); + return endpointDeque == null ? 0 : endpointDeque.size(); + } finally { + l.unlock(); + } }steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (1)
91-99: Don’t reuse the same HttpUriRequest across threads
HttpUriRequestisn’t thread-safe; build a new request per iteration.- var req = RequestBuilder.post(soapPath) - .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") - .setEntity(new StringEntity(body, contentType)) - .build(); + // build request per-iteration in toRepeat()And inside
toRepeat():@Override public void toRepeat() { + var req = RequestBuilder.post(soapPath) + .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") + .setEntity(new StringEntity(body, contentType)) + .build(); var mvr = threadLocalClient .get() .meterValues( new MeterValuesRequest() .withConnectorId(connectorId) .withTransactionId(transactionId) .withMeterValue(new MeterValue() .withTimestamp(stopDateTime) .withSampledValue(new SampledValue() .withMeasurand(Measurand.ENERGY_ACTIVE_IMPORT_REGISTER) .withValue("555") .withUnit(UnitOfMeasure.WH))), chargeBoxId); assertThat(mvr).isNotNull();Also applies to: 112-140
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (2)
99-109: Guard against nullschemaSourceto avoid invalid render mapping.If
schemais set butschemaSourceis null, you’ll configure aMappedSchemawithinput=null. Require both to be non-null before mapping.- var dbSchema = steveProperties.getJooq().getSchema(); - if (dbSchema != null && !dbSchema.equals(steveProperties.getJooq().getSchemaSource())) { + var target = steveProperties.getJooq().getSchema(); + var source = steveProperties.getJooq().getSchemaSource(); + if (source != null && target != null && !target.equals(source)) { // ... settings = settings.withRenderMapping(new RenderMapping() .withSchemata(new MappedSchema() - .withInput(steveProperties.getJooq().getSchemaSource()) - .withOutput(steveProperties.getJooq().getSchema()))); + .withInput(source) + .withOutput(target))); }
63-70: Avoid forcing MySQL SSL unconditionally; prefersslModeand make it configurable.Hard-coding
useSSL=truecan break non-SSL dev DBs; MySQL 8 recommendssslModeinstead.- hc.addDataSourceProperty(PropertyKey.useSSL.getKeyName(), true); + // Let configuration decide SSL. Prefer sslMode (e.g., PREFERRED/REQUIRED) when available. + // Example: expose via steve.datasource.sslMode or rely on spring.datasource.* properties. + // hc.addDataSourceProperty("sslMode", "PREFERRED");steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (1)
49-51: Consider RANDOM_PORT to avoid port conflicts in CI.
WebEnvironment.DEFINED_PORTrelies onserver.portand can collide across tests/builds.-@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)If you keep DEFINED_PORT, ensure the port is unique and substituted during tests.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (3)
74-76: Consider RANDOM_PORT to avoid port conflicts in CI.Using DEFINED_PORT can fail on shared runners; prefer RANDOM_PORT + @LocalServerPort.
If acceptable, I can provide a patch switching to RANDOM_PORT and adapting getSoapPath via @LocalServerPort.
131-132: Javadoc link likely unresolved.{@link OcppWebSocketUpgrader} isn’t imported; use fully qualified name or import the type to avoid broken Javadoc links.
613-618: Remove unused helper and ChronoField importnowWithoutMillis() (steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java:616–617) is declared but not referenced elsewhere; delete the method and the ChronoField import at line 54.
steve/pom.xml (3)
176-179: Scope spring-security-test to test.Avoid bundling it into the WAR.
Apply this diff:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> + <scope>test</scope> </dependency>
181-184: Jetty 12 alignment: use ee10 websocket client artifact.With Boot 3.5.x (Jetty 12), prefer ee10 coordinates.
Apply this diff:
- <dependency> - <groupId>org.eclipse.jetty.websocket</groupId> - <artifactId>jetty-websocket-jetty-client</artifactId> - <scope>test</scope> - </dependency> + <dependency> + <groupId>org.eclipse.jetty.ee10.websocket</groupId> + <artifactId>jetty-ee10-websocket-jetty-client</artifactId> + <scope>test</scope> + </dependency>
129-150: Jetty deps duplication with starter-jetty.Some core Jetty artifacts (jetty-server, ee10-webapp, annotations) are likely already pulled by spring-boot-starter-jetty; keep only extras (rewrite, websocket) to reduce surface.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
-
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttf -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.ico -
steve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svg -
steve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (61)
-
.github/workflows/main.yml(1 hunks) -
AGENTS.md(2 hunks) -
Dockerfile(1 hunks) -
pom.xml(5 hunks) -
steve-api/pom.xml(1 hunks) -
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks) -
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks) -
steve-core/pom.xml(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks) -
steve-jooq/pom.xml(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks) -
steve-ocpp-1-x/pom.xml(2 hunks) -
steve-ocpp-transport-soap/pom.xml(1 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks) -
steve-ocpp-transport-websocket/pom.xml(0 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(2 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(6 hunks) -
steve-ocpp/pom.xml(0 hunks) -
steve-ui-jsp/pom.xml(2 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks) -
steve/README.md(2 hunks) -
steve/pom.xml(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks) -
steve/src/main/resources/application.yml(1 hunks) -
steve/src/main/resources/config/main.properties(0 hunks) -
steve/src/main/resources/logback-spring.xml(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(4 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(4 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks)
💤 Files with no reviewable changes (11)
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
- steve-ocpp-transport-websocket/pom.xml
- steve-ocpp/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/Application.java
- steve/src/main/resources/config/main.properties
✅ Files skipped from review due to trivial changes (1)
- .github/workflows/main.yml
🚧 Files skipped from review as they are similar to previous changes (20)
- steve-core/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
- steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java
- steve-api/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java
- steve/README.md
- steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
- steve-ui-jsp/pom.xml
- steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java
- steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java
- steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java
- steve-ocpp-1-x/pom.xml
- steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java
- steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java
- steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java
- AGENTS.md
- steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-jooq/pom.xml
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javapom.xmlsteve-jooq/pom.xmlsteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javaDockerfilesteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/src/main/resources/logback-spring.xmlpom.xmlsteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve/pom.xmlsteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/main/resources/application.yml
📚 Learning: 2025-09-15T15:31:15.967Z
Learnt from: juherr
PR: juherr/evolve#91
File: steve-core/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java:125-129
Timestamp: 2025-09-15T15:31:15.967Z
Learning: In the steve-core OCPP project, JAXB bindings are customized in ocpp_binding.xml to map xs:dateTime XML types directly to java.time.OffsetDateTime using the JavaDateTimeConverter adapter, eliminating the need for XMLGregorianCalendar conversions that would normally be required in standard JAXB bindings.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
🧬 Code graph analysis (4)
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
Configuration(39-121)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java (1)
RequiredArgsConstructor(56-213)steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (1)
RequiredArgsConstructor(313-317)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
Slf4j(39-74)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(55-226)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 2-2: syntax error: found character '@' that cannot start any token
(syntax)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: license-check
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.6.14)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (java-kotlin)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.6.14)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-24.04, 24, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 24, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.3)
🔇 Additional comments (62)
steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java (2)
80-80: jsonPath is declared and initialized in StressTest — no guard required.
StressTest declares "protected String jsonPath" and sets it via jsonPath = getWsPath(serverProperties, steveProperties) before constructing OcppJsonChargePoint (see StressTest.java:60,80); OcppJsonChargePoint has matching constructors.Likely an incorrect or invalid review comment.
64-64: Switch to no-arg instantiation is safe — no remaining call sites or docs reference a String path.
Repo search found no Java usages passing arguments to new StressTestJsonOCPP16 and no markdown/docs mentioning the old ctor.steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (2)
43-47: Constructor-injected SteveProperties: LGTMClean switch to constructor injection. No concerns.
72-72: Confirm security filter coverage of SOAP endpointsCurrently only root mapping is filtered. If SOAP endpoints must pass through Spring Security (even for basic auth/IP filters), add the SOAP mapping as well.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (3)
49-53: Defensive null-check for endpointsPrevent NPE if endpoints is null.
Apply:
public MediatorInInterceptor(List<EndpointImpl> endpoints) { super(Phase.POST_STREAM); super.addBefore(StaxInInterceptor.class.getName()); - actualServers = initServerLookupMap(endpoints); + java.util.Objects.requireNonNull(endpoints, "endpoints must not be null"); + actualServers = initServerLookupMap(endpoints); }
88-94: Abort only when forwarded (avoid dropping unmatched requests)If no target server is found, consider not aborting the chain.
Suggested change:
- if (targetServer != null) { - targetServer.getDestination().getMessageObserver().onMessage(message); - } - - // Now the response has been put in the message, abort the chain - message.getInterceptorChain().abort(); + if (targetServer != null) { + targetServer.getDestination().getMessageObserver().onMessage(message); + // Now the response has been put in the message, abort the chain + message.getInterceptorChain().abort(); + }
62-69: Avoid fragile mark/reset; handle missing InputStreamavailable() can be 0 causing reset() to fail; also guard when InputStream is absent.
Apply:
- var is = message.getContent(InputStream.class); - var bis = new BufferedInputStream(is); - bis.mark(bis.available()); + var is = message.getContent(InputStream.class); + if (is == null) { + log.warn("No InputStream on message; cannot route"); + return; + } + var bis = new BufferedInputStream(is); + bis.mark(64 * 1024); message.setContent(InputStream.class, bis); - var encoding = (String) message.get(Message.ENCODING); - var reader = StaxUtils.createXMLStreamReader(bis, encoding); - var xmlReader = new DepthXMLStreamReader(reader); + var encoding = (String) message.get(Message.ENCODING); + var reader = StaxUtils.createXMLStreamReader(bis, encoding); + var xmlReader = new DepthXMLStreamReader(reader);steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (1)
22-24: LGTM – clearer, safer validationConstructor injection + precompiled Pattern and strict whitespace checks look good.
Also applies to: 39-48, 52-52, 56-61, 63-74
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (1)
76-92: Null-safe SSL and optional trust/trustless configs; avoid NPE when SSL unsetserverProperties.getSsl() can be null; current code will NPE. Also, only call keystore/truststore methods when configured. Support trust-store only setups.
Apply:
- private static @Nullable TLSClientParameters create(Ssl ssl) throws GeneralSecurityException, IOException { - if (!ssl.isEnabled()) { - return null; - } + private static @Nullable TLSClientParameters create(@Nullable Ssl ssl) + throws GeneralSecurityException, IOException { + if (ssl == null || !ssl.isEnabled()) { + return null; + } var tlsClientParams = new TLSClientParameters(); - var socketFactory = SslContextBuilder.builder() - .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) - .usingTLS() - .usingDefaultAlgorithm() - .usingKeyManagerPasswordFromKeyStore() - .buildMergedWithSystem() - .getSocketFactory(); + var builder = SslContextBuilder.builder(); + if (ssl.getKeyStore() != null) { + builder = builder.keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()); + } + if (ssl.getTrustStore() != null) { + builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword()); + } + builder = builder.usingTLS().usingDefaultAlgorithm(); + if (ssl.getKeyStore() != null) { + builder = builder.usingKeyManagerPasswordFromKeyStore(); + } + var socketFactory = builder.buildMergedWithSystem().getSocketFactory(); tlsClientParams.setSSLSocketFactory(socketFactory); return tlsClientParams; }steve-ocpp-transport-soap/pom.xml (3)
18-19: LGTM: Clean Spring Boot integration.The migration from CXF-specific dependencies to Spring Boot starter follows Spring Boot best practices. The spring-boot-starter-web provides the necessary web layer support for SOAP endpoints.
22-24: LGTM: Jakarta EE migration aligned with Spring Boot 3.Spring Boot 3.5 includes dependency updates including Jakarta EE components, so the migration from javax.servlet to jakarta.servlet-api is consistent with the Spring Boot 3.x ecosystem.
25-29: LGTM: CXF Spring Boot starter integration.The comment references the official CXF Spring Boot documentation, and the cxf-spring-boot-starter-jaxws is the correct starter for JAX-WS SOAP services in Spring Boot applications.
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (8)
30-31: LGTM: Direct BCrypt encoder configuration.Removing the dependency on SteveConfiguration and directly returning BCryptPasswordEncoder simplifies the configuration and aligns with Spring Boot's convention-over-configuration approach.
39-39: LGTM: Jakarta imports for Spring Boot 3 compatibility.The import change from javax.servlet to jakarta.servlet aligns with the Jakarta EE migration in Spring Boot 3.x.
59-61: LGTM: Simplified password encoder configuration.The method now directly returns a BCryptPasswordEncoder instance instead of relying on external configuration, which is cleaner and more explicit.
64-66: LGTM: SteveProperties integration for security configuration.The method signature change from SteveConfiguration to SteveProperties is consistent with the broader migration pattern across the codebase.
73-96: LGTM: Comprehensive security configuration with SteveProperties.The security configuration properly uses SteveProperties for path resolution and includes comprehensive dispatcher type matchers and request matchers. The addition of dispatcher type matchers for FORWARD, INCLUDE, and ERROR is a best practice for Spring Security configuration.
139-141: LGTM: Explicit permitAll for noAccess endpoints.Adding explicit permitAll() for noAccess endpoints ensures that access denied redirects work correctly, preventing potential redirect loops.
147-158: LGTM: Properties-driven CSRF and authentication configuration.The CSRF configuration now uses SteveProperties for SOAP mapping, and the form login configuration includes proper URLs derived from the properties. The request cache disable is a reasonable optimization for this use case.
164-167: LGTM: API key filter chain with SteveProperties integration.The method signature properly injects SteveProperties and ApiAuthenticationManager, with the security matcher correctly deriving the API path from properties.
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (7)
22-22: LGTM: SteveProperties migration in test.The import change from SteveConfiguration to SteveProperties aligns with the broader configuration migration.
30-38: LGTM: Spring Boot test annotations.The test annotations follow Spring Boot testing conventions with proper profile activation and defined port configuration for integration testing.
55-57: LGTM: Spring Boot test class declaration.The class is properly configured as a Spring Boot test with the correct naming convention.
61-68: LGTM: Spring dependency injection.The autowired fields for ServerProperties, SteveProperties, and DSLContext follow Spring testing best practices.
72-81: LGTM: Proper test lifecycle management.The @beforeeach and @AfterEach methods properly manage database state for each test, ensuring test isolation.
83-85: LGTM: Test method with proper WebSocket path resolution.The test method uses the helper function getWsPath with the injected properties, which is consistent with the SteveProperties migration.
121-121: LGTM: Instance method with database preparer integration.The sendStartTx method is correctly converted to an instance method to access the databasePreparer field, and the assertion logic properly validates the expected authorization status.
Also applies to: 130-130, 132-133
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (6)
30-31: LGTM: Jakarta EE imports for Spring Boot 3.The import changes from javax.xml.ws to jakarta.xml.ws align with the Jakarta EE migration in Spring Boot 3.x.
49-52: LGTM: MessageIdInterceptor as Spring bean.Exposing MessageIdInterceptor as a Spring bean enables proper dependency injection and lifecycle management.
54-62: LGTM: Endpoint beans with dependency injection.The endpoint methods properly inject dependencies and delegate to the helper method for consistent endpoint creation. The path constants are appropriately defined for each OCPP version.
Also applies to: 64-72, 74-82
87-101: LGTM: Router endpoint configuration.The router endpoint properly integrates SteveProperties for path configuration and creates a mediator with the list of EndpointImpl instances. The TODO comment is helpful for future refactoring.
103-115: LGTM: Clean endpoint creation helper.The createDefaultEndpoint method provides consistent configuration for all OCPP endpoints with proper interceptor and feature injection.
117-128: LGTM: Generic endpoint creation method.The createEndpoint method follows the builder pattern for endpoint configuration and ensures proper publication of the endpoint.
pom.xml (6)
5-10: LGTM: Spring Boot parent integration.Spring Boot 3.5.6 includes updates to Spring Security 6.5.0 and other framework components, making this a recent and stable version choice. The Spring Boot parent POM provides comprehensive dependency management and plugin configurations.
44-44: LGTM: Centralized CXF version management.Moving from plugin.cxf.version to cxf.version provides cleaner property naming and aligns with the consolidated CXF BOM approach.
47-47: LGTM: JAXB plugins version property.The jaxb-plugins.version property follows Maven property naming conventions and enables consistent version management across modules.
98-103: LGTM: Simplified CXF dependency management.Replacing multiple CXF BOMs with a single cxf-bom import reduces complexity and leverages CXF's own dependency management. This approach aligns with Spring Boot's philosophy of simplified dependency management.
178-179: LGTM: Explicit compiler plugin groupId.Adding the explicit groupId for maven-compiler-plugin follows Maven best practices and improves build reproducibility.
220-221: LGTM: Dynamic license header configuration.Using Maven properties in the license header makes the configuration more maintainable and consistent across the project.
Dockerfile (2)
82-82: LGTM: WAR artifact deployment.The change from steve.jar to steve.war aligns with the Spring Boot WAR packaging changes in the project.
90-90: LGTM: WAR execution command.The CMD update to run steve.war is consistent with the packaging change and maintains the same JVM options.
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java (2)
21-21: LGTM: SteveProperties import migration.The import change aligns with the broader migration from SteveConfiguration to SteveProperties across the codebase.
42-53: LGTM: Constructor updated for SteveProperties integration.The constructor properly uses SteveProperties for path resolution, maintaining the same functionality while aligning with the new configuration approach.
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java (4)
22-22: LGTM: SteveProperties import migration.The import change is consistent with the broader configuration migration pattern across the codebase.
45-49: LGTM: Constructor with SteveProperties integration.The constructor properly injects SteveProperties and extracts the auto-registration configuration, replacing the @value annotation approach with a more structured configuration access pattern.
52-63: LGTM: Improved error handling with proper lock management.The use of local variable inference (
var) is appropriate here, and the finally block ensures proper lock release even in exceptional cases. This is a reliability improvement over the previous implementation.
67-76: LGTM: Enhanced error handling for registration status conversion.The try-catch block around RegistrationStatus.fromValue() with proper logging is a good defensive programming practice, handling cases where database values might be corrupted or invalid.
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
125-126: LGTM: localvarusage improves readability in testsNo issues with these refactors.
Also applies to: 145-151, 169-175
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (1)
61-64: LGTM: properties-driven strategy selectionSwitching to SteveProperties is consistent with the migration and removes string parsing.
steve/src/main/resources/logback-spring.xml (1)
35-42: TRACE/DEBUG for Spring Security/Web are globally enabled — scope them to dev/testThese global loggers override the dev/test INFO settings and will flood prod logs and risk leaking sensitive details.
Apply:
- <logger name="org.springframework.security" level="TRACE"/> - <logger name="org.springframework.web" level="DEBUG"/>And move them into the dev,test block (adjusting the existing entries):
<springProfile name="dev,test"> <logger name="org.apache.cxf" level="INFO"/> <logger name="org.eclipse.jetty" level="INFO"/> <logger name="com.zaxxer.hikari" level="INFO"/> <logger name="org.springframework" level="INFO"/> - <logger name="org.springframework.security" level="INFO"/> - <logger name="org.springframework.web.servlet" level="INFO"/> + <logger name="org.springframework.security" level="TRACE"/> + <logger name="org.springframework.web" level="DEBUG"/> </springProfile>Optionally add a conservative prod override:
<springProfile name="prod"> <logger name="org.springframework.security" level="INFO"/> <logger name="org.springframework.web" level="INFO"/> </springProfile>Also applies to: 44-45
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (1)
136-146: URL parsing is too permissive — normalize, segment-bound, and URL-decode the IDCurrent logic may return trailing segments (e.g., "id/extra"), accept partial matches, or fail when
pathInfixis empty. This can break valid handshakes or admit malformed IDs.- public static String getLastBitFromUrl(String pathInfix, String input) { - if (Strings.isNullOrEmpty(input)) { - return ""; - } - - var index = input.indexOf(pathInfix); - if (index == -1) { - return ""; - } - return input.substring(index + pathInfix.length()); - } + public static String getLastBitFromUrl(String pathInfix, String input) { + if (Strings.isNullOrEmpty(pathInfix) || Strings.isNullOrEmpty(input)) { + return ""; + } + // Ensure we match a segment-delimited infix: "/infix/" + String normInfix = "/" + pathInfix.replaceAll("^/+|/+$", "") + "/"; + int index = input.indexOf(normInfix); + if (index < 0) { + return ""; + } + String tail = input.substring(index + normInfix.length()); + int slash = tail.indexOf('/'); + String id = (slash >= 0) ? tail.substring(0, slash) : tail; + return java.net.URLDecoder.decode(id, java.nio.charset.StandardCharsets.UTF_8); + }steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (1)
55-57: soapPath is declared and visible; no action required
Declared asprotected String soapPathin StressTest and assigned in StressTest.attack() before attackInternal() runs.steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java (1)
50-50: LGTM: switched to sharedsoapPathfrom baseStressTest.The migration to use
soapPathviagetForOcpp16(soapPath)looks correct and removes prior constructor coupling.Please confirm
soapPathalways ends with a slash (Helpers currently appends one), as CXF clients can be sensitive to path normalization.Also applies to: 65-71, 73-81, 90-92
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (2)
49-59: Harden SOAP URL building (nulls, IPv6, InetAddress.toString()).
getSsl()can be null;getAddress()isInetAddressandtoString()yieldsname/addr; IPv6 needs brackets; contextPath may be null.- public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) { - var prefix = "http://"; - if (serverProperties.getSsl().isEnabled()) { - prefix = "https://"; - } - - return prefix + serverProperties.getAddress() + ":" + serverProperties.getPort() - + serverProperties.getServlet().getContextPath() - + steveProperties.getPaths().getSoapMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/"; - } + public static String getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) { + var ssl = serverProperties.getSsl(); + var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "https://" : "http://"; + + var addr = serverProperties.getAddress(); + var host = (addr != null) ? addr.getHostAddress() : "localhost"; + if (host.indexOf(':') >= 0) { // IPv6 literal + host = "[" + host + "]"; + } + var port = serverProperties.getPort() != null ? serverProperties.getPort() : 8080; + var servlet = serverProperties.getServlet(); + var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : ""; + + return scheme + host + ":" + port + + ctx + + steveProperties.getPaths().getSoapMapping() + + steveProperties.getPaths().getRouterEndpointPath() + "/"; + }
61-71: Apply the same hardening for WS URL (wss vs ws, nulls, IPv6).- public static String getWsPath(ServerProperties serverProperties, SteveProperties steveProperties) { - var prefix = "ws://"; - if (serverProperties.getSsl().isEnabled()) { - prefix = "wss://"; - } - - return prefix + serverProperties.getAddress() + ":" + serverProperties.getPort() - + serverProperties.getServlet().getContextPath() - + steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/"; - } + public static String getWsPath(ServerProperties serverProperties, SteveProperties steveProperties) { + var ssl = serverProperties.getSsl(); + var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "wss://" : "ws://"; + + var addr = serverProperties.getAddress(); + var host = (addr != null) ? addr.getHostAddress() : "localhost"; + if (host.indexOf(':') >= 0) { + host = "[" + host + "]"; + } + var port = serverProperties.getPort() != null ? serverProperties.getPort() : 8080; + var servlet = serverProperties.getServlet(); + var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : ""; + + return scheme + host + ":" + port + + ctx + + steveProperties.getPaths().getWebsocketMapping() + + steveProperties.getPaths().getRouterEndpointPath() + "/"; + }steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (1)
68-73: LGTM: path derivation via injected properties.Resolving the SOAP endpoint with
getSoapPath(serverProperties, steveProperties)in@BeforeEachaligns with the new config model.steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
72-80: LGTM: instance-based preparer using injected DSLContext.Moving from static to instance methods improves test isolation and DI alignment.
162-175: No change required — schema constant matches CI/test DB.
SCHEMA_TO_TRUNCATE ("stevedb_test_2aa6a783d47d") matches DB_SCHEMA in .github/workflows/main.yml (CI creates that DB), so the hard-coded name is consistent.steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
89-96: Close the Spring context and reverse teardown order to avoid leaks.Use
close()and clean DB before shutting the context;stop()may not release resources and closing first can invalidate the DataSource used bydatabasePreparer.- try { - if (app != null) { - app.stop(); - } - } finally { - if (databasePreparer != null) { - databasePreparer.cleanUp(); - } - } + try { + if (databasePreparer != null) { + databasePreparer.cleanUp(); + } + } finally { + if (app != null) { + app.close(); + } + }steve/src/main/resources/application.yml (1)
2-2: Resource filtering is already enabled — no change requiredsteve/pom.xml configures true and includes application.yml; Maven's resources plugin supports @...@ delimiters by default, so the @token@ placeholders in steve/src/main/resources/application.yml are filtered at build time. SteveProperties is annotated with @ConfigurationProperties/@configuration and is bound at runtime/tests (tests set spring.profiles.active and CI passes -D... properties). Locations: steve/pom.xml, steve/src/main/resources/application.yml, steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
82-99: Spring Boot test wiring LGTM.Autowiring ServerProperties/SteveProperties + per‑test DB setup is clean.
steve/pom.xml (1)
248-271: Replace unpack-with-dependency-plugin with WAR overlay.Unpacking into ${project.build.directory}/${project.build.finalName} is fragile; prefer maven-war-plugin overlays to merge steve-ui-jsp.
Apply this diff:
- <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>unpack-steve-ui-jsp-webapp</id> - <goals> - <goal>unpack</goal> - </goals> - <phase>process-resources</phase> - <configuration> - <artifactItems> - <artifactItem> - <groupId>de.rwth.idsg</groupId> - <artifactId>steve-ui-jsp</artifactId> - <version>${project.version}</version> - <overWrite>true</overWrite> - <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> - <includes>WEB-INF/**,static/**</includes> - </artifactItem> - </artifactItems> - </configuration> - </execution> - </executions> - </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <overlays> + <overlay> + <groupId>de.rwth.idsg</groupId> + <artifactId>steve-ui-jsp</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin>Note: Keep spring-boot-maven-plugin; it will repackage the WAR produced by maven-war-plugin.
applied some of the relevant suggestions by AI review
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (1)
87-96: Abort chain only on successful forwarding
Return early when no server mapping exists; invokeabort()only afteronMessage.- if (targetServer == null) { - log.warn("No server mapped for namespace '{}'", schemaNamespace); - } else { - targetServer.getDestination().getMessageObserver().onMessage(message); - } - - // Now the response has been put in the message, abort the chain - message.getInterceptorChain().abort(); + if (targetServer == null) { + log.warn("No server mapped for namespace '{}'", schemaNamespace); + return; // allow the chain to continue on routing failure + } + targetServer.getDestination().getMessageObserver().onMessage(message); + // Now the response has been put in the message, abort the chain + message.getInterceptorChain().abort();steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
81-81: Potential duplicate PasswordEncoder field.There are two PasswordEncoder fields:
passwordEncoder(line 77) andencoder(line 81). This appears to be a duplicate field that could cause confusion.#!/bin/bash # Description: Verify usage of both PasswordEncoder fields to determine if one is redundant # Search for usage of both passwordEncoder fields rg -nP --type=java -C3 '\b(passwordEncoder|encoder)\.' steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.javaApply this diff to consolidate to a single PasswordEncoder field:
private final SteveProperties steveProperties; -private final PasswordEncoder passwordEncoder; private final ObjectMapper mapper; private final WebUserRepository webUserRepository; private final SecurityContextHolderStrategy securityContextHolderStrategy = getContextHolderStrategy(); -private final PasswordEncoder encoder; +private final PasswordEncoder passwordEncoder;Then update usages of
encoderto usepasswordEncoderconsistently throughout the class.
🧹 Nitpick comments (12)
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (1)
76-76: Use raw path to avoid premature decoding.Prefer
getRawPath(); the parser can then decode reliably.- var chargeBoxId = getLastBitFromUrl(pathInfix, request.getURI().getPath()); + var chargeBoxId = getLastBitFromUrl(pathInfix, request.getURI().getRawPath());steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (1)
104-111: Harden server map construction (skip unpublished endpoints, detect duplicates, return unmodifiable map).
Prevents NPEs when endpoints aren’t published and avoids accidental mutation.Apply:
- private static Map<String, Server> initServerLookupMap(List<EndpointImpl> endpoints) { - var actualServers = new HashMap<String, Server>(); - for (var endpoint : endpoints) { - var server = endpoint.getServer(); - var serverNamespace = - server.getEndpoint().getEndpointInfo().getName().getNamespaceURI(); - actualServers.put(serverNamespace, server); - } - return actualServers; - } + private static Map<String, Server> initServerLookupMap(List<EndpointImpl> endpoints) { + var map = new HashMap<String, Server>(endpoints.size()); + for (var endpoint : endpoints) { + var server = endpoint.getServer(); + if (server == null) { + log.warn("Skipping unpublished endpoint {}", endpoint); + continue; + } + var ns = server.getEndpoint().getEndpointInfo().getName().getNamespaceURI(); + var prev = map.put(ns, server); + if (prev != null && prev != server) { + log.warn("Duplicate namespace mapping for '{}'; overriding previous server {}", ns, prev); + } + } + return java.util.Collections.unmodifiableMap(map); + }steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (2)
99-99: Minor: prefer Path.of over Paths.getModern JDKs favor Path.of(...). This also lets you drop the unused Paths import.
- fileNameList.add(Paths.get(fileName)); + fileNameList.add(Path.of(fileName));And remove:
-import java.nio.file.Paths;
91-103: Direct ROOT appenders sufficient; no wrapper appenders detected
Search of logging configs found no AsyncAppender or SiftingAppender. Current iteration over ROOT’s appenders retrieves all configured log files. If wrapper appenders are added in the future, introduce a recursive collectFileAppenderPaths(Appender, List) helper to traverse AppenderAttachable and extract nested FileAppender paths.steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (2)
183-186: Remove stray semicolon in lambda for cleanliness.Tiny nit; the standalone
;is unnecessary.- .satisfies(c -> { - ; + .satisfies(c -> { var ue = (UpgradeException) c;
59-61: Consider RANDOM_PORT to avoid port collision in parallel runs.If your build enables JUnit parallelism,
DEFINED_PORTmay clash across test classes.RANDOM_PORTwith@LocalServerPortis safer.steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (3)
37-51: Avoid injecting one ConstraintValidator into another; extract shared logic or use container element constraints
ChargeBoxIdValidatorhas a lifecycle (initialize) and may be stateful per-annotation. Injecting and reusing it here risks incorrect state and threading concerns. Prefer:
- Extract the core check into a stateless utility/service both validators call, or
- Remove this validator and use Bean Validation 2.0 container element constraints: annotate as
List<@ChargeBoxId String>and combine with@NotEmptywhen needed.This produces per‑element errors and lets Hibernate Validator manage lifecycle properly.
49-54: Consider reporting element index in violations for better UXCurrently, failures attach to the list as a whole. If you keep this validator, customize the
ConstraintValidatorContextto include the offending index, or (preferably) rely on container element constraints which handle index/path automatically.
46-48: Companion constraints enforce non-emptiness; optional simplify null/empty checkThe
@ChargeBoxIdvalidator is paired with@NotBlankonChargePointForm.chargeBoxIdand@NotEmptyonChargePointBatchInsertForm.idList, so the early return for null/empty is safe.- if (CollectionUtils.isEmpty(value)) { - return true; // null or empty is valid, because it is another constraint's responsibility - } + if (value == null || value.isEmpty()) { + return true; // null or empty is valid; use @NotEmpty/@Size to enforce non-empty if needed + }steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
94-95: Review password encoding consistency across the service.Line 95 uses
passwordEncoder.encode()while line 99 usespasswordEncoder.encode(), but other methods in the class useencoder.encode(). Once the duplicate field issue is resolved, ensure all password encoding uses the same field consistently.The earlier script will help verify the usage patterns. After consolidating to a single PasswordEncoder field, ensure all encoding operations use the same reference.
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (1)
39-39: Document JooqAutoConfiguration exclusion
Add a comment at SteveApplication.java:39 explaining why the default jOOQ auto-configuration is disabled in favor of your custom JooqConfiguration bean.
Optional: consider using Spring Boot 3’s DefaultConfigurationCustomizer for customizations instead of excluding the auto-configuration.steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (1)
58-61: Set client timeouts on the HTTP conduit.
Avoid unbounded waits on external SOAP endpoints by setting connection/receive timeouts.Apply this diff:
var client = ClientProxy.getClient(clientObject); var http = (HTTPConduit) client.getConduit(); http.setTlsClientParameters(tlsClientParams); +// Avoid unbounded network waits +http.getClient().setConnectionTimeout(10_000); // 10s +http.getClient().setReceiveTimeout(30_000); // 30s
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(3 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks)steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(9 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(5 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.java
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java
🧬 Code graph analysis (3)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (2)
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(58-235)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(35-98)
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (2)
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (1)
Component(33-56)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (1)
Component(41-94)
🔇 Additional comments (24)
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (3)
126-132: LGTM: protocol selection is straightforward.Given the small endpoint set, the nested loop is acceptable.
136-146: Fix getLastBitFromUrl: match on segment boundary, extract single segment, and URL‑decode.Current logic can return extra path segments (e.g., "CP_1/other"), fail when
pathInfixis empty/null, or match unintended occurrences. Harden it.- public static String getLastBitFromUrl(String pathInfix, String input) { - if (Strings.isNullOrEmpty(input)) { - return ""; - } - - var index = input.indexOf(pathInfix); - if (index == -1) { - return ""; - } - return input.substring(index + pathInfix.length()); - } + public static String getLastBitFromUrl(String pathInfix, String input) { + if (Strings.isNullOrEmpty(pathInfix) || Strings.isNullOrEmpty(input)) { + return ""; + } + // Normalize to ensure we match on "/<infix>/" + String normInfix = "/" + pathInfix.replaceAll("^/+|/+$", "") + "/"; + String normInput = input; // already a path (raw or decoded), no query + int index = normInput.indexOf(normInfix); + if (index < 0) { + return ""; + } + String tail = normInput.substring(index + normInfix.length()); + int slash = tail.indexOf('/'); + String id = (slash >= 0) ? tail.substring(0, slash) : tail; + return java.net.URLDecoder.decode(id, java.nio.charset.StandardCharsets.UTF_8); + }I can add focused unit tests covering:
- encoded IDs (e.g., CP%2F1),
- trailing slashes,
- multiple occurrences of the infix,
- missing/empty infix or path.
Want me to draft them?
53-53: ConfirmpathInfixwiring and value normalization
OcppWebSocketHandshakeHandleris now constructed inOcppWebSocketConfiguration.javawith the newpathInfixargument. Verify thatsteveProperties.getPaths().getWebsocketMapping()andgetRouterEndpointPath()produce a correctly normalized prefix (no duplicate, leading or trailing slashes).steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (3)
25-25: Import looks correct for the new constructor signature.
Using EndpointImpl explicitly is fine here.
49-53: Defensive null check for endpoints is missing.
Constructor will NPE in initServerLookupMap when endpoints is null.Apply:
public MediatorInInterceptor(List<EndpointImpl> endpoints) { super(Phase.POST_STREAM); super.addBefore(StaxInInterceptor.class.getName()); - actualServers = initServerLookupMap(endpoints); + actualServers = initServerLookupMap(java.util.Objects.requireNonNull(endpoints, "endpoints must not be null")); }
61-69: Null InputStream and fragile mark/reset can drop requests.
message.getContent(InputStream.class) may be null (NPE), and using bis.available() as read limit often yields 0, making reset() fail.Apply:
- var is = message.getContent(InputStream.class); - var bis = new BufferedInputStream(is); - bis.mark(bis.available()); + var is = message.getContent(InputStream.class); + if (is == null) { + log.warn("No InputStream on message; cannot route"); + return; + } + var bis = new BufferedInputStream(is); + // mark enough bytes to parse envelope + first body element + bis.mark(64 * 1024); message.setContent(InputStream.class, bis); - var encoding = (String) message.get(Message.ENCODING); - var reader = StaxUtils.createXMLStreamReader(bis, encoding); - var xmlReader = new DepthXMLStreamReader(reader); + var encoding = (String) message.get(Message.ENCODING); + var reader = StaxUtils.createXMLStreamReader(bis, encoding); + var xmlReader = new DepthXMLStreamReader(reader);Optional: move bis.reset() into a finally block and log a warning if reset fails without aborting the chain.
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (1)
28-28: Spring bean registration — LGTMAnnotating this utility with @component (and adding the import) is appropriate and makes it injectable where needed.
Also applies to: 42-42
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
72-76: Property assertion may be brittle; assert as boolean and verify test override exists.Use typed access and ensure a test override actually sets it to true.
- assertThat(environment.getProperty("steve.ocpp.auto-register-unknown-stations")) - .isEqualTo("true"); + assertThat(environment.getProperty("steve.ocpp.auto-register-unknown-stations", Boolean.class)) + .isTrue();Run to verify a test override exists:
#!/bin/bash set -euo pipefail echo "Search for a test-specific override setting the property to true" rg -n -C2 -g '!**/target/**' -P --type=yaml 'steve:\s*\n\s*ocpp:\s*\n\s*auto-register-unknown-stations:\s*true' || true fd -HI 'application-test.*' || true rg -n -C2 -P --type=properties '^steve\.ocpp\.auto-register-unknown-stations\s*=\s*true' || truesteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
80-85: LGTM: Proper per-test setup of DB and JSON path.The DI-based
jsonPathcomputation and__DatabasePreparer__lifecycle look correct.steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
72-73: LGTM: soapPath computed via DI-backed properties.Path derivation aligns with Helpers and the new config.
40-41: Same Helpers address normalization caveat as JSON tests.Ensure
getSoapPathuses a normalized host (see comment in StressTest).
68-78: No change required: tearDown signature is correct DatabasePreparer.cleanUp() does not declare any checked exceptions, so omittingthrows Exceptioncompiles as is.Likely an incorrect or invalid review comment.
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (1)
34-37: Validation uses Spring’s ConstraintValidatorFactory
No direct Hibernate-Validator factory usage in production code andspring-boot-starter-validationis on the classpath; test-only setup ofLocalValidatorFactoryBeandoesn’t affect runtime.steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (4)
1-40: LGTM! Standard Spring Boot application entry point with proper licensing.The class structure and Spring Boot annotations are correctly implemented. The GPL license header is appropriate for an open-source project.
42-51: LGTM! Proper initialization of logging and timezone configuration.The static initializer correctly configures:
- Apache CXF to use SLF4J logging
- JBoss logging provider for Hibernate validator
- Default JVM timezone to UTC for consistency
This ensures predictable behavior across different deployment environments.
53-59: LGTM! Useful startup diagnostics.The event listener provides valuable runtime information about timezone configuration, which helps with debugging date/time related issues in a distributed environment.
61-63: LGTM! Standard Spring Boot main method.The main method correctly delegates to SpringApplication.run with proper parameter handling.
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (3)
26-26: LGTM! Updated import for SteveProperties migration.The import correctly reflects the migration from SteveConfiguration to SteveProperties-based configuration.
76-77: LGTM! Added dependencies for properties-based configuration.The addition of SteveProperties and PasswordEncoder dependencies supports the migration from legacy configuration to Spring Boot's properties-based approach.
94-100: LGTM! Properly migrated to SteveProperties-based configuration.The code correctly:
- Retrieves web API header value from
steveProperties.getWebApi().getHeaderValue()- Retrieves auth credentials from
steveProperties.getAuth().getUsername()andsteveProperties.getAuth().getPassword()- Handles null/blank header values appropriately
- Uses proper password encoding for both web and API passwords
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (4)
29-35: Imports look correct for Spring Boot 3.5 and exception propagation.
No concerns with using ServerProperties and Ssl here.
47-51: Constructor wiring LGTM.
Null-safe handoff to create(serverProperties.getSsl()) with exceptions surfaced at startup is acceptable.
76-93: NPE risk when key-store is not configured; add trust-store support.
ssl.isEnabled() can be true while ssl.getKeyStore() is null (e.g., relying on system CAs or only a trust store). Unconditionally calling keyStoreFromFile(...) will NPE and blocks valid configurations. Also, trust-store values are ignored.Apply this diff to make keystore/truststore handling optional and null-safe:
- private static @Nullable TLSClientParameters create(@Nullable Ssl ssl) - throws GeneralSecurityException, IOException { - if (ssl == null || !ssl.isEnabled()) { - return null; - } - var tlsClientParams = new TLSClientParameters(); - - var socketFactory = SslContextBuilder.builder() - .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) - .usingTLS() - .usingDefaultAlgorithm() - .usingKeyManagerPasswordFromKeyStore() - .buildMergedWithSystem() - .getSocketFactory(); - tlsClientParams.setSSLSocketFactory(socketFactory); - - return tlsClientParams; - } + private static @Nullable TLSClientParameters create(@Nullable Ssl ssl) + throws GeneralSecurityException, IOException { + if (ssl == null || !ssl.isEnabled()) { + return null; + } + var tlsClientParams = new TLSClientParameters(); + + var builder = SslContextBuilder.builder(); + if (ssl.getKeyStore() != null) { + builder = builder + .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) + .usingKeyManagerPasswordFromKeyStore(); + } + if (ssl.getTrustStore() != null) { + builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword()); + } + var socketFactory = builder + .usingTLS() + .usingDefaultAlgorithm() + .buildMergedWithSystem() + .getSocketFactory(); + tlsClientParams.setSSLSocketFactory(socketFactory); + + return tlsClientParams; + }Optionally, if you expect distinct private-key passwords, we can extend this to honor ssl.getKeyPassword() when present.
76-93: No SSL bundle configuration detected: noserver.ssl.*properties found in application configuration, so manual TLSClientParameters instantiation remains appropriate. If you later introduceserver.ssl.bundle(Spring Boot 3.1+), consider usingSslBundlesto produce the SSLSocketFactory.
| try { | ||
| app.stop(); | ||
| if (app != null) { | ||
| app.close(); | ||
| } | ||
| } finally { | ||
| __DatabasePreparer__.cleanUp(); | ||
| if (databasePreparer != null) { | ||
| databasePreparer.cleanUp(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Clean DB before closing the Spring context to avoid “pool is closed” failures.
__DatabasePreparer__ uses DSLContext from the app. Closing the context first can invalidate the DataSource before cleanup runs.
- try {
- if (app != null) {
- app.close();
- }
- } finally {
- if (databasePreparer != null) {
- databasePreparer.cleanUp();
- }
- }
+ try {
+ if (databasePreparer != null) {
+ databasePreparer.cleanUp();
+ }
+ } finally {
+ if (app != null) {
+ app.close();
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| app.stop(); | |
| if (app != null) { | |
| app.close(); | |
| } | |
| } finally { | |
| __DatabasePreparer__.cleanUp(); | |
| if (databasePreparer != null) { | |
| databasePreparer.cleanUp(); | |
| } | |
| } | |
| try { | |
| if (databasePreparer != null) { | |
| databasePreparer.cleanUp(); | |
| } | |
| } finally { | |
| if (app != null) { | |
| app.close(); | |
| } | |
| } |
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/StressTest.java around lines 88 to 96,
the DatabasePreparer.cleanup() is being invoked in the finally block after the
application context is closed which can close the DataSource and cause "pool is
closed" errors; move the databasePreparer.cleanUp() call to run before
app.close() (i.e., if databasePreparer != null call cleanUp() first, then if app
!= null call app.close()), keep null checks and preserve exception-safety (wrap
each call to cleanUp() and close() so one failing does not skip the other).
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Dockerfile (2)
73-79: Container won’t bind to ${PORT}: wrong Spring Boot properties.-Dhttp.port and -Dserver.host are ignored by Spring Boot; use server.port and server.address. Also server.gzip.enabled is not a Boot property; use server.compression.enabled.
-ENV JAVA_TOOL_OPTIONS="-Dserver.host=0.0.0.0 -Dhttp.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" +ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1.2,TLSv1.3' -Dserver.compression.enabled=false"
61-67: Avoid defaulting to weak TLS versions.Re-enabling TLSv1/1.1 downgrades security. Keep TLSv1.2/1.3 only (as in the fix above).
🧹 Nitpick comments (7)
.github/workflows/main.yml (2)
54-55: Remove spring-boot.run.profiles; use spring.profiles.active (or drop entirely for build).spring-boot.run.profiles only affects spring-boot:run, not package. Prefer spring.profiles.active or omit.
Apply:
- -Dspring-boot.run.profiles=test,dev \ + -Dspring.profiles.active=test,dev \
57-63: Make the app start probe robust (avoid fixed sleep).Replace fixed sleep with a readiness loop to reduce flakiness.
- sleep 30 - elinks -dump -no-references http://localhost:8080/manager/signin + for i in $(seq 1 60); do + if curl -fsS http://localhost:8080/manager/signin >/dev/null; then break; fi + sleep 1 + done + elinks -dump -no-references http://localhost:8080/manager/signinDockerfile (2)
81-83: Remove stale libs copy for executable WAR.spring-boot-maven-plugin packages dependencies inside the WAR; target/libs is not used here.
-COPY --from=builder --chown=app:app /code/steve/target/libs/ /app/libs/
34-41: Unused build ARG.PORT ARG in builder stage is unused.
-ARG PORT=8180steve/pom.xml (3)
108-121: Redundant logging deps; rely on Spring Boot managed logging.logback-classic, slf4j-api, and jcl-over-slf4j are provided by Boot BOM/starters. Keeping them risks version drift.
- <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>jcl-over-slf4j</artifactId> - </dependency>
90-96: Prefer Boot-managed transitive deps; remove direct Jackson and Hikari.jackson-databind/annotations come via spring-boot-starter-web; HikariCP via spring-boot-starter-jdbc. Keep mysql-connector-j.
- <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-annotations</artifactId> - </dependency> ... - <dependency> - <groupId>com.zaxxer</groupId> - <artifactId>HikariCP</artifactId> - </dependency>Also applies to: 98-101, 153-161, 163-167
122-142: Avoid mixing Boot’s Jetty starter with direct Jetty modules unless needed.spring-boot-starter-jetty already aligns Jetty versions. Extra jetty-* deps may cause conflicts. Keep only specific extras (e.g., jetty-rewrite) if required.
Option A (lean): remove direct jetty-* deps and let starter manage.
Option B (if features required): keep only needed artifacts and ensure versions are managed by Boot (no explicit versions).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.github/workflows/main.yml(1 hunks)Dockerfile(1 hunks)steve/README.md(2 hunks)steve/pom.xml(5 hunks)steve/src/main/resources/application-dev.yml(1 hunks)steve/src/main/resources/application.yml(1 hunks)steve/src/main/resources/logback-spring.xml(1 hunks)steve/src/test/resources/application-test.yml(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- steve/src/main/resources/application-dev.yml
- steve/src/test/resources/application-test.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- steve/README.md
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
.github/workflows/main.ymlsteve/pom.xml
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve/src/main/resources/application.ymlsteve/src/main/resources/logback-spring.xmlsteve/pom.xml
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
🪛 GitHub Actions: build and run tests
steve/pom.xml
[error] 1-1: Maven build failed: No goals have been specified for this build. The command is missing a valid lifecycle phase or goal. Command: './mvnw -B -V -PuseRealDatabase -DskipTests=false -Dmaven.javadoc.skip=true -Ddb.jdbc.url="jdbc:mysql://127.0.0.1:3306/${DB_SCHEMA}?sslMode=PREFERRED&serverTimezone=UTC" -Ddb.schema="${DB_SCHEMA}" -Ddb.user="${DB_USER}" -Ddb.*** -Dspring-boot.run.profiles=test,dev clean package --file pom.xml'
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (5)
steve/src/main/resources/logback-spring.xml (1)
50-51: Don't ship TRACE/DEBUG security/web logging to prod.The global TRACE/DEBUG loggers here override the dev/test INFO overrides declared above, so every profile—including prod—runs with extremely verbose Spring Security/Web logging, risking log flooding and sensitive detail leakage. Please scope these verbose levels to the dev/test profile (or drop them) and keep prod at INFO+. See the outstanding bot comment for the same issue.
- <logger name="org.springframework.security" level="TRACE"/> - <logger name="org.springframework.web" level="DEBUG"/>Then bump the dev/test overrides to the desired verbosity, for example:
- <logger name="org.springframework.security" level="INFO"/> - <logger name="org.springframework.web.servlet" level="INFO"/> + <logger name="org.springframework.security" level="TRACE"/> + <logger name="org.springframework.web" level="DEBUG"/>Dockerfile (1)
73-76: Healthcheck likely probes wrong port.Given the wrong props above, app would listen on 8080 while healthcheck hits ${PORT} (8180). After fixing props, healthcheck aligns; otherwise it will always fail.
steve/pom.xml (3)
225-232: Disable Maven filtering for application.yml to avoid breaking Spring placeholders.Already flagged previously; reiterating. Filtering may replace ${...} in YAML at build-time.
Example:
- <resources> - <resource> - <filtering>true</filtering> - <directory>src/main/resources</directory> - <includes> - <include>application.yml</include> - <include>logback-spring.xml</include> - </includes> - </resource> - </resources> + <resources> + <resource> + <filtering>false</filtering> + <directory>src/main/resources</directory> + <includes> + <include>application.yml</include> + <include>logback-spring.xml</include> + </includes> + </resource> + </resources>
241-264: Use WAR overlays instead of unpacking UI into target dir.This unpack approach is fragile; prefer maven-war-plugin overlays. This was noted earlier.
- <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>unpack-steve-ui-jsp-webapp</id> - <goals> - <goal>unpack</goal> - </goals> - <phase>process-resources</phase> - <configuration> - <artifactItems> - <artifactItem> - <groupId>de.rwth.idsg</groupId> - <artifactId>steve-ui-jsp</artifactId> - <version>${project.version}</version> - <overWrite>true</overWrite> - <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> - <includes>WEB-INF/**,static/**</includes> - </artifactItem> - </artifactItems> - </configuration> - </execution> - </executions> - </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <overlays> + <overlay> + <groupId>de.rwth.idsg</groupId> + <artifactId>steve-ui-jsp</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin>Note: add dependency de.rwth.idsg:steve-ui-jsp with war if not already resolved as WAR.
58-83: Good: Jetty chosen via Boot starter; Tomcat excluded.This aligns with the WAR + Jetty direction.
Confirm no conflicting embedded container on classpath at runtime (e.g., direct Tomcat deps).
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
steve-core/pom.xml (1)
18-31: Remove spring-webmvc from steve-core and relocate to the app module
steve-core contains no @Controller/@RestController – drop the direct spring-webmvc dependency (and its spring-jcl exclusion) and let the app’s spring-boot-starter-web provide the web stack and logging bridge.
🧹 Nitpick comments (4)
steve-core/pom.xml (2)
45-48: Remove explicit jakarta.mail-api; spring-boot-starter-mail already provides itRedundant direct dependency risks version skew against Boot’s managed version.
- <dependency> - <groupId>jakarta.mail</groupId> - <artifactId>jakarta.mail-api</artifactId> - </dependency>
32-43: Avoid Boot starters in library module (prefer narrow deps)If steve-core is intended as a library, depend on Spring modules (e.g., spring-security-core, jakarta.validation-api) instead of Boot starters to avoid dragging auto-config/logging into downstreams.
Example:
- <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-validation</artifactId> - </dependency> + <dependency> + <groupId>jakarta.validation</groupId> + <artifactId>jakarta.validation-api</artifactId> + </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> - </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-core</artifactId> + </dependency>.github/workflows/main.yml (1)
57-64: Prefer readiness probe over fixed sleepPoll an HTTP endpoint until ready to reduce flakes; avoid killall impacting other Java processes.
- java -Djava.net.preferIPv4Stack=true -jar steve/target/steve.war & - sleep 30 - elinks -dump -no-references http://localhost:8080/manager/signin - killall java + java -Djava.net.preferIPv4Stack=true -jar steve/target/steve.war & + for i in {1..30}; do + if curl -fsS http://localhost:8080/actuator/health >/dev/null 2>&1; then break; fi + sleep 2 + done + elinks -dump -no-references http://localhost:8080/manager/signin + pkill -f 'steve/target/steve\.war' || trueDockerfile (1)
77-77: Rename the gzip toggle to Spring Boot’s property nameSpring Boot ignores
server.gzip.enabled, so the flag is now a no-op. Either drop it or switch toserver.compression.enabled=falseto keep the intent clear.-ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" +ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.compression.enabled=false"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
-
.github/workflows/main.yml(1 hunks) -
Dockerfile(1 hunks) -
steve-api/pom.xml(1 hunks) -
steve-core/pom.xml(1 hunks) -
steve/pom.xml(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- steve-api/pom.xml
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
Dockerfilesteve/pom.xml
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
.github/workflows/main.ymlsteve/pom.xml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.6.14)
- GitHub Check: build (ubuntu-24.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.3)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.3)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-24.04, 24, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.5.21)
- GitHub Check: build (ubuntu-22.04, 24, mysql:8.0)
🔇 Additional comments (7)
steve/pom.xml (4)
197-226: Use WAR overlay instead of unpacking steve-ui-jsp into target dirUnpacking into ${project.build.directory}/${project.build.finalName} is fragile; the WAR plugin may overwrite that directory.
Replace dependency-plugin execution with a war overlay:
<plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>unpack-steve-ui-jsp-webapp</id> - <goals> - <goal>unpack</goal> - </goals> - <phase>process-resources</phase> - <configuration> - <artifactItems> - <artifactItem> - <groupId>de.rwth.idsg</groupId> - <artifactId>steve-ui-jsp</artifactId> - <version>${project.version}</version> - <overWrite>true</overWrite> - <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> - <includes>WEB-INF/**,static/**</includes> - </artifactItem> - </artifactItems> - </configuration> - </execution> - </executions> - </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <overlays> + <overlay> + <groupId>de.rwth.idsg</groupId> + <artifactId>steve-ui-jsp</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin> </plugins>
58-88: LGTM on Boot web/Jetty setupExcluding Tomcat and adding starter-jetty is consistent with WAR packaging and the run command.
124-134: Test scopes look correctspring-boot-starter-test and spring-security-test are test-scoped as expected.
186-195: Disable Maven filtering for application.yml to avoid placeholder/secret bake-inCurrent filtering=true will resolve ${...} in application.yml at build time, breaking Spring’s runtime property resolution and potentially baking secrets.
Safer split: filter only logback, keep application.yml unfiltered.
<resources> - <resource> - <filtering>true</filtering> - <directory>src/main/resources</directory> - <includes> - <include>application.yml</include> - <include>logback-spring.xml</include> - </includes> - </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + <includes> + <include>application.yml</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + <includes> + <include>logback-spring.xml</include> + </includes> + </resource> </resources>.github/workflows/main.yml (1)
46-55: Maven command formatting fixedMultiline args and goal placement look correct now.
Dockerfile (2)
81-81: Copying the WAR matches the packaging switchLooks good to mirror the new WAR packaging in the image build.
89-89: Confirm the WAR remains fully executable
The verification script failed due to an undefined JAVA_HOME; please set JAVA_HOME and run:jar tf steve/target/steve.war | grep -q 'org/springframework/boot/loader/WarLauncher.class'to ensure Spring Boot’s loader classes are present.
4b9e2db to
bad9eaa
Compare
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (1)
156-170: KeepbuildRequestaccepting a String (or calltoString()before concatenation).
soapPathis aURI, but the SOAP envelope needs its textual form. Either callpath.toString()when building the payload or simply keep the method parameter type asString. As written, concatenating aURIis fine for the<To>value, but the caller now has to pass aURI, which forced the compile error above. Revert the signature toString(or convert toStringlocally) for compatibility.Here’s one option:
- private static String buildRequest( - URI path, String chargeBoxId, int transactionId, String idTag, OffsetDateTime stop, int meterStop) { + private static String buildRequest( + String path, String chargeBoxId, int transactionId, String idTag, OffsetDateTime stop, int meterStop) {If you need to keep
URIelsewhere, just passsoapPath.toString()instead.steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (1)
130-149: Sensitive data risk: full SOAP payload loggingThe custom Slf4jEventSender logs entire payloads, which can include credentials/PII. At minimum, cap size and avoid pretty logging; ideally disable in prod or mask sensitive elements.
Apply a safe cap and avoid verbose formatting:
public LoggingFeature loggingFeature() { var feature = new LoggingFeature(); + feature.setLimit(8192); // cap payload size + feature.setPrettyLogging(false); // reduce verbosity feature.setSender(new Slf4jEventSender() { @Override protected String getLogMessage(LogEvent event) { var b = new StringBuilder(); b.append('\n') // Start from the next line to have the output well-aligned .append(" ExchangeId: ") .append(event.getExchangeId()) .append('\n') - .append(" Payload: ") - .append(event.getPayload()); + .append(" Payload (truncated): ") + .append(event.getPayload()); return b.toString(); } }); return feature; }Operationally, prefer enabling this bean only in dev/test and control CXF logging via application properties (e.g., set logging.level for CXF categories to DEBUG when needed).
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
114-117: Ensure Spring transaction participation (wrap DataSource with TransactionAwareDataSourceProxy).Without this, jOOQ may bypass Spring-managed transactions.
+import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; @@ - .set(new DataSourceConnectionProvider( - // new org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy() - dataSource)) + .set(new DataSourceConnectionProvider( + new TransactionAwareDataSourceProxy(dataSource)))steve-ui-jsp/pom.xml (1)
120-121: Define missing jetty.version property
Add<jetty.version>12.0.11</jetty.version>under<properties>in the parent POM (preferred) or in steve-ui-jsp/pom.xml to prevent build failures.pom.xml (1)
37-48: Define lombok.version property and specify lombok dependency version
- Add
<lombok.version>1.18.34</lombok.version>under<properties>in pom.xml- Add
<version>${lombok.version}</version>to the org.projectlombok:lombok<dependency>(scope provided) and ensure the same property is used in<annotationProcessorPaths>steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
79-86: Fix stray checkstyle text and line length in JavadocThe comment includes a pasted checkstyle error and exceeds line length.
- /** - * https://docs.spring.io/spring-framework/reference/web/websocket/server.html#websocket-server-runtime-configurationCheck failure[checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Check failure: [checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Line is longer than 120 characters (found 121).build and run tests / checkstyleView detailsCode has alerts. Press enter to view. - * - * Otherwise, defaults come from {@link WebSocketConstants} - */ + /** + * See: + * https://docs.spring.io/spring-framework/reference/web/websocket/server.html + * #websocket-server-runtime-configuration + * + * Otherwise, defaults come from {@link WebSocketConstants}. + */steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
588-595: Bug: Asserting empty list, then indexing element 0 → IndexOutOfBoundsException.
sampledValuesis asserted empty, butsampledValues.get(0)is accessed right after.- var sampledValues = meterValue.getSampledValue(); - assertThat(sampledValues).isEmpty(); + var sampledValues = meterValue.getSampledValue(); + assertThat(sampledValues).isNotEmpty(); var thisValueFound = false; // and check, if it can be found in the DB for (var values : details.getValues()) { if (values.getValue().equals(sampledValues.get(0).getValue())) {
🧹 Nitpick comments (29)
steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java (1)
93-106: Preserve URI semantics instead of string-concatenating the pathNow that
pathPrefixis aURI, concatenating it withchargeBoxIdby invokingtoString()defeats the point and can yield invalid targets (e.g., missing separators, clobbered queries/fragments). LetURI.resolve(...)do the heavy lifting and keep the field typed asURI, so downstream code doesn’t need to re-parse.- private final String connectionPath; + private final URI connectionUri; @@ - this.connectionPath = pathPrefix + chargeBoxId; + this.connectionUri = pathPrefix.resolve(chargeBoxId); @@ - var connect = client.connect(this, new URI(connectionPath), request); + var connect = client.connect(this, connectionUri, request);steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (3)
54-83: Avoid self-invoking @bean from non-@bean method; inject LoggingFeature insteadCalling loggingFeature() inside createDefaultEndpoint relies on @configuration proxying and is brittle if proxyBeanMethods=false. Pass LoggingFeature via bean method parameters and thread it into createDefaultEndpoint.
Apply:
@@ - public EndpointImpl ocpp12Endpoint( + public EndpointImpl ocpp12Endpoint( Bus bus, MessageIdInterceptor messageIdInterceptor, MessageHeaderInterceptor messageHeaderInterceptor, - ocpp.cs._2010._08.CentralSystemService ocpp12Server) { - return createDefaultEndpoint( - bus, messageIdInterceptor, messageHeaderInterceptor, ocpp12Server, "/CentralSystemServiceOCPP12"); + ocpp.cs._2010._08.CentralSystemService ocpp12Server, + LoggingFeature loggingFeature) { + return createDefaultEndpoint( + bus, messageIdInterceptor, messageHeaderInterceptor, loggingFeature, ocpp12Server, "/CentralSystemServiceOCPP12"); } @@ - public EndpointImpl ocpp15Endpoint( + public EndpointImpl ocpp15Endpoint( Bus bus, MessageIdInterceptor messageIdInterceptor, MessageHeaderInterceptor messageHeaderInterceptor, - ocpp.cs._2012._06.CentralSystemService ocpp15Server) { - return createDefaultEndpoint( - bus, messageIdInterceptor, messageHeaderInterceptor, ocpp15Server, "/CentralSystemServiceOCPP15"); + ocpp.cs._2012._06.CentralSystemService ocpp15Server, + LoggingFeature loggingFeature) { + return createDefaultEndpoint( + bus, messageIdInterceptor, messageHeaderInterceptor, loggingFeature, ocpp15Server, "/CentralSystemServiceOCPP15"); } @@ - public EndpointImpl ocpp16Endpoint( + public EndpointImpl ocpp16Endpoint( Bus bus, MessageIdInterceptor messageIdInterceptor, MessageHeaderInterceptor messageHeaderInterceptor, - ocpp.cs._2015._10.CentralSystemService ocpp16Server) { - return createDefaultEndpoint( - bus, messageIdInterceptor, messageHeaderInterceptor, ocpp16Server, "/CentralSystemServiceOCPP16"); + ocpp.cs._2015._10.CentralSystemService ocpp16Server, + LoggingFeature loggingFeature) { + return createDefaultEndpoint( + bus, messageIdInterceptor, messageHeaderInterceptor, loggingFeature, ocpp16Server, "/CentralSystemServiceOCPP16"); } @@ - private EndpointImpl createDefaultEndpoint( + private EndpointImpl createDefaultEndpoint( Bus bus, MessageIdInterceptor messageIdInterceptor, MessageHeaderInterceptor messageHeaderInterceptor, + LoggingFeature loggingFeature, Object serviceBean, String address) { return createEndpoint( bus, serviceBean, address, List.of(messageIdInterceptor, messageHeaderInterceptor), - List.of(loggingFeature())); + List.of(loggingFeature)); }Also applies to: 103-115
87-101: Router endpoint: remove dummy dependency on ocpp12 serviceUsing ocpp12Server as a placeholder couples the router bean to OCPP 1.2 types and may affect WSDL/service metadata. Prefer publishing the router with a minimal JAX‑WS Provider (or a lightweight POJO endpoint) and attach MediatorInInterceptor there.
Also verify steveProperties.paths.routerEndpointPath starts with “/”. If not, CXF publish(...) may create an unexpected relative address.
99-100: Use List.of() instead of Collections.emptyList() for consistencyMinor style nit; elsewhere List.of(...) is used.
- List.of(mediator), - Collections.emptyList()); + List.of(mediator), + List.of());steve/README.md (1)
103-104: Fix run command path (module vs repo root).The artifact is created under steve/target; either cd into steve or adjust the path.
- java -jar target/steve.war + java -jar steve/target/steve.warsteve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java (1)
27-28: Make PATH_INFIX a constant.Use static final for constant semantics.
- private final String PATH_INFIX = "/steve/websocket/CentralSystemService/"; + private static final String PATH_INFIX = "/steve/websocket/CentralSystemService/";steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (1)
49-51: Prefer explicit UTC ZoneId for clarity.Minor readability tweak.
+import java.time.ZoneId; @@ - var zoneId = ZoneOffset.UTC; - TimeZone.setDefault(TimeZone.getTimeZone(zoneId)); + TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC")));steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (1)
62-62: Avoid recreating the CXF client per phase; cache in a ThreadLocal.You’re creating a new proxy on each beforeRepeat/toRepeat. Cache it per thread for lower overhead.
- var client = getForOcpp16(soapPath); + client.set(getForOcpp16(soapPath)); @@ - var client = getForOcpp16(soapPath); + var client = this.client.get();Additions required outside this hunk:
// Add near other ThreadLocals import ocpp.cs._2015._10.CentralSystemService; // ... private final ThreadLocal<CentralSystemService> client = new ThreadLocal<>();Also applies to: 80-80
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (2)
68-68: Use utf8mb4 instead of utf8.MySQL’s utf8 is 3-byte; utf8mb4 is the correct full Unicode.
- hc.addDataSourceProperty(PropertyKey.characterEncoding.getKeyName(), "utf8"); + hc.addDataSourceProperty(PropertyKey.characterEncoding.getKeyName(), "utf8mb4");
98-101: Property naming nit: “executiveLogging” likely meant “executeLogging”.Consider renaming the SteveProperties flag to executeLogging (and keeping a legacy binder alias for backward compatibility).
Please confirm the intended application.yml key (e.g., steve.jooq.execute-logging). If you want, I can sketch a migration with @ConstructorBinding + @DeprecatedProperty and relaxed binding aliases.
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
60-66: Reuse the cached client instead of creating a new proxy.You already set client in beforeRepeat; use it here.
- var boot = getForOcpp16(soapPath) + var boot = client.get() .bootNotification( new BootNotificationRequest() .withChargePointVendor(getRandomString()) .withChargePointModel(getRandomString()), chargeBoxId.get());steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
101-108: Suppress unchecked cast or use typed factory to avoid warnings.Cast from f.create() to T is unchecked. Add @SuppressWarnings("unchecked") or use f.create(serviceClass).
- private static <T> T createBean(URI endpointAddress, Class<T> serviceClass) { + @SuppressWarnings("unchecked") + private static <T> T createBean(URI endpointAddress, Class<T> serviceClass) { var f = new JaxWsProxyFactoryBean(); ... return (T) f.create(); }steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java (1)
65-74: Reuse client to reduce overhead (optional).getForOcpp16(soapPath) is called multiple times; create once and reuse in this method to cut proxy creation cost.
Also applies to: 89-92
steve/src/test/resources/application-test.yml (1)
1-6: Avoid hard-coded DB creds in committed test config.Prefer env overrides (SPRING_DATASOURCE_URL/USERNAME/PASSWORD) or Testcontainers-based config to reduce local coupling and secret sprawl.
Do tests rely on a locally running MySQL? If not, consider switching to Testcontainers.
steve-api/pom.xml (1)
23-25: Mark jakarta.servlet-api as provided.For WAR/container deployments, servlet API should not be packaged into runtime artifacts.
<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> + <scope>provided</scope> </dependency>steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
42-56: Prefer externalized config over System.setProperty.Set springdoc.* in application.yml/application-test.yml instead of static block to avoid global side effects in tests.
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
65-76: Avoid global system property; use SpringApplication profilesUse application-scoped profile to prevent cross-test side effects.
- System.setProperty("spring.profiles.active", "test"); + var spring = new SpringApplication(SteveApplication.class); + spring.setAdditionalProfiles("test"); ... - app = SpringApplication.run(SteveApplication.class); + app = spring.run();steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java (1)
41-49: Inject UnidentifiedIncomingObjectService instead of newPrefer DI for configurability and testability.
- private final UnidentifiedIncomingObjectService unknownChargePointService = - new UnidentifiedIncomingObjectService(100); + private final UnidentifiedIncomingObjectService unknownChargePointService; ... - public ChargePointRegistrationService( - ChargePointRepository chargePointRepository, SteveProperties steveProperties) { + public ChargePointRegistrationService( + ChargePointRepository chargePointRepository, + SteveProperties steveProperties, + UnidentifiedIncomingObjectService unknownChargePointService) { this.chargePointRepository = chargePointRepository; this.autoRegisterUnknownStations = steveProperties.getOcpp().isAutoRegisterUnknownStations(); + this.unknownChargePointService = unknownChargePointService; }steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
74-76: Avoid wildcard CORS for WebSocketExternalize allowed origins to properties or restrict to known origins.
- registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") - .setHandshakeHandler(handshakeHandler) - .setAllowedOrigins("*"); + registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") + .setHandshakeHandler(handshakeHandler) + // TODO: replace with property-driven allowed origins + .setAllowedOriginPatterns("https://your.domain.example");steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
60-68: Return Validator instead of ValidatorFactory for clarityAvoids bean type ambiguity and matches getBean(Validator.class).
@Configuration static class ValidationConfig { @Bean - public ValidatorFactory validator(ApplicationContext applicationContext) { - var factory = new LocalValidatorFactoryBean(); - factory.setApplicationContext(applicationContext); - return factory; + public Validator validator(ApplicationContext applicationContext) { + var factory = new LocalValidatorFactoryBean(); + factory.setApplicationContext(applicationContext); + return factory; } }steve/src/main/resources/application.yml (2)
41-46: Avoid committing default credentialsShipping admin/1234 in the default profile is risky; prefer empty values with clear docs or use a separate sample config.
14-18: Non-standard logging propertylogging.appender is not a Spring Boot standard key; manage appenders in logback-spring.xml or use logging.file.name/path.
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java (1)
55-58: Satisfy NullAway: mark optional fields nullable and set sane defaultsAnnotate optional fields and default wsSessionSelectStrategy to avoid initialization warnings.
- private String steveVersion; - private String profile; + private @Nullable String steveVersion; + private @Nullable String profile; @@ public static class Jooq { private boolean executiveLogging; - private String schemaSource; + private @Nullable String schemaSource; private @Nullable String schema; } @@ public static class Ocpp { private boolean autoRegisterUnknownStations; private @Nullable String chargeBoxIdValidationRegex; - private String wsSessionSelectStrategy; + private String wsSessionSelectStrategy = "ALWAYS_LAST"; }Also applies to: 63-68, 82-87
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
615-621: Unused helper; consider removal.
nowWithoutMillis()is unused in this class.- /** - * https://github.com/steve-community/steve/issues/1371 - */ - private static OffsetDateTime nowWithoutMillis() { - return OffsetDateTime.now().with(ChronoField.MILLI_OF_SECOND, 0); - }steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (1)
123-139: Tighten exception assertion to only the call that should throw.Wrapping both BootNotification (which should not throw) and Authorize in one lambda hides intent and leaves unreachable assertions.
- assertThatExceptionOfType(WebServiceException.class).isThrownBy(() -> { - var boot = client.bootNotification( - new ocpp.cs._2015._10.BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - getRandomString()); - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isEqualTo(ocpp.cs._2015._10.RegistrationStatus.REJECTED); - - var auth = client.authorize( - new ocpp.cs._2015._10.AuthorizeRequest().withIdTag(getRandomString()), getRandomString()); - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(ocpp.cs._2015._10.AuthorizationStatus.INVALID); - }); + var boot = client.bootNotification( + new ocpp.cs._2015._10.BootNotificationRequest() + .withChargePointVendor(getRandomString()) + .withChargePointModel(getRandomString()), + getRandomString()); + assertThat(boot).isNotNull(); + assertThat(boot.getStatus()).isEqualTo(ocpp.cs._2015._10.RegistrationStatus.REJECTED); + + assertThatExceptionOfType(WebServiceException.class).isThrownBy(() -> + client.authorize( + new ocpp.cs._2015._10.AuthorizeRequest().withIdTag(getRandomString()), + getRandomString()) + );steve-jooq/pom.xml (1)
26-33: Reduce duplicate/managed deps; rely on Boot’s BOM where possible.When using spring-boot-starter-jooq, the following are usually redundant or BOM-managed: org.jooq:jooq, HikariCP, spring-context, slf4j-api, jackson-databind.
Consider removing these explicit deps unless this module intentionally shades/isolates versions. Ensure compilation still passes after removal and that jOOQ runtime/codegen versions stay aligned with Boot’s managed versions.
Also applies to: 47-53, 55-65
steve/pom.xml (3)
70-72: Clarify deployment model (embedded Jetty vs external container) and align scopes
- Executable WAR with embedded Jetty: keep spring-boot-starter-jetty (compile) but don’t add duplicate jetty-server dependency.
- Deploying to external servlet container: mark spring-boot-starter-jetty as provided to avoid conflicts.
Two options (pick one):
# Option A: Executable WAR (embedded Jetty) — remove duplicate jetty-server <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> @@ - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </dependency># Option B: WAR for external container — set provided scope <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> + <scope>provided</scope> </dependency>Also applies to: 99-107
100-103: Drop duplicate Jetty dependencyspring-boot-starter-jetty already pulls jetty-server; keeping both invites version skew.
Apply:
- <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </dependency>
124-129: Reduce testclasspath bloat; rely on spring-boot-starter-teststarter-test already brings JUnit Jupiter, AssertJ, JSON Path, Mockito (with Byte Buddy). Remove redundant explicit deps unless directly needed.
Apply:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-params</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>net.bytebuddy</groupId> - <artifactId>byte-buddy</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.jayway.jsonpath</groupId> - <artifactId>json-path</artifactId> - <scope>test</scope> - </dependency>If you directly use Byte Buddy APIs or custom JUnit engines, keep only those specific ones.
Also applies to: 146-174
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
-
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttf -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.ico -
steve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svg -
steve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (65)
-
.github/workflows/main.yml(1 hunks) -
AGENTS.md(2 hunks) -
Dockerfile(1 hunks) -
pom.xml(5 hunks) -
steve-api/pom.xml(1 hunks) -
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks) -
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks) -
steve-core/pom.xml(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks) -
steve-jooq/pom.xml(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks) -
steve-ocpp-1-x/pom.xml(2 hunks) -
steve-ocpp-transport-soap/pom.xml(1 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks) -
steve-ocpp-transport-websocket/pom.xml(0 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(2 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(6 hunks) -
steve-ocpp/pom.xml(0 hunks) -
steve-ui-jsp/pom.xml(2 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks) -
steve/README.md(2 hunks) -
steve/pom.xml(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks) -
steve/src/main/resources/application-dev.yml(1 hunks) -
steve/src/main/resources/application.yml(1 hunks) -
steve/src/main/resources/config/logback.xml(0 hunks) -
steve/src/main/resources/config/main.properties(0 hunks) -
steve/src/main/resources/logback-spring.xml(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(9 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(6 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(4 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks) -
steve/src/test/resources/application-test.yml(1 hunks)
💤 Files with no reviewable changes (12)
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve/src/main/resources/config/logback.xml
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
- steve/src/main/resources/config/main.properties
- steve-ocpp/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve-ocpp-transport-websocket/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/Application.java
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
🚧 Files skipped from review as they are similar to previous changes (16)
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java
- steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
- steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
- .github/workflows/main.yml
- steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java
- AGENTS.md
- steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
- steve-core/pom.xml
- steve/src/main/resources/application-dev.yml
- steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java
- steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java
- steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java
- Dockerfile
- steve-ocpp-transport-soap/pom.xml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.javasteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue81.javasteve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve/src/test/resources/application-test.ymlsteve/src/main/resources/logback-spring.xmlsteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javapom.xmlsteve/pom.xmlsteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/main/resources/application.ymlsteve/README.mdsteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
steve/src/test/resources/application-test.ymlsteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javapom.xmlsteve/pom.xmlsteve-jooq/pom.xmlsteve/README.mdsteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java
📚 Learning: 2025-09-15T15:31:15.967Z
Learnt from: juherr
PR: juherr/evolve#91
File: steve-core/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java:125-129
Timestamp: 2025-09-15T15:31:15.967Z
Learning: In the steve-core OCPP project, JAXB bindings are customized in ocpp_binding.xml to map xs:dateTime XML types directly to java.time.OffsetDateTime using the JavaDateTimeConverter adapter, eliminating the need for XMLGregorianCalendar conversions that would normally be required in standard JAXB bindings.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
steve-jooq/pom.xmlsteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java
🧬 Code graph analysis (4)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (2)
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(61-238)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java (1)
RequiredArgsConstructor(56-213)steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (1)
RequiredArgsConstructor(313-317)
🪛 GitHub Actions: build and run tests
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
[warning] 76-76: NullAway: initializer method does not guarantee @nonnull field chargePointsService (line 70) is initialized along all control-flow paths
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
[warning] 30-30: NullAway: initializer method does not guarantee @nonnull fields steveVersion (line 56), profile (line 57) are initialized along all control-flow paths.
[warning] 63-63: NullAway: initializer method does not guarantee @nonnull field schemaSource (line 66) is initialized along all control-flow paths.
[warning] 82-82: NullAway: initializer method does not guarantee @nonnull field wsSessionSelectStrategy (line 86) is initialized along all control-flow paths.
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
🔇 Additional comments (41)
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java (1)
31-31: Explicitly targeting the String constructor looks good.The
(String) nullcast neatly disambiguates the new overloads so the test still exercises the default-regex path. No further changes needed here.steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (3)
49-52: LGTM: exposing MessageIdInterceptor as a beanGood extraction; clear reuse across endpoints.
117-128: Confirm directionality of interceptorsOnly inbound interceptors are attached. If MessageHeaderInterceptor or MessageIdInterceptor must also affect outbound messages, add them to getOutInterceptors() as well.
58-59: Ensure bean availability for MessageHeaderInterceptorThese bean method parameters require a MessageHeaderInterceptor bean elsewhere. Verify it is defined and in the same ApplicationContext.
Also applies to: 68-69, 78-79
steve/README.md (1)
76-80: Config move to application.yml: LGTMThe docs now point to application.yml and list key knobs. Looks consistent with the PR’s property-driven bootstrapping.
steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java (1)
32-96: Tests correctly adapted to new getLastBitFromUrl signature.Coverage looks good across edge cases (empty, null, percent, dash, spaces, extra slash).
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (1)
41-64: Spring Boot entrypoint: LGTMClean bootstrapping, excludes JooqAutoConfiguration, sets CXF/validator logging and UTC default TZ, and logs at startup.
steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (1)
48-50: Main entry change: LGTMSwitch to no-arg construction and attack() works with the new path resolution approach.
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
46-52: DataSource bean wiring: LGTMDelegates to factory with Boot DataSourceProperties and SteveProperties TZ.
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
44-45: Main entry change: LGTMNo-arg constructor aligns with new config approach.
steve/src/main/resources/logback-spring.xml (1)
50-51: Remove global TRACE/DEBUG for Spring Security/Web; scope to dev/test.These global loggers override the dev/test INFO settings and will apply in prod, risking noise and sensitive leakage.
- <logger name="org.springframework.security" level="TRACE"/> - <logger name="org.springframework.web" level="DEBUG"/> + <!-- Limit higher verbosity to dev/test --> + <springProfile name="dev,test"> + <logger name="org.springframework.security" level="TRACE"/> + <logger name="org.springframework.web" level="DEBUG"/> + </springProfile>Alternatively, adjust the existing dev,test block and delete these globals:
@@ - <logger name="org.springframework.security" level="INFO"/> - <logger name="org.springframework.web.servlet" level="INFO"/> + <logger name="org.springframework.security" level="TRACE"/> + <logger name="org.springframework.web" level="DEBUG"/>steve-ocpp-1-x/pom.xml (1)
24-59: Confirm CXF and JAXB-plugins version properties are defined upstream.
Using${cxf.version}and${jaxb-plugins.version}aligns with central management; ensure those properties are declared in your parent POM or imported BOM (e.g.cxf-bom), and add any missing definitions or BOM imports.steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java (1)
49-51: Confirm soapPath availability/type.attack() now relies on a field soapPath. Ensure StressTest defines soapPath as a URI and initializes it before use.
pom.xml (4)
178-208: Annotation processor path uses undefined ${lombok.version}.Even with the property added, ensure the version matches the dependency and your IDE APT setup to avoid mismatches.
98-101: CXF BOM import looks good.Centralizes CXF versions; 4.1.3 aligns with Jakarta EE 10/JDK17+.
Double-check transitive CXF artifacts used across modules are covered by the BOM.
220-236: Inline license header OK.No functional impact; Spotless should keep formatting consistent.
5-10: Spring Boot 3.5.6 is released and aligns with Spring Framework 6.2.x. Compatibility verified.steve-api/pom.xml (1)
33-35: Consolidation to spring-boot-starter-test looks good.Brings JUnit 5, Mockito, AssertJ under one starter.
steve-ui-jsp/pom.xml (2)
96-106: Resource relocation LGTM.Copying from src/main/webapp with explicit includes is correct for JSP packaging.
129-132: Updated JSPC paths LGTM.Matches the new webapp layout.
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
58-77: Switching to SteveProperties for version is good.Avoids tight coupling to legacy configuration.
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
70-87: Mirror the hardening for WS URI builder (NPEs/IPv6).Same nullability and IPv6 pitfalls as SOAP builder. Use ws/wss scheme per SSL and guard nulls.
- public static URI getWsPath(ServerProperties serverProperties, SteveProperties steveProperties) - throws URISyntaxException { - var scheme = "ws"; - if (serverProperties.getSsl().isEnabled()) { - scheme = "wss"; - } - - return new URI( - scheme, - null, - serverProperties.getAddress().getHostName(), - serverProperties.getPort(), - serverProperties.getServlet().getContextPath() - + steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/", - null, - null); - } + public static URI getWsPath(ServerProperties serverProperties, SteveProperties steveProperties) + throws URISyntaxException { + var ssl = serverProperties.getSsl(); + var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "wss" : "ws"; + + var addr = serverProperties.getAddress(); + var host = (addr != null) ? addr.getHostAddress() : "localhost"; + if (host.indexOf(':') >= 0 && !host.startsWith("[")) { + host = "[" + host + "]"; + } + + Integer port = serverProperties.getPort(); + int p = (port != null) ? port : 8080; + + var servlet = serverProperties.getServlet(); + var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : ""; + + String path = ctx + + steveProperties.getPaths().getWebsocketMapping() + + steveProperties.getPaths().getRouterEndpointPath() + + "/"; + + return new URI(scheme, null, host, p, path, null, null); + }steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java (1)
21-21: LGTM: Migrates to SteveProperties cleanlyConstructor migration and path usage look correct.
Also applies to: 42-53
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
64-75: LGTM: pathInfix derived from properties and passed throughGood move away from hard-coded PATH_INFIX; handler wiring is consistent.
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (1)
21-24: LGTM: constructor injection and null/empty short-circuitCleaner injection and early return; implementation is fine.
Also applies to: 34-38, 46-55
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (1)
61-64: LGTM: config-driven session select strategyUsing SteveProperties for strategy selection aligns with the PR direction.
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
89-97: Close order: clean DB before closing Spring contextAvoid “pool is closed” by cleaning before app.close().
- } finally { - try { - if (app != null) { - app.close(); - } - } finally { - if (databasePreparer != null) { - databasePreparer.cleanUp(); - } - } - } + } finally { + try { + if (databasePreparer != null) { + databasePreparer.cleanUp(); + } + } finally { + if (app != null) { + app.close(); + } + } + }steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (2)
47-51: Good: constructor now null-safe for ServerProperties.getSsl()Passing nullable Ssl to create(...) prevents NPE when SSL is unset.
83-90: Null-unsafe keystore and missing trust store supportCalling keyStoreFromFile with nulls will fail when only trust store is configured; add null checks and honor optional trust store.
- var socketFactory = SslContextBuilder.builder() - .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) - .usingTLS() - .usingDefaultAlgorithm() - .usingKeyManagerPasswordFromKeyStore() - .buildMergedWithSystem() - .getSocketFactory(); + var builder = SslContextBuilder.builder(); + if (ssl.getKeyStore() != null) { + builder = builder.keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()); + } + if (ssl.getTrustStore() != null) { + builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword()); + } + var socketFactory = builder + .usingTLS() + .usingDefaultAlgorithm() + .usingKeyManagerPasswordFromKeyStore() + .buildMergedWithSystem() + .getSocketFactory();steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (2)
83-88: LGTM: jsonPath derived from properties fixes previous null endpointBuilding the WS URI from ServerProperties/SteveProperties in setUp() is correct and keeps tests environment-driven.
151-176: LGTM: Versioned JSON tests cover 1.2/1.5/1.6 happy pathsSolid assertions on registration and authorization responses.
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (2)
63-160: LGTM: Property-driven paths and CSRF exemptions are correctly wiredManager/API mappings, resource permits, and login/logout flows look consistent with SteveProperties.
162-173: LGTM: Separate API key chain with stateless sessionSecurityMatcher on API path and BasicAuthenticationFilter setup are appropriate.
steve/src/main/resources/application.yml (1)
7-9: Quote Maven @...@ tokens to make YAML validUnquoted scalars starting with @ break YAML parsing; quote them so IDEs/tests can load unfiltered YAML.
- url: @db.jdbc.url@ - username: @db.user@ - password: @db.password@ + url: "@db.jdbc.url@" + username: "@db.user@" + password: "@db.password@" @@ - version: @project.version@ + version: "@project.version@"Also applies to: 38-38
steve-jooq/pom.xml (1)
111-119: Verify that ${flyway.version}, ${jooq.version}, ${mysql.version} are defined.These properties must be set in a parent or here; otherwise Maven will fail.
Run to confirm and locate definitions:
#!/usr/bin/env bash set -euo pipefail echo "Searching for property definitions..." rg -nP '<properties>.*?(</properties>)' -g 'pom.xml' -C2 || true rg -nP '<(mysql\.version|jooq\.version|flyway\.version)>\s*[^<]+' -S || true echo echo "References in POMs:" rg -nP '\$\{(mysql\.version|jooq\.version|flyway\.version)\}' -S -C2Also applies to: 183-196, 231-234
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (1)
64-75: LGTM: Instance-based preparer with injected DSLContext.The move away from static/global state improves test isolation and clarity.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
299-299: Fix: LinkedHashMap has no lastEntry(); compute the last timestamp directly.Replace the invalid call with a computed value.
- var faultedTime = timeStatusMap.lastEntry().getKey().plusMinutes(1); + var faultedTime = startingTime.plusMinutes(ChargePointStatus.values().length);steve/pom.xml (4)
203-226: Use WAR overlays for steve-ui-jsp instead of unpacking into build dirUnpacking into ${project.build.directory}/${project.build.finalName} is fragile; the war plugin may overwrite it.
Apply:
- <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>unpack-steve-ui-jsp-webapp</id> - <goals> - <goal>unpack</goal> - </goals> - <phase>process-resources</phase> - <configuration> - <artifactItems> - <artifactItem> - <groupId>de.rwth.idsg</groupId> - <artifactId>steve-ui-jsp</artifactId> - <version>${project.version}</version> - <overWrite>true</overWrite> - <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> - <includes>WEB-INF/**,static/**</includes> - </artifactItem> - </artifactItems> - </configuration> - </execution> - </executions> - </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <overlays> + <overlay> + <groupId>de.rwth.idsg</groupId> + <artifactId>steve-ui-jsp</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin>
186-194: Disable Maven filtering for application.yml to avoid placeholder/secret injectionapplication.yml should be resolved by Spring at runtime, not Maven at build time. Split resources so only logback-spring.xml (if required) is filtered.
Apply this diff:
<resources> - <resource> - <filtering>true</filtering> - <directory>src/main/resources</directory> - <includes> - <include>application.yml</include> - <include>logback-spring.xml</include> - </includes> - </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + <includes> + <include>application.yml</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + <includes> + <include>logback-spring.xml</include> + </includes> + </resource> </resources>
197-201: Ensure spring-boot-maven-plugin version is managed
No version is declared in steve/pom.xml (lines 197–201); verify that your parent POM’s pins spring-boot-maven-plugin. If not, add an explicit version.
86-88: CXF BOM already manages this dependency
The root pom.xml imports org.apache.cxf:cxf-bom (lines 96–102), so cxf-spring-boot-starter-jaxws inherits its version. To update, bump the cxf.version property in the parent POM.
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
Show resolved
Hide resolved
| var endpointDeque = lookupTable.get(chargeBoxId); | ||
| if (endpointDeque == null) { | ||
| return 0; | ||
| } else { | ||
| return endpointDeque.size(); | ||
| } | ||
| return endpointDeque.size(); | ||
| } |
There was a problem hiding this comment.
Synchronize getSize() with per-id lock
Read of non-thread-safe Deque must be guarded by the same lock.
- var endpointDeque = lookupTable.get(chargeBoxId);
- if (endpointDeque == null) {
- return 0;
- }
- return endpointDeque.size();
+ var l = locks.get(chargeBoxId);
+ l.lock();
+ try {
+ var endpointDeque = lookupTable.get(chargeBoxId);
+ if (endpointDeque == null) {
+ return 0;
+ }
+ return endpointDeque.size();
+ } finally {
+ l.unlock();
+ }🤖 Prompt for AI Agents
In
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java
around lines 148 to 153, the method reads a non-thread-safe Deque without
holding the per-id lock; wrap the lookupTable.get(chargeBoxId) and the
subsequent size() call inside the same per-id lock used elsewhere (e.g., obtain
the lock object for chargeBoxId then synchronize on it or acquire its
ReentrantLock) so that the Deque access is guarded; if the lock is null or not
found, preserve the existing null/zero semantics, and ensure you release the
lock properly (or use a try/finally when locking).
| var req = RequestBuilder.post(soapPath) | ||
| .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") | ||
| .setEntity(new StringEntity(body, contentType)) | ||
| .build(); |
There was a problem hiding this comment.
Switch RequestBuilder.post back to a string URI.
RequestBuilder.post(...) in Apache HttpClient 4.5.x only accepts a String URI. With soapPath now being a URI, this call no longer compiles. Convert the URI to a string before passing it to RequestBuilder.post (or call post() and then .setUri(soapPath)).
Apply this fix:
- var req = RequestBuilder.post(soapPath)
+ var req = RequestBuilder.post(soapPath.toString())
.addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction")
.setEntity(new StringEntity(body, contentType))
.build();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var req = RequestBuilder.post(soapPath) | |
| .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") | |
| .setEntity(new StringEntity(body, contentType)) | |
| .build(); | |
| var req = RequestBuilder.post(soapPath.toString()) | |
| .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") | |
| .setEntity(new StringEntity(body, contentType)) | |
| .build(); |
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java around
lines 97 to 100, the call RequestBuilder.post(soapPath) fails because
RequestBuilder.post in HttpClient 4.5.x expects a String URI, while soapPath is
a URI; convert soapPath to a string (e.g. soapPath.toString()) before passing it
to RequestBuilder.post, or alternatively call RequestBuilder.post() with no args
and then call .setUri(soapPath) on the builder to set the URI.
| assertThat(environment.getProperty("steve.ocpp.auto-register-unknown-stations")) | ||
| .isEqualTo("true"); |
There was a problem hiding this comment.
🧩 Analysis chain
Verify test property override for auto-register-unknown-stations
Assertion expects true; ensure application-test.yml (or @TestPropertySource) sets it to true.
🏁 Script executed:
#!/bin/bash
# Look for test overrides setting the property to true
rg -n -C2 -g '!**/target/**' -P --type=yaml 'auto-register-unknown-stations\s*:\s*true' || true
rg -n -C2 -g '!**/target/**' -P --type=properties 'steve\.ocpp\.auto-register-unknown-stations\s*=\s*true' || true
fd -HI 'application-test.*\.(ya?ml|properties)' || trueLength of output: 229
🏁 Script executed:
#!/bin/bash
rg -nC2 'auto-register-unknown-stations' steve/src/test/resources/application-test.yml || trueLength of output: 0
Verify override of steve.ocpp.auto-register-unknown-stations in tests
src/test/resources/application-test.yml doesn’t set steve.ocpp.auto-register-unknown-stations, so the assertion may fail; add
steve:
ocpp:
auto-register-unknown-stations: true🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/StressTest.java around lines 75-76 the
test asserts that
environment.getProperty("steve.ocpp.auto-register-unknown-stations") is "true"
but src/test/resources/application-test.yml does not define that property; add
the property under the test YAML by adding
steve.ocpp.auto-register-unknown-stations: true (nesting under steve -> ocpp) so
the environment returns "true" and the assertion reliably passes.
bad9eaa to
80efa0c
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
steve-jooq/pom.xml (1)
176-194: Declare or inherit jOOQ version property
jooq.versionisn’t declared in any POM here; add it under<properties>in steve-parent or ensure it’s provided by your parent POM (e.g. Spring Boot parent) so builds don’t fail.
🧹 Nitpick comments (7)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java (1)
65-92: Consider reusing the SOAP client withinattackInternal.You’re recreating the CXF client three times before the stress loop starts. Grabbing it once keeps the intent clear and avoids redundant stub creation.
Apply this diff if you agree:
- var boot = getForOcpp16(soapPath) - .bootNotification( + var client = getForOcpp16(soapPath); + var boot = client.bootNotification( new BootNotificationRequest() .withChargePointVendor(getRandomString()) .withChargePointModel(getRandomString()), chargeBoxId); @@ - var start = getForOcpp16(soapPath) - .startTransaction( + var start = client.startTransaction( new StartTransactionRequest() .withConnectorId(connectorId) .withIdTag(idTag) .withTimestamp(startDateTime) .withMeterStart(meterStart), chargeBoxId);steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java (1)
93-97: Use URI.resolve for reliable connectionPath construction
Replace the implicit concatenation with a URI resolve to avoid missing slashes:- this.connectionPath = pathPrefix + chargeBoxId; + this.connectionPath = pathPrefix.resolve(chargeBoxId).toString();steve-core/pom.xml (1)
45-48: Remove redundant jakarta.mail-api dependency
spring-boot-starter-mail already brings in the Jakarta Mail API and its implementation transitively; drop the explicit<dependency>onjakarta.mail:jakarta.mail-api.steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (1)
166-168: getACopy() is a shallow copy; callers can observe concurrent mutation.Return a snapshot (deep copy of deques) to avoid iteration issues and surprising visibility.
Apply:
- public Map<String, Deque<SessionContext>> getACopy() { - return ImmutableMap.copyOf(lookupTable); - } + public Map<String, Deque<SessionContext>> getACopy() { + var copy = new ConcurrentHashMap<String, Deque<SessionContext>>(); + lookupTable.forEach((id, dq) -> { + var l = locks.get(id); + l.lock(); + try { + copy.put(id, new ArrayDeque<>(dq)); + } finally { + l.unlock(); + } + }); + return ImmutableMap.copyOf(copy); + }steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java (2)
70-75: Log actionable context when registration_status mapping failsInclude the raw DB value and chargeBoxId to aid diagnosis.
- } catch (Exception e) { - // in cases where the database entry (string) is altered, and therefore cannot be converted to enum - log.error("Exception happened", e); - return Optional.empty(); - } + } catch (Exception e) { + // in cases where the database entry (string) is altered, and therefore cannot be converted to enum + log.error("Invalid registration_status '{}' for chargeBoxId '{}'", status.get(), chargeBoxId, e); + return Optional.empty(); + }
83-91: Minor: prefer List.of over Collections.singletonListMore idiomatic on Java 17.
- chargePointRepository.addChargePointList(Collections.singletonList(chargeBoxId)); + chargePointRepository.addChargePointList(List.of(chargeBoxId));steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (1)
135-149: Manual record population may be error-prone.The manual field-by-field population of OcppTagActivityRecord could become inconsistent if the DTO structure changes. Consider using a mapping library or builder pattern.
public OcppTagActivityRecord getOcppTagRecord(String idTag) { var impl = new OcppTagRepositoryImpl(dslContext); var dto = impl.getRecord(idTag).orElseThrow(); - var activity = new OcppTagActivityRecord(); - activity.setOcppTagPk(dto.getOcppTagPk()); - activity.setIdTag(dto.getIdTag()); - activity.setParentIdTag(dto.getParentIdTag()); - activity.setExpiryDate(toLocalDateTime(dto.getExpiryDate())); - activity.setInTransaction(dto.isInTransaction()); - activity.setBlocked(dto.isBlocked()); - activity.setMaxActiveTransactionCount(dto.getMaxActiveTransactionCount()); - activity.setActiveTransactionCount(dto.getActiveTransactionCount()); - activity.setNote(dto.getNote()); - return activity; + return new OcppTagActivityRecord() + .setOcppTagPk(dto.getOcppTagPk()) + .setIdTag(dto.getIdTag()) + .setParentIdTag(dto.getParentIdTag()) + .setExpiryDate(toLocalDateTime(dto.getExpiryDate())) + .setInTransaction(dto.isInTransaction()) + .setBlocked(dto.isBlocked()) + .setMaxActiveTransactionCount(dto.getMaxActiveTransactionCount()) + .setActiveTransactionCount(dto.getActiveTransactionCount()) + .setNote(dto.getNote()); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
-
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttf -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.ico -
steve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svg -
steve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (67)
-
.github/workflows/main.yml(1 hunks) -
AGENTS.md(2 hunks) -
Dockerfile(1 hunks) -
pom.xml(5 hunks) -
steve-api/pom.xml(2 hunks) -
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks) -
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks) -
steve-core/pom.xml(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(6 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(0 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks) -
steve-jooq/pom.xml(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks) -
steve-ocpp-1-x/pom.xml(2 hunks) -
steve-ocpp-transport-soap/pom.xml(1 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks) -
steve-ocpp-transport-websocket/pom.xml(0 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(1 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(7 hunks) -
steve-ocpp/pom.xml(0 hunks) -
steve-ui-jsp/pom.xml(2 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks) -
steve/README.md(2 hunks) -
steve/pom.xml(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks) -
steve/src/main/resources/application-dev.yml(1 hunks) -
steve/src/main/resources/application.yml(1 hunks) -
steve/src/main/resources/config/logback.xml(0 hunks) -
steve/src/main/resources/config/main.properties(0 hunks) -
steve/src/main/resources/logback-spring.xml(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(8 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(6 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(4 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks) -
steve/src/test/resources/application-test.yml(1 hunks)
💤 Files with no reviewable changes (13)
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/resources/config/logback.xml
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve/src/main/resources/config/main.properties
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve-ocpp-transport-websocket/pom.xml
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
- steve/src/main/java/de/rwth/idsg/steve/Application.java
- steve-ocpp/pom.xml
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
🚧 Files skipped from review as they are similar to previous changes (12)
- steve-api/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java
- steve/src/test/resources/application-test.yml
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
- steve-ocpp-1-x/pom.xml
- steve-ui-jsp/pom.xml
- steve/src/test/java/de/rwth/idsg/steve/StressTest.java
- steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java
- steve/README.md
- steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
- steve-ocpp-transport-soap/pom.xml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.javasteve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.javasteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.javasteve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.javasteve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue81.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.javasteve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve/src/main/resources/application-dev.ymlDockerfilesteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javaAGENTS.mdsteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javapom.xmlsteve/src/main/resources/application.ymlsteve/src/main/resources/logback-spring.xmlsteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/pom.xmlsteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
.github/workflows/main.ymlsteve-jooq/pom.xmlAGENTS.mdpom.xmlsteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/pom.xml
📚 Learning: 2025-09-15T15:31:15.967Z
Learnt from: juherr
PR: juherr/evolve#91
File: steve-core/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java:125-129
Timestamp: 2025-09-15T15:31:15.967Z
Learning: In the steve-core OCPP project, JAXB bindings are customized in ocpp_binding.xml to map xs:dateTime XML types directly to java.time.OffsetDateTime using the JavaDateTimeConverter adapter, eliminating the need for XMLGregorianCalendar conversions that would normally be required in standard JAXB bindings.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
steve-jooq/pom.xmlAGENTS.mdsteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java
🧬 Code graph analysis (6)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (4)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
Slf4j(39-74)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(61-237)steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (1)
ActiveProfiles(56-136)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java (1)
RequiredArgsConstructor(56-213)steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (1)
RequiredArgsConstructor(313-317)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
Configuration(39-121)
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (1)
ActiveProfiles(56-136)
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
🪛 GitHub Actions: build and run tests
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
[warning] 75-75: NullAway: initializer method does not guarantee @nonnull field chargePointsService is initialized along all control-flow paths
🔇 Additional comments (82)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (3)
72-88: ConsistentsoapPathusage looks correct.Running all SOAP invocations through the shared
soapPathaligns this test with the new configuration bootstrap, so this part looks good to me.
156-170: URI-aware SOAP envelope looks good.Accepting a
URIhere and embedding it into theToheader keeps the payload in sync with the new configuration approach. No issues spotted.
97-100: Compile error:RequestBuilder.poststill needs aString.
RequestBuilder.post(URI)doesn’t exist in HttpClient 4.5.x, so this line won’t compile. Please convert theURIto a string (or set the URI on the builder afterwards) before building the request.- var req = RequestBuilder.post(soapPath) + var req = RequestBuilder.post(soapPath.toString()) .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") .setEntity(new StringEntity(body, contentType)) .build();steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (2)
41-47: Spring bean registration looks goodRegistering
LogFileRetrieveras a component gives us clean DI without altering its construction semantics. No concerns here.
89-101: Appender iteration update looks correctSwitching to a
whileloop and collecting everyFileAppenderpath handles multi-appender setups gracefully. 👍steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
69-84: Fix NullAway by assigningchargePointsServiceinsetUp.NullAway is still complaining (see pipeline warning) because the field can appear null when we build the controller. Inject the Mockito mock through the
@BeforeEachmethod and assign it to the field before using it.- @Mock - private ChargePointsService chargePointsService; + private ChargePointsService chargePointsService; @BeforeEach - public void setUp() { + public void setUp(@Mock ChargePointsService chargePointsService) { + this.chargePointsService = chargePointsService; var ctx = new AnnotationConfigApplicationContext();steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
44-44:soapPathis initialised in attack() before use; manual main() entry is safe.steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java (1)
49-51: No-arg StressTest wiring looks good.Switching to the default constructor plays nicely with the new
StressTestplumbing—no concerns here.steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java (1)
89-91: LGTM! Clean API improvement for URI handling.The constructor signature change from
String pathPrefixtoURI pathPrefixis appropriate for the Spring Boot migration and provides better type safety for URL handling.steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java (2)
80-80: LGTM! Consistent with the OcppJsonChargePoint API change.The update to pass
jsonPath(presumably a URI) instead ofpathaligns with the constructor signature change in OcppJsonChargePoint.
64-65: jsonPath is initialized in parent StressTest (line 82)
No further action required.steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (2)
62-62: LGTM! Consistent usage of soapPath for SOAP client configuration.The changes correctly use
soapPathinstead of the previouspathparameter, maintaining consistency throughout the test execution.Also applies to: 80-80
49-50: soapPath initialization confirmed in StressTest.attack()
soapPath is set via getSoapPath(...) in the parent attack() method before attackInternal() runs. No changes required.steve/src/main/resources/application-dev.yml (1)
1-9: LGTM! Development configuration is appropriate.The development profile correctly disables compression for debugging and enables verbose jOOQ logging to help trace SQL execution during development.
Dockerfile (3)
81-82: LGTM! WAR packaging changes correctly implemented.The switch from JAR to WAR packaging is properly reflected in both the COPY command and the subsequent CMD execution.
89-89: LGTM! Correct WAR execution command.The CMD properly executes the Spring Boot WAR file with appropriate memory settings.
77-77: Spring Boot property naming fix needed.The Java system properties for server configuration should use Spring Boot's standard property names. The current
-Dserver.addressand-Dserver.portare incorrect.Apply this diff to fix the property names:
-ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" +ENV JAVA_TOOL_OPTIONS="-Dspring.application.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.compression.enabled=false"Actually, for the server address binding, Spring Boot uses
server.address(notspring.application.address). Let me correct that:-ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" +ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.compression.enabled=false"Note: Changed
-Dserver.gzip.enabled=falseto-Dserver.compression.enabled=falseto match Spring Boot 3.x property naming.Likely an incorrect or invalid review comment.
.github/workflows/main.yml (3)
50-50: Spring profiles configuration looks correct.Using
-Dspring.profiles.active=test,devproperly activates both test and dev Spring profiles during the build, which aligns with the new Spring Boot configuration approach.
60-60: WAR execution correctly updated.The change from
steve.jartosteve.warproperly reflects the packaging migration to WAR format.
49-49: AdjustuseRealDatabaseprofile scopeThe
useRealDatabaseMaven profile is defined only insteve-jooq/pom.xml, so invokingmvn -PuseRealDatabaseat the repo root won’t activate it.
Options:
- Move the
<profile>definition into the rootpom.xml- Or run Maven in the
steve-jooqmodule (e.g. setworking-directory: steve-jooqin the workflow).github/workflows/main.yml:49
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (4)
39-43: Clean migration to SteveProperties-based configuration.The removal of static dependencies and adoption of constructor injection with
StevePropertiesaligns well with Spring Boot's configuration model and improves testability.
46-46: Proper removal of checked exception.Removing
throws ServletExceptionis correct as the method no longer performs operations that could throw this checked exception.
49-52: Spring context initialization looks good.The simplified context creation using component scanning is appropriate for Spring Boot applications and eliminates the previous complex initialization logic.
57-57: Properties-driven URL mappings properly implemented.Good use of
StevePropertiesto centralize URL mapping configuration. This makes the application more configurable and maintainable.Also applies to: 62-62, 72-72
steve-core/pom.xml (3)
32-35: LGTM! Spring Boot starter for validation properly added.The addition of
spring-boot-starter-validationbrings in all necessary validation dependencies including Hibernate Validator.
36-39: LGTM! Spring Security starter properly integrated.The
spring-boot-starter-securitydependency appropriately replaces the manual Spring Security configuration dependency.
40-43: LGTM! Mail starter simplifies email configuration.Using
spring-boot-starter-mailprovides auto-configuration for mail services, simplifying the setup.steve/src/main/resources/logback-spring.xml (2)
37-48: dev/test INFO overrides are neutralized by later global loggers.Because the global TRACE/DEBUG (Lines 50–51) are active for all profiles, the INFO levels here won’t take effect in dev/test.
Use the patch in my previous comment (remove global TRACE/DEBUG, add them inside dev,test; add conservative INFO in prod).
50-51: Don’t enable TRACE/DEBUG globally; restrict to dev/test.These two loggers apply in all profiles (including prod) and will flood logs and risk sensitive detail leakage.
Apply:
- <logger name="org.springframework.security" level="TRACE"/> - <logger name="org.springframework.web" level="DEBUG"/> + <springProfile name="dev,test"> + <logger name="org.springframework.security" level="TRACE"/> + <logger name="org.springframework.web" level="DEBUG"/> + </springProfile> + + <springProfile name="prod"> + <logger name="org.springframework.security" level="INFO"/> + <logger name="org.springframework.web" level="INFO"/> + </springProfile>steve-jooq/pom.xml (1)
21-35: Good: adopt spring-boot-starter-jooq, defer flyway starter.Using Boot’s jOOQ starter simplifies runtime wiring; keeping flyway-core (without the starter) is fine for Boot 3.x.
Please confirm Flyway runs at app startup as expected (Boot auto-config should pick up flyway-core on classpath).
steve/pom.xml (4)
186-201: Avoid filtering application.yml to prevent unintended placeholder substitution and secret baking.Filtering may resolve Spring placeholders (${...}) at build time.
Apply:
- <resources> - <resource> - <filtering>true</filtering> - <directory>src/main/resources</directory> - <includes> - <include>application.yml</include> - </includes> - </resource> - <resource> - <filtering>false</filtering> - <directory>src/main/resources</directory> - <excludes> - <exclude>application-*.yml</exclude> - <exclude>logback-spring.xml</exclude> - </excludes> - </resource> - </resources> + <resources> + <resource> + <filtering>false</filtering> + <directory>src/main/resources</directory> + </resource> + </resources>
210-234: Use WAR overlays instead of unpacking into target dir.Unpacking into ${project.build.directory}/${project.build.finalName} is fragile; the WAR plugin may overwrite it.
Apply:
- <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>unpack-steve-ui-jsp-webapp</id> - <goals> - <goal>unpack</goal> - </goals> - <phase>process-resources</phase> - <configuration> - <artifactItems> - <artifactItem> - <groupId>de.rwth.idsg</groupId> - <artifactId>steve-ui-jsp</artifactId> - <version>${project.version}</version> - <overWrite>true</overWrite> - <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> - <includes>WEB-INF/**,static/**</includes> - </artifactItem> - </artifactItems> - </configuration> - </execution> - </executions> - </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <overlays> + <overlay> + <groupId>de.rwth.idsg</groupId> + <artifactId>steve-ui-jsp</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin>
86-88: cxf-spring-boot-starter-jaxws looks good.Nice consolidation on the CXF Boot starter to simplify SOAP wiring.
29-34: Remove DB credentials from the POM.Storing db.user/password/url in the POM risks secret leakage (effective POM, artifact filtering).
Apply:
- <properties> - <db.user>steve</db.user> - <db.password>changeme</db.password> - <db.schema>stevedb</db.schema> - <db.jdbc.url>jdbc:mysql://localhost:3306/${db.schema}</db.jdbc.url> - </properties> + <!-- Database connection is configured via application.yml or environment variables. -->And define them in application.yml or via env (SPRING_DATASOURCE_URL/USERNAME/PASSWORD).
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java (1)
31-31: Constructor change reflected correctly.Passing (String) null aligns with the new constructor path; tests remain valid.
AGENTS.md (1)
71-72: Config location note is correct.Good call-out that defaults moved to application.yml.
steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java (2)
27-33: Test updates correctly reflect new API signature.Using a constant PATH_INFIX keeps tests readable and aligned with production.
64-97: Good coverage of edge cases.URLs with extra slashes, spaces, and percent signs are verified; behavior looks consistent.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (1)
147-153: Guard getSize() with the per-id lock.Reading ArrayDeque.size() without locking races with writers; can yield inconsistent results.
Apply:
- public int getSize(String chargeBoxId) { - var endpointDeque = lookupTable.get(chargeBoxId); - if (endpointDeque == null) { - return 0; - } - return endpointDeque.size(); - } + public int getSize(String chargeBoxId) { + var l = locks.get(chargeBoxId); + l.lock(); + try { + var endpointDeque = lookupTable.get(chargeBoxId); + return endpointDeque == null ? 0 : endpointDeque.size(); + } finally { + l.unlock(); + } + }pom.xml (2)
178-208: Ensure lombok.version is defined in the parent POM
The Lombok annotation‐processor dependency uses${lombok.version}; confirm your Spring Boot parent POM defines this property, or hardcode the version here.
5-10: Spring Boot parent 3.5.6 is valid and up-to-date; no changes required.steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java (1)
38-38: Improved per-element validation approach for list items.The migration from
@NotEmpty @ChargeBoxId private List<String>to@NotEmpty private List<@ChargeBoxId String>correctly leverages the TYPE_USE target added to the@ChargeBoxIdannotation. This approach provides cleaner, more idiomatic validation of individual list elements, replacing the need for a separate list validator class.steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (2)
91-93: LGTM! Configuration migration to SteveProperties is consistent.The refactoring from
SteveConfigurationtoStevePropertiesaligns with the broader Spring Boot migration pattern throughout the codebase.
107-107: Good use of type inference.Using
varfor the localobjectMappervariable improves readability while maintaining type safety through the compiler's inference.steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java (2)
28-28: Correct addition of TYPE_USE target for per-element validation.Adding
TYPE_USEto the@Targetannotation enables the@ChargeBoxIdannotation to be applied to type uses, which is essential for validating individual elements within collections (e.g.,List<@ChargeBoxId String>). This is a standard Jakarta Bean Validation pattern.Also applies to: 35-35
37-37: Simplified validator configuration is appropriate.Removing
ChargeBoxIdListValidatorfrom thevalidatedByattribute and keeping onlyChargeBoxIdValidatoris correct since the TYPE_USE target now enables per-element validation directly, eliminating the need for a separate list validator.steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (3)
41-48: Well-structured constructor injection for configuration.The constructor-based dependency injection pattern using
StevePropertiesis cleaner and more testable than field injection with@Value. The delegation pattern with the overloaded constructor accepting the regex string directly is good for both Spring DI and unit testing scenarios.
56-61: Correct null handling in validation context.The null check returning
trueis the standard Bean Validation pattern - null values should be validated by@NotNullconstraints, not by custom validators. This separation of concerns is correct.
63-74: No action needed: project targets Java ≥ 17
The pom.xml sets<java.version>21</java.version>and the README requires JDK 17 or newer, soString.strip()is fully supported.steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
59-59: Clean migration to SteveProperties.The parameter change from
SteveConfigurationtoStevePropertiesand the corresponding update to usesteveProperties.getSteveVersion()are consistent with the broader configuration refactoring pattern across the codebase.Also applies to: 76-76
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java (2)
45-49: Constructor migration to SteveProperties: LGTMClean switch from @value to typed properties; constructor wiring looks correct.
51-63: Proper lock release via try/finally: LGTMPrevents leaked locks under exceptions.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (2)
49-54: Wiring pathInfix via constructor: LGTMKeeps handler decoupled from static constants.
136-146: Harden getLastBitFromUrl: require segment match and URL-decodeCurrent indexOf may match partial infix (e.g., "ocpp" in "ocppx") and returns the entire tail; also misses URL-decoding.
- public static String getLastBitFromUrl(String pathInfix, String input) { - if (Strings.isNullOrEmpty(input)) { - return ""; - } - - var index = input.indexOf(pathInfix); - if (index == -1) { - return ""; - } - return input.substring(index + pathInfix.length()); - } + public static String getLastBitFromUrl(String pathInfix, String input) { + if (Strings.isNullOrEmpty(pathInfix) || Strings.isNullOrEmpty(input)) { + return ""; + } + var normInfix = "/" + pathInfix.replaceAll("^/+|/+$", "") + "/"; + var index = input.indexOf(normInfix); + if (index < 0) { + return ""; + } + var tail = input.substring(index + normInfix.length()); + var slash = tail.indexOf('/'); + var id = (slash >= 0) ? tail.substring(0, slash) : tail; + return java.net.URLDecoder.decode(id, java.nio.charset.StandardCharsets.UTF_8); + }steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
76-79: Switch to SteveProperties and PasswordEncoder: LGTMAligns with new config model and consistent encoder usage.
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
83-88: Compute jsonPath from properties: LGTMRemoves hard-coding and aligns with the new config.
Please confirm Helpers.getWsPath returns a trailing slash so OcppJsonChargePoint URL composition remains correct.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (1)
76-93: Null-safe SSL and optional trust store/keystore handlingCalling keyStoreFromFile unconditionally will NPE if keystore is unset; also omit trust store when configured.
- private static @Nullable TLSClientParameters create(@Nullable Ssl ssl) - throws GeneralSecurityException, IOException { - if (ssl == null || !ssl.isEnabled()) { - return null; - } - var tlsClientParams = new TLSClientParameters(); - - var socketFactory = SslContextBuilder.builder() - .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) - .usingTLS() - .usingDefaultAlgorithm() - .usingKeyManagerPasswordFromKeyStore() - .buildMergedWithSystem() - .getSocketFactory(); - tlsClientParams.setSSLSocketFactory(socketFactory); - - return tlsClientParams; - } + private static @Nullable TLSClientParameters create(@Nullable Ssl ssl) + throws GeneralSecurityException, IOException { + if (ssl == null || !ssl.isEnabled()) { + return null; + } + var builder = SslContextBuilder.builder(); + if (ssl.getKeyStore() != null) { + builder = builder + .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) + .usingKeyManagerPasswordFromKeyStore(); + } + if (ssl.getTrustStore() != null) { + builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword()); + } + var socketFactory = builder + .usingTLS() + .usingDefaultAlgorithm() + .buildMergedWithSystem() + .getSocketFactory(); + + var tlsClientParams = new TLSClientParameters(); + tlsClientParams.setSSLSocketFactory(socketFactory); + return tlsClientParams; + }steve/src/main/resources/application.yml (1)
7-9: Quote the @...@ tokens to keep the YAML valid.Also applies to: 38-38
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (3)
59-59: LGTM! Proper dependency injection.The migration from static PATH_INFIX to SteveProperties-driven configuration is well-implemented.
64-66: Dynamic path construction looks correct.The pathInfix computation properly combines websocket mapping with router endpoint path.
67-72: No changes needed: constructor signature already acceptspathInfix.steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (2)
49-53: Validate constructor input early.
62-69: Fragile mark/reset: available() can be 0 → reset() may fail and drop the request.steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (4)
51-68: Fix NPEs/IPv6 and wrong mapping in SOAP URI builder.
70-87: Same issues for WS URL (NPEs, IPv6, null host/port).
89-99: LGTM! Clean API refactor to use URI.The migration from String-based paths to URI-based endpoints is a good improvement for type safety.
101-108: Good refactoring with generic type safety.The createBean method properly uses generics and eliminates code duplication across OCPP versions.
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (5)
59-61: Simplified password encoder configuration.The direct BCryptPasswordEncoder instantiation removes external dependencies and is more straightforward.
64-66: Property-driven path configuration.The migration to SteveProperties for path derivation provides better centralized configuration management.
73-82: Enhanced security with dispatcher type matching.The addition of dispatcher type matchers (FORWARD, INCLUDE, ERROR) provides better security coverage for different request types.
147-148: Request cache disabled appropriately.Disabling request cache makes sense for the stateless nature of SOAP/API endpoints.
165-167: Clean API filter chain configuration.The property-driven API mapping configuration aligns well with the overall refactoring approach.
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
58-65: Proper Spring Boot test setup with DI.The migration to @SpringBootTest with proper dependency injection is a good improvement over static initialization.
70-75: Per-test lifecycle ensures isolation.The @BeforeEach/@AfterEach pattern with database preparation ensures proper test isolation.
84-84: URI-based client creation is type-safe.The switch from String paths to URI improves type safety and integrates well with the Helpers refactor.
Also applies to: 102-102, 123-123
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
64-65: Good migration to instance-based design.The switch from static to instance methods with proper dependency injection via @requiredargsconstructor improves testability and maintainability.
Also applies to: 72-72
99-109: Static accessors for test constants.Good design choice to keep identifier accessors as static while migrating operations to instance methods.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (4)
75-77: Proper Spring Boot test setup.The migration to @SpringBootTest with active profiles provides better integration testing capabilities.
84-94: Clean dependency injection setup.The @Autowired fields provide proper Spring context integration for the test.
96-101: Proper test setup with per-test lifecycle.The migration to per-test setup with proper database preparation ensures test isolation and matches the pattern used in other migrated tests.
259-264: **LinkedHashMap doesn't have lastEntry() method.**Based on the search results, LinkedHashMap gainedlastEntry()andfirstEntry()methods starting from Java 21. However, this codebase appears to be using an earlier Java version where these methods don't exist.LinkedHashMap doesn't have lastEntry() method in pre-Java 21.
The code uses
timeStatusMap.lastEntry().getKey()but LinkedHashMap doesn't provide any method to get first, last or any specific object in versions before Java 21. Use direct computation instead.- var faultedTime = timeStatusMap.lastEntry().getKey().plusMinutes(1); + var faultedTime = startingTime.plusMinutes(ChargePointStatus.values().length);steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (1)
85-86: Use the SOAP endpoint when creating the OCPP 1.6 client.
getForOcpp16instantiates a SOAP (HTTP/S) JAX-WS client; giving it the WebSocket URI returned bygetWsPathwill fail at runtime. Please switch back to the SOAP path helper so the test hits the correct transport.-import static de.rwth.idsg.steve.utils.Helpers.getWsPath; +import static de.rwth.idsg.steve.utils.Helpers.getSoapPath; ... - public void test() throws URISyntaxException { - var client = getForOcpp16(getWsPath(serverProperties, steveProperties)); + public void test() throws URISyntaxException { + var client = getForOcpp16(getSoapPath(serverProperties, steveProperties));
| public void updateApiPassword(WebUserForm form) { | ||
| String newPassword = null; | ||
| if (form.getApiPassword() != null && !form.getApiPassword().isEmpty()) { | ||
| newPassword = encoder.encode(form.getApiPassword()); | ||
| newPassword = passwordEncoder.encode(form.getApiPassword()); | ||
| } | ||
| webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword); | ||
| } |
There was a problem hiding this comment.
Invalidate API user cache after apiPassword change
Otherwise loadUserByUsernameForApi can serve stale credentials for up to 10 minutes.
public void updateApiPassword(WebUserForm form) {
String newPassword = null;
if (form.getApiPassword() != null && !form.getApiPassword().isEmpty()) {
newPassword = passwordEncoder.encode(form.getApiPassword());
}
webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword);
+ userCache.invalidate(form.getWebUsername());
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public void updateApiPassword(WebUserForm form) { | |
| String newPassword = null; | |
| if (form.getApiPassword() != null && !form.getApiPassword().isEmpty()) { | |
| newPassword = encoder.encode(form.getApiPassword()); | |
| newPassword = passwordEncoder.encode(form.getApiPassword()); | |
| } | |
| webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword); | |
| } | |
| public void updateApiPassword(WebUserForm form) { | |
| String newPassword = null; | |
| if (form.getApiPassword() != null && !form.getApiPassword().isEmpty()) { | |
| newPassword = passwordEncoder.encode(form.getApiPassword()); | |
| } | |
| webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword); | |
| userCache.invalidate(form.getWebUsername()); | |
| } |
🤖 Prompt for AI Agents
In steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java
around lines 227-233, after updating the API password you must evict the cached
API user so loadUserByUsernameForApi doesn't return stale credentials; add a
cache eviction call immediately after
webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword) (e.g.
cacheManager.getCache("<api-user-cache-name>").evict(form.getUsername()) or
userCache.evict(form.getWebUserPk()/form.getUsername()) depending on how the
cache is keyed and named), ensuring you use the same cache name and key strategy
as loadUserByUsernameForApi and always evict even when newPassword is null.
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java
Show resolved
Hide resolved
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java
Show resolved
Hide resolved
| public Endpoint routerEndpoint( | ||
| SteveProperties steveProperties, | ||
| Bus bus, | ||
| // TODO find another dummy service to remove to the strong dependency to ocpp12 | ||
| ocpp.cs._2010._08.CentralSystemService ocpp12Server, | ||
| List<EndpointImpl> ocppEndpoints) { | ||
| var mediator = new MediatorInInterceptor(ocppEndpoints); | ||
| return createEndpoint( | ||
| bus, | ||
| ocpp12Server, | ||
| steveProperties.getPaths().getRouterEndpointPath(), | ||
| List.of(mediator), | ||
| Collections.emptyList()); |
There was a problem hiding this comment.
Router interceptor sees only immutable endpoints list
MediatorInInterceptor calls setEndpoints and mutates the collection it receives. Passing Spring’s injection result wrapped in List.of(...) means it gets an immutable list and will throw UnsupportedOperationException once the router comes up. Provide a mutable collection instead.
- return createEndpoint(
- bus,
- ocpp12Server,
- steveProperties.getPaths().getRouterEndpointPath(),
- List.of(mediator),
- Collections.emptyList());
+ return createEndpoint(
+ bus,
+ ocpp12Server,
+ steveProperties.getPaths().getRouterEndpointPath(),
+ new ArrayList<>(List.of(mediator)),
+ Collections.emptyList());🤖 Prompt for AI Agents
In
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java
around lines 106 to 118, the code passes an immutable list (List.of(mediator))
into MediatorInInterceptor which later mutates the collection and will throw
UnsupportedOperationException; change the immutable list to a mutable collection
(e.g., wrap the single-element list in a new ArrayList) so the interceptor can
modify it without error.
9f00bfd to
5da65ca
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
steve-jooq/pom.xml (1)
110-116: Unresolved Maven properties break the build.
${flyway.version}, ${jooq.version}, ${mysql.version} (and ${testcontainers.version} used later) are not defined in parent or this module.Apply one of:
- Option A (module‑local properties):
<properties> <db.schema>stevedb</db.schema> <db.schema-source>stevedb</db.schema-source> <db.user>steve</db.user> <db.password>changeme</db.password> <!-- In Mysql: schema == database (http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_schema) --> <db.jdbc.url>jdbc:mysql://localhost:3306/${db.schema}?sslMode=PREFERRED&serverTimezone=UTC</db.jdbc.url> + <!-- Versions for plugins/deps not managed by Spring Boot --> + <mysql.version>8.4.0</mysql.version> + <jooq.version>3.20.6</jooq.version> + <flyway.version>10.21.0</flyway.version> + <testcontainers.version>1.20.4</testcontainers.version> </properties>
- Option B (preferred globally): define these in the parent pom.xml so all modules share them. See separate comment in parent POM. Based on learnings
Also applies to: 182-193, 230-231
pom.xml (2)
178-208: maven-compiler: unresolved ${lombok.version}.
Without the property, the build will fail to resolve the annotationProcessorPaths entry.Once the property is added (see above), this section is OK.
37-48: Add missing lombok.version property
pom.xml (): add<lombok.version>1.18.34</lombok.version>so the maven-compiler-plugin’s annotationProcessorPaths can resolve
${lombok.version}.steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
583-599: Test bug: asserting empty list but indexing into itsampledValues is asserted empty, then sampledValues.get(0) is used. This will either fail the assertion or throw IndexOutOfBoundsException. Assert non-empty instead.
Apply this diff:
- assertThat(sampledValues).isEmpty(); + assertThat(sampledValues).isNotEmpty();
🧹 Nitpick comments (16)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
55-76: Guard against nullclienton first use.
ThreadLocal.get()returnsnulluntilset()runs on the current thread. There is a short window betweenclient.set(getForOcpp16(soapPath))and the firstclient.get()call in the same method where the current thread hasn’t been populated yet, so the dereference atclient.get().bootNotification(...)risks an NPE. Capture the return value ofgetForOcpp16()into a local variable and reuse it for the rest of the method, or switch to aThreadLocal.withInitial(...)to guarantee a non-null client before dereferencing.steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (1)
46-48: Limit visibility of test-only constructorIf the String constructor is used only in tests, reduce visibility (package-private) or annotate with a testing hint to avoid exposing it as part of the public API.
Apply this diff:
- public ChargeBoxIdValidator(String regexFromConfig) { + ChargeBoxIdValidator(String regexFromConfig) { // intended for tests and non-Spring usage this.pattern = Pattern.compile(Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig); }steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (3)
91-91: Prefer JDK List.of over Guava Lists.newArrayListReduces dependency surface in tests.
Apply:
- sendBoot(client, Lists.newArrayList(chargeBox1, chargeBox2)); + sendBoot(client, List.of(chargeBox1, chargeBox2));And remove the Guava import:
-import com.google.common.collect.Lists;
104-122: Helper method consistency (static vs instance)sendBoot and sendAuth are static, sendStartTx is instance. For consistency, consider making all three instance methods (or all static if they don’t need state).
84-85: Name the test for intent clarityRename test() to a descriptive name (e.g., concurrentTx_isRejectedOnSecondChargeBox) or add @DisplayName for readability.
.github/workflows/main.yml (1)
60-63: Replace fixed sleep with a readiness probe.Avoid flakiness by waiting until HTTP is up instead of sleeping 30s.
- sleep 30 - elinks -dump -no-references http://localhost:8080/manager/signin + for i in {1..60}; do + if elinks -dump -no-references http://localhost:8080/manager/signin >/dev/null 2>&1; then + break + fi + sleep 1 + done + elinks -dump -no-references http://localhost:8080/manager/signinsteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (1)
47-51: Constructor throws checked exceptions; confirm bean init behavior.Throwing from a @component constructor will fail context refresh if SSL config is bad. If that’s desired, OK; otherwise, catch and log, or initialize lazily on first client creation.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
64-66: Normalize composed path to avoid missing/double slashes.
If properties contain/omit leading or trailing “/”, pathInfix may be malformed. Consider normalizing segments or assert formats in SteveProperties.Example normalization:
- var pathInfix = steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/"; + var paths = steveProperties.getPaths(); + var ws = paths.getWebsocketMapping(); + ws = (ws.startsWith("/")) ? ws : "/" + ws; + ws = ws.endsWith("/") ? ws.substring(0, ws.length() - 1) : ws; + var router = paths.getRouterEndpointPath(); + router = router.startsWith("/") ? router.substring(1) : router; + var pathInfix = ws + "/" + router + "/";steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
101-108: Avoid unchecked cast in createBean.
Use Class.cast to remove the need for @SuppressWarnings.Apply:
- private static <T> T createBean(URI endpointAddress, Class<T> serviceClass) { + private static <T> T createBean(URI endpointAddress, Class<T> serviceClass) { var f = new JaxWsProxyFactoryBean(); f.setBindingId(SOAPBinding.SOAP12HTTP_BINDING); f.getFeatures().add(new WSAddressingFeature()); f.setAddress(endpointAddress.toString()); f.setServiceClass(serviceClass); - return (T) f.create(); + return serviceClass.cast(f.create()); }steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (1)
91-97: Startup-time internet probe permanently fixes strategyIf the network is transiently down at startup, you’ll lock in DummyReleaseCheckService for the whole run. Consider property-gating (e.g., steve.release-check.enabled) or scheduling a retry to flip to GithubReleaseCheckService when connectivity returns.
Do you want a small ConditionalOnProperty-based refactor?
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
59-59: Switch static System properties to application.ymlPrefer configuration over code so values are visible/overridable and initialized early by Boot.
Add to application.yml:
springdoc: api-docs: path: /manager/v3/api-docs swagger-ui: path: /manager/swagger-ui/index.html tags-sorter: alpha operations-sorter: alpha writer-with-order-by-keys: true paths-to-match: /api/**Then remove the static block.
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
93-100: Header-value driven apiPassword only set on first bootafterStart exits early if any ADMIN exists, so changing steve.web-api.header-value won’t update stored apiPassword for existing admins.
Consider: if header-value is present, reconcile and update apiPassword for the configured admin on startup.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (1)
166-168: Return a safe snapshot rather than exposing live, mutable DequescopyOf(lookupTable) still exposes internal Deques to callers. Return copies per entry to prevent external mutation and reduce race windows.
Apply:
- return ImmutableMap.copyOf(lookupTable); + var copy = new java.util.HashMap<String, Deque<SessionContext>>(lookupTable.size()); + lookupTable.forEach((k, v) -> copy.put(k, new ArrayDeque<>(v))); + return copy;steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
90-93: Make tearDown null-safeGuard cleanup to avoid NPE if setUp fails.
Apply:
- public void tearDown() { - databasePreparer.cleanUp(); + public void tearDown() { + if (databasePreparer != null) { + databasePreparer.cleanUp(); + } }steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (1)
59-61: Prefer DelegatingPasswordEncoder for compatibilityKeeps support for prefixed hashes and eases migrations.
Apply:
- public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + }steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (1)
67-71: Consider deriving schema dynamically instead of a hard-coded constantUsing a fixed SCHEMA_TO_TRUNCATE risks failures when test DB/schema changes. Prefer reading the active schema from the configured connection (e.g., ctx.meta() or SELECT DATABASE()).
I can draft a helper that resolves the current schema and filters tables accordingly if you want.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
-
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttf -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.ico -
steve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.png -
steve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svg -
steve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.js -
steve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (69)
-
.github/workflows/main.yml(1 hunks) -
AGENTS.md(2 hunks) -
Dockerfile(1 hunks) -
pom.xml(5 hunks) -
steve-api/pom.xml(2 hunks) -
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks) -
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks) -
steve-core/pom.xml(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(6 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java(1 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(0 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks) -
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks) -
steve-jooq/pom.xml(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(4 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java(1 hunks) -
steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks) -
steve-ocpp-1-x/pom.xml(2 hunks) -
steve-ocpp-transport-soap/pom.xml(1 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks) -
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks) -
steve-ocpp-transport-websocket/pom.xml(0 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(1 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks) -
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(7 hunks) -
steve-ocpp/pom.xml(0 hunks) -
steve-ui-jsp/pom.xml(2 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks) -
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks) -
steve/README.md(2 hunks) -
steve/pom.xml(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks) -
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks) -
steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks) -
steve/src/main/resources/application-dev.yml(1 hunks) -
steve/src/main/resources/application.yml(1 hunks) -
steve/src/main/resources/config/logback.xml(0 hunks) -
steve/src/main/resources/config/main.properties(0 hunks) -
steve/src/main/resources/logback-spring.xml(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(8 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(6 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(4 hunks) -
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(3 hunks) -
steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(2 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java(1 hunks) -
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(5 hunks) -
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks) -
steve/src/test/resources/application-test.yml(1 hunks)
💤 Files with no reviewable changes (13)
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve/src/main/resources/config/main.properties
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve/src/main/java/de/rwth/idsg/steve/Application.java
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
- steve/src/main/resources/config/logback.xml
- steve-ocpp/pom.xml
- steve-ocpp-transport-websocket/pom.xml
🚧 Files skipped from review as they are similar to previous changes (18)
- steve/src/main/resources/application-dev.yml
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
- steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
- steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java
- steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
- steve/src/test/resources/application-test.yml
- steve/src/main/resources/logback-spring.xml
- steve/README.md
- Dockerfile
- steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java
- steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java
- steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java
- steve-core/pom.xml
- steve-ocpp-transport-soap/pom.xml
- steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java
- steve-ui-jsp/pom.xml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.javasteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.javasteve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue81.javasteve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.javasteve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.javasteve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javapom.xmlsteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/main/resources/application.ymlsteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javaAGENTS.mdsteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve/pom.xml
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/StressTest.java.github/workflows/main.ymlpom.xmlsteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javaAGENTS.mdsteve-jooq/pom.xmlsteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/pom.xml
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javaAGENTS.mdsteve-jooq/pom.xml
🧬 Code graph analysis (5)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java (1)
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/UpdateTransactionParams.java (1)
Getter(30-45)
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (1)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java (1)
RequiredArgsConstructor(56-213)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
Slf4j(39-74)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(61-237)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
🪛 GitHub Actions: analyze and review code
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java
[error] 27-27: Spotless formatting violation. The field 'name' has incorrect spacing. Run 'mvn spotless:apply' to fix code style issues in this file.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: Analyze (java-kotlin)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-24.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-24.04, 21, mariadb:10.3)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.5.21)
- GitHub Check: build (ubuntu-24.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.6.14)
- GitHub Check: build (ubuntu-22.04, 24, mariadb:10.3)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.4.30)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.5.21)
- GitHub Check: build (ubuntu-22.04, 24, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 21, mysql:8.0)
- GitHub Check: build (ubuntu-22.04, 21, mariadb:10.3)
🔇 Additional comments (61)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (1)
97-100: Restore String-based URI forRequestBuilder.post.
RequestBuilder.post(...)in HttpClient 4.5.x only accepts aString; passing theURIdirectly breaks compilation. ConvertsoapPathto a string before callingpost(...).Apply this diff to fix the issue:
- var req = RequestBuilder.post(soapPath) + var req = RequestBuilder.post(soapPath.toString()) .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") .setEntity(new StringEntity(body, contentType)) .build();steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java (1)
42-53: Constructor migration toStevePropertieslooks solidThe switch to constructor-injected
StevePropertiesties this DTO directly to the new YAML-bound configuration while preserving the expected endpoint concatenations. Nicely aligned with the broader configuration refactor.steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (3)
48-50: Simplified CLI bootstrap worksThe direct no-arg construction ties in with the new Spring-managed defaults and keeps the stress harness runnable from the command line. Looks good.
61-64: UsingsoapPathaligns with the shared stress-test basePulling the SOAP endpoint from the superclass instead of the old per-instance path keeps this test in sync with the rest of the backport.
79-82: Consistent client creationRepeating the same
soapPath-backed client setup intoRepeat()keeps behavior aligned with the initialization logic above. All good.steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
69-84: InitializechargePointsServicebefore building the controllerNullAway still sees
chargePointsServiceas potentially null when the controller is constructed, matching the earlier warning. Please bind the Mockito mock insidesetUpso the field is definitely assigned.- @Mock - private ChargePointsService chargePointsService; + private ChargePointsService chargePointsService; @@ - @BeforeEach - public void setUp() { + @BeforeEach + public void setUp(@Mock ChargePointsService mockService) { + this.chargePointsService = mockService; var ctx = new AnnotationConfigApplicationContext();steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
90-97: Clean the database before closing the Spring context
__DatabasePreparer__relies on theDSLContextfrom the still-open application context. Callingapp.close()first shuts down theDataSource, sodatabasePreparer.cleanUp()will fail with “pool is closed”, leaving the database dirty and breaking subsequent tests. Run the cleanup while the context is alive, then close the context.- try { - if (app != null) { - app.close(); - } - } finally { - if (databasePreparer != null) { - databasePreparer.cleanUp(); - } - } + try { + if (databasePreparer != null) { + databasePreparer.cleanUp(); + } + } finally { + if (app != null) { + app.close(); + } + }steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java (2)
63-65: ConfirmjsonPathinitialization for standalone runsLine 64 now relies on the no-arg constructor to populate
jsonPathimplicitly beforeattack()executes. Please double-check thatStressTestinitializesjsonPathwhen thismainis launched outside any Spring context; otherwise a null path will break the websocket bootstrap.
80-81: jsonPath switch matches new property bindingLine 81 correctly pulls the websocket path from the shared
jsonPathproperty, keeping this stress helper aligned with the new configuration flow.steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (4)
41-44: Constructor injection via SteveProperties looks goodUsing constructor injection for the regex source centralizes config and ensures immutability of the compiled Pattern.
56-61: Nulls are treated as valid — ensure callers add @NotNull when neededBy design, null returns true here. For collection element validation (e.g., List<@ChargeBoxId String>), add @NotNull at the use site to reject null items. See ChargePointBatchInsertForm.
68-74: LGTM: strict whitespace and regex matchingstrip() equality check correctly rejects leading/trailing whitespace; full-string regex match via Pattern#matches is appropriate.
41-48: No changes required: Spring Boot auto-configures a SpringConstraintValidatorFactory-backed validatorspring-boot-starter-validation pulls in ValidationAutoConfiguration, which registers a LocalValidatorFactoryBean using SpringConstraintValidatorFactory, so your @Autowired constructor will be invoked.
steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (3)
84-87: Fixed: correct SOAP endpoint and checked exception handlingSwitch to getSoapPath and declaring throws URISyntaxException resolves the earlier mismatch and unchecked exception. LGTM.
56-58: Spring Boot test wiring looks correctActive test profile, SpringBootTest(DEFINED_PORT), and autowired ServerProperties/SteveProperties/DSLContext are appropriate for this integration test and align with the helpers.
If your CI shares the test host, confirm DEFINED_PORT won’t collide with parallel builds. If collisions occur, consider RANDOM_PORT plus @LocalServerPort and a helper variant that accepts an explicit port.
Also applies to: 62-70, 22-22, 46-46
60-60: Ignore static-init concern for REGISTERED_OCPP_TAGgetRegisteredOcppTag() simply returns a static final literal (“id_tag_…”); it does not touch the DB or DSLContext, so class-load initialization can’t flake.
Likely an incorrect or invalid review comment.
.github/workflows/main.yml (1)
49-55: Maven flags now well-formed; confirm profile exists.Command structure looks correct. Ensure -PuseRealDatabase is defined in a POM (root or module), else drop it.
#!/bin/bash # Verify the profile definition rg -nP '(?s)<profile>\s*<id>\s*useRealDatabase\s*</id>' -C2 --glob '!**/target/**' -g 'pom.xml'AGENTS.md (3)
100-100: Fix typo: it’s a WAR, not a JAR.Use “WAR file” in the sentence to match the .war artifact.
-A runnable JAR file will be created at `steve/target/steve.war`. +A runnable WAR file will be created at `steve/target/steve.war`.
71-71: Config path update verified; no remaining main.properties references.
106-112: Executable WAR confirmed: POM uses<packaging>war</packaging>and includesspring-boot-maven-pluginwith no provided‐scope Tomcat—java -jar steve/target/steve.war is valid as shown.steve-ocpp-1-x/pom.xml (2)
53-59: jaxb-plugins.version property is already defined in the parent POM
The root pom.xml declares<jaxb-plugins.version>4.0.11</jaxb-plugins.version>.
24-24: Confirmed cxf.version is defined and consistent
Property<cxf.version>is declared in the parent POM as 4.1.3 and used for both the CXF BOM import and the cxf-codegen-plugin; no changes required.steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
57-73: Validate and normalize URL mapping patterns. Confirm thatpaths.rootMappingis configured with the proper trailing slash or wildcard for bothDispatcherServlet.addMapping()and the security filter’saddMappingForUrlPatterns()—don’t naively append"*"(e.g. avoid"/app*").steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java (1)
76-93: Avoid NPE and support optional trust/keystores in TLS setup.create(...) unconditionally accesses ssl.getKeyStore(); this can NPE if SSL is enabled without a keystore. Make keystore/truststore optional.
- var socketFactory = SslContextBuilder.builder() - .keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) - .usingTLS() - .usingDefaultAlgorithm() - .usingKeyManagerPasswordFromKeyStore() - .buildMergedWithSystem() - .getSocketFactory(); + var builder = SslContextBuilder.builder(); + if (ssl.getKeyStore() != null) { + builder = builder.keyStoreFromFile(ssl.getKeyStore(), ssl.getKeyStorePassword()) + .usingKeyManagerPasswordFromKeyStore(); + } + if (ssl.getTrustStore() != null) { + builder = builder.trustStoreFromFile(ssl.getTrustStore(), ssl.getTrustStorePassword()); + } + var socketFactory = builder + .usingTLS() + .usingDefaultAlgorithm() + .buildMergedWithSystem() + .getSocketFactory();steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (2)
59-59: DI via SteveProperties looks good.
Field injection of SteveProperties aligns with the migration to property‑driven config.
85-85: Jetty upgrade strategy with var is fine.
No functional issues; concise and clear.steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (3)
44-44: Minor: var usage in tests is fine.
No concerns.
51-68: Harden SOAP URI builder (NPEs, null port/context, IPv6).
getSsl(), getAddress(), getPort(), getServlet()/getContextPath() may be null; IPv6 needs brackets. This can NPE or build invalid URIs.Apply:
- public static URI getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) - throws URISyntaxException { - var scheme = "http"; - if (serverProperties.getSsl().isEnabled()) { - scheme = "https"; - } - - return new URI( - scheme, - null, - serverProperties.getAddress().getHostName(), - serverProperties.getPort(), - serverProperties.getServlet().getContextPath() - + steveProperties.getPaths().getSoapMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/", - null, - null); - } + public static URI getSoapPath(ServerProperties serverProperties, SteveProperties steveProperties) + throws URISyntaxException { + var ssl = serverProperties.getSsl(); + var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "https" : "http"; + + var addr = serverProperties.getAddress(); + var host = (addr != null) ? addr.getHostAddress() : "localhost"; + if (host.indexOf(':') >= 0 && !host.startsWith("[")) { + host = "[" + host + "]"; // IPv6 + } + + Integer port = serverProperties.getPort(); + int p = (port != null) ? port : 8080; + + var servlet = serverProperties.getServlet(); + var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : ""; + + var soap = steveProperties.getPaths().getSoapMapping(); + soap = (soap != null) ? soap : ""; + var router = steveProperties.getPaths().getRouterEndpointPath(); + router = (router != null) ? router : ""; + + String path = ctx + soap + router + "/"; + return new URI(scheme, null, host, p, path, null, null); + }
70-87: Harden WS URI builder (same issues as SOAP).
Mirror the null/IPv6 handling; choose wss when SSL enabled.Apply:
- public static URI getWsPath(ServerProperties serverProperties, SteveProperties steveProperties) - throws URISyntaxException { - var scheme = "ws"; - if (serverProperties.getSsl().isEnabled()) { - scheme = "wss"; - } - - return new URI( - scheme, - null, - serverProperties.getAddress().getHostName(), - serverProperties.getPort(), - serverProperties.getServlet().getContextPath() - + steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/", - null, - null); - } + public static URI getWsPath(ServerProperties serverProperties, SteveProperties steveProperties) + throws URISyntaxException { + var ssl = serverProperties.getSsl(); + var scheme = (ssl != null && Boolean.TRUE.equals(ssl.isEnabled())) ? "wss" : "ws"; + + var addr = serverProperties.getAddress(); + var host = (addr != null) ? addr.getHostAddress() : "localhost"; + if (host.indexOf(':') >= 0 && !host.startsWith("[")) { + host = "[" + host + "]"; // IPv6 + } + + Integer port = serverProperties.getPort(); + int p = (port != null) ? port : 8080; + + var servlet = serverProperties.getServlet(); + var ctx = (servlet != null && servlet.getContextPath() != null) ? servlet.getContextPath() : ""; + + var ws = steveProperties.getPaths().getWebsocketMapping(); + ws = (ws != null) ? ws : ""; + var router = steveProperties.getPaths().getRouterEndpointPath(); + router = (router != null) ? router : ""; + + String path = ctx + ws + router + "/"; + return new URI(scheme, null, host, p, path, null, null); + }steve-jooq/pom.xml (1)
27-29: Adopting spring-boot-starter-jooq is good.
This aligns with the Spring Boot migration and reduces explicit versioning.pom.xml (1)
5-10: Switching to Spring Boot parent is fine.
Centralizes dependency management and aligns with the Boot migration.steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (1)
490-491: Null‑tolerant enum mapping looks good.
lookupLiteral(actor.getName()) avoids IllegalArgumentException from valueOf and keeps null propagation. Confirm TRANSACTION_STOP.EVENT_ACTOR allows NULL; otherwise consider a fallback (e.g., manual) to avoid insert failures.steve-api/pom.xml (2)
34-36: LGTM: Consolidated tests via spring-boot-starter-testStarter brings JUnit Jupiter, Mockito, AssertJ, JSONPath, etc. Good simplification.
23-26: Manage servlet API version
The jakarta.servlet-api dependency in steve-api/pom.xml (lines 23–26) has no<version>and isn’t covered by your parent’s dependencyManagement. Confirm an imported BOM (e.g., cxf-bom) provides this version; otherwise add an explicit<version>or include it under<dependencyManagement>.steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (1)
107-113: LGTM: Jackson customizations on existing MVC converterModule discovery, ignoring unknown props, and ISO dates are correctly set on Spring’s ObjectMapper.
steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (1)
39-63: LGTM: Boot entrypoint, CXF SLF4J wiring, UTC default TZGood centralization; ApplicationStartedEvent log is harmless and informative.
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
76-76: LGTM: Version sourced from StevePropertiesOpenAPI.version reflects steve.version property.
steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java (1)
45-49: LGTM: Property-driven auto-register flagConstructor now reads from SteveProperties; behavior unchanged besides wiring.
steve/src/main/resources/application.yml (1)
7-9: Quote @...@ tokens to keep YAML validUnquoted @...@ is invalid YAML. Quote them so IDEs/parsers don’t choke before Maven filtering.
Apply:
- url: @db.jdbc.url@ - username: @db.user@ - password: @db.password@ + url: "@db.jdbc.url@" + username: "@db.user@" + password: "@db.password@" ... - version: @project.version@ + version: "@project.version@"Also applies to: 38-38
steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java (1)
227-233: Invalidate API-user cache after apiPassword changeWithout eviction, loadUserByUsernameForApi may serve stale credentials for up to 10 minutes.
Apply:
public void updateApiPassword(WebUserForm form) { String newPassword = null; if (form.getApiPassword() != null && !form.getApiPassword().isEmpty()) { newPassword = passwordEncoder.encode(form.getApiPassword()); } webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword); + userCache.invalidate(form.getWebUsername()); }steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (2)
61-64: Constructor migration to SteveProperties looks goodProperty-based strategy wiring is clear and keeps config centralized.
148-153: Guard Deque read with the per-id lock to avoid racesAccessing ArrayDeque without the same lock used for add/remove is unsafe; size() can be inconsistent.
Apply:
- var endpointDeque = lookupTable.get(chargeBoxId); - if (endpointDeque == null) { - return 0; - } - return endpointDeque.size(); + var l = locks.get(chargeBoxId); + l.lock(); + try { + var endpointDeque = lookupTable.get(chargeBoxId); + return endpointDeque == null ? 0 : endpointDeque.size(); + } finally { + l.unlock(); + }steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
71-88: WS path initialization via properties: goodAutowired ServerProperties/SteveProperties and getWsPath usage correctly derive jsonPath.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (2)
49-53: Validate constructor inputNull endpoints will NPE later; fail fast with a clear message.
Apply:
public MediatorInInterceptor(List<EndpointImpl> endpoints) { super(Phase.POST_STREAM); super.addBefore(StaxInInterceptor.class.getName()); - actualServers = initServerLookupMap(endpoints); + java.util.Objects.requireNonNull(endpoints, "endpoints must not be null"); + actualServers = initServerLookupMap(endpoints); }
61-69: Fix fragile mark/reset and handle missing InputStreamavailable() can be 0 causing reset() failure; also handle null stream.
Apply:
- var is = message.getContent(InputStream.class); - var bis = new BufferedInputStream(is); - bis.mark(bis.available()); + var is = message.getContent(InputStream.class); + if (is == null) { + log.warn("No InputStream on message; cannot route"); + return; + } + var bis = new BufferedInputStream(is); + // mark enough bytes to parse envelope and first body element + bis.mark(64 * 1024); message.setContent(InputStream.class, bis); - var encoding = (String) message.get(Message.ENCODING); - var reader = StaxUtils.createXMLStreamReader(bis, encoding); - var xmlReader = new DepthXMLStreamReader(reader); + var encoding = (String) message.get(Message.ENCODING); + var reader = StaxUtils.createXMLStreamReader(bis, encoding); + var xmlReader = new DepthXMLStreamReader(reader);steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
99-109: Guard render mapping when generator schema is absentWithout checking schemaSource, jOOQ may map the empty/default schema and rewrite all unqualified references.
Apply:
- var dbSchema = steveProperties.getJooq().getSchema(); - if (dbSchema != null && !dbSchema.equals(steveProperties.getJooq().getSchemaSource())) { + var dbSchema = steveProperties.getJooq().getSchema(); + var schemaSource = steveProperties.getJooq().getSchemaSource(); + if (dbSchema != null && schemaSource != null && !dbSchema.equals(schemaSource)) { // This is needed if the schema in the database is different from the one // that was used to generate the jOOQ classes. settings = settings.withRenderMapping(new RenderMapping() .withSchemata(new MappedSchema() - .withInput(steveProperties.getJooq().getSchemaSource()) - .withOutput(steveProperties.getJooq().getSchema()))); + .withInput(schemaSource) + .withOutput(dbSchema))); }steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (1)
164-172: BasicAuthenticationFilter usage OK ApiAuthenticationManager implements both AuthenticationManager and AuthenticationEntryPoint.steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (4)
51-53: Spring Boot test wiring looks goodTest profile + DEFINED_PORT context are correctly applied for CXF endpoints.
58-69: Per-test DI and path derivation LGTMAutowired ServerProperties/SteveProperties/DSLContext with per-test soapPath and DB setup is correct and consistent with Helpers.getSoapPath usage.
Small gotcha: Helpers.getSoapPath calls serverProperties.getAddress().getHostName(). If server.address isn’t configured, getAddress() can be null. Please confirm application-test.yml sets server.address (e.g., 127.0.0.1) or adjust Helpers to default to localhost when null. Based on learnings
70-80: Good test lifecyclePer-test prepare/cleanUp ensures isolation for DB-backed integration tests.
84-98: SOAP client usage via router path is correctCreating the client with soapPath and asserting expected statuses is aligned with the new mediator/router setup.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (2)
84-106: Per-test DB setup and router-based client LGTMAssertions around auto-register disabled and unregistered CP behavior look correct with the interceptor logic.
259-265: Fix: LinkedHashMap has no lastEntry(); won’t compileReplace lastEntry() usage with a computed timestamp from startingTime.
Apply this diff:
- var faultedTime = timeStatusMap.lastEntry().getKey().plusMinutes(1); + var faultedTime = startingTime.plusMinutes(ChargePointStatus.values().length);Also applies to: 299-307
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
64-73: Instance-based preparer is a solid improvementSwitching to DI/DSLContext instance methods removes static coupling and fits per-test lifecycle well.
111-119: Repository accessors read neatlyDirect repository calls via injected DSLContext are appropriate for tests.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (2)
106-119: Router endpoint bean wiring looks consistentMediator receives ocppEndpoints; interceptors registration is fine. Address uses steveProperties paths as expected.
Please confirm MediatorInInterceptor does not rely on mutating the passed ocppEndpoints List; if it does, ensure Spring injects a mutable List (it typically does). If mutation is needed, wrap with new ArrayList<>(ocppEndpoints). Based on learnings
136-147: Publishing endpoints explicitly is acceptable hereSince you construct EndpointImpl directly, calling publish(address) is appropriate (as opposed to using EndpointImplFactoryBean which auto-publishes). No change needed.
steve/pom.xml (4)
186-201: Reassess filtering of application.ymlFiltering true can substitute ${...} tokens intended for Spring at runtime (e.g., keystore paths/passwords), risking secrets baked into artifacts or broken placeholders.
Please confirm application.yml contains only Maven tokens you intend to resolve at build time. If not, disable filtering for it or switch Maven filtering delimiters. I can script-check placeholders across application.yml variants if helpful.
90-98: Verify version management for httpclient5httpclient5 has no explicit version here—ensure steve-parent’s dependencyManagement (or an imported BOM) pins it to a safe version.
Run a quick check in the parent POM and dependencyManagement before merging.
29-34: Avoid DB creds in POM; move to env/application.ymlKeeping db.user/password/url in the POM risks leakage via effective POM and build logs, and can be baked into filtered resources. Move them to application.yml or environment variables and reference via Spring placeholders.
I can propose an application.yml snippet using ${DB_URL}/${DB_USER}/${DB_PASSWORD} env vars and remove these POM properties if you’d like.
210-233: Replace unpack-with-dependency-plugin with WAR overlay for steve-ui-jspUnpacking into ${project.build.directory}/${project.build.finalName} is fragile and can be overwritten by the WAR build. Use a WAR overlay.
Apply this diff to remove the unpack execution and configure maven-war-plugin overlays:
- <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>unpack-steve-ui-jsp-webapp</id> - <goals> - <goal>unpack</goal> - </goals> - <phase>process-resources</phase> - <configuration> - <artifactItems> - <artifactItem> - <groupId>de.rwth.idsg</groupId> - <artifactId>steve-ui-jsp</artifactId> - <version>${project.version}</version> - <overWrite>true</overWrite> - <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> - <includes>WEB-INF/**,static/**</includes> - </artifactItem> - </artifactItems> - </configuration> - </execution> - </executions> - </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <overlays> + <overlay> + <groupId>de.rwth.idsg</groupId> + <artifactId>steve-ui-jsp</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin>
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java
Outdated
Show resolved
Hide resolved
steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java
Outdated
Show resolved
Hide resolved
| registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") | ||
| .setHandshakeHandler(handshakeHandler) | ||
| .setAllowedOrigins("*"); |
There was a problem hiding this comment.
Do not allow all origins ("*") for WebSocket.
Open CORS weakens security. Prefer configured origin patterns (env/property) and avoid "*" in production.
Example:
- registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*")
- .setHandshakeHandler(handshakeHandler)
- .setAllowedOrigins("*");
+ var handlerRegistration = registry
+ .addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*")
+ .setHandshakeHandler(handshakeHandler);
+ // Configure via properties; fallback to localhost in dev
+ var origins = steveProperties.getSecurity() != null
+ ? steveProperties.getSecurity().getAllowedOriginPatterns()
+ : java.util.List.of("http://localhost:*", "https://localhost:*");
+ handlerRegistration.setAllowedOriginPatterns(origins.toArray(String[]::new));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") | |
| .setHandshakeHandler(handshakeHandler) | |
| .setAllowedOrigins("*"); | |
| var handlerRegistration = registry | |
| .addHandler(handshakeHandler.getDummyWebSocketHandler(), pathInfix + "*") | |
| .setHandshakeHandler(handshakeHandler); | |
| // Configure via properties; fallback to localhost in dev | |
| var origins = steveProperties.getSecurity() != null | |
| ? steveProperties.getSecurity().getAllowedOriginPatterns() | |
| : java.util.List.of("http://localhost:*", "https://localhost:*"); | |
| handlerRegistration.setAllowedOriginPatterns(origins.toArray(String[]::new)); |
🤖 Prompt for AI Agents
In
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java
around lines 74-76 the WebSocket CORS is set to allow all origins ("*"); replace
this with a configurable allowed-origins value sourced from application
properties or env (e.g. a comma-separated list or pattern), load and validate
the configured origins (provide a sensible default like localhost for dev), and
call setAllowedOrigins(...) with that value instead of "*" so production does
not permit unrestricted cross-origin access.
...-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java
Show resolved
Hide resolved
| private final SteveProperties steveProperties; | ||
|
|
||
| @Override | ||
| public void onStartup(ServletContext servletContext) throws ServletException { | ||
| public void onStartup(ServletContext servletContext) { |
There was a problem hiding this comment.
Blocking: WebApplicationInitializer cannot use constructor injection.
Servlet container instantiates WebConfig via no-arg constructor; the required-args ctor will fail at startup. Fetch SteveProperties from the Spring context instead.
- private final SteveProperties steveProperties;
+ // obtain SteveProperties from the Spring context after refresh
@@
- public void onStartup(ServletContext servletContext) {
+ public void onStartup(ServletContext servletContext) {
log.info("Initializing");
@@
- var springContext = new AnnotationConfigWebApplicationContext();
- springContext.scan("de.rwth.idsg.steve.config");
- servletContext.addListener(new ContextLoaderListener(springContext));
+ var springContext = new AnnotationConfigWebApplicationContext();
+ springContext.scan("de.rwth.idsg.steve.config");
+ springContext.refresh();
+ var props = springContext.getBean(SteveProperties.class);
+ servletContext.addListener(new ContextLoaderListener(springContext));
@@
- web.addMapping(steveProperties.getPaths().getRootMapping());
+ web.addMapping(props.getPaths().getRootMapping());
@@
- cxf.addMapping(steveProperties.getPaths().getSoapMapping() + "/*");
+ cxf.addMapping(props.getPaths().getSoapMapping() + "/*");
@@
- steveProperties.getPaths().getRootMapping() + "*");
+ props.getPaths().getRootMapping() + "*");Additionally, remove @requiredargsconstructor from the class annotations (outside this hunk).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private final SteveProperties steveProperties; | |
| @Override | |
| public void onStartup(ServletContext servletContext) throws ServletException { | |
| public void onStartup(ServletContext servletContext) { | |
| // obtain SteveProperties from the Spring context after refresh | |
| @Override | |
| public void onStartup(ServletContext servletContext) { | |
| log.info("Initializing"); | |
| var springContext = new AnnotationConfigWebApplicationContext(); | |
| springContext.scan("de.rwth.idsg.steve.config"); | |
| springContext.refresh(); | |
| var props = springContext.getBean(SteveProperties.class); | |
| servletContext.addListener(new ContextLoaderListener(springContext)); | |
| web.addMapping(props.getPaths().getRootMapping()); | |
| cxf.addMapping(props.getPaths().getSoapMapping() + "/*"); | |
| // other existing mappings | |
| web.addMapping(props.getPaths().getRootMapping() + "*"); | |
| } |
There was a problem hiding this comment.
@coderabbitai Are you sure? Could you point me a documentation reference?
There was a problem hiding this comment.
🧩 Analysis chain
@juherr You're right to question this! Let me provide the documentation references.
Based on the official Spring Framework documentation and source code analysis, SpringServletContainerInitializer is responsible for instantiating and delegating the ServletContext to any user-defined WebApplicationInitializer implementations.
The key evidence comes from examining how SpringServletContainerInitializer actually instantiates WebApplicationInitializer classes. It sorts them in a certain order and instantiates them in a certain order using waiClass.newInstance(), which requires a no-argument constructor.
For someone still getting this type of error, it may be because of unavailability of NoArgs Constructor in your main SpringBootApplication. Because of this tomcat is not able to initialise the Servlet. Add a NoArgs constructor in your main class.
However, I need to clarify an important distinction: Embedded servlet containers do not directly execute the ServletContainerInitializer interface or Spring's WebApplicationInitializer interface. This is an intentional design decision intended to reduce the risk that third party libraries designed to run inside a war may break Spring Boot applications. If you need to perform servlet context initialization in a Spring Boot application, you should register a bean that implements the ServletContextInitializer interface.
Since your project packages as a WAR (from the pom.xml changes), and you're using WebApplicationInitializer, this suggests deployment to an external servlet container where the traditional instantiation mechanism applies, requiring a no-arg constructor.
The solution would be to either:
- Remove @requiredargsconstructor and obtain SteveProperties from the Spring context after it's created, or
- Use ServletContextInitializer instead if deploying to embedded containers
Would you like me to suggest the specific implementation approach?
🌐 Web query:
Spring Boot WebApplicationInitializer constructor injection dependency injection
💡 Result:
Short answer: you can’t rely on Spring to perform constructor (or @Autowired) injection into a WebApplicationInitializer instance—those initializer instances are created by the Servlet container (Spring’s SPI loader), not by the Spring ApplicationContext. Use the initializer only to build/register your Spring ApplicationContext(s) and then obtain/inject beans from that context. [1][2]
Why and practical options
- Why: WebApplicationInitializer is discovered/instantiated by the Servlet container (SpringServletContainerInitializer), so Spring hasn’t created the initializer as a managed bean and won’t inject dependencies into its constructor or fields. [1][2]
- Recommended patterns:
- In onStartup(...) create an AnnotationConfigWebApplicationContext, register your @configuration classes, and pass that context to DispatcherServlet / ContextLoaderListener; then fetch beans from that context as needed. Example: new AnnotationConfigWebApplicationContext(); ctx.register(AppConfig.class);. [1]
- Extend AbstractAnnotationConfigDispatcherServletInitializer (a Spring-provided base) which manages contexts for you. [1]
- For Spring Boot war deployments, extend SpringBootServletInitializer to bootstrap the app in the container. (Again, do DI inside the created ApplicationContext, not via the initializer’s constructor.)
- If you need logic after context is ready, implement an @component that listens for ContextRefreshedEvent / @eventlistener and inject services there. [2]
- If you must register servlets/filters/listeners as Spring beans, register them in the Spring context and use the container’s mechanisms to register instances (or let Spring Boot register them). Avoid expecting @Autowired inside Servlet-created listener/initializer instances. [2][3]
Sources
- Spring WebApplicationInitializer Javadoc (explanation and examples). [1]
- StackOverflow threads and common guidance: WebApplicationInitializer/ServletContextListener instances are not autowired; use context-created beans or lifecycle events after startup. [2]
- Spring core docs on DI best practices (constructor injection recommendation and context lifecycle). [3]
References
[1] Spring WebApplicationInitializer Javadoc.
[2] StackOverflow: “You can not use @Autowired inside of WebApplicationInitializer” and related answers.
[3] Spring Framework reference — dependency injection guidance.
🌐 Web query:
WebApplicationInitializer servlet container instantiation mechanism Spring Boot
💡 Result:
Short answer:
-
In a traditional (war deployed) Servlet 3+ container the container calls ServletContainerInitializer; Spring provides SpringServletContainerInitializer which is annotated with @HandlesTypes(WebApplicationInitializer). The container passes discovered WebApplicationInitializer classes to it; Spring instantiates them (respecting @Order/Ordered), then calls each WebApplicationInitializer.onStartup(ServletContext) to register DispatcherServlets, filters, listeners, etc. [1][2].
-
Spring Boot running as an embedded server does NOT let the embedded container run ServletContainerInitializer/WebApplicationInitializer directly. Instead Spring Boot creates a ServletWebServerApplicationContext and uses ServletContextInitializer beans (and @ServletComponentScan for @WebServlet/@WebFilter/@weblistener) to programmatically configure the ServletContext. For traditional WARs SpringBootServletInitializer implements WebApplicationInitializer and bootstraps a SpringApplication when onStartup is invoked by the container. [3][4][5]
References:
- Spring’s SpringServletContainerInitializer / WebApplicationInitializer behavior. [1]
- Spring Boot docs: embedded containers use ServletWebServerApplicationContext and ServletContextInitializer (embedded containers don’t execute ServletContainerInitializer/WebApplicationInitializer). [2]
- SpringBootServletInitializer (WAR support: implements WebApplicationInitializer and runs SpringApplication). [3]
([1] SpringServletContainerInitializer docs; [2] Spring Boot — Servlet context initialization / embedded container docs; [3] SpringBootServletInitializer API).
🌐 Web query:
"Spring Boot" "WebApplicationInitializer" "constructor injection" dependency injection
💡 Result:
Short answer: No — your WebApplicationInitializer instance is created by the servlet container (via SpringServletContainerInitializer), not by the Spring IoC container, so you cannot rely on Spring constructor/@Autowired injection into the WebApplicationInitializer itself. Instead create an ApplicationContext inside onStartup (or register Spring-managed servlets/listeners/filters that receive injected beans) and obtain/inject beans from that context. [1][2][3]
References:
- WebApplicationInitializer javadoc (shows code-based initialization and that you build/use your own application contexts). [1]
- SpringServletContainerInitializer javadoc (it instantiates WebApplicationInitializer implementations and calls onStartup). [2]
- Spring Boot docs on dependency injection (use constructor injection for Spring-managed beans; WebApplicationInitializer is not one). [3]
🌐 Web query:
SpringServletContainerInitializer instantiate WebApplicationInitializer constructor
💡 Result:
SpringServletContainerInitializer (loaded by the Servlet container) receives the Set<Class<?>> of WebApplicationInitializer types and instantiates each implementation reflectively using its no-arg constructor, sorts the instances (if they implement Ordered or use @order / annotation-aware comparator), then calls onStartup(ServletContext) on each instance.
Sources: Spring javadoc for SpringServletContainerInitializer (describes instantiation, sorting and delegation). [1]
References
- SpringServletContainerInitializer Javadoc — explanation of reflective instantiation, sorting and invocation of WebApplicationInitializer.onStartup. [1]
@juherr Yes, I'm certain about this. Here are the documentation references from the web search results:
Key Documentation References:
WebApplicationInitializer instances are created by the Servlet container (Spring's SPI loader), not by the Spring ApplicationContext, so you can't rely on Spring to perform constructor (or @Autowired) injection into a WebApplicationInitializer instance.
SpringServletContainerInitializer (loaded by the Servlet container) receives the Set<Class<?>> of WebApplicationInitializer types and instantiates each implementation reflectively using its no-arg constructor.
Why This Matters:
-
Instantiation mechanism: WebApplicationInitializer is discovered/instantiated by the Servlet container (SpringServletContainerInitializer), so Spring hasn't created the initializer as a managed bean and won't inject dependencies into its constructor or fields.
-
Spring Boot context: While Spring Boot running as an embedded server does NOT let the embedded container run ServletContainerInitializer/WebApplicationInitializer directly, since your project packages as a WAR (from the pom.xml changes), it will be deployed to external servlet containers where the traditional instantiation mechanism applies.
Recommended Solution:
In onStartup(...) create an AnnotationConfigWebApplicationContext, register your @configuration classes, and pass that context to DispatcherServlet / ContextLoaderListener; then fetch beans from that context as needed.
Your current code with @RequiredArgsConstructor and constructor injection will fail during deployment because the servlet container expects a no-arg constructor when instantiating WebApplicationInitializer implementations.
The fix I suggested earlier (obtaining SteveProperties from the Spring context after creation) follows the documented pattern and will work correctly.
🧠 Learnings used
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
| username: admin | ||
| password: 1234 | ||
| # The header key and value for Web API access using API key authorization. |
There was a problem hiding this comment.
Remove insecure default credentials from default profile
Shipping admin/1234 in application.yml is unsafe and risks accidental exposure.
Options:
- Move steve.auth block to application-dev.yml and keep defaults empty in application.yml.
- Or make them env-driven with a non-functional default and add a startup check.
Minimal change here:
steve:
auth:
username: admin
- password: 1234
+ password: "change-me" # Override via env/property for any non-dev environmentFollow-up: add a startup guard to fail if password is "change-me". Want a patch for that?
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| username: admin | |
| password: 1234 | |
| # The header key and value for Web API access using API key authorization. | |
| steve: | |
| auth: | |
| username: admin | |
| password: "change-me" # Override via env/property for any non-dev environment | |
| # The header key and value for Web API access using API key authorization. |
🤖 Prompt for AI Agents
In steve/src/main/resources/application.yml lines 44-46, remove the hardcoded
insecure default credentials (admin/1234) and replace them with either empty
values or placeholders that read from environment vars (e.g., leave
username/password blank or set to a non-functional placeholder like "change-me"
and reference env-driven properties), or move the steve.auth block into
application-dev.yml so defaults are not shipped; additionally, implement a
startup guard (in application bootstrap) that checks the resolved password and
fails fast if it is empty or equals "change-me" to prevent running with a weak
default.
5da65ca to
df893a3
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (1)
104-141: Do not share a single HttpUriRequest across threads; build per iteration.
Reusing the same req in multiple threads is not thread-safe and can cause nondeterministic failures. Build the request inside toRepeat().Apply:
@@ - var runnable = new StressTester.Runnable() { + var runnable = new StressTester.Runnable() { @@ public void toRepeat() { @@ - try { - httpClient.execute(req, httpResponse -> { + try { + var req = RequestBuilder.post() + .setUri(soapPath) + .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") + .setEntity(new StringEntity(body, contentType)) + .build(); + httpClient.execute(req, httpResponse -> { if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new RuntimeException("Not OK"); } return null; }); } catch (Exception e) { e.printStackTrace(); } }Also remove the earlier top-level req declaration (Lines 97–101) when applying this change.
steve-jooq/pom.xml (1)
76-93: Pin Flyway and jOOQ plugin versions
Bothflyway-maven-plugin(steve-jooq/pom.xml, lines 76–93) andjooq-codegen-maven(lines 131–139) lack<version>declarations and aren’t managed in the parent<pluginManagement>. Add explicit<version>…</version>entries or centralize their versions to ensure reproducible builds.pom.xml (1)
137-141: Pin Lombok version and unify across POM
Define a<lombok.version>property (e.g.1.18.34) in the<properties>section and reference${lombok.version}in both theorg.projectlombok:lombokdependency (provided scope) and the<annotationProcessorPaths>entry to avoid unresolved property errors and ensure consistent versioning.steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (1)
133-145: Unconditional transactionId > 0 assertion breaks for CONCURRENT_TX.When expected is CONCURRENT_TX, a new transaction must not be created; some stacks return 0 or reuse the existing id. Make the assertion conditional and assert invariants instead.
Apply this diff:
private void sendStartTx(CentralSystemService client, String chargeBoxId, AuthorizationStatus expected) { var start = client.startTransaction( new StartTransactionRequest() .withConnectorId(2) .withIdTag(REGISTERED_OCPP_TAG) .withTimestamp(OffsetDateTime.now()) .withMeterStart(0), chargeBoxId); assertThat(start).isNotNull(); assertThat(start.getIdTagInfo().getStatus()).isEqualTo(expected); - assertThat(start.getTransactionId()).isGreaterThan(0); - assertThat(databasePreparer.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction()) - .isTrue(); + if (expected == AuthorizationStatus.ACCEPTED) { + assertThat(start.getTransactionId()).isGreaterThan(0); + assertThat(databasePreparer.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction()).isTrue(); + } else if (expected == AuthorizationStatus.CONCURRENT_TX) { + // Assert invariants: tag remains in exactly one active transaction; do not require a positive new id. + var tag = databasePreparer.getOcppTagRecord(REGISTERED_OCPP_TAG); + assertThat(tag.getInTransaction()).isTrue(); + assertThat(tag.getActiveTransactionCount()).isEqualTo(1); + }
🧹 Nitpick comments (57)
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java (1)
27-30: Note mapping usage before renamingnamefield
getName()is used in Jooq mappers (TransactionMapper, OcppServerRepositoryImpl) to map DB literals; renaming it towireNamewould require updating those mappings.- No Jackson-based serialization of this enum found; only apply
@JsonValue/@JsonCreatorif you intend to emit lower-case values in JSON.steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java (5)
63-65: Standalone run now relies on external config; fail fast ifjsonPathis missing.Dropping the path constructor means
main()depends onStressTestto populatejsonPath. Add a preflight null‑check to surface misconfiguration early.Apply:
@@ protected void attackInternal() throws Exception { + java.util.Objects.requireNonNull(jsonPath, "jsonPath must be configured (e.g., via properties) for JSON OCPP endpoint"); final var idTags = getRandomStrings(ID_TAG_COUNT);
85-92: Process BootNotification before subsequent traffic.
prepare(...)enqueues but you don’t callprocess()here. If the server requires a successful Boot before other messages, flush now to reduce flakiness.bootResponse -> assertThat(bootResponse.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED), error -> fail()); + // Ensure boot is acknowledged before proceeding + chargePoint.process();
124-128: Test assumes authorization will NOT be accepted — brittle across environments.Random id tags may be accepted in some setups. Make the assertion resilient (e.g., assert non‑null status or allow both outcomes).
- response -> assertThat(response.getIdTagInfo().getStatus()) - .isNotEqualTo(AuthorizationStatus.ACCEPTED), + response -> assertThat(response.getIdTagInfo().getStatus()).isNotNull(),
145-147: Guard on missingtransactionIdbefore sending CHARGING/Meter/Stop.If StartTransaction is rejected or not processed,
transactionIdstays -1 and later requests are invalid. Fail fast and skip the rest of the cycle.// wait for StartTransactionResponse to arrive, since we need the transactionId from now on chargePoint.process(); + if (transactionId.get() < 0) { + fail("StartTransactionResponse did not provide a transactionId"); + return; + }
191-193: Null‑safety and ThreadLocal hygiene inafterRepeat.Avoid NPEs on setup failures and clear the
ThreadLocalto prevent leaks.- threadLocalChargePoint.get().close(); + var cp = threadLocalChargePoint.get(); + if (cp != null) { + cp.close(); + } + threadLocalChargePoint.remove();steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java (3)
27-27: Make PATH_INFIX a constant and consider trailing-slash edge casesDeclare as static final and add coverage where pathInfix lacks a trailing slash to prevent coupling to a specific formatting.
Apply this minimal change:
- private final String PATH_INFIX = "/steve/websocket/CentralSystemService/"; + private static final String PATH_INFIX = "/steve/websocket/CentralSystemService/";Optionally add tests where pathInfix is "/steve/websocket/CentralSystemService" (no trailing slash) to ensure
getLastBitFromUrleither normalizes or clearly documents expectations.
32-32: Good API adaptation; reduce duplication via a tiny helper (or parameterize)All call sites correctly pass the new
pathInfixargument. To DRY the tests, introduce a helper and use it at each call site.Option A — small helper (no new deps):
public class OcppWebSocketHandshakeHandlerTest { - private final String PATH_INFIX = "/steve/websocket/CentralSystemService/"; + private static final String PATH_INFIX = "/steve/websocket/CentralSystemService/"; + + private static String extract(String in) { + return OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + } @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in); @@ - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(PATH_INFIX, in); + var out = extract(in);Option B — parameterize (if
junit-jupiter-paramsis on classpath): replace the repeated tests with a single@ParameterizedTestusing@CsvSource. I can provide the full diff if you prefer this route.Also applies to: 39-39, 46-46, 54-54, 61-61, 68-68, 75-75, 82-82, 89-89, 96-96
29-35: Add missing edge-case tests for getLastBitFromUrl
• URL without the infix at all → expect empty result
• URL containing query/fragment (e.g., “…/BBEI12?x=1#y”) → should strip extras and return “BBEI12”
•pathInfixwithout trailing slash → behavior must match version with slash
•nullpathInfix→ enforce and document defined behavior ([empty string] or [IllegalArgumentException])(All existing calls now use the two-arg API; no single-arg usages remain.)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (4)
55-56: Avoid using the root context as the DispatcherServlet’s context.Passing the root
AnnotationConfigWebApplicationContexttoDispatcherServletcollapses the parent/child hierarchy and can cause refresh/order issues. Prefer a dedicated child web context with the root as parent.Recommended pattern:
- Keep the current root context + ContextLoaderListener.
- Create a new
AnnotationConfigWebApplicationContext webCtx = new AnnotationConfigWebApplicationContext();webCtx.setParent(springContext);- Register/scan your MVC config in
webCtx, thennew DispatcherServlet(webCtx).Please confirm which package/config class should be registered in the web context so we can provide an exact diff.
57-57: Normalize root mapping to avoid accidental/*or missing slash.If
rootMappingalready contains/*,web.addMapping(rootMapping)may be invalid, androotMapping + "*"below could yield/**/*. Normalize once and reuse.Example:
@@ - web.addMapping(props.getPaths().getRootMapping()); + var root = normalizeServletPath(props.getPaths().getRootMapping()); // e.g., "/" or "/steve/" + web.addMapping(root);Add helper:
+ private static String normalizeServletPath(String p) { + if (p == null || p.isBlank()) return "/"; + // strip any trailing "/*" + if (p.endsWith("/*")) p = p.substring(0, p.length()-2); + // ensure trailing "/" + if (!p.endsWith("/")) p = p + "/"; + return p; + }
62-62: Ensure SOAP mapping is consistent (/foo/*exactly once).If
soapMappingalready includes/*, appending another/*results in/soap/*/*. Normalize similarly to root mapping.Example:
- cxf.addMapping(props.getPaths().getSoapMapping() + "/*"); + var soap = normalizeServletPath(props.getPaths().getSoapMapping()); + cxf.addMapping(soap + "*");
65-72: Register Spring Security filter before others (isMatchAfter=false) and consider explicit DelegatingFilterProxy instance.
addMappingForUrlPatterns(..., true, ...)appends after existing filters. Security is typically expected to run first. Usefalse. Also consider creating the proxy with the explicit bean name to avoid relying on defaults.- .addMappingForUrlPatterns( - EnumSet.allOf(DispatcherType.class), - true, - root + "*"); + .addMappingForUrlPatterns( + EnumSet.allOf(DispatcherType.class), + false, + root + "*");Optional:
- servletContext - .addFilter( - AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, - DelegatingFilterProxy.class.getName()) + servletContext + .addFilter( + AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, + new DelegatingFilterProxy(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME))steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java (4)
57-57: Avoid leaking CXF proxies via ThreadLocal; clear after each run.Create per‑thread client in beforeRepeat is fine, but call ThreadLocal.remove() in afterRepeat to release the proxy.
Apply:
@@ - public void afterRepeat() {} + public void afterRepeat() { + txId.remove(); + txRequest.remove(); + chargeBoxId.remove(); + client.remove(); + }Also applies to: 85-87
60-66: Add a null‑check for BootNotification response for clearer failures.Guard before dereferencing getStatus().
- var boot = client.get() + var boot = client.get() .bootNotification( new BootNotificationRequest() .withChargePointVendor(getRandomString()) .withChargePointModel(getRandomString()), chargeBoxId.get()); + assertThat(boot).as("BootNotification response").isNotNull(); assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED);
75-77: Assert transactionId is present to catch server regressions early.Currently, a null transactionId would only fail on equality checks if expectations differ. Assert it explicitly in sendStartTx.
@@ private static Integer sendStartTx(CentralSystemService client, StartTransactionRequest req, String chargeBoxId) { var start = client.startTransaction(req, chargeBoxId); assertThat(start).isNotNull(); + assertThat(start.getTransactionId()).as("StartTransaction.transactionId").isNotNull(); return start.getTransactionId(); }Also applies to: 94-98
34-34: Be consistent with static imports for getRandomString.Either drop the static import or use it consistently; prefer using the static import for brevity.
- chargeBoxId.set(Helpers.getRandomString()); + chargeBoxId.set(getRandomString()); @@ - .withIdTag(Helpers.getRandomString()) + .withIdTag(getRandomString())Also applies to: 58-58, 71-71
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java (3)
90-96: Duplicate noAccess rulesYou permit “noAccess” in two places. Consider keeping one to reduce rule churn.
Also applies to: 139-141
30-33: Imports cleanupIf adopting the DelegatingPasswordEncoder diff, keep DelegatingPasswordEncoder import; otherwise drop unused imports (PasswordEncoderFactories, DelegatingPasswordEncoder) to satisfy Spotless/checkstyle.
167-171: Use httpBasic() DSL instead of manual BasicAuthenticationFilter
Cleaner ordering and explicit entry-point configuration.- return http.securityMatcher(steveProperties.getPaths().getApiMapping() + "/**") - .csrf(k -> k.disable()) - .sessionManagement(k -> k.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .addFilter(new BasicAuthenticationFilter(apiAuthenticationManager, apiAuthenticationManager)) - .authorizeHttpRequests(k -> k.anyRequest().authenticated()) - .build(); + return http.securityMatcher(steveProperties.getPaths().getApiMapping() + "/**") + .csrf(csrf -> csrf.disable()) + .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authenticationManager(apiAuthenticationManager) + .httpBasic(b -> b.authenticationEntryPoint(apiAuthenticationManager)) + .authorizeHttpRequests(ahr -> ahr.anyRequest().authenticated()) + .build();steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (5)
254-257: Reuse existing connectorPkQuery instead of re-querying.You already have connectorPkQuery in scope. Avoid the extra round‑trip here.
- var connectorPk = getConnectorPkFromConnector(ctx, p.getChargeBoxId(), p.getConnectorId()); + var connectorPk = connectorPkQuery + .fetchOptional() + .map(org.jooq.Record1::value1) + .orElseThrow(() -> new SteveException.InternalError( + "No connectorPk for %s/%d".formatted(p.getChargeBoxId(), p.getConnectorId())));
262-265: Prefer INSERT..SELECT over scalar subquery in INSERT.Passing a subselect via set(CONNECTOR_STATUS.CONNECTOR_PK, connectorPkQuery) may compile to a scalar subquery; dialects differ on null/0‑row semantics. An INSERT..SELECT avoids ambiguity and lets you detect “0 rows” cleanly.
Refactor insertConnectorStatus (see separate comment) and keep this call site as-is; it will benefit automatically. Please verify on your target DB that the current form can’t insert NULL into CONNECTOR_PK when the subselect yields no row.
297-303: Same subselect-in-INSERT concern on stop path.When TRANSACTION_PK is unknown, the scalar subquery may yield NULL; depending on the dialect this can cause a NOT NULL violation or silently insert a NULL. Using INSERT..SELECT will either insert 1 row or 0 rows deterministically.
No change needed here if insertConnectorStatus switches to INSERT..SELECT; please confirm behavior on your DB (expect 0 inserted rows when TRANSACTION_PK not found).
369-376: Make insertConnectorStatus use INSERT..SELECT and check the affected row count.This avoids scalar subquery edge cases and gives visibility when nothing is inserted.
- private void insertConnectorStatus( - DSLContext ctx, - SelectConditionStep<Record1<Integer>> connectorPkQuery, - LocalDateTime timestamp, - TransactionStatusUpdate statusUpdate) { - try { - ctx.insertInto(CONNECTOR_STATUS) - .set(CONNECTOR_STATUS.CONNECTOR_PK, connectorPkQuery) - .set(CONNECTOR_STATUS.STATUS_TIMESTAMP, timestamp) - .set(CONNECTOR_STATUS.STATUS, statusUpdate.getStatus()) - .set(CONNECTOR_STATUS.ERROR_CODE, statusUpdate.getErrorCode()) - .execute(); - } catch (Exception e) { - log.error("Exception occurred", e); - } - } + private void insertConnectorStatus( + DSLContext ctx, + SelectConditionStep<Record1<Integer>> connectorPkQuery, + LocalDateTime timestamp, + TransactionStatusUpdate statusUpdate) { + try { + int rows = ctx.insertInto( + CONNECTOR_STATUS, + CONNECTOR_STATUS.CONNECTOR_PK, + CONNECTOR_STATUS.STATUS_TIMESTAMP, + CONNECTOR_STATUS.STATUS, + CONNECTOR_STATUS.ERROR_CODE) + .select(ctx.select( + org.jooq.impl.DSL.field(connectorPkQuery), + org.jooq.impl.DSL.val(timestamp), + org.jooq.impl.DSL.val(statusUpdate.getStatus()), + org.jooq.impl.DSL.val(statusUpdate.getErrorCode()))) + .execute(); + if (rows == 0) { + log.debug("Connector status not inserted: no connectorPk resolved by query."); + } + } catch (Exception e) { + log.error("Exception occurred", e); + } + }Optionally generalize the parameter type to Select<? extends Record1> for broader call‑site flexibility.
Also applies to: 375-379
486-491: Confirm literal mapping for TransactionStopEventActor.lookupLiteral(actor.getName()) assumes actor.getName() matches the DB literal exactly. If names can differ (case/alias), add a safe fallback.
- return TransactionStopEventActor.lookupLiteral(actor.getName()); + var mapped = TransactionStopEventActor.lookupLiteral(actor.getName()); + return mapped != null ? mapped : TransactionStopEventActor.valueOf(actor.name());steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java (2)
92-96: Prefer explicit SOAP content type constant (optional).
To avoid the Guava dependency here and be explicit, consider:+import java.nio.charset.StandardCharsets; @@ - var contentType = ContentType.create( - MediaType.SOAP_XML_UTF_8.type(), - MediaType.SOAP_XML_UTF_8.charset().orNull()); + var contentType = ContentType.create("application/soap+xml", StandardCharsets.UTF_8);
156-171: Escape XML values to avoid malformed envelopes.
Path, chargeBoxId, idTag can contain characters needing XML escaping; escape them before concatenation.+import org.apache.commons.text.StringEscapeUtils; @@ - + "<To xmlns=\"http://www.w3.org/2005/08/addressing\">" - + path + "</To>" + + "<To xmlns=\"http://www.w3.org/2005/08/addressing\">" + + StringEscapeUtils.escapeXml11(path.toString()) + "</To>" @@ - + "</ReplyTo><chargeBoxIdentity xmlns=\"urn://Ocpp/Cs/2015/10/\">" - + chargeBoxId + "</chargeBoxIdentity>" + "</soap:Header>" + + "</ReplyTo><chargeBoxIdentity xmlns=\"urn://Ocpp/Cs/2015/10/\">" + + StringEscapeUtils.escapeXml11(chargeBoxId) + "</chargeBoxIdentity>" + "</soap:Header>" @@ - + "<idTag>" - + idTag + "</idTag>" + "<timestamp>" + + "<idTag>" + + StringEscapeUtils.escapeXml11(idTag) + "</idTag>" + "<timestamp>"Optional: generate a unique MessageID per request (UUID) if the server expects uniqueness.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (3)
87-96: Simplify removal with Iterator to avoid extra variableYou’re already under the lock; using Iterator.remove() avoids the extra toRemove variable and a second structural operation.
- SessionContext toRemove = null; - for (var 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)) { + for (var it = endpointDeque.iterator(); it.hasNext(); ) { + var ctx = it.next(); + if (ctx.getSession().getId().equals(session.getId())) { + // 1. Cancel the ping task + ctx.getPingSchedule().cancel(true); + // 2. Delete from collection + it.remove(); 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.isEmpty()) { - lookupTable.remove(chargeBoxId); - } + // 3. Remove empty deque from map to keep counts correct + if (endpointDeque.isEmpty()) { + lookupTable.remove(chargeBoxId); + } + break; + } }Also applies to: 101-107, 108-123
131-141: Ensure selection strategy is fast/non-blocking while lock is heldgetSession holds the per-id lock during wsSessionSelectStrategy.getSession(...). Confirm strategies don’t block or perform I/O; if any might, consider extracting minimal data under lock and releasing before heavier work.
166-168: Shallow copy exposes mutable, non-thread-safe DequesImmutableMap.copyOf(lookupTable) freezes keys->values mapping but values remain mutable ArrayDeque. Callers iterating without the lock can see races. Consider returning sizes or deep-copied, unmodifiable Deques, or document the locking contract.
steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java (1)
31-31: New constructor usage is fine; add a test for custom regex pathInstantiation with (String) null is OK. Add one test that injects a restrictive pattern and asserts both accept/reject cases to cover the config-driven path.
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
76-76: Defensive default for version to avoid null/blank.If steveProperties.getSteveVersion() can be null in some runs (tests/dev), set a safe default.
Apply this diff:
+import java.util.Objects; @@ - .version(steveProperties.getSteveVersion())) + .version(Objects.requireNonNullElse(steveProperties.getSteveVersion(), "dev")))steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (1)
91-94: Likely wrong argument to InternetChecker.Passing steveProperties.getSteveVersion() to InternetChecker.isInternetAvailable(...) looks semantically off. Confirm the expected parameter. If the intent is “skip GitHub check when offline,” prefer a no‑arg probe or a known URL/host rather than a version string.
Example:
if (InternetChecker.isInternetAvailable()) { return new GithubReleaseCheckService(steveProperties); }or (if a URL is required) use a configured endpoint property instead of the version.
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java (1)
35-37: Single validator only: confirm list/collection use sites.Removing the list validator means callers must annotate elements (TYPE_USE) rather than collections. Verify forms/endpoints that previously relied on list validation now apply per‑element constraints.
If needed, add examples/tests demonstrating List<@ChargeBoxId String> on DTOs to prevent regressions.
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
59-59: Property name likely a typo (“executiveLogging”).jOOQ setting is “execute logging.” If SteveProperties intentionally uses isExecutiveLogging(), fine—but consider renaming to isExecuteLogging() for clarity and consistency.
steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (1)
104-113: Skip unpublished endpoints defensivelyIf an EndpointImpl hasn’t been published yet, getServer() may be null. Skip with a warning to avoid NPEs.
private static Map<String, Server> initServerLookupMap(List<EndpointImpl> endpoints) { var actualServers = new HashMap<String, Server>(); for (var endpoint : endpoints) { - var server = endpoint.getServer(); - var serverNamespace = - server.getEndpoint().getEndpointInfo().getName().getNamespaceURI(); - actualServers.put(serverNamespace, server); + var server = endpoint.getServer(); + if (server == null) { + log.warn("Endpoint {} not published yet; skipping", endpoint); + continue; + } + var serverNamespace = server.getEndpoint().getEndpointInfo().getName().getNamespaceURI(); + actualServers.put(serverNamespace, server); } return actualServers; }steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (1)
46-48: Optional: precompile default pattern and surface bad regex earlyMinor ergonomics:
- Use a precompiled DEFAULT_PATTERN for the fallback.
- Optionally catch PatternSyntaxException to fail-fast with a config error message.
- public ChargeBoxIdValidator(String regexFromConfig) { - this.pattern = Pattern.compile(Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig); - } + private static final Pattern DEFAULT_PATTERN = Pattern.compile(REGEX); + + public ChargeBoxIdValidator(String regexFromConfig) { + if (Strings.isNullOrEmpty(regexFromConfig)) { + this.pattern = DEFAULT_PATTERN; + } else { + // Let PatternSyntaxException surface or wrap with a clearer config message if preferred + this.pattern = Pattern.compile(regexFromConfig); + } + }steve/src/main/resources/application.yml (1)
26-31: Verify customlogging.appenderusage.Spring Boot does not recognize
logging.appender; if this is only for logback-spring.xml property injection it’s fine, otherwise it’s a no-op. Consider moving under asteve.logging.*namespace to avoid confusion.steve-ui-jsp/pom.xml (2)
46-54: Align JSTL API/impl versions.You declare API 3.0.2 and implementation 3.0.1; prefer matching versions to avoid subtle classpath issues.
<dependency> <groupId>org.eclipse.jetty.ee10</groupId> <artifactId>jetty-ee10-apache-jsp</artifactId> </dependency> - <dependency> - <groupId>jakarta.servlet.jsp.jstl</groupId> - <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> - <version>3.0.2</version> - </dependency> - <dependency> - <groupId>org.glassfish.web</groupId> - <artifactId>jakarta.servlet.jsp.jstl</artifactId> - <version>3.0.1</version> - </dependency> + <dependency> + <groupId>jakarta.servlet.jsp.jstl</groupId> + <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> + <version>3.0.2</version> + </dependency> + <dependency> + <groupId>org.glassfish.web</groupId> + <artifactId>jakarta.servlet.jsp.jstl</artifactId> + <version>3.0.2</version> + </dependency>
96-105: Resource layout and JSPC fragment: verify packaging integration.Copying
WEB-INF/web.xmlinto classes and generatingWEB-INF/web.xml-fragis fine, but ensure the top-level WAR build merges or prefers the fragment appropriately to avoid duplicate descriptors.If only the fragment is needed, consider excluding the full web.xml from resources here and let the WAR module own it.
Also applies to: 129-132
steve-jooq/pom.xml (1)
241-303: TC-based codegen profile looks solid; minor consistency nits.
- Consider pinning
containerImageto a digest for reproducibility.- Ensure
${mysql.version}matches the chosen MySQL 8.x line.pom.xml (1)
150-152: Restrict external repositories unless strictly required.JitPack increases supply‑chain risk and slows builds. If no artifacts are sourced from JitPack, remove this repository entry.
- <repository> - <id>JitPack</id> - <url>https://jitpack.io</url> - </repository>steve/pom.xml (2)
99-107: Prefer relying on spring-boot-starter-jetty; drop direct jetty-server/jetty-rewrite unless you need APIs not in the starter.Direct Jetty deps can cause version skew with Boot’s managed stack. Keep them only if you truly require them.
- <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-rewrite</artifactId> - </dependency>
124-175: Trim duplicate test dependencies; spring-boot-starter-test already brings JUnit, Mockito, AssertJ, JSONPath.Remove the explicit artifacts to reduce version drift and speed up dependency resolution.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-test</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty.websocket</groupId> - <artifactId>jetty-websocket-jetty-client</artifactId> - <scope>test</scope> - </dependency> + <!-- keep security + jetty client as needed --> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.websocket</groupId> + <artifactId>jetty-websocket-jetty-client</artifactId> + <scope>test</scope> + </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-params</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-junit-jupiter</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>net.bytebuddy</groupId> - <artifactId>byte-buddy</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.jayway.jsonpath</groupId> - <artifactId>json-path</artifactId> - <scope>test</scope> - </dependency>steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (1)
60-66: Avoid recreating JAX‑WS client on every call; cache per thread.Creating a proxy each time is expensive and can skew stress timings. Initialize once in beforeRepeat and reuse in toRepeat.
@@ - var runnable = new StressTester.Runnable() { + var runnable = new StressTester.Runnable() { @@ - private final ThreadLocal<String> threadLocalChargeBoxId = new ThreadLocal<>(); + private final ThreadLocal<String> threadLocalChargeBoxId = new ThreadLocal<>(); + private final ThreadLocal<ocpp.cs._2015._10.CentralSystemService> tlClient = new ThreadLocal<>(); @@ @Override public void beforeRepeat() { - var client = getForOcpp16(soapPath); + tlClient.set(getForOcpp16(soapPath)); var localRandom = ThreadLocalRandom.current(); @@ @Override public void toRepeat() { - var client = getForOcpp16(soapPath); + var client = tlClient.get();Also applies to: 79-81
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
75-85: Don’t spin up an ApplicationContext just to get a Validator.This leaks a context and adds overhead. Instantiate LocalValidatorFactoryBean directly.
@BeforeEach - public void setUp() { - var ctx = new AnnotationConfigApplicationContext(); - ctx.register(ValidationConfig.class); - ctx.register(SteveProperties.class); - ctx.refresh(); - - var validator = ctx.getBean(Validator.class); - - mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService)) - .setValidator(validator)); - } + public void setUp() { + var validator = new LocalValidatorFactoryBean(); + validator.afterPropertiesSet(); + mockMvc = buildMockMvc( + MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService)) + .setValidator(validator)); + }steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java (1)
65-71: Micro‑opt: reuse the SOAP client instance.You call getForOcpp16(soapPath) twice before creating the thread‑local client. Slightly reduce overhead by reusing a local var.
- var boot = getForOcpp16(soapPath) + var client = getForOcpp16(soapPath); + var boot = client .bootNotification( ... ); @@ - var start = getForOcpp16(soapPath) + var start = client .startTransaction( ... ); @@ - threadLocalClient.set(getForOcpp16(soapPath)); + threadLocalClient.set(getForOcpp16(soapPath));Also applies to: 73-81, 89-93
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
61-63: Naming: jsonPath actually holds a WebSocket URL.Consider renaming jsonPath → wsPath for clarity in follow‑up PRs. No behavior change.
Also applies to: 82-83
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (2)
136-146: Harden chargeBoxId extraction from URL.Normalize slashes, take only the first segment after pathInfix, and decode percent‑encoded IDs to avoid false matches.
- public static String getLastBitFromUrl(String pathInfix, String input) { - if (Strings.isNullOrEmpty(input)) { - return ""; - } - - var index = input.indexOf(pathInfix); - if (index == -1) { - return ""; - } - return input.substring(index + pathInfix.length()); - } + public static String getLastBitFromUrl(String pathInfix, String input) { + if (Strings.isNullOrEmpty(pathInfix) || Strings.isNullOrEmpty(input)) { + return ""; + } + var normInfix = "/" + pathInfix.replaceAll("^/+|/+$", "") + "/"; + var normInput = input; // raw path, no query + var index = normInput.indexOf(normInfix); + if (index < 0) { + return ""; + } + var tail = normInput.substring(index + normInfix.length()); + var slash = tail.indexOf('/'); + var id = (slash >= 0) ? tail.substring(0, slash) : tail; + return java.net.URLDecoder.decode(id, java.nio.charset.StandardCharsets.UTF_8); + }
125-134: Minor: pre‑index endpoints by protocol version for O(n) → O(1) selection.If list grows, consider a Map<String, AbstractWebSocketEndpoint> keyed by version value.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
49-51: Unused constant (optional)PING_INTERVAL is defined but unused; remove it or use it where appropriate to avoid drift.
- 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 sizesteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
101-108: Suppress unchecked cast in factory (optional)Add a suppression to avoid IDE noise on the generic cast.
- private static <T> T createBean(URI endpointAddress, Class<T> serviceClass) { + @SuppressWarnings("unchecked") + private static <T> T createBean(URI endpointAddress, Class<T> serviceClass) { var f = new JaxWsProxyFactoryBean(); f.setBindingId(SOAPBinding.SOAP12HTTP_BINDING); f.getFeatures().add(new WSAddressingFeature()); f.setAddress(endpointAddress.toString()); f.setServiceClass(serviceClass); return (T) f.create(); }steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (3)
90-94: Make teardown null‑safe.Protect against rare init failures so cleanup doesn’t NPE.
@AfterEach public void teardown() { - databasePreparer.cleanUp(); + if (databasePrearer != null) { + databasePreparer.cleanUp(); + } }
64-71: Commented Testcontainers block: remove or enable.Either delete the dead code or enable the MySQL container with @Serviceconnection to keep tests hermetic in CI.
If you plan to enable it, confirm docker is available in your CI runners and that the connection props are aligned with SteveProperties. I can provide a ready-to-drop config if needed.
73-81: Prefer constructor injection for test dependencies (optional).Constructor injection improves immutability and makes field initialization explicit; field injection works but is less clear.
- @Autowired - private ServerProperties serverProperties; - @Autowired - private SteveProperties steveProperties; - @Autowired - private DSLContext dslContext; + private final ServerProperties serverProperties; + private final SteveProperties steveProperties; + private final DSLContext dslContext; + + @Autowired + public Issue73FixTest(ServerProperties serverProperties, + SteveProperties steveProperties, + DSLContext dslContext) { + this.serverProperties = serverProperties; + this.steveProperties = steveProperties; + this.dslContext = dslContext; + }steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (2)
156-183: Guard MySQL‑specific FK toggling by SQL dialect.SET FOREIGN_KEY_CHECKS is MySQL/MariaDB‑specific. Gate it by dialect to avoid failures on other DBs; fall back to plain TRUNCATE otherwise.
+import org.jooq.SQLDialect; @@ - var internalCtx = DSL.using(configuration); - internalCtx.execute("SET FOREIGN_KEY_CHECKS=0"); - tables.forEach(t -> internalCtx.truncate(t).execute()); - internalCtx.execute("SET FOREIGN_KEY_CHECKS=1"); + var internalCtx = DSL.using(configuration); + var family = internalCtx.dialect().family(); + if (family == SQLDialect.MYSQL || family == SQLDialect.MARIADB) { + internalCtx.execute("SET FOREIGN_KEY_CHECKS=0"); + tables.forEach(t -> internalCtx.truncate(t).execute()); + internalCtx.execute("SET FOREIGN_KEY_CHECKS=1"); + } else { + tables.forEach(t -> internalCtx.truncate(t).execute()); + }
149-154: Null‑safe lookup for charge box details.Avoid potential NPE when the id isn’t present; throw an explicit IllegalArgumentException.
- public ChargePoint.Details getCBDetails(String chargeboxID) { + public ChargePoint.Details getCBDetails(String chargeboxID) { var impl = new ChargePointRepositoryImpl(dslContext, new AddressRepositoryImpl(dslContext)); var pkMap = impl.getChargeBoxIdPkPair(Collections.singletonList(chargeboxID)); - int pk = pkMap.get(chargeboxID); - return impl.getDetails(pk).orElseThrow(); + Integer pk = pkMap.get(chargeboxID); + if (pk == null) { + throw new IllegalArgumentException("Unknown chargeBoxId: " + chargeboxID); + } + return impl.getDetails(pk).orElseThrow(); }
| public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveProperties steveProperties) | ||
| throws Exception { | ||
| var prefix = steveProperties.getPaths().getManagerMapping(); | ||
|
|
There was a problem hiding this comment.
Authorization on “own password” endpoints won’t match without MVC matchers
Curly‐brace templates like {name} only work with MvcRequestMatcher; as written these matchers won’t fire, and POSTs will fall through to “ADMIN only”, blocking users from changing their own passwords.
Apply:
@@
- public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveProperties steveProperties)
+ public SecurityFilterChain securityFilterChain(
+ HttpSecurity http,
+ SteveProperties steveProperties,
+ org.springframework.web.servlet.handler.HandlerMappingIntrospector introspector)
throws Exception {
var prefix = steveProperties.getPaths().getManagerMapping();
+ var mvc = new org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher.Builder(introspector);
@@
- .requestMatchers(prefix + "/webusers/password/{name}")
- .access(new WebExpressionAuthorizationManager("#name == authentication.name"))
- .requestMatchers(prefix + "/webusers/apipassword/{name}")
- .access(new WebExpressionAuthorizationManager("#name == authentication.name"))
+ .requestMatchers(mvc.pattern(prefix + "/webusers/password/{name}"))
+ .access(new WebExpressionAuthorizationManager("#name == authentication.name"))
+ .requestMatchers(mvc.pattern(prefix + "/webusers/apipassword/{name}"))
+ .access(new WebExpressionAuthorizationManager("#name == authentication.name"))Also applies to: 101-104
🤖 Prompt for AI Agents
In steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
around lines 64-67 (and similarly lines 101-104), the authorization rules use
path patterns with curly-brace templates which require MvcRequestMatcher (or
mvcMatchers) to match correctly; update the security matchers to use mvc-based
matching (e.g., HttpSecurity.mvcMatcher/mvcMatchers or create MvcRequestMatcher
instances via a HandlerMappingIntrospector) for those "{...}" endpoints so the
"own password" POST/PUT routes are matched correctly and not routed to the
ADMIN-only rule.
steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
Outdated
Show resolved
Hide resolved
| var environment = app.getEnvironment(); | ||
| assertThat(environment.getActiveProfiles()).hasSize(1).contains("test"); | ||
| assertThat(environment.getProperty("steve.ocpp.auto-register-unknown-stations")) | ||
| .isEqualTo("true"); |
There was a problem hiding this comment.
🧩 Analysis chain
Ensure test override sets auto‑register property to true.
This assertion will fail unless tests set steve.ocpp.auto-register-unknown-stations=true.
🏁 Script executed:
#!/bin/bash
# Verify test override exists
rg -n -g '!**/target/**' -P --type=yaml 'steve:\s*\n\s*ocpp:\s*\n\s*auto-register-unknown-stations:\s*true' || true
rg -n -P --type=properties '^steve\.ocpp\.auto-register-unknown-stations\s*=\s*true$' || true
fd -HI 'application-test.*\.(ya?ml|properties)' || trueLength of output: 229
🏁 Script executed:
#!/bin/bash
# Display test configuration to check for ocpp auto-register override
sed -n '1,200p' steve/src/test/resources/application-test.ymlLength of output: 127
Add test override for auto-register-unknown-stations
The application-test.yml (steve/src/test/resources/application-test.yml) doesn’t set steve.ocpp.auto-register-unknown-stations, so the assertion in StressTest.java will fail. Add the following under the root of that file:
steve:
ocpp:
auto-register-unknown-stations: true🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/StressTest.java around lines 74 to 77,
the test asserts that the property steve.ocpp.auto-register-unknown-stations
equals "true" but application-test.yml does not define this property; add the
property to steve/src/test/resources/application-test.yml at the root under
steve.ocpp with value true so the test environment picks it up and the assertion
passes.
| public void prepare() { | ||
| truncateTables(dslContext, steveProperties.getJooq().getSchemaSource()); | ||
| insertChargeBox(dslContext); | ||
| insertOcppIdTag(dslContext); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Verify SteveProperties.jooq.schemaSource is the intended property.
Double‑check that getJooq().getSchemaSource() maps to the generated schema name used by jOOQ meta; some configs expose it as “schema”. If misaligned, truncation will fail to find the schema.
🏁 Script executed:
#!/bin/bash
# Inspect SteveProperties and usages of 'schemaSource' vs 'schema'
rg -nP "class\s+SteveProperties\b" -C3 --type=java
rg -nP "schemaSource\b" -n -C2
rg -nP "getSchemaSource\s*\(" -n -C2
rg -nP "steve\.jooq\." -n -C2 --type=java
rg -nP "steve:\s*jooq:" -n -C3 --type=yaml -g '!**/target/**'Length of output: 14516
Use the runtime schema (Jooq.schema) instead of schemaSource in __DatabasePreparer__.
In both prepare() and cleanUp(), replace
truncateTables(dslContext, steveProperties.getJooq().getSchemaSource());with something like
String schema = Optional.ofNullable(steveProperties.getJooq().getSchema())
.orElse(steveProperties.getJooq().getSchemaSource());
truncateTables(dslContext, schema);so truncation targets the actual database schema.
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java around
lines 74 to 78, the truncateTables call uses
steveProperties.getJooq().getSchemaSource() instead of the runtime schema;
update both prepare() and cleanUp() so you compute String schema =
Optional.ofNullable(steveProperties.getJooq().getSchema()).orElse(steveProperties.getJooq().getSchemaSource())
and pass that schema to truncateTables(dslContext, schema) so truncation targets
the actual database schema at runtime.
df893a3 to
f7b7018
Compare
b450472 to
fae552f
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
Dockerfile (1)
73-75: Healthcheck path likely wrong; probes “/” instead of a known healthy endpointWith server.servlet.context-path=/steve and Boot, / may 404. Prefer /actuator/health (if Actuator enabled) or /steve/.
Apply one of:
-HEALTHCHECK --interval=10s --timeout=3s --retries=20 \ - CMD curl -fsS "http://127.0.0.1:${PORT}/" || exit 1 +HEALTHCHECK --interval=10s --timeout=3s --retries=20 \ + CMD curl -fsS "http://127.0.0.1:${PORT}/actuator/health" || exit 1or:
- CMD curl -fsS "http://127.0.0.1:${PORT}/" || exit 1 + CMD curl -fsS "http://127.0.0.1:${PORT}/steve/" || exit 1steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java (1)
89-97: Use URI.resolve instead of string concatenation
Replace theconnectionPathString field with aURI connectionUribuilt via a helper that handles trailing-slash logic and encodes the segment. InOcppJsonChargePoint:
- Change
private final String connectionPath;
to
private final URI connectionUri;- In the constructor, replace
this.connectionPath = pathPrefix + chargeBoxId;
with
this.connectionUri = resolvePath(pathPrefix, chargeBoxId);- In
start(), replace
new URI(connectionPath)
with
connectionUri- Add a static helper, e.g.:
private static URI resolvePath(URI base, String segment) { String s = segment.startsWith("/") ? segment.substring(1) : segment; String sep = base.getPath() != null && base.getPath().endsWith("/") ? "" : "/"; return base.resolve(sep + s); }steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java (1)
191-193: Guard against NPE in afterRepeatIf beforeRepeat fails, threadLocalChargePoint may be null; close() will NPE. Add a null check and clear the ThreadLocal.
- public void afterRepeat() { - threadLocalChargePoint.get().close(); - } + public void afterRepeat() { + var cp = threadLocalChargePoint.get(); + if (cp != null) { + try { cp.close(); } finally { threadLocalChargePoint.remove(); } + } + }steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
72-79: Bind jOOQ to Spring-managed transactions.Wrap the DataSource with
TransactionAwareDataSourceProxy; otherwise jOOQ may obtain connections outside the active transaction.+import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; @@ - .set(new DataSourceConnectionProvider( - // new org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy() - dataSource)) + .set(new DataSourceConnectionProvider( + new TransactionAwareDataSourceProxy(dataSource))) .set(settings);steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java (1)
149-169: Avoid logging full SOAP payloads in production; gate LoggingFeature and limit sizePayloads can contain PII (e.g., idTags). Make logging opt-in and cap size.
Option A (property-gated):
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ - @Bean + @Bean + @ConditionalOnProperty(prefix = "steve.logging.soap", name = "enabled", havingValue = "true") public LoggingFeature loggingFeature() { var feature = new LoggingFeature(); + feature.setLimit(64 * 1024); // cap payload size feature.setSender(new Slf4jEventSender() {Option B (dev-only):
- @Bean + @Bean + @org.springframework.context.annotation.Profile("dev") public LoggingFeature loggingFeature() {Ensure docs mention how to enable when needed. Based on learnings.
🧹 Nitpick comments (39)
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java (2)
30-31: Avoid confusion with Enum.name(): prefer a more specific field (e.g., wireName) and add a lookupRename the field to better reflect intent and reduce confusion with Enum.name(). Also consider a lookup helper for parsing.
- private final String name; + private final String wireName;Add the following helper (optional but useful for mapping/parsing):
// place below the field public static TransactionStopEventActor fromWireName(String value) { for (var a : values()) { if (a.getWireName().equals(value)) return a; } throw new IllegalArgumentException("Unknown TransactionStopEventActor: " + value); }If JSON should use the wire value, annotate the getter with @jsonvalue (and add the Jackson import) or provide a custom serializer.
27-28: Annotate enum for JSON serialization
Jackson will still serialize this enum using itsname()("STATION"/"MANUAL"). If you intend to expose the lowercase literals (“station”/“manual”), add@JsonValuetogetName()(or overridetoString()) inTransactionStopEventActor.java. The JOOQ mapping already usesactor.getName()withlookupLiteral(…), so no further DB-binding changes are required.steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (2)
41-47: Bean lifecycle: cached log-path list may go stale after startupWith @component, the constructor caches paths once. If logging appenders/config change at runtime, the list won’t refresh. Consider computing on demand inside getPath() (or refreshing on a context/logback reconfig event). If the intent is “snapshot at boot,” please confirm and add a short comment.
93-101: Deduplicate paths, harden against invalid paths, and return unmodifiable listPrevents duplicate entries (multiple appenders pointing at the same file), guards against odd InvalidPathException, and avoids accidental mutation.
- var fileNameList = new ArrayList<Path>(); + var filePaths = new java.util.LinkedHashSet<Path>(); while (appenderIterator.hasNext()) { var appender = appenderIterator.next(); var fileName = extractFileName(appender); if (fileName != null) { - fileNameList.add(Path.of(fileName)); + try { + filePaths.add(Path.of(fileName)); + } catch (java.nio.file.InvalidPathException e) { + log.debug("Skipping invalid log file path: {}", fileName, e); + } } } - return fileNameList; + return List.copyOf(filePaths);steve/src/main/resources/application.yml (1)
20-20: Prefer utf8mb4 for MySQL/Connector/Jutf8 maps to MySQL’s legacy utf8mb3. Use utf8mb4 to match the DB creation example and avoid truncation.
Apply:
- characterEncoding: utf8 + characterEncoding: utf8mb4steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java (1)
42-53: Normalize path joins to avoid accidental “//” or missing “/”Direct string concatenation is brittle if mappings change trailing slashes.
Apply a small helper:
public class EndpointInfo { @@ - public EndpointInfo(SteveProperties steveProperties) { - this.webInterface = new ItemsWithInfo( - "Access the web interface using", steveProperties.getPaths().getManagerMapping() + "/home"); - this.ocppSoap = new ItemsWithInfo( - "SOAP endpoint for OCPP", - steveProperties.getPaths().getSoapMapping() - + steveProperties.getPaths().getRouterEndpointPath()); - this.ocppWebSocket = new ItemsWithInfo( - "WebSocket/JSON endpoint for OCPP", - steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/(chargeBoxId)"); - } + public EndpointInfo(SteveProperties steveProperties) { + this.webInterface = new ItemsWithInfo( + "Access the web interface using", + joinPaths(steveProperties.getPaths().getManagerMapping(), "home")); + this.ocppSoap = new ItemsWithInfo( + "SOAP endpoint for OCPP", + joinPaths(steveProperties.getPaths().getSoapMapping(), + steveProperties.getPaths().getRouterEndpointPath())); + this.ocppWebSocket = new ItemsWithInfo( + "WebSocket/JSON endpoint for OCPP", + joinPaths(steveProperties.getPaths().getWebsocketMapping(), + steveProperties.getPaths().getRouterEndpointPath(), "(chargeBoxId)")); + } + + private static String joinPaths(String... parts) { + String out = ""; + for (String p : parts) { + if (p == null || p.isEmpty()) continue; + if (out.endsWith("/")) out = out.substring(0, out.length() - 1); + if (!p.startsWith("/")) p = "/" + p; + out += p; + } + return out; + }steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java (1)
37-37: ChargeBoxId annotation validator update
Verified no remaining collection-level usages of@ChargeBoxId. Consider adding@Documentedto the annotation for clarity:+ import java.lang.annotation.Documented; @@ - @Constraint(validatedBy = ChargeBoxIdValidator.class) + @Documented + @Constraint(validatedBy = ChargeBoxIdValidator.class)steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (1)
42-51: Timezone default in static initializer — optional alignment with JSON serializationSetting JVM default TZ to UTC here is fine. Optionally also set Jackson’s timezone via config (e.g.,
spring.jackson.time-zone: UTC) to ensure consistent JSON timestamps across services, if not already inapplication.yml. Based on learnings.steve/src/main/resources/logback-spring.xml (1)
31-35: Clarify prod appender selection to avoid misconfiguration.Root in prod uses ${APPENDER} with default “CONSOLE”. If not overridden, prod logs won’t roll. Either default prod to FILE explicitly or validate allowed values.
Option A (explicit prod FILE):
<springProfile name="default,prod"> - <root level="${ROOT_LEVEL}"> - <appender-ref ref="${APPENDER}"/> - </root> + <root level="${ROOT_LEVEL}"> + <appender-ref ref="FILE"/> + </root> </springProfile>Option B (keep property but validate):
- Document accepted values: CONSOLE|FILE.
- Consider Logback conditional
<if>to fall back to FILE in prod if value is invalid.Also applies to: 37-48
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
101-108: Optional: set CXF client timeouts to avoid hanging tests.Default JAX‑WS/CXF client timeouts are infinite. Consider setting reasonable connect/read timeouts on the conduit for test stability.
steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
269-275: Nit: avoid recomputingChargePointStatus.values()in loop.Cache the array once for clarity and micro-efficiency.
- for (int i = 0; i < ChargePointStatus.values().length; i++) { - var status = ChargePointStatus.values()[i]; + var statuses = ChargePointStatus.values(); + for (int i = 0; i < statuses.length; i++) { + var status = statuses[i]; timeStatusMap.put(startingTime.plusMinutes(i), status); }steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java (1)
27-27: Good alignment with new 2‑arg API; consider parameterizing to reduce duplication.Introduce a @ParameterizedTest over inputs/expected outputs to keep this suite compact and easier to extend.
Also applies to: 32-32, 39-39, 46-46, 54-54, 60-60, 67-67, 75-75, 82-82, 89-89, 95-95
steve-ocpp-transport-soap/pom.xml (3)
18-21: jakarta.servlet-api without an explicit version — verify it’s managed.If not managed by steve-parent or an imported BOM, Maven will fail. Either add a version aligned to your target servlet level or keep relying on management if present.
Example (adjust to your managed version if any):
<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> + <version>6.0.0</version> <!-- or 6.1.x, matching your runtime/BOM --> <scope>provided</scope> </dependency>
23-32: CXF starter version and container dependency — confirm version management and server presence.
- No explicit version for cxf-spring-boot-starter-jaxws. Ensure it’s managed by a BOM/parent; otherwise pin it.
- Excluding spring-boot-starter-tomcat: confirm another web server is brought in (Jetty/Undertow) or that you deploy as WAR to an external container; otherwise the app may lack an HTTP runtime.
Options:
- Import CXF BOM in dependencyManagement to manage CXF artifacts; or
- Pin the starter explicitly:
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> + <version>${cxf.version}</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
34-36: cxf-rt-features-logging version alignment.If you keep this explicit dependency, ensure its version is managed (via CXF BOM) or pin it to match the starter’s version to avoid classpath skew.
Example:
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-features-logging</artifactId> + <version>${cxf.version}</version> </dependency>steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (1)
91-94: Switch to SteveProperties aligns with the new config model.Behavior unchanged except sourcing version from steveProperties; looks correct.
If desired, make this bean conditional (e.g., disable via property) to avoid a startup-time network check on certain deployments.
steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java (1)
111-121: Connector indexing: confirm intent (0..N vs 1..N)Loop runs 0..CONNECTOR_COUNT_PER_CHARGE_BOX inclusive, which emits N+1 notifications and includes connector 0, while later logic selects 1..N. If 0 is intentionally the EVSE/CP status, keep as-is; otherwise change to 1..N.
steve-core/pom.xml (1)
18-31: Don’t exclude spring-jcl from spring-webmvc under Spring Bootspring-jcl is the bridge Spring uses for logging; excluding it can degrade logging/init. Keep the commons-logging exclusion, remove the spring-jcl exclusion.
<exclusions> <!-- http://docs.spring.io/spring/docs/current/spring-framework-reference/html/overview.html#overview-not-using-commons-logging --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> - <exclusion> - <groupId>org.springframework</groupId> - <artifactId>spring-jcl</artifactId> - </exclusion> </exclusions>steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (2)
55-58: Verify mapping patterns align with property semanticsIf getRootMapping() may be a path prefix (e.g., "/api"), DispatcherServlet usually expects "/api/*" rather than "/api/". Confirm values include the correct wildcards; adjust addMapping() accordingly.
Also applies to: 72-73
49-58: Consider separate root and MVC contexts (optional)Best practice is a root ApplicationContext (services/config) plus a child DispatcherServlet context (web MVC). Using one shared context works but couples concerns and slows Servlet init.
steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (2)
58-65: Lazy-init the SOAP client per thread to avoid per-iteration re-creationReuse the port across repeats for the same thread; reduces overhead in stress runs.
- public void beforeRepeat() { - var client = getForOcpp16(soapPath); - tlClient.set(client); + public void beforeRepeat() { + var client = tlClient.get(); + if (client == null) { + client = getForOcpp16(soapPath); + tlClient.set(client); + }Optionally clear in afterRepeat():
- public void afterRepeat() {} + public void afterRepeat() { tlClient.remove(); }Also applies to: 80-83
95-104: Connector indexing: confirm 0..N inclusive vs 1..NSame as the JSON test: loop includes connector 0 and emits N+1 notifications. Verify intent; adjust if only 1..N is desired.
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
22-36: Optional: simplify test contextSteveProperties isn’t required to create a LocalValidatorFactoryBean; you can drop it from the test context for faster setup.
- ctx.register(SteveProperties.class);Also applies to: 59-67, 74-85
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java (3)
61-64: Validate wsSessionSelectStrategy at startup (fail-fast or default).If the property is missing/invalid, fromName(...) may throw or yield unexpected behavior. Prefer explicit validation with a clear error, or choose a documented default.
Would you like a small patch that validates and logs a clear configuration error?
129-144: Consider mapping “no session” to a domain-specific NotFound instead of InternalError.Throwing InternalError suggests a server fault; “no session for chargeBoxId” is likely a normal not-found condition. Consider a NotFound/ClientError or Optional.
166-168: Optional: return a stable snapshot from getACopy().ImmutableMap.copyOf(lookupTable) freezes keys but values are live Deques; consumers can see concurrent mutation. Return per-id copied Deques under lock for a consistent snapshot.
Example:
- return ImmutableMap.copyOf(lookupTable); + var b = ImmutableMap.<String, Deque<SessionContext>>builder(); + lookupTable.forEach((id, dq) -> { + var l = locks.get(id); + l.lock(); + try { + b.put(id, new ArrayDeque<>(dq)); + } finally { + l.unlock(); + } + }); + return b.build();steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java (1)
34-38: LGTM: ctor injection + empty-is-valid guard.Constructor-based DI and short-circuiting on null/empty align with Bean Validation patterns.
Minor: you can drop the Spring dependency here by using
value == null || value.isEmpty()instead ofCollectionUtils.isEmpty(value).Also applies to: 46-50
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
73-75: Avoid hard-coding SQLDialect (support MariaDB/MySQL variants).Detect dialect from configuration or JDBC metadata, or source it from properties.
Example:
- .set(SQLDialect.MYSQL) + .set(steveProperties.getJooq().getDialect() != null + ? SQLDialect.valueOf(steveProperties.getJooq().getDialect()) + : SQLDialect.MYSQL)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
64-66: Null-safety for path configuration (defensive).If any of the path properties are unset, concatenation may yield NPE or malformed mappings. Consider defaults or
requireNonNullElse.- var pathInfix = steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/"; + var paths = steveProperties.getPaths(); + var base = java.util.Objects.requireNonNullElse(paths.getWebsocketMapping(), "/websocket/"); + var router = java.util.Objects.requireNonNullElse(paths.getRouterEndpointPath(), "CentralSystemService"); + var pathInfix = (base.endsWith("/") ? base : base + "/") + router + "/";Also applies to: 67-73
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java (1)
68-74: Tiny readability nit: rename temp variable.
str1→trimmedto self-document intent.- String str1 = str.strip(); - if (!str1.equals(str)) { + var trimmed = str.strip(); + if (!trimmed.equals(str)) { return false; }steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java (1)
63-68: Typo/semantics:executiveLogginglikely meantexecuteLogging(jOOQ term)jOOQ uses “execute logging”. Consider renaming for clarity and consistency; update usages and YAML key accordingly.
- public static class Jooq { - private boolean executiveLogging; + public static class Jooq { + private boolean executeLogging; private String schemaSource; private @Nullable String schema; }If you keep the current field name, align documentation/YAML to avoid confusion.
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (1)
52-52: Prefer RANDOM_PORT + @LocalServerPort for isolationDEFINED_PORT can collide on CI or local runs. RANDOM_PORT with @LocalServerPort is safer.
-@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)Add:
// add near other fields @org.springframework.boot.test.web.server.LocalServerPort private int port;And let Helpers use this port (see next comment).
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (3)
149-154: Avoid NPE if chargeboxID isn’t foundpkMap.get(chargeboxID) may return null. Fail fast with a clear message.
- int pk = pkMap.get(chargeboxID); - return impl.getDetails(pk).orElseThrow(); + Integer pk = pkMap.get(chargeboxID); + if (pk == null) { + throw new IllegalArgumentException("Unknown chargeBoxId: " + chargeboxID); + } + return impl.getDetails(pk).orElseThrow();
156-183: Make truncation toggle dialect-aware (MySQL/MariaDB only)SET FOREIGN_KEY_CHECKS is MySQL/MariaDB specific. Guard it to keep tests portable.
- var internalCtx = DSL.using(configuration); - internalCtx.execute("SET FOREIGN_KEY_CHECKS=0"); - tables.forEach(t -> internalCtx.truncate(t).execute()); - internalCtx.execute("SET FOREIGN_KEY_CHECKS=1"); + var internalCtx = DSL.using(configuration); + boolean fkToggle = switch (internalCtx.dialect().family()) { + case MYSQL, MARIADB -> true; + default -> false; + }; + if (fkToggle) internalCtx.execute("SET FOREIGN_KEY_CHECKS=0"); + tables.forEach(t -> internalCtx.truncate(t).execute()); + if (fkToggle) internalCtx.execute("SET FOREIGN_KEY_CHECKS=1");To verify supported dialects in CI, please confirm which Testcontainers DBs are used in this module.
21-21: Drop Guava Sets in favor of JDK Set.of (Java 21 baseline)Simplifies deps in tests and removes an unnecessary import.
-import com.google.common.collect.Sets; +import java.util.Set; @@ - var skipList = Sets.newHashSet( + final var skipList = Set.of( SchemaVersion.SCHEMA_VERSION, Settings.SETTINGS, OcppTagActivity.OCPP_TAG_ACTIVITY, // only a view TRANSACTION // only a view );Also applies to: 157-163
pom.xml (2)
43-49: Align Lombok dependency with the configured annotation processor versionThe dependency currently has no version while the processor uses ${lombok.version}. Pin the dependency to the same version to avoid mismatch.
<properties> @@ <lombok.version>1.18.42</lombok.version> @@ <dependencies> <dependency> <groupId>org.jspecify</groupId> <artifactId>jspecify</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> + <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies>Also applies to: 145-156, 211-229
169-197: Enforce Java 21 in Maven Enforcer to match the build releasemaven-compiler uses release 21; tighten Enforcer from [17,) to [21,) for earlier failure.
- <requireJavaVersion> - <version>[17,)</version> - </requireJavaVersion> + <requireJavaVersion> + <version>[21,)</version> + </requireJavaVersion>steve/pom.xml (2)
99-107: Avoid redundant Jetty artifacts; rely on the starter for jetty-serverspring-boot-starter-jetty already brings jetty-server. Keep jetty-rewrite if needed, but drop the direct jetty-server to prevent version skew.
<!-- Jetty related dependencies --> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-rewrite</artifactId> </dependency>
184-201: application.yml filtering—excludekeystore.passwordfrom filtering
Detected placeholders:${steve.time-zone-id},${keystore.path}, and${keystore.password}. Sincekeystore.passwordis a secret, don’t bake it into the build via Maven filtering. Isolate it by removing it from the filtered resource (e.g., use a separate unfiltered<resource>for secrets) or shift its resolution to runtime via environment variables.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttfsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.icosteve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svgsteve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (69)
.github/workflows/main.yml(1 hunks)AGENTS.md(2 hunks)Dockerfile(1 hunks)pom.xml(6 hunks)steve-api/pom.xml(2 hunks)steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks)steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks)steve-core/pom.xml(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks)steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(6 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java(2 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks)steve-jooq/pom.xml(4 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(2 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java(5 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks)steve-ocpp-1-x/pom.xml(2 hunks)steve-ocpp-transport-soap/pom.xml(1 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks)steve-ocpp-transport-websocket/pom.xml(0 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(1 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(7 hunks)steve-ocpp/pom.xml(0 hunks)steve-ui-jsp/pom.xml(2 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks)steve/README.md(2 hunks)steve/pom.xml(6 hunks)steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks)steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks)steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks)steve/src/main/resources/application-dev.yml(1 hunks)steve/src/main/resources/application.yml(1 hunks)steve/src/main/resources/config/logback.xml(0 hunks)steve/src/main/resources/config/main.properties(0 hunks)steve/src/main/resources/logback-spring.xml(1 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(8 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(5 hunks)steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(7 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java(1 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(7 hunks)steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks)steve/src/test/resources/application-test.yml(1 hunks)
💤 Files with no reviewable changes (12)
- steve/src/main/resources/config/logback.xml
- steve-ocpp-transport-websocket/pom.xml
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve/src/main/java/de/rwth/idsg/steve/Application.java
- steve-ocpp/pom.xml
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve/src/main/resources/config/main.properties
✅ Files skipped from review due to trivial changes (1)
- steve-jooq/pom.xml
🚧 Files skipped from review as they are similar to previous changes (13)
- steve/src/test/resources/application-test.yml
- .github/workflows/main.yml
- steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
- steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
- steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java
- steve/src/main/resources/application-dev.yml
- steve/README.md
- steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java
- steve-ui-jsp/pom.xml
- steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java
- steve-ocpp-1-x/pom.xml
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
- steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.javasteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.javasteve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.javaAGENTS.mdsteve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javaDockerfilesteve/src/main/resources/logback-spring.xmlpom.xmlsteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve/pom.xmlsteve/src/main/resources/application.yml
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
AGENTS.mdsteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javapom.xmlsteve/pom.xmlsteve/src/main/resources/application.yml
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
AGENTS.mdsteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java
📚 Learning: 2025-09-15T15:31:15.967Z
Learnt from: juherr
PR: juherr/evolve#91
File: steve-core/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java:125-129
Timestamp: 2025-09-15T15:31:15.967Z
Learning: In the steve-core OCPP project, JAXB bindings are customized in ocpp_binding.xml to map xs:dateTime XML types directly to java.time.OffsetDateTime using the JavaDateTimeConverter adapter, eliminating the need for XMLGregorianCalendar conversions that would normally be required in standard JAXB bindings.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
🧬 Code graph analysis (3)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
Slf4j(39-74)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(61-237)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java (1)
steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/UpdateTransactionParams.java (1)
Getter(30-45)
🪛 GitHub Actions: build and run tests
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
[warning] 27-27: MissingSummary: A summary line is required on public/protected Javadocs.
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
[warning] 75-75: NullAway: initializer method does not guarantee @nonnull field chargePointsService (line 69) is initialized along all control-flow paths.
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
| CMD curl -fsS "http://127.0.0.1:${PORT}/" || exit 1 | ||
|
|
||
| ENV JAVA_TOOL_OPTIONS="-Dserver.host=0.0.0.0 -Dhttp.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" | ||
| ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" |
There was a problem hiding this comment.
Do not single‑quote -D values; drop insecure TLS protocols; use Boot’s compression property
Single quotes become part of the property values (JDBC URL/protocols). Also prefer TLSv1.2/1.3 and server.compression.enabled.
Apply:
-ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false"
+ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url=jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols=TLSv1.2,TLSv1.3 -Dserver.compression.enabled=false"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 -Dserver.port=${PORT} -Ddb.jdbc.url='jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC' -Ddb.schema=${DB_DATABASE} -Ddb.user=${DB_USER} -Ddb.password=${DB_PASSWORD} -Djdk.tls.client.protocols='TLSv1,TLSv1.1,TLSv1.2' -Dserver.gzip.enabled=false" | |
| ENV JAVA_TOOL_OPTIONS="-Dserver.address=0.0.0.0 \ | |
| -Dserver.port=${PORT} \ | |
| -Ddb.jdbc.url=jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}?sslMode=PREFERRED&serverTimezone=UTC \ | |
| -Ddb.schema=${DB_DATABASE} \ | |
| -Ddb.user=${DB_USER} \ | |
| -Ddb.password=${DB_PASSWORD} \ | |
| -Djdk.tls.client.protocols=TLSv1.2,TLSv1.3 \ | |
| -Dserver.compression.enabled=false" |
🤖 Prompt for AI Agents
In Dockerfile around line 77, the ENV line incorrectly wraps -D values in single
quotes (which become part of the value), exposes insecure TLS protocols, and
uses the deprecated Boot property server.gzip.enabled; remove the single quotes
around the JDBC URL and the tls protocols value so the actual values are passed,
restrict protocols to TLSv1.2,TLSv1.3, and replace server.gzip.enabled=false
with Spring Boot’s server.compression.enabled=false; ensure the entire ENV
assignment preserves correct shell quoting so variables like ${PORT},
${DB_HOST}, ${DB_PORT}, ${DB_DATABASE}, ${DB_USER}, and ${DB_PASSWORD} are
expanded correctly.
| @Mock | ||
| private ChargePointsService chargePointsService; | ||
|
|
||
| private MockMvcTester mockMvc; | ||
|
|
||
| @BeforeEach | ||
| public void setup() { | ||
| mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService))); | ||
| public void setUp() { | ||
| var ctx = new AnnotationConfigApplicationContext(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Initialize chargePointsService in setUp to satisfy NullAway
@mock with MockitoExtension isn’t recognized by NullAway for field init. Assign explicitly in setUp before using it.
@BeforeEach
- public void setUp() {
+ public void setUp() {
+ this.chargePointsService = org.mockito.Mockito.mock(ChargePointsService.class);
var ctx = new AnnotationConfigApplicationContext();
ctx.register(ValidationConfig.class);
- ctx.register(SteveProperties.class);
ctx.refresh();
var validator = ctx.getBean(Validator.class);
mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService))
.setValidator(validator));
}Please re-run the build; the NullAway warning at Line 69 should disappear.
Also applies to: 81-85
🏁 Script executed:
#!/bin/bash
set -e
echo "=== Imports & Class Header ==="
sed -n '1,60p' steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
echo "=== @ExtendWith Occurrences ==="
grep -Rn '@ExtendWith' steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java || echo "No @ExtendWith found"
echo "=== setUp Method Body ==="
sed -n '61,120p' steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javaLength of output: 5099
Open mocks in setUp to satisfy NullAway and initialize @Mock fields
Add a call to MockitoAnnotations.openMocks(this) at the start of setUp instead of manual mocking:
@BeforeEach
public void setUp() {
+ MockitoAnnotations.openMocks(this);
var ctx = new AnnotationConfigApplicationContext();
ctx.register(ValidationConfig.class);
ctx.refresh();
var validator = ctx.getBean(Validator.class);
mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService))
.setValidator(validator));
}Also add the import:
import org.mockito.MockitoAnnotations;This ensures the @Mock field is properly initialized and silences NullAway warnings.
🧰 Tools
🪛 GitHub Actions: build and run tests
[warning] 75-75: NullAway: initializer method does not guarantee @nonnull field chargePointsService (line 69) is initialized along all control-flow paths.
🤖 Prompt for AI Agents
In
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java
around lines 69 to 76, the @Mock fields are not initialized causing NullAway
warnings; add a call to MockitoAnnotations.openMocks(this) as the first
statement inside setUp() and add the import for org.mockito.MockitoAnnotations
so Mockito will initialize the @Mock-annotated fields before tests run.
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-mail</artifactId> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>org.hibernate.validator</groupId> | ||
| <artifactId>hibernate-validator</artifactId> | ||
| <groupId>jakarta.mail</groupId> | ||
| <artifactId>jakarta.mail-api</artifactId> | ||
| </dependency> |
There was a problem hiding this comment.
Remove duplicate Jakarta Mail API (provided by starter-mail)
spring-boot-starter-mail already brings jakarta.mail-api; keeping both risks classpath duplication/version skew. Drop the direct jakarta.mail-api dependency.
<dependency>
- <groupId>jakarta.mail</groupId>
- <artifactId>jakarta.mail-api</artifactId>
- </dependency>
+ <!-- removed: provided transitively by spring-boot-starter-mail -->
+ </dependency>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In steve-core/pom.xml around lines 41 to 48, there is a duplicate dependency
declaration for jakarta.mail:jakarta.mail-api alongside
spring-boot-starter-mail; remove the explicit jakarta.mail-api dependency entry
so the project relies on the transitive jakarta.mail-api provided by
spring-boot-starter-mail to avoid classpath duplication and version skew,
leaving only the spring-boot-starter-mail dependency block.
| @Autowired | ||
| public ChargeBoxIdValidator(SteveProperties steveProperties) { | ||
| this(steveProperties.getOcpp().getChargeBoxIdValidationRegex()); | ||
| } | ||
|
|
||
| // Default constructor for Hibernate Validator | ||
| // Spring will inject the value from properties | ||
| // And then HV will call `initialize` | ||
| public ChargeBoxIdValidator() { | ||
| initialize(null); | ||
| public ChargeBoxIdValidator(String regexFromConfig) { | ||
| this.pattern = Pattern.compile(Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig); | ||
| } |
There was a problem hiding this comment.
Constructor can NPE when properties subtree is missing.
steveProperties.getOcpp() may be null in some profiles; dereferencing it NPEs before reaching the null-safe constructor. Make the autowired ctor null‑safe.
- @Autowired
- public ChargeBoxIdValidator(SteveProperties steveProperties) {
- this(steveProperties.getOcpp().getChargeBoxIdValidationRegex());
- }
+ @Autowired
+ public ChargeBoxIdValidator(SteveProperties steveProperties) {
+ var regex = (steveProperties != null && steveProperties.getOcpp() != null)
+ ? steveProperties.getOcpp().getChargeBoxIdValidationRegex()
+ : null;
+ this(regex);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Autowired | |
| public ChargeBoxIdValidator(SteveProperties steveProperties) { | |
| this(steveProperties.getOcpp().getChargeBoxIdValidationRegex()); | |
| } | |
| // Default constructor for Hibernate Validator | |
| // Spring will inject the value from properties | |
| // And then HV will call `initialize` | |
| public ChargeBoxIdValidator() { | |
| initialize(null); | |
| public ChargeBoxIdValidator(String regexFromConfig) { | |
| this.pattern = Pattern.compile(Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig); | |
| } | |
| @Autowired | |
| public ChargeBoxIdValidator(SteveProperties steveProperties) { | |
| var regex = (steveProperties != null && steveProperties.getOcpp() != null) | |
| ? steveProperties.getOcpp().getChargeBoxIdValidationRegex() | |
| : null; | |
| this(regex); | |
| } | |
| public ChargeBoxIdValidator(String regexFromConfig) { | |
| this.pattern = Pattern.compile(Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig); | |
| } |
🤖 Prompt for AI Agents
In
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
around lines 41 to 48, the @Autowired constructor directly calls
steveProperties.getOcpp() which can be null and cause an NPE; change it to
safely retrieve the regex by first checking if steveProperties is null or
steveProperties.getOcpp() is null and then pass either the configured regex or
null/empty to the delegated constructor so the null‑safe constructor can choose
the default REGEX (i.e., compute a local String regex = (steveProperties != null
&& steveProperties.getOcpp() != null) ?
steveProperties.getOcpp().getChargeBoxIdValidationRegex() : null; and call
this(regex)).
| url: @db.jdbc.url@ | ||
| username: @db.user@ | ||
| password: @db.password@ |
There was a problem hiding this comment.
YAML won’t parse: quote all @...@ Maven tokens
Unquoted scalars starting with @ are invalid YAML; Boot will fail to start before filtering in many contexts (IDE, tests).
Apply:
- url: @db.jdbc.url@
- username: @db.user@
- password: @db.password@
+ url: "@db.jdbc.url@"
+ username: "@db.user@"
+ password: "@db.password@"
@@
-steve:
- version: @project.version@
+steve:
+ version: "@project.version@"
@@
- schema-source: @db.schema@
+ schema-source: "@db.schema@"Also applies to: 51-51, 55-55
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
🤖 Prompt for AI Agents
In steve/src/main/resources/application.yml around lines 7-9 (also apply same
change at lines 51 and 55), the Maven token scalars like @db.jdbc.url@,
@db.user@ and @db.password@ are unquoted which is invalid YAML and will prevent
Spring Boot from parsing before Maven filtering; wrap each @...@ token in quotes
(e.g. "@db.jdbc.url@") so the YAML is valid while allowing Maven to replace the
tokens during resource filtering.
| @Override | ||
| public void beforeRepeat() { | ||
| threadLocalClient.set(getForOcpp16(path)); | ||
| threadLocalClient.set(client); |
There was a problem hiding this comment.
Critical: shared SOAP client assigned to all threads via ThreadLocal — not thread‑safe
threadLocalClient.set(client) assigns the same CentralSystemService instance to every thread. CXF/JAX‑WS client proxies are not guaranteed thread‑safe; message contexts and conduits can be mutated per call. Under load this can cause interleaved headers, timeouts, or sporadic 5xx/soap faults.
Create a per‑thread client and clean up the ThreadLocal:
@@
public void beforeRepeat() {
- threadLocalClient.set(client);
+ // Create an isolated client per thread to avoid sharing CXF proxy state
+ threadLocalClient.set(getForOcpp16(soapPath));
}
@@
- public void afterRepeat() {}
+ public void afterRepeat() {
+ // Prevent leaks of thread-local references after thread finishes
+ threadLocalClient.remove();
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| threadLocalClient.set(client); | |
| public void beforeRepeat() { | |
| // Create an isolated client per thread to avoid sharing CXF proxy state | |
| threadLocalClient.set(getForOcpp16(soapPath)); | |
| } | |
| public void afterRepeat() { | |
| // Prevent leaks of thread-local references after thread finishes | |
| threadLocalClient.remove(); | |
| } |
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java around line 90,
the code sets the same CentralSystemService instance into a ThreadLocal via
threadLocalClient.set(client), which shares a single CXF/JAX-WS proxy across
threads and is unsafe; change to create a dedicated client instance per thread
(e.g., use ThreadLocal.withInitial(() -> createNewCentralSystemServiceClient())
or instantiate a new proxy inside each worker thread) and ensure you call
threadLocalClient.remove() (or close/destroy the client) in a finally block or
on thread termination to avoid leaking resources and cross-thread mutation of
message contexts.
| for (var meterValue : meterValues) { | ||
| var sampledValues = meterValue.getSampledValue(); | ||
| assertThat(sampledValues).isEmpty(); | ||
| assertThat(sampledValues).isNotEmpty(); | ||
| var thisValueFound = false; | ||
| // and check, if it can be found in the DB | ||
| for (var values : details.getValues()) { | ||
| if (values.getValue().equals(sampledValues.get(0).getValue())) { | ||
| if (values.getValue().equals(sampledValues.getFirst().getValue())) { | ||
| thisValueFound = true; |
There was a problem hiding this comment.
Avoid Java 21-only List.getFirst() to keep Java 17 compatibility.
If the project builds on JDK 17 (common with Boot 3.x), getFirst() will not compile. Use index access; you already assert non-empty.
- var sampledValues = meterValue.getSampledValue();
- assertThat(sampledValues).isNotEmpty();
+ var sampledValues = meterValue.getSampledValue();
+ assertThat(sampledValues).isNotEmpty();
var thisValueFound = false;
// and check, if it can be found in the DB
for (var values : details.getValues()) {
- if (values.getValue().equals(sampledValues.getFirst().getValue())) {
+ if (values.getValue().equals(sampledValues.get(0).getValue())) {
thisValueFound = true;
break;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (var meterValue : meterValues) { | |
| var sampledValues = meterValue.getSampledValue(); | |
| assertThat(sampledValues).isEmpty(); | |
| assertThat(sampledValues).isNotEmpty(); | |
| var thisValueFound = false; | |
| // and check, if it can be found in the DB | |
| for (var values : details.getValues()) { | |
| if (values.getValue().equals(sampledValues.get(0).getValue())) { | |
| if (values.getValue().equals(sampledValues.getFirst().getValue())) { | |
| thisValueFound = true; | |
| for (var meterValue : meterValues) { | |
| var sampledValues = meterValue.getSampledValue(); | |
| assertThat(sampledValues).isNotEmpty(); | |
| var thisValueFound = false; | |
| // and check, if it can be found in the DB | |
| for (var values : details.getValues()) { | |
| if (values.getValue().equals(sampledValues.get(0).getValue())) { | |
| thisValueFound = true; | |
| break; | |
| } | |
| } |
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java around
lines 597-604, the test uses the Java 21-only list method getFirst() on
sampledValues which breaks Java 17 compatibility; replace that call by accessing
the first element by index (index 0) since you already asserted the list is not
empty, and compare its value the same way so the test compiles on JDK 17.
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java (1)
70-76: Missing connect/response timeouts — calls may hang despite commentOnly
connectionRequestTimeoutis set. WithoutconnectTimeoutandresponseTimeout, slow GitHub responses can block indefinitely, contradicting the stated intent.Apply:
- var requestConfig = - RequestConfig.custom().setConnectionRequestTimeout(timeout).build(); + var requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(timeout) // waiting for a connection from pool + .setConnectTimeout(timeout) // TCP connect timeout + .setResponseTimeout(timeout) // time to wait for the first byte / response + .build();Optionally, set GitHub API version header:
restTemplate.getInterceptors().add((request, body, execution) -> { request.getHeaders().add("User-Agent", userAgent); request.getHeaders().setAccept(List.of(MediaType.valueOf("application/vnd.github+json"))); + request.getHeaders().add("X-GitHub-Api-Version", "2022-11-28"); return execution.execute(request, body); });steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java (1)
104-113: Skip unpublished endpoints and log; avoid NPEs in map build.EndpointImpl.getServer() can be null if not published.
- private static Map<String, Server> initServerLookupMap(List<EndpointImpl> endpoints) { - var actualServers = new HashMap<String, Server>(); - for (var endpoint : endpoints) { - var server = endpoint.getServer(); - var serverNamespace = - server.getEndpoint().getEndpointInfo().getName().getNamespaceURI(); - actualServers.put(serverNamespace, server); - } - return actualServers; - } + private static Map<String, Server> initServerLookupMap(List<EndpointImpl> endpoints) { + var servers = new HashMap<String, Server>(); + for (var endpoint : endpoints) { + var server = endpoint.getServer(); + if (server == null) { + log.warn("Ignoring unpublished endpoint: {}", endpoint.getAddress()); + continue; + } + var ns = server.getEndpoint().getEndpointInfo().getName().getNamespaceURI(); + servers.put(ns, server); + } + return servers; + }steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java (1)
156-183: MaketruncateTablesruntime-aware and failure-safe
- Use
internalCtx.meta()to load schemas/tables at runtime instead ofDefaultCatalog- Wrap the truncate loop in
try/finallysoSET FOREIGN_KEY_CHECKS=1always runs on errors- Filter tables by name (e.g.
t.getName()) against askipNamesset rather than by object equality
🧹 Nitpick comments (34)
steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java (3)
254-257: Reservation branch: avoid extra round‑trip (optional).You already have connectorPkQuery above; instead of re-querying via getConnectorPkFromConnector, derive the scalar once from the existing select to save a DB round‑trip.
- var connectorPk = getConnectorPkFromConnector(ctx, p.getChargeBoxId(), p.getConnectorId()); + var connectorPk = connectorPkQuery.fetchOptional() + .map(Record1::value1) + .orElseThrow(() -> new SteveException.InternalError( + "No connectorPk for %s/%d".formatted(p.getChargeBoxId(), p.getConnectorId())));
369-380: Broaden insertConnectorStatus parameter type to reduce coupling.Accept Select<? extends Record1> instead of SelectConditionStep… to keep the helper generic for any single‑column select.
- private void insertConnectorStatus( - DSLContext ctx, - SelectConditionStep<Record1<Integer>> connectorPkQuery, - LocalDateTime timestamp, - TransactionStatusUpdate statusUpdate) { + private void insertConnectorStatus( + DSLContext ctx, + org.jooq.Select<? extends Record1<Integer>> connectorPkQuery, + LocalDateTime timestamp, + TransactionStatusUpdate statusUpdate) {You can optionally add
import org.jooq.Select;at the top for brevity.
486-491: lookupLiteral may return null — fail fast with a clear error (recommended).If actor.getName() doesn’t match the DB enum literal, lookupLiteral returns null and you’ll get a later constraint error. Throwing explicitly here makes the cause obvious and preserves existing failure flow (caught and recorded as “failed”).
- return TransactionStopEventActor.lookupLiteral(actor.getName()); + var mapped = TransactionStopEventActor.lookupLiteral(actor.getName()); + if (mapped == null) { + throw new IllegalArgumentException("Unknown TransactionStopEventActor literal: " + actor.getName()); + } + return mapped;Please confirm TRANSACTION_STOP.EVENT_ACTOR allows NULL; if not, this change improves error clarity without changing behavior.
steve/src/test/resources/application-test.yml (1)
1-5: Avoid hardcoded test DB name and credentials; prefer env-overrides/Testcontainers.Hardcoding jdbc URL and password can break dev/CI environments and risks accidental reuse. Use property placeholders with env defaults or a Testcontainers-based test profile.
Apply this change:
spring: datasource: - url: jdbc:mysql://localhost:3306/stevedb_test_2aa6a783d47d - username: steve - password: changeme + url: ${TEST_DB_URL:jdbc:mysql://localhost:3306/stevedb_test} + username: ${TEST_DB_USERNAME:steve} + password: ${TEST_DB_PASSWORD:} +# Optional: explicitly set driver if auto-detection is an issue +# driver-class-name: com.mysql.cj.jdbc.DriverIf you rely on jOOQ/Flyway during tests, confirm the DB is reachable in CI. Based on learnings.
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (2)
59-73: Verify servlet/filter URL patterns; guard against double slashes or trailing wildcards.String concatenations assume certain trailing slashes. If
soapMappingalready ends with/*orrootMappingends with/*, resulting patterns could be invalid or duplicated.Consider normalizing:
- cxf.addMapping(props.getPaths().getSoapMapping() + "/*"); + cxf.addMapping(normalizeSuffix(props.getPaths().getSoapMapping(), "/*")); ... - .addMappingForUrlPatterns(..., props.getPaths().getRootMapping() + "*"); + .addMappingForUrlPatterns(..., normalizeSuffix(props.getPaths().getRootMapping(), "*"));Where
normalizeSuffix(path, suffix)appendssuffixonly when not already present.
45-73: Alternative: Prefer SpringBootServletInitializer/ServletContextInitializer with Boot.Given the Boot migration, consider replacing manual WebApplicationInitializer wiring with:
SpringBootServletInitializerfor WAR deployment, or- Register
ServletContextInitializerbeans and let Boot auto-configure DispatcherServlet, Security filter, and CXF starter.This reduces custom init complexity and aligns with Boot 3.5 conventions.
steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java (2)
41-47: Consider lazy initialization to avoid early logging-setup timing issues.As a Spring
@Component, the constructor runs at context creation. If logging appenders aren’t fully configured yet, discovery may miss file appenders. Initialize on first use or mark the bean@Lazy.Example:
-@Component +@Component +@org.springframework.context.annotation.Lazy public class LogFileRetriever { private final List<Path> logPathList; public LogFileRetriever() { - logPathList = getActiveLogFilePaths(); + this.logPathList = getActiveLogFilePaths(); }
89-102: Return an unmodifiable list; minor cleanup.Make the collected paths unmodifiable to prevent accidental mutation and small robustness for invalid paths.
Apply:
- private List<Path> getActiveLogFilePaths() { + private List<Path> getActiveLogFilePaths() { var logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); var appenderIterator = logger.iteratorForAppenders(); var fileNameList = new ArrayList<Path>(); while (appenderIterator.hasNext()) { var appender = appenderIterator.next(); var fileName = extractFileName(appender); if (fileName != null) { - fileNameList.add(Path.of(fileName)); + try { + fileNameList.add(Path.of(fileName)); + } catch (RuntimeException ex) { + log.debug("Skipping invalid log file path '{}': {}", fileName, ex.toString()); + } } } - return fileNameList; + return List.copyOf(fileNameList); }steve-core/pom.xml (1)
46-48: Consider removing redundant jakarta.mail-api dependency.The
spring-boot-starter-mailalready provides Jakarta Mail API implementation. The explicitjakarta.mail-apidependency may be redundant and could potentially cause version conflicts.Verify if the explicit API dependency is needed:
#!/bin/bash # Check if jakarta.mail-api is used directly in steve-core rg -n "jakarta\.mail" steve-core/src --type java -C2steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java (1)
31-31: Prefer explicit factory over null-cast; add empty-config coverageThe
(String) nullcast disambiguates the ctor, but it’s a bit brittle. Consider a small factory/helper for the “no-config” path (or a named static method) to improve readability. Also add a test for empty-string config to ensure parity with null.Example addition:
+ @Test + public void testEmptyRegexConfigBehavesLikeNull() { + var v = new ChargeBoxIdValidator(""); + assertThat(v.isValid("TesT")).isTrue(); + assertThat(v.isValid(" test")).isFalse(); + }steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java (1)
112-127: Guard against non-SemVer tags to avoid IllegalArgumentExceptionIf a release tag deviates from strict SemVer,
Version.parse(...)will throw and escape theRestClientExceptioncatch-path.Apply:
- var build = Version.parse(buildVersion); - var github = Version.parse(githubVersion); + Version build; + Version github; + try { + build = Version.parse(buildVersion); + github = Version.parse(githubVersion); + } catch (IllegalArgumentException e) { + log.warn("Unable to parse versions (build='{}', github='{}')", buildVersion, githubVersion, e); + return ReleaseReport.builder() + .moreRecent(false) + .githubVersion(githubVersion) + .downloadUrl(decideDownloadUrl(response)) + .htmlUrl(response.getHtmlUrl()) + .build(); + }steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java (1)
39-39: Consider rejecting duplicates if business rules require unique IDsIf duplicates should be rejected at input time, add
@UniqueElements.Example:
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import org.hibernate.validator.constraints.UniqueElements; - @NotEmpty private List<@NotNull @ChargeBoxId String> idList; + @NotEmpty + @UniqueElements + private List<@NotNull @ChargeBoxId String> idList;steve-ui-jsp/pom.xml (1)
129-132: jspc wiring looks correct; consider failing fast on JSP errorsPaths align with
src/main/webappand fragment undertarget/classes/WEB-INF. To surface issues earlier, enable fail-on-error if not default.Example:
<configuration> <webAppSourceDirectory>${basedir}/src/main/webapp</webAppSourceDirectory> <webXml>${basedir}/src/main/webapp/WEB-INF/web.xml</webXml> <webXmlFragment>${basedir}/target/classes/WEB-INF/web.xml-frag</webXmlFragment> + <failOnError>true</failOnError> </configuration>steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java (1)
91-97: ReleaseCheckService wired via SteveProperties: LGTM; consider centralizing HTTP settingsChange is consistent with the migration. Given
GithubReleaseCheckServicebuilds its own client, consider injecting a centrally configuredRestTemplate(timeouts, proxy, TLS) to avoid drift across services.steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java (1)
42-56: Avoid setting SpringDoc config via System properties; move to application.ymlStatic System.setProperty calls are order-sensitive and override environment. Prefer declarative config under springdoc.* in application.yml (paths-to-match, api-docs.path, swagger-ui.path, sorters, writer-with-order-by-keys). Keep this class focused on OpenAPI bean. I can draft the YAML block if helpful.
steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java (2)
35-36: Consider allowing PARAMETER target as wellAdding
PARAMETERenables annotating method parameters (e.g., controller endpoints) directly.-import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; @@ -@Target({TYPE_USE, FIELD, METHOD}) +@Target({TYPE_USE, FIELD, METHOD, PARAMETER})
35-38: Remove unused ChargeBoxIdListValidator
No field-level @ChargeBoxId on collections was found; the standalone ChargeBoxIdListValidator class is now dead code and can be removed.steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java (1)
53-59: Add a startup guard for insecure default admin passwordGiven application.yml ships a placeholder/default, fail fast if the resolved password is empty/"change-me"/"1234". Suggest an ApplicationRunner bean here.
@@ -import org.springframework.context.event.EventListener; +import org.springframework.context.event.EventListener; +import org.springframework.context.annotation.Bean; +import org.springframework.boot.ApplicationRunner; +import de.rwth.idsg.steve.config.SteveProperties; @@ public void logStartup() { @@ } + @Bean + ApplicationRunner securityPasswordGuard(SteveProperties props) { + return args -> { + var pwd = props.getAuth().getPassword(); + if (pwd == null || pwd.isBlank() || "change-me".equals(pwd) || "1234".equals(pwd)) { + throw new IllegalStateException("Refusing to start with insecure admin password"); + } + }; + }steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (2)
52-60: Name nit: “executive-logging” likely meant “execute-logging”Property name reads “executive-logging”; if intentional, ignore. If not, consider renaming in SteveProperties + YAML to “execute-logging”.
72-81: Make DataSource transaction-aware for jOOQWrap the DataSource to participate in Spring-managed transactions, or jOOQ may run outside tx boundaries.
+import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; @@ - .set(new DataSourceConnectionProvider( - // new org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy() - dataSource)) + .set(new DataSourceConnectionProvider( + new TransactionAwareDataSourceProxy(dataSource)))steve/src/main/resources/application.yml (1)
27-31: logging.appender is not a standard Spring Boot propertyUse
logging.file.name/logging.file.pathor a logback-spring.xml. As-is,logging.appender: FILEis ignored.Example:
logging: level: root: INFO - appender: FILE + file: + name: logs/steve.logsteve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
74-77: Prefer boolean property binding in the assertion.Avoid string comparison; read as Boolean with default.
- assertThat(environment.getProperty("steve.ocpp.auto-register-unknown-stations")) - .isEqualTo("true"); + assertThat(environment.getProperty( + "steve.ocpp.auto-register-unknown-stations", Boolean.class, false)) + .isTrue();steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
59-67: Avoid creating a Spring context just to get a Validator; remove leak and simplify setup.Build LocalValidatorFactoryBean directly; no need for AnnotationConfigApplicationContext here.
- @Configuration - static class ValidationConfig { - @Bean - public LocalValidatorFactoryBean validator(ApplicationContext applicationContext) { - var factory = new LocalValidatorFactoryBean(); - factory.setApplicationContext(applicationContext); - return factory; - } - } + // Validation can be wired without spinning up a Spring context for tests. @@ - public void setUp() { - var ctx = new AnnotationConfigApplicationContext(); - ctx.register(ValidationConfig.class); - ctx.register(SteveProperties.class); - ctx.refresh(); - - var validator = ctx.getBean(Validator.class); - - mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService)) - .setValidator(validator)); - } + public void setUp() { + var validator = new LocalValidatorFactoryBean(); + validator.afterPropertiesSet(); + mockMvc = buildMockMvc( + MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService)) + .setValidator(validator)); + }Also applies to: 75-85
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java (1)
125-133: Consider comma‑separated Sec‑WebSocket‑Protocol valuesSome clients send a single header like “ocpp1.6, ocpp1.5”. To be robust, split on commas per header entry and trim before matching.
- private @Nullable AbstractWebSocketEndpoint selectEndpoint(List<String> requestedProtocols) { - for (var requestedProtocol : requestedProtocols) { - for (var item : endpoints) { - if (item.getVersion().getValue().equals(requestedProtocol)) { - return item; - } - } - } - return null; - } + private @Nullable AbstractWebSocketEndpoint selectEndpoint(List<String> requestedProtocols) { + for (var headerVal : requestedProtocols) { + for (var token : headerVal.split(",")) { + var requested = token.trim(); + for (var item : endpoints) { + if (item.getVersion().getValue().equals(requested)) { + return item; + } + } + } + } + return null; + }steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
83-90: Guard against null ServerProperties.address in getWsPathHelpers.getWsPath uses serverProperties.getAddress().getHostName(); if address is null (Boot defaults), tests will NPE. Ensure test profile sets server.address or update Helpers to default to “localhost” when address is null.
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (1)
70-76: Same as JSON tests: ensure getSoapPath won’t NPE when address is unsetEither set server.address in test profile or make Helpers.getSoapPath default to “localhost” when ServerProperties.getAddress() is null.
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java (1)
64-66: Normalize slashes when composing pathInfix (defensive).Guard against accidental double slashes if properties carry trailing/leading slashes.
- var pathInfix = steveProperties.getPaths().getWebsocketMapping() - + steveProperties.getPaths().getRouterEndpointPath() + "/"; + var base = steveProperties.getPaths().getWebsocketMapping(); + var router = steveProperties.getPaths().getRouterEndpointPath(); + var pathInfix = (base.endsWith("/") ? base : base + "/") + + (router.startsWith("/") ? router.substring(1) : router) + + (router.endsWith("/") ? "" : "/");steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java (2)
154-156: Clear ThreadLocal to avoid lingering refs in long-lived pools.Remove the per-thread proxy after use to prevent accidental retention if threads are reused.
- public void afterRepeat() {} + public void afterRepeat() { + tlClient.remove(); + threadLocalChargeBoxId.remove(); + }
82-83: Optional: guard against null client in toRepeat.Defensive check prevents NPE if beforeRepeat fails early.
- var client = tlClient.get(); + var client = tlClient.get(); + if (client == null) { + throw new IllegalStateException("Client not initialized for thread"); + }steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java (1)
53-61: Add Security.allowedOriginPatterns to back WebSocket CORS config.Expose a list for allowed origin patterns; default to localhost for dev.
Add:
@@ private WebApi webApi = new WebApi(); private Jooq jooq = new Jooq(); + private Security security = new Security(); @@ public static class Ocpp { private boolean autoRegisterUnknownStations; private @Nullable String chargeBoxIdValidationRegex; private String wsSessionSelectStrategy; } + + @Data + public static class Security { + private java.util.List<String> allowedOriginPatterns = + java.util.List.of("http://localhost:*", "https://localhost:*"); + }steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java (1)
75-77: Defined port can collide in CI; consider RANDOM_PORT with @LocalServerPortUsing WebEnvironment.DEFINED_PORT risks collisions in parallel CI. If feasible, switch to RANDOM_PORT and resolve the port via @LocalServerPort (and adapt Helpers.getSoapPath to accept the actual port). Optional if your CI enforces single-threaded tests.
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
101-108: Optional: add client timeouts to avoid test hangsCXF proxies default to no/large timeouts; a stalled endpoint can hang tests. Consider setting reasonable connect/read timeouts.
// after creating proxy: var proxy = (T) f.create(); var client = org.apache.cxf.frontend.ClientProxy.getClient(proxy); var http = (org.apache.cxf.transport.http.HTTPConduit) client.getConduit(); var policy = new org.apache.cxf.transports.http.configuration.HTTPClientPolicy(); policy.setConnectionTimeout(10_000); policy.setReceiveTimeout(20_000); http.setClient(policy); return proxy;steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java (1)
64-71: Remove or re‑enable the commented Testcontainers block.Leaving this commented can confuse future readers. Either wire it back with @Container/@DynamicPropertySource or drop it.
steve/pom.xml (1)
99-108: Remove redundant Jetty direct dependencies when using Boot’s Jetty starter.spring-boot-starter-jetty already manages jetty-server and jetty-rewrite. The direct deps can cause version drift.
- <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-rewrite</artifactId> - </dependency>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (24)
steve-ui-jsp/src/main/webapp/static/css/DroidSerif.ttfis excluded by!**/*.ttfsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_18_b81900_40x40.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_diagonals-thick_20_666666_40x40.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_flat_10_000000_40x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_f6f6f6_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_100_fdf5ce_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_glass_65_ffffff_1x400.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_gloss-wave_35_f6a828_500x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_222222_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_228ef1_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ef8c08_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffd27a_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/css/images/ui-icons_ffffff_256x240.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/favicon.icois excluded by!**/*.icosteve-ui-jsp/src/main/webapp/static/images/info.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/logo.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/logo2.pngis excluded by!**/*.pngsteve-ui-jsp/src/main/webapp/static/images/offline-icon.svgis excluded by!**/*.svgsteve-ui-jsp/src/main/webapp/static/js/jquery-2.0.3.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/jquery-ui-timepicker-addon.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/jquery-ui.min.jsis excluded by!**/*.min.jssteve-ui-jsp/src/main/webapp/static/js/stupidtable.min.jsis excluded by!**/*.min.js
📒 Files selected for processing (69)
.github/workflows/main.yml(1 hunks)AGENTS.md(2 hunks)Dockerfile(1 hunks)pom.xml(6 hunks)steve-api/pom.xml(2 hunks)steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java(2 hunks)steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java(6 hunks)steve-core/pom.xml(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java(0 hunks)steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java(6 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.java(2 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.java(1 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java(3 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java(2 hunks)steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java(0 hunks)steve-jooq/pom.xml(4 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java(2 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java(5 hunks)steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java(0 hunks)steve-ocpp-1-x/pom.xml(2 hunks)steve-ocpp-transport-soap/pom.xml(1 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java(2 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java(4 hunks)steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java(4 hunks)steve-ocpp-transport-websocket/pom.xml(0 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java(1 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java(3 hunks)steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java(7 hunks)steve-ocpp/pom.xml(0 hunks)steve-ui-jsp/pom.xml(2 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java(3 hunks)steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java(2 hunks)steve/README.md(2 hunks)steve/pom.xml(6 hunks)steve/src/main/java/de/rwth/idsg/steve/Application.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/JettyServer.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java(1 hunks)steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java(4 hunks)steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java(2 hunks)steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java(0 hunks)steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java(0 hunks)steve/src/main/resources/application-dev.yml(1 hunks)steve/src/main/resources/application.yml(1 hunks)steve/src/main/resources/config/logback.xml(0 hunks)steve/src/main/resources/config/main.properties(0 hunks)steve/src/main/resources/logback-spring.xml(1 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java(8 hunks)steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java(5 hunks)steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java(24 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTest.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java(7 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java(3 hunks)steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java(1 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java(2 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java(1 hunks)steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java(7 hunks)steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java(1 hunks)steve/src/test/resources/application-test.yml(1 hunks)
💤 Files with no reviewable changes (12)
- steve-ocpp-transport-websocket/pom.xml
- steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
- steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java
- steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java
- steve-ocpp/pom.xml
- steve/src/main/resources/config/logback.xml
- steve-jooq/src/main/java/de/rwth/idsg/steve/utils/FlywayMigrationRunner.java
- steve/src/main/java/de/rwth/idsg/steve/JettyServer.java
- steve-core/src/main/java/de/rwth/idsg/steve/web/validation/SpringConstraintValidatorFactory.java
- steve/src/main/java/de/rwth/idsg/steve/Application.java
- steve/src/main/java/de/rwth/idsg/steve/utils/PropertiesFileLoader.java
- steve/src/main/resources/config/main.properties
🚧 Files skipped from review as they are similar to previous changes (15)
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
- .github/workflows/main.yml
- steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java
- AGENTS.md
- steve/src/main/resources/application-dev.yml
- Dockerfile
- steve-core/src/main/java/de/rwth/idsg/steve/repository/dto/TransactionStopEventActor.java
- steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java
- steve-core/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java
- steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java
- steve-ocpp-1-x/pom.xml
- steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java
- steve/src/main/resources/logback-spring.xml
- steve/README.md
- steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Java source code must be formatted using the Spotless Maven plugin (run spotless:apply/spotless:check before committing)
Files:
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.javasteve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.javasteve-ui-jsp/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.javasteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve-jooq/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/web/dto/ChargePointBatchInsertForm.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue72.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxId.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.javasteve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.javasteve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.javasteve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.javasteve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.javasteve/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.javasteve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.javasteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve/src/main/resources/config/main.properties : Default application configuration resides in steve/src/main/resources/config/main.properties; edit this file or override via command-line properties
Applied to files:
steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/config/WebConfig.javasteve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.javasteve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/java/de/rwth/idsg/steve/utils/Helpers.javasteve-core/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.javasteve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.javasteve/src/test/resources/application-test.ymlpom.xmlsteve/pom.xmlsteve/src/main/java/de/rwth/idsg/steve/SteveApplication.javasteve/src/main/resources/application.yml
📚 Learning: 2025-09-02T20:01:57.746Z
Learnt from: CR
PR: juherr/evolve#0
File: AGENTS.md:0-0
Timestamp: 2025-09-02T20:01:57.746Z
Learning: Applies to steve-jooq/src/main/resources/db/migration/**/*.sql : Place Flyway database migration scripts under steve-jooq/src/main/resources/db/migration
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve-jooq/pom.xml
📚 Learning: 2025-08-29T07:17:42.970Z
Learnt from: juherr
PR: juherr/evolve#62
File: steve/README.md:119-121
Timestamp: 2025-08-29T07:17:42.970Z
Learning: SteVe project uses jOOQ which requires live database connection during Maven build process to run Flyway migrations and generate Java code from database schema in the generate-sources phase.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.javasteve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.javasteve/src/test/java/de/rwth/idsg/steve/StressTest.javasteve/src/test/resources/application-test.ymlpom.xmlsteve/pom.xmlsteve-jooq/pom.xmlsteve/src/test/java/de/rwth/idsg/steve/ApplicationTest.javasteve/src/main/resources/application.yml
📚 Learning: 2025-09-15T15:31:15.967Z
Learnt from: juherr
PR: juherr/evolve#91
File: steve-core/src/main/java/de/rwth/idsg/steve/ocpp/task/SetChargingProfileTask.java:125-129
Timestamp: 2025-09-15T15:31:15.967Z
Learning: In the steve-core OCPP project, JAXB bindings are customized in ocpp_binding.xml to map xs:dateTime XML types directly to java.time.OffsetDateTime using the JavaDateTimeConverter adapter, eliminating the need for XMLGregorianCalendar conversions that would normally be required in standard JAXB bindings.
Applied to files:
steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
🧬 Code graph analysis (3)
steve/src/test/java/de/rwth/idsg/steve/StressTest.java (1)
steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
steve-api/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java (1)
steve-jooq/src/main/java/de/rwth/idsg/steve/jooq/config/JooqConfiguration.java (1)
Configuration(35-82)
steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java (3)
steve/src/main/java/de/rwth/idsg/steve/config/WebConfig.java (1)
Slf4j(39-74)steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java (1)
Slf4j(61-237)steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java (1)
Helpers(37-109)
🪛 GitHub Actions: build and run tests
steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
[warning] 30-30: [NullAway] initializer method does not guarantee @nonnull fields timeZoneId (line 55), steveVersion (line 56), profile (line 57) are initialized along all control-flow paths.
🪛 YAMLlint (1.37.1)
steve/src/main/resources/application.yml
[error] 7-7: syntax error: found character '@' that cannot start any token
(syntax)
| private String timeZoneId; | ||
| private String steveVersion; | ||
| private String profile; |
There was a problem hiding this comment.
Fix NullAway: annotate nullable or provide defaults for uninitialized fields.
These fields are bound at runtime; static analysis flags them. Either supply safe defaults or mark as @nullable. Minimal change shown:
- private String timeZoneId;
- private String steveVersion;
- private String profile;
+ private @Nullable String timeZoneId;
+ private @Nullable String steveVersion;
+ private @Nullable String profile;Optionally, add @validated and constraints if you want to enforce presence at startup.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private String timeZoneId; | |
| private String steveVersion; | |
| private String profile; | |
| private @Nullable String timeZoneId; | |
| private @Nullable String steveVersion; | |
| private @Nullable String profile; |
🤖 Prompt for AI Agents
In steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
around lines 55 to 57, three String fields (timeZoneId, steveVersion, profile)
are uninitialized and flagged by NullAway; annotate each field with a nullable
annotation (e.g., javax.annotation.Nullable or
org.jetbrains.annotations.Nullable) or initialize them to safe defaults (e.g.,
empty string) to satisfy static analysis; if you prefer runtime validation
instead, annotate the class with @Validated and add @NotBlank/@NotNull
constraints to the fields and ensure the appropriate validation
dependency/imports are present.
| <groupId>com.mysql</groupId> | ||
| <artifactId>mysql-connector-j</artifactId> | ||
| <version>${mysql.jdbc.version}</version> | ||
| <version>${mysql.version}</version> |
There was a problem hiding this comment.
🧩 Analysis chain
Verify consistency of property references.
The change from ${mysql.jdbc.version} to ${mysql.version} should be consistent across all Maven properties. Based on past review comments, these properties should now be defined in the parent POM.
Verify the property definitions are available:
Also applies to: 192-192, 230-230
🏁 Script executed:
#!/bin/bash
# Check if mysql.version, jooq.version, flyway.version are defined in parent
rg -n "mysql\.version|jooq\.version|flyway\.version" pom.xml -A1 -B1Length of output: 0
🏁 Script executed:
#!/bin/bash
# List all POMs to identify parent
find . -type f -name pom.xml
# Search for mysql.version, jooq.version, flyway.version definitions in each POM
rg -n "<(mysql\.version|jooq\.version|flyway\.version)>" -C1 -g 'pom.xml' .Length of output: 258
🏁 Script executed:
#!/bin/bash
# Search for property definitions of mysql.version, jooq.version, and flyway.version
rg -n "<mysql\.version>" -g '*.xml'
rg -n "<jooq\.version>" -g '*.xml'
rg -n "<flyway\.version>" -g '*.xml'Length of output: 0
Declare missing Maven properties in the parent POM.
The properties mysql.version, jooq.version, and flyway.version aren’t defined in any pom.xml; add them under <properties> in the root pom.xml so ${mysql.version} (and the others) can resolve.
🤖 Prompt for AI Agents
In steve-jooq/pom.xml around line 115, the build references ${mysql.version}
(and other modules reference ${jooq.version} and ${flyway.version}) but those
properties are not declared; open the root/parent pom.xml and add a <properties>
block declaring mysql.version, jooq.version, and flyway.version with the desired
version values (pick the project-approved versions), then re-run mvn to ensure
the placeholders resolve.
| /** | ||
| * See Spring docs: | ||
| * https://docs.spring.io/spring-framework/reference/web/websocket/server.html#websocket-server-runtime-configurationCheck failure[checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Check failure: [checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Line is longer than 120 characters (found 121).build and run tests / checkstyleView detailsCode has alerts. Press enter to view. | ||
| * | ||
| * Otherwise, defaults come from {@link WebSocketConstants} | ||
| */ |
There was a problem hiding this comment.
Fix Javadoc and remove pasted build output; wrap long URL.
Current block includes CI “Check failure...” text and exceeds 120 chars; this fails Checkstyle.
- /**
- * See Spring docs:
- * https://docs.spring.io/spring-framework/reference/web/websocket/server.html#websocket-server-runtime-configurationCheck failure[checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Check failure: [checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Line is longer than 120 characters (found 121).build and run tests / checkstyleView detailsCode has alerts. Press enter to view.
- * Otherwise, defaults come from {@link WebSocketConstants}
- */
+ /**
+ * See Spring docs:
+ * https://docs.spring.io/spring-framework/reference/web/websocket/server.html
+ * #websocket-server-runtime-configuration
+ * Otherwise, defaults come from {@link WebSocketConstants}.
+ */📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * See Spring docs: | |
| * https://docs.spring.io/spring-framework/reference/web/websocket/server.html#websocket-server-runtime-configurationCheck failure[checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Check failure: [checkstyle] src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java#L73 <com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck>Line is longer than 120 characters (found 121).build and run tests / checkstyleView detailsCode has alerts. Press enter to view. | |
| * | |
| * Otherwise, defaults come from {@link WebSocketConstants} | |
| */ | |
| /** | |
| * See Spring docs: | |
| * https://docs.spring.io/spring-framework/reference/web/websocket/server.html | |
| * #websocket-server-runtime-configuration | |
| * Otherwise, defaults come from {@link WebSocketConstants}. | |
| */ |
🤖 Prompt for AI Agents
In
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/config/OcppWebSocketConfiguration.java
around lines 79 to 83, the Javadoc contains pasted CI/build output and an
overlong URL that exceeds the 120-character checkstyle limit; remove the
extraneous "Check failure..." text and any build output, reformat the Javadoc so
the reference to the Spring docs is wrapped into multiple shorter lines (or use
{@code <a href="...">...}</code> if preferred) keeping each line under 120
chars, and ensure the remaining sentence about defaults referencing {@link
WebSocketConstants} remains properly formatted as Javadoc.
| public SessionContextStoreImpl(SteveProperties steveProperties) { | ||
| this.wsSessionSelectStrategy = | ||
| WsSessionSelectStrategyEnum.fromName(steveProperties.getOcpp().getWsSessionSelectStrategy()); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Validate or default the session selection strategy.
If the property is absent/invalid, fromName(...) may throw; consider a default or a clear error.
- Option A: default
this.wsSessionSelectStrategy =
WsSessionSelectStrategyEnum.fromName(
Objects.requireNonNullElse(steveProperties.getOcpp().getWsSessionSelectStrategy(), "ROUND_ROBIN"));- Option B: validate early with a helpful message.
Please confirm SteveProperties defines a non‑null default. If unsure, run:
🏁 Script executed:
#!/bin/bash
rg -n -C3 --type=java 'wsSessionSelectStrategy' steve | sed -n '1,200p'Length of output: 0
🏁 Script executed:
#!/bin/bash
# Locate the SteveProperties class
rg -n --type=java 'class SteveProperties' -g '*.java'
# Find where getOcpp() is defined to inspect nested Ocpp properties
rg -n 'getOcpp' -C3 --type=java -g '*.java'
# Search for any default for wsSessionSelectStrategy in properties classes
rg -n 'wsSessionSelectStrategy' -C5 --type=java .Length of output: 63822
Validate or default the session selection strategy
SteveProperties.wsSessionSelectStrategy has no default, so getWsSessionSelectStrategy() can return null or invalid and cause fromName(...) to throw at runtime. Provide an explicit default or pre-validate the value before calling fromName().
- Option A: default to ROUND_ROBIN
this.wsSessionSelectStrategy =
WsSessionSelectStrategyEnum.fromName(
Objects.requireNonNullElse(
steveProperties.getOcpp().getWsSessionSelectStrategy(),
"ROUND_ROBIN"));- Option B: validate early and fail fast
String strategy = steveProperties.getOcpp().getWsSessionSelectStrategy();
Preconditions.checkArgument(strategy != null, "wsSessionSelectStrategy must be set");
this.wsSessionSelectStrategy = WsSessionSelectStrategyEnum.fromName(strategy);🤖 Prompt for AI Agents
In
steve-ocpp-transport-websocket/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java
around lines 61 to 64, the call to WsSessionSelectStrategyEnum.fromName(...)
uses a value from SteveProperties that can be null or invalid and thus may throw
at runtime; change the constructor to either (A) supply a safe default when the
configured value is null (e.g. use Objects.requireNonNullElse(...,
"ROUND_ROBIN") or equivalent) before calling fromName, or (B) validate early and
fail fast by checking the string is non-null (and optionally matches allowed
names) and throw a clear IllegalArgumentException/Preconditions.checkArgument if
not set/invalid; implement one of these two approaches so fromName is never
called with a null or unexpected value.
| <resource> | ||
| <targetPath>.</targetPath> | ||
| <filtering>false</filtering> | ||
| <directory>src/main/webapp</directory> | ||
| <includes> | ||
| <include>static/**</include> | ||
| <include>WEB-INF/web.xml</include> | ||
| <include>WEB-INF/views/**</include> | ||
| </includes> | ||
| </resource> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Verify resource copy doesn’t duplicate WAR content or shadow packaging
Copying src/main/webapp/** into target/classes is fine for jspc, but can double-include assets if this module (or an aggregator WAR) also packages src/main/webapp via maven-war-plugin.
Run:
🏁 Script executed:
#!/bin/bash
# Check packaging and WAR plugin config in this module and parents
rg -nC2 '<packaging>\s*war\s*</packaging>|maven-war-plugin' pom.xml **/pom.xmlLength of output: 161
Prevent double-inclusion of src/main/webapp in WAR
This module uses <packaging>war</packaging>, so the default War plugin already includes src/main/webapp at the WAR root. Copying these files into target/classes via your <resource> block will include them again under WEB-INF/classes. Remove or isolate this resource declaration to only the JSPC execution, or configure the War plugin’s <webResources>/excludes to avoid duplicating assets.
🤖 Prompt for AI Agents
In steve-ui-jsp/pom.xml around lines 96 to 105, the <resource> block is copying
src/main/webapp into target/classes causing the webapp to be packaged twice;
remove this resource entry or restrict it so it only applies to the JSPC
execution (e.g., move it into a profile or execution-specific configuration), or
alternatively remove it and instead configure the maven-war-plugin with
<webResources> or <excludes> to prevent duplicating src/main/webapp in
WEB-INF/classes.
| var start = client.startTransaction( | ||
| new StartTransactionRequest() | ||
| .withConnectorId(connectorId) | ||
| .withIdTag(idTag) | ||
| .withTimestamp(startDateTime) | ||
| .withMeterStart(meterStart), | ||
| chargeBoxId); | ||
| assertThat(start).isNotNull(); |
There was a problem hiding this comment.
🧩 Analysis chain
Start/stop mismatch across the stress loop (start outside, stop inside).
startTransaction runs once before the loop, while stopTransaction is invoked inside toRepeat(). With REPEAT_COUNT_PER_THREAD > 1, subsequent iterations will attempt to stop an already‑stopped transaction or send MeterValues after stop. Consider:
- Move
startTransactionintobeforeRepeat()(ortoRepeat()) and store the transactionId in aThreadLocal, then stop within the same iteration; or - Keep
startoutside and movestopafter the loop (once), and only sendMeterValuesinside the loop.
Note: Using OffsetDateTime for timestamps is correct and matches the project’s JAXB customization that maps xs:dateTime to OffsetDateTime. Based on learnings.
🏁 Script executed:
#!/bin/bash
set -e
# Print top of Issue72.java for context
echo "=== Issue72.java (1-200) ==="
sed -n '1,200p' steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
echo -e "\n=== toRepeat occurrences ==="
rg -n "toRepeat" -C3 steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
echo -e "\n=== stopTransaction occurrences ==="
rg -n "stopTransaction" -C3 steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
echo -e "\n=== REPEAT_COUNT_PER_THREAD occurrences in Java ==="
rg -n "REPEAT_COUNT_PER_THREAD" -C1 --type javaLength of output: 7234
🏁 Script executed:
#!/bin/bash
set -e
echo "=== StressTester.test() implementation ==="
rg -n "test\\s*\\(" -C3 steve/src/test/java/de/rwth/idsg/steve/utils/StressTester.javaLength of output: 341
🏁 Script executed:
#!/bin/bash
# Print first 200 lines of StressTester.java to inspect loop logic
sed -n '1,200p' steve/src/test/java/de/rwth/idsg/steve/utils/StressTester.javaLength of output: 2289
Correct transactional flow in Issue72.java
startTransaction is called once before the stress loop but stopTransaction runs inside toRepeat(), so with REPEAT_COUNT_PER_THREAD = 50 you stop the same transaction repeatedly and send MeterValues after it’s stopped. Either move startTransaction into beforeRepeat()/toRepeat() so each iteration has its own start–stop pair, or keep the start outside and invoke stopTransaction in afterRepeat() (once after the loop).
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java around lines
73-80, the test calls startTransaction once before the stress loop but calls
stopTransaction inside toRepeat(), causing the same transaction to be stopped
repeatedly and MeterValues to be sent after stop; fix by making start/stop pair
per iteration or stop once after the loop: either move the startTransaction call
into beforeRepeat() or toRepeat() so each iteration creates its own transaction
and corresponding stopTransaction, or keep the single startTransaction outside
the loop and move stopTransaction into afterRepeat() so it is invoked only once;
also ensure MeterValues are sent only while the transaction is active and any
transactionId used in iterations is unique or correctly scoped.
| @Override | ||
| public void beforeRepeat() { | ||
| threadLocalClient.set(getForOcpp16(path)); | ||
| threadLocalClient.set(client); |
There was a problem hiding this comment.
Create a per‑thread client instance instead of reusing the same proxy.
Use a fresh proxy per thread to avoid concurrency issues.
Apply this diff:
- threadLocalClient.set(client);
+ // Create a fresh proxy per thread to avoid CXF proxy concurrency issues
+ threadLocalClient.set(getForOcpp16(soapPath));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| threadLocalClient.set(client); | |
| // Create a fresh proxy per thread to avoid CXF proxy concurrency issues | |
| threadLocalClient.set(getForOcpp16(soapPath)); |
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java around line 90,
the test currently sets a shared proxy client into threadLocalClient
(threadLocalClient.set(client)); instead create a fresh client/proxy instance
per thread to avoid concurrency issues: change the code to instantiate a new
client/proxy inside the thread before calling threadLocalClient.set(...) (or
initialize threadLocalClient with ThreadLocal.withInitial(...) returning a newly
created client), ensuring each thread uses its own independent client instance
and not a reused shared proxy.
| var faultedTime = timeStatusMap.lastEntry().getKey().plusMinutes(1); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
Java 21 API used: LinkedHashMap.lastEntry() breaks on Java 17
LinkedHashMap.lastEntry() is also Java 21+. Use an alternative that works on 17, e.g., track the last key during insertion or use an iterator over entrySet().
- var faultedTime = timeStatusMap.lastEntry().getKey().plusMinutes(1);
+ var it = timeStatusMap.entrySet().iterator();
+ java.util.Map.Entry<ZonedDateTime,?> last = null;
+ while (it.hasNext()) last = it.next();
+ var faultedTime = (last != null ? last.getKey() : startingTime).plusMinutes(1);🏁 Script executed:
rg -n "timeStatusMap" -C3 -t java .Length of output: 2975
Replace Java 21 API usage of LinkedHashMap.lastEntry() to support Java 17
LinkedHashMap.lastEntry() is only available since Java 21 (via SequencedMap) and won’t compile under Java 17—use entrySet() iteration (or track the last key during insertion) to retrieve the last key instead (e.g., in steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java:309). (docs.oracle.com)
🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java around
line 309, the code uses LinkedHashMap.lastEntry() which is Java 21-only and
fails to compile on Java 17; replace this call by obtaining the last entry via
iteration over the map's entrySet (or by tracking the last key during
insertion). Concretely, iterate the map's entrySet() to find the final Map.Entry
(or store the lastKey when filling the map) and then call
getKey().plusMinutes(1) on that key to compute faultedTime, ensuring no Java 21
APIs are used.
| var environment = app.getEnvironment(); | ||
| assertThat(environment.getActiveProfiles()).hasSize(1).contains("test"); | ||
| assertThat(environment.getProperty("steve.ocpp.auto-register-unknown-stations")) | ||
| .isEqualTo("true"); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
Verify test override for auto‑register property.
Assertion expects "true". Please ensure application‑test config sets it accordingly.
🏁 Script executed:
#!/bin/bash
# Check test overrides for the property
echo "Searching for steve.ocpp.auto-register-unknown-stations: true in test resources..."
rg -n -C2 -g '!**/target/**' -P --type=yaml 'steve:\s*\n\s*ocpp:\s*\n\s*auto-register-unknown-stations\s*:\s*true' steve/src/test/resources || true
rg -n -C2 -g '!**/target/**' -P --type=properties '^steve\.ocpp\.auto-register-unknown-stations\s*=\s*true$' steve/src/test/resources || trueLength of output: 117
🏁 Script executed:
#!/bin/bash
echo "Listing all test resource files:"
find steve/src/test/resources -type f -maxdepth 2
echo
echo "Searching for 'auto-register-unknown-stations' in test resources:"
rg -n 'auto-register-unknown-stations' -C2 steve/src/test/resources || trueLength of output: 145
Add missing test override for auto-register property
The application-test.yml under steve/src/test/resources does not define steve.ocpp.auto-register-unknown-stations. Add the following to ensure the test passes:
steve:
ocpp:
auto-register-unknown-stations: true🤖 Prompt for AI Agents
In steve/src/test/java/de/rwth/idsg/steve/StressTest.java around lines 74 to 78,
the test expects the property steve.ocpp.auto-register-unknown-stations to be
defined but application-test.yml in steve/src/test/resources does not set it;
update application-test.yml to include the YAML entry for
steve.ocpp.auto-register-unknown-stations set to true under the steve.ocpp
hierarchy so the environment property resolves and the assertion passes.
Backport of steve-community#1822
Summary by CodeRabbit
New Features
Changes
Tests