diff --git a/pom.xml b/pom.xml index 820205c66..2db17fc63 100644 --- a/pom.xml +++ b/pom.xml @@ -31,16 +31,15 @@ ${java.version} ${java.version} UTF-8 + 3.5.5 1.0.0 5.0.0 4.1.3 1.18.40 - 12.1.1 9.4.0 1.3.1 - 2.0.17 3.20.7 11.12.0 4.0.10 @@ -50,6 +49,13 @@ + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + de.rwth.idsg steve-core @@ -95,20 +101,6 @@ steve-ui-jsp ${project.version} - - org.springframework - spring-framework-bom - 6.2.11 - pom - import - - - org.springframework.security - spring-security-bom - 6.5.3 - pom - import - org.junit junit-bom @@ -123,20 +115,6 @@ pom import - - org.eclipse.jetty - jetty-bom - ${jetty.version} - pom - import - - - org.eclipse.jetty.ee10 - jetty-ee10-bom - ${jetty.version} - pom - import - org.hibernate.validator hibernate-validator @@ -152,16 +130,6 @@ jakarta.mail-api 2.1.4 - - org.slf4j - slf4j-api - 2.0.17 - - - ch.qos.logback - logback-classic - 1.5.18 - io.swagger.core.v3 swagger-annotations-jakarta @@ -225,6 +193,11 @@ + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + org.apache.maven.plugins maven-enforcer-plugin diff --git a/steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java b/steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java index e4e1e80a7..f91c09493 100644 --- a/steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java +++ b/steve-api/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java @@ -18,7 +18,6 @@ */ package de.rwth.idsg.steve.config; -import de.rwth.idsg.steve.SteveConfiguration; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; @@ -74,7 +73,7 @@ public class ApiDocsConfiguration { } @Bean - public OpenAPI apiDocs(SteveConfiguration config) { + public OpenAPI apiDocs(SteveProperties steveProperties) { var title = "SteVe REST API Documentation"; var securityName = "basicAuth"; @@ -91,7 +90,7 @@ public OpenAPI apiDocs(SteveConfiguration config) { .license(new License() .name("GPL-3.0") .url("https://github.com/steve-community/steve/blob/master/LICENSE.txt")) - .version(config.getSteveVersion())) + .version(steveProperties.getVersion())) // https://stackoverflow.com/a/68185254 .servers(List.of(new Server().url("/").description("Default Server URL"))) // define a security schema diff --git a/steve-core/pom.xml b/steve-core/pom.xml index 5da02874a..dafab6f32 100644 --- a/steve-core/pom.xml +++ b/steve-core/pom.xml @@ -10,6 +10,10 @@ steve-core + + org.springframework.boot + spring-boot-starter + de.rwth.idsg steve-ocpp-1-x @@ -17,17 +21,6 @@ org.springframework spring-webmvc - - - - commons-logging - commons-logging - - - org.springframework - spring-jcl - - org.springframework.security @@ -50,10 +43,6 @@ io.swagger.core.v3 swagger-annotations-jakarta - - org.slf4j - slf4j-api - com.google.guava diff --git a/steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java b/steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java deleted file mode 100644 index 23bd7468b..000000000 --- a/steve-core/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import lombok.Builder; -import lombok.Getter; -import org.jspecify.annotations.Nullable; -import org.springframework.security.crypto.password.PasswordEncoder; - -/** - * @author Sevket Goekay - * @since 19.08.2014 - */ -@Builder -@Getter -public class SteveConfiguration { - - @Builder - @Getter - public static class Paths { - // Root mapping for Spring - private final String rootMapping; - // Web frontend - private final String managerMapping; - // Mapping for SOAP services - private final String soapMapping; - // Mapping for Websocket services - private final String websocketMapping; - // Mapping for Web APIs - private final String apiMapping; - // Dummy service path - private final String routerEndpointPath; - - private final @Nullable String contextPath; - } - - private final Paths paths; - // Time zone for the application and database connections - private final String timeZoneId; - private final String steveVersion; - private final @Nullable String gitDescribe; - private final ApplicationProfile profile; - private final Ocpp ocpp; - private final Auth auth; - private final WebApi webApi; - private final DB db; - private final Jetty jetty; - - public String getSteveCompositeVersion() { - if (gitDescribe == null) { - return steveVersion; - } else { - return steveVersion + "-g" + gitDescribe; - } - } - - public void postConstruct() { - if (!(jetty.httpEnabled || jetty.httpsEnabled)) { - throw new IllegalArgumentException( - "HTTP and HTTPS are both disabled. Well, how do you want to access the server, then?"); - } - } - - // ------------------------------------------------------------------------- - // Class declarations - // ------------------------------------------------------------------------- - - // Jetty configuration - @Builder - @Getter - public static class Jetty { - private final String serverHost; - private final boolean gzipEnabled; - - // HTTP - private final boolean httpEnabled; - private final int httpPort; - - // HTTPS - private final boolean httpsEnabled; - private final int httpsPort; - private final @Nullable String keyStorePath; - private final @Nullable String keyStorePassword; - } - - // Database configuration - @Builder - @Getter - public static class DB { - private final String jdbcUrl; - private final String userName; - private final String password; - private final boolean sqlLogging; - private final String schemaSource; - private final @Nullable String schema; - } - - // Credentials for Web interface access - @Builder - @Getter - public static class Auth { - private final PasswordEncoder passwordEncoder; - private final String userName; - private final String encodedPassword; - } - - @Builder - @Getter - public static class WebApi { - private final @Nullable String headerKey; - private final @Nullable String headerValue; - } - - // OCPP-related configuration - @Builder - @Getter - public static class Ocpp { - private final boolean autoRegisterUnknownStations; - private final @Nullable String chargeBoxIdValidationRegex; - private final String wsSessionSelectStrategy; - } -} diff --git a/steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java b/steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java new file mode 100644 index 000000000..cfd69417e --- /dev/null +++ b/steve-core/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java @@ -0,0 +1,58 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties("steve") +public class SteveProperties { + + private Auth auth = new Auth(); + private WebApi webapi = new WebApi(); + private Ocpp ocpp = new Ocpp(); + private String version; + private String gitDescribe; + + @Data + public static class Auth { + private String user; + private String password; + } + + @Data + public static class WebApi { + private String key; + private String value; + } + + @Data + public static class Ocpp { + private String wsSessionSelectStrategy; + private boolean autoRegisterUnknownStations; + private String chargeBoxIdValidationRegex; + private Soap soap = new Soap(); + } + + @Data + public static class Soap { + private String routerEndpointPath; + } +} diff --git a/steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java b/steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java index d8a356cdd..134676995 100644 --- a/steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java +++ b/steve-core/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java @@ -22,8 +22,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import de.rwth.idsg.steve.SteveConfiguration; import de.rwth.idsg.steve.SteveException; +import de.rwth.idsg.steve.config.SteveProperties; import de.rwth.idsg.steve.repository.WebUserRepository; import de.rwth.idsg.steve.repository.dto.WebUser; import de.rwth.idsg.steve.service.dto.WebUserOverview; @@ -73,7 +73,7 @@ public class WebUsersService implements UserDetailsManager { // Because Guava's cache does not accept a null value private static final UserDetails DUMMY_USER = new User("#", "#", Collections.emptyList()); - private final SteveConfiguration config; + private final SteveProperties steveProperties; private final ObjectMapper mapper; private final WebUserRepository webUserRepository; private final SecurityContextHolderStrategy securityContextHolderStrategy = getContextHolderStrategy(); @@ -91,8 +91,8 @@ public void afterStart(ContextRefreshedEvent event) { } var user = WebUser.builder() - .login(config.getAuth().getUserName()) - .password(config.getAuth().getEncodedPassword()) + .login(steveProperties.getAuth().getUser()) + .password(encoder.encode(steveProperties.getAuth().getPassword())) .enabled(true) .authorities(EnumSet.of(WebUserAuthority.ADMIN)) .build(); diff --git a/steve-core/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java b/steve-core/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java index bc60c4c4d..037edde15 100644 --- a/steve-core/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java +++ b/steve-core/src/main/java/de/rwth/idsg/steve/web/dto/EndpointInfo.java @@ -18,7 +18,6 @@ */ package de.rwth.idsg.steve.web.dto; -import de.rwth.idsg.steve.SteveConfiguration; import lombok.Getter; import lombok.ToString; @@ -37,15 +36,10 @@ public class EndpointInfo { private final ItemsWithInfo ocppSoap; private final ItemsWithInfo ocppWebSocket; - public EndpointInfo(SteveConfiguration config) { - this.webInterface = new ItemsWithInfo( - "Access the web interface using", config.getPaths().getManagerMapping() + "/home"); - this.ocppSoap = new ItemsWithInfo( - "SOAP endpoint for OCPP", - config.getPaths().getSoapMapping() + config.getPaths().getRouterEndpointPath()); - this.ocppWebSocket = new ItemsWithInfo( - "WebSocket/JSON endpoint for OCPP", - config.getPaths().getWebsocketMapping() + config.getPaths().getRouterEndpointPath() + "/(chargeBoxId)"); + public EndpointInfo() { + this.webInterface = new ItemsWithInfo("Access the web interface using", "/manager/home"); + this.ocppSoap = new ItemsWithInfo("SOAP endpoint for OCPP", "/soap/services/ocpp"); + this.ocppWebSocket = new ItemsWithInfo("WebSocket/JSON endpoint for OCPP", "/ws/ocpp/(chargeBoxId)"); } @Getter diff --git a/steve-jooq/pom.xml b/steve-jooq/pom.xml index ff383699c..0f3b2d8cf 100644 --- a/steve-jooq/pom.xml +++ b/steve-jooq/pom.xml @@ -9,313 +9,31 @@ steve-jooq - - stevedb - stevedb - steve - changeme - - jdbc:mysql://localhost:3306/${db.schema}?sslMode=PREFERRED&serverTimezone=UTC - - - - - de.rwth.idsg - steve-core - - - org.jooq - jooq - ${jooq.version} - - - org.flywaydb - flyway-core - ${flyway.version} - - - org.flywaydb - flyway-mysql - ${flyway.version} - - - com.mysql - mysql-connector-j - ${mysql.jdbc.version} - - - com.zaxxer - HikariCP - 7.0.2 - - - org.springframework - spring-context - - - com.fasterxml.jackson.core - jackson-databind - - - com.neovisionaries - nv-i18n - - - org.slf4j - slf4j-api - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - - - useRealDatabase - - - - org.flywaydb - flyway-maven-plugin - ${flyway.version} - - - SET default_storage_engine=InnoDB; - - true - - - schema_version
- - true - com.mysql.cj.jdbc.Driver - ${db.jdbc.url} - ${db.user} - ${db.password} - - ${db.schema} - - - filesystem:src/main/resources/db/migration - -
- - - org.flywaydb - flyway-mysql - ${flyway.version} - - - com.mysql - mysql-connector-j - ${mysql.jdbc.version} - - - - - - - 1-flyway - - migrate - - generate-sources - - -
- - - org.jooq - jooq-codegen-maven - ${jooq.version} - - - - - com.mysql.cj.jdbc.Driver - ${db.jdbc.url} - ${db.user} - ${db.password} - - - - - org.jooq.meta.mysql.MySQLDatabase - .* - - ${db.schema} - false - - - - BOOLEAN - - (?i:(TINY|SMALL|MEDIUM|BIG)?INT(UNSIGNED)?\(1\)) - - - BOOLEAN - .*\.OCPP_TAG_ACTIVITY\.(IN_TRANSACTION|BLOCKED) - - - JSON - .*\.WEB_USER\.(AUTHORITIES) - - - - - true - - - - jooq.steve.db - ${project.build.directory}/generated-sources/jooq - - - - - - org.jooq - jooq-meta - ${jooq.version} - - - org.jooq - jooq-codegen - ${jooq.version} - - - com.mysql - mysql-connector-j - ${mysql.jdbc.version} - - - - - - - 2-jooq - - generate - - generate-sources - - - -
-
-
- - useTestContainers - - true - - - - - org.testcontainers - testcontainers-jooq-codegen-maven-plugin - 0.0.4 - - - org.testcontainers - mysql - ${testcontainers.version} - - - com.mysql - mysql-connector-j - ${mysql.jdbc.version} - - - - - generate-jooq-sources - - generate - - generate-sources - - - MYSQL - mysql:8.0 - root - - ${db.schema} - - - - SET default_storage_engine=InnoDB; - - true - - - schema_version
- - true - - filesystem:src/main/resources/db/migration - -
- - - - - org.jooq.meta.mysql.MySQLDatabase - .* - - - ${db.schema} - false - - - - BOOLEAN - - (?i:(TINY|SMALL|MEDIUM|BIG)?INT(UNSIGNED)?\(1\)) - - - BOOLEAN - .*\.OCPP_TAG_ACTIVITY\.(IN_TRANSACTION|BLOCKED) - - - JSON - .*\.WEB_USER\.(AUTHORITIES) - - - - - true - - - jooq.steve.db - ${project.build.directory}/generated-sources/jooq - - - -
-
-
-
-
-
-
-
+ + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + + default-compile + none + + + default-testCompile + none + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.4 + + true + + + + diff --git a/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java b/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java index 426a2880c..44327ec06 100644 --- a/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java +++ b/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/config/OcppSoapConfiguration.java @@ -18,7 +18,6 @@ */ package de.rwth.idsg.steve.config; -import de.rwth.idsg.steve.SteveConfiguration; import de.rwth.idsg.steve.ocpp.soap.MediatorInInterceptor; import de.rwth.idsg.steve.ocpp.soap.MessageHeaderInterceptor; import de.rwth.idsg.steve.ocpp.soap.MessageIdInterceptor; @@ -59,7 +58,7 @@ public class OcppSoapConfiguration { LogUtils.setLoggerClass(Slf4jLogger.class); } - private final SteveConfiguration config; + private final SteveProperties steveProperties; private final ocpp.cs._2010._08.CentralSystemService ocpp12Server; private final ocpp.cs._2012._06.CentralSystemService ocpp15Server; private final ocpp.cs._2015._10.CentralSystemService ocpp16Server; @@ -72,9 +71,9 @@ public OcppSoapConfiguration( ocpp.cs._2012._06.CentralSystemService ocpp15Server, ocpp.cs._2015._10.CentralSystemService ocpp16Server, MessageHeaderInterceptor messageHeaderInterceptor, - SteveConfiguration config, + SteveProperties steveProperties, Bus bus) { - this.config = config; + this.steveProperties = steveProperties; this.ocpp12Server = ocpp12Server; this.ocpp15Server = ocpp15Server; this.ocpp16Server = ocpp16Server; @@ -94,9 +93,13 @@ public void configure() { // Just a dummy service to route incoming messages to the appropriate service version. This should be the last // one to be created, since in MediatorInInterceptor we go over created/registered services and build a map. - List> mediator = singletonList(new MediatorInInterceptor(bus, config)); + List> mediator = singletonList(new MediatorInInterceptor(bus, steveProperties)); createOcppService( - bus, ocpp12Server, config.getPaths().getRouterEndpointPath(), mediator, Collections.emptyList()); + bus, + ocpp12Server, + steveProperties.getOcpp().getSoap().getRouterEndpointPath(), + mediator, + Collections.emptyList()); } @Bean(name = Bus.DEFAULT_BUS_ID, destroyMethod = "shutdown") diff --git a/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java b/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java index 90ede9ca0..2658aafc6 100644 --- a/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java +++ b/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java @@ -19,7 +19,6 @@ package de.rwth.idsg.steve.ocpp.soap; import com.oneandone.compositejks.SslContextBuilder; -import de.rwth.idsg.steve.SteveConfiguration; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.endpoint.Client; import org.apache.cxf.ext.logging.LoggingFeature; @@ -28,6 +27,7 @@ import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.ws.addressing.WSAddressingFeature; import org.jspecify.annotations.Nullable; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.net.ssl.SSLContext; @@ -43,12 +43,15 @@ public class ClientProvider { private final @Nullable TLSClientParameters tlsClientParams; private final LoggingFeature loggingFeature; - public ClientProvider(SteveConfiguration config, LoggingFeature loggingFeature) { + public ClientProvider( + LoggingFeature loggingFeature, + @Value("${server.ssl.key-store:}") String keyStore, + @Value("${server.ssl.key-store-password:}") String keyStorePassword, + @Value("${server.ssl.enabled:false}") boolean sslEnabled) { this.loggingFeature = loggingFeature; - var jettyConfig = config.getJetty(); - if (shouldInitSSL(jettyConfig)) { + if (shouldInitSSL(sslEnabled, keyStore, keyStorePassword)) { tlsClientParams = new TLSClientParameters(); - tlsClientParams.setSslContext(setupSSL(jettyConfig)); + tlsClientParams.setSslContext(setupSSL(keyStore, keyStorePassword)); } else { tlsClientParams = null; } @@ -77,19 +80,18 @@ private JaxWsProxyFactoryBean createBean(String endpointAddress) { return f; } - private static boolean shouldInitSSL(SteveConfiguration.Jetty jettyConfig) { - return jettyConfig.getKeyStorePath() != null - && !jettyConfig.getKeyStorePath().isBlank() - && jettyConfig.getKeyStorePassword() != null - && !jettyConfig.getKeyStorePassword().isBlank(); + private static boolean shouldInitSSL(boolean sslEnabled, String keyStore, String keyStorePassword) { + return sslEnabled + && keyStore != null + && !keyStore.isBlank() + && keyStorePassword != null + && !keyStorePassword.isBlank(); } - private static SSLContext setupSSL(SteveConfiguration.Jetty jettyConfig) { + private static SSLContext setupSSL(String keyStore, String keyStorePassword) { try { - var keyStorePath = jettyConfig.getKeyStorePath(); - var keyStorePwd = jettyConfig.getKeyStorePassword(); return SslContextBuilder.builder() - .keyStoreFromFile(keyStorePath, keyStorePwd) + .keyStoreFromFile(keyStore, keyStorePassword) .usingTLS() .usingDefaultAlgorithm() .usingKeyManagerPasswordFromKeyStore() diff --git a/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java b/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java index deb87a99e..0e6fc4e53 100644 --- a/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java +++ b/steve-ocpp-transport-soap/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java @@ -18,7 +18,7 @@ */ package de.rwth.idsg.steve.ocpp.soap; -import de.rwth.idsg.steve.SteveConfiguration; +import de.rwth.idsg.steve.config.SteveProperties; import lombok.extern.slf4j.Slf4j; import org.apache.cxf.Bus; import org.apache.cxf.binding.soap.SoapMessage; @@ -51,10 +51,10 @@ public class MediatorInInterceptor extends AbstractPhaseInterceptor private final Map actualServers; - public MediatorInInterceptor(Bus bus, SteveConfiguration config) { + public MediatorInInterceptor(Bus bus, SteveProperties steveProperties) { super(Phase.POST_STREAM); super.addBefore(StaxInInterceptor.class.getName()); - actualServers = initServerLookupMap(bus, config); + actualServers = initServerLookupMap(bus, steveProperties); } public final void handleMessage(SoapMessage message) { @@ -104,7 +104,7 @@ public final void handleMessage(SoapMessage message) { * redirect to the version-specific implementation according to the namespace * of the incoming message. */ - private static Map initServerLookupMap(Bus bus, SteveConfiguration config) { + private static Map initServerLookupMap(Bus bus, SteveProperties steveProperties) { String exceptionMsg = "The services are not created and/or registered to the bus yet."; ServerRegistry serverRegistry = bus.getExtension(ServerRegistry.class); @@ -123,7 +123,7 @@ private static Map initServerLookupMap(Bus bus, SteveConfigurati String address = info.getAddress(); // exclude the 'dummy' routing server - if (config.getPaths().getRouterEndpointPath().equals(address)) { + if (steveProperties.getOcpp().getSoap().getRouterEndpointPath().equals(address)) { continue; } diff --git a/steve-ui-jsp/pom.xml b/steve-ui-jsp/pom.xml index f49f23de7..f46a28c92 100644 --- a/steve-ui-jsp/pom.xml +++ b/steve-ui-jsp/pom.xml @@ -22,26 +22,11 @@ org.springframework spring-webmvc - - - - commons-logging - commons-logging - - - org.springframework - spring-jcl - - org.springframework.security spring-security-web - - org.hibernate.validator - hibernate-validator - jakarta.servlet.jsp.jstl jakarta.servlet.jsp.jstl-api @@ -52,27 +37,19 @@ jakarta.servlet.jsp.jstl 3.0.1 - - org.eclipse.jetty.ee10 - jetty-ee10-apache-jsp - - - - - org.owasp.encoder encoder-jakarta-jsp ${owasp-encoder.version} - - ch.qos.logback - logback-classic - com.neovisionaries nv-i18n + + org.apache.tomcat.embed + tomcat-embed-jasper + org.junit.jupiter @@ -102,28 +79,5 @@ - - - - - org.eclipse.jetty.ee10 - jetty-ee10-jspc-maven-plugin - ${jetty.version} - - - jspc - - jspc - - compile - - ${basedir}/src/main/resources/webapp - ${basedir}/src/main/resources/webapp/WEB-INF/web.xml - ${basedir}/target/classes/webapp/WEB-INF/web.xml-frag - - - - -
diff --git a/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/config/JspConfiguration.java b/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/config/JspConfiguration.java deleted file mode 100644 index dcf055cf6..000000000 --- a/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/config/JspConfiguration.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.view.InternalResourceViewResolver; - -@Configuration -public class JspConfiguration implements WebMvcConfigurer { - - /** - * Resolver for JSP views/templates. Controller classes process the requests - * and forward to JSP files for rendering. - */ - @Bean - public InternalResourceViewResolver urlBasedViewResolver() { - InternalResourceViewResolver resolver = new InternalResourceViewResolver(); - resolver.setPrefix("/WEB-INF/views/"); - resolver.setSuffix(".jsp"); - return resolver; - } - - /** - * Resource path for static content of the Web interface. - */ - @Override - public void addResourceHandlers(final ResourceHandlerRegistry registry) { - registry.addResourceHandler("/static/**").addResourceLocations("static/"); - } - - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/manager/signin").setViewName("signin"); - registry.setOrder(Ordered.HIGHEST_PRECEDENCE); - } -} diff --git a/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java b/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java index 60dbb2ce2..6692a2105 100644 --- a/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java +++ b/steve-ui-jsp/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java @@ -19,7 +19,7 @@ package de.rwth.idsg.steve.web.controller; import de.rwth.idsg.steve.NotificationFeature; -import de.rwth.idsg.steve.SteveConfiguration; +import de.rwth.idsg.steve.config.SteveProperties; import de.rwth.idsg.steve.repository.GenericRepository; import de.rwth.idsg.steve.repository.SettingsRepository; import de.rwth.idsg.steve.service.MailService; @@ -56,7 +56,7 @@ public class AboutSettingsController { private final SettingsRepository settingsRepository; private final MailService mailService; private final ReleaseCheckService releaseCheckService; - private final SteveConfiguration config; + private final SteveProperties steveProperties; private final EndpointInfo info; // ------------------------------------------------------------------------- @@ -72,7 +72,7 @@ public class AboutSettingsController { @GetMapping(value = ABOUT_PATH) public String getAbout(Model model) { - model.addAttribute("version", config.getSteveVersion()); + model.addAttribute("version", steveProperties.getVersion()); model.addAttribute("db", genericRepository.getDBVersion().orElse(null)); model.addAttribute("logFile", logController.getLogFilePath()); model.addAttribute("systemTime", Instant.now()); diff --git a/steve/pom.xml b/steve/pom.xml index 227c90147..6c334630e 100644 --- a/steve/pom.xml +++ b/steve/pom.xml @@ -41,6 +41,32 @@ + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-jetty + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-jooq + + + org.springframework.boot + spring-boot-starter-validation + de.rwth.idsg steve-jooq @@ -85,58 +111,6 @@ 5.5 - - - org.springframework.security - spring-security-config - - - - - ch.qos.logback - logback-classic - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - jcl-over-slf4j - ${slf4j.version} - - - - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty.ee10 - jetty-ee10-webapp - - - org.eclipse.jetty.ee10 - jetty-ee10-annotations - - - org.eclipse.jetty - jetty-rewrite - - - org.eclipse.jetty.ee10.websocket - jetty-ee10-websocket-jetty-server - - - org.eclipse.jetty.compression - jetty-compression-server - - - org.eclipse.jetty.compression - jetty-compression-gzip - - com.mysql @@ -151,8 +125,8 @@ - org.springframework - spring-test + org.springframework.boot + spring-boot-starter-test test @@ -160,37 +134,12 @@ jetty-websocket-jetty-client test - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.jupiter - junit-jupiter-params - test - - - org.mockito - mockito-junit-jupiter - test - net.bytebuddy byte-buddy 1.17.7 test - - org.assertj - assertj-core - test - - - com.jayway.jsonpath - json-path - test - @@ -210,6 +159,10 @@ + + org.springframework.boot + spring-boot-maven-plugin + io.github.git-commit-id git-commit-id-maven-plugin @@ -227,38 +180,6 @@ - - - org.apache.maven.plugins - maven-dependency-plugin - 3.8.1 - - - copy-dependencies - - copy-dependencies - - prepare-package - - ${project.build.directory}/libs - runtime - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - libs/ - de.rwth.idsg.steve.Application - - - - diff --git a/steve/src/main/java/de/rwth/idsg/steve/Application.java b/steve/src/main/java/de/rwth/idsg/steve/Application.java deleted file mode 100644 index 3640a6a52..000000000 --- a/steve/src/main/java/de/rwth/idsg/steve/Application.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import lombok.extern.slf4j.Slf4j; - -import java.nio.file.Path; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.Optional; -import java.util.TimeZone; - -/** - * @author Sevket Goekay - * @since 14.01.2015 - */ -@Slf4j -public class Application { - - private final SteveConfiguration config; - private final JettyServer server; - - public Application(SteveConfiguration config, LogFileRetriever logFileRetriever) { - this.config = config; - this.server = new JettyServer(config, logFileRetriever); - } - - public static void main(String[] args) throws Exception { - // For Hibernate validator - System.setProperty("org.jboss.logging.provider", "slf4j"); - - SteveConfiguration sc = SteveConfigurationReader.readSteveConfiguration("main.properties"); - log.info("Loaded the properties. Starting with the '{}' profile", sc.getProfile()); - - var zoneId = ZoneId.of(sc.getTimeZoneId()); - TimeZone.setDefault(TimeZone.getTimeZone(zoneId)); - log.info( - "Date/time zone of the application is set to {}. Current date/time: {}", - sc.getTimeZoneId(), - ZonedDateTime.now(zoneId)); - - LogFileRetriever logFileRetriever = new LogFileRetriever(); - Optional path = logFileRetriever.getPath(); - boolean loggingToFile = path.isPresent(); - if (loggingToFile) { - System.out.println("Log file: " + path.get().toAbsolutePath()); - } - - Application app = new Application(sc, logFileRetriever); - - try { - app.start(); - app.join(); - } catch (Exception e) { - log.error("Application failed to start", e); - - if (loggingToFile) { - System.err.println("Application failed to start"); - e.printStackTrace(); - } - - app.stop(); - } - } - - public void start() throws Exception { - server.start(); - } - - public void join() throws Exception { - server.join(); - } - - public void stop() throws Exception { - server.stop(); - } -} diff --git a/steve/src/main/java/de/rwth/idsg/steve/JettyServer.java b/steve/src/main/java/de/rwth/idsg/steve/JettyServer.java deleted file mode 100644 index 2efe1d698..000000000 --- a/steve/src/main/java/de/rwth/idsg/steve/JettyServer.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.web.dto.EndpointInfo; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.ForwardedRequestCustomizer; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.SecureRequestCustomizer; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.SslConnectionFactory; -import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; -import org.jspecify.annotations.Nullable; - -import java.net.DatagramSocket; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.Socket; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -/** - * @author Sevket Goekay - * @since 12.12.2014 - */ -@Slf4j -public class JettyServer { - - private final SteveConfiguration config; - private final LogFileRetriever logFileRetriever; - private final EndpointInfo info; - - private @Nullable Server server; - private SteveAppContext steveAppContext; - - private static final int MIN_THREADS = 4; - private static final int MAX_THREADS = 50; - - private static final long STOP_TIMEOUT = TimeUnit.SECONDS.toMillis(5); - private static final long IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(1); - - public JettyServer(SteveConfiguration config, LogFileRetriever logFileRetriever) { - this.config = config; - this.logFileRetriever = logFileRetriever; - this.info = new EndpointInfo(config); - } - - /** - * A fully configured Jetty Server instance - */ - private void prepare() { - - // === jetty.xml === - // Setup Threadpool - QueuedThreadPool threadPool = new QueuedThreadPool(); - threadPool.setMinThreads(MIN_THREADS); - threadPool.setMaxThreads(MAX_THREADS); - - // Server - server = new Server(threadPool); - - // Scheduler - server.addBean(new ScheduledExecutorScheduler()); - - // HTTP Configuration - HttpConfiguration httpConfig = new HttpConfiguration(); - httpConfig.setSecureScheme(HttpScheme.HTTPS.asString()); - httpConfig.setSecurePort(config.getJetty().getHttpsPort()); - httpConfig.setOutputBufferSize(32768); - httpConfig.setRequestHeaderSize(8192); - httpConfig.setResponseHeaderSize(8192); - httpConfig.setSendServerVersion(false); - httpConfig.setSendDateHeader(false); - httpConfig.setSendXPoweredBy(false); - - // make sure X-Forwarded-For headers are picked up if set (e.g. by a load balancer) - // https://github.com/steve-community/steve/pull/570 - httpConfig.addCustomizer(new ForwardedRequestCustomizer()); - - // Extra options - server.setDumpAfterStart(false); - server.setDumpBeforeStop(false); - server.setStopAtShutdown(true); - server.setStopTimeout(STOP_TIMEOUT); - - if (config.getJetty().isHttpEnabled()) { - server.addConnector(httpConnector(httpConfig)); - } - - if (config.getJetty().isHttpsEnabled()) { - server.addConnector(httpsConnector(httpConfig)); - } - - steveAppContext = new SteveAppContext(config, logFileRetriever, info); - server.setHandler(steveAppContext.getHandler()); - } - - private ServerConnector httpConnector(HttpConfiguration httpconfig) { - // === jetty-http.xml === - ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpconfig)); - http.setHost(config.getJetty().getServerHost()); - http.setPort(config.getJetty().getHttpPort()); - http.setIdleTimeout(IDLE_TIMEOUT); - return http; - } - - private ServerConnector httpsConnector(HttpConfiguration httpconfig) { - // === jetty-https.xml === - // SSL Context Factory - SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(config.getJetty().getKeyStorePath()); - sslContextFactory.setKeyStorePassword(config.getJetty().getKeyStorePassword()); - sslContextFactory.setKeyManagerPassword(config.getJetty().getKeyStorePassword()); - - // SSL HTTP Configuration - HttpConfiguration httpsConfig = new HttpConfiguration(httpconfig); - httpsConfig.addCustomizer(new SecureRequestCustomizer()); - - // SSL Connector - ServerConnector https = new ServerConnector( - server, - new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), - new HttpConnectionFactory(httpsConfig)); - https.setHost(config.getJetty().getServerHost()); - https.setPort(config.getJetty().getHttpsPort()); - https.setIdleTimeout(IDLE_TIMEOUT); - return https; - } - - /** - * Starts the Jetty Server instance - */ - public void start() throws Exception { - prepare(); - - if (server != null) { - server.start(); - steveAppContext.configureWebSocket(); - populateEndpointInfo(); - } - } - - /** - * Join the server thread with the current thread - */ - public void join() throws Exception { - if (server != null) { - server.join(); - } - } - - public void stop() throws Exception { - if (server != null) { - server.stop(); - steveAppContext.close(); - } - } - - public boolean isStarted() { - return server != null && server.isStarted(); - } - - public void populateEndpointInfo() { - if (server == null) { - return; - } - var list = Arrays.stream(server.getConnectors()) - .map(this::getConnectorPath) - .flatMap(ips -> ips.entrySet().stream()) - .toList(); - setList(list, isSecured -> isSecured ? "https" : "http", info.getWebInterface(), info.getOcppSoap()); - setList(list, isSecured -> isSecured ? "wss" : "ws", info.getOcppWebSocket()); - } - - private Map getConnectorPath(Connector c) { - var sc = (ServerConnector) c; - - var isSecure = sc.getDefaultConnectionFactory() instanceof SslConnectionFactory; - var layout = "://%s:%d" + config.getPaths().getContextPath(); - - return getIps(sc, config.getJetty().getServerHost()).stream() - .map(k -> String.format(layout, k, sc.getPort())) - .collect(HashMap::new, (m, v) -> m.put(v, isSecure), HashMap::putAll); - } - - private static Set getIps(ServerConnector sc, String serverHost) { - var ips = new HashSet(); - var host = sc.getHost(); - if (host == null || host.equals("0.0.0.0")) { - ips.addAll(getPossibleIpAddresses(serverHost)); - } else { - ips.add(host); - } - return ips; - } - - private static void setList( - List> list, - Function prefix, - EndpointInfo.ItemsWithInfo... items) { - var ws = list.stream().map(e -> prefix.apply(e.getValue()) + e.getKey()).toList(); - for (EndpointInfo.ItemsWithInfo item : items) { - item.setData(ws); - } - } - - /** - * Uses different APIs to find out the IP of this machine. - */ - private static List getPossibleIpAddresses(String serverHost) { - final String host = "treibhaus.informatik.rwth-aachen.de"; - final List ips = new ArrayList<>(); - - try { - ips.add(InetAddress.getLocalHost().getHostAddress()); - } catch (Exception e) { - // fail silently - } - - try { - Socket socket = new Socket(); - socket.connect(new InetSocketAddress(host, 80)); - ips.add(socket.getLocalAddress().getHostAddress()); - } catch (Exception e) { - // fail silently - } - - // https://stackoverflow.com/a/38342964 - try (DatagramSocket socket = new DatagramSocket()) { - socket.connect(InetAddress.getByName("8.8.8.8"), 10002); - ips.add(socket.getLocalAddress().getHostAddress()); - } catch (Exception e) { - // fail silently - } - - // https://stackoverflow.com/a/20418809 - try { - for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); - ifaces.hasMoreElements(); ) { - NetworkInterface iface = ifaces.nextElement(); - for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) { - InetAddress inetAddr = inetAddrs.nextElement(); - if (!inetAddr.isLoopbackAddress() && (inetAddr instanceof Inet4Address)) { - ips.add(inetAddr.getHostAddress()); - } - } - } - - } catch (Exception e) { - // fail silently - } - - ips.removeIf("0.0.0.0"::equals); - - if (ips.isEmpty()) { - // Well, we failed to read from system, fall back to main.properties. - // Better than nothing - ips.add(serverHost); - } - - return ips; - } -} diff --git a/steve/src/main/java/de/rwth/idsg/steve/SteveAppContext.java b/steve/src/main/java/de/rwth/idsg/steve/SteveAppContext.java deleted file mode 100644 index a079b9341..000000000 --- a/steve/src/main/java/de/rwth/idsg/steve/SteveAppContext.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.web.dto.EndpointInfo; -import lombok.Getter; -import org.apache.cxf.transport.servlet.CXFServlet; -import org.apache.tomcat.InstanceManager; -import org.apache.tomcat.SimpleInstanceManager; -import org.eclipse.jetty.compression.server.CompressionHandler; -import org.eclipse.jetty.ee10.apache.jsp.JettyJasperInitializer; -import org.eclipse.jetty.ee10.servlet.FilterHolder; -import org.eclipse.jetty.ee10.servlet.ServletContextHandler; -import org.eclipse.jetty.ee10.servlet.ServletHolder; -import org.eclipse.jetty.ee10.webapp.WebAppContext; -import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer; -import org.eclipse.jetty.rewrite.handler.RedirectPatternRule; -import org.eclipse.jetty.rewrite.handler.RewriteHandler; -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.util.component.AbstractLifeCycle; -import org.eclipse.jetty.util.resource.ResourceFactory; -import org.eclipse.jetty.websocket.core.WebSocketConstants; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; -import org.springframework.web.context.ContextLoaderListener; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.filter.DelegatingFilterProxy; -import org.springframework.web.servlet.DispatcherServlet; - -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Set; -import jakarta.servlet.DispatcherType; - -import static de.rwth.idsg.steve.config.OcppWebSocketConfiguration.IDLE_TIMEOUT; -import static de.rwth.idsg.steve.config.OcppWebSocketConfiguration.MAX_MSG_SIZE; - -/** - * @author Sevket Goekay - * @since 07.04.2015 - */ -public class SteveAppContext { - - private final SteveConfiguration config; - private final AnnotationConfigWebApplicationContext springContext; - private final WebAppContext webAppContext; - - @Getter - private final Handler handler; - // ClosedFileSystemException if the resource factory is linked to the context - // https://github.com/jetty/jetty.project/issues/13592 - private final ResourceFactory.Closeable webAppResourceFactory = ResourceFactory.closeable(); - - public SteveAppContext(SteveConfiguration config, LogFileRetriever logFileRetriever, EndpointInfo info) { - this.config = config; - springContext = new AnnotationConfigWebApplicationContext(); - var context = new GenericApplicationContext(); - context.registerBean(SteveConfiguration.class, () -> config); - context.registerBean(LogFileRetriever.class, () -> logFileRetriever); - context.registerBean(EndpointInfo.class, () -> info); - context.refresh(); - context.setParent(springContext.getParent()); - springContext.setParent(context); - springContext.scan("de.rwth.idsg.steve.config"); - webAppContext = createWebAppContext(); - // Run rewrite first, then dispatch to contexts - handler = new Handler.Sequence(createRedirectHandler(config), webAppContext); - } - - public void close() { - webAppResourceFactory.close(); - } - - /** - * Otherwise, defaults come from {@link WebSocketConstants} - */ - public void configureWebSocket() { - var container = JettyWebSocketServerContainer.getContainer(webAppContext.getServletContext()); - container.setMaxTextMessageSize(MAX_MSG_SIZE); - container.setIdleTimeout(IDLE_TIMEOUT); - } - - private WebAppContext createWebAppContext() { - var ctx = new WebAppContext(); - ctx.setContextPath(config.getPaths().getContextPath()); - ctx.setBaseResource(webAppResourceFactory.newClassLoaderResource("webapp/")); - - // if during startup an exception happens, do not swallow it, throw it - ctx.setThrowUnavailableOnStartupException(true); - - // Disable directory listings if no index.html is found. - ctx.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); - - ctx.addEventListener(new ContextLoaderListener(springContext)); - - var web = new ServletHolder("spring-dispatcher", new DispatcherServlet(springContext)); - ctx.addServlet(web, config.getPaths().getRootMapping()); - - var cxf = new ServletHolder("cxf", new CXFServlet()); - ctx.addServlet(cxf, config.getPaths().getSoapMapping() + "/*"); - - // add spring security - ctx.addFilter( - // The bean name is not arbitrary, but is as expected by Spring - new FilterHolder( - new DelegatingFilterProxy(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)), - config.getPaths().getRootMapping() + "*", - EnumSet.allOf(DispatcherType.class)); - - /* - * Help by: - * https://github.com/jetty/jetty-examples/tree/12.0.x/embedded/ee10-jsp - * https://github.com/jetty-project/embedded-jetty-jsp - * https://github.com/jasonish/jetty-springmvc-jsp-template - * http://examples.javacodegeeks.com/enterprise-java/jetty/jetty-jsp-example - */ - ctx.addBean(new EmbeddedJspStarter(ctx)); - ctx.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); - - if (config.getJetty().isGzipEnabled()) { - // Insert compression in front of the webapp handler chain, keep the context intact - ctx.insertHandler(new CompressionHandler()); - } - - return ctx; - } - - private static Handler createRedirectHandler(SteveConfiguration config) { - var rewrite = new RewriteHandler(); - for (var redirect : getRedirectSet(config)) { - var rule = new RedirectPatternRule(); - rule.setTerminating(true); - rule.setPattern(redirect); - rule.setLocation( - config.getPaths().getContextPath() + config.getPaths().getManagerMapping() + "/home"); - rewrite.addRule(rule); - } - return rewrite; - } - - private static Set getRedirectSet(SteveConfiguration config) { - var path = config.getPaths().getContextPath(); - - var redirectSet = HashSet.newHashSet(3); - redirectSet.add(""); - redirectSet.add(path); - - // Otherwise (if path = ""), we would already be at root of the server ("/") - // and using the redirection below would cause an infinite loop. - if (!"".equals(path)) { - redirectSet.add(path + "/"); - } - - return redirectSet; - } - - /** - * From: https://github.com/jetty/jetty-examples/blob/12.0.x/embedded/ee10-jsp/src/main/java/examples/EmbeddedJspStarter.java - * - * JspStarter for embedded ServletContextHandlers - * - * This is added as a bean that is a jetty LifeCycle on the ServletContextHandler. - * This bean's doStart method will be called as the ServletContextHandler starts, - * and will call the ServletContainerInitializer for the jsp engine. - */ - public static class EmbeddedJspStarter extends AbstractLifeCycle { - - private final JettyJasperInitializer sci; - private final ServletContextHandler context; - - public EmbeddedJspStarter(ServletContextHandler context) { - this.sci = new JettyJasperInitializer(); - this.context = context; - - // we dont need all this from the example, since our JSPs are precompiled - // - // StandardJarScanner jarScanner = new StandardJarScanner(); - // StandardJarScanFilter jarScanFilter = new StandardJarScanFilter(); - // jarScanFilter.setTldScan("taglibs-standard-impl-*"); - // jarScanFilter.setTldSkip("apache-*,ecj-*,jetty-*,asm-*,javax.servlet-*,javax.annotation-*,taglibs-standard-spec-*"); - // jarScanner.setJarScanFilter(jarScanFilter); - // this.context.setAttribute("org.apache.tomcat.JarScanner", jarScanner); - } - - @Override - protected void doStart() throws Exception { - var old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(context.getClassLoader()); - try { - sci.onStartup(null, context.getServletContext()); - super.doStart(); - } finally { - Thread.currentThread().setContextClassLoader(old); - } - } - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/ocpp/OcppProtocolTest.java b/steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java similarity index 61% rename from steve/src/test/java/de/rwth/idsg/steve/ocpp/OcppProtocolTest.java rename to steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java index 02b4902f4..e02562aad 100644 --- a/steve/src/test/java/de/rwth/idsg/steve/ocpp/OcppProtocolTest.java +++ b/steve/src/main/java/de/rwth/idsg/steve/SteveApplication.java @@ -16,20 +16,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package de.rwth.idsg.steve.ocpp; +package de.rwth.idsg.steve; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; +import de.rwth.idsg.steve.config.SteveProperties; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; -import static org.assertj.core.api.Assertions.assertThat; +@SpringBootApplication +@EnableConfigurationProperties(SteveProperties.class) +public class SteveApplication { -public class OcppProtocolTest { - - @ParameterizedTest - @EnumSource(OcppProtocol.class) - public void testFromCompositeValue(OcppProtocol input) { - var toTest = input.getCompositeValue(); - var inputBack = OcppProtocol.fromCompositeValue(toTest); - assertThat(inputBack).isEqualTo(input); + public static void main(String[] args) { + SpringApplication.run(SteveApplication.class, args); } } diff --git a/steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java b/steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java index 1a80363cc..c57658323 100644 --- a/steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java +++ b/steve/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import de.rwth.idsg.steve.SteveConfiguration; import de.rwth.idsg.steve.service.DummyReleaseCheckService; import de.rwth.idsg.steve.service.GithubReleaseCheckService; import de.rwth.idsg.steve.service.ReleaseCheckService; @@ -31,19 +30,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import org.springframework.format.support.FormattingConversionService; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.web.accept.ContentNegotiationManager; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; - -import java.util.List; /** * Configuration and beans of Spring Framework. @@ -53,34 +40,9 @@ */ @Slf4j @Configuration -@EnableWebMvc @EnableScheduling @ComponentScan("de.rwth.idsg.steve") -public class BeanConfiguration implements WebMvcConfigurer { - - @Bean(destroyMethod = "close") - public DelegatingTaskScheduler asyncTaskScheduler() { - var scheduler = new ThreadPoolTaskScheduler(); - scheduler.setPoolSize(5); - scheduler.setThreadNamePrefix("SteVe-TaskScheduler-"); - scheduler.setWaitForTasksToCompleteOnShutdown(true); - scheduler.setAwaitTerminationSeconds(30); - scheduler.initialize(); - - return new DelegatingTaskScheduler(scheduler); - } - - @Bean(destroyMethod = "close") - public DelegatingTaskExecutor asyncTaskExecutor() { - var executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(5); - executor.setThreadNamePrefix("SteVe-TaskExecutor-"); - executor.setWaitForTasksToCompleteOnShutdown(true); - executor.setAwaitTerminationSeconds(30); - executor.initialize(); - - return new DelegatingTaskExecutor(executor); - } +public class BeanConfiguration { /** * There might be instances deployed in a local/closed network with no internet connection. In such situations, @@ -89,47 +51,26 @@ public DelegatingTaskExecutor asyncTaskExecutor() { * steps and return a "no new version" report immediately. */ @Bean - public ReleaseCheckService releaseCheckService(SteveConfiguration config) { - if (InternetChecker.isInternetAvailable(config.getSteveCompositeVersion())) { - return new GithubReleaseCheckService(config); - } else { - return new DummyReleaseCheckService(); + public ReleaseCheckService releaseCheckService(SteveProperties steveProperties) { + String compositeVersion = steveProperties.getVersion(); + if (steveProperties.getGitDescribe() != null) { + compositeVersion += "-g" + steveProperties.getGitDescribe(); } - } - - // ------------------------------------------------------------------------- - // API config - // ------------------------------------------------------------------------- - @Override - public void extendMessageConverters(List> converters) { - for (var converter : converters) { - if (converter instanceof MappingJackson2HttpMessageConverter conv) { - ObjectMapper objectMapper = conv.getObjectMapper(); - objectMapper.findAndRegisterModules(); - // if the client sends unknown props, just ignore them instead of failing - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - // default is true - objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - break; - } + if (InternetChecker.isInternetAvailable(compositeVersion)) { + return new GithubReleaseCheckService(steveProperties); + } else { + return new DummyReleaseCheckService(); } } - /** - * Find the ObjectMapper used in MappingJackson2HttpMessageConverter and initialized by Spring automatically. - * MappingJackson2HttpMessageConverter is not a Bean. It is created in {@link WebMvcConfigurationSupport#addDefaultHttpMessageConverters(List)}. - * Therefore, we have to access it via proxies that reference it. RequestMappingHandlerAdapter is a Bean, created in - * {@link WebMvcConfigurationSupport#requestMappingHandlerAdapter(ContentNegotiationManager, FormattingConversionService, org.springframework.validation.Validator)}. - */ - @Primary @Bean - public ObjectMapper jacksonObjectMapper(RequestMappingHandlerAdapter requestMappingHandlerAdapter) { - return requestMappingHandlerAdapter.getMessageConverters().stream() - .filter(MappingJackson2HttpMessageConverter.class::isInstance) - .findAny() - .map(conv -> ((MappingJackson2HttpMessageConverter) conv).getObjectMapper()) - .orElseThrow(() -> - new IllegalStateException("There is no MappingJackson2HttpMessageConverter in Spring context")); + @Primary + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return objectMapper; } } diff --git a/steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java b/steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java deleted file mode 100644 index edb981808..000000000 --- a/steve/src/main/java/de/rwth/idsg/steve/config/PropertyConfig.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.config; - -import de.rwth.idsg.steve.SteveConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; - -import java.util.Properties; - -@Configuration -public class PropertyConfig { - - // > To avoid these lifecycle issues, mark BFPP-returning @Bean methods as static - // From [the Spring - // documentation](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html) - @Bean - public static PropertySourcesPlaceholderConfigurer valueConfigurer(SteveConfiguration config) { - var configurer = new PropertySourcesPlaceholderConfigurer(); - - var props = new Properties(); - var chargeBoxIdValidationRegex = config.getOcpp().getChargeBoxIdValidationRegex(); - if (chargeBoxIdValidationRegex != null) { - props.put("charge-box-id.validation.regex", chargeBoxIdValidationRegex); - } - var autoRegisterUnknownStations = config.getOcpp().isAutoRegisterUnknownStations(); - props.put("auto.register.unknown.stations", autoRegisterUnknownStations); - var wsSessionSelectStrategy = config.getOcpp().getWsSessionSelectStrategy(); - props.put("ws.session.select.strategy", wsSessionSelectStrategy); - configurer.setProperties(props); - - return configurer; - } -} diff --git a/steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java b/steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java index a90910c21..ee012545d 100644 --- a/steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java +++ b/steve/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java @@ -18,7 +18,6 @@ */ package de.rwth.idsg.steve.config; -import de.rwth.idsg.steve.SteveConfiguration; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -29,38 +28,26 @@ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.util.matcher.RequestMatcher; -/** - * @author Sevket Goekay - * @since 07.01.2015 - */ @Slf4j @RequiredArgsConstructor @Configuration @EnableWebSecurity public class SecurityConfiguration { - /** - * Password encoding changed with spring-security 5.0.0. We either have to use a prefix before the password to - * indicate which actual encoder {@link DelegatingPasswordEncoder} should use [1, 2] or specify the encoder as we do. - * - * [1] https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-storage-format - * [2] {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()} - */ @Bean - public PasswordEncoder passwordEncoder(SteveConfiguration config) { - return config.getAuth().getPasswordEncoder(); + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveConfiguration config) throws Exception { - final String prefix = config.getPaths().getManagerMapping(); + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + final String prefix = "/manager"; RequestMatcher toOverview = request -> { String param = request.getParameter("backToOverview"); @@ -69,7 +56,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveConfigura return http.authorizeHttpRequests(req -> req.requestMatchers( "/static/**", - config.getPaths().getSoapMapping() + "/**", + "/soap/**", OcppWebSocketConfiguration.PATH_INFIX + "**", "/WEB-INF/views/**" // https://github.com/spring-projects/spring-security/issues/13285#issuecomment-1579097065 ) @@ -124,7 +111,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveConfigura // for // all access, there is a global default behaviour from spring security: enable CSRF for all POSTs. // we need to disable CSRF for SOAP paths explicitly. - .csrf(c -> c.ignoringRequestMatchers(config.getPaths().getSoapMapping() + "/**")) + .csrf(c -> c.ignoringRequestMatchers("/soap/**")) .sessionManagement(req -> req.invalidSessionUrl(prefix + "/signin")) .formLogin(req -> req.loginPage(prefix + "/signin").permitAll()) .logout(req -> req.logoutUrl(prefix + "/signout")) @@ -134,10 +121,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, SteveConfigura @Bean @Order(1) - public SecurityFilterChain apiKeyFilterChain( - HttpSecurity http, SteveConfiguration config, ApiAuthenticationManager apiAuthenticationManager) + public SecurityFilterChain apiKeyFilterChain(HttpSecurity http, ApiAuthenticationManager apiAuthenticationManager) throws Exception { - return http.securityMatcher(config.getPaths().getApiMapping() + "/**") + return http.securityMatcher("/api/**") .csrf(k -> k.disable()) .sessionManagement(k -> k.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilter(new BasicAuthenticationFilter(apiAuthenticationManager, apiAuthenticationManager)) diff --git a/steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java b/steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java index 5d224c7ef..e8ea8bea5 100644 --- a/steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java +++ b/steve/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.github.zafarkhaja.semver.Version; -import de.rwth.idsg.steve.SteveConfiguration; +import de.rwth.idsg.steve.config.SteveProperties; import de.rwth.idsg.steve.web.dto.ReleaseReport; import de.rwth.idsg.steve.web.dto.ReleaseResponse; import lombok.extern.slf4j.Slf4j; @@ -61,9 +61,9 @@ public class GithubReleaseCheckService implements ReleaseCheckService { private final String steveVersion; private final RestTemplate restTemplate; - public GithubReleaseCheckService(SteveConfiguration config) { - this.steveVersion = config.getSteveVersion(); - this.restTemplate = createRestTemplate(createGitHubMapper(), "steve/" + config.getSteveVersion()); + public GithubReleaseCheckService(SteveProperties steveProperties) { + this.steveVersion = steveProperties.getVersion(); + this.restTemplate = createRestTemplate(createGitHubMapper(), "steve/" + steveProperties.getVersion()); } private static RestTemplate createRestTemplate(ObjectMapper mapper, String userAgent) { diff --git a/steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java b/steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java deleted file mode 100644 index e3af81910..000000000 --- a/steve/src/main/java/de/rwth/idsg/steve/utils/SteveConfigurationReader.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import de.rwth.idsg.steve.ApplicationProfile; -import de.rwth.idsg.steve.SteveConfiguration; -import lombok.experimental.UtilityClass; -import org.jspecify.annotations.Nullable; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; - -import java.util.Locale; - -@UtilityClass -public class SteveConfigurationReader { - - public static SteveConfiguration readSteveConfiguration(String name) { - PropertiesFileLoader p = new PropertiesFileLoader(name); - - var profile = ApplicationProfile.fromName(p.getString("profile")); - System.setProperty("spring.profiles.active", profile.name().toLowerCase(Locale.getDefault())); - - PasswordEncoder encoder = new BCryptPasswordEncoder(); - - var config = SteveConfiguration.builder() - .paths(SteveConfiguration.Paths.builder() - .rootMapping("/") - .managerMapping("/manager") - .apiMapping("/api") - .soapMapping("/services") - .websocketMapping("/websocket") - .routerEndpointPath("/CentralSystemService") - .contextPath(sanitizeContextPath( - p.getOptionalString("context.path").orElse(null))) - .build()) - .timeZoneId("UTC") - .steveVersion(p.getString("steve.version")) - .gitDescribe( - useFallbackIfNotSet(p.getOptionalString("git.describe").orElse(null), null)) - .profile(profile) - .jetty(SteveConfiguration.Jetty.builder() - .serverHost(p.getString("server.host")) - .gzipEnabled(p.getBoolean("server.gzip.enabled")) - .httpEnabled(p.getBoolean("http.enabled")) - .httpPort(p.getInt("http.port")) - .httpsEnabled(p.getBoolean("https.enabled")) - .httpsPort(p.getInt("https.port")) - .keyStorePath(p.getOptionalString("keystore.path").orElse(null)) - .keyStorePassword( - p.getOptionalString("keystore.password").orElse(null)) - .build()) - .db(SteveConfiguration.DB - .builder() - .jdbcUrl(p.getString("db.jdbc.url")) - .userName(p.getString("db.user")) - .password(p.getString("db.password")) - .sqlLogging(p.getBoolean("db.sql.logging")) - .schema(p.getOptionalString("db.schema").orElse(null)) - .schemaSource(p.getOptionalString("db.schema-source").orElse("stevedb")) - .build()) - .auth(SteveConfiguration.Auth.builder() - .passwordEncoder(encoder) - .userName(p.getString("auth.user")) - .encodedPassword(encoder.encode(p.getString("auth.password"))) - .build()) - .webApi(SteveConfiguration.WebApi.builder() - .headerKey(p.getOptionalString("webapi.key").orElse(null)) - .headerValue(p.getOptionalString("webapi.value").orElse(null)) - .build()) - .ocpp(SteveConfiguration.Ocpp.builder() - .autoRegisterUnknownStations(p.getOptionalBoolean("auto.register.unknown.stations") - .orElse(false)) - .chargeBoxIdValidationRegex(p.getOptionalString("charge-box-id.validation.regex") - .orElse(null)) - .wsSessionSelectStrategy(p.getString("ws.session.select.strategy")) - .build()) - .build(); - - config.postConstruct(); - return config; - } - - private static @Nullable String useFallbackIfNotSet(@Nullable String value, @Nullable String fallback) { - if (value == null) { - // if the property is optional, value will be null - return fallback; - } else if (value.startsWith("${")) { - // property value variables start with "${" (if maven is not used, the value will not be set) - return fallback; - } else { - return value; - } - } - - private static String sanitizeContextPath(@Nullable String s) { - if (s == null || "/".equals(s)) { - return ""; - - } else if (s.startsWith("/")) { - return s; - - } else { - return "/" + s; - } - } -} diff --git a/steve/src/main/resources/application.yml b/steve/src/main/resources/application.yml new file mode 100644 index 000000000..ce23f826d --- /dev/null +++ b/steve/src/main/resources/application.yml @@ -0,0 +1,97 @@ +server: + port: ${PORT:8180} + servlet: + context-path: /${context.path:} + jetty: + threads: + min: 4 + max: 50 + idle-timeout: 1m + forward-headers-strategy: framework + ssl: + enabled: ${https.enabled:false} + port: ${https.port:8443} + key-store: ${keystore.path:} + key-store-password: ${keystore.password:} + +spring: + application: + name: steve + profiles: + active: ${profile:dev} + main: + banner-mode: "off" + datasource: + url: jdbc:mysql://${db.host:localhost}:${db.port:3306}/${db.schema:stevedb}?sslMode=PREFERRED&serverTimezone=UTC + username: ${db.user:steve} + password: ${db.password:changeme} + driver-class-name: com.mysql.cj.jdbc.Driver + jooq: + sql-dialect: MYSQL + flyway: + init-sql: SET default_storage_engine=InnoDB + out-of-order: true + table: schema_version + clean-disabled: true + locations: classpath:db/migration + mvc: + view: + prefix: /WEB-INF/views/ + suffix: .jsp + +steve: + auth: + user: ${auth.user:admin} + password: ${auth.password:1234} + webapi: + key: ${webapi.key:STEVE-API-KEY} + value: ${webapi.value:} + ocpp: + ws-session-select-strategy: ALWAYS_LAST + auto-register-unknown-stations: false + charge-box-id-validation-regex: + soap: + router-endpoint-path: / + version: ${project.version} + git-describe: ${git.commit.id.describe:} + +logging: + level: + de: + rwth: + idsg: + steve: INFO + org: + springframework: INFO + jooq: INFO + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + file: + name: logs/steve.log + +--- +spring: + profiles: dev + jooq: + sql-dialect: MYSQL + flyway: + enabled: true + +logging: + level: + de.rwth.idsg.steve: DEBUG + org.jooq: DEBUG + +--- +spring: + profiles: prod + jooq: + sql-dialect: MYSQL + flyway: + enabled: true + +logging: + level: + de.rwth.idsg.steve: INFO + org.jooq: INFO diff --git a/steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java b/steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java deleted file mode 100644 index 7f9895f3b..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import com.fasterxml.jackson.databind.ObjectMapper; -import de.rwth.idsg.steve.ocpp.OcppVersion; -import de.rwth.idsg.steve.ocpp.ws.JsonObjectMapper; -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.utils.OcppJsonChargePoint; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import lombok.extern.slf4j.Slf4j; -import ocpp.cs._2015._10.AuthorizationStatus; -import ocpp.cs._2015._10.AuthorizeRequest; -import ocpp.cs._2015._10.AuthorizeResponse; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.BootNotificationResponse; -import ocpp.cs._2015._10.HeartbeatResponse; -import ocpp.cs._2015._10.RegistrationStatus; -import org.eclipse.jetty.websocket.core.exception.UpgradeException; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; - -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static de.rwth.idsg.steve.utils.Helpers.getWsPath; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.assertj.core.api.Assertions.fail; -import static org.assertj.core.api.BDDAssertions.then; - -/** - * @author Sevket Goekay - * @since 21.03.2018 - */ -@Slf4j -public class ApplicationJsonTest { - - private static final ObjectMapper OCPP_MAPPER = JsonObjectMapper.createObjectMapper(); - - private static final String REGISTERED_CHARGE_BOX_ID = __DatabasePreparer__.getRegisteredChargeBoxId(); - private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag(); - - private static String path; - private static Application app; - - @BeforeAll - public static void init() throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - assertThat(config.getProfile()).isEqualTo(ApplicationProfile.TEST); - __DatabasePreparer__.prepare(config); - - path = getWsPath(config); - - app = new Application(config, new LogFileRetriever()); - app.start(); - } - - @AfterAll - public static void destroy() throws Exception { - if (app != null) { - app.stop(); - } - __DatabasePreparer__.cleanUp(); - } - - @Test - public void testOcpp12() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, OcppVersion.V_12, REGISTERED_CHARGE_BOX_ID, path); - chargePoint.start(); - - var boot = new ocpp.cs._2010._08.BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()); - - chargePoint.prepare( - boot, - ocpp.cs._2010._08.BootNotificationResponse.class, - bootResponse -> - assertThat(bootResponse.getStatus()).isEqualTo(ocpp.cs._2010._08.RegistrationStatus.ACCEPTED), - error -> fail()); - - var auth = new ocpp.cs._2010._08.AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG); - - chargePoint.prepare( - auth, - ocpp.cs._2010._08.AuthorizeResponse.class, - authResponse -> assertThat(authResponse.getIdTagInfo().getStatus()) - .isEqualTo(ocpp.cs._2010._08.AuthorizationStatus.ACCEPTED), - error -> fail()); - - chargePoint.processAndClose(); - } - - @Test - public void testOcpp15() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, OcppVersion.V_15, REGISTERED_CHARGE_BOX_ID, path); - chargePoint.start(); - - var boot = new ocpp.cs._2012._06.BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()); - - chargePoint.prepare( - boot, - ocpp.cs._2012._06.BootNotificationResponse.class, - bootResponse -> - assertThat(bootResponse.getStatus()).isEqualTo(ocpp.cs._2012._06.RegistrationStatus.ACCEPTED), - error -> fail()); - - var auth = new ocpp.cs._2012._06.AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG); - - chargePoint.prepare( - auth, - ocpp.cs._2012._06.AuthorizeResponse.class, - authResponse -> assertThat(authResponse.getIdTagInfo().getStatus()) - .isEqualTo(ocpp.cs._2012._06.AuthorizationStatus.ACCEPTED), - error -> fail()); - - chargePoint.processAndClose(); - } - - @Test - public void testOcpp16() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, OcppVersion.V_16, REGISTERED_CHARGE_BOX_ID, path); - chargePoint.start(); - - var boot = new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()); - - chargePoint.prepare( - boot, - BootNotificationResponse.class, - bootResponse -> assertThat(bootResponse.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED), - error -> fail()); - - var auth = new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG); - - chargePoint.prepare( - auth, - AuthorizeResponse.class, - authResponse -> - assertThat(authResponse.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED), - error -> fail()); - - chargePoint.processAndClose(); - } - - @Test - public void testWithMissingVersion() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, (String) null, REGISTERED_CHARGE_BOX_ID, path); - var thrown = catchThrowable(chargePoint::start); - then(thrown) - .isInstanceOf(RuntimeException.class) - .hasRootCauseInstanceOf(UpgradeException.class) - .rootCause() - .satisfies(c -> { - ; - var ue = (UpgradeException) c; - assertThat(ue.getResponseStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); - }); - } - - @Test - public void testWithWrongVersion() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, "ocpp1234", REGISTERED_CHARGE_BOX_ID, path); - var thrown = catchThrowable(chargePoint::start); - then(thrown) - .isInstanceOf(RuntimeException.class) - .hasRootCauseInstanceOf(UpgradeException.class) - .rootCause() - .satisfies(c -> { - var ue = (UpgradeException) c; - assertThat(ue.getResponseStatusCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); - }); - } - - @Test - public void testWithUnauthorizedStation() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, OcppVersion.V_16, "unauth1234", path); - var thrown = catchThrowable(chargePoint::start); - then(thrown) - .isInstanceOf(RuntimeException.class) - .hasRootCauseInstanceOf(UpgradeException.class) - .rootCause() - .satisfies(c -> { - var ue = (UpgradeException) c; - assertThat(ue.getResponseStatusCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); - }); - } - - /** - * https://github.com/steve-community/steve/issues/1109 - */ - @Test - public void testWithNullPayload() { - var chargePoint = new OcppJsonChargePoint(OCPP_MAPPER, OcppVersion.V_16, REGISTERED_CHARGE_BOX_ID, path); - chargePoint.start(); - - chargePoint.prepare( - null, - "Heartbeat", - HeartbeatResponse.class, - response -> assertThat(response.getCurrentTime()).isNotNull(), - error -> fail()); - - chargePoint.processAndClose(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java b/steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java deleted file mode 100644 index 1a0bab569..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/ApplicationTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import jakarta.xml.ws.WebServiceException; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp12; -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp15; -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * @author Sevket Goekay - * @since 10.03.2018 - */ -@Slf4j -public class ApplicationTest { - - private static final String REGISTERED_CHARGE_BOX_ID = __DatabasePreparer__.getRegisteredChargeBoxId(); - private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag(); - - private static String path; - private static Application app; - - @BeforeAll - public static void init() throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - assertThat(config.getProfile()).isEqualTo(ApplicationProfile.TEST); - __DatabasePreparer__.prepare(config); - - path = getHttpPath(config); - - app = new Application(config, new LogFileRetriever()); - app.start(); - } - - @AfterAll - public static void destroy() throws Exception { - if (app != null) { - app.stop(); - } - __DatabasePreparer__.cleanUp(); - } - - @Test - public void testOcpp12() { - var client = getForOcpp12(path); - - var boot = client.bootNotification( - new ocpp.cs._2010._08.BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - REGISTERED_CHARGE_BOX_ID); - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isEqualTo(ocpp.cs._2010._08.RegistrationStatus.ACCEPTED); - - var auth = client.authorize( - new ocpp.cs._2010._08.AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID); - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(ocpp.cs._2010._08.AuthorizationStatus.ACCEPTED); - } - - @Test - public void testOcpp15() { - var client = getForOcpp15(path); - - var boot = client.bootNotification( - new ocpp.cs._2012._06.BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - REGISTERED_CHARGE_BOX_ID); - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isEqualTo(ocpp.cs._2012._06.RegistrationStatus.ACCEPTED); - - ocpp.cs._2012._06.AuthorizeResponse auth = client.authorize( - new ocpp.cs._2012._06.AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID); - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(ocpp.cs._2012._06.AuthorizationStatus.ACCEPTED); - } - - /** - * WebServiceException because we are sending an AuthorizeRequest from a random/unknown station. - */ - @Test - public void testOcpp16() { - var client = getForOcpp16(path); - - 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); - }); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/OperationalTestSoapOCPP16.java b/steve/src/test/java/de/rwth/idsg/steve/OperationalTestSoapOCPP16.java deleted file mode 100644 index 60f609084..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/OperationalTestSoapOCPP16.java +++ /dev/null @@ -1,600 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.ocpp.OcppProtocol; -import de.rwth.idsg.steve.ocpp.soap.MessageHeaderInterceptor; -import de.rwth.idsg.steve.repository.ReservationStatus; -import de.rwth.idsg.steve.service.CentralSystemService16_Service; -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import lombok.extern.slf4j.Slf4j; -import ocpp.cs._2015._10.AuthorizationStatus; -import ocpp.cs._2015._10.AuthorizeRequest; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.CentralSystemService; -import ocpp.cs._2015._10.ChargePointErrorCode; -import ocpp.cs._2015._10.ChargePointStatus; -import ocpp.cs._2015._10.HeartbeatRequest; -import ocpp.cs._2015._10.MeterValue; -import ocpp.cs._2015._10.MeterValuesRequest; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.SampledValue; -import ocpp.cs._2015._10.StartTransactionRequest; -import ocpp.cs._2015._10.StatusNotificationRequest; -import ocpp.cs._2015._10.StopTransactionRequest; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.OffsetDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.List; -import jakarta.xml.ws.WebServiceException; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.byLessThan; - -/** - * @author Andreas Heuvels - * @since 22.03.18 - */ -@Slf4j -public class OperationalTestSoapOCPP16 { - - private static final String REGISTERED_CHARGE_BOX_ID = __DatabasePreparer__.getRegisteredChargeBoxId(); - private static final String REGISTERED_CHARGE_BOX_ID_2 = __DatabasePreparer__.getRegisteredChargeBoxId2(); - private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag(); - private static final int numConnectors = 5; - - private static SteveConfiguration config; - private static String path; - private static Application app; - - @BeforeAll - public static void initClass() throws Exception { - config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - assertThat(config.getProfile()).isEqualTo(ApplicationProfile.TEST); - - path = getHttpPath(config); - - app = new Application(config, new LogFileRetriever()); - app.start(); - } - - @AfterAll - public static void destroyClass() throws Exception { - app.stop(); - } - - @BeforeEach - public void init() { - __DatabasePreparer__.prepare(config); - } - - @AfterEach - public void destroy() { - __DatabasePreparer__.cleanUp(); - } - - @Test - public void testUnregisteredCP() { - assertThat(config.getOcpp().isAutoRegisterUnknownStations()).isFalse(); - - var client = getForOcpp16(path); - - var boot = client.bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - getRandomString()); - - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isNotEqualTo(RegistrationStatus.ACCEPTED); - } - - /** - * Reason: We started to check registration status by intercepting every SOAP message other than BootNotification - * in {@link MessageHeaderInterceptor} and throw exception if station is not registered and auto-register is - * disabled (and therefore early-exit the processing pipeline of the message). - * - * In case of BootNotification, the expected behaviour is to set RegistrationStatus.REJECTED in response, as done - * by {@link CentralSystemService16_Service#bootNotification(BootNotificationRequest, String, OcppProtocol)}. - * Therefore, no exception. This case is tested by {@link OperationalTestSoapOCPP16#testUnregisteredCP()} already. - * - * WS/JSON stations cannot connect at all if they are not registered, as ensured by {@link OcppWebSocketUpgrader}. - */ - @Test - public void testUnregisteredCPWithInterceptor() { - assertThat(config.getOcpp().isAutoRegisterUnknownStations()).isFalse(); - var client = getForOcpp16(path); - var request = new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG); - var chargeBoxIdentity = getRandomString(); - assertThatExceptionOfType(WebServiceException.class).isThrownBy(() -> { - client.authorize(request, chargeBoxIdentity); - }); - } - - @Test - public void testRegisteredCP() { - var client = getForOcpp16(path); - - initStationWithBootNotification(client); - - var details = __DatabasePreparer__.getCBDetails(REGISTERED_CHARGE_BOX_ID); - assertThat(details.getOcppProtocol()).contains("ocpp1.6"); - } - - @Test - public void testRegisteredIdTag() { - var client = getForOcpp16(path); - - var auth = client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID); - - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - } - - @Test - public void testUnregisteredIdTag() { - var client = getForOcpp16(path); - - var auth = client.authorize(new AuthorizeRequest().withIdTag(getRandomString()), REGISTERED_CHARGE_BOX_ID); - - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.INVALID); - } - - @Test - public void testInTransactionStatusOfIdTag() { - var client = getForOcpp16(path); - - var start = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(2) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0), - REGISTERED_CHARGE_BOX_ID); - - assertThat(start).isNotNull(); - assertThat(start.getTransactionId()).isGreaterThan(0); - assertThat(__DatabasePreparer__.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction()) - .isTrue(); - - var stop = client.stopTransaction( - new StopTransactionRequest() - .withTransactionId(start.getTransactionId()) - .withTimestamp(OffsetDateTime.now()) - .withIdTag(REGISTERED_OCPP_TAG) - .withMeterStop(30), - REGISTERED_CHARGE_BOX_ID); - - assertThat(stop).isNotNull(); - assertThat(__DatabasePreparer__.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction()) - .isFalse(); - } - - /** - * https://github.com/steve-community/steve/issues/217 - * https://github.com/steve-community/steve/issues/219 - */ - @Test - public void testAuthorizationStatus() { - var client = getForOcpp16(path); - - { - var auth1 = - client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID); - assertThat(auth1.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - - var start1 = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(2) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0), - REGISTERED_CHARGE_BOX_ID); - assertThat(start1.getTransactionId()).isGreaterThan(0); - assertThat(start1.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - - var auth1Retry = - client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID); - assertThat(auth1Retry.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - } - - { - var auth2 = - client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID_2); - assertThat(auth2.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - - var start2 = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(2) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0), - REGISTERED_CHARGE_BOX_ID_2); - assertThat(start2.getTransactionId()).isGreaterThan(0); - assertThat(start2.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.CONCURRENT_TX); - - var auth2Retry = - client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID_2); - assertThat(auth2Retry.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - } - } - - @Test - public void testStatusNotification() { - var client = getForOcpp16(path); - - // ------------------------------------------------------------------------- - // init the station and verify db connector status values - // ------------------------------------------------------------------------- - - initStationWithBootNotification(client); - - // test all status enum values - for (ChargePointStatus chargePointStatus : ChargePointStatus.values()) { - // status for numConnectors connectors + connector 0 (main controller of CP) - for (var i = 0; i <= numConnectors; i++) { - var status = client.statusNotification( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(chargePointStatus) - .withConnectorId(i) - .withTimestamp(OffsetDateTime.now()), - REGISTERED_CHARGE_BOX_ID); - assertThat(status).isNotNull(); - } - - var connectorStatusList = __DatabasePreparer__.getChargePointConnectorStatus(); - for (var connectorStatus : connectorStatusList) { - assertThat(connectorStatus.getStatus()).isEqualTo(chargePointStatus.value()); - assertThat(connectorStatus.getErrorCode()).isEqualTo(ChargePointErrorCode.NO_ERROR.value()); - } - } - - // ------------------------------------------------------------------------- - // send status for faulty connector and verify db values - // ------------------------------------------------------------------------- - - int faultyConnectorId = 1; - - var statusConnectorError = client.statusNotification( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.HIGH_TEMPERATURE) - .withStatus(ChargePointStatus.FAULTED) - .withConnectorId(faultyConnectorId) - .withTimestamp(OffsetDateTime.now()), - REGISTERED_CHARGE_BOX_ID); - assertThat(statusConnectorError).isNotNull(); - - var connectorStatusList = __DatabasePreparer__.getChargePointConnectorStatus(); - for (var connectorStatus : connectorStatusList) { - if (connectorStatus.getConnectorId() == faultyConnectorId) { - assertThat(connectorStatus.getStatus()).isEqualTo(ChargePointStatus.FAULTED.value()); - assertThat(connectorStatus.getErrorCode()).isEqualTo(ChargePointErrorCode.HIGH_TEMPERATURE.value()); - } else { - assertThat(connectorStatus.getStatus()).isNotEqualTo(ChargePointStatus.FAULTED.value()); - assertThat(connectorStatus.getErrorCode()).isNotEqualTo(ChargePointErrorCode.HIGH_TEMPERATURE.value()); - } - } - } - - @Test - public void testReservation() { - int usedConnectorId = 1; - - var client = getForOcpp16(path); - - // ------------------------------------------------------------------------- - // init the station and make reservation - // ------------------------------------------------------------------------- - - initStationWithBootNotification(client); - initConnectorsWithStatusNotification(client); - - var reservationId = __DatabasePreparer__.makeReservation(usedConnectorId); - - // ------------------------------------------------------------------------- - // startTransaction (invalid reservationId) - // ------------------------------------------------------------------------- - - var nonExistingReservationId = reservationId + 17; - - var startInvalid = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(usedConnectorId) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0) - .withReservationId(nonExistingReservationId), - REGISTERED_CHARGE_BOX_ID); - assertThat(startInvalid).isNotNull(); - - // validate that the transaction is written to db, even though reservation was invalid - var transactions = __DatabasePreparer__.getTransactions(); - assertThat(transactions).hasSize(1); - assertThat(transactions.get(0).getId()).isEqualTo(startInvalid.getTransactionId()); - - // make sure that this invalid reservation had no side effects - { - var reservations = __DatabasePreparer__.getReservations(); - assertThat(reservations).hasSize(1); - var res = reservations.get(0); - assertThat(res.getId()).isEqualTo(reservationId); - assertThat(res.getStatus()).isEqualTo(ReservationStatus.ACCEPTED.value()); - } - - // ------------------------------------------------------------------------- - // startTransaction (idtag and connectorid are not the ones from the reservation) - // ------------------------------------------------------------------------- - - var startWrongTag = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(3) - .withIdTag(getRandomString()) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0) - .withReservationId(reservationId), - REGISTERED_CHARGE_BOX_ID); - assertThat(startWrongTag).isNotNull(); - - { - var reservations = __DatabasePreparer__.getReservations(); - assertThat(reservations).hasSize(1); - var res = reservations.get(0); - assertThat(res.getStatus()).isEqualTo(ReservationStatus.ACCEPTED.value()); - assertThat(res.getTransactionId()).isNull(); - } - - // ------------------------------------------------------------------------- - // startTransaction (valid) - // ------------------------------------------------------------------------- - - var startValidId = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(usedConnectorId) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0) - .withReservationId(reservationId), - REGISTERED_CHARGE_BOX_ID); - assertThat(startValidId).isNotNull(); - var transactionIdValid = startValidId.getTransactionId(); - - { - var reservations = __DatabasePreparer__.getReservations(); - assertThat(reservations).hasSize(1); - var res = reservations.get(0); - assertThat(res.getStatus()).isEqualTo(ReservationStatus.USED.value()); - assertThat(res.getTransactionId()).isEqualTo(transactionIdValid); - } - - // ------------------------------------------------------------------------- - // startTransaction (valid again) - // ------------------------------------------------------------------------- - - var startValidIdUsedTwice = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(usedConnectorId) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0) - .withReservationId(reservationId), - REGISTERED_CHARGE_BOX_ID); - assertThat(startValidIdUsedTwice).isNotNull(); - - { - var reservations = __DatabasePreparer__.getReservations(); - assertThat(reservations).hasSize(1); - var res = reservations.get(0); - assertThat(res.getStatus()).isEqualTo(ReservationStatus.USED.value()); - assertThat(res.getTransactionId()).isEqualTo(transactionIdValid); - } - } - - @Test - public void testWithMeterValuesAndTransactionData() { - testBody(getMeterValues(), getTransactionData()); - } - - @Test - public void testWithMeterValues() { - testBody(getMeterValues(), null); - } - - @Test - public void testWithTransactionData() { - testBody(null, getTransactionData()); - } - - @Test - public void testWithoutMeterValuesAndTransactionData() { - testBody(null, null); - } - - private void testBody(List meterValues, List transactionData) { - final var usedConnectorId = 1; - - var client = getForOcpp16(path); - - initStationWithBootNotification(client); - initConnectorsWithStatusNotification(client); - - // heartbeat - var heartbeat = client.heartbeat(new HeartbeatRequest(), REGISTERED_CHARGE_BOX_ID); - assertThat(heartbeat).isNotNull(); - - // Auth - var auth = client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), REGISTERED_CHARGE_BOX_ID); - // Simple request, not much done here - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(AuthorizationStatus.ACCEPTED); - - // startTransaction - var startTimeStamp = OffsetDateTime.now(); - var start = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(usedConnectorId) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(startTimeStamp) - .withMeterStart(0), - REGISTERED_CHARGE_BOX_ID); - assertThat(start).isNotNull(); - - var transactionID = start.getTransactionId(); - - var allTransactions = __DatabasePreparer__.getTransactionRecords(); - assertThat(allTransactions).hasSize(1); - - { - var t = allTransactions.get(0); - assertThat(t.getStartTimestamp()) - .isCloseTo(startTimeStamp.toLocalDateTime(), byLessThan(1, ChronoUnit.SECONDS)); - assertThat(t.getStartValue()).isEqualTo("0"); - - assertThat(t.getStopTimestamp()).isNull(); - assertThat(t.getStopReason()).isNull(); - assertThat(t.getStopValue()).isNull(); - } - - // status - var statusStart = client.statusNotification( - new StatusNotificationRequest() - .withStatus(ChargePointStatus.CHARGING) - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withConnectorId(0) - .withTimestamp(OffsetDateTime.now()), - REGISTERED_CHARGE_BOX_ID); - assertThat(statusStart).isNotNull(); - - // send meterValues - if (meterValues != null) { - var meter = client.meterValues( - new MeterValuesRequest() - .withConnectorId(usedConnectorId) - .withTransactionId(transactionID) - .withMeterValue(meterValues), - REGISTERED_CHARGE_BOX_ID); - assertThat(meter).isNotNull(); - checkMeterValues(meterValues, transactionID); - } - - // stopTransaction - var stopTimeStamp = OffsetDateTime.now(); - var stopValue = 30; - var stop = client.stopTransaction( - new StopTransactionRequest() - .withTransactionId(transactionID) - .withTransactionData(transactionData) - .withTimestamp(stopTimeStamp) - .withIdTag(REGISTERED_OCPP_TAG) - .withMeterStop(stopValue), - REGISTERED_CHARGE_BOX_ID); - - { - assertThat(stop).isNotNull(); - var transactionsStop = __DatabasePreparer__.getTransactionRecords(); - assertThat(transactionsStop).hasSize(1); - var t = transactionsStop.get(0); - assertThat(t.getStopTimestamp()) - .isCloseTo(stopTimeStamp.toLocalDateTime(), byLessThan(1, ChronoUnit.SECONDS)); - assertThat(t.getStopValue()).isEqualTo(Integer.toString(stopValue)); - - if (transactionData != null) { - checkMeterValues(transactionData, transactionID); - } - } - - // status - var statusStop = client.statusNotification( - new StatusNotificationRequest() - .withStatus(ChargePointStatus.AVAILABLE) - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withConnectorId(usedConnectorId) - .withTimestamp(OffsetDateTime.now()), - REGISTERED_CHARGE_BOX_ID); - assertThat(statusStop).isNotNull(); - } - - private void initStationWithBootNotification(CentralSystemService client) { - var boot = client.bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - REGISTERED_CHARGE_BOX_ID); - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED); - } - - private void initConnectorsWithStatusNotification(CentralSystemService client) { - for (var i = 0; i <= numConnectors; i++) { - var statusBoot = client.statusNotification( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.AVAILABLE) - .withConnectorId(i) - .withTimestamp(OffsetDateTime.now()), - REGISTERED_CHARGE_BOX_ID); - assertThat(statusBoot).isNotNull(); - } - } - - private void checkMeterValues(List meterValues, int transactionPk) { - var details = __DatabasePreparer__.getDetails(transactionPk); - - // iterate over all created meter values - for (var meterValue : meterValues) { - var sampledValues = meterValue.getSampledValue(); - assertThat(sampledValues).isEmpty(); - 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; - } - } - assertThat(thisValueFound).isTrue(); - } - } - - private List getTransactionData() { - return Arrays.asList( - createMeterValue("0.0"), createMeterValue("10.0"), createMeterValue("20.0"), createMeterValue("30.0")); - } - - private List getMeterValues() { - return Arrays.asList(createMeterValue("3.0"), createMeterValue("13.0"), createMeterValue("23.0")); - } - - private static MeterValue createMeterValue(String val) { - return new MeterValue().withTimestamp(OffsetDateTime.now()).withSampledValue(new SampledValue().withValue(val)); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/StressTest.java b/steve/src/test/java/de/rwth/idsg/steve/StressTest.java deleted file mode 100644 index 70c1d87f1..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/StressTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import ocpp.cs._2015._10.MeterValue; -import ocpp.cs._2015._10.SampledValue; - -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 10.05.2018 - */ -public abstract class StressTest { - - // higher values -> more stress - // - protected static final int THREAD_COUNT = 50; - protected static final int REPEAT_COUNT_PER_THREAD = 50; - - // lower values -> more stress - // - // reason: these only specify the size of the values "bag" from which a test picks a value randomly. if there are - // less values to pick from, it is more likely that tests will use the same value at the same time. this produces - // more overhead for steve (especially db) when multiple threads "fight" for inserting/updating a db row/cell. - // - protected static final int ID_TAG_COUNT = 50; - protected static final int CHARGE_BOX_COUNT = THREAD_COUNT; - protected static final int CONNECTOR_COUNT_PER_CHARGE_BOX = 25; - - protected void attack() throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - assertThat(config.getProfile()).isEqualTo(ApplicationProfile.TEST); - assertThat(config.getOcpp().isAutoRegisterUnknownStations()).isTrue(); - - __DatabasePreparer__.prepare(config); - - var app = new Application(config, new LogFileRetriever()); - try { - app.start(); - attackInternal(); - } finally { - try { - app.stop(); - } finally { - __DatabasePreparer__.cleanUp(); - } - } - } - - protected abstract void attackInternal() throws Exception; - - protected static List getMeterValues(int transactionStart, int transactionStop) { - final var size = 4; - var delta = (transactionStop - transactionStart) / size; - if (delta == 0) { - return Collections.emptyList(); - } - - var list = new ArrayList(size); - for (var i = 0; i < size; i++) { - var meterValue = transactionStart + delta * (i + 1); - list.add(createMeterValue(meterValue)); - } - return list; - } - - protected static MeterValue createMeterValue(int val) { - return new MeterValue() - .withTimestamp(OffsetDateTime.now()) - .withSampledValue(new SampledValue().withValue(Integer.toString(val))); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java b/steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java deleted file mode 100644 index 1417db13c..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import com.fasterxml.jackson.databind.ObjectMapper; -import de.rwth.idsg.steve.ocpp.OcppVersion; -import de.rwth.idsg.steve.ocpp.ws.JsonObjectMapper; -import de.rwth.idsg.steve.utils.OcppJsonChargePoint; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.StressTester; -import lombok.RequiredArgsConstructor; -import ocpp.cs._2015._10.AuthorizationStatus; -import ocpp.cs._2015._10.AuthorizeRequest; -import ocpp.cs._2015._10.AuthorizeResponse; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.BootNotificationResponse; -import ocpp.cs._2015._10.ChargePointErrorCode; -import ocpp.cs._2015._10.ChargePointStatus; -import ocpp.cs._2015._10.HeartbeatRequest; -import ocpp.cs._2015._10.HeartbeatResponse; -import ocpp.cs._2015._10.MeterValuesRequest; -import ocpp.cs._2015._10.MeterValuesResponse; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.StartTransactionRequest; -import ocpp.cs._2015._10.StartTransactionResponse; -import ocpp.cs._2015._10.StatusNotificationRequest; -import ocpp.cs._2015._10.StatusNotificationResponse; -import ocpp.cs._2015._10.StopTransactionRequest; -import ocpp.cs._2015._10.StopTransactionResponse; - -import java.time.OffsetDateTime; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; - -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static de.rwth.idsg.steve.utils.Helpers.getRandomStrings; -import static de.rwth.idsg.steve.utils.Helpers.getWsPath; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -/** - * @author Sevket Goekay - * @since 09.05.2018 - */ -@RequiredArgsConstructor -public class StressTestJsonOCPP16 extends StressTest { - - private static final OcppVersion VERSION = OcppVersion.V_16; - private static final ObjectMapper OCPP_MAPPER = JsonObjectMapper.createObjectMapper(); - - private final String path; - - public static void main(String[] args) throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - var path = getWsPath(config); - new StressTestJsonOCPP16(path).attack(); - } - - protected void attackInternal() throws Exception { - final var idTags = getRandomStrings(ID_TAG_COUNT); - final var chargeBoxIds = getRandomStrings(CHARGE_BOX_COUNT); - - var runnable = new StressTester.Runnable() { - - private final ThreadLocal threadLocalChargePoint = new ThreadLocal<>(); - - @Override - public void beforeRepeat() { - var localRandom = ThreadLocalRandom.current(); - - var chargeBoxId = chargeBoxIds.get(localRandom.nextInt(chargeBoxIds.size())); - threadLocalChargePoint.set(new OcppJsonChargePoint(OCPP_MAPPER, VERSION, chargeBoxId, path)); - - var chargePoint = threadLocalChargePoint.get(); - chargePoint.start(); - - chargePoint.prepare( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - BootNotificationResponse.class, - bootResponse -> assertThat(bootResponse.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED), - error -> fail()); - } - - @Override - public void toRepeat() { - var localRandom = ThreadLocalRandom.current(); - - var chargePoint = threadLocalChargePoint.get(); - - var idTag = idTags.get(localRandom.nextInt(idTags.size())); - var connectorId = localRandom.nextInt(1, CONNECTOR_COUNT_PER_CHARGE_BOX + 1); - var transactionStart = localRandom.nextInt(0, Integer.MAX_VALUE); - var transactionStop = localRandom.nextInt(transactionStart + 1, Integer.MAX_VALUE); - - chargePoint.prepare( - new HeartbeatRequest(), - HeartbeatResponse.class, - response -> assertThat(response).isNotNull(), - error -> fail()); - - for (var i = 0; i <= CONNECTOR_COUNT_PER_CHARGE_BOX; i++) { - chargePoint.prepare( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.AVAILABLE) - .withConnectorId(i) - .withTimestamp(OffsetDateTime.now()), - StatusNotificationResponse.class, - response -> assertThat(response).isNotNull(), - error -> fail()); - } - - chargePoint.prepare( - new AuthorizeRequest().withIdTag(idTag), - AuthorizeResponse.class, - response -> assertThat(response.getIdTagInfo().getStatus()) - .isNotEqualTo(AuthorizationStatus.ACCEPTED), - error -> fail()); - - final var transactionId = new AtomicInteger(-1); - - chargePoint.prepare( - new StartTransactionRequest() - .withConnectorId(connectorId) - .withIdTag(idTag) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(transactionStart), - StartTransactionResponse.class, - response -> { - assertThat(response).isNotNull(); - transactionId.set(response.getTransactionId()); - }, - error -> fail()); - - // wait for StartTransactionResponse to arrive, since we need the transactionId from now on - chargePoint.process(); - - chargePoint.prepare( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.CHARGING) - .withConnectorId(connectorId) - .withTimestamp(OffsetDateTime.now()), - StatusNotificationResponse.class, - response -> assertThat(response).isNotNull(), - error -> fail()); - - chargePoint.prepare( - new MeterValuesRequest() - .withConnectorId(connectorId) - .withTransactionId(transactionId.get()) - .withMeterValue(getMeterValues(transactionStart, transactionStop)), - MeterValuesResponse.class, - response -> assertThat(response).isNotNull(), - error -> fail()); - - chargePoint.prepare( - new StopTransactionRequest() - .withTransactionId(transactionId.get()) - .withTimestamp(OffsetDateTime.now()) - .withIdTag(idTag) - .withMeterStop(transactionStop), - StopTransactionResponse.class, - response -> assertThat(response).isNotNull(), - error -> fail()); - - chargePoint.prepare( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.AVAILABLE) - .withConnectorId(connectorId) - .withTimestamp(OffsetDateTime.now()), - StatusNotificationResponse.class, - response -> assertThat(response).isNotNull(), - error -> fail()); - - chargePoint.process(); - } - - @Override - public void afterRepeat() { - threadLocalChargePoint.get().close(); - } - }; - - var tester = new StressTester(THREAD_COUNT, REPEAT_COUNT_PER_THREAD); - tester.test(runnable); - tester.shutDown(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java b/steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java deleted file mode 100644 index cdda91bd5..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.StressTester; -import lombok.RequiredArgsConstructor; -import ocpp.cs._2015._10.AuthorizationStatus; -import ocpp.cs._2015._10.AuthorizeRequest; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.ChargePointErrorCode; -import ocpp.cs._2015._10.ChargePointStatus; -import ocpp.cs._2015._10.HeartbeatRequest; -import ocpp.cs._2015._10.MeterValuesRequest; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.StartTransactionRequest; -import ocpp.cs._2015._10.StatusNotificationRequest; -import ocpp.cs._2015._10.StopTransactionRequest; - -import java.time.OffsetDateTime; -import java.util.concurrent.ThreadLocalRandom; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static de.rwth.idsg.steve.utils.Helpers.getRandomStrings; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 18.04.2018 - */ -@RequiredArgsConstructor -public class StressTestSoapOCPP16 extends StressTest { - - private final String path; - - public static void main(String[] args) throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - var path = getHttpPath(config); - new StressTestSoapOCPP16(path).attack(); - } - - protected void attackInternal() throws Exception { - final var idTags = getRandomStrings(ID_TAG_COUNT); - final var chargeBoxIds = getRandomStrings(CHARGE_BOX_COUNT); - - var runnable = new StressTester.Runnable() { - - private final ThreadLocal threadLocalChargeBoxId = new ThreadLocal<>(); - - @Override - public void beforeRepeat() { - var client = getForOcpp16(path); - var localRandom = ThreadLocalRandom.current(); - - threadLocalChargeBoxId.set(chargeBoxIds.get(localRandom.nextInt(chargeBoxIds.size()))); - - var chargeBoxId = threadLocalChargeBoxId.get(); - - // to insert threadLocalChargeBoxId into db - var boot = client.bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - chargeBoxId); - assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED); - } - - @Override - public void toRepeat() { - var client = getForOcpp16(path); - var localRandom = ThreadLocalRandom.current(); - - var chargeBoxId = threadLocalChargeBoxId.get(); - - var idTag = idTags.get(localRandom.nextInt(idTags.size())); - var connectorId = localRandom.nextInt(1, CONNECTOR_COUNT_PER_CHARGE_BOX + 1); - var transactionStart = localRandom.nextInt(0, Integer.MAX_VALUE); - var transactionStop = localRandom.nextInt(transactionStart + 1, Integer.MAX_VALUE); - - var heartbeat = client.heartbeat(new HeartbeatRequest(), chargeBoxId); - assertThat(heartbeat).isNotNull(); - - for (var i = 0; i <= CONNECTOR_COUNT_PER_CHARGE_BOX; i++) { - var status = client.statusNotification( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.AVAILABLE) - .withConnectorId(i) - .withTimestamp(OffsetDateTime.now()), - chargeBoxId); - assertThat(status).isNotNull(); - } - - var auth = client.authorize(new AuthorizeRequest().withIdTag(idTag), chargeBoxId); - assertThat(auth.getIdTagInfo().getStatus()).isNotEqualTo(AuthorizationStatus.ACCEPTED); - - var start = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(connectorId) - .withIdTag(idTag) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(transactionStart), - chargeBoxId); - assertThat(start).isNotNull(); - - var statusStart = client.statusNotification( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.CHARGING) - .withConnectorId(connectorId) - .withTimestamp(OffsetDateTime.now()), - chargeBoxId); - assertThat(statusStart).isNotNull(); - - var meter = client.meterValues( - new MeterValuesRequest() - .withConnectorId(connectorId) - .withTransactionId(start.getTransactionId()) - .withMeterValue(getMeterValues(transactionStart, transactionStop)), - chargeBoxId); - assertThat(meter).isNotNull(); - - var stop = client.stopTransaction( - new StopTransactionRequest() - .withTransactionId(start.getTransactionId()) - .withTimestamp(OffsetDateTime.now()) - .withIdTag(idTag) - .withMeterStop(transactionStop), - chargeBoxId); - assertThat(stop).isNotNull(); - - var statusStop = client.statusNotification( - new StatusNotificationRequest() - .withErrorCode(ChargePointErrorCode.NO_ERROR) - .withStatus(ChargePointStatus.AVAILABLE) - .withConnectorId(connectorId) - .withTimestamp(OffsetDateTime.now()), - chargeBoxId); - assertThat(statusStop).isNotNull(); - } - - @Override - public void afterRepeat() {} - }; - - var tester = new StressTester(THREAD_COUNT, REPEAT_COUNT_PER_THREAD); - tester.test(runnable); - tester.shutDown(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/TypeStoreTest.java b/steve/src/test/java/de/rwth/idsg/steve/TypeStoreTest.java deleted file mode 100644 index 1e35b41f4..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/TypeStoreTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve; - -import de.rwth.idsg.steve.ocpp.ws.ocpp12.Ocpp12TypeStore; -import de.rwth.idsg.steve.ocpp.ws.ocpp15.Ocpp15TypeStore; -import de.rwth.idsg.steve.ocpp.ws.ocpp16.Ocpp16TypeStore; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 10.03.2018 - */ -public class TypeStoreTest { - - @Test - public void ocpp12Test() { - var typeStore = new Ocpp12TypeStore(); - - var actionResponse = typeStore.findActionResponse(new ocpp.cp._2010._08.ResetRequest()); - assertThat(actionResponse).isNotNull(); - assertThat(actionResponse.getAction()).isEqualTo("Reset"); - assertThat(actionResponse.getResponseClass()).isEqualTo(ocpp.cp._2010._08.ResetResponse.class); - - var requestClass = typeStore.findRequestClass("BootNotification"); - assertThat(requestClass).isSameAs(ocpp.cs._2010._08.BootNotificationRequest.class); - } - - @Test - public void ocpp15Test() { - var typeStore = new Ocpp15TypeStore(); - - var actionResponse = typeStore.findActionResponse(new ocpp.cp._2012._06.UpdateFirmwareRequest()); - assertThat(actionResponse).isNotNull(); - assertThat(actionResponse.getAction()).isEqualTo("UpdateFirmware"); - assertThat(actionResponse.getResponseClass()).isEqualTo(ocpp.cp._2012._06.UpdateFirmwareResponse.class); - - var requestClass = typeStore.findRequestClass("BootNotification"); - assertThat(requestClass).isSameAs(ocpp.cs._2012._06.BootNotificationRequest.class); - } - - @Test - public void ocpp16Test() { - var typeStore = new Ocpp16TypeStore(); - - var actionResponse = typeStore.findActionResponse(new ocpp.cp._2015._10.UpdateFirmwareRequest()); - assertThat(actionResponse).isNotNull(); - assertThat(actionResponse.getAction()).isEqualTo("UpdateFirmware"); - assertThat(actionResponse.getResponseClass()).isEqualTo(ocpp.cp._2015._10.UpdateFirmwareResponse.class); - - var requestClass = typeStore.findRequestClass("BootNotification"); - assertThat(requestClass).isSameAs(ocpp.cs._2015._10.BootNotificationRequest.class); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java b/steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java deleted file mode 100644 index 65a84041d..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue1219.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.issues; - -import de.rwth.idsg.steve.SteveException; -import de.rwth.idsg.steve.repository.dto.InsertTransactionParams; -import de.rwth.idsg.steve.repository.dto.OcppTag; -import de.rwth.idsg.steve.repository.dto.Transaction; -import de.rwth.idsg.steve.repository.dto.TransactionStopEventActor; -import de.rwth.idsg.steve.repository.dto.UpdateTransactionParams; -import de.rwth.idsg.steve.repository.impl.AddressRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.ChargePointRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.OcppServerRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.OcppTagRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.ReservationRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.TransactionRepositoryImpl; -import de.rwth.idsg.steve.web.dto.OcppTagForm; -import de.rwth.idsg.steve.web.dto.OcppTagQueryForm; -import de.rwth.idsg.steve.web.dto.TransactionQueryForm; -import lombok.RequiredArgsConstructor; -import org.jooq.DSLContext; -import org.jooq.SQLDialect; -import org.jooq.impl.DSL; -import org.jooq.impl.DataSourceConnectionProvider; -import org.jooq.impl.DefaultConfiguration; -import org.jooq.tools.jdbc.SingleConnectionDataSource; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.IntStream; - -@RequiredArgsConstructor -public class Issue1219 { - - private static final String url = "jdbc:mysql://localhost:3306/stevedb"; - private static final String userName = "steve"; - private static final String password = "changeme"; - - private final DSLContext ctx; - - public static void main(String[] args) throws Exception { - Connection con = DriverManager.getConnection(url, userName, password); - - org.jooq.Configuration conf = new DefaultConfiguration() - .set(SQLDialect.MYSQL) - .set(new DataSourceConnectionProvider(new SingleConnectionDataSource(con))); - - DSLContext ctx = DSL.using(conf); - - Issue1219 issue1219 = new Issue1219(ctx); - - var ocppTags = issue1219.insertOcppTags(1_000); - System.out.println("inserted ocppTags"); - - var chargeBoxIds = issue1219.insertChargeBoxes(500); - System.out.println("inserted chargeBoxIds"); - - var transactionIds = issue1219.insertStartTransactions(10_000, ocppTags, chargeBoxIds); - System.out.println("inserted transaction_starts"); - - var stoppedTransactionIds = issue1219.insertStopTransactions(transactionIds); - System.out.println("inserted transaction_stops: " + stoppedTransactionIds.size()); - - System.out.println("-- TESTING --"); - issue1219.realTest(); - } - - private void realTest() { - var repository = new OcppTagRepositoryImpl(ctx); - - var start = Instant.now(); - List values = repository.getOverview(new OcppTagQueryForm()); - var stop = Instant.now(); - - System.out.println("took " + Duration.between(start, stop).toMillis() + " ms"); - } - - private List insertStopTransactions(List insertedTransactionIds) { - var ocppServerRepository = new OcppServerRepositoryImpl(ctx, new ReservationRepositoryImpl(ctx)); - var transactionRepository = new TransactionRepositoryImpl(ctx); - - List stopped = new ArrayList<>(); - for (Integer transactionId : insertedTransactionIds) { - if (!ThreadLocalRandom.current().nextBoolean()) { - continue; - } - - TransactionQueryForm form = new TransactionQueryForm(); - form.setTransactionPk(transactionId); - Transaction transaction = - transactionRepository.getTransactions(form).get(0); - - var stopTimestamp = transaction.getStartTimestamp().plus(1, ChronoUnit.HOURS); - UpdateTransactionParams p = UpdateTransactionParams.builder() - .chargeBoxId(transaction.getChargeBoxId()) - .transactionId(transaction.getId()) - .stopTimestamp(stopTimestamp) - .eventTimestamp(stopTimestamp) - .stopMeterValue(transaction.getStartValue() + "0") - .eventActor(TransactionStopEventActor.STATION) - .build(); - - ocppServerRepository.updateTransaction(p); - System.out.println("stopped transaction " + transactionId); - stopped.add(transactionId); - } - return stopped; - } - - private List insertStartTransactions(int count, List ocppTags, List chargeBoxIds) { - var repository = new OcppServerRepositoryImpl(ctx, new ReservationRepositoryImpl(ctx)); - - List transactionIds = new ArrayList<>(); - for (int i = 0; i < count; i++) { - var now = Instant.now(); - InsertTransactionParams params = InsertTransactionParams.builder() - .idTag(ocppTags.get(ThreadLocalRandom.current().nextInt(0, ocppTags.size()))) - .chargeBoxId(chargeBoxIds.get(ThreadLocalRandom.current().nextInt(0, chargeBoxIds.size()))) - .connectorId(ThreadLocalRandom.current().nextInt(4)) - .startMeterValue(String.valueOf(ThreadLocalRandom.current().nextLong(5_000, 20_000))) - .startTimestamp(now) - .eventTimestamp(now) - .build(); - int transactionId = repository.insertTransaction(params); - System.out.println("started transaction " + transactionId); - transactionIds.add(transactionId); - } - return transactionIds; - } - - private List insertChargeBoxes(int count) { - var repository = new ChargePointRepositoryImpl(ctx, new AddressRepositoryImpl(ctx)); - - List ids = IntStream.range(0, count) - .mapToObj(val -> UUID.randomUUID().toString()) - .toList(); - repository.addChargePointList(ids); - - return ids; - } - - private List insertOcppTags(int count) { - var repository = new OcppTagRepositoryImpl(ctx); - - List idTags = IntStream.range(0, count) - .mapToObj(val -> UUID.randomUUID().toString()) - .toList(); - List insertedTags = new ArrayList<>(); - - for (String idTag : idTags) { - OcppTagForm form = new OcppTagForm(); - form.setIdTag(idTag); - form.setExpiryDate(getRandomExpiry()); - form.setParentIdTag(getRandomParentIdTag(idTag, insertedTags)); - form.setMaxActiveTransactionCount(ThreadLocalRandom.current().nextInt(1, 4)); - - try { - repository.addOcppTag(form); - } catch (SteveException.AlreadyExists e) { - // because the referenced idTag was not inserted yet. just inserted without it. - form.setParentIdTag(null); - repository.addOcppTag(form); - } - insertedTags.add(idTag); - } - - return insertedTags; - } - - private static String getRandomParentIdTag(String current, List source) { - if (source.isEmpty()) { - return null; - } - if (ThreadLocalRandom.current().nextBoolean()) { - String parent = source.get(ThreadLocalRandom.current().nextInt(0, source.size())); - if (!Objects.equals(parent, current)) { - return parent; - } - } - return null; - } - - private static Instant getRandomExpiry() { - if (ThreadLocalRandom.current().nextBoolean()) { - return Instant.now().plus(ThreadLocalRandom.current().nextInt(1, 365), ChronoUnit.DAYS); - } - return null; - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java b/steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java deleted file mode 100644 index 4d9b2da23..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue72.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.issues; - -import de.rwth.idsg.steve.StressTest; -import de.rwth.idsg.steve.utils.Helpers; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.StressTester; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import lombok.RequiredArgsConstructor; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.CentralSystemService; -import ocpp.cs._2015._10.MeterValue; -import ocpp.cs._2015._10.MeterValuesRequest; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.SampledValue; -import ocpp.cs._2015._10.StartTransactionRequest; -import ocpp.cs._2015._10.StopTransactionRequest; -import ocpp.cs._2015._10.UnitOfMeasure; - -import java.time.OffsetDateTime; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * https://github.com/steve-community/steve/issues/72 - * - * @author Sevket Goekay - * @since 27.06.2018 - */ -@RequiredArgsConstructor -public class Issue72 extends StressTest { - - private final String path; - - public static void main(String[] args) throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - var path = getHttpPath(config); - new Issue72(path).attack(); - } - - protected void attackInternal() throws Exception { - var idTag = __DatabasePreparer__.getRegisteredOcppTag(); - var chargeBoxId = Helpers.getRandomString(); - - var startDateTime = OffsetDateTime.now(); - var stopDateTime = startDateTime.plusHours(5); - - var connectorId = 2; - - var meterStart = 444; - var meterStop = 99999; - - var boot = getForOcpp16(path) - .bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - chargeBoxId); - assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED); - - var start = getForOcpp16(path) - .startTransaction( - new StartTransactionRequest() - .withConnectorId(connectorId) - .withIdTag(idTag) - .withTimestamp(startDateTime) - .withMeterStart(meterStart), - chargeBoxId); - assertThat(start).isNotNull(); - - var transactionId = start.getTransactionId(); - - var runnable = new StressTester.Runnable() { - - private final ThreadLocal threadLocalClient = new ThreadLocal<>(); - - @Override - public void beforeRepeat() { - threadLocalClient.set(getForOcpp16(path)); - } - - @Override - public void toRepeat() { - var mvr = threadLocalClient - .get() - .meterValues( - new MeterValuesRequest() - .withConnectorId(connectorId) - .withTransactionId(transactionId) - .withMeterValue(new MeterValue() - .withTimestamp(stopDateTime) - .withSampledValue(new SampledValue() - .withValue("555") - .withUnit(UnitOfMeasure.WH))), - chargeBoxId); - assertThat(mvr).isNotNull(); - - var stop = threadLocalClient - .get() - .stopTransaction( - new StopTransactionRequest() - .withTransactionId(transactionId) - .withTimestamp(stopDateTime) - .withIdTag(idTag) - .withMeterStop(meterStop), - chargeBoxId); - assertThat(stop).isNotNull(); - } - - @Override - public void afterRepeat() {} - }; - - var tester = new StressTester(THREAD_COUNT, REPEAT_COUNT_PER_THREAD); - tester.test(runnable); - tester.shutDown(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java b/steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java deleted file mode 100644 index e80f7ec72..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.issues; - -import com.google.common.net.MediaType; -import de.rwth.idsg.steve.StressTest; -import de.rwth.idsg.steve.utils.Helpers; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.StressTester; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import lombok.RequiredArgsConstructor; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.CentralSystemService; -import ocpp.cs._2015._10.Measurand; -import ocpp.cs._2015._10.MeterValue; -import ocpp.cs._2015._10.MeterValuesRequest; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.SampledValue; -import ocpp.cs._2015._10.StartTransactionRequest; -import ocpp.cs._2015._10.UnitOfMeasure; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.RequestBuilder; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClients; - -import java.time.OffsetDateTime; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * https://github.com/steve-community/steve/issues/72 - * - * @author Sevket Goekay - * @since 27.06.2018 - */ -@RequiredArgsConstructor -public class Issue72LowLevelSoap extends StressTest { - - private final String path; - - public static void main(String[] args) throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - var path = getHttpPath(config); - new Issue72LowLevelSoap(path).attack(); - } - - protected void attackInternal() throws Exception { - var idTag = __DatabasePreparer__.getRegisteredOcppTag(); - var chargeBoxId = Helpers.getRandomString(); - - var startDateTime = OffsetDateTime.parse("2018-06-27T01:10:10Z"); - var stopDateTime = OffsetDateTime.parse("2018-06-27T04:10:10Z"); - - var connectorId = 2; - - var meterStart = 444; - var meterStop = 99999; - - var boot = getForOcpp16(path) - .bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - chargeBoxId); - assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED); - - var start = getForOcpp16(path) - .startTransaction( - new StartTransactionRequest() - .withConnectorId(connectorId) - .withIdTag(idTag) - .withTimestamp(startDateTime) - .withMeterStart(meterStart), - chargeBoxId); - assertThat(start).isNotNull(); - - var transactionId = start.getTransactionId(); - - var body = buildRequest(path, chargeBoxId, transactionId, idTag, stopDateTime, meterStop); - var contentType = ContentType.create( - MediaType.SOAP_XML_UTF_8.type(), - MediaType.SOAP_XML_UTF_8.charset().orNull()); - - var req = RequestBuilder.post(path) - .addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction") - .setEntity(new StringEntity(body, contentType)) - .build(); - - var httpClient = HttpClients.createDefault(); - - var runnable = new StressTester.Runnable() { - - private final ThreadLocal threadLocalClient = new ThreadLocal<>(); - - @Override - public void beforeRepeat() { - threadLocalClient.set(getForOcpp16(path)); - } - - @Override - public void toRepeat() { - - 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(); - - try { - httpClient.execute(req, httpResponse -> { - if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new RuntimeException("Not OK"); - } - return null; - }); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void afterRepeat() {} - }; - - try { - var tester = new StressTester(100, 100); - tester.test(runnable); - tester.shutDown(); - } finally { - httpClient.close(); - } - } - - private static String buildRequest( - String path, String chargeBoxId, int transactionId, String idTag, OffsetDateTime stop, int meterStop) { - return "" - + "/StopTransaction" - + "urn:uuid:47c9e1d9-a278-4e9c-8f08-565c29d86167" - + "" - + path + "" - + "
http://www.w3.org/2005/08/addressing/anonymous
" - + "
" - + chargeBoxId + "" + "
" - + "" - + transactionId + "" + "" - + idTag + "" + "" - + stop + "" + "" - + meterStop + "" + "
"; - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue73Fix.java b/steve/src/test/java/de/rwth/idsg/steve/issues/Issue73Fix.java deleted file mode 100644 index c84d48ca0..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue73Fix.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.issues; - -import com.google.common.collect.Lists; -import de.rwth.idsg.steve.Application; -import de.rwth.idsg.steve.ApplicationProfile; -import de.rwth.idsg.steve.utils.LogFileRetriever; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.__DatabasePreparer__; -import ocpp.cs._2015._10.AuthorizationStatus; -import ocpp.cs._2015._10.AuthorizeRequest; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.CentralSystemService; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.StartTransactionRequest; - -import java.time.OffsetDateTime; -import java.util.List; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * https://github.com/steve-community/steve/issues/73 - * - * @author Sevket Goekay - * @since 02.07.2018 - */ -public class Issue73Fix { - - private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag(); - - private static String path; - - public static void main(String[] args) throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - assertThat(config.getProfile()).isEqualTo(ApplicationProfile.TEST); - assertThat(config.getOcpp().isAutoRegisterUnknownStations()).isTrue(); - - __DatabasePreparer__.prepare(config); - - path = getHttpPath(config); - - var app = new Application(config, new LogFileRetriever()); - try { - app.start(); - test(); - } finally { - try { - app.stop(); - } finally { - __DatabasePreparer__.cleanUp(); - } - } - } - - private static void test() { - var client = getForOcpp16(path); - - var chargeBox1 = getRandomString(); - var chargeBox2 = getRandomString(); - - sendBoot(client, Lists.newArrayList(chargeBox1, chargeBox2)); - - sendAuth(client, chargeBox1, AuthorizationStatus.ACCEPTED); - - sendStartTx(client, chargeBox1); - - sendAuth(client, chargeBox1, AuthorizationStatus.ACCEPTED); - - sendAuth(client, chargeBox2, AuthorizationStatus.CONCURRENT_TX); - } - - private static void sendBoot(CentralSystemService client, List chargeBoxIdList) { - for (var chargeBoxId : chargeBoxIdList) { - var boot = client.bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - chargeBoxId); - assertThat(boot).isNotNull(); - assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED); - } - } - - private static void sendAuth(CentralSystemService client, String chargeBoxId, AuthorizationStatus expected) { - var auth = client.authorize(new AuthorizeRequest().withIdTag(REGISTERED_OCPP_TAG), chargeBoxId); - assertThat(auth).isNotNull(); - assertThat(auth.getIdTagInfo().getStatus()).isEqualTo(expected); - } - - private static void sendStartTx(CentralSystemService client, String chargeBoxId) { - var start = client.startTransaction( - new StartTransactionRequest() - .withConnectorId(2) - .withIdTag(REGISTERED_OCPP_TAG) - .withTimestamp(OffsetDateTime.now()) - .withMeterStart(0), - chargeBoxId); - assertThat(start).isNotNull(); - assertThat(start.getTransactionId()).isGreaterThan(0); - assertThat(__DatabasePreparer__.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction()) - .isTrue(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java b/steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java deleted file mode 100644 index 5aa211159..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/issues/Issue81.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.issues; - -import de.rwth.idsg.steve.StressTest; -import de.rwth.idsg.steve.utils.Helpers; -import de.rwth.idsg.steve.utils.SteveConfigurationReader; -import de.rwth.idsg.steve.utils.StressTester; -import lombok.RequiredArgsConstructor; -import ocpp.cs._2015._10.BootNotificationRequest; -import ocpp.cs._2015._10.CentralSystemService; -import ocpp.cs._2015._10.RegistrationStatus; -import ocpp.cs._2015._10.StartTransactionRequest; - -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.concurrent.ThreadLocalRandom; - -import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16; -import static de.rwth.idsg.steve.utils.Helpers.getHttpPath; -import static de.rwth.idsg.steve.utils.Helpers.getRandomString; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 19.07.2018 - */ -@RequiredArgsConstructor -public class Issue81 extends StressTest { - - private final String path; - - public static void main(String[] args) throws Exception { - var config = SteveConfigurationReader.readSteveConfiguration("main.properties"); - var path = getHttpPath(config); - new Issue81(path).attack(); - } - - protected void attackInternal() throws Exception { - var runnable = new StressTester.Runnable() { - - private final ThreadLocal client = new ThreadLocal<>(); - private final ThreadLocal chargeBoxId = new ThreadLocal<>(); - private final ThreadLocal txRequest = new ThreadLocal<>(); - private final ThreadLocal txId = new ThreadLocal<>(); - - @Override - public void beforeRepeat() { - client.set(getForOcpp16(path)); - chargeBoxId.set(Helpers.getRandomString()); - - var boot = getForOcpp16(path) - .bootNotification( - new BootNotificationRequest() - .withChargePointVendor(getRandomString()) - .withChargePointModel(getRandomString()), - chargeBoxId.get()); - assertThat(boot.getStatus()).isEqualTo(RegistrationStatus.ACCEPTED); - - var req = new StartTransactionRequest() - .withConnectorId(ThreadLocalRandom.current().nextInt(1, 8)) - .withIdTag(Helpers.getRandomString()) - .withTimestamp(OffsetDateTime.now(ZoneOffset.UTC)) - .withMeterStart(ThreadLocalRandom.current().nextInt(0, 1_000_000)); - txRequest.set(req); - - Integer t1 = sendStartTx(client.get(), txRequest.get(), chargeBoxId.get()); - txId.set(t1); - } - - @Override - public void toRepeat() { - var t2 = sendStartTx(client.get(), txRequest.get(), chargeBoxId.get()); - assertThat(t2).isEqualTo(txId.get()); - } - - @Override - public void afterRepeat() {} - }; - - var tester = new StressTester(THREAD_COUNT, REPEAT_COUNT_PER_THREAD); - tester.test(runnable); - tester.shutDown(); - } - - private static Integer sendStartTx(CentralSystemService client, StartTransactionRequest req, String chargeBoxId) { - var start = client.startTransaction(req, chargeBoxId); - assertThat(start).isNotNull(); - return start.getTransactionId(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/ocpp/OcppVersionTest.java b/steve/src/test/java/de/rwth/idsg/steve/ocpp/OcppVersionTest.java deleted file mode 100644 index 6af1d6041..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/ocpp/OcppVersionTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.ocpp; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -import static org.assertj.core.api.Assertions.assertThat; - -public class OcppVersionTest { - - @ParameterizedTest - @EnumSource(OcppVersion.class) - public void testFromValue(OcppVersion input) { - var toTest = input.getValue(); - var inputBack = OcppVersion.fromValue(toTest); - assertThat(inputBack).isEqualTo(input); - } - - @ParameterizedTest - @EnumSource(OcppTransport.class) - public void testToProtocol(OcppTransport transport) { - for (var version : OcppVersion.values()) { - var protocol = version.toProtocol(transport); - - assertThat(protocol.getTransport()).isEqualTo(transport); - assertThat(protocol.getVersion()).isEqualTo(version); - } - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java b/steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java deleted file mode 100644 index d44252c2b..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandlerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.ocpp.ws; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class OcppWebSocketHandshakeHandlerTest { - - @Test - public void testGetLastBitFromUrl_empty() { - var in = ""; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEmpty(); - } - - @Test - public void testGetLastBitFromUrl_null() { - String in = null; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEmpty(); - } - - @Test - public void testGetLastBitFromUrl_successFull() { - var in = "https://www.google.com/steve/websocket/CentralSystemService/BBEI12"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("BBEI12"); - } - - @Test - public void testGetLastBitFromUrl_noPostfix() { - var in = "/steve/websocket/CentralSystemService/"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEmpty(); - } - - @Test - public void testGetLastBitFromUrl_successPartial() { - var in = "/steve/websocket/CentralSystemService/BBEI12"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("BBEI12"); - } - - @Test - public void testGetLastBitFromUrl_successWithPercent() { - var in = "/steve/websocket/CentralSystemService/BBE%I12"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("BBE%I12"); - } - - @Test - public void testGetLastBitFromUrl_successWithDash() { - var in = "/steve/websocket/CentralSystemService/BBE-I12"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("BBE-I12"); - } - - @Test - public void testGetLastBitFromUrl_successWithSpace() { - var in = "/steve/websocket/CentralSystemService/BBE I12"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("BBE I12"); - } - - @Test - public void testGetLastBitFromUrl_successWithExtraSlash() { - var in = "/steve/websocket/CentralSystemService/889/BBEI12"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("889/BBEI12"); - } - - @Test - public void testGetLastBitFromUrl_successComplex() { - var in = "/steve/websocket/CentralSystemService/%889 /BBEI12-"; - var out = OcppWebSocketHandshakeHandler.getLastBitFromUrl(in); - assertThat(out).isEqualTo("%889 /BBEI12-"); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java b/steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java deleted file mode 100644 index ea585c05c..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/utils/Helpers.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import de.rwth.idsg.steve.SteveConfiguration; -import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; -import org.apache.cxf.ws.addressing.WSAddressingFeature; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import jakarta.xml.ws.soap.SOAPBinding; - -/** - * @author Andreas Heuvels - * @since 06.04.18 - */ -public class Helpers { - - public static String getRandomString() { - return UUID.randomUUID().toString(); - } - - public static List getRandomStrings(int size) { - List list = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - list.add(getRandomString()); - } - return list; - } - - public static String getHttpPath(SteveConfiguration config) { - String prefix; - int port; - - if (config.getJetty().isHttpEnabled()) { - prefix = "http://"; - port = config.getJetty().getHttpPort(); - } else if (config.getJetty().isHttpsEnabled()) { - 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(); - } - - public static String getWsPath(SteveConfiguration config) { - String prefix; - int port; - - if (config.getJetty().isHttpEnabled()) { - prefix = "ws://"; - port = config.getJetty().getHttpPort(); - } else if (config.getJetty().isHttpsEnabled()) { - prefix = "wss://"; - port = config.getJetty().getHttpsPort(); - } else { - throw new RuntimeException(); - } - - return prefix + config.getJetty().getServerHost() + ":" + port - + config.getPaths().getContextPath() - + config.getPaths().getWebsocketMapping() - + config.getPaths().getRouterEndpointPath() + "/"; - } - - public static ocpp.cs._2015._10.CentralSystemService getForOcpp16(String path) { - JaxWsProxyFactoryBean f = getBean(path); - f.setServiceClass(ocpp.cs._2015._10.CentralSystemService.class); - return (ocpp.cs._2015._10.CentralSystemService) f.create(); - } - - public static ocpp.cs._2012._06.CentralSystemService getForOcpp15(String path) { - JaxWsProxyFactoryBean f = getBean(path); - f.setServiceClass(ocpp.cs._2012._06.CentralSystemService.class); - return (ocpp.cs._2012._06.CentralSystemService) f.create(); - } - - public static ocpp.cs._2010._08.CentralSystemService getForOcpp12(String path) { - JaxWsProxyFactoryBean f = getBean(path); - f.setServiceClass(ocpp.cs._2010._08.CentralSystemService.class); - return (ocpp.cs._2010._08.CentralSystemService) f.create(); - } - - private static JaxWsProxyFactoryBean getBean(String endpointAddress) { - JaxWsProxyFactoryBean f = new JaxWsProxyFactoryBean(); - f.setBindingId(SOAPBinding.SOAP12HTTP_BINDING); - f.getFeatures().add(new WSAddressingFeature()); - f.setAddress(endpointAddress); - return f; - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java b/steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java deleted file mode 100644 index 7215cc0f1..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/utils/OcppJsonChargePoint.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.NullNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import de.rwth.idsg.ocpp.jaxb.RequestType; -import de.rwth.idsg.ocpp.jaxb.ResponseType; -import de.rwth.idsg.steve.ocpp.OcppVersion; -import de.rwth.idsg.steve.ocpp.ws.data.CommunicationContext; -import de.rwth.idsg.steve.ocpp.ws.data.ErrorCode; -import de.rwth.idsg.steve.ocpp.ws.data.MessageType; -import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonCall; -import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonError; -import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonMessage; -import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonResponse; -import de.rwth.idsg.steve.ocpp.ws.data.OcppJsonResult; -import de.rwth.idsg.steve.ocpp.ws.pipeline.Serializer; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.StatusCode; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.function.Consumer; - -import static java.util.Collections.synchronizedMap; -import static org.eclipse.jetty.websocket.api.Callback.NOOP; - -/** - * @author Sevket Goekay - * @since 21.03.2018 - */ -@Slf4j -@WebSocket -public class OcppJsonChargePoint { - - private final ObjectMapper ocppMapper; - private final String version; - private final String chargeBoxId; - private final String connectionPath; - private final Map responseContextMap; - private final Map requestContextMap; - private final MessageDeserializer deserializer; - private final WebSocketClient client; - private final CountDownLatch closeHappenedSignal; - private final Serializer serializer; - - private final Thread testerThread; - private RuntimeException testerThreadInterruptReason; - - private CountDownLatch receivedMessagesSignal; - private Session session; - - public OcppJsonChargePoint(ObjectMapper ocppMapper, OcppVersion version, String chargeBoxId, String pathPrefix) { - this(ocppMapper, version.getValue(), chargeBoxId, pathPrefix); - } - - public OcppJsonChargePoint(ObjectMapper ocppMapper, String ocppVersion, String chargeBoxId, String pathPrefix) { - this.ocppMapper = ocppMapper; - this.version = ocppVersion; - this.chargeBoxId = chargeBoxId; - this.connectionPath = pathPrefix + chargeBoxId; - this.responseContextMap = - synchronizedMap(new LinkedHashMap<>()); // because we want to keep the insertion order of test cases - this.requestContextMap = new ConcurrentHashMap<>(); - this.deserializer = new MessageDeserializer(); - this.client = new WebSocketClient(); - this.closeHappenedSignal = new CountDownLatch(1); - this.testerThread = Thread.currentThread(); - this.serializer = new Serializer(ocppMapper); - } - - @OnWebSocketOpen - public void onConnect(Session session) { - this.session = session; - } - - @OnWebSocketClose - public void onClose(Session session, int statusCode, String reason) { - this.session = null; - this.closeHappenedSignal.countDown(); - } - - @OnWebSocketError - public void onError(Session session, Throwable throwable) { - log.error("Exception", throwable); - } - - @OnWebSocketMessage - public void onMessage(Session session, String msg) { - try { - var ocppMsg = deserializer.extract(msg); - switch (ocppMsg) { - case OcppJsonResult result -> { - var ctx = responseContextMap.remove(result.getMessageId()); - ctx.responseHandler.accept(result.getPayload()); - } - case OcppJsonError error -> { - var ctx = responseContextMap.remove(error.getMessageId()); - ctx.errorHandler.accept(error); - } - case OcppJsonCall call -> { - handleCall(call); - } - default -> throw new IllegalStateException("Unexpected value: " + ocppMsg); - } - } catch (Exception e) { - log.error("Exception", e); - } finally { - if (receivedMessagesSignal != null) { - receivedMessagesSignal.countDown(); - } - } - } - - public void start() { - try { - var request = new ClientUpgradeRequest(); - if (version != null) { - request.setSubProtocols(version); - } - - client.start(); - - var connect = client.connect(this, new URI(connectionPath), request); - connect.get(); // block until session is created - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - public void prepare( - RequestType request, - Class responseClass, - Consumer responseHandler, - Consumer errorHandler) { - prepare(request, getOperationName(request), responseClass, responseHandler, errorHandler); - } - - public void prepare( - RequestType payload, - String action, - Class responseClass, - Consumer responseHandler, - Consumer errorHandler) { - var messageId = UUID.randomUUID().toString(); - - var call = new OcppJsonCall(); - call.setMessageId(messageId); - call.setPayload(payload); - call.setAction(action); - - // session is null, because we do not need org.springframework.web.socket.WebSocketSession - var ctx = new CommunicationContext(null, chargeBoxId); - ctx.setOutgoingMessage(call); - - serializer.accept(ctx); - - var resCtx = new ResponseContext(ctx.getOutgoingString(), responseClass, responseHandler, errorHandler); - responseContextMap.put(messageId, resCtx); - } - - public void process() { - var requestCount = requestContextMap.size(); - var responseCount = responseContextMap.size(); - receivedMessagesSignal = new CountDownLatch(requestCount + responseCount); - - // copy the values in a new list to be iterated over, because otherwise we get a - // ConcurrentModificationException, - // since the onMessage(..) uses the same responseContextMap to remove an item while looping over its items here. - var values = new ArrayList<>(responseContextMap.values()); - - // send all messages - for (var ctx : values) { - try { - session.sendText(ctx.outgoingMessage, NOOP); - } catch (Exception e) { - log.error("Exception", e); - } - } - - // wait for all responses to arrive and be processed - try { - receivedMessagesSignal.await(); - } catch (InterruptedException e) { - if (testerThreadInterruptReason != null) { - throw testerThreadInterruptReason; - } - throw new RuntimeException(e); - } - } - - public void close() { - try { - // "enqueue" a graceful close - session.close(StatusCode.NORMAL, "Finished", NOOP); - - // wait for close to happen - closeHappenedSignal.await(); - - // well, stop the client - client.stop(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public void processAndClose() { - process(); - close(); - } - - // ------------------------------------------------------------------------- - // Private helpers - // ------------------------------------------------------------------------- - - private static String getOperationName(RequestType requestType) { - var s = requestType.getClass().getSimpleName(); - if (s.endsWith("Request")) { - s = s.substring(0, s.length() - 7); - } - return s; - } - - private void handleCall(OcppJsonCall call) { - JsonNode responsePayload = null; // TODO - try { - var node = ocppMapper - .createArrayNode() - .add(MessageType.CALL_RESULT.getTypeNr()) - .add(call.getMessageId()) - .add(responsePayload); - - var str = ocppMapper.writeValueAsString(node); - session.sendText(str, NOOP); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static class ResponseContext { - private final String outgoingMessage; - private final Class responseClass; - private final Consumer responseHandler; - private final Consumer errorHandler; - - @SuppressWarnings("unchecked") - private ResponseContext( - String outgoingMessage, - Class responseClass, - Consumer responseHandler, - Consumer errorHandler) { - this.outgoingMessage = outgoingMessage; - this.responseClass = (Class) responseClass; - this.responseHandler = (Consumer) responseHandler; - this.errorHandler = errorHandler; - } - } - - private class MessageDeserializer { - - private OcppJsonMessage extract(String msg) throws Exception { - - try (var parser = ocppMapper.getFactory().createParser(msg)) { - parser.nextToken(); // set cursor to '[' - - parser.nextToken(); - var messageTypeNr = parser.getIntValue(); - - parser.nextToken(); - var messageId = parser.getText(); - - var messageType = MessageType.fromTypeNr(messageTypeNr); - return switch (messageType) { - case CALL_RESULT -> handleResult(messageId, parser); - case CALL_ERROR -> handleError(messageId, parser); - case CALL -> handleCall(messageId, parser); - }; - } - } - - private OcppJsonResponse handleResult(String messageId, JsonParser parser) throws Exception { - parser.nextToken(); - var responsePayload = parser.readValueAsTree(); - var clazz = responseContextMap.get(messageId).responseClass; - var res = ocppMapper.treeToValue(responsePayload, clazz); - - var result = new OcppJsonResult(); - result.setMessageId(messageId); - result.setPayload(res); - return result; - } - - private OcppJsonResponse handleError(String messageId, JsonParser parser) throws Exception { - parser.nextToken(); - var code = ErrorCode.fromValue(parser.getText()); - - parser.nextToken(); - var desc = parser.getText(); - if ("".equals(desc)) { - desc = null; - } - - String details = null; - parser.nextToken(); - var detailsNode = parser.readValueAsTree(); - if (detailsNode != null && detailsNode.size() != 0) { - details = ocppMapper.writeValueAsString(detailsNode); - } - - var error = new OcppJsonError(); - error.setMessageId(messageId); - error.setErrorCode(code); - error.setErrorDescription(desc); - error.setErrorDetails(details); - return error; - } - - private OcppJsonCall handleCall(String messageId, JsonParser parser) { - // parse action - String action; - try { - parser.nextToken(); - action = parser.getText(); - } catch (IOException e) { - throw new RuntimeException(); - } - - // parse request payload - String req; - try { - parser.nextToken(); - JsonNode requestPayload = parser.readValueAsTree(); - - // https://github.com/steve-community/steve/issues/1109 - if (requestPayload instanceof NullNode) { - requestPayload = new ObjectNode(JsonNodeFactory.instance); - } - - req = requestPayload.toString(); - } catch (IOException e) { - log.error("Exception occurred", e); - throw new RuntimeException(); - } - - var request = requestContextMap.get(action); - if (request == null) { - testerThreadInterruptReason = new RuntimeException("Unexpected message arrived: " + req); - testerThread.interrupt(); - } else if (Objects.equals(request.toString(), req)) { // FIXME - requestContextMap.remove(action); - } - - var call = new OcppJsonCall(); - call.setAction(action); - call.setMessageId(messageId); - call.setPayload(request); - return call; - } - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/utils/StressTester.java b/steve/src/test/java/de/rwth/idsg/steve/utils/StressTester.java deleted file mode 100644 index f6e7c9f21..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/utils/StressTester.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * @author Sevket Goekay - * @since 18.04.2018 - */ -public class StressTester { - - private final int threadCount; - private final int perThreadRepeatCount; - private final ExecutorService executorService; - - public StressTester(int threadCount, int perThreadRepeatCount) { - this.threadCount = threadCount; - this.perThreadRepeatCount = perThreadRepeatCount; - this.executorService = Executors.newCachedThreadPool(); - } - - public void test(StressTester.Runnable runnable) throws InterruptedException { - final CountDownLatch doneSignal = new CountDownLatch(threadCount); - - for (int i = 0; i < threadCount; i++) { - executorService.execute(() -> { - try { - runnable.beforeRepeat(); - for (int j = 0; j < perThreadRepeatCount; j++) { - runnable.toRepeat(); - } - runnable.afterRepeat(); - } finally { - doneSignal.countDown(); - } - }); - } - - doneSignal.await(); - } - - public void shutDown() { - executorService.shutdown(); - } - - public interface Runnable { - void beforeRepeat(); - - void toRepeat(); - - void afterRepeat(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/utils/StringUtilsTest.java b/steve/src/test/java/de/rwth/idsg/steve/utils/StringUtilsTest.java deleted file mode 100644 index 6d0847b39..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/utils/StringUtilsTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import de.rwth.idsg.steve.ocpp.task.CancelReservationTask; -import de.rwth.idsg.steve.ocpp.task.ClearCacheTask; -import de.rwth.idsg.steve.ocpp.task.GetCompositeScheduleTask; -import de.rwth.idsg.steve.web.dto.ocpp.CancelReservationParams; -import de.rwth.idsg.steve.web.dto.ocpp.GetCompositeScheduleParams; -import de.rwth.idsg.steve.web.dto.ocpp.MultipleChargePointSelect; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 05.10.2021 - */ -public class StringUtilsTest { - - @Test - public void testOperationName_ocpp12andMultiple() { - var operationName = StringUtils.getOperationName(new ClearCacheTask(new MultipleChargePointSelect())); - assertThat(operationName).isEqualTo("Clear Cache"); - } - - @Test - public void testOperationName_ocpp15andSingle() { - var operationName = - StringUtils.getOperationName(new CancelReservationTask(new CancelReservationParams(), null)); - assertThat(operationName).isEqualTo("Cancel Reservation"); - } - - @Test - public void testOperationName_ocpp16() { - var operationName = - StringUtils.getOperationName(new GetCompositeScheduleTask(new GetCompositeScheduleParams())); - assertThat(operationName).isEqualTo("Get Composite Schedule"); - } - - @Test - public void testJoinByComma_inputNull() { - var val = StringUtils.joinByComma(null); - assertThat(val).isNull(); - } - - @Test - public void testJoinByComma_inputEmpty() { - var val = StringUtils.joinByComma(List.of()); - assertThat(val).isNull(); - } - - @Test - public void testJoinByComma_inputOneElement() { - var val = StringUtils.joinByComma(List.of("hey")); - assertThat(val).isEqualTo("hey"); - } - - @Test - public void testJoinByComma_inputTwoElements() { - var val = StringUtils.joinByComma(List.of("hey", "ho")); - assertThat(val).isEqualTo("hey,ho"); - } - - @Test - public void testJoinByComma_inputDuplicateElements() { - var val = StringUtils.joinByComma(List.of("hey", "ho", "hey")); - assertThat(val).isEqualTo("hey,ho"); - } - - @Test - public void testSplitByComma_inputNull() { - var val = StringUtils.splitByComma(null); - assertThat(val).isNotNull().isEmpty(); - } - - @Test - public void testSplitByComma_inputEmpty() { - var val = StringUtils.splitByComma(""); - assertThat(val).isNotNull().isEmpty(); - } - - @Test - public void testSplitByComma_inputOneElement() { - var val = StringUtils.splitByComma("1one"); - assertThat(val).hasSize(1); - assertThat(val.get(0)).isEqualTo("1one"); - } - - @Test - public void testSplitByComma_inputTwoElements() { - var val = StringUtils.splitByComma("1one,2two"); - assertThat(val).hasSize(2); - - var sortedVal = val.stream().sorted().toList(); - assertThat(sortedVal).containsExactly("1one", "2two"); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/utils/TransactionStopServiceHelperTest.java b/steve/src/test/java/de/rwth/idsg/steve/utils/TransactionStopServiceHelperTest.java deleted file mode 100644 index 64664d99a..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/utils/TransactionStopServiceHelperTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import de.rwth.idsg.steve.repository.dto.TransactionDetails; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TransactionStopServiceHelperTest { - - @Test - public void testFloatingStringToIntString() { - var actual = TransactionStopServiceHelper.floatingStringToIntString("11.01"); - - assertThat(actual).isEqualTo("12"); - } - - @Test - public void testFloatingStringToIntString2() { - var actual = TransactionStopServiceHelper.floatingStringToIntString("234.678"); - - assertThat(actual).isEqualTo("235"); - } - - @Test - public void testKWhStringToWhString() { - var actual = TransactionStopServiceHelper.kWhStringToWhString("12"); - - assertThat(actual).isEqualTo("12000.0"); - } - - @Test - public void testIsEnergy_empty() { - var value = TransactionDetails.MeterValues.builder().build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isFalse(); - } - - @Test - public void testIsEnergy_onlyValue() { - var value = TransactionDetails.MeterValues.builder().value("22").build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isTrue(); - } - - @Test - public void testIsEnergy_onlyValueDecimal() { - var value = TransactionDetails.MeterValues.builder().value("22.5").build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isTrue(); - } - - @Test - public void testIsEnergy_signedData() { - var value = TransactionDetails.MeterValues.builder() - .value("some gibberish that is not an energy value") - .format("SignedData") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isFalse(); - } - - @Test - public void testIsEnergy_notEnergyUnit() { - var value = TransactionDetails.MeterValues.builder() - .value("22") - .format("Raw") - .unit("Celsius") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isFalse(); - } - - @Test - public void testIsEnergy_notActiveImportMeasurand() { - var value = TransactionDetails.MeterValues.builder() - .value("22") - .format("Raw") - .unit("Wh") - .measurand("Current.Export") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isFalse(); - } - - @Test - public void testIsEnergy_nullFormat() { - var value = TransactionDetails.MeterValues.builder() - .value("22") - .format(null) - .unit("Wh") - .measurand("Energy.Active.Import.Register") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isTrue(); - } - - @Test - public void testIsEnergy_rawFormat() { - var value = TransactionDetails.MeterValues.builder() - .value("22") - .format("Raw") - .unit("Wh") - .measurand("Energy.Active.Import.Register") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isTrue(); - } - - @Test - public void testIsEnergy_kWhUnit() { - var value = TransactionDetails.MeterValues.builder() - .value("22") - .format("Raw") - .unit("kWh") - .measurand("Energy.Active.Import.Register") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isTrue(); - } - - @Test - public void testIsEnergy_nonNumericValue() { - var value = TransactionDetails.MeterValues.builder() - .value("22a819()b") - .format("Raw") - .unit("Wh") - .measurand("Energy.Active.Import.Register") - .build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isFalse(); - } - - @Test - public void testIsEnergy_valueAndUnit() { - var value = - TransactionDetails.MeterValues.builder().value("22").unit("Wh").build(); - - assertThat(TransactionStopServiceHelper.isEnergyValue(value)).isTrue(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java b/steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java deleted file mode 100644 index b282766de..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.utils; - -import com.google.common.collect.Sets; -import de.rwth.idsg.steve.SteveConfiguration; -import de.rwth.idsg.steve.jooq.config.JooqConfiguration; -import de.rwth.idsg.steve.repository.dto.ChargePoint; -import de.rwth.idsg.steve.repository.dto.ConnectorStatus; -import de.rwth.idsg.steve.repository.dto.InsertReservationParams; -import de.rwth.idsg.steve.repository.dto.Reservation; -import de.rwth.idsg.steve.repository.dto.Transaction; -import de.rwth.idsg.steve.repository.dto.TransactionDetails; -import de.rwth.idsg.steve.repository.impl.AddressRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.ChargePointRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.OcppTagRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.ReservationRepositoryImpl; -import de.rwth.idsg.steve.repository.impl.TransactionRepositoryImpl; -import de.rwth.idsg.steve.web.dto.ReservationQueryForm; -import de.rwth.idsg.steve.web.dto.TransactionQueryForm; -import jooq.steve.db.DefaultCatalog; -import jooq.steve.db.tables.OcppTagActivity; -import jooq.steve.db.tables.SchemaVersion; -import jooq.steve.db.tables.Settings; -import jooq.steve.db.tables.records.OcppTagActivityRecord; -import jooq.steve.db.tables.records.TransactionRecord; -import org.jooq.DSLContext; -import org.jooq.impl.DSL; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -import static de.rwth.idsg.steve.utils.DateTimeUtils.toLocalDateTime; -import static jooq.steve.db.tables.ChargeBox.CHARGE_BOX; -import static jooq.steve.db.tables.OcppTag.OCPP_TAG; -import static jooq.steve.db.tables.Transaction.TRANSACTION; - -/** - * This is a dangerous class. It performs database operations no class should do, like truncating all tables and - * inserting data while bypassing normal mechanisms of SteVe. However, for integration testing with reproducible - * results we need a clean and isolated database. - * - * @author Sevket Goekay - * @since 21.03.2018 - */ -public class __DatabasePreparer__ { - - private static final String SCHEMA_TO_TRUNCATE = "stevedb_test_2aa6a783d47d"; - private static final String REGISTERED_CHARGE_BOX_ID = "charge_box_2aa6a783d47d"; - private static final String REGISTERED_CHARGE_BOX_ID_2 = "charge_box_2aa6a783d47d_2"; - private static final String REGISTERED_OCPP_TAG = "id_tag_2aa6a783d47d"; - - private static final JooqConfiguration jooqConfiguration = new JooqConfiguration(); - - private static DSLContext dslContext; - - public static void prepare(SteveConfiguration config) { - dslContext = jooqConfiguration.dslContext( - JooqConfiguration.dataSource( - config.getDb().getJdbcUrl(), - config.getDb().getUserName(), - config.getDb().getPassword(), - config.getTimeZoneId()), - config); - runOperation(ctx -> { - truncateTables(ctx); - insertChargeBox(ctx); - insertOcppIdTag(ctx); - }); - } - - public static int makeReservation(int connectorId) { - var r = new ReservationRepositoryImpl(dslContext); - var params = InsertReservationParams.builder() - .chargeBoxId(REGISTERED_CHARGE_BOX_ID) - .idTag(REGISTERED_OCPP_TAG) - .connectorId(connectorId) - .expiryTimestamp(Instant.now().plus(1, ChronoUnit.HOURS)) - .build(); - var reservationId = r.insert(params); - r.accepted(reservationId); - return reservationId; - } - - public static void cleanUp() { - runOperation(__DatabasePreparer__::truncateTables); - } - - public static String getRegisteredChargeBoxId() { - return REGISTERED_CHARGE_BOX_ID; - } - - public static String getRegisteredChargeBoxId2() { - return REGISTERED_CHARGE_BOX_ID_2; - } - - public static String getRegisteredOcppTag() { - return REGISTERED_OCPP_TAG; - } - - public static List getTransactions() { - var impl = new TransactionRepositoryImpl(dslContext); - return impl.getTransactions(new TransactionQueryForm()); - } - - public static List getTransactionRecords() { - return dslContext.selectFrom(TRANSACTION).fetch(); - } - - public static List getReservations() { - var impl = new ReservationRepositoryImpl(dslContext); - return impl.getReservations(new ReservationQueryForm()); - } - - public static List getChargePointConnectorStatus() { - var impl = new ChargePointRepositoryImpl(dslContext, new AddressRepositoryImpl(dslContext)); - return impl.getChargePointConnectorStatus(); - } - - public static TransactionDetails getDetails(int transactionPk) { - var impl = new TransactionRepositoryImpl(dslContext); - return impl.getDetails(transactionPk).orElseThrow(); - } - - public static 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; - } - - public static 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(); - } - - private static void runOperation(Consumer consumer) { - consumer.accept(dslContext); - } - - private static void truncateTables(DSLContext ctx) { - var skipList = Sets.newHashSet( - SchemaVersion.SCHEMA_VERSION, - Settings.SETTINGS, - OcppTagActivity.OCPP_TAG_ACTIVITY, // only a view - TRANSACTION // only a view - ); - - ctx.transaction(configuration -> { - var schema = DefaultCatalog.DEFAULT_CATALOG.getSchemas().stream() - .filter(s -> SCHEMA_TO_TRUNCATE.equals(s.getName())) - .findFirst() - .orElseThrow(() -> new RuntimeException("Could not find schema")); - - var tables = schema.getTables().stream() - .filter(t -> !skipList.contains(t)) - .toList(); - - if (tables.isEmpty()) { - throw new RuntimeException("Could not find tables to truncate"); - } - - 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"); - }); - } - - private static void insertChargeBox(DSLContext ctx) { - ctx.insertInto(CHARGE_BOX) - .set(CHARGE_BOX.CHARGE_BOX_ID, getRegisteredChargeBoxId()) - .execute(); - - ctx.insertInto(CHARGE_BOX) - .set(CHARGE_BOX.CHARGE_BOX_ID, getRegisteredChargeBoxId2()) - .execute(); - } - - private static void insertOcppIdTag(DSLContext ctx) { - ctx.insertInto(OCPP_TAG).set(OCPP_TAG.ID_TAG, getRegisteredOcppTag()).execute(); - } -} diff --git a/steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java b/steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java deleted file mode 100644 index 3bce941d4..000000000 --- a/steve/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2025 SteVe Community Team - * All Rights Reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package de.rwth.idsg.steve.web.validation; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 01.08.2024 - */ -public class ChargeBoxIdValidatorTest { - - private final ChargeBoxIdValidator validator = new ChargeBoxIdValidator(); - - @Test - public void testNull() { - assertThat(validator.isValid(null)).isFalse(); - } - - @Test - public void testEmpty() { - assertThat(validator.isValid("")).isFalse(); - } - - @Test - public void testSpace() { - assertThat(validator.isValid(" ")).isFalse(); - } - - @Test - public void testAllLowercaseLetters() { - assertThat(validator.isValid("test")).isTrue(); - } - - @Test - public void testAllUppercaseLetters() { - assertThat(validator.isValid("TEST")).isTrue(); - } - - @Test - public void testMixedCaseLetters() { - assertThat(validator.isValid("TesT")).isTrue(); - assertThat(validator.isValid("tEst")).isTrue(); - } - - @Test - public void testLettersAndNumbers() { - assertThat(validator.isValid("test12")).isTrue(); - assertThat(validator.isValid("89test")).isTrue(); - assertThat(validator.isValid("te9s0t")).isTrue(); - } - - @Test - public void testDot() { - assertThat(validator.isValid(".test")).isTrue(); - assertThat(validator.isValid("test.")).isTrue(); - assertThat(validator.isValid("te..st")).isTrue(); - } - - @Test - public void testDash() { - assertThat(validator.isValid("-test")).isTrue(); - assertThat(validator.isValid("test-")).isTrue(); - assertThat(validator.isValid("te--st")).isTrue(); - } - - @Test - public void testUnderscore() { - assertThat(validator.isValid("_test")).isTrue(); - assertThat(validator.isValid("test_")).isTrue(); - assertThat(validator.isValid("te__st")).isTrue(); - } - - @Test - public void testColon() { - assertThat(validator.isValid(":test")).isTrue(); - assertThat(validator.isValid("test:")).isTrue(); - assertThat(validator.isValid("te::st")).isTrue(); - assertThat(validator.isValid("VID:00XXXXXXXXXX")).isTrue(); - } - - @Test - public void testPoundSign() { - assertThat(validator.isValid("#test")).isTrue(); - assertThat(validator.isValid("test#")).isTrue(); - assertThat(validator.isValid("te##st")).isTrue(); - assertThat(validator.isValid("#FreeCharging")).isTrue(); - } - - @Test - public void testCombined() { - assertThat(validator.isValid("1t.E-S_:t20#")).isTrue(); - } - - @Test - public void testSpaceAtBeginning() { - assertThat(validator.isValid(" test")).isFalse(); - } - - @Test - public void testSpaceAtEnd() { - assertThat(validator.isValid("test ")).isFalse(); - } - - @Test - public void testSpaceInMiddle() { - assertThat(validator.isValid("test1 test2")).isTrue(); - } - - @Test - public void testOpeningParenthesis() { - assertThat(validator.isValid("te(st")).isFalse(); - } - - @Test - public void testClosingParenthesis() { - assertThat(validator.isValid("te)st")).isFalse(); - } - - @Test - public void testBiggerSymbol() { - assertThat(validator.isValid("te>st")).isFalse(); - } - - @Test - public void testSmallerSymbol() { - assertThat(validator.isValid("te. - */ -package de.rwth.idsg.steve.web.validation; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sevket Goekay - * @since 22.03.2021 - */ -public class IdTagValidatorTest { - - private final IdTagValidator validator = new IdTagValidator(); - - @Test - public void testNull() { - assertThat(validator.isValid(null, null)).isTrue(); - } - - @Test - public void testAllLowercaseLetters() { - assertThat(validator.isValid("test", null)).isTrue(); - } - - @Test - public void testAllUppercaseLetters() { - assertThat(validator.isValid("TEST", null)).isTrue(); - } - - @Test - public void testMixedCaseLetters() { - assertThat(validator.isValid("TesT", null)).isTrue(); - assertThat(validator.isValid("tEst", null)).isTrue(); - } - - @Test - public void testLettersAndNumbers() { - assertThat(validator.isValid("test12", null)).isTrue(); - assertThat(validator.isValid("89test", null)).isTrue(); - assertThat(validator.isValid("te9s0t", null)).isTrue(); - } - - @Test - public void testDot() { - assertThat(validator.isValid(".test", null)).isTrue(); - assertThat(validator.isValid("test.", null)).isTrue(); - assertThat(validator.isValid("te..st", null)).isTrue(); - } - - @Test - public void testDash() { - assertThat(validator.isValid("-test", null)).isTrue(); - assertThat(validator.isValid("test-", null)).isTrue(); - assertThat(validator.isValid("te--st", null)).isTrue(); - } - - @Test - public void testUnderscore() { - assertThat(validator.isValid("_test", null)).isTrue(); - assertThat(validator.isValid("test_", null)).isTrue(); - assertThat(validator.isValid("te__st", null)).isTrue(); - } - - /** - * https://github.com/steve-community/steve/issues/475 - */ - @Test - public void testColon() { - assertThat(validator.isValid(":test", null)).isTrue(); - assertThat(validator.isValid("test:", null)).isTrue(); - assertThat(validator.isValid("te::st", null)).isTrue(); - - assertThat(validator.isValid("VID:00XXXXXXXXXX", null)).isTrue(); - } - - @Test - public void testPoundSign() { - assertThat(validator.isValid("#test", null)).isTrue(); - assertThat(validator.isValid("test#", null)).isTrue(); - assertThat(validator.isValid("te##st", null)).isTrue(); - - // Tag provided by Webasto charge points - // https://github.com/steve-community/steve/pull/1322 - assertThat(validator.isValid("#FreeCharging", null)).isTrue(); - } - - @Test - public void testCombined() { - assertThat(validator.isValid("1t.E-S_:t20#", null)).isTrue(); - } -}