diff --git a/.gitignore b/.gitignore
index 898c9192c..b94b8ce17 100644
--- a/.gitignore
+++ b/.gitignore
@@ -348,6 +348,9 @@ scripts/dockerize/dockerfiles
/jdk_21_maven/cs/rest/lovemining/target/
/jdk_21_maven/em/external/rest/lovemining/target/
/jdk_21_maven/em/embedded/rest/lovemining/target/
+/jdk_21_maven/cs/rest/movies-xml/target/
+/jdk_21_maven/em/external/rest/movies-xml/target/
+/jdk_21_maven/em/embedded/rest/movies-xml/target/
/jdk_11_maven/em/embedded/rest/tracking-system/target/
/jdk_11_maven/em/external/rest/tracking-system/target/
/jdk_17_maven/em/embedded/rest/tiltaksgjennomforing/target/
diff --git a/README.md b/README.md
index f14750bbe..9956b363d 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,7 @@ How to setup authentication information, based on the current content of the ini
Auth configuration files can found in the [auth](auth) folder.
-### REST: Java/Kotlin (45)
+### REST: Java/Kotlin (46)
* **AdoptMe** (not-known license), [jdk_21_maven/cs/rest/adoptme](jdk_21_maven/cs/rest/adoptme), from [https://github.com/daanimelian/Programacion3-TPO](https://github.com/daanimelian/Programacion3-TPO)
@@ -129,6 +129,8 @@ Auth configuration files can found in the [auth](auth) folder.
* **Microcks** (Apache), [jdk_21_maven/cs/rest-gui/microcks](jdk_21_maven/cs/rest-gui/microcks), from [https://github.com/microcks/microcks](https://github.com/microcks/microcks)
+* **Movies XML** (LGPL), [jdk_21_maven/cs/rest/movies-xml](jdk_21_maven/cs/rest/movies-xml), from [https://github.com/suarezrominajulieta/movies-xml](https://github.com/suarezrominajulieta/movies-xml)
+
* **NCS**, [jdk_8_maven/cs/rest/artificial/ncs](jdk_8_maven/cs/rest/artificial/ncs), (not-known license, artificial numerical examples coming from different sources)
* **News** (LGPL), [jdk_8_maven/cs/rest/artificial/news](jdk_8_maven/cs/rest/artificial/news), from [https://github.com/arcuri82/testing_security_development_enterprise_systems](https://github.com/arcuri82/testing_security_development_enterprise_systems)
diff --git a/dockerfiles/movies-xml.dockerfile b/dockerfiles/movies-xml.dockerfile
new file mode 100644
index 000000000..73a029f86
--- /dev/null
+++ b/dockerfiles/movies-xml.dockerfile
@@ -0,0 +1,13 @@
+FROM amazoncorretto:21-alpine-jdk
+
+COPY ./dist/movies-xml-sut.jar .
+COPY ./dist/jacocoagent.jar .
+
+
+
+
+ENTRYPOINT \
+ java \
+ -javaagent:jacocoagent.jar=output=tcpserver,address=*,port=6300,append=false,dumponexit=false \
+ -jar movies-xml-sut.jar \
+ --server.port=8080
\ No newline at end of file
diff --git a/dockerfiles/movies-xml.yaml b/dockerfiles/movies-xml.yaml
new file mode 100644
index 000000000..da72a0f9c
--- /dev/null
+++ b/dockerfiles/movies-xml.yaml
@@ -0,0 +1,30 @@
+services:
+ mitmproxy:
+ image: mitmproxy/mitmproxy:10.2.4
+ user: "0:0"
+ environment:
+ MITM_LOG_DIR: /custom-logs
+ MITM_LOG_FILE: ${MITM_LOG_FILE:-minlog.csv}
+ command: >
+ mitmdump
+ --mode reverse:http://sut-movies-xml:8080
+ --listen-host 0.0.0.0
+ --listen-port 8080
+ --set keep_host_header=true
+ -s /addons/minlog.py
+ volumes:
+ - ${HOST_LOG_DIR:-./mitm-logs}:/custom-logs
+ - ./addons:/addons:ro
+ ports:
+ - "${HOST_PORT:-8080}:8080"
+ depends_on:
+ - sut-movies-xml
+ sut-movies-xml:
+ image: webfuzzing/wfd-movies-xml:FINAL
+# build:
+# dockerfile: ./dockerfiles/movies-xml.dockerfile
+# context: ..
+ environment:
+ AUTH_PORT: ${AUTH_PORT:-8081}
+ ports:
+ - "${JACOCO_PORT:-6300}:6300"
\ No newline at end of file
diff --git a/experiments/bb-exp.py b/experiments/bb-exp.py
index da8fb838f..a8cb13189 100644
--- a/experiments/bb-exp.py
+++ b/experiments/bb-exp.py
@@ -144,6 +144,7 @@ def __init__(self, name, auth, sleep, schemaformat, classfiles):
"jdk_21_maven/cs/rest-gui/microcks/distro/uber-async-minion/target/classes",
"jdk_21_maven/cs/rest-gui/microcks/minions/async/target/classes",
"jdk_21_maven/cs/rest-gui/microcks/webapp/target/classes"]),
+ Sut("movies-xml",False,SLEEP,JSON,["jdk_21_maven/cs/rest/movies-xml/target/classes"]),
Sut("ocvn",True,SLEEP,JSON,["jdk_8_maven/cs/rest-gui/ocvn/persistence/target/classes",
"jdk_8_maven/cs/rest-gui/ocvn/persistence-mongodb/target/classes",
"jdk_8_maven/cs/rest-gui/ocvn/web/target/classes"]),
diff --git a/experiments/wb-exp.py b/experiments/wb-exp.py
index e09f19858..230052a9b 100644
--- a/experiments/wb-exp.py
+++ b/experiments/wb-exp.py
@@ -203,6 +203,7 @@ def __init__(self, name, platform):
Sut("lovemining", JDK_21),
Sut("market", JDK_11),
Sut("microcks", JDK_21),
+ Sut("movies-xml", JDK_21),
Sut("ocvn", JDK_8),
Sut("ohsome-api", JDK_17),
Sut("pay-publicapi",JDK_11),
diff --git a/jacoco.dockerfile b/jacoco.dockerfile
index b818f8fcb..866ad536c 100644
--- a/jacoco.dockerfile
+++ b/jacoco.dockerfile
@@ -80,6 +80,7 @@ COPY jdk_21_maven/cs/rest-gui/microcks/distro/uber/target/classes ${CLASS_FILES
COPY jdk_21_maven/cs/rest-gui/microcks/distro/uber-async-minion/target/classes ${CLASS_FILES}/jdk_21_maven/cs/rest-gui/microcks/distro/uber-async-minion/target/classes
COPY jdk_21_maven/cs/rest-gui/microcks/minions/async/target/classes ${CLASS_FILES}/jdk_21_maven/cs/rest-gui/microcks/minions/async/target/classes
COPY jdk_21_maven/cs/rest-gui/microcks/webapp/target/classes ${CLASS_FILES}/jdk_21_maven/cs/rest-gui/microcks/webapp/target/classes
+COPY jdk_21_maven/cs/rest/movies-xml/target/classes ${CLASS_FILES}/jdk_21_maven/cs/rest/movies-xml/target/classes
COPY jdk_8_maven/cs/rest-gui/ocvn/persistence/target/classes ${CLASS_FILES}/jdk_8_maven/cs/rest-gui/ocvn/persistence/target/classes
COPY jdk_8_maven/cs/rest-gui/ocvn/persistence-mongodb/target/classes ${CLASS_FILES}/jdk_8_maven/cs/rest-gui/ocvn/persistence-mongodb/target/classes
COPY jdk_8_maven/cs/rest-gui/ocvn/web/target/classes ${CLASS_FILES}/jdk_8_maven/cs/rest-gui/ocvn/web/target/classes
diff --git a/jdk_21_maven/cs/rest/pom.xml b/jdk_21_maven/cs/rest/pom.xml
index d6a895853..e30878198 100644
--- a/jdk_21_maven/cs/rest/pom.xml
+++ b/jdk_21_maven/cs/rest/pom.xml
@@ -19,6 +19,7 @@
redis-sample
arimaa
lovemining
+ movies-xml
diff --git a/jdk_21_maven/em/embedded/rest/movies-xml/pom.xml b/jdk_21_maven/em/embedded/rest/movies-xml/pom.xml
new file mode 100644
index 000000000..d61b57966
--- /dev/null
+++ b/jdk_21_maven/em/embedded/rest/movies-xml/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ 4.0.0
+
+ evomaster-benchmark-jdk21-em-embedded-rest-movies-xml
+ jar
+
+
+ org.evomaster
+ evomaster-benchmark-jdk21-em-embedded-rest
+ 4.3.1-SNAPSHOT
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 3.5.16
+ pom
+ import
+
+
+
+
+
+
+
+ org.movies
+ movies-xml
+ 1.0.0
+
+
+
+
diff --git a/jdk_21_maven/em/embedded/rest/movies-xml/src/main/java/em/embedded/org/movies/xml/EmbeddedEvoMasterController.java b/jdk_21_maven/em/embedded/rest/movies-xml/src/main/java/em/embedded/org/movies/xml/EmbeddedEvoMasterController.java
new file mode 100644
index 000000000..9d3bbef0e
--- /dev/null
+++ b/jdk_21_maven/em/embedded/rest/movies-xml/src/main/java/em/embedded/org/movies/xml/EmbeddedEvoMasterController.java
@@ -0,0 +1,129 @@
+package em.embedded.org.movies.xml;
+
+import org.evomaster.client.java.controller.EmbeddedSutController;
+import org.evomaster.client.java.controller.InstrumentedSutStarter;
+import org.evomaster.client.java.controller.api.dto.SutInfoDto;
+import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto;
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+import org.evomaster.client.java.controller.problem.ProblemInfo;
+import org.evomaster.client.java.controller.problem.RestProblem;
+import org.evomaster.client.java.sql.DbSpecification;
+import org.movies.xml.MoviesXmlApplication;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class EmbeddedEvoMasterController extends EmbeddedSutController {
+
+ public static void main(String[] args) {
+
+ int port = 40100;
+ if (args.length > 0) {
+ port = Integer.parseInt(args[0]);
+ }
+
+ EmbeddedEvoMasterController controller = new EmbeddedEvoMasterController(port);
+ InstrumentedSutStarter starter = new InstrumentedSutStarter(controller);
+
+ starter.start();
+ }
+
+ private ConfigurableApplicationContext ctx;
+ private Connection sqlConnection;
+ private List dbSpecification;
+
+ public EmbeddedEvoMasterController() {
+ this(40100);
+ }
+
+ public EmbeddedEvoMasterController(int port) {
+ setControllerPort(port);
+ }
+
+ @Override
+ public String startSut() {
+
+ ctx = SpringApplication.run(MoviesXmlApplication.class, new String[]{
+ "--server.port=0",
+ "--logging.level.root=OFF",
+ "--logging.level.org.springframework=INFO"
+ });
+
+ closeDataBaseConnection();
+ try {
+ sqlConnection = ctx.getBean(JdbcTemplate.class).getDataSource().getConnection();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ dbSpecification = Arrays.asList(new DbSpecification(DatabaseType.H2, sqlConnection));
+
+ return "http://localhost:" + getSutPort();
+ }
+
+ protected int getSutPort() {
+ return (Integer) ((Map) ctx.getEnvironment()
+ .getPropertySources().get("server.ports").getSource())
+ .get("local.server.port");
+ }
+
+ @Override
+ public boolean isSutRunning() {
+ return ctx != null && ctx.isRunning();
+ }
+
+ @Override
+ public void stopSut() {
+ closeDataBaseConnection();
+ ctx.stop();
+ ctx.close();
+ }
+
+ private void closeDataBaseConnection() {
+ if (sqlConnection != null) {
+ try {
+ sqlConnection.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ sqlConnection = null;
+ }
+ }
+
+ @Override
+ public String getPackagePrefixesToCover() {
+ return "org.movies.xml.";
+ }
+
+ @Override
+ public void resetStateOfSUT() {
+ }
+
+ @Override
+ public List getDbSpecifications() {
+ return dbSpecification;
+ }
+
+ @Override
+ public List getInfoForAuthentication() {
+ return null;
+ }
+
+ @Override
+ public ProblemInfo getProblemInfo() {
+ return new RestProblem(
+ "http://localhost:" + getSutPort() + "/v3/api-docs",
+ null
+ );
+ }
+
+ @Override
+ public SutInfoDto.OutputFormat getPreferredOutputFormat() {
+ return SutInfoDto.OutputFormat.JAVA_JUNIT_5;
+ }
+}
diff --git a/jdk_21_maven/em/embedded/rest/pom.xml b/jdk_21_maven/em/embedded/rest/pom.xml
index 59c861f8f..bea19c910 100644
--- a/jdk_21_maven/em/embedded/rest/pom.xml
+++ b/jdk_21_maven/em/embedded/rest/pom.xml
@@ -18,6 +18,7 @@
redis-sample
arimaa
lovemining
+ movies-xml
diff --git a/jdk_21_maven/em/external/rest/movies-xml/pom.xml b/jdk_21_maven/em/external/rest/movies-xml/pom.xml
new file mode 100644
index 000000000..dfc4dff79
--- /dev/null
+++ b/jdk_21_maven/em/external/rest/movies-xml/pom.xml
@@ -0,0 +1,71 @@
+
+
+
+ 4.0.0
+
+ evomaster-benchmark-jdk21-em-external-rest-movies-xml
+ jar
+
+
+ org.evomaster
+ evomaster-benchmark-jdk21-em-external-rest
+ 4.3.1-SNAPSHOT
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 3.5.16
+ pom
+ import
+
+
+
+
+
+
+
+ com.h2database
+ h2
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+ package
+
+ shade
+
+
+ movies-xml-evomaster-runner
+
+
+
+ em.external.org.movies.xml.ExternalEvoMasterController
+
+ org.evomaster.client.java.instrumentation.InstrumentingAgent
+
+ org.evomaster.client.java.instrumentation.InstrumentingAgent
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jdk_21_maven/em/external/rest/movies-xml/src/main/java/em/external/org/movies/xml/ExternalEvoMasterController.java b/jdk_21_maven/em/external/rest/movies-xml/src/main/java/em/external/org/movies/xml/ExternalEvoMasterController.java
new file mode 100644
index 000000000..2fe2cb0ce
--- /dev/null
+++ b/jdk_21_maven/em/external/rest/movies-xml/src/main/java/em/external/org/movies/xml/ExternalEvoMasterController.java
@@ -0,0 +1,205 @@
+package em.external.org.movies.xml;
+
+import org.evomaster.client.java.controller.ExternalSutController;
+import org.evomaster.client.java.controller.InstrumentedSutStarter;
+import org.evomaster.client.java.controller.api.dto.SutInfoDto;
+import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto;
+import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType;
+import org.evomaster.client.java.controller.problem.ProblemInfo;
+import org.evomaster.client.java.controller.problem.RestProblem;
+import org.evomaster.client.java.sql.DbSpecification;
+import org.h2.tools.Server;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+public class ExternalEvoMasterController extends ExternalSutController {
+
+ public static void main(String[] args) {
+
+ int controllerPort = 40100;
+ if (args.length > 0) {
+ controllerPort = Integer.parseInt(args[0]);
+ }
+ int sutPort = 12345;
+ if (args.length > 1) {
+ sutPort = Integer.parseInt(args[1]);
+ }
+ String jarLocation = "cs/rest/movies-xml/target";
+ if (args.length > 2) {
+ jarLocation = args[2];
+ }
+ if (!jarLocation.endsWith(".jar")) {
+ jarLocation += "/movies-xml-sut.jar";
+ }
+ int timeoutSeconds = 120;
+ if (args.length > 3) {
+ timeoutSeconds = Integer.parseInt(args[3]);
+ }
+ String command = "java";
+ if (args.length > 4) {
+ command = args[4];
+ }
+
+ ExternalEvoMasterController controller =
+ new ExternalEvoMasterController(controllerPort, jarLocation, sutPort, timeoutSeconds, command);
+ controller.setNeedsJdk17Options(true);
+
+ InstrumentedSutStarter starter = new InstrumentedSutStarter(controller);
+ starter.start();
+ }
+
+ private final int timeoutSeconds;
+ private final int sutPort;
+ private final int dbPort;
+ private String jarLocation;
+ private Connection sqlConnection;
+ private List dbSpecification;
+ private Server h2;
+
+ public ExternalEvoMasterController() {
+ this(40100, "cs/rest/movies-xml/target", 12345, 120, "java");
+ }
+
+ public ExternalEvoMasterController(String jarLocation) {
+ this();
+ this.jarLocation = jarLocation;
+ }
+
+ public ExternalEvoMasterController(
+ int controllerPort, String jarLocation, int sutPort, int timeoutSeconds, String command) {
+
+ if (jarLocation == null || jarLocation.isEmpty()) {
+ throw new IllegalArgumentException("Missing jar location");
+ }
+
+ this.sutPort = sutPort;
+ this.dbPort = sutPort + 1;
+ this.jarLocation = jarLocation;
+ this.timeoutSeconds = timeoutSeconds;
+ setControllerPort(controllerPort);
+ setJavaCommand(command);
+ }
+
+ private String dbUrl() {
+ return "jdbc:h2:tcp://localhost:" + dbPort + "/mem:movies_" + dbPort + ";DB_CLOSE_DELAY=-1";
+ }
+
+ @Override
+ public String[] getInputParameters() {
+ return new String[]{
+ "--server.port=" + sutPort,
+ "--spring.datasource.url=" + dbUrl(),
+ "--spring.datasource.username=sa",
+ "--spring.datasource.password=",
+ "--logging.level.root=OFF",
+ "--logging.level.org.springframework=INFO"
+ };
+ }
+
+ @Override
+ public String[] getJVMParameters() {
+ return new String[]{};
+ }
+
+ @Override
+ public String getBaseURL() {
+ return "http://localhost:" + sutPort;
+ }
+
+ @Override
+ public String getPathToExecutableJar() {
+ return jarLocation;
+ }
+
+ @Override
+ public String getLogMessageOfInitializedServer() {
+ return "Tomcat started on ";
+ }
+
+ @Override
+ public long getMaxAwaitForInitializationInSeconds() {
+ return timeoutSeconds;
+ }
+
+ @Override
+ public void preStart() {
+ try {
+ // -ifNotExists: H2 2.x refuses to create a database over a TCP connection without it
+ h2 = Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", "" + dbPort, "-ifNotExists");
+ h2.start();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void postStart() {
+ closeDataBaseConnection();
+
+ try {
+ sqlConnection = DriverManager.getConnection(dbUrl(), "sa", "");
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ dbSpecification = Arrays.asList(new DbSpecification(DatabaseType.H2, sqlConnection));
+ }
+
+ @Override
+ public void resetStateOfSUT() {
+ }
+
+ @Override
+ public void preStop() {
+ closeDataBaseConnection();
+ }
+
+ @Override
+ public void postStop() {
+ if (h2 != null) {
+ h2.stop();
+ }
+ }
+
+ private void closeDataBaseConnection() {
+ if (sqlConnection != null) {
+ try {
+ sqlConnection.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ sqlConnection = null;
+ }
+ }
+
+ @Override
+ public String getPackagePrefixesToCover() {
+ return "org.movies.xml.";
+ }
+
+ @Override
+ public ProblemInfo getProblemInfo() {
+ return new RestProblem(
+ getBaseURL() + "/v3/api-docs",
+ null
+ );
+ }
+
+ @Override
+ public SutInfoDto.OutputFormat getPreferredOutputFormat() {
+ return SutInfoDto.OutputFormat.JAVA_JUNIT_5;
+ }
+
+ @Override
+ public List getInfoForAuthentication() {
+ return null;
+ }
+
+ @Override
+ public List getDbSpecifications() {
+ return dbSpecification;
+ }
+}
diff --git a/jdk_21_maven/em/external/rest/pom.xml b/jdk_21_maven/em/external/rest/pom.xml
index 0e905bd43..a5a273710 100644
--- a/jdk_21_maven/em/external/rest/pom.xml
+++ b/jdk_21_maven/em/external/rest/pom.xml
@@ -19,6 +19,7 @@
redis-sample
arimaa
lovemining
+ movies-xml
\ No newline at end of file
diff --git a/openapi/movies-xml.json b/openapi/movies-xml.json
new file mode 100644
index 000000000..197a0fb24
--- /dev/null
+++ b/openapi/movies-xml.json
@@ -0,0 +1,776 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "title": "movies-xml",
+ "description": "An XML-only movie catalogue. Every request and response body is application/xml.",
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "http://localhost:8080",
+ "description": "Generated server url"
+ }
+ ],
+ "paths": {
+ "/movies/{id}": {
+ "get": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "Fetch one movie",
+ "operationId": "byId",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The movie",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "No movie with that id",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "Replace a movie in full",
+ "operationId": "replace",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "The stored movie",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Malformed or invalid document",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "No movie with that id",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "Title and year already taken",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "415": {
+ "description": "Body is not application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "Delete a movie",
+ "operationId": "delete",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Deleted"
+ },
+ "404": {
+ "description": "No movie with that id",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "Update only the fields present in the document",
+ "operationId": "patch",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "The stored movie",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Malformed or invalid document",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "No movie with that id",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "Title and year already taken",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "415": {
+ "description": "Body is not application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/movies": {
+ "get": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "List movies, optionally filtered and sorted",
+ "operationId": "search",
+ "parameters": [
+ {
+ "name": "genre",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "ACTION",
+ "COMEDY",
+ "DRAMA",
+ "HORROR",
+ "SCIFI"
+ ]
+ }
+ },
+ {
+ "name": "titleContains",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "minYear",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ {
+ "name": "maxYear",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ {
+ "name": "minRating",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ {
+ "name": "sort",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Matching movies, possibly none",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MoviesDto"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid query parameters",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "Create a movie",
+ "operationId": "create",
+ "requestBody": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "201": {
+ "description": "Created",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Malformed or invalid document",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "Title and year already taken",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "415": {
+ "description": "Body is not application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/movies/stats": {
+ "get": {
+ "tags": [
+ "movie-controller"
+ ],
+ "summary": "Per genre counts and average ratings",
+ "operationId": "stats",
+ "responses": {
+ "200": {
+ "description": "Catalogue statistics",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/StatsDto"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Method not supported on this path",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ },
+ "406": {
+ "description": "Client asked for a type other than application/xml",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorDto"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ActorDto": {
+ "required": [
+ "name"
+ ],
+ "type": "object",
+ "properties": {
+ "billing": {
+ "maximum": 99,
+ "minimum": 1,
+ "type": "integer",
+ "format": "int32",
+ "xml": {
+ "name": "billing",
+ "attribute": true
+ }
+ },
+ "name": {
+ "maxLength": 120,
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "actor"
+ }
+ },
+ "DirectorDto": {
+ "required": [
+ "name"
+ ],
+ "type": "object",
+ "properties": {
+ "nationality": {
+ "pattern": "^[A-Z]{2}$",
+ "type": "string",
+ "xml": {
+ "name": "nationality",
+ "attribute": true
+ }
+ },
+ "name": {
+ "maxLength": 120,
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "director"
+ }
+ },
+ "MovieDto": {
+ "required": [
+ "director",
+ "genre",
+ "title",
+ "year"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "xml": {
+ "name": "id",
+ "attribute": true
+ }
+ },
+ "year": {
+ "maximum": 2100,
+ "minimum": 1888,
+ "type": "integer",
+ "format": "int32",
+ "xml": {
+ "name": "year",
+ "attribute": true
+ }
+ },
+ "title": {
+ "maxLength": 200,
+ "minLength": 1,
+ "type": "string"
+ },
+ "genre": {
+ "type": "string",
+ "enum": [
+ "ACTION",
+ "COMEDY",
+ "DRAMA",
+ "HORROR",
+ "SCIFI"
+ ]
+ },
+ "rating": {
+ "maximum": 10.0,
+ "exclusiveMaximum": false,
+ "minimum": 0.0,
+ "exclusiveMinimum": false,
+ "type": "number",
+ "format": "double"
+ },
+ "director": {
+ "$ref": "#/components/schemas/DirectorDto"
+ },
+ "cast": {
+ "maxItems": 10,
+ "minItems": 0,
+ "type": "array",
+ "xml": {
+ "name": "cast",
+ "wrapped": true
+ },
+ "items": {
+ "$ref": "#/components/schemas/ActorDto"
+ }
+ }
+ },
+ "xml": {
+ "name": "movie"
+ }
+ },
+ "ErrorDto": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "integer",
+ "format": "int32",
+ "xml": {
+ "name": "status",
+ "attribute": true
+ }
+ },
+ "message": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "error"
+ }
+ },
+ "MoviesDto": {
+ "type": "object",
+ "properties": {
+ "movies": {
+ "type": "array",
+ "xml": {
+ "name": "movie",
+ "wrapped": false
+ },
+ "items": {
+ "$ref": "#/components/schemas/MovieDto"
+ }
+ }
+ },
+ "xml": {
+ "name": "movies"
+ }
+ },
+ "GenreStatsDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "xml": {
+ "name": "name",
+ "attribute": true
+ }
+ },
+ "count": {
+ "type": "integer",
+ "format": "int64",
+ "xml": {
+ "name": "count",
+ "attribute": true
+ }
+ },
+ "averageRating": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "xml": {
+ "name": "genre"
+ }
+ },
+ "StatsDto": {
+ "type": "object",
+ "properties": {
+ "total": {
+ "type": "integer",
+ "format": "int64",
+ "xml": {
+ "name": "total",
+ "attribute": true
+ }
+ },
+ "genres": {
+ "type": "array",
+ "xml": {
+ "name": "genre",
+ "wrapped": false
+ },
+ "items": {
+ "$ref": "#/components/schemas/GenreStatsDto"
+ }
+ }
+ },
+ "xml": {
+ "name": "stats"
+ }
+ }
+ }
+ }
+}
diff --git a/scripts/build/docker-compose.build.yml b/scripts/build/docker-compose.build.yml
index 265b3e4aa..031cd6d93 100644
--- a/scripts/build/docker-compose.build.yml
+++ b/scripts/build/docker-compose.build.yml
@@ -264,6 +264,7 @@ services:
cp cs/rest/arimaa/target/arimaa-sut.jar /dist/
cp cs/rest/joinus/target/joinus-sut.jar /dist/
cp cs/rest/lovemining/target/lovemining-sut.jar /dist/
+ cp cs/rest/movies-xml/target/movies-xml-sut.jar /dist/
cp cs/rest-gui/webgoat/target/webgoat-sut.jar /dist/
cp cs/rest-gui/microcks/webapp/target/microcks-sut.jar /dist/
if [ "$${BUILD_EVOMASTER:-false}" = "true" ]; then
@@ -273,6 +274,7 @@ services:
cp em/external/rest/arimaa/target/arimaa-evomaster-runner.jar /dist/
cp em/external/rest/joinus/target/joinus-evomaster-runner.jar /dist/
cp em/external/rest/lovemining/target/lovemining-evomaster-runner.jar /dist/
+ cp em/external/rest/movies-xml/target/movies-xml-evomaster-runner.jar /dist/
cp em/external/rest-gui/webgoat/target/webgoat-evomaster-runner.jar /dist/
cp em/external/rest-gui/microcks/target/microcks-evomaster-runner.jar /dist/
fi
diff --git a/scripts/dist-wb.py b/scripts/dist-wb.py
index f67ce750e..7b11aa998 100644
--- a/scripts/dist-wb.py
+++ b/scripts/dist-wb.py
@@ -309,6 +309,8 @@ def build_jdk_21_maven():
copy(folder + "/em/external/rest/joinus/target/joinus-evomaster-runner.jar", DIST)
copy(folder + "/cs/rest/lovemining/target/lovemining-sut.jar", DIST)
copy(folder + "/em/external/rest/lovemining/target/lovemining-evomaster-runner.jar", DIST)
+ copy(folder + "/cs/rest/movies-xml/target/movies-xml-sut.jar", DIST)
+ copy(folder + "/em/external/rest/movies-xml/target/movies-xml-evomaster-runner.jar", DIST)
copy(folder + "/cs/rest-gui/webgoat/target/webgoat-sut.jar", DIST)
copy(folder + "/em/external/rest-gui/webgoat/target/webgoat-evomaster-runner.jar", DIST)
diff --git a/scripts/dockerize/data/sut.csv b/scripts/dockerize/data/sut.csv
index a08fe924e..3e8cc9c4e 100644
--- a/scripts/dockerize/data/sut.csv
+++ b/scripts/dockerize/data/sut.csv
@@ -44,3 +44,4 @@ joinus,TRUE,,--server.port=8080 --spring.mongodb.uri=mongodb://db:27017/joinUs -
lovemining,TRUE,,--server.port=8080 --spring.data.mongodb.uri=mongodb://db:27017/LoveMining --spring.neo4j.uri=bolt://neo4j:7687 --spring.neo4j.authentication.username=neo4j --spring.neo4j.authentication.password=wfdNeo4jPass123,http://localhost:8080/v3/api-docs,http://localhost:8080,FALSE,db;neo4j,"[{""image_name"": ""mongo:7"", ""tmp_fs"": """", ""environment"": """", ""volume"": ""./additional_files/lovemining/lovemining-mongo-seed.js:/docker-entrypoint-initdb.d/lovemining-mongo-seed.js"", ""health_check_command"": ""mongosh --quiet --eval \""db.adminCommand('ping')\""""}, {""name"": ""neo4j"", ""image_name"": ""neo4j:5.26"", ""tmp_fs"": """", ""environment"": ""NEO4J_AUTH: neo4j/wfdNeo4jPass123"", ""volume"": ""./additional_files/lovemining/lovemining-neo4j-seed.cypher:/seed.cypher"", ""health_check_command"": ""cypher-shell -u neo4j -p wfdNeo4jPass123 \""MATCH (:WfdSeedComplete) RETURN 1\"""", ""command"": ""[\""bash\"", \""-c\"", '/startup/docker-entrypoint.sh neo4j & pid=$!; until cypher-shell -u neo4j -p wfdNeo4jPass123 \""RETURN 1\"" >/dev/null 2>&1; do sleep 2; done; cypher-shell -u neo4j -p wfdNeo4jPass123 -f /seed.cypher; cypher-shell -u neo4j -p wfdNeo4jPass123 \""CREATE (:WfdSeedComplete)\""; wait $pid']""}]"
digitalbanking,TRUE,,--server.port=8080 --REDIS_HOST=db --REDIS_PORT=6379 --REDIS_PASSWORD=jasonrocks --redis.host=db --redis.port=6379 --KAFKA_HOST=kafka --KAFKA_PORT=9092 --SPRING_CASSANDRA_HOST=cassandra --SPRING_CASSANDRA_PORT=9042 --SPRING_CASSANDRA_CLUSTER=test --SPRING_CASSANDRA_DATACENTER=datacenter1,http://localhost:8080/v3/api-docs,http://localhost:8080,FALSE,db;cassandra,"[{""image_name"": ""redis/redis-stack-server:7.4.0-v1"", ""tmp_fs"": """", ""environment"": ""REDIS_ARGS: --requirepass jasonrocks"", ""volume"": """", ""health_check_command"": ""redis-cli -a jasonrocks ping | grep PONG""}, {""name"": ""cassandra"", ""image_name"": ""cassandra:4.1"", ""tmp_fs"": """", ""environment"": ""CASSANDRA_CLUSTER_NAME: test;CASSANDRA_DC: datacenter1;CASSANDRA_ENDPOINT_SNITCH: SimpleSnitch;HEAP_NEWSIZE: 128M;MAX_HEAP_SIZE: 512M"", ""volume"": """", ""health_check_command"": ""cqlsh -e \""CREATE KEYSPACE IF NOT EXISTS banking WITH replication = {'class':'SimpleStrategy','replication_factor':1};CREATE TABLE IF NOT EXISTS banking.transaction (tranid text, accountno text, amounttype text, merchant text, referencekeytype text, referencekeyvalue text, originalamount text, amount text, trancd text, description text, initialdate text, settlementdate text, postingdate text, status text, disputeid text, transactionreturn text, location text, transactiontags text, primary key (tranid));\""""}, {""name"": ""kafka"", ""image_name"": ""apache/kafka:3.8.1"", ""tmp_fs"": """", ""environment"": ""KAFKA_NODE_ID: 1;KAFKA_PROCESS_ROLES: broker,controller;KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093;KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092;KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER;KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT;KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093;KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1;KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1;KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1;KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0"", ""volume"": """", ""health_check_command"": """"}]"
komga,TRUE,,--server.port=8080 --komga.config-dir=/tmp/komga --komga.database.file=/komga-database.sqlite,http://localhost:8080/v3/api-docs,http://localhost:8080,TRUE,,
+movies-xml,TRUE,,--server.port=8080,http://localhost:8080/v3/api-docs,http://localhost:8080,FALSE,,
diff --git a/statistics/data.csv b/statistics/data.csv
index b34ad7374..0435ca66c 100644
--- a/statistics/data.csv
+++ b/statistics/data.csv
@@ -56,4 +56,5 @@ TRUE,digitalbanking,REST,Java,JDK 17,Maven,27,2835,Redis;Cassandra,UNDEFINED,55,
TRUE,komga,REST,Kotlin,JDK 17,Gradle,543,62303,SQLite,MIT,174,TRUE,https://github.com/gotson/komga
FALSE,ind0,REST,Java,JDK 8,Gradle,103,17039,PostgreSQL,Proprietary,20,FALSE,UNDEFINED
FALSE,ind1,REST,Java;Kotlin,JDK 11,Gradle,163,15240,PostgreSQL,Proprietary,53,FALSE,UNDEFINED
+TRUE,movies-xml,REST,Java,JDK 21,Maven,22,2502,H2,LGPL,7,FALSE,https://github.com/suarezrominajulieta/movies-xml
diff --git a/statistics/table_emb.md b/statistics/table_emb.md
index 96943940b..dc646ee81 100644
--- a/statistics/table_emb.md
+++ b/statistics/table_emb.md
@@ -29,6 +29,7 @@
|REST|__lovemining__|1793|25|18|Java|JDK 21|Maven|MongoDB, Neo4j|✓|
|REST|__market__|9861|124|13|Java|JDK 11|Maven|H2|✓|
|REST|__microcks__|66186|471|88|Java|JDK 21|Maven|MongoDB|✓|
+|REST|__movies-xml__|2502|22|7|Java|JDK 21|Maven|H2||
|REST|__ocvn__|45521|526|258|Java|JDK 8|Maven|H2, MongoDB|✓|
|REST|__ohsome-api__|14166|87|134|Java|JDK 17|Maven|OSHDB||
|REST|__pay-publicapi__|34576|377|10|Java|JDK 11|Maven|Redis|✓|