diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f04fa4c55..8c0e1c0f1 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -42,7 +42,7 @@ jobs:
- name: Start the app and visit signin web page
run: |
sudo apt-get install -y elinks
- java -Djava.net.preferIPv4Stack=true -jar target/steve.jar &
+ java -Djava.net.preferIPv4Stack=true -jar target/steve.war &
sleep 30
elinks -dump -no-references http://localhost:8080/steve/manager/signin
killall java
diff --git a/Dockerfile b/Dockerfile
index eac7b53a5..001d53af1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -22,5 +22,5 @@ COPY . /code
# Build and run steve, requires a db to be available on port 3306
CMD dockerize -wait tcp://mariadb:3306 -timeout 60s && \
./mvnw clean package -Pdocker -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2" && \
- java -XX:MaxRAMPercentage=85 -jar target/steve.jar
+ java -XX:MaxRAMPercentage=85 -jar target/steve.war
diff --git a/README.md b/README.md
index d21dab9b6..37652c204 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ SteVe is designed to run standalone, a java servlet container / web server (e.g.
4. Build SteVe:
- To compile SteVe simply use Maven. A runnable `jar` file containing the application and configuration will be created in the subdirectory `steve/target`.
+ To compile SteVe simply use Maven. A runnable `war` file containing the application and configuration will be created in the subdirectory `steve/target`.
```
# ./mvnw package
@@ -93,7 +93,7 @@ SteVe is designed to run standalone, a java servlet container / web server (e.g.
To start the application run (please do not run SteVe as root):
```
- # java -jar target/steve.jar
+ # java -jar target/steve.war
```
# Docker
diff --git a/k8s/docker/Dockerfile b/k8s/docker/Dockerfile
index 05f20eeb3..f5def7630 100644
--- a/k8s/docker/Dockerfile
+++ b/k8s/docker/Dockerfile
@@ -42,11 +42,11 @@ RUN ./mvnw clean package -Pkubernetes -Djdk.tls.client.protocols="TLSv1,TLSv1.1,
FROM base AS release
# Copy relevant files from the build stage
-COPY --from=build /code/target/steve.jar /app/steve.jar
+COPY --from=build /code/target/steve.jar /app/steve.war
COPY --from=build /code/target/libs /app/libs
# Expose any necessary ports (example: 8080)
EXPOSE 8080
# Define the entrypoint for the release stage
-CMD ["java", "-jar", "/app/steve.jar"]
+CMD ["java", "-jar", "/app/steve.war"]
diff --git a/pom.xml b/pom.xml
index 403d05c4d..f076b1719 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,14 @@
de.rwth.idsg
steve
3.9.0-SNAPSHOT
- jar
+ war
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.5
+
+
SteVe
SteckdosenVerwaltung
@@ -35,16 +42,7 @@
21
UTF-8
- 3.19.18
- 11.12.0
4.1.3
- 6.2.0
- 6.5.3
- 9.4.0
- 12.1.1
- 1.18.40
- 2.19.2
- 2.0.17
5.0.0
@@ -99,6 +97,7 @@
src/main/resources
true
+ application.yml
application**.properties
logback-spring**.xml
@@ -157,52 +156,6 @@
-
- org.apache.maven.plugins
- maven-enforcer-plugin
- 3.6.1
-
-
- enforce-java
-
- enforce
-
-
-
-
- [21,)
-
-
- [3.3.9,)
-
-
-
-
-
-
-
- maven-compiler-plugin
- 3.14.0
-
- ${java.version}
- ${java.version}
-
-
- org.projectlombok
- lombok
- ${lombok.version}
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 3.5.4
-
-
org.apache.maven.plugins
@@ -239,24 +192,6 @@
-
- io.github.git-commit-id
- git-commit-id-maven-plugin
- 9.0.2
-
-
- initialize
-
- revision
-
-
-
-
- false
- false
-
-
-
org.codehaus.mojo
@@ -270,7 +205,6 @@
- src/main/resources/application.properties
src/main/resources/application-${envName}.properties
@@ -280,60 +214,32 @@
org.apache.maven.plugins
- maven-dependency-plugin
- 3.8.1
-
-
- copy-dependencies
- prepare-package
-
- copy-dependencies
-
-
- ${project.build.directory}/libs
- runtime
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 3.4.2
+ maven-compiler-plugin
-
-
- true
- libs/
- de.rwth.idsg.steve.Application
-
-
+
+
+ org.projectlombok
+ lombok
+
+
-
-
- org.eclipse.jetty.ee10
- jetty-ee10-jspc-maven-plugin
- ${jetty.version}
-
-
- compile
- jspc
-
- jspc
-
-
- ${basedir}/target/classes/webapp/WEB-INF/web.xml-frag
-
-
-
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
org.flywaydb
flyway-maven-plugin
- ${flyway.version}
@@ -386,7 +292,6 @@
org.jooq
jooq-codegen-maven
- ${jooq.version}
@@ -413,7 +318,7 @@
com.mysql
mysql-connector-j
- ${mysql.jdbc.version}
+ ${mysql.version}
@@ -492,37 +397,43 @@
-
-
-
- org.springframework
- spring-framework-bom
- ${spring.version}
- import
- pom
-
-
- org.junit
- junit-bom
- 5.13.4
- pom
- import
-
-
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- 2.8.13
+ org.springframework.boot
+ spring-boot-starter-web
- tomcat-embed-el
- org.apache.tomcat.embed
+ spring-boot-starter-tomcat
+ org.springframework.boot
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.8.13
+
com.github.goekay
@@ -533,21 +444,6 @@
com.github.steve-community
ocpp-jaxb
0.0.9
-
-
- jakarta.activation
- com.sun.activation
-
-
- jakarta.activation-api
- jakarta.activation
-
-
-
-
- jakarta.mail
- jakarta.mail-api
- 2.1.3
org.jetbrains
@@ -559,7 +455,6 @@
org.projectlombok
lombok
- ${lombok.version}
provided
@@ -567,130 +462,33 @@
joda-time
2.14.0
-
- org.hibernate.validator
- hibernate-validator
- 8.0.2.Final
-
-
- com.google.guava
- guava
- 33.4.0-jre
-
+
com.fasterxml.jackson.core
jackson-databind
- ${jackson.version}
com.fasterxml.jackson.module
jackson-module-jakarta-xmlbind-annotations
- ${jackson.version}
com.fasterxml.jackson.core
jackson-annotations
- ${jackson.version}
com.fasterxml.jackson.datatype
jackson-datatype-joda
- ${jackson.version}
org.apache.httpcomponents.client5
httpclient5
- 5.5
-
-
- jakarta.websocket
- jakarta.websocket-api
- 2.2.0
-
-
- jakarta.websocket
- jakarta.websocket-client-api
- 2.2.0
-
-
- jakarta.servlet
- jakarta.servlet-api
- 6.1.0
-
-
- jakarta.servlet.jsp.jstl
- jakarta.servlet.jsp.jstl-api
- 3.0.2
-
-
- org.glassfish.web
- jakarta.servlet.jsp.jstl
- 3.0.1
-
-
-
-
- org.springframework
- spring-webmvc
-
-
-
- commons-logging
- commons-logging
-
-
- org.springframework
- spring-jcl
-
-
-
-
- org.springframework
- spring-websocket
-
-
- org.springframework.security
- spring-security-web
- ${spring.security.version}
-
-
- org.springframework.security
- spring-security-config
- ${spring.security.version}
-
-
-
-
- ch.qos.logback
- logback-classic
- 1.5.18
-
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
-
-
- org.slf4j
- jcl-over-slf4j
- ${slf4j.version}
-
+
org.apache.cxf
- cxf-rt-frontend-jaxws
- ${cxf.version}
-
-
- org.apache.cxf
- cxf-rt-transports-http
- ${cxf.version}
-
-
- org.apache.cxf
- cxf-rt-transports-http-hc
+ cxf-spring-boot-starter-jaxws
${cxf.version}
@@ -699,104 +497,50 @@
${cxf.version}
-
-
- org.eclipse.jetty
- jetty-server
- ${jetty.version}
-
-
- org.eclipse.jetty.ee10
- jetty-ee10-webapp
- ${jetty.version}
-
-
- org.eclipse.jetty.ee10
- jetty-ee10-annotations
- ${jetty.version}
-
+
org.eclipse.jetty.ee10
jetty-ee10-apache-jsp
- ${jetty.version}
-
-
-
-
-
- org.eclipse.jetty
- jetty-rewrite
- ${jetty.version}
+ jakarta.servlet.jsp.jstl
+ jakarta.servlet.jsp.jstl-api
- org.eclipse.jetty.ee10.websocket
- jetty-ee10-websocket-jetty-server
- ${jetty.version}
+ org.glassfish.web
+ jakarta.servlet.jsp.jstl
com.mysql
mysql-connector-j
- ${mysql.jdbc.version}
-
-
- com.zaxxer
- HikariCP
- 7.0.2
org.jooq
jooq
- ${jooq.version}
- org.springframework
- spring-test
- test
-
-
- org.eclipse.jetty.websocket
- jetty-websocket-jetty-client
- ${jetty.version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.junit.jupiter
- junit-jupiter-params
- test
-
-
- org.mockito
- mockito-junit-jupiter
- 5.19.0
+ org.springframework.boot
+ spring-boot-starter-test
test
- net.bytebuddy
- byte-buddy
- 1.17.7
+ org.springframework.security
+ spring-security-test
test
- org.hamcrest
- hamcrest
- 3.0
+ org.eclipse.jetty.websocket
+ jetty-websocket-jetty-client
test
- com.jayway.jsonpath
- json-path
- 2.9.0
+ org.apache.cxf
+ cxf-rt-transports-http-hc
+ ${cxf.version}
test
@@ -815,6 +559,11 @@
1.29
+
+ com.google.guava
+ guava
+ 33.4.0-jre
+
org.owasp.encoder
encoder-jakarta-jsp
diff --git a/src/main/java/de/rwth/idsg/steve/Application.java b/src/main/java/de/rwth/idsg/steve/Application.java
deleted file mode 100644
index 55c141183..000000000
--- a/src/main/java/de/rwth/idsg/steve/Application.java
+++ /dev/null
@@ -1,84 +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 lombok.extern.slf4j.Slf4j;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.TimeZone;
-
-/**
- * @author Sevket Goekay
- * @since 14.01.2015
- */
-@Slf4j
-public class Application {
-
- private final JettyServer server = new JettyServer();
-
- public static void main(String[] args) throws Exception {
- // For Hibernate validator
- System.setProperty("org.jboss.logging.provider", "slf4j");
-
- SteveConfiguration sc = SteveConfiguration.CONFIG;
- log.info("Loaded the properties. Starting with the '{}' profile", sc.getProfile());
-
- TimeZone.setDefault(TimeZone.getTimeZone(sc.getTimeZoneId()));
- DateTimeZone.setDefault(DateTimeZone.forID(sc.getTimeZoneId()));
- log.info("Date/time zone of the application is set to {}. Current date/time: {}", sc.getTimeZoneId(), DateTime.now());
-
- Optional path = LogFileRetriever.INSTANCE.getPath();
- boolean loggingToFile = path.isPresent();
- if (loggingToFile) {
- System.out.println("Log file: " + path.get().toAbsolutePath());
- }
-
- Application app = new Application();
-
- 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/src/main/java/de/rwth/idsg/steve/JettyServer.java b/src/main/java/de/rwth/idsg/steve/JettyServer.java
deleted file mode 100644
index cdb046aee..000000000
--- a/src/main/java/de/rwth/idsg/steve/JettyServer.java
+++ /dev/null
@@ -1,197 +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.extern.slf4j.Slf4j;
-import org.eclipse.jetty.ee10.webapp.WebAppContext;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.server.ForwardedRequestCustomizer;
-import org.eclipse.jetty.server.Handler;
-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.server.handler.gzip.GzipHandler;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.springframework.core.io.ClassPathResource;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
-/**
- * @author Sevket Goekay
- * @since 12.12.2014
- */
-@Slf4j
-public class JettyServer {
-
- private Server server;
-
- 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);
-
- // scan all jars in the classpath for ServletContainerInitializers
- // (e.g. Spring's WebApplicationInitializer)
- private static final String SCAN_PATTERN = ".*\\.jar$|.*/classes/.*";
-
- /**
- * A fully configured Jetty Server instance
- */
- private void prepare() throws IOException {
-
- // === 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));
- }
-
- server.setHandler(getWebApp());
- }
-
- 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;
- }
-
- private Handler getWebApp() throws IOException {
- var webAppContext = new WebAppContext();
- webAppContext.setContextPath(CONFIG.getContextPath());
- webAppContext.setBaseResourceAsString(new ClassPathResource("webapp").getURI().toString());
-
- // if during startup an exception happens, do not swallow it, throw it
- webAppContext.setThrowUnavailableOnStartupException(true);
-
- // Disable directory listings if no index.html is found.
- webAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
-
- // Crucial for Spring's WebApplicationInitializer to be discovered
- // and for the DispatcherServlet to be initialized.
- // It tells Jetty to scan for classes implementing WebApplicationInitializer.
- // The pattern ensures that Jetty finds the Spring classes in the classpath.
- //
- // https://jetty.org/docs/jetty/12.1/programming-guide/maven-jetty/jetty-maven-plugin.html
- // https://jetty.org/docs/jetty/12.1/operations-guide/annotations/index.html#og-container-include-jar-pattern
- webAppContext.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", SCAN_PATTERN);
-
- if (CONFIG.getJetty().isGzipEnabled()) {
- // Wraps the whole web app in a gzip handler to make Jetty return compressed content
- // http://www.eclipse.org/jetty/documentation/current/gzip-filter.html
- return new GzipHandler(webAppContext);
- } else {
- return webAppContext;
- }
- }
-
- /**
- * Starts the Jetty Server instance
- */
- public void start() throws Exception {
- prepare();
-
- if (server != null) {
- server.start();
- }
- }
-
- /**
- * 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();
- }
- }
-}
diff --git a/src/main/java/de/rwth/idsg/steve/SteveApplication.java b/src/main/java/de/rwth/idsg/steve/SteveApplication.java
new file mode 100644
index 000000000..7ea0151e6
--- /dev/null
+++ b/src/main/java/de/rwth/idsg/steve/SteveApplication.java
@@ -0,0 +1,68 @@
+/*
+ * 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.config.SteveProperties;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.logging.Slf4jLogger;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.event.EventListener;
+
+import java.util.TimeZone;
+
+/**
+ * @author Sevket Goekay
+ * @since 19.09.2025
+ */
+@Slf4j
+@SpringBootApplication(exclude = {JooqAutoConfiguration.class})
+public class SteveApplication {
+
+ static {
+ // Apache CXF
+ LogUtils.setLoggerClass(Slf4jLogger.class);
+
+ // For Hibernate validator
+ System.setProperty("org.jboss.logging.provider", "slf4j");
+
+ TimeZone.setDefault(TimeZone.getTimeZone(SteveProperties.TIME_ZONE_ID));
+ DateTimeZone.setDefault(DateTimeZone.forID(SteveProperties.TIME_ZONE_ID));
+ }
+
+ @EventListener(ApplicationStartedEvent.class)
+ public void logStartup() {
+ log.info("Date/time zone of the application is set to {}. Current date/time: {}", SteveProperties.TIME_ZONE_ID, DateTime.now());
+ }
+
+ public static void main(String[] args) throws Exception {
+ start(args);
+ }
+
+ public static ConfigurableApplicationContext start(String... args) throws Exception {
+ return SpringApplication.run(SteveApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java b/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java
deleted file mode 100644
index 4a8d52e7d..000000000
--- a/src/main/java/de/rwth/idsg/steve/SteveConfiguration.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;
-
-import de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategy;
-import de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategyEnum;
-import de.rwth.idsg.steve.utils.PropertiesFileLoader;
-import lombok.Builder;
-import lombok.Getter;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
-
-/**
- * @author Sevket Goekay
- * @since 19.08.2014
- */
-@Getter
-public enum SteveConfiguration {
- CONFIG;
-
- // Root mapping for Spring
- private final String springMapping = "/";
- // Web frontend
- private final String springManagerMapping = "/manager";
- // Mapping for CXF SOAP services
- private final String cxfMapping = "/services";
- // Mapping for Web APIs
- private final String apiMapping = "/api";
- // Dummy service path
- private final String routerEndpointPath = "/CentralSystemService";
- // Time zone for the application and database connections
- private final String timeZoneId = "UTC"; // or ZoneId.systemDefault().getId();
-
- // -------------------------------------------------------------------------
- // application.properties
- // -------------------------------------------------------------------------
-
- private final String contextPath;
- private final String steveVersion;
- private final String gitDescribe;
- private final String profile;
- private final Ocpp ocpp;
- private final Auth auth;
- private final WebApi webApi;
- private final DB db;
- private final Jetty jetty;
-
- SteveConfiguration() {
- // Load the profile in order to load the correct application-.properties file
- String profileTemp = new PropertiesFileLoader("application.properties").getString("profile");
-
- PropertiesFileLoader p = new PropertiesFileLoader("application-" + profileTemp.toLowerCase() + ".properties");
-
- contextPath = sanitizeContextPath(p.getOptionalString("context.path"));
- steveVersion = p.getString("steve.version");
- gitDescribe = useFallbackIfNotSet(p.getOptionalString("git.describe"), null);
-
- profile = p.getString("profile");
- System.setProperty("spring.profiles.active", profile);
-
- jetty = 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"))
- .keyStorePassword(p.getOptionalString("keystore.password"))
- .build();
-
- db = DB.builder()
- .ip(p.getString("db.ip"))
- .port(p.getInt("db.port"))
- .schema(p.getString("db.schema"))
- .userName(p.getString("db.user"))
- .password(p.getString("db.password"))
- .sqlLogging(p.getBoolean("db.sql.logging"))
- .build();
-
- PasswordEncoder encoder = new BCryptPasswordEncoder();
-
- auth = Auth.builder()
- .passwordEncoder(encoder)
- .userName(p.getString("auth.user"))
- .encodedPassword(encoder.encode(p.getString("auth.password")))
- .build();
-
- webApi = WebApi.builder()
- .headerKey(p.getOptionalString("webapi.key"))
- .headerValue(p.getOptionalString("webapi.value"))
- .build();
-
- ocpp = Ocpp.builder()
- .autoRegisterUnknownStations(p.getOptionalBoolean("auto.register.unknown.stations"))
- .chargeBoxIdValidationRegex(p.getOptionalString("charge-box-id.validation.regex"))
- .wsSessionSelectStrategy(
- WsSessionSelectStrategyEnum.fromName(p.getString("ws.session.select.strategy")))
- .build();
-
- validate();
- }
-
- public String getSteveCompositeVersion() {
- if (gitDescribe == null) {
- return steveVersion;
- } else {
- return steveVersion + "-g" + gitDescribe;
- }
- }
-
- private static String useFallbackIfNotSet(String value, 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 String sanitizeContextPath(String s) {
- if (s == null || "/".equals(s)) {
- return "";
-
- } else if (s.startsWith("/")) {
- return s;
-
- } else {
- return "/" + s;
- }
- }
-
- private void validate() {
- 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 String keyStorePath;
- private final String keyStorePassword;
- }
-
- // Database configuration
- @Builder @Getter
- public static class DB {
- private final String ip;
- private final int port;
- private final String schema;
- private final String userName;
- private final String password;
- private final boolean sqlLogging;
- }
-
- // 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 String headerKey;
- private final String headerValue;
- }
-
- // OCPP-related configuration
- @Builder @Getter
- public static class Ocpp {
- private final boolean autoRegisterUnknownStations;
- private final String chargeBoxIdValidationRegex;
- private final WsSessionSelectStrategy wsSessionSelectStrategy;
- }
-
-}
diff --git a/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java
index 64eb1b030..0e04f5367 100644
--- a/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java
+++ b/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;
@@ -26,18 +25,8 @@
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
-import org.springdoc.core.configuration.SpringDocConfiguration;
-import org.springdoc.core.properties.SwaggerUiConfigProperties;
-import org.springdoc.core.properties.SwaggerUiOAuthProperties;
-import org.springdoc.webmvc.core.configuration.SpringDocWebMvcConfiguration;
-import org.springdoc.webmvc.ui.SwaggerConfig;
-import org.springdoc.webmvc.ui.SwaggerUiHome;
-import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.FilterType;
-import org.springframework.context.annotation.Import;
import java.util.List;
@@ -48,16 +37,6 @@
* @since 15.09.2022
*/
@Configuration
-@ComponentScan(basePackages = {"org.springdoc"},
- // exclude because SwaggerUiHome's root redirect clashes with our own RootRedirectController
- excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SwaggerUiHome.class)
-)
-@Import({SpringDocConfiguration.class,
- SpringDocWebMvcConfiguration.class,
- SwaggerConfig.class,
- SwaggerUiConfigProperties.class,
- SwaggerUiOAuthProperties.class,
- JacksonAutoConfiguration.class})
public class ApiDocsConfiguration {
static {
@@ -77,7 +56,7 @@ public class ApiDocsConfiguration {
}
@Bean
- public OpenAPI apiDocs() {
+ public OpenAPI apiDocs(SteveProperties steveProperties) {
String title = "SteVe REST API Documentation";
String securityName = "basicAuth";
@@ -95,7 +74,7 @@ public OpenAPI apiDocs() {
.name("GPL-3.0")
.url("https://github.com/steve-community/steve/blob/master/LICENSE.txt")
)
- .version(SteveConfiguration.CONFIG.getSteveVersion())
+ .version(steveProperties.getVersion())
)
// https://stackoverflow.com/a/68185254
.servers(List.of(new Server().url("/").description("Default Server URL")))
diff --git a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java
index 78aecdf4b..e6364bc30 100644
--- a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java
+++ b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java
@@ -24,7 +24,6 @@
import com.mysql.cj.conf.PropertyKey;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
-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;
@@ -36,33 +35,28 @@
import org.jooq.impl.DSL;
import org.jooq.impl.DataSourceConnectionProvider;
import org.jooq.impl.DefaultConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.Ordered;
+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.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
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 org.springframework.web.servlet.view.InternalResourceViewResolver;
-import jakarta.validation.Validator;
-
import javax.sql.DataSource;
import java.util.List;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* Configuration and beans of Spring Framework.
*
@@ -80,15 +74,13 @@ public class BeanConfiguration implements WebMvcConfigurer {
* https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
*/
@Bean
- public DataSource dataSource() {
- SteveConfiguration.DB dbConfig = CONFIG.getDb();
-
+ public HikariDataSource dataSource(DataSourceProperties properties) {
HikariConfig hc = new HikariConfig();
// set standard params
- hc.setJdbcUrl("jdbc:mysql://" + dbConfig.getIp() + ":" + dbConfig.getPort() + "/" + dbConfig.getSchema());
- hc.setUsername(dbConfig.getUserName());
- hc.setPassword(dbConfig.getPassword());
+ hc.setJdbcUrl(properties.getUrl());
+ hc.setUsername(properties.getUsername());
+ hc.setPassword(properties.getPassword());
// set non-standard params
hc.addDataSourceProperty(PropertyKey.cachePrepStmts.getKeyName(), true);
@@ -96,7 +88,7 @@ public DataSource dataSource() {
hc.addDataSourceProperty(PropertyKey.prepStmtCacheSize.getKeyName(), 250);
hc.addDataSourceProperty(PropertyKey.prepStmtCacheSqlLimit.getKeyName(), 2048);
hc.addDataSourceProperty(PropertyKey.characterEncoding.getKeyName(), "utf8");
- hc.addDataSourceProperty(PropertyKey.connectionTimeZone.getKeyName(), CONFIG.getTimeZoneId());
+ hc.addDataSourceProperty(PropertyKey.connectionTimeZone.getKeyName(), SteveProperties.TIME_ZONE_ID);
hc.addDataSourceProperty(PropertyKey.useSSL.getKeyName(), true);
// https://github.com/steve-community/steve/issues/736
@@ -118,7 +110,8 @@ public DataSource dataSource() {
* - http://stackoverflow.com/questions/32848865/jooq-dslcontext-correct-autowiring-with-spring
*/
@Bean
- public DSLContext dslContext(DataSource dataSource) {
+ public DSLContext dslContext(DataSource dataSource,
+ SteveProperties steveProperties) {
Settings settings = new Settings()
// Normally, the records are "attached" to the Configuration that created (i.e. fetch/insert) them.
// This means that they hold an internal reference to the same database connection that was used.
@@ -126,7 +119,7 @@ public DSLContext dslContext(DataSource dataSource) {
// operations. We do not use or need that.
.withAttachRecords(false)
// To log or not to log the sql queries, that is the question
- .withExecuteLogging(CONFIG.getDb().isSqlLogging());
+ .withExecuteLogging(steveProperties.getJooq().isExecutiveLogging());
// Configuration for JOOQ
org.jooq.Configuration conf = new DefaultConfiguration()
@@ -161,11 +154,6 @@ public DelegatingTaskExecutor asyncTaskExecutor() {
return new DelegatingTaskExecutor(executor);
}
- @Bean
- public Validator validator() {
- return new LocalValidatorFactoryBean();
- }
-
/**
* There might be instances deployed in a local/closed network with no internet connection. In such situations,
* it is unnecessary to try to access Github every time, even though the request will time out and result
@@ -173,9 +161,9 @@ public Validator validator() {
* steps and return a "no new version" report immediately.
*/
@Bean
- public ReleaseCheckService releaseCheckService() {
+ public ReleaseCheckService releaseCheckService(SteveProperties steveProperties) {
if (InternetChecker.isInternetAvailable()) {
- return new GithubReleaseCheckService();
+ return new GithubReleaseCheckService(steveProperties);
} else {
return new DummyReleaseCheckService();
}
@@ -232,6 +220,7 @@ public void extendMessageConverters(List> converters) {
* {@link WebMvcConfigurationSupport#requestMappingHandlerAdapter(ContentNegotiationManager, FormattingConversionService, org.springframework.validation.Validator)}.
*/
@Bean
+ @Primary
public ObjectMapper jacksonObjectMapper(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
return requestMappingHandlerAdapter.getMessageConverters().stream()
.filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
diff --git a/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java
index 158f8aa51..a2cb555db 100644
--- a/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java
+++ b/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java
@@ -24,24 +24,16 @@
import de.rwth.idsg.steve.ocpp.soap.MessageIdInterceptor;
import lombok.RequiredArgsConstructor;
import org.apache.cxf.Bus;
-import org.apache.cxf.bus.spring.SpringBus;
-import org.apache.cxf.common.logging.LogUtils;
-import org.apache.cxf.common.logging.Slf4jLogger;
import org.apache.cxf.feature.Feature;
import org.apache.cxf.interceptor.Interceptor;
-import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
+import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-
/**
* Configuration and beans related to OCPP.
*
@@ -52,46 +44,55 @@
@Configuration
public class OcppConfiguration {
- static {
- LogUtils.setLoggerClass(Slf4jLogger.class);
- }
-
+ private final Bus bus;
private final ocpp.cs._2010._08.CentralSystemService ocpp12Server;
private final ocpp.cs._2012._06.CentralSystemService ocpp15Server;
private final ocpp.cs._2015._10.CentralSystemService ocpp16Server;
private final MessageHeaderInterceptor messageHeaderInterceptor;
- @Bean(name = Bus.DEFAULT_BUS_ID, destroyMethod = "shutdown")
- public SpringBus cxf() {
- SpringBus bus = new SpringBus();
- configure(bus);
- return bus;
+ private final MessageIdInterceptor messageIdInterceptor = new MessageIdInterceptor();
+
+ @Bean
+ public EndpointImpl ocpp12Endpoint() {
+ return createDefaultEndpoint(ocpp12Server, "/CentralSystemServiceOCPP12");
}
- private void configure(Bus bus) {
- List> interceptors = asList(new MessageIdInterceptor(), messageHeaderInterceptor);
- List logging = singletonList(LoggingFeatureProxy.INSTANCE.get());
+ @Bean
+ public EndpointImpl ocpp15Endpoint() {
+ return createDefaultEndpoint(ocpp15Server, "/CentralSystemServiceOCPP15");
+ }
+
+ @Bean
+ public EndpointImpl ocpp16Endpoint() {
+ return createDefaultEndpoint(ocpp16Server, "/CentralSystemServiceOCPP16");
+ }
- createOcppService(bus, ocpp12Server, "/CentralSystemServiceOCPP12", interceptors, logging);
- createOcppService(bus, ocpp15Server, "/CentralSystemServiceOCPP15", interceptors, logging);
- createOcppService(bus, ocpp16Server, "/CentralSystemServiceOCPP16", interceptors, logging);
+ /**
+ * Just a dummy service to route incoming messages to the appropriate service version.
+ */
+ @Bean
+ public EndpointImpl routerEndpoint(EndpointImpl ocpp12Endpoint,
+ EndpointImpl ocpp15Endpoint,
+ EndpointImpl ocpp16Endpoint) {
+ var mediator = new MediatorInInterceptor(List.of(ocpp12Endpoint, ocpp15Endpoint, ocpp16Endpoint));
+ return createEndpoint(
+ ocpp12Server, SteveProperties.ROUTER_ENDPOINT_PATH, List.of(mediator), Collections.emptyList()
+ );
+ }
- // 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));
- createOcppService(bus, ocpp12Server, CONFIG.getRouterEndpointPath(), mediator, Collections.emptyList());
+ private EndpointImpl createDefaultEndpoint(Object serviceBean, String address) {
+ return createEndpoint(
+ serviceBean, address, List.of(messageIdInterceptor, messageHeaderInterceptor), List.of(LoggingFeatureProxy.INSTANCE.get())
+ );
}
- private void createOcppService(Bus bus, Object serviceBean, String address,
- List> interceptors,
- Collection extends Feature> features) {
- JaxWsServerFactoryBean f = new JaxWsServerFactoryBean();
- f.setBus(bus);
- f.setServiceBean(serviceBean);
- f.setAddress(address);
- f.getFeatures().addAll(features);
- f.getInInterceptors().addAll(interceptors);
- f.create();
+ private EndpointImpl createEndpoint(Object serviceBean, String address,
+ List> interceptors,
+ List extends Feature> features) {
+ EndpointImpl endpoint = new EndpointImpl(bus, serviceBean);
+ endpoint.getInInterceptors().addAll(interceptors);
+ endpoint.getFeatures().addAll(features);
+ endpoint.publish(address);
+ return endpoint;
}
}
diff --git a/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
index 6da0f5981..005cdfc1b 100644
--- a/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
+++ b/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
@@ -26,14 +26,13 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.authentication.www.BasicAuthenticationFilter;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* @author Sevket Goekay
* @since 07.01.2015
@@ -53,12 +52,12 @@ public class SecurityConfiguration {
*/
@Bean
public PasswordEncoder passwordEncoder() {
- return CONFIG.getAuth().getPasswordEncoder();
+ return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- final String prefix = CONFIG.getSpringManagerMapping();
+ final String prefix = SteveProperties.SPRING_MANAGER_MAPPING;
return http
.authorizeHttpRequests(
@@ -66,7 +65,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(
"/", // we have RootRedirectController to redirect "/" to "/manager"
"/static/**",
- CONFIG.getCxfMapping() + "/**",
+ SteveProperties.CXF_MAPPING + "/**",
WebSocketConfiguration.PATH_INFIX + "**",
"/WEB-INF/views/**" // https://github.com/spring-projects/spring-security/issues/13285#issuecomment-1579097065
).permitAll()
@@ -75,7 +74,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
// SOAP stations are making POST calls for communication. even though the following path is permitted 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.getCxfMapping() + "/**"))
+ .csrf(c -> c.ignoringRequestMatchers(SteveProperties.CXF_MAPPING + "/**"))
.sessionManagement(req -> req.invalidSessionUrl(prefix + "/signin"))
.formLogin(req -> req.loginPage(prefix + "/signin").permitAll())
.logout(req -> req.logoutUrl(prefix + "/signout"))
@@ -85,7 +84,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
@Bean
@Order(1)
public SecurityFilterChain apiKeyFilterChain(HttpSecurity http, ApiAuthenticationManager apiAuthenticationManager) throws Exception {
- return http.securityMatcher(CONFIG.getApiMapping() + "/**")
+ return http.securityMatcher(SteveProperties.API_MAPPING + "/**")
.csrf(k -> k.disable())
.sessionManagement(k -> k.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilter(new BasicAuthenticationFilter(apiAuthenticationManager, apiAuthenticationManager))
diff --git a/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java b/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
new file mode 100644
index 000000000..95f9a88a1
--- /dev/null
+++ b/src/main/java/de/rwth/idsg/steve/config/SteveProperties.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ocpp.ws.custom.WsSessionSelectStrategyEnum;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Sevket Goekay
+ * @since 19.08.2014
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "steve")
+public class SteveProperties {
+
+ // Web frontend
+ public static final String SPRING_MANAGER_MAPPING = "/manager";
+ // Mapping for CXF SOAP services
+ public static final String CXF_MAPPING = "/services";
+ // Mapping for Web APIs
+ public static final String API_MAPPING = "/api";
+ // Dummy service path
+ public static final String ROUTER_ENDPOINT_PATH = "/CentralSystemService";
+ // Time zone for the application and database connections
+ public static final String TIME_ZONE_ID = "UTC"; // or ZoneId.systemDefault().getId();
+
+ String version;
+ Auth auth = new Auth();
+ Jooq jooq = new Jooq();
+ Ocpp ocpp = new Ocpp();
+
+ @Data
+ public static class Jooq {
+ boolean executiveLogging;
+ }
+
+ @Data
+ public static class Auth {
+ String username;
+ String password;
+ String webApiKey;
+ String webApiSecret;
+ }
+
+ @Data
+ public static class Ocpp {
+ WsSessionSelectStrategyEnum wsSessionSelectStrategy;
+ boolean autoRegisterUnknownStations;
+ String chargeBoxIdValidationRegex;
+ }
+}
diff --git a/src/main/java/de/rwth/idsg/steve/config/WebConfig.java b/src/main/java/de/rwth/idsg/steve/config/WebConfig.java
deleted file mode 100644
index 353990a8a..000000000
--- a/src/main/java/de/rwth/idsg/steve/config/WebConfig.java
+++ /dev/null
@@ -1,71 +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 lombok.extern.slf4j.Slf4j;
-import org.apache.cxf.transport.servlet.CXFServlet;
-import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
-import org.springframework.web.WebApplicationInitializer;
-import org.springframework.web.context.ContextLoaderListener;
-import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
-import org.springframework.web.servlet.DispatcherServlet;
-
-import jakarta.servlet.DispatcherType;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletException;
-
-import java.util.EnumSet;
-
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
-/**
- * Jetty will automatically detect this class because of {@link de.rwth.idsg.steve.JettyServer#SCAN_PATTERN} and
- * use it to initialize the Spring and servlets and filters.
- *
- * @author Sevket Goekay
- * @since 17.09.2025
- */
-@Slf4j
-public class WebConfig implements WebApplicationInitializer {
-
- @Override
- public void onStartup(ServletContext servletContext) throws ServletException {
- log.info("Initializing");
-
- // Spring root context
- AnnotationConfigWebApplicationContext springContext = new AnnotationConfigWebApplicationContext();
- springContext.scan("de.rwth.idsg.steve.config");
- servletContext.addListener(new ContextLoaderListener(springContext));
-
- // Spring MVC
- var dispatcher = servletContext.addServlet("spring-dispatcher", new DispatcherServlet(springContext));
- dispatcher.setLoadOnStartup(1);
- dispatcher.addMapping(CONFIG.getSpringMapping());
-
- // Apache CXF
- var cxf = servletContext.addServlet("cxf", new CXFServlet());
- cxf.setLoadOnStartup(1);
- cxf.addMapping(CONFIG.getCxfMapping() + "/*");
-
- // add spring security
- servletContext
- .addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, "org.springframework.web.filter.DelegatingFilterProxy")
- .addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, CONFIG.getSpringMapping() + "*");
- }
-}
diff --git a/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java
index 3a9294fcc..0da47c3d8 100644
--- a/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java
+++ b/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java
@@ -24,6 +24,7 @@
import de.rwth.idsg.steve.ocpp.ws.ocpp15.Ocpp15WebSocketEndpoint;
import de.rwth.idsg.steve.ocpp.ws.ocpp16.Ocpp16WebSocketEndpoint;
import de.rwth.idsg.steve.service.ChargePointRegistrationService;
+import de.rwth.idsg.steve.web.validation.ChargeBoxIdValidator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.websocket.core.WebSocketConstants;
@@ -47,6 +48,7 @@
public class WebSocketConfiguration implements WebSocketConfigurer {
private final ChargePointRegistrationService chargePointRegistrationService;
+ private final ChargeBoxIdValidator chargeBoxIdValidator;
private final Ocpp12WebSocketEndpoint ocpp12WebSocketEndpoint;
private final Ocpp15WebSocketEndpoint ocpp15WebSocketEndpoint;
private final Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint;
@@ -59,6 +61,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
OcppWebSocketHandshakeHandler handshakeHandler = new OcppWebSocketHandshakeHandler(
+ chargeBoxIdValidator,
defaultHandshakeHandler(),
Lists.newArrayList(ocpp16WebSocketEndpoint, ocpp15WebSocketEndpoint, ocpp12WebSocketEndpoint),
chargePointRegistrationService
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java
index c47e52e88..df899ebcf 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/ClientProvider.java
@@ -26,15 +26,13 @@
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.ws.addressing.WSAddressingFeature;
import org.jetbrains.annotations.Nullable;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.web.server.Ssl;
import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
import jakarta.xml.ws.soap.SOAPBinding;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* @author Sevket Goekay
* @since 21.10.2015
@@ -44,12 +42,12 @@ public class ClientProvider {
@Nullable private final TLSClientParameters tlsClientParams;
- public ClientProvider() {
- if (shouldInitSSL()) {
- tlsClientParams = new TLSClientParameters();
- tlsClientParams.setSSLSocketFactory(setupSSL());
- } else {
- tlsClientParams = null;
+ public ClientProvider(ServerProperties serverProperties) {
+ Ssl ssl = serverProperties.getSsl();
+ try {
+ tlsClientParams = create(ssl);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
}
@@ -76,24 +74,29 @@ private static JaxWsProxyFactoryBean getBean(String endpointAddress) {
return f;
}
- private static boolean shouldInitSSL() {
- return CONFIG.getJetty().getKeyStorePath() != null && CONFIG.getJetty().getKeyStorePassword() != null;
- }
+ private static TLSClientParameters create(Ssl ssl) throws Exception {
+ if (ssl == null || !ssl.isEnabled()) {
+ return null;
+ }
- private static SSLSocketFactory setupSSL() {
- SSLContext ssl;
- try {
- String keyStorePath = CONFIG.getJetty().getKeyStorePath();
- String keyStorePwd = CONFIG.getJetty().getKeyStorePassword();
- ssl = SslContextBuilder.builder()
- .keyStoreFromFile(keyStorePath, keyStorePwd)
- .usingTLS()
- .usingDefaultAlgorithm()
- .usingKeyManagerPasswordFromKeyStore()
- .buildMergedWithSystem();
- } catch (Exception e) {
- throw new RuntimeException(e);
+ String keyStorePath = ssl.getKeyStore();
+ String keyStorePwd = ssl.getKeyStorePassword();
+
+ boolean shouldInit = StringUtils.hasLength(keyStorePath) && StringUtils.hasLength(keyStorePwd);
+ if (!shouldInit) {
+ return null;
}
- return ssl.getSocketFactory();
+
+ var socketFactory = SslContextBuilder.builder()
+ .keyStoreFromFile(keyStorePath, keyStorePwd)
+ .usingTLS()
+ .usingDefaultAlgorithm()
+ .usingKeyManagerPasswordFromKeyStore()
+ .buildMergedWithSystem()
+ .getSocketFactory();
+
+ var tlsClientParams = new TLSClientParameters();
+ tlsClientParams.setSSLSocketFactory(socketFactory);
+ return tlsClientParams;
}
}
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
index f12175117..20927e333 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MediatorInInterceptor.java
@@ -19,16 +19,14 @@
package de.rwth.idsg.steve.ocpp.soap;
import lombok.extern.slf4j.Slf4j;
-import org.apache.cxf.Bus;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.SoapVersion;
import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.endpoint.ServerRegistry;
import org.apache.cxf.interceptor.StaxInInterceptor;
+import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
-import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.staxutils.DepthXMLStreamReader;
import org.apache.cxf.staxutils.StaxUtils;
@@ -42,8 +40,6 @@
import java.util.List;
import java.util.Map;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* Taken from http://cxf.apache.org/docs/service-routing.html and modified.
*/
@@ -52,10 +48,10 @@ public class MediatorInInterceptor extends AbstractPhaseInterceptor
private final Map actualServers;
- public MediatorInInterceptor(Bus bus) {
+ public MediatorInInterceptor(List endpoints) {
super(Phase.POST_STREAM);
super.addBefore(StaxInInterceptor.class.getName());
- actualServers = initServerLookupMap(bus);
+ actualServers = initServerLookupMap(endpoints);
}
public final void handleMessage(SoapMessage message) {
@@ -91,7 +87,9 @@ public final void handleMessage(SoapMessage message) {
Server targetServer = actualServers.get(schemaNamespace);
// Redirect the request
- if (targetServer != null) {
+ if (targetServer == null) {
+ log.warn("No server mapped for namespace '{}'", schemaNamespace);
+ } else {
targetServer.getDestination().getMessageObserver().onMessage(message);
}
@@ -105,30 +103,11 @@ 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) {
- String exceptionMsg = "The services are not created and/or registered to the bus yet.";
-
- ServerRegistry serverRegistry = bus.getExtension(ServerRegistry.class);
- if (serverRegistry == null) {
- throw new RuntimeException(exceptionMsg);
- }
-
- List temp = serverRegistry.getServers();
- if (temp.isEmpty()) {
- throw new RuntimeException(exceptionMsg);
- }
-
- Map actualServers = new HashMap<>(temp.size() - 1);
- for (Server server : temp) {
- EndpointInfo info = server.getEndpoint().getEndpointInfo();
- String address = info.getAddress();
-
- // exclude the 'dummy' routing server
- if (CONFIG.getRouterEndpointPath().equals(address)) {
- continue;
- }
-
- String serverNamespace = info.getName().getNamespaceURI();
+ private static Map initServerLookupMap(List endpoints) {
+ Map actualServers = new HashMap<>();
+ for (EndpointImpl endpoint : endpoints) {
+ Server server = endpoint.getServer();
+ String serverNamespace = server.getEndpoint().getEndpointInfo().getName().getNamespaceURI();
actualServers.put(serverNamespace, server);
}
return actualServers;
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java
index 2636670a0..c2dacf771 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java
@@ -20,6 +20,7 @@
import com.google.common.base.Strings;
import de.rwth.idsg.steve.config.DelegatingTaskScheduler;
+import de.rwth.idsg.steve.config.SteveProperties;
import de.rwth.idsg.steve.config.WebSocketConfiguration;
import de.rwth.idsg.steve.ocpp.OcppTransport;
import de.rwth.idsg.steve.ocpp.OcppVersion;
@@ -64,9 +65,9 @@ public abstract class AbstractWebSocketEndpoint extends ConcurrentWebSocketHandl
private final OcppServerRepository ocppServerRepository;
private final FutureResponseContextStore futureResponseContextStore;
private final IncomingPipeline pipeline;
+ private final SessionContextStore sessionContextStore;
private final Logger log = LoggerFactory.getLogger(getClass());
- private final SessionContextStore sessionContextStore = new SessionContextStoreImpl();
private final List> connectedCallbackList = new ArrayList<>();
private final List> disconnectedCallbackList = new ArrayList<>();
private final Object sessionContextLock = new Object();
@@ -75,11 +76,13 @@ public AbstractWebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler,
OcppServerRepository ocppServerRepository,
FutureResponseContextStore futureResponseContextStore,
ApplicationEventPublisher applicationEventPublisher,
+ SteveProperties steveProperties,
AbstractTypeStore typeStore) {
this.asyncTaskScheduler = asyncTaskScheduler;
this.ocppServerRepository = ocppServerRepository;
this.futureResponseContextStore = futureResponseContextStore;
this.pipeline = new IncomingPipeline(new Deserializer(futureResponseContextStore, typeStore), this);
+ this.sessionContextStore = new SessionContextStoreImpl(steveProperties.getOcpp().getWsSessionSelectStrategy());
connectedCallbackList.add((chargeBoxId) -> applicationEventPublisher.publishEvent(new OcppStationWebSocketConnected(chargeBoxId)));
disconnectedCallbackList.add((chargeBoxId) -> applicationEventPublisher.publishEvent(new OcppStationWebSocketDisconnected(chargeBoxId)));
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java
index 963ff23ff..89eee32e7 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java
@@ -48,8 +48,7 @@
@RequiredArgsConstructor
public class OcppWebSocketHandshakeHandler implements HandshakeHandler {
- private static final ChargeBoxIdValidator CHARGE_BOX_ID_VALIDATOR = new ChargeBoxIdValidator();
-
+ private final ChargeBoxIdValidator chargeBoxIdValidator;
private final DefaultHandshakeHandler delegate;
private final List endpoints;
private final ChargePointRegistrationService chargePointRegistrationService;
@@ -72,7 +71,7 @@ public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse respons
// -------------------------------------------------------------------------
String chargeBoxId = getLastBitFromUrl(request.getURI().getPath());
- boolean isValid = CHARGE_BOX_ID_VALIDATOR.isValid(chargeBoxId);
+ boolean isValid = chargeBoxIdValidator.isValid(chargeBoxId);
if (!isValid) {
log.error("ChargeBoxId '{}' violates the configured pattern.", chargeBoxId);
response.setStatusCode(HttpStatus.BAD_REQUEST);
@@ -118,9 +117,9 @@ public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse respons
}
private AbstractWebSocketEndpoint selectEndpoint(List requestedProtocols ) {
- for (String requestedProcotol : requestedProtocols) {
+ for (String requestedProtocol : requestedProtocols) {
for (AbstractWebSocketEndpoint item : endpoints) {
- if (item.getVersion().getValue().equals(requestedProcotol)) {
+ if (item.getVersion().getValue().equals(requestedProtocol)) {
return item;
}
}
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java
index 6b6696ca7..e584a64fe 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/SessionContextStoreImpl.java
@@ -23,6 +23,7 @@
import de.rwth.idsg.steve.SteveException;
import de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategy;
import de.rwth.idsg.steve.ocpp.ws.data.SessionContext;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.web.socket.WebSocketSession;
@@ -37,13 +38,12 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* @author Sevket Goekay
* @since 17.03.2015
*/
@Slf4j
+@RequiredArgsConstructor
public class SessionContextStoreImpl implements SessionContextStore {
/**
@@ -54,7 +54,7 @@ public class SessionContextStoreImpl implements SessionContextStore {
private final Striped locks = Striped.lock(16);
- private final WsSessionSelectStrategy wsSessionSelectStrategy = CONFIG.getOcpp().getWsSessionSelectStrategy();
+ private final WsSessionSelectStrategy wsSessionSelectStrategy;
@Override
public void add(String chargeBoxId, WebSocketSession session, ScheduledFuture pingSchedule) {
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java
index dc330c6f8..383de53b7 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java
@@ -21,6 +21,7 @@
import de.rwth.idsg.ocpp.jaxb.RequestType;
import de.rwth.idsg.ocpp.jaxb.ResponseType;
import de.rwth.idsg.steve.config.DelegatingTaskScheduler;
+import de.rwth.idsg.steve.config.SteveProperties;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import de.rwth.idsg.steve.ocpp.OcppVersion;
import de.rwth.idsg.steve.ocpp.soap.CentralSystemService12_SoapServer;
@@ -52,8 +53,9 @@ public Ocpp12WebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler,
OcppServerRepository ocppServerRepository,
FutureResponseContextStore futureResponseContextStore,
ApplicationEventPublisher applicationEventPublisher,
- CentralSystemService12_SoapServer server) {
- super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher, Ocpp12TypeStore.INSTANCE);
+ CentralSystemService12_SoapServer server,
+ SteveProperties steveProperties) {
+ super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher, steveProperties, Ocpp12TypeStore.INSTANCE);
this.server = server;
}
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java
index ee4d72b18..f3765a487 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java
@@ -21,6 +21,7 @@
import de.rwth.idsg.ocpp.jaxb.RequestType;
import de.rwth.idsg.ocpp.jaxb.ResponseType;
import de.rwth.idsg.steve.config.DelegatingTaskScheduler;
+import de.rwth.idsg.steve.config.SteveProperties;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import de.rwth.idsg.steve.ocpp.OcppVersion;
import de.rwth.idsg.steve.ocpp.soap.CentralSystemService15_SoapServer;
@@ -53,8 +54,9 @@ public Ocpp15WebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler,
OcppServerRepository ocppServerRepository,
FutureResponseContextStore futureResponseContextStore,
ApplicationEventPublisher applicationEventPublisher,
- CentralSystemService15_SoapServer server) {
- super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher, Ocpp15TypeStore.INSTANCE);
+ CentralSystemService15_SoapServer server,
+ SteveProperties steveProperties) {
+ super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher, steveProperties, Ocpp15TypeStore.INSTANCE);
this.server = server;
}
diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java
index d0cdeab29..6c0517c55 100644
--- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java
+++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java
@@ -21,6 +21,7 @@
import de.rwth.idsg.ocpp.jaxb.RequestType;
import de.rwth.idsg.ocpp.jaxb.ResponseType;
import de.rwth.idsg.steve.config.DelegatingTaskScheduler;
+import de.rwth.idsg.steve.config.SteveProperties;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import de.rwth.idsg.steve.ocpp.OcppVersion;
import de.rwth.idsg.steve.ocpp.soap.CentralSystemService16_SoapServer;
@@ -53,8 +54,9 @@ public Ocpp16WebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler,
OcppServerRepository ocppServerRepository,
FutureResponseContextStore futureResponseContextStore,
ApplicationEventPublisher applicationEventPublisher,
- CentralSystemService16_SoapServer server) {
- super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher, Ocpp16TypeStore.INSTANCE);
+ CentralSystemService16_SoapServer server,
+ SteveProperties steveProperties) {
+ super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher, steveProperties, Ocpp16TypeStore.INSTANCE);
this.server = server;
}
diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java
index 64e2c9588..dab61e9bb 100644
--- a/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java
+++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java
@@ -19,6 +19,7 @@
package de.rwth.idsg.steve.service;
import com.google.common.util.concurrent.Striped;
+import de.rwth.idsg.steve.config.SteveProperties;
import de.rwth.idsg.steve.repository.ChargePointRepository;
import de.rwth.idsg.steve.service.dto.UnidentifiedIncomingObject;
import lombok.RequiredArgsConstructor;
@@ -31,8 +32,6 @@
import java.util.Optional;
import java.util.concurrent.locks.Lock;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* @author Sevket Goekay
* @since 15.09.2025
@@ -43,7 +42,7 @@
public class ChargePointRegistrationService {
private final UnidentifiedIncomingObjectService unknownChargePointService = new UnidentifiedIncomingObjectService(100);
- private final boolean autoRegisterUnknownStations = CONFIG.getOcpp().isAutoRegisterUnknownStations();
+ private final SteveProperties steveProperties;
private final Striped isRegisteredLocks = Striped.lock(16);
private final ChargePointRepository chargePointRepository;
@@ -84,7 +83,7 @@ private Optional getRegistrationStatusInternal(String charge
}
// 2. ok, this chargeBoxId is unknown. exit if auto-register is disabled
- if (!autoRegisterUnknownStations) {
+ if (!steveProperties.getOcpp().isAutoRegisterUnknownStations()) {
return Optional.empty();
}
diff --git a/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java b/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
index 74c08c71b..afde05f4d 100644
--- a/src/main/java/de/rwth/idsg/steve/service/GithubReleaseCheckService.java
+++ b/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.joda.JodaModule;
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;
@@ -37,7 +37,6 @@
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
-import jakarta.annotation.PostConstruct;
import java.io.File;
import java.util.Collections;
@@ -58,9 +57,12 @@ public class GithubReleaseCheckService implements ReleaseCheckService {
private static final String TAG_NAME_PREFIX = "steve-";
private static final String FILE_SEPARATOR = File.separator;
+ private final SteveProperties steveProperties;
private final RestTemplate restTemplate;
- public GithubReleaseCheckService() {
+ public GithubReleaseCheckService(SteveProperties steveProperties) {
+ this.steveProperties = steveProperties;
+
var timeout = Timeout.ofMilliseconds(API_TIMEOUT_IN_MILLIS);
var connectionConfig = ConnectionConfig.custom().setConnectTimeout(timeout).build();
@@ -99,10 +101,10 @@ public ReleaseReport check() {
// Private helpers
// -------------------------------------------------------------------------
- private static ReleaseReport getReport(ReleaseResponse response) {
+ private ReleaseReport getReport(ReleaseResponse response) {
String githubVersion = extractVersion(response);
- Version build = Version.valueOf(SteveConfiguration.CONFIG.getSteveVersion());
+ Version build = Version.valueOf(steveProperties.getVersion());
Version github = Version.valueOf(githubVersion);
boolean isGithubMoreRecent = github.greaterThan(build);
diff --git a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java b/src/main/java/de/rwth/idsg/steve/service/WebUserService.java
index f2305e94e..00d95eab0 100644
--- a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java
+++ b/src/main/java/de/rwth/idsg/steve/service/WebUserService.java
@@ -22,7 +22,7 @@
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.config.SteveProperties;
import de.rwth.idsg.steve.repository.WebUserRepository;
import jooq.steve.db.tables.records.WebUserRecord;
import lombok.RequiredArgsConstructor;
@@ -39,6 +39,7 @@
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
@@ -69,6 +70,8 @@ public class WebUserService implements UserDetailsManager {
private final ObjectMapper jacksonObjectMapper;
private final WebUserRepository webUserRepository;
+ private final SteveProperties steveProperties;
+ private final PasswordEncoder passwordEncoder;
private final SecurityContextHolderStrategy securityContextHolderStrategy = getContextHolderStrategy();
private final Cache userCache = CacheBuilder.newBuilder()
@@ -82,15 +85,15 @@ public void afterStart(ContextRefreshedEvent event) {
return;
}
- var headerVal = SteveConfiguration.CONFIG.getWebApi().getHeaderValue();
+ var headerVal = steveProperties.getAuth().getWebApiSecret();
- var encodedApiPassword = StringUtils.isEmpty(headerVal)
+ var encodedApiPassword = StringUtils.isBlank(headerVal)
? null
- : SteveConfiguration.CONFIG.getAuth().getPasswordEncoder().encode(headerVal);
+ : passwordEncoder.encode(headerVal);
var user = new WebUserRecord()
- .setUsername(SteveConfiguration.CONFIG.getAuth().getUserName())
- .setPassword(SteveConfiguration.CONFIG.getAuth().getEncodedPassword())
+ .setUsername(steveProperties.getAuth().getUsername())
+ .setPassword(passwordEncoder.encode(steveProperties.getAuth().getPassword()))
.setApiPassword(encodedApiPassword)
.setEnabled(true)
.setAuthorities(toJson(AuthorityUtils.createAuthorityList("ADMIN")));
diff --git a/src/main/java/de/rwth/idsg/steve/utils/InternetChecker.java b/src/main/java/de/rwth/idsg/steve/utils/InternetChecker.java
index 26fd40208..70667878b 100644
--- a/src/main/java/de/rwth/idsg/steve/utils/InternetChecker.java
+++ b/src/main/java/de/rwth/idsg/steve/utils/InternetChecker.java
@@ -18,7 +18,6 @@
*/
package de.rwth.idsg.steve.utils;
-import de.rwth.idsg.steve.SteveConfiguration;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@@ -49,7 +48,7 @@ public final class InternetChecker {
);
static {
- System.setProperty("http.agent", "SteVe/" + SteveConfiguration.CONFIG.getSteveCompositeVersion());
+ System.setProperty("http.agent", "SteVe (github)");
}
/**
diff --git a/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java b/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java
index 4592d9f86..daf6e17ae 100644
--- a/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java
+++ b/src/main/java/de/rwth/idsg/steve/utils/LogFileRetriever.java
@@ -93,7 +93,7 @@ private List getActiveLogFilePaths() {
Iterator> appenderIterator = logger.iteratorForAppenders();
List fileNameList = new ArrayList<>();
- if (appenderIterator.hasNext()) {
+ while (appenderIterator.hasNext()) {
var appender = appenderIterator.next();
String fileName = extractFileName(appender);
if (fileName != null) {
diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
index 58f286a19..711ff6883 100644
--- a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
+++ b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java
@@ -19,6 +19,7 @@
package de.rwth.idsg.steve.web.controller;
import de.rwth.idsg.steve.NotificationFeature;
+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;
@@ -40,8 +41,6 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* One controller for about and settings pages
*
@@ -57,6 +56,7 @@ public class AboutSettingsController {
private final SettingsRepository settingsRepository;
private final MailService mailService;
private final ReleaseCheckService releaseCheckService;
+ private final SteveProperties steveProperties;
// -------------------------------------------------------------------------
// Paths
@@ -73,7 +73,7 @@ public class AboutSettingsController {
public String getAbout(Model model, @RequestHeader(HttpHeaders.HOST) String host, HttpServletRequest request) {
String scheme = request.getScheme();
- model.addAttribute("version", CONFIG.getSteveVersion());
+ model.addAttribute("version", steveProperties.getVersion());
model.addAttribute("db", genericRepository.getDBVersion());
model.addAttribute("logFile", logController.getLogFilePath());
model.addAttribute("systemTime", DateTime.now());
diff --git a/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java b/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
index 0bee84a5e..49da5a555 100644
--- a/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
+++ b/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdListValidator.java
@@ -18,17 +18,24 @@
*/
package de.rwth.idsg.steve.web.validation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
+
import java.util.List;
/**
* @author Sevket Goekay
* @since 21.01.2016
*/
+@Component
+@RequiredArgsConstructor
public class ChargeBoxIdListValidator implements ConstraintValidator> {
- private static final ChargeBoxIdValidator VALIDATOR = new ChargeBoxIdValidator();
+ private final ChargeBoxIdValidator validator;
@Override
public void initialize(ChargeBoxId constraintAnnotation) {
@@ -37,8 +44,11 @@ public void initialize(ChargeBoxId constraintAnnotation) {
@Override
public boolean isValid(List value, ConstraintValidatorContext context) {
+ if (CollectionUtils.isEmpty(value)) {
+ return true; // null or empty is valid, because it is another constraint's responsibility
+ }
for (String s : value) {
- if (!VALIDATOR.isValid(s, context)) {
+ if (!validator.isValid(s, context)) {
return false;
}
}
diff --git a/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java b/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
index 5c21ad58f..de2aade67 100644
--- a/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
+++ b/src/main/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidator.java
@@ -19,7 +19,10 @@
package de.rwth.idsg.steve.web.validation;
import com.google.common.base.Strings;
-import de.rwth.idsg.steve.SteveConfiguration;
+import de.rwth.idsg.steve.config.SteveProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
@@ -29,10 +32,21 @@
* @author Sevket Goekay
* @since 21.01.2016
*/
+@Component
public class ChargeBoxIdValidator implements ConstraintValidator {
private static final String REGEX = "[^=/()<>]*";
- private static final Pattern PATTERN = Pattern.compile(getRegexToUse());
+
+ private final Pattern pattern;
+
+ @Autowired
+ public ChargeBoxIdValidator(SteveProperties steveProperties) {
+ this(steveProperties.getOcpp().getChargeBoxIdValidationRegex());
+ }
+
+ public ChargeBoxIdValidator(String regexFromConfig) {
+ this.pattern = Pattern.compile(Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig);
+ }
@Override
public void initialize(ChargeBoxId idTag) {
@@ -57,11 +71,6 @@ public boolean isValid(String str) {
return false;
}
- return PATTERN.matcher(str).matches();
- }
-
- private static String getRegexToUse() {
- String regexFromConfig = SteveConfiguration.CONFIG.getOcpp().getChargeBoxIdValidationRegex();
- return Strings.isNullOrEmpty(regexFromConfig) ? REGEX : regexFromConfig;
+ return pattern.matcher(str).matches();
}
}
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index 60393536b..27f4f6926 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -62,7 +62,5 @@ auto.register.unknown.stations = false
charge-box-id.validation.regex =
### DO NOT MODIFY ###
-steve.version = ${project.version}
-git.describe = ${git.commit.id.describe}
db.sql.logging = true
profile = dev
diff --git a/src/main/resources/application-docker.properties b/src/main/resources/application-docker.properties
index 0a5f04886..0417a1d60 100644
--- a/src/main/resources/application-docker.properties
+++ b/src/main/resources/application-docker.properties
@@ -62,7 +62,5 @@ auto.register.unknown.stations = false
charge-box-id.validation.regex =
### DO NOT MODIFY ###
-steve.version = ${project.version}
-git.describe = ${git.commit.id.describe}
db.sql.logging = false
profile = prod
diff --git a/src/main/resources/application-kubernetes.properties b/src/main/resources/application-kubernetes.properties
index a6585dddd..c236425f0 100644
--- a/src/main/resources/application-kubernetes.properties
+++ b/src/main/resources/application-kubernetes.properties
@@ -62,7 +62,5 @@ auto.register.unknown.stations = false
charge-box-id.validation.regex =
### DO NOT MODIFY ###
-steve.version = ${project.version}
-git.describe = ${git.commit.id.describe}
db.sql.logging = false
profile = prod
diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties
index 1d52f855c..4b420f783 100644
--- a/src/main/resources/application-prod.properties
+++ b/src/main/resources/application-prod.properties
@@ -62,7 +62,5 @@ auto.register.unknown.stations = false
charge-box-id.validation.regex =
### DO NOT MODIFY ###
-steve.version = ${project.version}
-git.describe = ${git.commit.id.describe}
db.sql.logging = false
profile = prod
diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties
index a16d52434..cf7436299 100644
--- a/src/main/resources/application-test.properties
+++ b/src/main/resources/application-test.properties
@@ -62,7 +62,5 @@ auto.register.unknown.stations = false
charge-box-id.validation.regex =
### DO NOT MODIFY ###
-steve.version = ${project.version}
-git.describe = ${git.commit.id.describe}
db.sql.logging = true
profile = test
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644
index b9fe48da6..000000000
--- a/src/main/resources/application.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-### DO NOT MODIFY ###
-profile = ${envName}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 000000000..78641ba79
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,33 @@
+spring:
+ profiles.active: @envName@
+ application:
+ name: steve
+ datasource:
+ url: jdbc:mysql://${db.ip}:${db.port}/${db.schema}
+ username: ${db.user}
+ password: ${db.password}
+
+server:
+ address: ${server.host}
+ port: ${http.port}
+ servlet.context-path: /${context.path}
+ compression:
+ enabled: ${server.gzip.enabled}
+ ssl:
+ enabled: ${https.enabled}
+ key-store: ${keystore.path}
+ key-store-password: ${keystore.password}
+
+steve:
+ version: @project.version@
+ jooq:
+ executive-logging: ${db.sql.logging}
+ auth:
+ username: ${auth.user}
+ password: ${auth.password}
+ web-api-key: ${webapi.key}
+ web-api-secret: ${webapi.value}
+ ocpp:
+ ws-session-select-strategy: ${ws.session.select.strategy}
+ auto-register-unknown-stations: ${auto.register.unknown.stations}
+ charge-box-id-validation-regex: ${charge-box-id.validation.regex}
diff --git a/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java b/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java
index a3554a77c..52e3fbafd 100644
--- a/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java
+++ b/src/test/java/de/rwth/idsg/steve/ApplicationJsonTest.java
@@ -30,11 +30,16 @@
import ocpp.cs._2015._10.HeartbeatResponse;
import ocpp.cs._2015._10.RegistrationStatus;
import org.eclipse.jetty.websocket.api.exceptions.UpgradeException;
-import org.junit.jupiter.api.AfterAll;
+import org.jooq.DSLContext;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.HttpStatus;
+import org.springframework.test.context.ActiveProfiles;
import static de.rwth.idsg.steve.utils.Helpers.getRandomString;
@@ -43,6 +48,8 @@
* @since 21.03.2018
*/
@Slf4j
+@ActiveProfiles(profiles = "test")
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class ApplicationJsonTest {
private static final String PATH = "ws://localhost:8080/steve/websocket/CentralSystemService/";
@@ -50,23 +57,20 @@ public class ApplicationJsonTest {
private static final String REGISTERED_CHARGE_BOX_ID = __DatabasePreparer__.getRegisteredChargeBoxId();
private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag();
- private static Application app;
+ @Autowired
+ private DSLContext dslContext;
- @BeforeAll
- public static void init() throws Exception {
- Assertions.assertEquals("test", SteveConfiguration.CONFIG.getProfile());
- __DatabasePreparer__.prepare();
+ private __DatabasePreparer__ databasePreparer;
- app = new Application();
- app.start();
+ @BeforeEach
+ public void setup() {
+ databasePreparer = new __DatabasePreparer__(dslContext);
+ databasePreparer.prepare();
}
- @AfterAll
- public static void destroy() throws Exception {
- if (app != null) {
- app.stop();
- }
- __DatabasePreparer__.cleanUp();
+ @AfterEach
+ public void teardown() {
+ databasePreparer.cleanUp();
}
@Test
diff --git a/src/test/java/de/rwth/idsg/steve/ApplicationTest.java b/src/test/java/de/rwth/idsg/steve/ApplicationTest.java
index 52e5c8f18..909f0cff4 100644
--- a/src/test/java/de/rwth/idsg/steve/ApplicationTest.java
+++ b/src/test/java/de/rwth/idsg/steve/ApplicationTest.java
@@ -27,10 +27,16 @@
import ocpp.cs._2010._08.BootNotificationResponse;
import ocpp.cs._2010._08.RegistrationStatus;
import ocpp.cs._2012._06.CentralSystemService;
-import org.junit.jupiter.api.AfterAll;
+import org.jooq.DSLContext;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.ActiveProfiles;
import jakarta.xml.ws.WebServiceException;
@@ -45,29 +51,31 @@
* @since 10.03.2018
*/
@Slf4j
+@ActiveProfiles(profiles = "test")
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class ApplicationTest {
private static final String REGISTERED_CHARGE_BOX_ID = __DatabasePreparer__.getRegisteredChargeBoxId();
private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag();
- private static final String path = getPath();
- private static Application app;
+ @Autowired
+ private ServerProperties serverProperties;
+ @Autowired
+ private DSLContext dslContext;
- @BeforeAll
- public static void init() throws Exception {
- Assertions.assertEquals("test", SteveConfiguration.CONFIG.getProfile());
- __DatabasePreparer__.prepare();
+ private __DatabasePreparer__ databasePreparer;
+ private String path;
- app = new Application();
- app.start();
+ @BeforeEach
+ public void setup() {
+ databasePreparer = new __DatabasePreparer__(dslContext);
+ databasePreparer.prepare();
+ path = getPath(serverProperties);
}
- @AfterAll
- public static void destroy() throws Exception {
- if (app != null) {
- app.stop();
- }
- __DatabasePreparer__.cleanUp();
+ @AfterEach
+ public void teardown() {
+ databasePreparer.cleanUp();
}
@Test
diff --git a/src/test/java/de/rwth/idsg/steve/OperationalTestSoapOCPP16.java b/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java
similarity index 84%
rename from src/test/java/de/rwth/idsg/steve/OperationalTestSoapOCPP16.java
rename to src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java
index 1632a78a8..4532c7cb8 100644
--- a/src/test/java/de/rwth/idsg/steve/OperationalTestSoapOCPP16.java
+++ b/src/test/java/de/rwth/idsg/steve/OperationalSoapOCPP16Test.java
@@ -18,6 +18,7 @@
*/
package de.rwth.idsg.steve;
+import de.rwth.idsg.steve.config.SteveProperties;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import de.rwth.idsg.steve.ocpp.soap.MessageHeaderInterceptor;
import de.rwth.idsg.steve.repository.ReservationStatus;
@@ -52,15 +53,21 @@
import ocpp.cs._2015._10.StopTransactionRequest;
import ocpp.cs._2015._10.StopTransactionResponse;
import org.joda.time.DateTime;
-import org.junit.jupiter.api.AfterAll;
+import org.jooq.DSLContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.ActiveProfiles;
import jakarta.xml.ws.WebServiceException;
+
import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.List;
import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16;
@@ -72,41 +79,40 @@
* @since 22.03.18
*/
@Slf4j
-public class OperationalTestSoapOCPP16 {
+@ActiveProfiles(profiles = "test")
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+public class OperationalSoapOCPP16Test {
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 String path = getPath();
private static final int numConnectors = 5;
- private static Application app;
-
- @BeforeAll
- public static void initClass() throws Exception {
- Assertions.assertEquals("test", SteveConfiguration.CONFIG.getProfile());
- app = new Application();
- app.start();
- }
+ @Autowired
+ private ServerProperties serverProperties;
+ @Autowired
+ private SteveProperties steveProperties;
+ @Autowired
+ private DSLContext dslContext;
- @AfterAll
- public static void destroyClass() throws Exception {
- app.stop();
- }
+ private __DatabasePreparer__ databasePreparer;
+ private String path;
@BeforeEach
- public void init() throws Exception {
- __DatabasePreparer__.prepare();
+ public void setup() {
+ databasePreparer = new __DatabasePreparer__(dslContext);
+ databasePreparer.prepare();
+ path = getPath(serverProperties);
}
@AfterEach
- public void destroy() throws Exception {
- __DatabasePreparer__.cleanUp();
+ public void teardown() {
+ databasePreparer.cleanUp();
}
@Test
public void testUnregisteredCP() {
- Assertions.assertFalse(SteveConfiguration.CONFIG.getOcpp().isAutoRegisterUnknownStations());
+ Assertions.assertFalse(steveProperties.getOcpp().isAutoRegisterUnknownStations());
CentralSystemService client = getForOcpp16(path);
@@ -127,14 +133,14 @@ public void testUnregisteredCP() {
*
* 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.
+ * Therefore, no exception. This case is tested by {@link OperationalSoapOCPP16Test#testUnregisteredCP()} already.
*
* WS/JSON stations cannot connect at all if they are not registered, as ensured by {@link OcppWebSocketUpgrader}.
*/
@Test
public void testUnregisteredCPWithInterceptor() {
Assertions.assertThrows(WebServiceException.class, () -> {
- Assertions.assertFalse(SteveConfiguration.CONFIG.getOcpp().isAutoRegisterUnknownStations());
+ Assertions.assertFalse(steveProperties.getOcpp().isAutoRegisterUnknownStations());
CentralSystemService client = getForOcpp16(path);
@@ -150,7 +156,7 @@ public void testRegisteredCP() {
initStationWithBootNotification(client);
- ChargePoint.Details details = __DatabasePreparer__.getCBDetails(REGISTERED_CHARGE_BOX_ID);
+ ChargePoint.Details details = databasePreparer.getCBDetails(REGISTERED_CHARGE_BOX_ID);
Assertions.assertTrue(details.getChargeBox().getOcppProtocol().contains("ocpp1.6"));
}
@@ -186,26 +192,26 @@ public void testInTransactionStatusOfIdTag() {
new StartTransactionRequest()
.withConnectorId(2)
.withIdTag(REGISTERED_OCPP_TAG)
- .withTimestamp(DateTime.now())
+ .withTimestamp(nowWithoutMillis())
.withMeterStart(0),
REGISTERED_CHARGE_BOX_ID
);
Assertions.assertNotNull(start);
Assertions.assertTrue(start.getTransactionId() > 0);
- Assertions.assertTrue(__DatabasePreparer__.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction());
+ Assertions.assertTrue(databasePreparer.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction());
StopTransactionResponse stop = client.stopTransaction(
new StopTransactionRequest()
.withTransactionId(start.getTransactionId())
- .withTimestamp(DateTime.now())
+ .withTimestamp(nowWithoutMillis())
.withIdTag(REGISTERED_OCPP_TAG)
.withMeterStop(30),
REGISTERED_CHARGE_BOX_ID
);
Assertions.assertNotNull(stop);
- Assertions.assertFalse(__DatabasePreparer__.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction());
+ Assertions.assertFalse(databasePreparer.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction());
}
/**
@@ -226,7 +232,7 @@ public void testAuthorizationStatus() {
new StartTransactionRequest()
.withConnectorId(2)
.withIdTag(REGISTERED_OCPP_TAG)
- .withTimestamp(DateTime.now())
+ .withTimestamp(nowWithoutMillis())
.withMeterStart(0),
REGISTERED_CHARGE_BOX_ID);
Assertions.assertTrue(start1.getTransactionId() > 0);
@@ -248,7 +254,7 @@ public void testAuthorizationStatus() {
new StartTransactionRequest()
.withConnectorId(2)
.withIdTag(REGISTERED_OCPP_TAG)
- .withTimestamp(DateTime.now())
+ .withTimestamp(nowWithoutMillis())
.withMeterStart(0),
REGISTERED_CHARGE_BOX_ID_2);
Assertions.assertTrue(start2.getTransactionId() > 0);
@@ -265,6 +271,13 @@ public void testAuthorizationStatus() {
public void testStatusNotification() {
CentralSystemService client = getForOcpp16(path);
+ DateTime startingTime = DateTime.parse("2020-10-01T00:00:00.000Z");
+ LinkedHashMap timeStatusMap = new LinkedHashMap<>();
+ for (int i = 0; i < ChargePointStatus.values().length; i++) {
+ ChargePointStatus status = ChargePointStatus.values()[i];
+ timeStatusMap.put(startingTime.plusMinutes(i), status);
+ }
+
// -------------------------------------------------------------------------
// init the station and verify db connector status values
// -------------------------------------------------------------------------
@@ -272,23 +285,23 @@ public void testStatusNotification() {
initStationWithBootNotification(client);
// test all status enum values
- for (ChargePointStatus chargePointStatus : ChargePointStatus.values()) {
+ for (var entry : timeStatusMap.entrySet()) {
// status for numConnectors connectors + connector 0 (main controller of CP)
for (int i = 0; i <= numConnectors; i++) {
StatusNotificationResponse status = client.statusNotification(
new StatusNotificationRequest()
.withErrorCode(ChargePointErrorCode.NO_ERROR)
- .withStatus(chargePointStatus)
+ .withStatus(entry.getValue())
.withConnectorId(i)
- .withTimestamp(DateTime.now()),
+ .withTimestamp(entry.getKey()),
REGISTERED_CHARGE_BOX_ID
);
Assertions.assertNotNull(status);
}
- List connectorStatusList = __DatabasePreparer__.getChargePointConnectorStatus();
+ List connectorStatusList = databasePreparer.getChargePointConnectorStatus();
for (ConnectorStatus connectorStatus : connectorStatusList) {
- Assertions.assertEquals(chargePointStatus.value(), connectorStatus.getStatus());
+ Assertions.assertEquals(entry.getValue().value(), connectorStatus.getStatus());
Assertions.assertEquals(ChargePointErrorCode.NO_ERROR.value(), connectorStatus.getErrorCode());
}
}
@@ -298,19 +311,19 @@ public void testStatusNotification() {
// -------------------------------------------------------------------------
int faultyConnectorId = 1;
+ DateTime faultedTime = timeStatusMap.lastEntry().getKey().plusMinutes(1);
StatusNotificationResponse statusConnectorError = client.statusNotification(
new StatusNotificationRequest()
.withErrorCode(ChargePointErrorCode.HIGH_TEMPERATURE)
.withStatus(ChargePointStatus.FAULTED)
.withConnectorId(faultyConnectorId)
- .withTimestamp(DateTime.now()),
+ .withTimestamp(faultedTime),
REGISTERED_CHARGE_BOX_ID
);
Assertions.assertNotNull(statusConnectorError);
-
- List connectorStatusList = __DatabasePreparer__.getChargePointConnectorStatus();
+ List connectorStatusList = databasePreparer.getChargePointConnectorStatus();
for (ConnectorStatus connectorStatus : connectorStatusList) {
if (connectorStatus.getConnectorId() == faultyConnectorId) {
Assertions.assertEquals(ChargePointStatus.FAULTED.value(), connectorStatus.getStatus());
@@ -327,6 +340,7 @@ public void testReservation() {
int usedConnectorID = 1;
CentralSystemService client = getForOcpp16(path);
+ DateTime baseTime = nowWithoutMillis();
// -------------------------------------------------------------------------
// init the station and make reservation
@@ -335,7 +349,7 @@ public void testReservation() {
initStationWithBootNotification(client);
initConnectorsWithStatusNotification(client);
- int reservationId = __DatabasePreparer__.makeReservation(usedConnectorID);
+ int reservationId = databasePreparer.makeReservation(usedConnectorID);
// -------------------------------------------------------------------------
// startTransaction (invalid reservationId)
@@ -347,7 +361,7 @@ public void testReservation() {
new StartTransactionRequest()
.withConnectorId(usedConnectorID)
.withIdTag(REGISTERED_OCPP_TAG)
- .withTimestamp(DateTime.now())
+ .withTimestamp(baseTime.plusSeconds(1))
.withMeterStart(0)
.withReservationId(nonExistingReservationId),
REGISTERED_CHARGE_BOX_ID
@@ -355,13 +369,13 @@ public void testReservation() {
Assertions.assertNotNull(startInvalid);
// validate that the transaction is written to db, even though reservation was invalid
- List transactions = __DatabasePreparer__.getTransactions();
+ List transactions = databasePreparer.getTransactions();
Assertions.assertEquals(1, transactions.size());
Assertions.assertEquals(startInvalid.getTransactionId(), transactions.get(0).getId());
// make sure that this invalid reservation had no side effects
{
- List reservations = __DatabasePreparer__.getReservations();
+ List reservations = databasePreparer.getReservations();
Assertions.assertEquals(1, reservations.size());
Reservation res = reservations.get(0);
Assertions.assertEquals(reservationId, res.getId());
@@ -376,7 +390,7 @@ public void testReservation() {
new StartTransactionRequest()
.withConnectorId(3)
.withIdTag(getRandomString())
- .withTimestamp(DateTime.now())
+ .withTimestamp(baseTime.plusSeconds(2))
.withMeterStart(0)
.withReservationId(reservationId),
REGISTERED_CHARGE_BOX_ID
@@ -384,7 +398,7 @@ public void testReservation() {
Assertions.assertNotNull(startWrongTag);
{
- List reservations = __DatabasePreparer__.getReservations();
+ List reservations = databasePreparer.getReservations();
Assertions.assertEquals(1, reservations.size());
Reservation res = reservations.get(0);
Assertions.assertEquals(ReservationStatus.ACCEPTED.value(), res.getStatus());
@@ -399,7 +413,7 @@ public void testReservation() {
new StartTransactionRequest()
.withConnectorId(usedConnectorID)
.withIdTag(REGISTERED_OCPP_TAG)
- .withTimestamp(DateTime.now())
+ .withTimestamp(baseTime.plusSeconds(3))
.withMeterStart(0)
.withReservationId(reservationId),
REGISTERED_CHARGE_BOX_ID
@@ -408,7 +422,7 @@ public void testReservation() {
Integer transactionIdValid = startValidId.getTransactionId();
{
- List reservations = __DatabasePreparer__.getReservations();
+ List reservations = databasePreparer.getReservations();
Assertions.assertEquals(reservations.size(), 1);
Reservation res = reservations.get(0);
Assertions.assertEquals(ReservationStatus.USED.value(), res.getStatus());
@@ -423,7 +437,7 @@ public void testReservation() {
new StartTransactionRequest()
.withConnectorId(usedConnectorID)
.withIdTag(REGISTERED_OCPP_TAG)
- .withTimestamp(DateTime.now())
+ .withTimestamp(baseTime.plusSeconds(4))
.withMeterStart(0)
.withReservationId(reservationId),
REGISTERED_CHARGE_BOX_ID
@@ -431,7 +445,7 @@ public void testReservation() {
Assertions.assertNotNull(startValidIdUsedTwice);
{
- List reservations = __DatabasePreparer__.getReservations();
+ List reservations = databasePreparer.getReservations();
Assertions.assertEquals(reservations.size(), 1);
Reservation res = reservations.get(0);
Assertions.assertEquals(ReservationStatus.USED.value(), res.getStatus());
@@ -485,7 +499,7 @@ private void testBody(List meterValues, List transaction
// startTransaction
- DateTime startTimeStamp = DateTime.now();
+ DateTime startTimeStamp = nowWithoutMillis();
StartTransactionResponse start = client.startTransaction(
new StartTransactionRequest()
.withConnectorId(usedConnectorID)
@@ -498,7 +512,7 @@ private void testBody(List meterValues, List transaction
int transactionID = start.getTransactionId();
- List allTransactions = __DatabasePreparer__.getTransactionRecords();
+ List allTransactions = databasePreparer.getTransactionRecords();
Assertions.assertEquals(1, allTransactions.size());
{
@@ -517,7 +531,7 @@ private void testBody(List meterValues, List transaction
.withStatus(ChargePointStatus.CHARGING)
.withErrorCode(ChargePointErrorCode.NO_ERROR)
.withConnectorId(0)
- .withTimestamp(DateTime.now()),
+ .withTimestamp(nowWithoutMillis()),
REGISTERED_CHARGE_BOX_ID
);
@@ -537,7 +551,7 @@ private void testBody(List meterValues, List transaction
}
// stopTransaction
- DateTime stopTimeStamp = DateTime.now();
+ DateTime stopTimeStamp = nowWithoutMillis();
int stopValue = 30;
StopTransactionResponse stop = client.stopTransaction(
new StopTransactionRequest()
@@ -551,7 +565,7 @@ private void testBody(List meterValues, List transaction
{
Assertions.assertNotNull(stop);
- List transactionsStop = __DatabasePreparer__.getTransactionRecords();
+ List transactionsStop = databasePreparer.getTransactionRecords();
Assertions.assertEquals(1, transactionsStop.size());
TransactionRecord t = transactionsStop.get(0);
Assertions.assertEquals(stopTimeStamp, t.getStopTimestamp());
@@ -568,7 +582,7 @@ private void testBody(List meterValues, List transaction
.withStatus(ChargePointStatus.AVAILABLE)
.withErrorCode(ChargePointErrorCode.NO_ERROR)
.withConnectorId(usedConnectorID)
- .withTimestamp(DateTime.now()),
+ .withTimestamp(nowWithoutMillis()),
REGISTERED_CHARGE_BOX_ID
);
Assertions.assertNotNull(statusStop);
@@ -591,7 +605,7 @@ private void initConnectorsWithStatusNotification(CentralSystemService client) {
.withErrorCode(ChargePointErrorCode.NO_ERROR)
.withStatus(ChargePointStatus.AVAILABLE)
.withConnectorId(i)
- .withTimestamp(DateTime.now()),
+ .withTimestamp(nowWithoutMillis()),
REGISTERED_CHARGE_BOX_ID
);
Assertions.assertNotNull(statusBoot);
@@ -599,7 +613,7 @@ private void initConnectorsWithStatusNotification(CentralSystemService client) {
}
private void checkMeterValues(List meterValues, int transactionPk) {
- TransactionDetails details = __DatabasePreparer__.getDetails(transactionPk);
+ TransactionDetails details = databasePreparer.getDetails(transactionPk);
// iterate over all created meter values
for (MeterValue meterValue : meterValues) {
@@ -635,7 +649,14 @@ private List getMeterValues() {
}
private static MeterValue createMeterValue(String val) {
- return new MeterValue().withTimestamp(DateTime.now())
+ return new MeterValue().withTimestamp(nowWithoutMillis())
.withSampledValue(new SampledValue().withValue(val));
}
+
+ /**
+ * https://github.com/steve-community/steve/issues/1371
+ */
+ private static DateTime nowWithoutMillis() {
+ return DateTime.now().withMillisOfSecond(0);
+ }
}
diff --git a/src/test/java/de/rwth/idsg/steve/StressTest.java b/src/test/java/de/rwth/idsg/steve/StressTest.java
index 56ec4d029..f64f2dbd7 100644
--- a/src/test/java/de/rwth/idsg/steve/StressTest.java
+++ b/src/test/java/de/rwth/idsg/steve/StressTest.java
@@ -22,12 +22,19 @@
import ocpp.cs._2015._10.MeterValue;
import ocpp.cs._2015._10.SampledValue;
import org.joda.time.DateTime;
+import org.jooq.DSLContext;
import org.junit.jupiter.api.Assertions;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.ConfigurableEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import static de.rwth.idsg.steve.utils.Helpers.getJsonPath;
+import static de.rwth.idsg.steve.utils.Helpers.getPath;
+
/**
* @author Sevket Goekay
* @since 10.05.2018
@@ -49,21 +56,40 @@ public abstract class StressTest {
protected static final int CHARGE_BOX_COUNT = THREAD_COUNT;
protected static final int CONNECTOR_COUNT_PER_CHARGE_BOX = 25;
+ protected String soapPath;
+ protected String jsonPath;
+
protected void attack() throws Exception {
- Assertions.assertEquals("test", SteveConfiguration.CONFIG.getProfile());
- Assertions.assertTrue(SteveConfiguration.CONFIG.getOcpp().isAutoRegisterUnknownStations());
+ System.setProperty("spring.profiles.active", "test");
- __DatabasePreparer__.prepare();
+ __DatabasePreparer__ databasePreparer = null;
+ ConfigurableApplicationContext app = null;
- Application app = new Application();
try {
- app.start();
+ app = SteveApplication.start();
+
+ ConfigurableEnvironment environment = app.getEnvironment();
+ Assertions.assertEquals(new String[]{"test"}, environment.getActiveProfiles());
+ Assertions.assertEquals("true", environment.getProperty("steve.ocpp.auto-register-unknown-stations"));
+
+ ServerProperties serverProperties = app.getBean(ServerProperties.class);
+ soapPath = getPath(serverProperties);
+ jsonPath = getJsonPath(serverProperties);
+
+ DSLContext dslContext = app.getBean(DSLContext.class);
+ databasePreparer = new __DatabasePreparer__(dslContext);
+ databasePreparer.prepare();
+
attackInternal();
} finally {
try {
- app.stop();
+ if (app != null) {
+ app.close();
+ }
} finally {
- __DatabasePreparer__.cleanUp();
+ if (databasePreparer != null) {
+ databasePreparer.cleanUp();
+ }
}
}
}
diff --git a/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java b/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java
index 3517c1792..fb8b42718 100644
--- a/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java
+++ b/src/test/java/de/rwth/idsg/steve/StressTestJsonOCPP16.java
@@ -46,7 +46,6 @@
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
-import static de.rwth.idsg.steve.utils.Helpers.getJsonPath;
import static de.rwth.idsg.steve.utils.Helpers.getRandomString;
import static de.rwth.idsg.steve.utils.Helpers.getRandomStrings;
@@ -56,7 +55,6 @@
*/
public class StressTestJsonOCPP16 extends StressTest {
- private static final String PATH = getJsonPath();
private static final OcppVersion VERSION = OcppVersion.V_16;
public static void main(String[] args) throws Exception {
@@ -76,7 +74,7 @@ public void beforeRepeat() {
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
String chargeBoxId = chargeBoxIds.get(localRandom.nextInt(chargeBoxIds.size()));
- threadLocalChargePoint.set(new OcppJsonChargePoint(VERSION, chargeBoxId, PATH));
+ threadLocalChargePoint.set(new OcppJsonChargePoint(VERSION, chargeBoxId, jsonPath));
OcppJsonChargePoint chargePoint = threadLocalChargePoint.get();
chargePoint.start();
diff --git a/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java b/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java
index dc6e79cd2..bb89ac584 100644
--- a/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java
+++ b/src/test/java/de/rwth/idsg/steve/StressTestSoapOCPP16.java
@@ -45,7 +45,6 @@
import java.util.concurrent.ThreadLocalRandom;
import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16;
-import static de.rwth.idsg.steve.utils.Helpers.getPath;
import static de.rwth.idsg.steve.utils.Helpers.getRandomString;
import static de.rwth.idsg.steve.utils.Helpers.getRandomStrings;
@@ -55,8 +54,6 @@
*/
public class StressTestSoapOCPP16 extends StressTest {
- private static final String path = getPath();
-
public static void main(String[] args) throws Exception {
new StressTestSoapOCPP16().attack();
}
@@ -71,7 +68,7 @@ protected void attackInternal() throws Exception {
@Override
public void beforeRepeat() {
- CentralSystemService client = getForOcpp16(path);
+ CentralSystemService client = getForOcpp16(soapPath);
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
threadLocalChargeBoxId.set(chargeBoxIds.get(localRandom.nextInt(chargeBoxIds.size())));
@@ -89,7 +86,7 @@ public void beforeRepeat() {
@Override
public void toRepeat() {
- CentralSystemService client = getForOcpp16(path);
+ CentralSystemService client = getForOcpp16(soapPath);
ThreadLocalRandom localRandom = ThreadLocalRandom.current();
String chargeBoxId = threadLocalChargeBoxId.get();
@@ -183,4 +180,4 @@ public void afterRepeat() {
tester.test(runnable);
tester.shutDown();
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/de/rwth/idsg/steve/issues/Issue72.java b/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
index c78e34380..1b097cdb7 100644
--- a/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
+++ b/src/test/java/de/rwth/idsg/steve/issues/Issue72.java
@@ -39,7 +39,6 @@
import org.junit.jupiter.api.Assertions;
import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16;
-import static de.rwth.idsg.steve.utils.Helpers.getPath;
import static de.rwth.idsg.steve.utils.Helpers.getRandomString;
/**
@@ -50,8 +49,6 @@
*/
public class Issue72 extends StressTest {
- private static final String path = getPath();
-
public static void main(String[] args) throws Exception {
new Issue72().attack();
}
@@ -68,14 +65,14 @@ protected void attackInternal() throws Exception {
int meterStart = 444;
int meterStop = 99999;
- BootNotificationResponse boot = getForOcpp16(path).bootNotification(
+ BootNotificationResponse boot = getForOcpp16(soapPath).bootNotification(
new BootNotificationRequest()
.withChargePointVendor(getRandomString())
.withChargePointModel(getRandomString()),
chargeBoxId);
Assertions.assertEquals(RegistrationStatus.ACCEPTED, boot.getStatus());
- StartTransactionResponse start = getForOcpp16(path).startTransaction(
+ StartTransactionResponse start = getForOcpp16(soapPath).startTransaction(
new StartTransactionRequest()
.withConnectorId(connectorId)
.withIdTag(idTag)
@@ -93,7 +90,7 @@ protected void attackInternal() throws Exception {
@Override
public void beforeRepeat() {
- threadLocalClient.set(getForOcpp16(path));
+ threadLocalClient.set(getForOcpp16(soapPath));
}
@Override
@@ -134,4 +131,4 @@ public void afterRepeat() {
tester.test(runnable);
tester.shutDown();
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java b/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
index f73646e6a..d6370558b 100644
--- a/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
+++ b/src/test/java/de/rwth/idsg/steve/issues/Issue72LowLevelSoap.java
@@ -46,7 +46,6 @@
import org.junit.jupiter.api.Assertions;
import static de.rwth.idsg.steve.utils.Helpers.getForOcpp16;
-import static de.rwth.idsg.steve.utils.Helpers.getPath;
import static de.rwth.idsg.steve.utils.Helpers.getRandomString;
/**
@@ -57,8 +56,6 @@
*/
public class Issue72LowLevelSoap extends StressTest {
- private static final String path = getPath();
-
public static void main(String[] args) throws Exception {
new Issue72LowLevelSoap().attack();
}
@@ -75,14 +72,14 @@ protected void attackInternal() throws Exception {
int meterStart = 444;
int meterStop = 99999;
- BootNotificationResponse boot = getForOcpp16(path).bootNotification(
+ BootNotificationResponse boot = getForOcpp16(soapPath).bootNotification(
new BootNotificationRequest()
.withChargePointVendor(getRandomString())
.withChargePointModel(getRandomString()),
chargeBoxId);
Assertions.assertEquals(RegistrationStatus.ACCEPTED, boot.getStatus());
- StartTransactionResponse start = getForOcpp16(path).startTransaction(
+ StartTransactionResponse start = getForOcpp16(soapPath).startTransaction(
new StartTransactionRequest()
.withConnectorId(connectorId)
.withIdTag(idTag)
@@ -97,7 +94,7 @@ protected void attackInternal() throws Exception {
String body = buildRequest(chargeBoxId, transactionId, idTag, stopDateTime, meterStop);
ContentType contentType = ContentType.create(MediaType.SOAP_XML_UTF_8.type(), MediaType.SOAP_XML_UTF_8.charset().orNull());
- HttpUriRequest req = RequestBuilder.post(path)
+ HttpUriRequest req = RequestBuilder.post(soapPath)
.addHeader("SOAPAction", "urn://Ocpp/Cs/2015/10/StopTransaction")
.setEntity(new StringEntity(body, contentType))
.build();
@@ -110,7 +107,7 @@ protected void attackInternal() throws Exception {
@Override
public void beforeRepeat() {
- threadLocalClient.set(getForOcpp16(path));
+ threadLocalClient.set(getForOcpp16(soapPath));
}
@Override
@@ -158,12 +155,12 @@ public void afterRepeat() {
}
}
- private static String buildRequest(String chargeBoxId, int transactionId, String idTag,
- DateTime stop, int meterStop) {
+ private String buildRequest(String chargeBoxId, int transactionId, String idTag,
+ DateTime stop, int meterStop) {
return "" +
"/StopTransaction" +
"urn:uuid:47c9e1d9-a278-4e9c-8f08-565c29d86167" +
- "" + path + "" +
+ "" + soapPath + "" +
"http://www.w3.org/2005/08/addressing/anonymous" +
"" + chargeBoxId + "" +
"" +
@@ -173,4 +170,4 @@ private static String buildRequest(String chargeBoxId, int transactionId, String
"" + meterStop + "" +
"";
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/de/rwth/idsg/steve/issues/Issue73Fix.java b/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java
similarity index 66%
rename from src/test/java/de/rwth/idsg/steve/issues/Issue73Fix.java
rename to src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java
index 510137781..cde821c0e 100644
--- a/src/test/java/de/rwth/idsg/steve/issues/Issue73Fix.java
+++ b/src/test/java/de/rwth/idsg/steve/issues/Issue73FixTest.java
@@ -19,8 +19,6 @@
package de.rwth.idsg.steve.issues;
import com.google.common.collect.Lists;
-import de.rwth.idsg.steve.Application;
-import de.rwth.idsg.steve.SteveConfiguration;
import de.rwth.idsg.steve.utils.__DatabasePreparer__;
import ocpp.cs._2015._10.AuthorizationStatus;
import ocpp.cs._2015._10.AuthorizeRequest;
@@ -32,7 +30,16 @@
import ocpp.cs._2015._10.StartTransactionRequest;
import ocpp.cs._2015._10.StartTransactionResponse;
import org.joda.time.DateTime;
+import org.jooq.DSLContext;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.ActiveProfiles;
import java.util.List;
@@ -42,49 +49,53 @@
/**
* https://github.com/steve-community/steve/issues/73
+ * https://github.com/steve-community/steve/issues/219
*
* @author Sevket Goekay
* @since 02.07.2018
*/
-public class Issue73Fix {
+@ActiveProfiles(profiles = "test")
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+public class Issue73FixTest {
private static final String REGISTERED_OCPP_TAG = __DatabasePreparer__.getRegisteredOcppTag();
- private static final String path = getPath();
-
- public static void main(String[] args) throws Exception {
- Assertions.assertEquals("test", SteveConfiguration.CONFIG.getProfile());
- Assertions.assertTrue(SteveConfiguration.CONFIG.getOcpp().isAutoRegisterUnknownStations());
-
- __DatabasePreparer__.prepare();
-
- Application app = new Application();
- try {
- app.start();
- test();
- } finally {
- try {
- app.stop();
- } finally {
- __DatabasePreparer__.cleanUp();
- }
- }
+
+ @Autowired
+ private ServerProperties serverProperties;
+ @Autowired
+ private DSLContext dslContext;
+
+ private __DatabasePreparer__ databasePreparer;
+
+ @BeforeEach
+ public void setup() {
+ databasePreparer = new __DatabasePreparer__(dslContext);
+ databasePreparer.prepare();
}
- private static void test() {
- ocpp.cs._2015._10.CentralSystemService client = getForOcpp16(path);
+ @AfterEach
+ public void teardown() {
+ databasePreparer.cleanUp();
+ }
+
+ @Test
+ public void test() {
+ ocpp.cs._2015._10.CentralSystemService client = getForOcpp16(getPath(serverProperties));
- String chargeBox1 = getRandomString();
- String chargeBox2 = getRandomString();
+ String chargeBox1 = __DatabasePreparer__.getRegisteredChargeBoxId();
+ String chargeBox2 = __DatabasePreparer__.getRegisteredChargeBoxId2();
sendBoot(client, Lists.newArrayList(chargeBox1, chargeBox2));
sendAuth(client, chargeBox1, AuthorizationStatus.ACCEPTED);
- sendStartTx(client, chargeBox1);
+ sendStartTx(client, chargeBox1, AuthorizationStatus.ACCEPTED);
sendAuth(client, chargeBox1, AuthorizationStatus.ACCEPTED);
- sendAuth(client, chargeBox2, AuthorizationStatus.CONCURRENT_TX);
+ sendAuth(client, chargeBox2, AuthorizationStatus.ACCEPTED);
+
+ sendStartTx(client, chargeBox2, AuthorizationStatus.CONCURRENT_TX);
}
private static void sendBoot(CentralSystemService client, List chargeBoxIdList) {
@@ -105,7 +116,7 @@ private static void sendAuth(CentralSystemService client, String chargeBoxId, Au
Assertions.assertEquals(expected, auth.getIdTagInfo().getStatus());
}
- private static void sendStartTx(CentralSystemService client, String chargeBoxId) {
+ private void sendStartTx(CentralSystemService client, String chargeBoxId, AuthorizationStatus expected) {
StartTransactionResponse start = client.startTransaction(
new StartTransactionRequest()
.withConnectorId(2)
@@ -115,7 +126,8 @@ private static void sendStartTx(CentralSystemService client, String chargeBoxId)
chargeBoxId
);
Assertions.assertNotNull(start);
+ Assertions.assertEquals(expected, start.getIdTagInfo().getStatus());
Assertions.assertTrue(start.getTransactionId() > 0);
- Assertions.assertTrue(__DatabasePreparer__.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction());
+ Assertions.assertTrue(databasePreparer.getOcppTagRecord(REGISTERED_OCPP_TAG).getInTransaction());
}
}
diff --git a/src/test/java/de/rwth/idsg/steve/issues/Issue81.java b/src/test/java/de/rwth/idsg/steve/issues/Issue81.java
index 525a30174..26831ebee 100644
--- a/src/test/java/de/rwth/idsg/steve/issues/Issue81.java
+++ b/src/test/java/de/rwth/idsg/steve/issues/Issue81.java
@@ -42,8 +42,6 @@
*/
public class Issue81 extends StressTest {
- private static final String path = getPath();
-
public static void main(String[] args) throws Exception {
new Issue81().attack();
}
@@ -58,10 +56,10 @@ protected void attackInternal() throws Exception {
@Override
public void beforeRepeat() {
- client.set(getForOcpp16(path));
+ client.set(getForOcpp16(soapPath));
chargeBoxId.set(Helpers.getRandomString());
- BootNotificationResponse boot = getForOcpp16(path).bootNotification(
+ BootNotificationResponse boot = getForOcpp16(soapPath).bootNotification(
new BootNotificationRequest()
.withChargePointVendor(getRandomString())
.withChargePointModel(getRandomString()),
@@ -101,4 +99,4 @@ private static Integer sendStartTx(CentralSystemService client, StartTransaction
Assertions.assertNotNull(start);
return start.getTransactionId();
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/de/rwth/idsg/steve/utils/Helpers.java b/src/test/java/de/rwth/idsg/steve/utils/Helpers.java
index 0135ce56f..a4f8cb6ad 100644
--- a/src/test/java/de/rwth/idsg/steve/utils/Helpers.java
+++ b/src/test/java/de/rwth/idsg/steve/utils/Helpers.java
@@ -18,16 +18,17 @@
*/
package de.rwth.idsg.steve.utils;
+import de.rwth.idsg.steve.config.SteveProperties;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.ws.addressing.WSAddressingFeature;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
import jakarta.xml.ws.soap.SOAPBinding;
+
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
-import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
-
/**
* @author Andreas Heuvels
* @since 06.04.18
@@ -46,40 +47,41 @@ public static List getRandomStrings(int size) {
return list;
}
- public static String getPath() {
- 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();
+ /**
+ * only http:/ and https:/ because serverProperties.getAddress() starts with a slash.
+ */
+ public static String getPath(ServerProperties serverProperties) {
+ String prefix = "http:/";
+
+ if (serverProperties.getSsl().isEnabled()) {
+ prefix = "https:/";
}
- return prefix + CONFIG.getJetty().getServerHost() + ":" + port
- + CONFIG.getContextPath() + "/services" + CONFIG.getRouterEndpointPath();
+ return prefix
+ + serverProperties.getAddress()
+ + ":"
+ + serverProperties.getPort()
+ + serverProperties.getServlet().getContextPath()
+ + "/services"
+ + SteveProperties.ROUTER_ENDPOINT_PATH;
}
- public static String getJsonPath() {
- 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();
+ /**
+ * only ws:/ and wss:/ because serverProperties.getAddress() starts with a slash.
+ */
+ public static String getJsonPath(ServerProperties serverProperties) {
+ String prefix = "ws:/";
+
+ if (serverProperties.getSsl().isEnabled()) {
+ prefix = "wss:/";
}
- return prefix + CONFIG.getJetty().getServerHost() + ":" + port
- + CONFIG.getContextPath() + "/websocket/CentralSystemService/";
+ return prefix
+ + serverProperties.getAddress()
+ + ":"
+ + serverProperties.getPort()
+ + serverProperties.getServlet().getContextPath()
+ + "/websocket/CentralSystemService/";
}
public static ocpp.cs._2015._10.CentralSystemService getForOcpp16(String path) {
diff --git a/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java b/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java
index 5eafc5e90..92165ffe0 100644
--- a/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java
+++ b/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java
@@ -39,6 +39,7 @@
import jooq.steve.db.tables.Settings;
import jooq.steve.db.tables.records.OcppTagActivityRecord;
import jooq.steve.db.tables.records.TransactionRecord;
+import lombok.RequiredArgsConstructor;
import org.joda.time.DateTime;
import org.jooq.DSLContext;
import org.jooq.Schema;
@@ -64,6 +65,7 @@
* @author Sevket Goekay
* @since 21.03.2018
*/
+@RequiredArgsConstructor
public class __DatabasePreparer__ {
private static final String SCHEMA_TO_TRUNCATE = "stevedb_test_2aa6a783d47d";
@@ -71,10 +73,9 @@ public class __DatabasePreparer__ {
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 BeanConfiguration beanConfiguration = new BeanConfiguration();
- private static final DSLContext dslContext = beanConfiguration.dslContext(beanConfiguration.dataSource());
+ private final DSLContext dslContext;
- public static void prepare() {
+ public void prepare() {
runOperation(ctx -> {
truncateTables(ctx);
insertChargeBox(ctx);
@@ -82,7 +83,7 @@ public static void prepare() {
});
}
- public static int makeReservation(int connectorId) {
+ public int makeReservation(int connectorId) {
ReservationRepositoryImpl r = new ReservationRepositoryImpl(dslContext);
InsertReservationParams params = InsertReservationParams.builder()
.chargeBoxId(REGISTERED_CHARGE_BOX_ID)
@@ -95,8 +96,8 @@ public static int makeReservation(int connectorId) {
return reservationId;
}
- public static void cleanUp() {
- runOperation(__DatabasePreparer__::truncateTables);
+ public void cleanUp() {
+ runOperation(this::truncateTables);
}
public static String getRegisteredChargeBoxId() {
@@ -111,46 +112,46 @@ public static String getRegisteredOcppTag() {
return REGISTERED_OCPP_TAG;
}
- public static List getTransactions() {
+ public List getTransactions() {
TransactionRepositoryImpl impl = new TransactionRepositoryImpl(dslContext);
return impl.getTransactions(new TransactionQueryForm());
}
- public static List getTransactionRecords() {
+ public List getTransactionRecords() {
return dslContext.selectFrom(TRANSACTION).fetch();
}
- public static List getReservations() {
+ public List getReservations() {
ReservationRepositoryImpl impl = new ReservationRepositoryImpl(dslContext);
return impl.getReservations(new ReservationQueryForm());
}
- public static List getChargePointConnectorStatus() {
+ public List getChargePointConnectorStatus() {
ChargePointRepositoryImpl impl = new ChargePointRepositoryImpl(dslContext, new AddressRepositoryImpl());
return impl.getChargePointConnectorStatus();
}
- public static TransactionDetails getDetails(int transactionPk) {
+ public TransactionDetails getDetails(int transactionPk) {
TransactionRepositoryImpl impl = new TransactionRepositoryImpl(dslContext);
return impl.getDetails(transactionPk);
}
- public static OcppTagActivityRecord getOcppTagRecord(String idTag) {
+ public OcppTagActivityRecord getOcppTagRecord(String idTag) {
OcppTagRepositoryImpl impl = new OcppTagRepositoryImpl(dslContext);
return impl.getRecord(idTag);
}
- public static ChargePoint.Details getCBDetails(String chargeboxID) {
+ public ChargePoint.Details getCBDetails(String chargeboxID) {
ChargePointRepositoryImpl impl = new ChargePointRepositoryImpl(dslContext, new AddressRepositoryImpl());
Map pkMap = impl.getChargeBoxIdPkPair(Arrays.asList(chargeboxID));
int pk = pkMap.get(chargeboxID);
return impl.getDetails(pk);
}
- private static void runOperation(Consumer consumer) {
+ private void runOperation(Consumer consumer) {
consumer.accept(dslContext);
}
- private static void truncateTables(DSLContext ctx) {
+ private void truncateTables(DSLContext ctx) {
Set> skipList = Sets.newHashSet(
SchemaVersion.SCHEMA_VERSION,
Settings.SETTINGS,
diff --git a/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java b/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java
index 0504e18b5..985dffa8b 100644
--- a/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java
+++ b/src/test/java/de/rwth/idsg/steve/web/validation/ChargeBoxIdValidatorTest.java
@@ -27,7 +27,7 @@
*/
public class ChargeBoxIdValidatorTest {
- ChargeBoxIdValidator validator = new ChargeBoxIdValidator();
+ ChargeBoxIdValidator validator = new ChargeBoxIdValidator((String) null);
@Test
public void testNull() {